PUBLIC HTStream * HTRules (HTRequest * request, void * param, HTFormat input_format, HTFormat output_format, HTStream * output_stream) { HTAlertCallback *cbf = HTAlert_find(HT_A_CONFIRM); /* ** If the library has been compiled so that we automatically accept ** rule files then it's OK not to ask the user. */ #ifdef HT_AUTOMATIC_RULES if (!cbf || (cbf && (*cbf)(request,HT_A_CONFIRM, HT_MSG_RULES, NULL,NULL,NULL))) { #else if ((cbf && (*cbf)(request,HT_A_CONFIRM, HT_MSG_RULES, NULL,NULL,NULL))) { #endif HTStream * me; HTTRACE(APP_TRACE, "Rule file... Parser object created\n"); if ((me = (HTStream *) HT_CALLOC(1, sizeof(HTStream))) == NULL) HT_OUTOFMEM("HTRules"); me->isa = &HTRuleClass; me->request = request; me->buffer = HTChunk_new(512); me->EOLstate = EOL_BEGIN; if (!rules) rules = HTList_new(); return me; } else { HTRequest_addError(request, ERR_FATAL, NO, HTERR_NO_AUTO_RULES, NULL, 0, "HTRules"); return HTErrorStream(); } } /* ** Parse a rule file - don't ask don't tell - be carefull with this one! */ PUBLIC HTStream * HTRules_parseAutomatically (HTRequest * request, void * param, HTFormat input_format, HTFormat output_format, HTStream * output_stream) { if (request) { HTStream * me; HTTRACE(APP_TRACE, "Rule file... Automatic parser object created\n"); if ((me = (HTStream *) HT_CALLOC(1, sizeof(HTStream))) == NULL) HT_OUTOFMEM("HTRules"); me->isa = &HTRuleClass; me->request = request; me->buffer = HTChunk_new(512); me->EOLstate = EOL_BEGIN; if (!rules) rules = HTList_new(); return me; } else { HTRequest_addError(request, ERR_FATAL, NO, HTERR_NO_AUTO_RULES, NULL, 0, "HTRules"); return HTErrorStream(); } }
/* Take action using a system command ** ---------------------------------- ** Creates temporary file, writes to it and then executes system ** command (maybe an external viewer) when EOF has been reached. The ** stream finds a suitable name of the temporary file which preserves the ** suffix. This way, the system command can find out the file type from ** the name of the temporary file name. */ PUBLIC HTStream* HTSaveAndExecute (HTRequest * request, void * param, HTFormat input_format, HTFormat output_format, HTStream * output_stream) { FILE * fp = NULL; char * filename = NULL; HTUserProfile * up = HTRequest_userProfile(request); char * tmproot = HTUserProfile_tmp(up); if (HTLib_secure()) { HTRequest_addError(request, ERR_NON_FATAL, NO, HTERR_UNAUTHORIZED, NULL, 0, "HTSaveLocally"); return HTErrorStream(); } if (!tmproot) { HTTRACE(STREAM_TRACE, "Save File... turned off"); return HTErrorStream(); } /* Let's find a hash name for this file without asking user */ { HTParentAnchor *anchor = (HTParentAnchor *) HTRequest_anchor(request); char *suffix = HTBind_getSuffix(anchor); filename = get_filename(tmproot, HTAnchor_physical(anchor), suffix, NO); HT_FREE(suffix); if (filename) { if ((fp = fopen(filename, "wb")) == NULL) { HTRequest_addError(request, ERR_NON_FATAL, NO, HTERR_NO_FILE, filename, strlen(filename),"HTSaveAndExecute"); HT_FREE(filename); return HTErrorStream(); } } else { HTTRACE(STREAM_TRACE, "Save File... No file name\n"); return HTErrorStream(); } } /* Now we are ready for creating the file writer stream */ if (fp) { HTStream * me = HTFileSave_new(request, fp, NO); me->filename = filename; if (param) { if ((me->end_command = (char *) HT_MALLOC((strlen((char *) param) + 10 + 3*strlen(filename)))) == NULL) HT_OUTOFMEM("SaveAndExecute"); sprintf (me->end_command, (char *)param, filename, filename, filename); } return me; } HT_FREE(filename); return HTErrorStream(); }
/* ** Retry through Proxy AFTER Filter ** -------------------------------- ** This filter handles a 305 Use Proxy response and retries the request ** through the proxy */ PUBLIC int HTUseProxyFilter (HTRequest * request, HTResponse * response, void * param, int status) { HTAlertCallback * cbf = HTAlert_find(HT_A_CONFIRM); HTAnchor * proxy_anchor = HTResponse_redirection(response); if (!proxy_anchor) { HTTRACE(PROT_TRACE, "Use Proxy... No proxy location\n"); return HT_OK; } /* ** Add the proxy to the list. Assume HTTP access method only! ** Because evil servers may rediret the client to an untrusted ** proxy, we can only accept redirects for this particular ** server. Also, we do not know whether this is for HTTP or all ** other requests as well */ if ((cbf && (*cbf)(request, HT_A_CONFIRM, HT_MSG_PROXY, NULL,NULL,NULL))) { char * addr = HTAnchor_address(proxy_anchor); HTProxy_add("http", addr); HT_FREE(addr); /* ** Start new request through the proxy if we haven't reached the max ** number of redirections for this request */ if (HTRequest_doRetry(request)) { HTLoadAnchor(proxy_anchor, request); } else { HTRequest_addError(request, ERR_FATAL, NO, HTERR_MAX_REDIRECT, NULL, 0, "HTRedirectFilter"); } /* ** By returning HT_ERROR we make sure that this is the last handler to be ** called. We do this as we don't want any other filter to delete the ** request object now when we have just started a new one ourselves */ return HT_ERROR; } else { HTRequest_addError(request, ERR_FATAL, NO, HTERR_NO_AUTO_PROXY, NULL, 0, "HTUseProxyFilter"); return HT_OK; } }
/* ** Scan the request line for METHOD, URI and VERSION ** Returns: HT_OK if 1.x request and OK ** HT_LOADED if 0.9 request and OK ** HT_ERROR if invalid request line */ PRIVATE int ParseRequest (HTStream * me) { HTRequest * client = HTList_firstObject(me->http->clients); char * line = HTChunk_data(me->buffer); char * method_str = HTNextField(&line); char * request_uri = HTNextField(&line); char * version_str = HTNextField(&line); HTMethod method; /* Check if method is allowed */ if (!method_str || (method = HTMethod_enum(method_str))==METHOD_INVALID) { HTRequest_addError(client, ERR_FATAL, NO, HTERR_NOT_ALLOWED, NULL, 0, "ParseRequest"); return HT_ERROR; } HTRequest_setMethod(client, method); /* Find an anchor for the request URI */ if (request_uri) { char * uri = HTParse(request_uri, "file:", PARSE_ALL); HTRequest_setAnchor(client, HTAnchor_findAddress(uri)); HT_FREE(uri); } else { HTRequest_addError(client, ERR_FATAL, NO, HTERR_BAD_REQUEST, NULL, 0, "ParseRequest"); return HT_ERROR; } /* Get ready to get the rest of the request */ if (version_str) { me->target = HTStreamStack(WWW_MIME_HEAD, HTRequest_debugFormat(client), HTRequest_debugStream(client), client, NO); return HT_OK; } else { HTRequest_addError(client, ERR_FATAL, NO, HTERR_BAD_VERSION, NULL, 0, "ParseRequest"); return HT_ERROR; } }
/* ** Rule Translation BEFORE Filter ** ------------------------------ ** If we have a set of rules loaded (see the Rule manager) then check ** before each request whether how that should be translated. The trick ** is that a parent anchor has a "address" which is the part from the URL ** we used when we created the anchor. However, it also have a "physical ** address" which is the place we are actually going to look for the ** resource. Hence this filter translates the physical address ** (if any translations are found) */ PUBLIC int HTRuleFilter (HTRequest * request, void * param, int mode) { HTList * list = HTRule_global(); HTParentAnchor * anchor = HTRequest_anchor(request); char * addr = HTAnchor_physical(anchor); char * physical = HTRule_translate(list, addr, NO); if (!physical) { HTRequest_addError(request, ERR_FATAL, NO, HTERR_FORBIDDEN, NULL, 0, "HTRuleFilter"); return HT_ERROR; } HTAnchor_setPhysical(anchor, physical); HT_FREE(physical); return HT_OK; }
/* ** Client side authentication BEFORE filter ** ---------------------------------------- ** The filter generates the credentials required to access a document ** Getting the credentials may involve asking the user */ PUBLIC int HTCredentialsFilter (HTRequest * request, void * param, int mode) { /* ** Ask the authentication module to call the right credentials generator ** that understands this scheme */ if (HTAA_beforeFilter(request, param, mode) == HT_OK) { HTTRACE(PROT_TRACE, "Credentials. verified\n"); return HT_OK; } else { HTRequest_addError(request, ERR_FATAL, NO, HTERR_UNAUTHORIZED, NULL, 0, "HTCredentialsFilter"); return HT_ERROR; } }
/* ** Search registered transports to find a suitable transport object. ** Return transport object or NULL */ PUBLIC HTTransport * HTTransport_find (HTRequest * request, const char * name) { if (name) { HTList * cur = transports; HTTransport * pres; if (cur) { while ((pres = (HTTransport *) HTList_nextObject(cur))) { if (!strcmp(pres->name, name)) return pres; } } if (request) HTRequest_addError(request, ERR_FATAL, NO, HTERR_CLASS, (char *) name, (int) strlen(name), "HTTransport_find"); } return NULL; }
/* ** Search registered protocols to find suitable protocol object. ** Return protocol object or NULL */ PUBLIC HTProtocol * HTProtocol_find (HTRequest * request, const char * access) { if (access) { HTList * cur = protocols; HTProtocol * pres; if (cur) { while ((pres = (HTProtocol *) HTList_nextObject(cur))) { if (!strcmp(pres->name, access)) return pres; } } if (request) HTRequest_addError(request, ERR_FATAL, NO, HTERR_CLASS, (char*) access, (int) strlen(access), "HTProtocol_find"); } return NULL; }
/* ScanResponse ** ------------ ** Analyzes the response from the NNTP server. ** We only expect one line response codes. ** Returns HT_LOADED if OK, HT_ERROR if error */ PRIVATE int ScanResponse (HTStream * me) { news_info *news = me->news; *(me->buffer+me->buflen) = '\0'; if (isdigit((int) *(me->buffer))) sscanf(me->buffer, "%d", &news->repcode); me->buflen = 0; news->reply = me->buffer+4; HTTRACE(PROT_TRACE, "News Rx..... `%s\'\n" _ news->reply); /* If 2xx code and we expect data then go into semi-transparent mode */ if (me->news->format && news->repcode/100 == 2) { HTRequest *req = me->request; me->target = HTStreamStack(me->news->format, req->output_format, req->output_stream, req, NO); me->semi_trans = YES; if (!me->target) return HT_ERROR; } else if (news->repcode/100 == 4) { HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_NOT_FOUND, news->reply, strlen(news->reply), "ScanResponse"); } return HT_LOADED; }
PRIVATE int FileEvent (SOCKET soc, void * pVoid, HTEventType type) { file_info *file = pVoid; /* Specific access information */ int status = HT_ERROR; HTNet * net = file->net; HTRequest * request = HTNet_request(net); HTParentAnchor * anchor = HTRequest_anchor(request); /* ** Initiate a new file structure and bind to request structure ** This is actually state FILE_BEGIN, but it can't be in the state ** machine as we need the structure first. */ if (type == HTEvent_CLOSE) { /* Interrupted */ HTRequest_addError(request, ERR_FATAL, NO, HTERR_INTERRUPTED, NULL, 0, "HTLoadFile"); FileCleanup(request, HT_INTERRUPTED); return HT_OK; } /* Now jump into the machine. We know the state from the previous run */ while (1) { switch (file->state) { case FS_BEGIN: /* We only support safe (GET, HEAD, etc) methods for the moment */ if (!HTMethod_isSafe(HTRequest_method(request))) { HTRequest_addError(request, ERR_FATAL, NO, HTERR_NOT_ALLOWED, NULL, 0, "HTLoadFile"); file->state = FS_ERROR; break; } /* Check whether we have access to local disk at all */ if (HTLib_secure()) { HTTRACE(PROT_TRACE, "LoadFile.... No access to local file system\n"); file->state = FS_TRY_FTP; break; } file->local = HTWWWToLocal(HTAnchor_physical(anchor), "", HTRequest_userProfile(request)); if (!file->local) { file->state = FS_TRY_FTP; break; } /* Create a new host object and link it to the net object */ { HTHost * host = NULL; if ((host = HTHost_new("localhost", 0)) == NULL) return HT_ERROR; HTNet_setHost(net, host); if (HTHost_addNet(host, net) == HT_PENDING) { HTTRACE(PROT_TRACE, "HTLoadFile.. Pending...\n"); /* move to the hack state */ file->state = FS_PENDING; return HT_OK; } } file->state = FS_DO_CN; break; case FS_PENDING: /* ** 2000/08/10 JK : This is a funny state. Because of the ** internal libwww stacks, when doing multiple local ** requests (e.g., while using the Robot), we need to ask ** again for the host object. If we had jumped directly to ** the FS_DO_CN state, libwww would have blocked because ** of socket starvation. ** This state is similar to FS_BEGINNING, but just requests ** the host object. ** YES. THIS IS AN UGLY HACK!! */ { HTHost * host = NULL; if ((host = HTHost_new("localhost", 0)) == NULL) return HT_ERROR; HTNet_setHost(net, host); if (HTHost_addNet(host, net) == HT_PENDING) { HTTRACE(PROT_TRACE, "HTLoadFile.. Pending...\n"); file->state = FS_PENDING; return HT_OK; } } file->state = FS_DO_CN; break; case FS_DO_CN: /* ** If we have to do content negotiation then find the object that ** fits best into either what the client has indicated in the ** accept headers or what the client has registered on its own. ** The object chosen can in fact be a directory! However, content ** negotiation only makes sense if we can read the directory! ** We stat the file in order to find the size and to see it if ** exists. */ if (HTRequest_negotiation(request) && HTMethod_isSafe(HTRequest_method(request))) { char * conneg = HTMulti(request, file->local,&file->stat_info); if (conneg) { HT_FREE(file->local); file->local = conneg; HTAnchor_setPhysical(anchor, conneg); HTTRACE(PROT_TRACE, "Load File... Found `%s\'\n" _ conneg); } else { HTTRACE(PROT_TRACE, "Load File... Not found - even tried content negotiation\n"); HTRequest_addError(request, ERR_FATAL, NO, HTERR_NOT_FOUND, NULL, 0, "HTLoadFile"); file->state = FS_ERROR; break; } } else { if (HT_STAT(file->local, &file->stat_info) == -1) { HTTRACE(PROT_TRACE, "Load File... Not found `%s\'\n" _ file->local); HTRequest_addError(request, ERR_FATAL, NO, HTERR_NOT_FOUND, NULL, 0, "HTLoadFile"); file->state = FS_ERROR; break; } } /* ** Check to see if the 'localname' is in fact a directory. ** Note that we can't do a HEAD on a directory */ if (((file->stat_info.st_mode) & S_IFMT) == S_IFDIR) { if (HTRequest_method(request) == METHOD_GET) file->state = FS_PARSE_DIR; else { HTRequest_addError(request, ERR_INFO, NO, HTERR_NO_CONTENT, NULL, 0, "HTLoadFile"); file->state = FS_NO_DATA; } break; } /* ** If empty file then only serve it if it is editable. We also get ** the bindings for the file suffixes in lack of better bindings */ { BOOL editable = HTEditable(file->local, &file->stat_info); if (file_suffix_binding) HTBind_getAnchorBindings(anchor); if (editable) HTAnchor_appendAllow(anchor, METHOD_PUT); /* Set the file size */ if (file->stat_info.st_size) HTAnchor_setLength(anchor, file->stat_info.st_size); /* Set the file last modified time stamp */ if (file->stat_info.st_mtime > 0) HTAnchor_setLastModified(anchor, file->stat_info.st_mtime); /* Check to see if we can edit it */ if (!editable && !file->stat_info.st_size) { HTRequest_addError(request, ERR_INFO, NO, HTERR_NO_CONTENT, NULL, 0, "HTLoadFile"); file->state = FS_NO_DATA; } else { file->state = (HTRequest_method(request)==METHOD_GET) ? FS_NEED_OPEN_FILE : FS_GOT_DATA; } } break; case FS_NEED_OPEN_FILE: status = HTFileOpen(net, file->local, HT_FB_RDONLY); if (status == HT_OK) { /* ** Create the stream pipe FROM the channel to the application. ** The target for the input stream pipe is set up using the ** stream stack. */ { HTStream * rstream = HTStreamStack(HTAnchor_format(anchor), HTRequest_outputFormat(request), HTRequest_outputStream(request), request, YES); HTNet_setReadStream(net, rstream); HTRequest_setOutputConnected(request, YES); } /* ** Create the stream pipe TO the channel from the application ** and hook it up to the request object */ { HTOutputStream * output = HTNet_getOutput(net, NULL, 0); HTRequest_setInputStream(request, (HTStream *) output); } /* ** Set up concurrent read/write if this request isn't the ** source for a PUT or POST. As source we don't start reading ** before all destinations are ready. If destination then ** register the input stream and get ready for read */ if (HTRequest_isSource(request) && !HTRequest_destinationsReady(request)) return HT_OK; HTRequest_addError(request, ERR_INFO, NO, HTERR_OK, NULL, 0, "HTLoadFile"); file->state = FS_NEED_BODY; /* If we are _not_ using preemptive mode and we are Unix fd's ** then return here to get the same effect as when we are ** connecting to a socket. That way, HTFile acts just like any ** other protocol module even though we are in fact doing ** blocking connect */ if (HTEvent_isCallbacksRegistered()) { if (!HTRequest_preemptive(request)) { if (!HTNet_preemptive(net)) { HTTRACE(PROT_TRACE, "HTLoadFile.. Returning\n"); HTHost_register(HTNet_host(net), net, HTEvent_READ); } else if (!file->timer) { HTTRACE(PROT_TRACE, "HTLoadFile.. Returning\n"); file->timer = HTTimer_new(NULL, ReturnEvent, file, 1, YES, NO); } return HT_OK; } } } else if (status == HT_WOULD_BLOCK || status == HT_PENDING) return HT_OK; else { HTRequest_addError(request, ERR_INFO, NO, HTERR_INTERNAL, NULL, 0, "HTLoadFile"); file->state = FS_ERROR; /* Error or interrupt */ } break; case FS_NEED_BODY: status = HTHost_read(HTNet_host(net), net); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_LOADED || status == HT_CLOSED) { file->state = FS_GOT_DATA; } else { HTRequest_addError(request, ERR_INFO, NO, HTERR_FORBIDDEN, NULL, 0, "HTLoadFile"); file->state = FS_ERROR; } break; case FS_PARSE_DIR: status = HTFile_readDir(request, file); if (status == HT_LOADED) file->state = FS_GOT_DATA; else file->state = FS_ERROR; break; case FS_TRY_FTP: { char *url = HTAnchor_physical(anchor); HTAnchor *anchor; char *newname = NULL; StrAllocCopy(newname, "ftp:"); if (!strncmp(url, "file:", 5)) StrAllocCat(newname, url+5); else StrAllocCat(newname, url); anchor = HTAnchor_findAddress(newname); HTRequest_setAnchor(request, anchor); HT_FREE(newname); FileCleanup(request, HT_IGNORE); return HTLoad(request, YES); } break; case FS_GOT_DATA: FileCleanup(request, HT_LOADED); return HT_OK; break; case FS_NO_DATA: FileCleanup(request, HT_NO_DATA); return HT_OK; break; case FS_RETRY: FileCleanup(request, HT_RETRY); return HT_OK; break; case FS_ERROR: FileCleanup(request, HT_ERROR); return HT_OK; break; } } /* End of while(1) */ }
/* HTFile_readDir ** -------------- ** Reads the directory "path" ** Returns: ** HT_ERROR Error ** HT_FORBIDDEN Directory reading not allowed ** HT_LOADED Successfully read the directory */ PRIVATE int HTFile_readDir (HTRequest * request, file_info *file) { #ifdef HAVE_READDIR DIR * dp; struct stat file_info; HTParentAnchor * anchor = HTRequest_anchor(request); char *url = HTAnchor_physical(anchor); char fullname[HT_MAX_PATH+1]; char *name; HTTRACE(PROT_TRACE, "Reading..... directory\n"); if (dir_access == HT_DIR_FORBID) { HTRequest_addError(request, ERR_FATAL, NO, HTERR_FORBIDDEN, NULL, 0, "HTFile_readDir"); return HT_FORBIDDEN; } /* Initialize path name for stat() */ if (*(name = (url+strlen(url)-1)) != '/') { char *newurl = NULL; StrAllocCopy(newurl, url); StrAllocCat(newurl, "/"); HT_FREE(file->local); file->local = HTWWWToLocal(newurl, "", HTRequest_userProfile(request)); HT_FREE(newurl); } strcpy(fullname, file->local); name = fullname+strlen(fullname); /* Point to end of fullname */ /* Check if access is enabled */ if (dir_access == HT_DIR_SELECTIVE) { strcpy(name, DEFAULT_DIR_FILE); if (HT_STAT(fullname, &file_info)) { HTTRACE(PROT_TRACE, "Read dir.... `%s\' not found\n" _ DEFAULT_DIR_FILE); HTRequest_addError(request, ERR_FATAL, NO, HTERR_FORBIDDEN, NULL, 0, "HTFile_readDir"); return HT_FORBIDDEN; } } if ((dp = opendir(file->local))) { struct dirent * dirbuf; HTDir *dir = HTDir_new(request, dir_show, dir_key); char datestr[20]; char sizestr[10]; HTFileMode mode; #ifdef HT_REENTRANT struct dirent result; /* For readdir_r */ #endif #ifdef HAVE_READDIR_R_2 while ((dirbuf = (struct dirent *) readdir_r(dp, &result))) #elif defined(HAVE_READDIR_R_3) while (readdir_r(dp, &result, &dirbuf) == 0) #else while ((dirbuf = readdir(dp))) #endif /* HAVE_READDIR_R_2 */ { /* Current and parent directories are never shown in list */ #ifdef HAVE_DIRENT_INO if (!dirbuf->d_ino || !strcmp(dirbuf->d_name, ".") || !strcmp(dirbuf->d_name, "..")) #else if (!strcmp(dirbuf->d_name, ".") || !strcmp(dirbuf->d_name, "..")) #endif continue; /* Make a lstat on the file */ strcpy(name, dirbuf->d_name); if (HT_LSTAT(fullname, &file_info)) { HTTRACE(PROT_TRACE, "Read dir.... lstat failed: %s\n" _ fullname); continue; } /* Convert stat info to fit our setup */ if (((mode_t) file_info.st_mode & S_IFMT) == S_IFDIR) { #ifdef VMS char *dot = strstr(name, ".DIR"); /* strip .DIR part... */ if (dot) *dot = '\0'; #endif /* VMS */ mode = HT_IS_DIR; if (dir_show & HT_DS_SIZE) strcpy(sizestr, "-"); } else { mode = HT_IS_FILE; if (dir_show & HT_DS_SIZE) HTNumToStr(file_info.st_size, sizestr, 10); } if (dir_show & HT_DS_DATE) HTDateDirStr(&file_info.st_mtime, datestr, 20); /* Add to the list */ if (HTDir_addElement(dir, name, datestr, sizestr, mode) != YES) break; } closedir(dp); HTDir_free(dir); return HT_LOADED; } else { HTRequest_addSystemError(request, ERR_FATAL, errno, NO, "opendir"); return HT_ERROR; } #else return HT_ERROR; /* needed for WWW_MSWINDOWS */ #endif /* HAVE_READDIR */ }
PRIVATE int NewsEvent (SOCKET soc, void * pVoid, HTEventType type) { news_info *news = (news_info *)pVoid; int status = HT_ERROR; HTNet * net = news->net; HTRequest * request = HTNet_request(net); HTParentAnchor * anchor = HTRequest_anchor(request); char * url = HTAnchor_physical(anchor); HTHost * host = HTNet_host(net); /* ** Initiate a new nntp structure and bind to request structure ** This is actually state NNTP_BEGIN, but it can't be in the state ** machine as we need the structure first. */ if (type == HTEvent_CLOSE) { /* Interrupted */ HTRequest_addError(request, ERR_FATAL, NO, HTERR_INTERRUPTED, NULL, 0, "HTLoadHTTP"); HTNewsCleanup(request, HT_INTERRUPTED); return HT_OK; } else news = (news_info *) HTNet_context(net); /* Get existing copy */ /* Now jump into the machine. We know the state from the previous run */ while (1) { switch (news->state) { case NEWS_BEGIN: news->state = (!strchr(url, '@') && strchr(url, '*')) ? NEWS_SEEK_CACHE : NEWS_NEED_CONNECTION; break; case NEWS_SEEK_CACHE: if (HTNewsCache_before(request, NULL, 0) == HT_LOADED) news->state = NEWS_SUCCESS; else news->state = NEWS_NEED_CONNECTION; break; case NEWS_NEED_CONNECTION: /* Let's set up a connection */ if (!strncasecomp(url, "news:", 5)) { HTUserProfile * up = HTRequest_userProfile(request); char * newshost = HTUserProfile_news(up); StrAllocCopy(news->name, url+5); if (newshost) { char *newshack = NULL; /* Then we can use HTParse :-) */ StrAllocCopy(newshack, "news://"); StrAllocCat(newshack, newshost); status = HTHost_connect(host, net, (char *) newshack); host = HTNet_host(net); HT_FREE(newshack); } else news->state = NEWS_ERROR; } else if (!strncasecomp(url, "nntp:", 5)) { news->name = HTParse(url, "", PARSE_PATH); status = HTHost_connect(host, net, url); host = HTNet_host(net); } else { HTTRACE(PROT_TRACE, "News........ Huh?"); news->state = NEWS_ERROR; } if (status == HT_OK) { BOOL greeting = NO; /* Set up the persistent connection */ if (!HTNet_persistent(net)) { HTNet_setPersistent(net, YES, HT_TP_SINGLE); greeting = YES; } /* ** Check the protocol class to see if we have connected to a ** the right class of server, in this case HTTP. */ { HTHost * host = HTNet_host(net); char * s_class = HTHost_class(host); if (s_class && strcasecomp(s_class, "nntp")) { HTRequest_addError(request, ERR_FATAL, NO, HTERR_CLASS, NULL, 0, "HTLoadNews"); news->state = NEWS_ERROR; break; } HTHost_setClass(host, "nntp"); } /* ** Create the stream pipe FROM the channel to the application. ** The target for the input stream pipe is set up using the ** stream stack. */ { HTStream * rstream = HTNewsStatus_new(request, news, host); HTNet_setReadStream(net, rstream); HTRequest_setOutputConnected(request, YES); } /* ** Create the stream pipe TO the channel from the application ** and hook it up to the request object */ { HTOutputStream * output = HTNet_getOutput(net, NULL, 0); HTRequest_setInputStream(request, (HTStream *) output); } news->state = greeting ? NEWS_NEED_GREETING : NEWS_NEED_SWITCH; } else if (status == HT_WOULD_BLOCK || status == HT_PENDING) return HT_OK; else news->state = NEWS_ERROR; break; case NEWS_NEED_GREETING: status = HTHost_read(HTNet_host(net), net); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_LOADED) { if (news->repcode/100 == 2) news->state = NEWS_NEED_SWITCH; else news->state = NEWS_ERROR; } else news->state = NEWS_ERROR; break; case NEWS_NEED_SWITCH: { HTMethod method = HTRequest_method(request); /* ** Find out what to ask the news server. Syntax of address is ** xxx@yyy Article ** <xxx@yyy> Same article ** xxxxx News group (no "@") */ if (method == METHOD_GET) { if (strchr(url, '@')) { /* ARTICLE */ if (*(news->name) != '<') { /* Add '<' and '>' */ char *newart; if ((newart = (char *) HT_MALLOC(strlen(news->name)+3)) == NULL) HT_OUTOFMEM("HTLoadNews"); sprintf(newart, "<%s>", news->name); HT_FREE(news->name); news->name = newart; } news->state = NEWS_NEED_ARTICLE; } else if (strchr(url, '*')) news->state = NEWS_NEED_LIST; else news->state = NEWS_NEED_GROUP; } else if (method == METHOD_POST) news->state = NEWS_NEED_POST; else { HTRequest_addError(request, ERR_FATAL, NO, HTERR_NOT_IMPLEMENTED,NULL, 0,"HTLoadNews"); news->state = NEWS_ERROR; } HTUnEscape(news->name); HTCleanTelnetString(news->name); } break; case NEWS_NEED_ARTICLE: if (!news->sent) { status = SendCommand(request, news, "ARTICLE", news->name); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_ERROR) news->state = NEWS_ERROR; news->format = WWW_MIME; /* ** Set the default content type to plain text as news servers ** almost never send any useful information about the length ** of the body or the type - the success of MIME! */ HTAnchor_setFormat(anchor, WWW_PLAINTEXT); news->sent = YES; } else { status = HTHost_read(HTNet_host(net), net); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_OK) news->state = NEWS_NEED_BODY; else if (status == HT_LOADED) { news->state = (news->repcode/100 == 2) ? NEWS_SUCCESS : NEWS_ERROR; } else news->state = NEWS_ERROR; news->sent = NO; } break; #if HT_LISTGROUP case NEWS_NEED_LGRP: if (!news->sent) { status = SendCommand(request, news, "LIST", "NEWSGROUPS"); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_ERROR) news->state = NEWS_ERROR; news->format = WWW_NNTP_LIST; news->sent = YES; } else { status = HTHost_read(HTNet_host(net), net); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_OK) news->state = NEWS_NEED_BODY; else if (status == HT_LOADED) { news->state = (news->repcode/100 == 2) ? NEWS_SUCCESS : NEWS_NEED_LIST; } else news->state = NEWS_ERROR; news->sent = NO; } break; #endif /* HT_LISTGROUP */ case NEWS_NEED_LIST: if (!news->sent) { status = SendCommand(request, news, "LIST", NULL); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_ERROR) news->state = NEWS_ERROR; news->format = WWW_NNTP_LIST; news->sent = YES; } else { status = HTHost_read(HTNet_host(net), net); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_OK) news->state = NEWS_NEED_BODY; else if (status == HT_LOADED) { news->state = (news->repcode/100 == 2) ? NEWS_SUCCESS : NEWS_ERROR; } else news->state = NEWS_ERROR; news->sent = NO; } break; case NEWS_NEED_GROUP: if (!news->sent) { status = SendCommand(request, news, "GROUP", news->name); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_ERROR) news->state = NEWS_ERROR; news->sent = YES; } else { status = HTHost_read(HTNet_host(net), net); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_LOADED) { if (news->repcode/100 == 2) { if (sscanf(news->reply, "%d%d%d", &news->total, &news->first, &news->last) == 3) { if (MaxArt && news->total>MaxArt) news->last = news->first-MaxArt; news->current = news->first; /* If no content in this group */ if (news->first == news->last) { HTRequest_addError(request, ERR_FATAL, NO, HTERR_NO_CONTENT, NULL, 0, "HTLoadNews"); news->state = NEWS_NO_DATA; break; } news->state = NEWS_NEED_XOVER; } else news->state = NEWS_ERROR; } else news->state = NEWS_ERROR; } else news->state = NEWS_ERROR; news->sent = NO; } break; case NEWS_NEED_XOVER: if (!news->sent) { char buf[20]; sprintf(buf, "%d-%d", news->first, news->last); status = SendCommand(request, news, "XOVER", buf); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_ERROR) news->state = NEWS_ERROR; news->format = WWW_NNTP_OVER; news->sent = YES; } else { status = HTHost_read(HTNet_host(net), net); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_OK) news->state = NEWS_NEED_BODY; else if (status == HT_LOADED) { if (news->repcode/100 == 2) news->state = NEWS_SUCCESS; else { news->format = WWW_NNTP_HEAD; news->state = NEWS_NEED_HEAD; } } else news->state = NEWS_ERROR; news->sent = NO; } break; case NEWS_NEED_HEAD: if (!news->sent) { char buf[10]; sprintf(buf, "%d", news->current++); status = SendCommand(request, news, "HEAD", buf); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_ERROR) news->state = NEWS_ERROR; news->sent = YES; } else { status = HTHost_read(HTNet_host(net), net); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_LOADED) { if (news->repcode/100 == 2) { if (news->current > news->last) news->state = NEWS_SUCCESS; } else news->state = NEWS_ERROR; } else news->state = NEWS_ERROR; news->sent = NO; } break; case NEWS_NEED_POST: { HTStream * oldinput = HTRequest_inputStream(request); HTStream * newinput = HTNewsPost_new(request, HTBuffer_new(oldinput, request,512)); HTRequest_setInputStream(request, newinput); /* Remember to convert to CRLF */ } news->state = NEWS_NEED_BODY; break; case NEWS_NEED_BODY: if (type == HTEvent_WRITE || type == HTEvent_BEGIN) { if (HTRequest_isDestination(request)) { HTRequest * source = HTRequest_source(request); HTNet * srcnet = HTRequest_net(source); if (srcnet) { HTHost_register(HTNet_host(srcnet), srcnet, HTEvent_READ); HTHost_unregister(HTNet_host(srcnet), srcnet, HTEvent_WRITE); } return HT_OK; } /* ** Should we use the input stream directly or call the post ** callback function to send data down to the network? */ { HTStream * input = HTRequest_inputStream(request); HTPostCallback * pcbf = HTRequest_postCallback(request); if (pcbf) { status = pcbf(request, input); if (status == HT_PAUSE || status == HT_LOADED) type = HTEvent_READ; } else { status = (*input->isa->flush)(input); type = HTEvent_READ; } if (status == HT_WOULD_BLOCK) return HT_OK; } status = request->PostCallback ? request->PostCallback(request, request->input_stream) : (*request->input_stream->isa->flush)(request->input_stream); if (status == HT_WOULD_BLOCK) return HT_OK; else type = HTEvent_READ; /* Trick to ensure that we do READ */ } else if (type == HTEvent_READ) { status = HTHost_read(HTNet_host(net), net); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_LOADED) news->state = NEWS_SUCCESS; else news->state = NEWS_ERROR; } else { news->state = NEWS_ERROR; } break; case NEWS_SUCCESS: HTNewsCleanup(request, HT_LOADED); return HT_OK; break; case NEWS_NO_DATA: HTNewsCleanup(request, HT_NO_DATA); return HT_OK; break; case NEWS_ERROR: HTNewsCleanup(request, HT_NOT_FOUND); return HT_OK; break; } } /* End of while(1) */ }
PRIVATE int SocketEvent (SOCKET soc, void * pVoid, HTEventType type) { raw_info * raw = (raw_info *) pVoid; int status = HT_ERROR; HTNet * net = raw->net; HTRequest * request = raw->request; HTHost * host = HTNet_host(net); /* ** Check whether we have been interrupted or timed out */ if (type == HTEvent_BEGIN) { raw->state = RAW_BEGIN; } else if (type == HTEvent_CLOSE) { /* Interrupted */ RawCleanup(request, HT_INTERRUPTED); return HT_OK; } else if (type == HTEvent_TIMEOUT) { HTRequest_addError(request, ERR_FATAL, NO, HTERR_TIME_OUT, NULL, 0, "HTLoadSocket"); RawCleanup(request, HT_TIMEOUT); return HT_OK; } else if (type == HTEvent_END) { RawCleanup(request, HT_OK); return HT_OK; } /* Now jump into the state machine */ while (1) { switch(raw->state) { case RAW_BEGIN: status = HTHost_accept(host, net, NULL); host = HTNet_host(net); if (status == HT_OK) { raw->state = RAW_NEED_STREAM; } else if (status == HT_WOULD_BLOCK || status == HT_PENDING) { return HT_OK; } else raw->state = RAW_ERROR; /* Error or interrupt */ break; case RAW_NEED_STREAM: { /* ** Create the stream pipe FROM the channel to the application. ** The target for the input stream pipe is set up using the ** stream stack. */ HTStream * in_stream = HTStreamStack(WWW_RAW, HTRequest_outputFormat(request), HTRequest_outputStream(request), request, YES); HTNet_setReadStream(net, in_stream); HTRequest_setOutputConnected(request, YES); raw->state = RAW_READ; break; } case RAW_READ: status = HTHost_read(host, net); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status==HT_CLOSED) raw->state = RAW_OK; else raw->state = RAW_ERROR; break; case RAW_OK: RawCleanup(request, HT_OK); return HT_OK; break; case RAW_ERROR: RawCleanup(request, HT_ERROR); return HT_OK; break; default: HTDEBUGBREAK("Bad raw state %d\n" _ raw->state); } } return HT_OK; }
/* Save Locally ** ------------ ** Saves a file to local disk. This can for example be used to dump ** data objects of unknown media types to local disk. The stream prompts ** for a file name for the temporary file. */ PUBLIC HTStream* HTSaveLocally (HTRequest * request, void * param, HTFormat input_format, HTFormat output_format, HTStream * output_stream) { FILE * fp = NULL; char * filename = NULL; HTUserProfile * up = HTRequest_userProfile(request); char * tmproot = HTUserProfile_tmp(up); if (HTLib_secure()) { HTRequest_addError(request, ERR_NON_FATAL, NO, HTERR_UNAUTHORIZED, NULL, 0, "HTSaveLocally"); return HTErrorStream(); } if (!tmproot) { HTTRACE(STREAM_TRACE, "Save File... turned off\n"); return HTErrorStream(); } /* Let's prompt the user for a file name for this file */ { HTAlertCallback *cbf = HTAlert_find(HT_A_PROMPT); HTParentAnchor *anchor = (HTParentAnchor *) HTRequest_anchor(request); /* ** If we found an alert handler for prompting the user then call it. ** If not then either we are in non-interactive mode or no handler ** has been registered. For now we then return a blackhole which may ** not be the best thing to do. */ if (cbf) { HTAlertPar * reply = HTAlert_newReply(); char * suffix = HTBind_getSuffix(anchor); char * deflt = get_filename(tmproot, HTAnchor_physical(anchor), suffix, YES); if ((*cbf)(request, HT_A_PROMPT, HT_MSG_FILENAME,deflt,NULL,reply)) filename = HTAlert_replyMessage(reply); HTAlert_deleteReply(reply); HT_FREE(suffix); HT_FREE(deflt); } if (filename) { if ((fp = fopen(filename, "wb")) == NULL) { HTRequest_addError(request, ERR_NON_FATAL, NO, HTERR_NO_FILE, filename, strlen(filename),"HTSaveLocally"); HT_FREE(filename); return HTErrorStream(); } } else if (cbf) { HTTRACE(STREAM_TRACE, "Save File... No file name - error stream\n"); return HTErrorStream(); } else { HTTRACE(STREAM_TRACE, "Save File... No file name - black hole\n"); return HTBlackHole(); } } /* Now we are ready for creating the file writer stream */ if (fp) { HTStream * me = HTFileSave_new(request, fp, NO); me->filename = filename; return me; } HT_FREE(filename); return HTErrorStream(); }
/* ** Redirection AFTER filter ** ------------------------ ** The redirection handler only handles redirections ** on the GET or HEAD method (or any other safe method) */ PUBLIC int HTRedirectFilter (HTRequest * request, HTResponse * response, void * param, int status) { HTMethod method = HTRequest_method(request); HTAnchor * new_anchor = HTResponse_redirection(response); /* Check for destination */ if (!new_anchor) { HTTRACE(PROT_TRACE, "Redirection. No destination\n"); return HT_OK; } /* ** Only do automatic redirect on GET and HEAD. Ask for all ** other methods. */ if (!HTMethod_isSafe(method)) { /* ** If we got a 303 See Other then change the method to GET. ** Otherwise ask the user whether we should continue. */ if (status == HT_SEE_OTHER) { HTTRACE(PROT_TRACE, "Redirection. Changing method from %s to GET\n" _ HTMethod_name(method)); HTRequest_setMethod(request, METHOD_GET); } else { HTAlertCallback * prompt = HTAlert_find(HT_A_CONFIRM); if (prompt) { if ((*prompt)(request, HT_A_CONFIRM, HT_MSG_REDIRECTION, NULL, NULL, NULL) != YES) return HT_OK; } } } /* Register the redirection as a link relationship */ { HTLinkType ltype = status==HT_PERM_REDIRECT ? HT_LR_PERM_REDIRECT : (status==HT_TEMP_REDIRECT || status==HT_FOUND) ? HT_LR_TEMP_REDIRECT : status==HT_SEE_OTHER ? HT_LR_SEE_OTHER : NULL; if (ltype) { HTLink_add((HTAnchor *) HTRequest_anchor(request), new_anchor, ltype, method); } } /* Delete any auth credendials as they get regenerated */ HTRequest_deleteCredentialsAll(request); /* ** Start new request with the redirect anchor found in the headers. ** Note that we reuse the same request object which means that we must ** keep this around until the redirected request has terminated. It also ** allows us in an easy way to keep track of the number of redirections ** so that we can detect endless loops. */ if (HTRequest_doRetry(request)) { HTLoadAnchor(new_anchor, request); } else { HTRequest_addError(request, ERR_FATAL, NO, HTERR_MAX_REDIRECT, NULL, 0, "HTRedirectFilter"); return HT_OK; /* Wanna fall through */ } /* ** By returning HT_ERROR we make sure that this is the last handler to be ** called. We do this as we don't want any other filter to delete the ** request object now when we have just started a new one ourselves */ return HT_ERROR; }
/* HTDir_new ** --------- ** Creates a structured stream object and sets up the initial HTML stuff ** Returns the dir object if OK, else NULL */ PUBLIC HTDir * HTDir_new (HTRequest * request, HTDirShow show, HTDirKey key) { HTDir *dir; char *title = NULL; if (!request) return NULL; /* Create object */ if ((dir = (HTDir *) HT_CALLOC(1, sizeof (HTDir))) == NULL || (dir->fnbuf = (char *) HT_MALLOC(MaxFileW+HT_DLEN_SPACE)) == NULL) HT_OUTOFMEM("HTDir_new"); dir->target = HTMLGenerator(request, NULL, WWW_HTML, HTRequest_outputFormat(request), HTRequest_outputStream(request)); HTRequest_setOutputConnected(request, YES); HTAnchor_setFormat(HTRequest_anchor(request), WWW_HTML); dir->request = request; dir->show = show; dir->key = key; if (key==HT_DK_NONE) dir->curfw = MaxFileW; else { dir->curfw = MinFileW; dir->array = HTArray_new(256); } /* We're all OK */ HTRequest_addError(request, ERR_INFO, NO, HTERR_OK, NULL, 0, "HTDir_new"); /* Find the length of the fields */ { int len = HT_DLEN_SPACE+1; if (show & HT_DS_SIZE) len += (HT_DLEN_SIZE+HT_DLEN_SPACE); if (show & HT_DS_DATE) len += (HT_DLEN_DATE+HT_DLEN_SPACE); if (show & HT_DS_DES) len += HT_DLEN_DES; if ((dir->lnbuf = (char *) HT_MALLOC(len)) == NULL) HT_OUTOFMEM("HTDir_new"); } /* Find the title and the base URL */ { char *addr = HTAnchor_address((HTAnchor *) HTRequest_anchor(request)); char *path = HTParse(addr, "", PARSE_PATH+PARSE_PUNCTUATION); char *ptr; if ((ptr = strchr(path, ';')) || (ptr = strchr(path, '?'))) *ptr = '\0'; StrAllocCopy(title, path); HTUnEscape(title); /* Title */ if((ptr=strrchr(path, '/')) && (ptr<path+strlen(path)-1 || ptr==path)){ StrAllocCopy(dir->base, ++ptr); StrAllocCat(dir->base, "/"); } HTTRACE(PROT_TRACE, "HTDir_new... base is `%s\'\n" _ dir->base ? dir->base : ""); HT_FREE(addr); HT_FREE(path); } /* Start the HTML stuff */ { HTStructured *target = dir->target; START(HTML_HTML); START(HTML_HEAD); START(HTML_TITLE); PUTS("Current index is "); PUTS(title); END(HTML_TITLE); END(HTML_HEAD); START(HTML_BODY); START(HTML_H1); PUTS("Index of "); PUTS(title); END(HTML_H1); } HT_FREE(title); return dir; }