/* ** ACCESS MANAGER ** ** (c) COPYRIGHT MIT 1995. ** Please first read the full copyright statement in the file COPYRIGH. ** @(#) $Id: HTAccess.c,v 1.159 2000/09/01 12:58:40 kahan Exp $ ** ** Authors ** TBL Tim Berners-Lee timbl@w3.org ** JFG Jean-Francois Groff jfg@dxcern.cern.ch ** DD Denis DeLaRoca (310) 825-4580 ** HFN Henrik Frystyk, frystyk@w3.org ** JK Jose Kahan, kahan@w3.org ** History ** 8 Jun 92 Telnet hopping prohibited as telnet is not secure TBL ** 26 Jun 92 When over DECnet, suppressed FTP, Gopher and News. JFG ** 6 Oct 92 Moved HTClientHost and HTlogfile into here. TBL ** 17 Dec 92 Tn3270 added, bug fix. DD ** 4 Feb 93 Access registration, Search escapes bad chars TBL ** PARAMETERS TO HTSEARCH AND HTLOADRELATIVE CHANGED ** 28 May 93 WAIS gateway explicit if no WAIS library linked in. ** Dec 93 Bug change around, more reentrant, etc ** 09 May 94 logfile renamed to HTlogfile to avoid clash with WAIS ** 8 Jul 94 Insulate HT_FREE(); ** Sep 95 Rewritten, HFN ** 21 Jun 00 Added a Cache-Control: no-cache when doing PUT, as some ** proxies do cache PUT requests, JK */ /* Library include files */ #include "WWWUtil.h" #include "WWWCore.h" #include "WWWStream.h" #include "HTProxy.h" #include "HTRules.h" #include "HTReqMan.h" #include "HTAccess.h" /* Implemented here */ #define PUTBLOCK(b, l) (*target->isa->put_block)(target, b, l) struct _HTStream { HTStreamClass * isa; }; typedef enum _HTPutState { HT_LOAD_SOURCE = 0, HT_SAVE_DEST, HT_ABORT_SAVE } HTPutState; typedef struct _HTPutContext { HTParentAnchor * source; HTAnchor * destination; HTChunk * document; HTFormat format; HTStream * target; /* Any existing output stream */ void * placeholder; /* Any existing doc in anchor */ HTPutState state; } HTPutContext; /* --------------------------------------------------------------------------*/ /* THE GET METHOD */ /* --------------------------------------------------------------------------*/ /* Request a document ** ----------------- ** Private version that requests a document from the request manager ** Returns YES if request accepted, else NO */ PRIVATE BOOL launch_request (HTRequest * request, BOOL recursive) { #ifdef HTDEBUG if (PROT_TRACE) { HTParentAnchor *anchor = HTRequest_anchor(request); char * full_address = HTAnchor_address((HTAnchor *) anchor); HTTRACE(PROT_TRACE, "HTAccess.... Accessing document %s\n" _ full_address); HT_FREE(full_address); } #endif /* HTDEBUG */ return HTLoad(request, recursive); } /* Request a document from absolute name ** ------------------------------------- ** Request a document referencd by an absolute URL. ** Returns YES if request accepted, else NO */ PUBLIC BOOL HTLoadAbsolute (const char * url, HTRequest * request) { if (url && request) { HTAnchor * anchor = HTAnchor_findAddress(url); HTRequest_setAnchor(request, anchor); return launch_request(request, NO); } return NO; } /* Request a document from relative name ** ------------------------------------- ** Request a document referenced by a relative URL. The relative URL is ** made absolute by resolving it relative to the address of the 'base' ** anchor. ** Returns YES if request accepted, else NO */ PUBLIC BOOL HTLoadRelative (const char * relative, HTParentAnchor * base, HTRequest * request) { BOOL status = NO; if (relative && base && request) { char * full_url = NULL; char * base_url = HTAnchor_address((HTAnchor *) base); full_url = HTParse(relative, base_url, PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION); status = HTLoadAbsolute(full_url, request); HT_FREE(full_url); HT_FREE(base_url); } return status; } /* Request a document from absolute name to stream ** ----------------------------------------------- ** Request a document referencd by an absolute URL and sending the data ** down a stream. ** Returns YES if request accepted, else NO */ PUBLIC BOOL HTLoadToStream (const char * url, HTStream * output, HTRequest * request) { if (url && output && request) { HTRequest_setOutputStream(request, output); return HTLoadAbsolute(url, request); } return NO; } /* Load a document and save it ASIS in a local file ** ------------------------------------------------ ** Returns YES if request accepted, else NO */ PUBLIC BOOL HTLoadToFile (const char * url, HTRequest * request, const char * filename) { if (url && filename && request) { FILE * fp = NULL; /* Check if file exists. If so then ask user if we can replace it */ if (access(filename, F_OK) != -1) { HTAlertCallback * prompt = HTAlert_find(HT_A_CONFIRM); if (prompt) { if ((*prompt)(request, HT_A_CONFIRM, HT_MSG_FILE_REPLACE, NULL, NULL, NULL) != YES) return NO; } } /* If replace then open the file */ if ((fp = fopen(filename, "wb")) == NULL) { HTRequest_addError(request, ERR_FATAL, NO, HTERR_NO_FILE, (char *) filename, strlen(filename), "HTLoadToFile"); return NO; } /* Set the output stream and start the request */ HTRequest_setOutputFormat(request, WWW_SOURCE); HTRequest_setOutputStream(request, HTFWriter_new(request, fp, NO)); if (HTLoadAbsolute(url, request) == NO) { fclose(fp); return NO; } else return YES; } return NO; } /* ** Load a URL to a mem buffer ** -------------------------- ** Load a request and store the result in a memory buffer. ** Returns chunk if OK - else NULL */ PUBLIC HTChunk * HTLoadToChunk (const char * url, HTRequest * request) { if (url && request) { HTChunk * chunk = NULL; HTStream * target = HTStreamToChunk(request, &chunk, 0); HTAnchor * anchor = HTAnchor_findAddress(url); HTRequest_setAnchor(request, anchor); HTRequest_setOutputStream(request, target); if (launch_request(request, NO) == YES) return chunk; else { HTChunk_delete(chunk); return NULL; } } return NULL; } /* Request an anchor ** ----------------- ** Request the document referenced by the anchor ** Returns YES if request accepted, else NO */ PUBLIC BOOL HTLoadAnchor (HTAnchor * anchor, HTRequest * request) { if (anchor && request) { HTRequest_setAnchor(request, anchor); return launch_request(request, NO); } return NO; } /* Request an anchor ** ----------------- ** Same as HTLoadAnchor but any information in the Error Stack in the ** request object is kept, so that any error messages in one ** This function is almost identical to HTLoadAnchor, but it doesn't ** clear the error stack so that the information in there is kept. ** Returns YES if request accepted, else NO */ PUBLIC BOOL HTLoadAnchorRecursive (HTAnchor * anchor, HTRequest * request) { if (anchor && request) { HTRequest_setAnchor(request, anchor); return launch_request(request, YES); } return NO; } /* ** Load a URL to a mem buffer ** -------------------------- ** Load a request and store the result in a memory buffer. ** Returns chunk if OK - else NULL */ PUBLIC HTChunk * HTLoadAnchorToChunk (HTAnchor * anchor, HTRequest * request) { HTChunk * chunk = NULL; if (anchor && request) { HTStream * target = HTStreamToChunk(request, &chunk, 0); HTRequest_setAnchor(request, anchor); HTRequest_setOutputStream(request, target); if (launch_request(request, NO) == YES) return chunk; else { HTChunk_delete(chunk); return NULL; } } return NULL; } /* ** Load a Rule File ** ---------------- ** Load a rule find with the URL specified and add the set of rules to ** the existing set. */ PUBLIC BOOL HTLoadRules (const char * url) { BOOL status = NO; if (url) { HTList * list = HTList_new(); HTRequest * request = HTRequest_new(); HTRequest_setPreemptive(request, YES); /* ** We do only accept a new rules files when we are in interactive ** mode and can ask the user for it. If HT_AUTOMATIC_RULES is ** defined then we accept new rules files without explicit ack ** from the user */ #ifdef HT_AUTOMATIC_RULES HTAlert_setInteractive(NO); #endif /* ** Add the rules parsing stream for this particular request only. ** That is, we only accept a rules file when we have explicitly ** asked for it using this function. */ HTConversion_add(list, "application/x-www-rules", "*/*", HTRules, 1.0, 0.0, 0.0); HTRequest_setConversion(request, list, YES); status = HTLoadAbsolute(url, request); HTConversion_deleteAll(list); HTRequest_delete(request); } return status; } /* ** Load a Rule File without asking the user ** ---------------------------------------- ** Load a rule find with the URL specified and add the set of rules to ** the existing set. We don't ask the user as she would have to call this ** method explicitly anyway. */ PUBLIC BOOL HTLoadRulesAutomatically (const char * url) { BOOL status = NO; if (url) { HTList * list = HTList_new(); HTRequest * request = HTRequest_new(); /* ** Stop all other loads and concentrate on this one */ HTRequest_setPreemptive(request, YES); /* ** Add the rules parsing stream for this particular request only. */ HTConversion_add(list, "application/x-www-rules", "*/*", HTRules_parseAutomatically, 1.0, 0.0, 0.0); HTRequest_setConversion(request, list, YES); status = HTLoadAbsolute(url, request); HTConversion_deleteAll(list); HTRequest_delete(request); } return status; } /* --------------------------------------------------------------------------*/ /* GET WITH KEYWORDS (SEARCH) */ /* --------------------------------------------------------------------------*/ /* ** This function creates a URL with a searh part as defined by RFC 1866 ** Both the baseurl and the keywords must be escaped. ** ** 1. The form field names and values are escaped: space ** characters are replaced by `+', and then reserved characters ** are escaped as per [URL]; that is, non-alphanumeric ** characters are replaced by `%HH', a percent sign and two ** hexadecimal digits representing the ASCII code of the ** character. Line breaks, as in multi-line text field values, ** are represented as CR LF pairs, i.e. `%0D%0A'. ** ** 2. The fields are listed in the order they appear in the ** document with the name separated from the value by `=' and ** the pairs separated from each other by `&'. Fields with null ** values may be omitted. In particular, unselected radio ** buttons and checkboxes should not appear in the encoded ** data, but hidden fields with VALUE attributes present ** should. ** ** NOTE - The URI from a query form submission can be ** used in a normal anchor style hyperlink. ** Unfortunately, the use of the `&' character to ** separate form fields interacts with its use in SGML ** attribute values as an entity reference delimiter. ** For example, the URI `http://host/?x=1&y=2' must be ** written `'. ** ** HTTP server implementors, and in particular, CGI ** implementors are encouraged to support the use of ** `;' in place of `&' to save users the trouble of ** escaping `&' characters this way. */ PRIVATE char * query_url_encode (const char * baseurl, HTChunk * keywords) { char * fullurl = NULL; if (baseurl && keywords && HTChunk_size(keywords)) { int len = strlen(baseurl); fullurl = (char *) HT_MALLOC(len + HTChunk_size(keywords) + 2); sprintf(fullurl, "%s?%s", baseurl, HTChunk_data(keywords)); { char * ptr = fullurl+len; while (*ptr) { if (*ptr == ' ') *ptr = '+'; ptr++; } } } return fullurl; } PRIVATE char * form_url_encode (const char * baseurl, HTAssocList * formdata) { if (formdata) { BOOL first = YES; int cnt = HTList_count((HTList *) formdata); HTChunk * fullurl = HTChunk_new(128); HTAssoc * pres; if (baseurl) { HTChunk_puts(fullurl, baseurl); HTChunk_putc(fullurl, '?'); } while (cnt > 0) { pres = (HTAssoc *) HTList_objectAt((HTList *) formdata, --cnt); if (first) first = NO; else HTChunk_putc(fullurl, '&'); /* Could use ';' instead */ HTChunk_puts(fullurl, HTAssoc_name(pres)); HTChunk_putc(fullurl, '='); HTChunk_puts(fullurl, HTAssoc_value(pres)); } return HTChunk_toCString(fullurl); } return NULL; } /* Search a document from absolute name ** ------------------------------------ ** Request a document referencd by an absolute URL appended with the ** keywords given. The URL can NOT contain any fragment identifier! ** The list of keywords must be a space-separated list and spaces will ** be converted to '+' before the request is issued. ** Returns YES if request accepted, else NO */ PUBLIC BOOL HTSearchAbsolute (HTChunk * keywords, const char * base, HTRequest * request) { if (keywords && base && request) { char * full = query_url_encode(base, keywords); if (full) { HTAnchor * anchor = HTAnchor_findAddress(full); HTRequest_setAnchor(request, anchor); HT_FREE(full); return launch_request(request, NO); } } return NO; } /* Search a document from relative name ** ------------------------------------- ** Request a document referenced by a relative URL. The relative URL is ** made absolute by resolving it relative to the address of the 'base' ** anchor. ** Returns YES if request accepted, else NO */ PUBLIC BOOL HTSearchRelative (HTChunk * keywords, const char * relative, HTParentAnchor * base, HTRequest * request) { BOOL status = NO; if (keywords && relative && base && request) { char * full_url = NULL; char * base_url = HTAnchor_address((HTAnchor *) base); full_url = HTParse(relative, base_url, PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION); status = HTSearchAbsolute(keywords, full_url, request); HT_FREE(full_url); HT_FREE(base_url); } return status; } /* ** Search a string ** --------------- ** This is the same as HTSearchAbsolute but instead of using a chunk ** you can pass a string. */ PUBLIC BOOL HTSearchString (const char * keywords, HTAnchor * anchor, HTRequest * request) { BOOL status = NO; if (keywords && anchor && request) { char * base_url = HTAnchor_address((HTAnchor *) anchor); HTChunk * chunk = HTChunk_new(strlen(keywords)+2); HTChunk_puts(chunk, keywords); status = HTSearchAbsolute(chunk, base_url, request); HT_FREE(base_url); HTChunk_delete(chunk); } return status; } /* Search an Anchor ** ---------------- ** Performs a keyword search on word given by the user. Adds the keyword ** to the end of the current address and attempts to open the new address. ** The list of keywords must be a space-separated list and spaces will ** be converted to '+' before the request is issued. ** Search can also be performed by HTLoadAbsolute() etc. ** Returns YES if request accepted, else NO */ PUBLIC BOOL HTSearchAnchor (HTChunk * keywords, HTAnchor * anchor, HTRequest * request) { BOOL status = NO; if (keywords && anchor && request) { char * base_url = HTAnchor_address(anchor); status = HTSearchAbsolute(keywords, base_url, request); HT_FREE(base_url); } return status; } /* --------------------------------------------------------------------------*/ /* HANDLING FORMS USING GET AND POST */ /* --------------------------------------------------------------------------*/ /* Send a Form request using GET from absolute name ** ------------------------------------------------ ** Request a document referencd by an absolute URL appended with the ** formdata given. The URL can NOT contain any fragment identifier! ** The list of form data must be given as an association list where ** the name is the field name and the value is the value of the field. ** Returns YES if request accepted, else NO */ PUBLIC BOOL HTGetFormAbsolute (HTAssocList * formdata, const char * base, HTRequest * request) { if (formdata && base && request) { char * full = form_url_encode(base, formdata); if (full) { HTAnchor * anchor = HTAnchor_findAddress(full); HTRequest_setAnchor(request, anchor); HT_FREE(full); return launch_request(request, NO); } } return NO; } /* Send a Form request using GET from relative name ** ------------------------------------------------ ** Request a document referencd by a relative URL appended with the ** formdata given. The URL can NOT contain any fragment identifier! ** The list of form data must be given as an association list where ** the name is the field name and the value is the value of the field. ** Returns YES if request accepted, else NO */ PUBLIC BOOL HTGetFormRelative (HTAssocList * formdata, const char * relative, HTParentAnchor * base, HTRequest * request) { BOOL status = NO; if (formdata && relative && base && request) { char * full_url = NULL; char * base_url = HTAnchor_address((HTAnchor *) base); full_url=HTParse(relative, base_url, PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION); status = HTGetFormAbsolute(formdata, full_url, request); HT_FREE(full_url); HT_FREE(base_url); } return status; } /* Send a Form request using GET from an anchor ** -------------------------------------------- ** Request a document referencd by an anchor object appended with the ** formdata given. The URL can NOT contain any fragment identifier! ** The list of form data must be given as an association list where ** the name is the field name and the value is the value of the field. ** Returns YES if request accepted, else NO */ PUBLIC BOOL HTGetFormAnchor (HTAssocList * formdata, HTAnchor * anchor, HTRequest * request) { BOOL status = NO; if (formdata && anchor && request) { char * base_url = HTAnchor_address((HTAnchor *) anchor); status = HTGetFormAbsolute(formdata, base_url, request); HT_FREE(base_url); } return status; } PRIVATE int HTEntity_callback (HTRequest * request, HTStream * target) { HTParentAnchor * entity = HTRequest_entityAnchor(request); HTTRACE(APP_TRACE, "Posting Data from callback function\n"); if (!request || !entity || !target) return HT_ERROR; { BOOL chunking = NO; int status; char * document = (char *) HTAnchor_document(entity); int len = HTAnchor_length(entity); if (!document) { HTTRACE(PROT_TRACE, "Posting Data No document\n"); return HT_ERROR; } /* ** If the length is unknown (-1) then see if the document is a text ** type and in that case take the strlen. If not then we don't know ** how much data we can write and must stop */ if (len < 0) { HTFormat actual = HTAnchor_format(entity); HTFormat tmplate = HTAtom_for("text/*"); if (HTMIMEMatch(tmplate, actual)) { len = strlen(document); /* Naive! */ chunking = YES; } else { HTTRACE(PROT_TRACE, "Posting Data Must know the length of document %p\n" _ document); return HT_ERROR; } } /* Send the data down the pipe */ status = (*target->isa->put_block)(target, document, len); if (status == HT_WOULD_BLOCK) { HTTRACE(PROT_TRACE, "Posting Data Target WOULD BLOCK\n"); return HT_WOULD_BLOCK; } else if (status == HT_PAUSE) { HTTRACE(PROT_TRACE, "Posting Data Target PAUSED\n"); return HT_PAUSE; } else if (chunking && status == HT_OK) { HTTRACE(PROT_TRACE, "Posting Data Target is SAVED using chunked\n"); return (*target->isa->put_block)(target, "", 0); } else if (status == HT_LOADED || status == HT_OK) { HTTRACE(PROT_TRACE, "Posting Data Target is SAVED\n"); (*target->isa->flush)(target); return HT_LOADED; } else if (status > 0) { /* Stream specific return code */ HTTRACE(PROT_TRACE, "Posting Data. Target returns %d\n" _ status); return status; } else { /* we have a real error */ HTTRACE(PROT_TRACE, "Posting Data Target ERROR %d\n" _ status); return status; } } } /* Send a Form request using POST from absolute name ** ------------------------------------------------- ** Request a document referencd by an absolute URL appended with the ** formdata given. The URL can NOT contain any fragment identifier! ** The list of form data must be given as an association list where ** the name is the field name and the value is the value of the field. */ PUBLIC HTParentAnchor * HTPostFormAbsolute (HTAssocList * formdata, const char * base, HTRequest * request) { if (formdata && base && request) { HTAnchor * anchor = HTAnchor_findAddress(base); return HTPostFormAnchor(formdata, anchor, request); } return NULL; } /* Send a Form request using POST from relative name ** ------------------------------------------------- ** Request a document referencd by a relative URL appended with the ** formdata given. The URL can NOT contain any fragment identifier! ** The list of form data must be given as an association list where ** the name is the field name and the value is the value of the field. */ PUBLIC HTParentAnchor * HTPostFormRelative (HTAssocList * formdata, const char * relative, HTParentAnchor * base, HTRequest * request) { HTParentAnchor * postanchor = NULL; if (formdata && relative && base && request) { char * full_url = NULL; char * base_url = HTAnchor_address((HTAnchor *) base); full_url=HTParse(relative, base_url, PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION); postanchor = HTPostFormAbsolute(formdata, full_url, request); HT_FREE(full_url); HT_FREE(base_url); } return postanchor; } /* Send a Form request using POST from an anchor ** --------------------------------------------- ** Request a document referencd by an anchor object appended with the ** formdata given. The URL can NOT contain any fragment identifier! ** The list of form data must be given as an association list where ** the name is the field name and the value is the value of the field. */ PUBLIC HTParentAnchor * HTPostFormAnchor (HTAssocList * formdata, HTAnchor * anchor, HTRequest * request) { HTParentAnchor * postanchor = NULL; if (formdata && anchor && request) { HTUserProfile * up = HTRequest_userProfile(request); char * tmpfile = HTGetTmpFileName(HTUserProfile_tmp(up)); char * tmpurl = HTParse(tmpfile, "file:", PARSE_ALL); char * form_encoded = form_url_encode(NULL, formdata); if (form_encoded) { /* ** Now create a new anchor for the post data and set up ** the rest of the metainformation we know about this anchor. The ** tmp anchor may actually already exist from previous postings. */ postanchor = (HTParentAnchor *) HTAnchor_findAddress(tmpurl); HTAnchor_clearHeader(postanchor); HTAnchor_setDocument(postanchor, form_encoded); HTAnchor_setLength(postanchor, strlen(form_encoded)); HTAnchor_setFormat(postanchor, WWW_FORM); /* ** Bind the temporary anchor to the anchor that will contain the ** response */ HTLink_removeAll((HTAnchor *) postanchor); HTLink_add((HTAnchor *) postanchor, (HTAnchor *) anchor, NULL, METHOD_POST); /* Set up the request object */ HTRequest_addGnHd(request, HT_G_DATE); /* Send date header */ HTRequest_setAnchor(request, anchor); HTRequest_setEntityAnchor(request, postanchor); HTRequest_setMethod(request, METHOD_POST); /* Add the post form callback function to provide the form data */ HTRequest_setPostCallback(request, HTEntity_callback); /* Now start the load normally */ launch_request(request, NO); } HT_FREE(tmpfile); HT_FREE(tmpurl); } return postanchor; } /* ** POST a URL and save the response in a mem buffer ** ------------------------------------------------ ** Returns chunk if OK - else NULL */ PUBLIC HTChunk * HTPostFormAnchorToChunk (HTAssocList * formdata, HTAnchor * anchor, HTRequest * request) { if (formdata && anchor && request) { HTChunk * chunk = NULL; HTStream * target = HTStreamToChunk(request, &chunk, 0); HTRequest_setOutputStream(request, target); if (HTPostFormAnchor(formdata, anchor, request) != NULL) return chunk; else { HTChunk_delete(chunk); return NULL; } } return NULL; } /* --------------------------------------------------------------------------*/ /* PUT A DOCUMENT */ /* --------------------------------------------------------------------------*/ /* ** If we use our persistent cache then we can protect ** against the lost update problem by saving the etag ** or last modified date in the cache and use it on all ** our PUT operations. */ PRIVATE BOOL set_preconditions (HTRequest * request) { if (request) { HTPreconditions precons = HTRequest_preconditions(request); HTRqHd if_headers = HTRequest_rqHd (request); HTRqHd all_if_headers = (HT_C_IF_MATCH | HT_C_IF_MATCH_ANY | HT_C_IF_NONE_MATCH | HT_C_IF_NONE_MATCH_ANY | HT_C_IMS | HT_C_IF_UNMOD_SINCE); switch (precons) { case HT_NO_MATCH: if_headers &= ~(all_if_headers); break; case HT_MATCH_THIS: if_headers &= ~(all_if_headers); if_headers |= (HT_C_IF_MATCH | HT_C_IF_UNMOD_SINCE); break; case HT_MATCH_ANY: if_headers &= ~(all_if_headers); if_headers |= (HT_C_IF_MATCH_ANY); break; case HT_DONT_MATCH_THIS: if_headers &= ~(all_if_headers); if_headers |= (HT_C_IF_NONE_MATCH | HT_C_IMS); break; case HT_DONT_MATCH_ANY: if_headers &= ~(all_if_headers); if_headers |= (HT_C_IF_NONE_MATCH_ANY); break; default: HTTRACE(APP_TRACE, "Precondition %d not understood\n" _ precons); } /* Set the if-* bit flag */ HTRequest_setRqHd(request, if_headers); return YES; } return NO; } PRIVATE BOOL setup_anchors (HTRequest * request, HTParentAnchor * source, HTParentAnchor * dest, HTMethod method) { if (!(method & (METHOD_PUT | METHOD_POST))) { HTTRACE(APP_TRACE, "Posting..... Bad method\n"); return NO; } /* ** Check whether we know if it is possible to PUT to this destination. ** We both check the local set of allowed methods in the anchor and any ** site information that we may have about the location of the origin ** server. */ { char * addr = HTAnchor_address((HTAnchor *) source); char * hostname = HTParse(addr, "", PARSE_HOST); #if 0 HTHost * host = HTHost_find(hostname); HTMethod public_methods = HTHost_publicMethods(host); HTMethod private_methods = HTAnchor_allow(dest); #endif HT_FREE(hostname); HT_FREE(addr); #if 0 /* ** Again, this may be too cautios for normal operations */ if (!(method & (private_methods | public_methods))) { HTAlertCallback * prompt = HTAlert_find(HT_A_CONFIRM); if (prompt) { if ((*prompt)(request, HT_A_CONFIRM, HT_MSG_METHOD, NULL, NULL, NULL) != YES) return NO; } } #endif } /* ** Bind the source anchor to the dest anchor that will contain the ** response. If link already exists then ask is we should do it again. ** If so then remove the old link and replace it with a new one. */ { HTLink *link = HTLink_find((HTAnchor *) source, (HTAnchor *) dest); if (link && HTLink_method(link) == METHOD_PUT) { HTAlertCallback * prompt = HTAlert_find(HT_A_CONFIRM); if (prompt) { if ((*prompt)(request, HT_A_CONFIRM, HT_MSG_REDO, NULL, NULL, NULL) != YES) return NO; } HTLink_remove((HTAnchor *) source, (HTAnchor *) dest); } HTLink_add((HTAnchor*) source, (HTAnchor*) dest, NULL, METHOD_PUT); } return YES; } /* Send an Anchor using PUT from absolute name ** ------------------------------------------- ** Upload a document referenced by an absolute URL appended. ** The URL can NOT contain any fragment identifier! ** The list of form data must be given as an association list where ** the name is the field name and the value is the value of the field. */ PUBLIC BOOL HTPutAbsolute (HTParentAnchor * source, const char * destination, HTRequest * request) { if (source && destination && request) { HTAnchor * dest = HTAnchor_findAddress(destination); return HTPutAnchor(source, dest, request); } return NO; } /* Send an Anchor using PUT from relative name ** ------------------------------------------- ** Upload a document referenced by a relative URL appended. ** The URL can NOT contain any fragment identifier! ** The list of form data must be given as an association list where ** the name is the field name and the value is the value of the field. */ PUBLIC BOOL HTPutRelative (HTParentAnchor * source, const char * relative, HTParentAnchor * destination_base, HTRequest * request) { if (source && relative && destination_base && request) { BOOL status; char * full_url = NULL; char * base_url = HTAnchor_address((HTAnchor *) destination_base); full_url=HTParse(relative, base_url, PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION); status = HTPutAbsolute(source, full_url, request); HT_FREE(full_url); HT_FREE(base_url); return status; } return NO; } /* Send an Anchor using PUT from an anchor ** --------------------------------------- ** Upload a document referenced by an anchor object appended ** The URL can NOT contain any fragment identifier! ** The list of form data must be given as an association list where ** the name is the field name and the value is the value of the field. */ PUBLIC BOOL HTPutAnchor (HTParentAnchor * source, HTAnchor * destination, HTRequest * request) { HTParentAnchor * dest = HTAnchor_parent(destination); if (source && dest && request) { if (setup_anchors(request, source, dest, METHOD_PUT) == YES) { /* Set up the request object */ HTRequest_addGnHd(request, HT_G_DATE); HTRequest_setEntityAnchor(request, source); HTRequest_setMethod(request, METHOD_PUT); HTRequest_setAnchor(request, destination); /* Setup preconditions */ set_preconditions(request); /* Add the entity callback function to provide the form data */ HTRequest_setPostCallback(request, HTEntity_callback); /* Now start the load normally */ return launch_request(request, NO); } } return NO; } /* Send an Anchor using POST from absolute name ** ------------------------------------------- ** Upload a document referenced by an absolute URL appended. ** The URL can NOT contain any fragment identifier! ** The list of form data must be given as an association list where ** the name is the field name and the value is the value of the field. */ PUBLIC BOOL HTPostAbsolute (HTParentAnchor * source, const char * destination, HTRequest * request) { if (source && destination && request) { HTAnchor * dest = HTAnchor_findAddress(destination); return HTPostAnchor(source, dest, request); } return NO; } /* Send an Anchor using POST from relative name ** ------------------------------------------- ** Upload a document referenced by a relative URL appended. ** The URL can NOT contain any fragment identifier! ** The list of form data must be given as an association list where ** the name is the field name and the value is the value of the field. */ PUBLIC BOOL HTPostRelative (HTParentAnchor * source, const char * relative, HTParentAnchor * destination_base, HTRequest * request) { if (source && relative && destination_base && request) { BOOL status; char * full_url = NULL; char * base_url = HTAnchor_address((HTAnchor *) destination_base); full_url=HTParse(relative, base_url, PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION); status = HTPostAbsolute(source, full_url, request); HT_FREE(full_url); HT_FREE(base_url); return status; } return NO; } /* Send an Anchor using POST from an anchor ** --------------------------------------- ** Upload a document referenced by an anchor object appended ** The URL can NOT contain any fragment identifier! ** The list of form data must be given as an association list where ** the name is the field name and the value is the value of the field. */ PUBLIC BOOL HTPostAnchor (HTParentAnchor * source, HTAnchor * destination, HTRequest * request) { HTParentAnchor * dest = HTAnchor_parent(destination); if (source && dest && request) { if (setup_anchors(request, source, dest, METHOD_POST) == YES) { /* Set up the request object */ HTRequest_addGnHd(request, HT_G_DATE); HTRequest_setEntityAnchor(request, source); HTRequest_setMethod(request, METHOD_POST); HTRequest_setAnchor(request, destination); /* Add the entity callback function to provide the form data */ HTRequest_setPostCallback(request, HTEntity_callback); /* Now start the load normally */ return launch_request(request, NO); } } return NO; } /* Send an Anchor using PUT from absolute name ** ------------------------------------------- ** Upload a document referenced by an absolute URL appended. ** The URL can NOT contain any fragment identifier! ** The list of form data must be given as an association list where ** the name is the field name and the value is the value of the field. */ PUBLIC BOOL HTPutStructuredAbsolute (HTParentAnchor * source, const char * destination, HTRequest * request, HTPostCallback * input) { if (source && destination && request && input) { HTAnchor * dest = HTAnchor_findAddress(destination); return HTPutStructuredAnchor(source, dest, request, input); } return NO; } /* Send an Anchor using PUT from relative name ** ------------------------------------------- ** Upload a document referenced by a relative URL appended. ** The URL can NOT contain any fragment identifier! ** The list of form data must be given as an association list where ** the name is the field name and the value is the value of the field. */ PUBLIC BOOL HTPutStructuredRelative (HTParentAnchor * source, const char * relative, HTParentAnchor * destination_base, HTRequest * request, HTPostCallback * input) { if (source && relative && destination_base && request && input) { BOOL status; char * full_url = NULL; char * base_url = HTAnchor_address((HTAnchor *) destination_base); full_url=HTParse(relative, base_url, PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION); status = HTPutStructuredAbsolute(source, full_url, request, input); HT_FREE(full_url); HT_FREE(base_url); return status; } return NO; } /* Send an Anchor using PUT from an anchor ** --------------------------------------- ** Upload a document referenced by an anchor object appended ** The URL can NOT contain any fragment identifier! ** The list of form data must be given as an association list where ** the name is the field name and the value is the value of the field. */ PUBLIC BOOL HTPutStructuredAnchor (HTParentAnchor * source, HTAnchor * destination, HTRequest * request, HTPostCallback * input) { HTParentAnchor * dest = HTAnchor_parent(destination); if (source && dest && request) { if (setup_anchors(request, source, dest, METHOD_PUT) == YES) { /* Set up the request object */ HTRequest_addGnHd(request, HT_G_DATE); HTRequest_setEntityAnchor(request, source); HTRequest_setMethod(request, METHOD_PUT); HTRequest_setAnchor(request, destination); /* Setup preconditions */ set_preconditions(request); /* Add the entity callback function to provide the form data */ HTRequest_setPostCallback(request, input); /* Now start the load normally */ return launch_request(request, NO); } } return NO; } /* ** After filter for handling PUT of document. */ PRIVATE int HTSaveFilter (HTRequest * request, HTResponse * response, void * param, int status) { HTPutContext * me = (HTPutContext *) param; HTTRACE(APP_TRACE, "Save Filter. Using context %p with state %c\n" _ me _ me->state+0x30); /* ** Just ignore authentication in the hope that some other filter will ** handle this. */ if (status == HT_NO_ACCESS || status == HT_NO_PROXY_ACCESS || status == HT_REAUTH || status == HT_PROXY_REAUTH) { HTTRACE(APP_TRACE, "Save Filter. Waiting for authentication\n"); return HT_OK; } /* ** If either the source or the destination has moved then ask the user ** what to do. If there is no user then stop */ if (status == HT_TEMP_REDIRECT || status == HT_PERM_REDIRECT || status == HT_FOUND || status == HT_SEE_OTHER) { HTAlertCallback * prompt = HTAlert_find(HT_A_CONFIRM); HTAnchor * redirection = HTResponse_redirection(response); if (prompt && redirection) { if (me->state == HT_LOAD_SOURCE) { if ((*prompt)(request, HT_A_CONFIRM, HT_MSG_SOURCE_MOVED, NULL, NULL, NULL) == YES) { me->source = HTAnchor_parent(redirection); } else { /* ** Make sure that the operation stops */ me->state = HT_ABORT_SAVE; } } else { #if 0 /* ** If you are very precautios then you can ask here whether ** we should continue or not in case of a redirection */ if ((*prompt)(request, HT_A_CONFIRM, HT_MSG_DESTINATION_MOVED, NULL, NULL, NULL) == YES) { me->destination = redirection; } else { /* ** Make sure that the operation stops */ me->state = HT_ABORT_SAVE; } #else HTTRACE(APP_TRACE, "Save Filter. Destination hae moved!\n"); me->destination = redirection; #endif } } return HT_OK; } /* ** If we succeeded getting the source then start the PUT itself. Otherwise ** cleanup the mess */ if (me->state == HT_LOAD_SOURCE && (status == HT_LOADED || status == HT_NOT_MODIFIED) && !HTError_hasSeverity(HTRequest_error(request), ERR_INFO)) { /* Swap the document in the anchor with the new one */ me->placeholder = HTAnchor_document(me->source); HTAnchor_setDocument(me->source, HTChunk_data(me->document)); /* Set up the request object */ HTRequest_addGnHd(request, HT_G_DATE); HTRequest_setEntityAnchor(request, me->source); HTRequest_setMethod(request, METHOD_PUT); HTRequest_setAnchor(request, me->destination); HTRequest_setOutputFormat(request, me->format); HTRequest_setOutputStream(request, me->target); /* Set up preconditions */ set_preconditions(request); /* Delete existing credentials as they are generated anew */ HTRequest_deleteCredentialsAll(request); /* Make sure we flush the output immediately */ HTRequest_forceFlush(request); /* Turn progress notifications back on */ HTRequest_setInternal(request, NO); /* Add the entity callback function to provide the form data */ HTRequest_setPostCallback(request, HTEntity_callback); /* Now start the load normally */ if (launch_request(request, NO) == YES) me->state = HT_SAVE_DEST; else { HTAnchor_setDocument(me->source, me->placeholder); HTChunk_delete(me->document); HT_FREE(me); } #if 0 me->state = launch_request(request, NO) ? HT_SAVE_DEST : HT_LOAD_SOURCE; #endif /* ** 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 { #if 0 /* @@ JK 28/03/2000: invalidated this code as we're doing this exact treatment later on. In addition, it was a source of dangling pointer error */ /* @@ JK: Added it again, because it's now working! */ #endif HTAnchor_setDocument(me->source, me->placeholder); HTChunk_delete(me->document); HT_FREE(me); } return HT_OK; } /* Send an Anchor using PUT from absolute name ** ------------------------------------------- ** Upload a document referenced by an absolute URL appended. ** The URL can NOT contain any fragment identifier! ** The list of form data must be given as an association list where ** the name is the field name and the value is the value of the field. */ PUBLIC BOOL HTPutDocumentAbsolute (HTParentAnchor * source, const char * destination, HTRequest * request) { if (source && destination && request) { HTAnchor * dest = HTAnchor_findAddress(destination); return HTPutDocumentAnchor(source, dest, request); } return NO; } /* Send an Anchor using PUT from relative name ** ------------------------------------------- ** Upload a document referenced by a relative URL appended. ** The URL can NOT contain any fragment identifier! ** The list of form data must be given as an association list where ** the name is the field name and the value is the value of the field. */ PUBLIC BOOL HTPutDocumentRelative (HTParentAnchor * source, const char * relative, HTParentAnchor * destination_base, HTRequest * request) { if (source && relative && destination_base && request) { BOOL status; char * full_url = NULL; char * base_url = HTAnchor_address((HTAnchor *) destination_base); full_url=HTParse(relative, base_url, PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION); status = HTPutDocumentAbsolute(source, full_url, request); HT_FREE(full_url); HT_FREE(base_url); return status; } return NO; } /* Send an Anchor using PUT from an anchor ** --------------------------------------- ** Upload a document referenced by an anchor object appended ** The URL can NOT contain any fragment identifier! ** The source document is first loaded into memory and then the PUT ** to the remote server is done using the memory version */ PUBLIC BOOL HTPutDocumentAnchor (HTParentAnchor * source, HTAnchor * destination, HTRequest * request) { HTParentAnchor * dest = HTAnchor_parent(destination); if (source && dest && request) { if (setup_anchors(request, source, dest, METHOD_PUT) == YES) { HTPutContext * context = NULL; /* ** First we register an AFTER filter that can check the result ** of the source load if success then it can start the PUT ** operation to the destination. */ if (!(context=(HTPutContext *) HT_CALLOC(1, sizeof(HTPutContext)))) HT_OUTOFMEM("HTPutDocumentAnchor"); context->source = source; context->destination = destination; /* ** We register the after filter with a NULL template as we ** don't know the source of the data. */ HTRequest_addAfter(request, HTSaveFilter, NULL, context, HT_ALL, HT_FILTER_FIRST, NO); /* Turn off progress notifications */ HTRequest_setInternal(request, YES); /* ** We make sure that we are not using a memory cached element. ** It's OK to use a file cached element! */ HTRequest_setReloadMode(request, HT_CACHE_FLUSH_MEM); /* ** Some proxy servers don't clean up their cache ** when receiving a PUT, specially if this PUT is ** redirected. We remove this problem by adding ** an explicit Cache-Control: no-cache header to ** all PUT requests. */ HTRequest_addCacheControl(request, "no-cache", ""); /* ** Now we load the source document into a chunk. We specify that ** we want the document ASIS from the source location. */ context->format = HTRequest_outputFormat(request); context->target = HTRequest_outputStream(request); HTRequest_setOutputFormat(request, WWW_SOURCE); context->document = HTLoadAnchorToChunk((HTAnchor*)source,request); if (context->document == NULL) { HTTRACE(APP_TRACE, "Put Document No source\n"); HT_FREE(context); return NO; } return YES; } } return NO; } /* ------------------------------------------------------------------------- */ /* Copy an anchor ** -------------- ** Fetch the URL (possibly local file URL) and send it using either PUT ** or POST to the remote destination using HTTP. The caller can decide the ** exact method used and which HTTP header fields to transmit by setting ** the user fields in the request structure. ** If posting to NNTP then we can't dispatch at this level but must pass ** the source anchor to the news module that then takes all the refs ** to NNTP and puts into the "newsgroups" header */ PUBLIC BOOL HTCopyAnchor (HTAnchor * src_anchor, HTRequest * main_dest) { HTRequest * src_req; HTList * cur; if (!src_anchor || !main_dest) { HTTRACE(APP_TRACE, "Copy........ BAD ARGUMENT\n"); return NO; } /* Set the source anchor */ main_dest->source_anchor = HTAnchor_parent(src_anchor); /* Build the POST web if not already there */ if (!main_dest->source) { src_req = HTRequest_dupInternal(main_dest); /* Get a duplicate */ HTAnchor_clearHeader((HTParentAnchor *) src_anchor); src_req->method = METHOD_GET; src_req->reload = HT_CACHE_FLUSH_MEM; src_req->output_stream = NULL; src_req->output_format = WWW_SOURCE; /* We want source (for now) */ /* Set up the main link in the source anchor */ { HTLink * main_link = HTAnchor_mainLink((HTAnchor *) src_anchor); HTAnchor *main_anchor = HTLink_destination(main_link); HTMethod method = HTLink_method(main_link); if (!main_link || method==METHOD_INVALID) { HTTRACE(APP_TRACE, "Copy Anchor. No destination found or unspecified method\n"); HTRequest_delete(src_req); return NO; } main_dest->GenMask |= HT_G_DATE; /* Send date header */ main_dest->reload = HT_CACHE_VALIDATE; main_dest->method = method; main_dest->input_format = WWW_SOURCE; HTRequest_addDestination(src_req, main_dest); if (HTLoadAnchor(main_anchor, main_dest) == NO) return NO; } /* For all other links in the source anchor */ if ((cur = HTAnchor_subLinks(src_anchor))) { HTLink * pres; while ((pres = (HTLink *) HTList_nextObject(cur))) { HTAnchor *dest = HTLink_destination(pres); HTMethod method = HTLink_method(pres); HTRequest *dest_req; if (!dest || method==METHOD_INVALID) { HTTRACE(APP_TRACE, "Copy Anchor. Bad anchor setup %p\n" _ dest); return NO; } dest_req = HTRequest_dupInternal(main_dest); dest_req->GenMask |= HT_G_DATE; /* Send date header */ dest_req->reload = HT_CACHE_VALIDATE; dest_req->method = method; dest_req->output_stream = NULL; dest_req->output_format = WWW_SOURCE; HTRequest_addDestination(src_req, dest_req); if (HTLoadAnchor(dest, dest_req) == NO) return NO; } } } else { /* Use the existing Post Web and restart it */ src_req = main_dest->source; if (src_req->mainDestination) if (launch_request(main_dest, NO) == NO) return NO; if (src_req->destinations) { HTRequest * pres; cur = HTAnchor_subLinks(src_anchor); while ((pres = (HTRequest *) HTList_nextObject(cur)) != NULL) { if (launch_request(pres, NO) == NO) return NO; } } } /* Now open the source */ return HTLoadAnchor(src_anchor, src_req); } /* Upload an Anchor ** ---------------- ** This function can be used to send data along with a request to a remote ** server. It can for example be used to POST form data to a remote HTTP ** server - or it can be used to post a newsletter to a NNTP server. In ** either case, you pass a callback function which the request calls when ** the remote destination is ready to accept data. In this callback ** you get the current request object and a stream into where you can ** write data. It is very important that you return the value returned ** by this stream to the Library so that it knows what to do next. The ** reason is that the outgoing stream might block or an error may ** occur and in that case the Library must know about it. The source ** anchor represents the data object in memory and it points to ** the destination anchor by using the POSTWeb method. The source anchor ** contains metainformation about the data object in memory and the ** destination anchor represents the reponse from the remote server. ** Returns YES if request accepted, else NO */ PUBLIC BOOL HTUploadAnchor (HTAnchor * source_anchor, HTRequest * request, HTPostCallback * callback) { HTLink * link = HTAnchor_mainLink((HTAnchor *) source_anchor); HTAnchor * dest_anchor = HTLink_destination(link); HTMethod method = HTLink_method(link); if (!link || method==METHOD_INVALID || !callback) { HTTRACE(APP_TRACE, "Upload...... No destination found or unspecified method\n"); return NO; } request->GenMask |= HT_G_DATE; /* Send date header */ request->reload = HT_CACHE_VALIDATE; request->method = method; request->source_anchor = HTAnchor_parent(source_anchor); request->PostCallback = callback; return HTLoadAnchor(dest_anchor, request); } /* POST Callback Handler ** --------------------- ** If you do not want to handle the stream interface on your own, you ** can use this function which writes the source anchor hyperdoc to the ** target stream for the anchor upload and also handles the return value ** from the stream. If you don't want to write the source anchor hyperdoc ** then you can register your own callback function that can get the data ** you want. */ PUBLIC int HTUpload_callback (HTRequest * request, HTStream * target) { HTTRACE(APP_TRACE, "Uploading... from callback function\n"); if (!request || !request->source_anchor || !target) return HT_ERROR; { int status; HTParentAnchor * source = request->source_anchor; char * document = (char *) HTAnchor_document(request->source_anchor); int len = HTAnchor_length(source); if (len < 0) { len = strlen(document); HTAnchor_setLength(source, len); } status = (*target->isa->put_block)(target, document, len); if (status == HT_OK) return (*target->isa->flush)(target); if (status == HT_WOULD_BLOCK) { HTTRACE(PROT_TRACE, "POST Anchor. Target WOULD BLOCK\n"); return HT_WOULD_BLOCK; } else if (status == HT_PAUSE) { HTTRACE(PROT_TRACE, "POST Anchor. Target PAUSED\n"); return HT_PAUSE; } else if (status > 0) { /* Stream specific return code */ HTTRACE(PROT_TRACE, "POST Anchor. Target returns %d\n" _ status); return status; } else { /* we have a real error */ HTTRACE(PROT_TRACE, "POST Anchor. Target ERROR\n"); return status; } } } /* ------------------------------------------------------------------------- */ /* HEAD METHOD */ /* ------------------------------------------------------------------------- */ /* Request metainformation about a document from absolute name ** ----------------------------------------------------------- ** Request a document referencd by an absolute URL. ** Returns YES if request accepted, else NO */ PUBLIC BOOL HTHeadAbsolute (const char * url, HTRequest * request) { if (url && request) { HTAnchor * anchor = HTAnchor_findAddress(url); return HTHeadAnchor(anchor, request); } return NO; } /* Request metainformation about a document from relative name ** ----------------------------------------------------------- ** Request a document referenced by a relative URL. The relative URL is ** made absolute by resolving it relative to the address of the 'base' ** anchor. ** Returns YES if request accepted, else NO */ PUBLIC BOOL HTHeadRelative (const char * relative, HTParentAnchor * base, HTRequest * request) { BOOL status = NO; if (relative && base && request) { char * rel = NULL; char * full_url = NULL; char * base_url = HTAnchor_address((HTAnchor *) base); StrAllocCopy(rel, relative); full_url = HTParse(HTStrip(rel), base_url, PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION); status = HTHeadAbsolute(full_url, request); HT_FREE(rel); HT_FREE(full_url); HT_FREE(base_url); } return status; } /* Request metainformation about an anchor ** -------------------------------------- ** Request the document referenced by the anchor ** Returns YES if request accepted, else NO */ PUBLIC BOOL HTHeadAnchor (HTAnchor * anchor, HTRequest * request) { if (anchor && request) { HTRequest_setAnchor(request, anchor); HTRequest_setMethod(request, METHOD_HEAD); return launch_request(request, NO); } return NO; } /* ------------------------------------------------------------------------- */ /* DELETE METHOD */ /* ------------------------------------------------------------------------- */ /* Delete a document on a remote server ** ------------------------------------ ** Request a document referencd by an absolute URL. ** Returns YES if request accepted, else NO */ PUBLIC BOOL HTDeleteAbsolute (const char * url, HTRequest * request) { if (url && request) { HTAnchor * anchor = HTAnchor_findAddress(url); return HTDeleteAnchor(anchor, request); } return NO; } /* Request metainformation about a document from relative name ** ----------------------------------------------------------- ** Request a document referenced by a relative URL. The relative URL is ** made absolute by resolving it relative to the address of the 'base' ** anchor. ** Returns YES if request accepted, else NO */ PUBLIC BOOL HTDeleteRelative (const char * relative, HTParentAnchor * base, HTRequest * request) { BOOL status = NO; if (relative && base && request) { char * rel = NULL; char * full_url = NULL; char * base_url = HTAnchor_address((HTAnchor *) base); StrAllocCopy(rel, relative); full_url = HTParse(HTStrip(rel), base_url, PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION); status = HTDeleteAbsolute(full_url, request); HT_FREE(rel); HT_FREE(full_url); HT_FREE(base_url); } return status; } /* Request metainformation about an anchor ** -------------------------------------- ** Request the document referenced by the anchor ** Returns YES if request accepted, else NO */ PUBLIC BOOL HTDeleteAnchor (HTAnchor * anchor, HTRequest * request) { if (anchor && request) { HTRequest_setAnchor(request, anchor); HTRequest_setMethod(request, METHOD_DELETE); return launch_request(request, NO); } return NO; } /* ------------------------------------------------------------------------- */ /* OPTIONS METHOD */ /* ------------------------------------------------------------------------- */ /* Options availeble for document from absolute name ** ------------------------------------------------- ** Request a document referencd by an absolute URL. ** Returns YES if request accepted, else NO */ PUBLIC BOOL HTOptionsAbsolute (const char * url, HTRequest * request) { if (url && request) { HTAnchor * anchor = HTAnchor_findAddress(url); return HTOptionsAnchor(anchor, request); } return NO; } /* Options available for document from relative name ** ------------------------------------------------- ** Request a document referenced by a relative URL. The relative URL is ** made absolute by resolving it relative to the address of the 'base' ** anchor. ** Returns YES if request accepted, else NO */ PUBLIC BOOL HTOptionsRelative (const char * relative, HTParentAnchor * base, HTRequest * request) { BOOL status = NO; if (relative && base && request) { char * rel = NULL; char * full_url = NULL; char * base_url = HTAnchor_address((HTAnchor *) base); StrAllocCopy(rel, relative); full_url = HTParse(HTStrip(rel), base_url, PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION); status = HTOptionsAbsolute(full_url, request); HT_FREE(rel); HT_FREE(full_url); HT_FREE(base_url); } return status; } /* Options available for document using Anchor ** ------------------------------------------- ** Request the document referenced by the anchor ** Returns YES if request accepted, else NO */ PUBLIC BOOL HTOptionsAnchor (HTAnchor * anchor, HTRequest * request) { if (anchor && request) { HTRequest_setAnchor(request, anchor); HTRequest_setMethod(request, METHOD_OPTIONS); return launch_request(request, NO); } return NO; } /* ------------------------------------------------------------------------- */ /* TRACE METHOD */ /* ------------------------------------------------------------------------- */ /* Traces available for document from absolute name ** ------------------------------------------------ ** Request a document referencd by an absolute URL. ** Returns YES if request accepted, else NO */ PUBLIC BOOL HTTraceAbsolute (const char * url, HTRequest * request) { if (url && request) { HTAnchor * anchor = HTAnchor_findAddress(url); return HTTraceAnchor(anchor, request); } return NO; } /* Traces available for document from relative name ** ------------------------------------------------ ** Request a document referenced by a relative URL. The relative URL is ** made absolute by resolving it relative to the address of the 'base' ** anchor. ** Returns YES if request accepted, else NO */ PUBLIC BOOL HTTraceRelative (const char * relative, HTParentAnchor * base, HTRequest * request) { BOOL status = NO; if (relative && base && request) { char * rel = NULL; char * full_url = NULL; char * base_url = HTAnchor_address((HTAnchor *) base); StrAllocCopy(rel, relative); full_url = HTParse(HTStrip(rel), base_url, PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION); status = HTTraceAbsolute(full_url, request); HT_FREE(rel); HT_FREE(full_url); HT_FREE(base_url); } return status; } /* Trace available for document using Anchor ** ------------------------------------------- ** Request the document referenced by the anchor ** Returns YES if request accepted, else NO */ PUBLIC BOOL HTTraceAnchor (HTAnchor * anchor, HTRequest * request) { if (anchor && request) { HTRequest_setAnchor(request, anchor); HTRequest_setMethod(request, METHOD_TRACE); return launch_request(request, NO); } return NO; } /* ------------------------------------------------------------------------- */ /* SERVER METHODS */ /* ------------------------------------------------------------------------- */ PRIVATE BOOL launch_server (HTRequest * request, BOOL recursive) { #ifdef HTDEBUG if (PROT_TRACE) { HTParentAnchor *anchor = HTRequest_anchor(request); char * full_address = HTAnchor_address((HTAnchor *) anchor); HTTRACE(PROT_TRACE, "HTAccess.... Serving %s\n" _ full_address); HT_FREE(full_address); } #endif /* HTDEBUG */ return HTServe(request, recursive); } /* Serving a request ** ----------------- ** Returns YES if request accepted, else NO */ PUBLIC BOOL HTServeAbsolute (const char * url, HTRequest * request) { if (url && request) { HTAnchor * anchor = HTAnchor_findAddress(url); HTRequest_setAnchor(request, anchor); return launch_server(request, NO); } return NO; }