/* HTNewsDir_new ** ---------- ** Creates a structured stream object and sets up the initial HTML stuff ** Returns the newsdir object if OK, else NULL */ PUBLIC HTNewsDir * HTNewsDir_new (HTRequest * request, const char * title, HTNewsDirKey key, BOOL cache) { HTNewsDir *dir; if (!request) return NULL; /* Create object */ if ((dir = (HTNewsDir *) HT_CALLOC(1, sizeof (HTNewsDir))) == NULL) HT_OUTOFMEM("HTNewsDir_new"); dir->target = HTMLGenerator(request, NULL, WWW_HTML, HTRequest_outputFormat(request), HTRequest_outputStream(request)); HTAnchor_setFormat(HTRequest_anchor(request), WWW_HTML); dir->request = request; dir->key = key; dir->lastLevel = -1; /* Added by MP. */ /* Get the newsgroup(s) name; added by MP. */ { char* url = HTAnchor_physical(HTRequest_anchor(request)); char* p = url+strlen(url); while (p > url && p[-1] != ':' && p[-1] != '/' && p[-1] != '\\') p--; StrAllocCopy (dir->name, p); } if (key != HT_NDK_NONE) { /* Thread is unsorted */ int total = HTNews_maxArticles(); dir->array = HTArray_new(total > 0 ? total : 128); } /* If we are asked to prepare a cache entry then create the cache array */ if (cache) { int total = HTNews_maxArticles(); dir->cache = HTArray_new(total > 0 ? total : 128); } /* Start the HTML stuff */ { HTStructured *target = dir->target; const char *msg = title ? title : "News Listing"; START(HTML_HTML); START(HTML_HEAD); START(HTML_TITLE); PUTS(msg); END(HTML_TITLE); END(HTML_HEAD); START(HTML_BODY); START(HTML_H1); PUTS(msg); END(HTML_H1); } return dir; }
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) */ }
/* 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; }
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; }
/* HTTPMakeRequest ** --------------- ** Makes a HTTP/1.0-1.1 request header. */ PRIVATE int HTTPMakeRequest (HTStream * me, HTRequest * request) { HTMethod method = HTRequest_method(request); HTRqHd request_mask = HTRequest_rqHd(request); HTParentAnchor * anchor = HTRequest_anchor(request); char * etag = HTAnchor_etag(anchor); char crlf[3]; char qstr[10]; *crlf = CR; *(crlf+1) = LF; *(crlf+2) = '\0'; /* Generate the HTTP/1.x RequestLine */ if (me->state == 0) { if (method != METHOD_INVALID) { PUTS(HTMethod_name(method)); PUTC(' '); } else PUTS("GET "); me->state++; } /* ** Generate the Request URI. If we are using full request URI then it's ** easy. Otherwise we must filter out the path part of the URI. ** In case it's a OPTIONS request then if there is no pathinfo then use ** a * instead. If we use a method different from GET or HEAD then use ** the content-location if available. */ if (me->state == 1) { char * abs_location = NULL; char * addr = HTAnchor_physical(anchor); char * location; /* JK: If the application specified a content-location (which is stored in the request in default put-name!), we use it instead of the URL that's being saved to. This is like having a user defined Content-Location */ location = HTRequest_defaultPutName (request); if (location) { if (HTURL_isAbsolute (location)) { char * relative; relative = HTRelative (location, location); abs_location = HTParse (relative + 2, addr, PARSE_ALL); HT_FREE (relative); } else abs_location = HTParse (location, addr, PARSE_ALL); addr = abs_location; } #if 0 /* ** We don't use the content-location any more as it is superseeded ** by etags and the combination of the two might do more harm than ** good (The etag is not guaranteed to be unique over multiple URIs) */ /* ** If we are using a method different from HEAD and GET then use ** the Content-Location if available, else the Request-URI. */ if (!HTMethod_isSafe(method)) { char * location = HTAnchor_location(anchor); if (location) { if (HTURL_isAbsolute(location)) addr = location; else { /* ** We have a content-location but it is relative and ** must expand it either to the content-base or to ** the Request-URI itself. */ char * base = HTAnchor_base(anchor); abs_location = HTParse(location, base, PARSE_ALL); addr = abs_location; } } } #endif /* ** If we are using a proxy or newer versions of HTTP then we can ** send the full URL. Otherwise we only send the path. */ if (HTRequest_fullURI(request)) StrAllocCopy(me->url, addr); else { me->url = HTParse(addr, "", PARSE_PATH | PARSE_PUNCTUATION); if (method == METHOD_OPTIONS) { /* ** We don't preserve the final slash or lack of same through ** out the code. This is mainly for optimization reasons ** but it gives a problem OPTIONS. We can either send a "*" ** or a "/" but not both. For now we send a "*". */ if (!strcmp(me->url, "/")) *me->url = '*'; } } HT_FREE(abs_location); me->state++; } /* ** Now send the URL that we have put together */ if (me->state == 2) { int status = HT_OK; if ((status = PUTS(me->url)) != HT_OK) return status; me->state++; #if 0 fprintf(stderr, "Requesting '%s'\n", me->url); #endif } PUTC(' '); /* ** Send out the version number. If we know it is a HTTP/1.0 server we ** are talking to then use HTTP/1.0, else use HTTP/1.1 as default version ** number */ if (me->version == HTTP_10) PUTS(HTTP_VERSION_10); else PUTS(HTTP_VERSION); PUTBLOCK(crlf, 2); /* Request Headers */ if (request_mask & HT_C_ACCEPT_TYPE) { HTFormat format = HTRequest_outputFormat(request); /* ** If caller has specified a specific output format then use this. ** Otherwise use all the registered converters to generate the ** accept header */ if (format == WWW_PRESENT) { int list; HTList *cur; BOOL first=YES; for (list=0; list<2; list++) { if ((!list && ((cur = HTFormat_conversion()) != NULL)) || (list && ((cur = HTRequest_conversion(request))!=NULL))) { HTPresentation * pres; while ((pres=(HTPresentation *) HTList_nextObject(cur))) { if (pres->rep_out==WWW_PRESENT && pres->quality<=1.0) { if (first) { PUTS("Accept: "); first=NO; } else PUTC(','); PUTS(HTAtom_name(pres->rep)); if (pres->quality < 1.0 && pres->quality >= 0.0) { sprintf(qstr, ";q=%1.1f", pres->quality); PUTS(qstr); } } } } } if (!first) PUTBLOCK(crlf, 2); } else { /* ** If we have an explicit output format then only send ** this one if not this is an internal libwww format ** of type www/<star> */ if (!HTMIMEMatch(WWW_INTERNAL, format)) { PUTS("Accept: "); PUTS(HTAtom_name(format)); PUTBLOCK(crlf, 2); } } } if (request_mask & HT_C_ACCEPT_CHAR) { int list; HTList *cur; BOOL first=YES; for (list=0; list<2; list++) { if ((!list && ((cur = HTFormat_charset()) != NULL)) || (list && ((cur = HTRequest_charset(request)) != NULL))) { HTAcceptNode *pres; while ((pres = (HTAcceptNode *) HTList_nextObject(cur))) { if (first) { PUTS("Accept-Charset: "); first=NO; } else PUTC(','); PUTS(HTAtom_name(pres->atom)); if (pres->quality < 1.0 && pres->quality >= 0.0) { sprintf(qstr, ";q=%1.1f", pres->quality); PUTS(qstr); } } } } if (!first) PUTBLOCK(crlf, 2); } if (request_mask & HT_C_ACCEPT_ENC) { int list; HTList *cur; BOOL first=YES; for (list=0; list<2; list++) { if ((!list && ((cur = HTFormat_contentCoding()) != NULL)) || (list && ((cur = HTRequest_encoding(request)) != NULL))) { HTCoding * pres; while ((pres = (HTCoding *) HTList_nextObject(cur))) { double quality = HTCoding_quality(pres); if (first) { PUTS("Accept-Encoding: "); first = NO; } else PUTC(','); PUTS(HTCoding_name(pres)); if (quality < 1.0 && quality >= 0.0) { sprintf(qstr, ";q=%1.1f", quality); PUTS(qstr); } } } } if (!first) PUTBLOCK(crlf, 2); } if (request_mask & HT_C_ACCEPT_TE) { int list; HTList *cur; BOOL first=YES; for (list=0; list<2; list++) { if ((!list && ((cur = HTFormat_transferCoding()) != NULL)) || (list && ((cur = HTRequest_transfer(request)) != NULL))) { HTCoding * pres; while ((pres = (HTCoding *) HTList_nextObject(cur))) { double quality = HTCoding_quality(pres); const char * coding = HTCoding_name(pres); if (first) { PUTS("TE: "); first = NO; } else PUTC(','); /* Special check for "chunked" which is translated to "trailers" */ if (!strcasecomp(coding, "chunked")) PUTS("trailers"); else PUTS(coding); if (quality < 1.0 && quality >= 0.0) { sprintf(qstr, ";q=%1.1f", quality); PUTS(qstr); } } } } if (!first) PUTBLOCK(crlf, 2); } if (request_mask & HT_C_ACCEPT_LAN) { int list; HTList *cur; BOOL first=YES; for (list=0; list<2; list++) { if ((!list && ((cur = HTFormat_language()) != NULL)) || (list && ((cur = HTRequest_language(request)) != NULL))) { HTAcceptNode *pres; while ((pres = (HTAcceptNode *) HTList_nextObject(cur))) { if (first) { PUTS("Accept-Language: "); first=NO; } else PUTC(','); PUTS(HTAtom_name(pres->atom)); if (pres->quality < 1.0 && pres->quality >= 0.0) { sprintf(qstr, ";q=%1.1f", pres->quality); PUTS(qstr); } } } } if (!first) PUTBLOCK(crlf, 2); } if (request_mask & HT_C_AUTH) { HTAssocList * cur = HTRequest_credentials(request); if (cur) { /* Access authentication */ HTAssoc * pres; while ((pres = (HTAssoc *) HTAssocList_nextObject(cur))) { PUTS(HTAssoc_name(pres)); PUTS(": "); PUTS(HTAssoc_value(pres)); PUTBLOCK(crlf, 2); } } } if (request_mask & HT_C_EXPECT) { HTAssocList * cur = HTRequest_expect(request); if (cur) { BOOL first=YES; HTAssoc * pres; while ((pres = (HTAssoc *) HTAssocList_nextObject(cur))) { char * value = HTAssoc_value(pres); if (first) { PUTS("Expect: "); first = NO; } else PUTC(','); /* Output the name */ PUTS(HTAssoc_name(pres)); /* Only output the value if not empty string */ if (*value) { PUTS("="); PUTS(value); } } PUTBLOCK(crlf, 2); } } if (request_mask & HT_C_FROM) { HTUserProfile * up = HTRequest_userProfile(request); const char * mailaddress = HTUserProfile_email(up); if (mailaddress) { PUTS("From: "); PUTS(mailaddress); PUTBLOCK(crlf, 2); } } if (request_mask & HT_C_HOST) { char *orig = HTAnchor_address((HTAnchor *) anchor); char *host = HTParse(orig, "", PARSE_HOST); char hostace[256]; #if 0 /* Keep the port number for HTTP/1.1 compliance */ char *ptr = strchr(host, ':'); /* Chop off port number */ if (ptr) *ptr = '\0'; #endif PUTS("Host: "); /****** still have to check UTF8toACE with port number */ if (!HTACEfromUTF8 (host, hostace, 255)) { PUTS(hostace); } else { PUTS(host); /* this may be dangerous, but helps server side debugging */ HTTRACE(PROT_TRACE, "HTTP........ Error: Cannot convert to ACE: `%s\'\n" _ host); } PUTBLOCK(crlf, 2); HT_FREE(orig); HT_FREE(host); } /* ** In the "If-*" series of headers, the ones related to etags have higher ** priority than the date relates ones. That is, if we have a etag then ** use that, otherwise use the date. First we check for range, match, and ** unmodified-since. */ if (request_mask & HT_C_IF_RANGE && etag) { PUTS("If-Range: \""); PUTS(etag); PUTC('"'); PUTBLOCK(crlf, 2); HTTRACE(PROT_TRACE, "HTTP........ If-Range using etag `%s\'\n" _ etag); } else if (request_mask & HT_C_IF_MATCH_ANY) { PUTS("If-Match: *"); PUTBLOCK(crlf, 2); HTTRACE(PROT_TRACE, "HTTP........ If-Match using `*\'\n"); } else if (request_mask & HT_C_IF_MATCH && etag) { PUTS("If-Match: \""); PUTS(etag); PUTC('"'); PUTBLOCK(crlf, 2); HTTRACE(PROT_TRACE, "HTTP........ If-Match using etag `%s\'\n" _ etag); } else if (request_mask & HT_C_IF_UNMOD_SINCE) { time_t lm = HTAnchor_lastModified(anchor); if (lm > 0) { PUTS("If-Unmodified-Since: "); PUTS(HTDateTimeStr(&lm, NO)); PUTBLOCK(crlf, 2); HTTRACE(PROT_TRACE, "HTTP........ If-Unmodified-Since `%s\'\n" _ HTDateTimeStr(&lm, NO)); } } /* ** If-None-Match and If-Modified-Since are equivalent except that the ** first uses etags and the second uses dates. Etags have precedence over ** dates. */ if (request_mask & HT_C_IF_NONE_MATCH_ANY) { PUTS("If-None-Match: *"); PUTBLOCK(crlf, 2); HTTRACE(PROT_TRACE, "HTTP........ If-None-Match using `*\'\n"); } else if (request_mask & HT_C_IF_NONE_MATCH && etag) { PUTS("If-None-Match: \""); PUTS(etag); PUTC('"'); PUTBLOCK(crlf, 2); HTTRACE(PROT_TRACE, "HTTP........ If-None-Match `%s\'\n" _ etag); } if (request_mask & HT_C_IMS) { time_t lm = HTAnchor_lastModified(anchor); if (lm > 0) { PUTS("If-Modified-Since: "); PUTS(HTDateTimeStr(&lm, NO)); PUTBLOCK(crlf, 2); HTTRACE(PROT_TRACE, "HTTP........ If-Modified-Since `%s\'\n" _ HTDateTimeStr(&lm, NO)); } } /* ** Max forwards is mainly for TRACE where we want to be able to stop the ** TRACE at a specific location un the message path. */ if (request_mask & HT_C_MAX_FORWARDS) { int hops = HTRequest_maxForwards(request); if (hops >= 0) { sprintf(qstr, "%d", hops); PUTS("Max-Forwards: "); PUTS(qstr); PUTBLOCK(crlf, 2); } } /* ** Range requests. For now, we only take the first entry registered for ** this request. This means that you can only send a single "unit" and ** then a set of range within this unit. This is in accordance with ** HTTP/1.1. Multiple units will go on multiple lines. */ if (request_mask & HT_C_RANGE) { HTAssocList * cur = HTRequest_range(request); if (cur) { /* Range requests */ HTAssoc * pres; while ((pres = (HTAssoc *) HTAssocList_nextObject(cur))) { PUTS("Range: "); PUTS(HTAssoc_name(pres)); /* Unit */ PUTS("="); PUTS(HTAssoc_value(pres)); /* Ranges within this unit */ PUTBLOCK(crlf, 2); } } } if (request_mask & HT_C_REFERER) { HTParentAnchor * parent_anchor = HTRequest_parent(request); if (parent_anchor) { char * act = HTAnchor_address((HTAnchor *) anchor); char * parent = HTAnchor_address((HTAnchor *) parent_anchor); #if 1 char * relative = HTRelative(parent, act); #else char * relative = HTParse(parent, act, PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION); #endif if (relative && *relative) { PUTS("Referer: "); PUTS(relative); PUTBLOCK(crlf, 2); } HT_FREE(act); HT_FREE(parent); HT_FREE(relative); } } if (request_mask & HT_C_USER_AGENT) { PUTS("User-Agent: "); PUTS(HTLib_appName()); PUTC('/'); PUTS(HTLib_appVersion()); PUTC(' '); PUTS(HTLib_name()); PUTC('/'); PUTS(HTLib_version()); PUTBLOCK(crlf, 2); } HTTRACE(PROT_TRACE, "HTTP........ Generating HTTP/1.x Request Headers\n"); return HT_OK; }