Annotation of libwww/Library/src/HTAccess.c, revision 1.131

1.120     eric        1: /*                                                                  htaccess.c
1.61      frystyk     2: **     ACCESS MANAGER
                      3: **
1.75      frystyk     4: **     (c) COPYRIGHT MIT 1995.
1.61      frystyk     5: **     Please first read the full copyright statement in the file COPYRIGH.
1.131   ! frystyk     6: **     @(#) $Id: HTAccess.c,v 1.130 1996/08/19 18:30:10 frystyk Exp $
1.1       timbl       7: **
                      8: ** Authors
1.79      frystyk     9: **     TBL     Tim Berners-Lee timbl@w3.org
1.4       timbl      10: **     JFG     Jean-Francois Groff jfg@dxcern.cern.ch
1.1       timbl      11: **     DD      Denis DeLaRoca (310) 825-4580  <CSP1DWD@mvs.oac.ucla.edu>
1.122     frystyk    12: **     HFN     Henrik Frystyk, frystyk@w3.org
1.1       timbl      13: ** History
                     14: **       8 Jun 92 Telnet hopping prohibited as telnet is not secure TBL
                     15: **     26 Jun 92 When over DECnet, suppressed FTP, Gopher and News. JFG
1.42      frystyk    16: **      6 Oct 92 Moved HTClientHost and HTlogfile into here. TBL
1.1       timbl      17: **     17 Dec 92 Tn3270 added, bug fix. DD
1.2       timbl      18: **      4 Feb 93 Access registration, Search escapes bad chars TBL
1.9       timbl      19: **               PARAMETERS TO HTSEARCH AND HTLOADRELATIVE CHANGED
                     20: **     28 May 93 WAIS gateway explicit if no WAIS library linked in.
1.19      timbl      21: **        Dec 93 Bug change around, more reentrant, etc
1.42      frystyk    22: **     09 May 94 logfile renamed to HTlogfile to avoid clash with WAIS
1.114     frystyk    23: **      8 Jul 94 Insulate HT_FREE();
1.88      frystyk    24: **        Sep 95 Rewritten, HFN
1.1       timbl      25: */
                     26: 
1.67      frystyk    27: /* Library include files */
1.122     frystyk    28: #include "WWWUtil.h"
                     29: #include "WWWCore.h"
                     30: #include "WWWStream.h"
1.123     frystyk    31: #include "WWWRules.h"
1.93      frystyk    32: #include "HTReqMan.h"
                     33: #include "HTAccess.h"                                   /* Implemented here */
1.88      frystyk    34: 
1.111     frystyk    35: #define PUTBLOCK(b, l) (*target->isa->put_block)(target, b, l)
                     36: 
                     37: struct _HTStream {
                     38:     HTStreamClass * isa;
                     39: };
                     40: 
1.124     frystyk    41: typedef enum _HTPutState {
1.125     frystyk    42:     HT_LOAD_SOURCE     = 0,
1.128     frystyk    43:     HT_SAVE_DEST,
                     44:     HT_ABORT_SAVE
1.124     frystyk    45: } HTPutState;
                     46: 
                     47: typedef struct _HTPutContext {
                     48:     HTParentAnchor *   source;
                     49:     HTAnchor *         destination;
                     50:     HTChunk *          document;
                     51:     HTFormat           format;
                     52:     HTStream *         target;                /* Any existing output stream */
                     53:     void *             placeholder;           /* Any existing doc in anchor */
                     54:     HTPutState         state;
                     55: } HTPutContext;
                     56: 
1.123     frystyk    57: /* --------------------------------------------------------------------------*/
                     58: /*                             THE GET METHOD                               */
                     59: /* --------------------------------------------------------------------------*/
1.33      luotonen   60: 
1.90      frystyk    61: /*     Request a document
                     62: **     -----------------
                     63: **     Private version that requests a document from the request manager
                     64: **     Returns YES if request accepted, else NO
1.88      frystyk    65: */
1.124     frystyk    66: PRIVATE BOOL launch_request (HTRequest * request, BOOL recursive)
1.88      frystyk    67: {
                     68:     if (PROT_TRACE) {
1.90      frystyk    69:        HTParentAnchor *anchor = HTRequest_anchor(request);
                     70:        char * full_address = HTAnchor_address((HTAnchor *) anchor);
1.115     eric       71:        HTTrace("HTAccess.... Accessing document %s\n", full_address);
1.114     frystyk    72:        HT_FREE(full_address);
1.88      frystyk    73:     }
1.96      frystyk    74:     return HTLoad(request, recursive);
1.58      frystyk    75: }
1.1       timbl      76: 
1.90      frystyk    77: /*     Request a document from absolute name
                     78: **     -------------------------------------
                     79: **     Request a document referencd by an absolute URL.
                     80: **     Returns YES if request accepted, else NO
                     81: */
1.122     frystyk    82: PUBLIC BOOL HTLoadAbsolute (const char * url, HTRequest * request)
1.90      frystyk    83: {
                     84:     if (url && request) {
                     85:        HTAnchor * anchor = HTAnchor_findAddress(url);
                     86:        HTRequest_setAnchor(request, anchor);
1.124     frystyk    87:        return launch_request(request, NO);
1.90      frystyk    88:     }
                     89:     return NO;
                     90: }
                     91: 
1.123     frystyk    92: /*     Request a document from relative name
                     93: **     -------------------------------------
                     94: **     Request a document referenced by a relative URL. The relative URL is 
                     95: **     made absolute by resolving it relative to the address of the 'base' 
                     96: **     anchor.
                     97: **     Returns YES if request accepted, else NO
                     98: */
                     99: PUBLIC BOOL HTLoadRelative (const char *       relative,
                    100:                            HTParentAnchor *    base,
                    101:                            HTRequest *         request)
                    102: {
                    103:     BOOL status = NO;
                    104:     if (relative && base && request) {
                    105:        char * full_url = NULL;
                    106:        char * base_url = HTAnchor_address((HTAnchor *) base);
                    107:        full_url = HTParse(relative, base_url,
                    108:                         PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
                    109:        status = HTLoadAbsolute(full_url, request);
                    110:        HT_FREE(full_url);
                    111:        HT_FREE(base_url);
                    112:     }
                    113:     return status;
                    114: }
1.90      frystyk   115: 
                    116: /*     Request a document from absolute name to stream
                    117: **     -----------------------------------------------
                    118: **     Request a document referencd by an absolute URL and sending the data
1.123     frystyk   119: **     down a stream.
                    120: **     Returns YES if request accepted, else NO
                    121: */
                    122: PUBLIC BOOL HTLoadToStream (const char * url, HTStream * output,
                    123:                            HTRequest * request)
                    124: {
                    125:     if (url && output && request) {
                    126:        HTRequest_setOutputStream(request, output);
                    127:        return HTLoadAbsolute(url, request);
                    128:     }
                    129:     return NO;
                    130: }
                    131: 
                    132: /*     Load a document and save it ASIS in a local file
                    133: **     ------------------------------------------------
1.90      frystyk   134: **     Returns YES if request accepted, else NO
                    135: */
1.123     frystyk   136: PUBLIC BOOL HTLoadToFile (const char * url, HTRequest * request,
                    137:                          const char * filename)
1.90      frystyk   138: {
1.123     frystyk   139:     if (url && filename && request) {
                    140:        FILE * fp = NULL;
                    141:        
                    142:        /* Check if file exists. If so then ask user if we can replace it */
                    143:        if (access(filename, F_OK) != -1) {
                    144:            HTAlertCallback * prompt = HTAlert_find(HT_A_CONFIRM);
                    145:            if (prompt) {
                    146:                if ((*prompt)(request, HT_A_CONFIRM, HT_MSG_FILE_REPLACE, NULL,
                    147:                              NULL, NULL) != YES)
                    148:                    return NO;
                    149:            }
                    150:        }
                    151: 
                    152:        /* If replace then open the file */
                    153:        if ((fp = fopen(filename, "wb")) == NULL) {
                    154:            HTRequest_addError(request, ERR_NON_FATAL, NO, HTERR_NO_FILE, 
                    155:                               (char *) filename, strlen(filename),
                    156:                               "HTLoadToFile"); 
                    157:            return NO;
                    158:        }
                    159: 
                    160:        /* Set the output stream and start the request */
                    161:        HTRequest_setOutputFormat(request, WWW_SOURCE);
                    162:        HTRequest_setOutputStream(request, HTFWriter_new(request, fp, NO));
                    163:        return HTLoadAbsolute(url, request);
                    164:     }
                    165:     return NO;
1.90      frystyk   166: }
                    167: 
1.122     frystyk   168: /*
                    169: **     Load a URL to a mem buffer
                    170: **     --------------------------
                    171: **     Load a request and store the result in a memory buffer.
                    172: **     Returns chunk if OK - else NULL
                    173: */
                    174: PUBLIC HTChunk * HTLoadToChunk (const char * url, HTRequest * request)
                    175: {
                    176:     if (url && request) {
                    177:        HTChunk * chunk = NULL;
                    178:        HTStream * target = HTStreamToChunk(request, &chunk, 0);
                    179:        HTAnchor * anchor = HTAnchor_findAddress(url);
                    180:        HTRequest_setAnchor(request, anchor);
                    181:        HTRequest_setOutputStream(request, target);
1.124     frystyk   182:        if (launch_request(request, NO) == YES)
1.122     frystyk   183:            return chunk;
                    184:        else {
                    185:            HTChunk_delete(chunk);
                    186:            return NULL;
                    187:        }
                    188:     }
                    189:     return NULL;
                    190: }
1.90      frystyk   191: 
                    192: /*     Request an anchor
                    193: **     -----------------
                    194: **     Request the document referenced by the anchor
                    195: **     Returns YES if request accepted, else NO
                    196: */
                    197: PUBLIC BOOL HTLoadAnchor (HTAnchor * anchor, HTRequest * request)
                    198: {
                    199:     if (anchor && request) {
                    200:        HTRequest_setAnchor(request, anchor);
1.124     frystyk   201:        return launch_request(request, NO);
1.90      frystyk   202:     }
                    203:     return NO;
                    204: }
                    205: 
                    206: /*     Request an anchor
                    207: **     -----------------
                    208: **     Same as HTLoadAnchor but any information in the Error Stack in the 
                    209: **     request object is kept, so that any error messages in one 
1.52      frystyk   210: **     This function is almost identical to HTLoadAnchor, but it doesn't
                    211: **     clear the error stack so that the information in there is kept.
1.90      frystyk   212: **     Returns YES if request accepted, else NO
                    213: */
                    214: PUBLIC BOOL HTLoadAnchorRecursive (HTAnchor * anchor, HTRequest * request)
                    215: {
                    216:     if (anchor && request) {
                    217:        HTRequest_setAnchor(request, anchor);
1.124     frystyk   218:         return launch_request(request, YES);
1.90      frystyk   219:     }
                    220:     return NO;
                    221: }
                    222: 
1.122     frystyk   223: /*
                    224: **     Load a URL to a mem buffer
                    225: **     --------------------------
                    226: **     Load a request and store the result in a memory buffer.
                    227: **     Returns chunk if OK - else NULL
                    228: */
                    229: PUBLIC HTChunk * HTLoadAnchorToChunk (HTAnchor * anchor, HTRequest * request)
                    230: {
1.124     frystyk   231:     HTChunk * chunk = NULL;
1.122     frystyk   232:     if (anchor && request) {
                    233:        HTStream * target = HTStreamToChunk(request, &chunk, 0);
                    234:        HTRequest_setAnchor(request, anchor);
                    235:        HTRequest_setOutputStream(request, target);
1.124     frystyk   236:        if (launch_request(request, NO) == YES)
1.122     frystyk   237:            return chunk;
                    238:        else {
                    239:            HTChunk_delete(chunk);
                    240:            return NULL;
                    241:        }
                    242:     }
                    243:     return NULL;
                    244: }
1.90      frystyk   245: 
1.123     frystyk   246: /*
                    247: **     Load a Rule File
                    248: **     ----------------
                    249: **     Load a rule find with the URL specified and add the set of rules to
                    250: **     the existing set.
                    251: */
                    252: PUBLIC BOOL HTLoadRules (const char * url)
                    253: {
                    254:     BOOL status = NO;
                    255:     if (url) {
                    256:        HTList * list = HTList_new();
                    257:        HTRequest * request = HTRequest_new();
                    258:        HTRequest_setPreemptive(request, YES);
                    259:        HTAlert_setInteractive(NO);
                    260:        HTConversion_add(list, "application/x-www-rules", "*/*", HTRules,
                    261:                         1.0, 0.0, 0.0);
                    262:        HTRequest_setConversion(request, list, YES);
                    263:        status = HTLoadAbsolute(url, request);
                    264:        HTConversion_deleteAll(list);
                    265:        HTRequest_delete(request);
                    266:     }
                    267:     return status;
                    268: }
                    269: 
                    270: /* --------------------------------------------------------------------------*/
                    271: /*                      GET WITH KEYWORDS (SEARCH)                          */
                    272: /* --------------------------------------------------------------------------*/
                    273: 
                    274: /*
                    275: **     This function creates a URL with a searh part as defined by RFC 1866
                    276: **     Both the baseurl and the keywords must be escaped.
                    277: **
                    278: **     1. The form field names and values are escaped: space
                    279: **     characters are replaced by `+', and then reserved characters
                    280: **     are escaped as per [URL]; that is, non-alphanumeric
                    281: **     characters are replaced by `%HH', a percent sign and two
                    282: **     hexadecimal digits representing the ASCII code of the
                    283: **     character. Line breaks, as in multi-line text field values,
                    284: **     are represented as CR LF pairs, i.e. `%0D%0A'.
                    285: **
                    286: **     2. The fields are listed in the order they appear in the
                    287: **     document with the name separated from the value by `=' and
                    288: **     the pairs separated from each other by `&'. Fields with null
                    289: **     values may be omitted. In particular, unselected radio
                    290: **     buttons and checkboxes should not appear in the encoded
                    291: **     data, but hidden fields with VALUE attributes present
                    292: **     should.
                    293: **
                    294: **         NOTE - The URI from a query form submission can be
                    295: **         used in a normal anchor style hyperlink.
                    296: **         Unfortunately, the use of the `&' character to
                    297: **         separate form fields interacts with its use in SGML
                    298: **         attribute values as an entity reference delimiter.
                    299: **         For example, the URI `http://host/?x=1&y=2' must be
                    300: **         written `<a href="http://host/?x=1&#38;y=2"' or `<a
                    301: **         href="http://host/?x=1&amp;y=2">'.
                    302: **
                    303: **         HTTP server implementors, and in particular, CGI
                    304: **         implementors are encouraged to support the use of
                    305: **         `;' in place of `&' to save users the trouble of
                    306: **         escaping `&' characters this way.
                    307: */
                    308: PRIVATE char * query_url_encode (const char * baseurl, HTChunk * keywords)
                    309: {
                    310:     char * fullurl = NULL;
                    311:     if (baseurl && keywords && HTChunk_size(keywords)) {
                    312:        int len = strlen(baseurl);
                    313:        fullurl = (char *) HT_MALLOC(len + HTChunk_size(keywords) + 2);
                    314:        sprintf(fullurl, "%s?%s", baseurl, HTChunk_data(keywords));
                    315:        {
                    316:            char * ptr = fullurl+len;
                    317:            while (*ptr) {
                    318:                if (*ptr == ' ') *ptr = '+';
                    319:                ptr++;
                    320:            }
                    321:        }
                    322:     }
                    323:     return fullurl;
                    324: }
                    325: 
                    326: PRIVATE char * form_url_encode (const char * baseurl, HTAssocList * formdata)
                    327: {
                    328:     if (formdata) {
                    329:        BOOL first = YES;
                    330:        int cnt = HTList_count((HTList *) formdata);
                    331:        HTChunk * fullurl = HTChunk_new(128);
                    332:        HTAssoc * pres;
1.124     frystyk   333:        if (baseurl) {
                    334:            HTChunk_puts(fullurl, baseurl);
                    335:            HTChunk_putc(fullurl, '?');
                    336:        }
1.123     frystyk   337:        while (cnt > 0) {
                    338:            pres = (HTAssoc *) HTList_objectAt((HTList *) formdata, --cnt);
                    339:            if (first)
                    340:                first = NO;
                    341:            else
                    342:                HTChunk_putc(fullurl, '&');         /* Could use ';' instead */
                    343:            HTChunk_puts(fullurl, HTAssoc_name(pres));
                    344:            HTChunk_putc(fullurl, '=');
                    345:            HTChunk_puts(fullurl, HTAssoc_value(pres));
                    346:        }
                    347:        return HTChunk_toCString(fullurl);
                    348:     }
                    349:     return NULL;
                    350: }
                    351: 
                    352: /*     Search a document from absolute name
                    353: **     ------------------------------------
                    354: **     Request a document referencd by an absolute URL appended with the
                    355: **     keywords given. The URL can NOT contain any fragment identifier!
                    356: **     The list of keywords must be a space-separated list and spaces will
                    357: **     be converted to '+' before the request is issued.
                    358: **     Returns YES if request accepted, else NO
                    359: */
                    360: PUBLIC BOOL HTSearchAbsolute (HTChunk *                keywords,
                    361:                              const char *      base,
                    362:                              HTRequest *       request)
                    363: {
                    364:     if (keywords && base && request) {
                    365:        char * full = query_url_encode(base, keywords);
                    366:        if (full) {
                    367:            HTAnchor * anchor = HTAnchor_findAddress(full);
                    368:            HTRequest_setAnchor(request, anchor);
                    369:            HT_FREE(full);
1.124     frystyk   370:            return launch_request(request, NO);
1.123     frystyk   371:        }
                    372:     }
                    373:     return NO;
                    374: }
                    375: 
                    376: /*     Search a document from relative name
                    377: **     -------------------------------------
                    378: **     Request a document referenced by a relative URL. The relative URL is 
                    379: **     made absolute by resolving it relative to the address of the 'base' 
                    380: **     anchor.
                    381: **     Returns YES if request accepted, else NO
                    382: */
                    383: PUBLIC BOOL HTSearchRelative (HTChunk *        keywords,
                    384:                              const char *      relative,
                    385:                              HTParentAnchor *  base,
                    386:                              HTRequest *       request)
                    387: {
                    388:     BOOL status = NO;
                    389:     if (keywords && relative && base && request) {
                    390:        char * full_url = NULL;
                    391:        char * base_url = HTAnchor_address((HTAnchor *) base);
                    392:        full_url = HTParse(relative, base_url,
                    393:                         PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
                    394:        status = HTSearchAbsolute(keywords, full_url, request);
                    395:        HT_FREE(full_url);
                    396:        HT_FREE(base_url);
                    397:     }
                    398:     return status;
                    399: }
                    400: 
                    401: /*
                    402: **     Search a string
                    403: **     ---------------
                    404: **     This is the same as HTSearchAbsolute but instead of using a chunk
                    405: **     you can pass a string.
                    406: */
                    407: PUBLIC BOOL HTSearchString (const char *       keywords,
                    408:                            HTAnchor *          anchor,
                    409:                            HTRequest *         request)
                    410: {
                    411:     BOOL status = NO;
                    412:     if (keywords && anchor && request) {       
                    413:        char * base_url = HTAnchor_address((HTAnchor *) anchor);
                    414:        HTChunk * chunk = HTChunk_new(strlen(keywords)+2);
                    415:        HTChunk_puts(chunk, keywords);
                    416:        status = HTSearchAbsolute(chunk, base_url, request);    
                    417:        HT_FREE(base_url);
                    418:        HTChunk_delete(chunk);
                    419:     }
                    420:     return status;
                    421: }      
                    422: 
1.90      frystyk   423: /*     Search an Anchor
                    424: **     ----------------
                    425: **     Performs a keyword search on word given by the user. Adds the keyword
                    426: **     to the end of the current address and attempts to open the new address.
                    427: **     The list of keywords must be a space-separated list and spaces will
                    428: **     be converted to '+' before the request is issued.
                    429: **     Search can also be performed by HTLoadAbsolute() etc.
                    430: **     Returns YES if request accepted, else NO
                    431: */
1.123     frystyk   432: PUBLIC BOOL HTSearchAnchor (HTChunk *          keywords,
                    433:                            HTAnchor *          anchor,
                    434:                            HTRequest *         request)
1.90      frystyk   435: {
1.99      frystyk   436:     BOOL status = NO;
1.123     frystyk   437:     if (keywords && anchor && request) {
                    438:        char * base_url = HTAnchor_address(anchor);
                    439:        status = HTSearchAbsolute(keywords, base_url, request); 
1.114     frystyk   440:        HT_FREE(base_url);
1.90      frystyk   441:     }
1.99      frystyk   442:     return status;
1.2       timbl     443: }
                    444: 
1.123     frystyk   445: /* --------------------------------------------------------------------------*/
                    446: /*                      HANDLING FORMS USING GET AND POST                   */
                    447: /* --------------------------------------------------------------------------*/
1.2       timbl     448: 
1.123     frystyk   449: /*     Send a Form request using GET from absolute name
                    450: **     ------------------------------------------------
1.90      frystyk   451: **     Request a document referencd by an absolute URL appended with the
1.123     frystyk   452: **     formdata given. The URL can NOT contain any fragment identifier!
                    453: **     The list of form data must be given as an association list where 
                    454: **     the name is the field name and the value is the value of the field.
                    455: **     Returns YES if request accepted, else NO
                    456: */
                    457: PUBLIC BOOL HTGetFormAbsolute (HTAssocList *   formdata,
                    458:                               const char *     base,
                    459:                               HTRequest *      request)
                    460: {
                    461:     if (formdata && base && request) {
                    462:        char * full = form_url_encode(base, formdata);
                    463:        if (full) {
                    464:            HTAnchor * anchor = HTAnchor_findAddress(full);
                    465:            HTRequest_setAnchor(request, anchor);
                    466:            HT_FREE(full);
1.124     frystyk   467:            return launch_request(request, NO);
1.123     frystyk   468:        }
                    469:     }
                    470:     return NO;
                    471: }
                    472: 
                    473: /*     Send a Form request using GET from relative name
                    474: **     ------------------------------------------------
                    475: **     Request a document referencd by a relative URL appended with the
                    476: **     formdata given. The URL can NOT contain any fragment identifier!
                    477: **     The list of form data must be given as an association list where 
                    478: **     the name is the field name and the value is the value of the field.
                    479: **     Returns YES if request accepted, else NO
                    480: */
                    481: PUBLIC BOOL HTGetFormRelative (HTAssocList *   formdata,
                    482:                               const char *     relative,
                    483:                               HTParentAnchor * base,
                    484:                               HTRequest *      request)
                    485: {
                    486:     BOOL status = NO;
                    487:     if (formdata && relative && base && request) {
                    488:        char * full_url = NULL;
                    489:        char * base_url = HTAnchor_address((HTAnchor *) base);
                    490:        full_url=HTParse(relative, base_url,
                    491:                         PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
                    492:        status = HTGetFormAbsolute(formdata, full_url, request);
                    493:        HT_FREE(full_url);
                    494:        HT_FREE(base_url);
                    495:     }
                    496:     return status;
                    497: }
                    498: 
                    499: /*     Send a Form request using GET from an anchor
                    500: **     --------------------------------------------
                    501: **     Request a document referencd by an anchor object appended with the
                    502: **     formdata given. The URL can NOT contain any fragment identifier!
                    503: **     The list of form data must be given as an association list where 
                    504: **     the name is the field name and the value is the value of the field.
1.90      frystyk   505: **     Returns YES if request accepted, else NO
                    506: */
1.123     frystyk   507: PUBLIC BOOL HTGetFormAnchor (HTAssocList *     formdata,
                    508:                             HTAnchor *         anchor,
                    509:                             HTRequest *        request)
                    510: {
                    511:     BOOL status = NO;
                    512:     if (formdata && anchor && request) {
                    513:        char * base_url = HTAnchor_address((HTAnchor *) anchor);
                    514:        status = HTGetFormAbsolute(formdata, base_url, request);        
                    515:        HT_FREE(base_url);
                    516:     }
                    517:     return status;
                    518: }
                    519: 
                    520: PRIVATE int HTEntity_callback (HTRequest * request, HTStream * target)
                    521: {
                    522:     HTParentAnchor * entity = HTRequest_entityAnchor(request);
1.124     frystyk   523:     if (WWWTRACE) HTTrace("Posting Data from callback function\n");
1.123     frystyk   524:     if (!request || !entity || !target) return HT_ERROR;
                    525:     {
1.126     frystyk   526:        BOOL chunking = NO;
1.123     frystyk   527:        int status;
                    528:        char * document = (char *) HTAnchor_document(entity);
                    529:        int len = HTAnchor_length(entity);
1.126     frystyk   530:        if (!document) {
                    531:            if (PROT_TRACE) HTTrace("Posting Data No document\n");
                    532:            return HT_ERROR;
                    533:        }
                    534: 
                    535:        /*
                    536:        ** If the length is unknown (-1) then see if the document is a text
                    537:        ** type and in that case take the strlen. If not then we don't know
                    538:        ** how much data we can write and must stop
                    539:        */
                    540:        if (len < 0) {
                    541:            HTFormat actual = HTAnchor_format(entity);
                    542:            HTFormat tmplate = HTAtom_for("text/*");
                    543:            if (HTMIMEMatch(tmplate, actual)) {
                    544:                len = strlen(document);                 /* Naive! */
                    545:                chunking = YES;
                    546:            } else {
                    547:                if (PROT_TRACE)
                    548:                    HTTrace("Posting Data Must know the length of document %p\n",
                    549:                            document);
                    550:                return HT_ERROR;
                    551:            }
                    552:        }
                    553: 
                    554:        /* Send the data down the pipe */
1.123     frystyk   555:        status = (*target->isa->put_block)(target, document, len);
1.125     frystyk   556:        if (status == HT_LOADED) {
                    557:            if (PROT_TRACE) HTTrace("Posting Data Target is SAVED\n");
                    558:            (*target->isa->flush)(target);
                    559:            return HT_LOADED;
                    560:        }
1.123     frystyk   561:        if (status == HT_WOULD_BLOCK) {
1.124     frystyk   562:            if (PROT_TRACE)HTTrace("Posting Data Target WOULD BLOCK\n");
1.123     frystyk   563:            return HT_WOULD_BLOCK;
                    564:        } else if (status == HT_PAUSE) {
1.126     frystyk   565:            if (PROT_TRACE) HTTrace("Posting Data Target PAUSED\n");
1.123     frystyk   566:            return HT_PAUSE;
1.126     frystyk   567:        } else if (chunking && status == HT_OK) {
                    568:            if (PROT_TRACE) HTTrace("Posting Data Target is SAVED using chunked\n");
                    569:            return (*target->isa->put_block)(target, "", 0);
                    570:        } else if (status > 0) {              /* Stream specific return code */
1.123     frystyk   571:            if (PROT_TRACE)
1.124     frystyk   572:                HTTrace("Posting Data. Target returns %d\n", status);
1.123     frystyk   573:            return status;
                    574:        } else {                                     /* we have a real error */
1.129     frystyk   575:            if (PROT_TRACE) HTTrace("Posting Data Target ERROR %d\n", status);
1.123     frystyk   576:            return status;
                    577:        }
                    578:     }
                    579: }
                    580: 
                    581: /*     Send a Form request using POST from absolute name
                    582: **     -------------------------------------------------
                    583: **     Request a document referencd by an absolute URL appended with the
                    584: **     formdata given. The URL can NOT contain any fragment identifier!
                    585: **     The list of form data must be given as an association list where 
                    586: **     the name is the field name and the value is the value of the field.
                    587: */
                    588: PUBLIC HTParentAnchor * HTPostFormAbsolute (HTAssocList *      formdata,
                    589:                                            const char *        base,
                    590:                                            HTRequest *         request)
                    591: {
                    592:     if (formdata && base && request) {
                    593:        HTAnchor * anchor = HTAnchor_findAddress(base);
                    594:        return HTPostFormAnchor(formdata, anchor, request);
                    595:     }
                    596:     return NULL;
                    597: }
                    598: 
                    599: /*     Send a Form request using POST from relative name
                    600: **     -------------------------------------------------
                    601: **     Request a document referencd by a relative URL appended with the
                    602: **     formdata given. The URL can NOT contain any fragment identifier!
                    603: **     The list of form data must be given as an association list where 
                    604: **     the name is the field name and the value is the value of the field.
                    605: */
                    606: PUBLIC HTParentAnchor * HTPostFormRelative (HTAssocList *      formdata,
                    607:                                            const char *        relative,
                    608:                                            HTParentAnchor *    base,
                    609:                                            HTRequest *         request)
1.90      frystyk   610: {
1.123     frystyk   611:     HTParentAnchor * postanchor = NULL;
                    612:     if (formdata && relative && base && request) {
                    613:        char * full_url = NULL;
                    614:        char * base_url = HTAnchor_address((HTAnchor *) base);
                    615:        full_url=HTParse(relative, base_url,
                    616:                         PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
                    617:        postanchor = HTPostFormAbsolute(formdata, full_url, request);
                    618:        HT_FREE(full_url);
                    619:        HT_FREE(base_url);
                    620:     }
                    621:     return postanchor;
                    622: }
                    623: 
                    624: /*     Send a Form request using POST from an anchor
                    625: **     ---------------------------------------------
                    626: **     Request a document referencd by an anchor object appended with the
                    627: **     formdata given. The URL can NOT contain any fragment identifier!
                    628: **     The list of form data must be given as an association list where 
                    629: **     the name is the field name and the value is the value of the field.
                    630: */
                    631: PUBLIC HTParentAnchor * HTPostFormAnchor (HTAssocList *        formdata,
                    632:                                          HTAnchor *    anchor,
                    633:                                          HTRequest *   request)
                    634: {
                    635:     HTParentAnchor * postanchor = NULL;
                    636:     if (formdata && anchor && request) {
                    637:        HTUserProfile * up = HTRequest_userProfile(request);
                    638:        char * tmpfile = HTGetTmpFileName(HTUserProfile_tmp(up));
                    639:        char * tmpurl = HTParse(tmpfile, "file:", PARSE_ALL);
                    640:        char * form_encoded = form_url_encode(NULL, formdata);
                    641:        if (form_encoded) {
                    642: 
                    643:            /*
                    644:            **  Now create a new anchor for the post data and set up
                    645:            **  the rest of the metainformation we know about this anchor. The
                    646:            **  tmp anchor may actually already exist from previous postings.
                    647:            */
                    648:            postanchor = (HTParentAnchor *) HTAnchor_findAddress(tmpurl);
                    649:            HTAnchor_clearHeader(postanchor);
                    650:            HTAnchor_setDocument(postanchor, form_encoded);
                    651:            HTAnchor_setLength(postanchor, strlen(form_encoded));
                    652:            HTAnchor_setFormat(postanchor, WWW_FORM);
                    653: 
                    654:            /*
                    655:            **  Bind the temporary anchor to the anchor that will contain the
                    656:            **  response 
                    657:            */
                    658:            HTLink_removeAll((HTAnchor *) postanchor);
                    659:            HTLink_add((HTAnchor *) postanchor, (HTAnchor *) anchor, 
                    660:                       NULL, METHOD_POST);
                    661: 
                    662:            /* Set up the request object */
                    663:            HTRequest_addGnHd(request, HT_G_DATE);       /* Send date header */
                    664:            HTRequest_setAnchor(request, anchor);
                    665:            HTRequest_setEntityAnchor(request, postanchor);
                    666:            HTRequest_setMethod(request, METHOD_POST);
                    667: 
                    668:            /* Add the post form callback function to provide the form data */
                    669:            HTRequest_setPostCallback(request, HTEntity_callback);
                    670: 
                    671:            /* Now start the load normally */
1.124     frystyk   672:            launch_request(request, NO);
1.123     frystyk   673:        }
                    674:        HT_FREE(tmpfile);
                    675:        HT_FREE(tmpurl);
1.90      frystyk   676:     }
1.123     frystyk   677:     return postanchor;
1.57      howcome   678: }
                    679: 
1.70      frystyk   680: /* --------------------------------------------------------------------------*/
1.124     frystyk   681: /*                             PUT A DOCUMENT                               */
                    682: /* --------------------------------------------------------------------------*/ 
1.125     frystyk   683: PRIVATE BOOL setup_anchors (HTRequest * request,
1.131   ! frystyk   684:                            HTParentAnchor * source, HTParentAnchor * dest,
        !           685:                            HTMethod method)
1.124     frystyk   686: {
1.131   ! frystyk   687:     if (!(method & (METHOD_PUT | METHOD_POST))) {
        !           688:        if (APP_TRACE) HTTrace("Posting..... Bad method\n");
        !           689:        return NO;
        !           690:     }
        !           691: 
1.124     frystyk   692:     /*
1.127     frystyk   693:     **  Check whether we know if it is possible to PUT to this destination.
                    694:     **  We both check the local set of allowed methods in the anchor and any
                    695:     **  site information that we may have about the location of the origin 
                    696:     **  server.
1.124     frystyk   697:     */
                    698:     {
1.131   ! frystyk   699:        char * addr = HTAnchor_address((HTAnchor *) source);
        !           700:        char * hostname = HTParse(addr, "", PARSE_HOST);
        !           701:        HTHost * host = HTHost_find(hostname);
        !           702:        HTMethod public_methods = HTHost_publicMethods(host);
        !           703:        HTMethod private_methods = HTAnchor_methods(dest);
        !           704:        HT_FREE(hostname);
        !           705:        HT_FREE(addr);
        !           706:        if (!(method & (private_methods | public_methods))) {
1.124     frystyk   707:            HTAlertCallback * prompt = HTAlert_find(HT_A_CONFIRM);
                    708:            if (prompt) {
                    709:                if ((*prompt)(request, HT_A_CONFIRM, HT_MSG_METHOD,
                    710:                              NULL, NULL, NULL) != YES)
                    711:                    return NO;
                    712:            }
                    713:        }
                    714:     }
                    715: 
                    716:     /*
                    717:     **  Bind the source anchor to the dest anchor that will contain the
                    718:     **  response. If link already exists then ask is we should do it again.
                    719:     **  If so then remove the old link and replace it with a new one.
                    720:     */
                    721:     {
                    722:        HTLink *link = HTLink_find((HTAnchor *) source, (HTAnchor *) dest);
                    723:        if (link && HTLink_method(link) == METHOD_PUT) {
                    724:            HTAlertCallback * prompt = HTAlert_find(HT_A_CONFIRM);
                    725:            if (prompt) {
                    726:                if ((*prompt)(request, HT_A_CONFIRM, HT_MSG_REDO,
                    727:                              NULL, NULL, NULL) != YES)
                    728:                    return NO;
                    729:            }
                    730:            HTLink_remove((HTAnchor *) source, (HTAnchor *) dest);
                    731:        }
                    732:        HTLink_add((HTAnchor*) source, (HTAnchor*) dest, NULL, METHOD_PUT);
                    733:     }
                    734:     return YES;
                    735: }
                    736: 
                    737: /*     Send an Anchor using PUT from absolute name
                    738: **     -------------------------------------------
                    739: **     Upload a document referenced by an absolute URL appended.
                    740: **     The URL can NOT contain any fragment identifier!
                    741: **     The list of form data must be given as an association list where 
                    742: **     the name is the field name and the value is the value of the field.
                    743: */
                    744: PUBLIC BOOL HTPutAbsolute (HTParentAnchor *    source,
                    745:                           const char *         destination,
                    746:                           HTRequest *          request)
                    747: {
                    748:     if (source && destination && request) {
                    749:        HTAnchor * dest = HTAnchor_findAddress(destination);
                    750:        return HTPutAnchor(source, dest, request);
                    751:     }
                    752:     return NO;
                    753: }
                    754: 
                    755: /*     Send an Anchor using PUT from relative name
                    756: **     -------------------------------------------
                    757: **     Upload a document referenced by a relative URL appended.
                    758: **     The URL can NOT contain any fragment identifier!
                    759: **     The list of form data must be given as an association list where 
                    760: **     the name is the field name and the value is the value of the field.
                    761: */
                    762: PUBLIC BOOL HTPutRelative (HTParentAnchor *    source,
                    763:                           const char *         relative,
                    764:                           HTParentAnchor *     destination_base,
                    765:                           HTRequest *          request)
                    766: {
                    767:     if (source && relative && destination_base && request) {
                    768:        BOOL status;
                    769:        char * full_url = NULL;
                    770:        char * base_url = HTAnchor_address((HTAnchor *) destination_base);
                    771:        full_url=HTParse(relative, base_url,
                    772:                         PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
                    773:        status = HTPutAbsolute(source, full_url, request);
                    774:        HT_FREE(full_url);
                    775:        HT_FREE(base_url);
                    776:        return status;
                    777:     }
                    778:     return NO;
                    779: }
                    780: 
                    781: /*     Send an Anchor using PUT from an anchor
                    782: **     ---------------------------------------
                    783: **     Upload a document referenced by an anchor object appended
                    784: **     The URL can NOT contain any fragment identifier!
                    785: **     The list of form data must be given as an association list where 
                    786: **     the name is the field name and the value is the value of the field.
                    787: */
                    788: PUBLIC BOOL HTPutAnchor (HTParentAnchor *      source,
                    789:                         HTAnchor *             destination,
                    790:                         HTRequest *            request)
                    791: {
                    792:     HTParentAnchor * dest = HTAnchor_parent(destination);
                    793:     if (source && dest && request) {
1.131   ! frystyk   794:        if (setup_anchors(request, source, dest, METHOD_PUT) == YES) {
1.124     frystyk   795: 
                    796:            /* Set up the request object */
                    797:            HTRequest_addGnHd(request, HT_G_DATE);
                    798:            HTRequest_setEntityAnchor(request, source);
                    799:            HTRequest_setMethod(request, METHOD_PUT);
                    800:            HTRequest_setAnchor(request, destination);
                    801: 
                    802:            /* Add the entity callback function to provide the form data */
                    803:            HTRequest_setPostCallback(request, HTEntity_callback);
                    804: 
                    805:            /* Now start the load normally */
                    806:            return launch_request(request, NO);
                    807:        }
                    808:     }
                    809:     return NO;
                    810: }
                    811: 
1.125     frystyk   812: /*     Send an Anchor using POST from absolute name
                    813: **     -------------------------------------------
                    814: **     Upload a document referenced by an absolute URL appended.
                    815: **     The URL can NOT contain any fragment identifier!
                    816: **     The list of form data must be given as an association list where 
                    817: **     the name is the field name and the value is the value of the field.
                    818: */
                    819: PUBLIC BOOL HTPostAbsolute (HTParentAnchor *   source,
                    820:                           const char *         destination,
                    821:                           HTRequest *          request)
                    822: {
                    823:     if (source && destination && request) {
                    824:        HTAnchor * dest = HTAnchor_findAddress(destination);
                    825:        return HTPostAnchor(source, dest, request);
                    826:     }
                    827:     return NO;
                    828: }
                    829: 
                    830: /*     Send an Anchor using POST from relative name
                    831: **     -------------------------------------------
                    832: **     Upload a document referenced by a relative URL appended.
                    833: **     The URL can NOT contain any fragment identifier!
                    834: **     The list of form data must be given as an association list where 
                    835: **     the name is the field name and the value is the value of the field.
                    836: */
                    837: PUBLIC BOOL HTPostRelative (HTParentAnchor *   source,
                    838:                           const char *         relative,
                    839:                           HTParentAnchor *     destination_base,
                    840:                           HTRequest *          request)
                    841: {
                    842:     if (source && relative && destination_base && request) {
                    843:        BOOL status;
                    844:        char * full_url = NULL;
                    845:        char * base_url = HTAnchor_address((HTAnchor *) destination_base);
                    846:        full_url=HTParse(relative, base_url,
                    847:                         PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
                    848:        status = HTPostAbsolute(source, full_url, request);
                    849:        HT_FREE(full_url);
                    850:        HT_FREE(base_url);
                    851:        return status;
                    852:     }
                    853:     return NO;
                    854: }
                    855: 
                    856: /*     Send an Anchor using POST from an anchor
                    857: **     ---------------------------------------
                    858: **     Upload a document referenced by an anchor object appended
                    859: **     The URL can NOT contain any fragment identifier!
                    860: **     The list of form data must be given as an association list where 
                    861: **     the name is the field name and the value is the value of the field.
                    862: */
                    863: PUBLIC BOOL HTPostAnchor (HTParentAnchor *     source,
                    864:                         HTAnchor *             destination,
                    865:                         HTRequest *            request)
                    866: {
                    867:     HTParentAnchor * dest = HTAnchor_parent(destination);
                    868:     if (source && dest && request) {
1.131   ! frystyk   869:        if (setup_anchors(request, source, dest, METHOD_POST) == YES) {
1.125     frystyk   870: 
                    871:            /* Set up the request object */
                    872:            HTRequest_addGnHd(request, HT_G_DATE);
                    873:            HTRequest_setEntityAnchor(request, source);
                    874:            HTRequest_setMethod(request, METHOD_POST);
                    875:            HTRequest_setAnchor(request, destination);
                    876: 
                    877:            /* Add the entity callback function to provide the form data */
                    878:            HTRequest_setPostCallback(request, HTEntity_callback);
                    879: 
                    880:            /* Now start the load normally */
                    881:            return launch_request(request, NO);
                    882:        }
                    883:     }
                    884:     return NO;
                    885: }
                    886: 
1.124     frystyk   887: /*     Send an Anchor using PUT from absolute name
                    888: **     -------------------------------------------
                    889: **     Upload a document referenced by an absolute URL appended.
                    890: **     The URL can NOT contain any fragment identifier!
                    891: **     The list of form data must be given as an association list where 
                    892: **     the name is the field name and the value is the value of the field.
                    893: */
                    894: PUBLIC BOOL HTPutStructuredAbsolute (HTParentAnchor *  source,
                    895:                                     const char *       destination,
                    896:                                     HTRequest *        request,
                    897:                                     HTPostCallback *   input)
                    898: {
                    899:     if (source && destination && request && input) {
                    900:        HTAnchor * dest = HTAnchor_findAddress(destination);
                    901:        return HTPutStructuredAnchor(source, dest, request, input);
                    902:     }
                    903:     return NO;
                    904: }
                    905: 
                    906: /*     Send an Anchor using PUT from relative name
                    907: **     -------------------------------------------
                    908: **     Upload a document referenced by a relative URL appended.
                    909: **     The URL can NOT contain any fragment identifier!
                    910: **     The list of form data must be given as an association list where 
                    911: **     the name is the field name and the value is the value of the field.
                    912: */
                    913: PUBLIC BOOL HTPutStructuredRelative (HTParentAnchor *  source,
                    914:                                     const char *       relative,
                    915:                                     HTParentAnchor *   destination_base,
                    916:                                     HTRequest *        request,
                    917:                                     HTPostCallback *   input)
                    918: {
                    919:     if (source && relative && destination_base && request && input) {
                    920:        BOOL status;
                    921:        char * full_url = NULL;
                    922:        char * base_url = HTAnchor_address((HTAnchor *) destination_base);
                    923:        full_url=HTParse(relative, base_url,
                    924:                         PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
                    925:        status = HTPutStructuredAbsolute(source, full_url, request, input);
                    926:        HT_FREE(full_url);
                    927:        HT_FREE(base_url);
                    928:        return status;
                    929:     }
                    930:     return NO;
                    931: }
                    932: 
                    933: /*     Send an Anchor using PUT from an anchor
                    934: **     ---------------------------------------
                    935: **     Upload a document referenced by an anchor object appended
                    936: **     The URL can NOT contain any fragment identifier!
                    937: **     The list of form data must be given as an association list where 
                    938: **     the name is the field name and the value is the value of the field.
                    939: */
                    940: PUBLIC BOOL HTPutStructuredAnchor (HTParentAnchor *    source,
                    941:                                   HTAnchor *           destination,
                    942:                                   HTRequest *          request,
                    943:                                   HTPostCallback *     input)
                    944: {
                    945:     HTParentAnchor * dest = HTAnchor_parent(destination);
                    946:     if (source && dest && request) {
1.131   ! frystyk   947:        if (setup_anchors(request, source, dest, METHOD_PUT) == YES) {
1.124     frystyk   948: 
                    949:            /* Set up the request object */
                    950:            HTRequest_addGnHd(request, HT_G_DATE);
                    951:            HTRequest_setEntityAnchor(request, source);
                    952:            HTRequest_setMethod(request, METHOD_PUT);
                    953:            HTRequest_setAnchor(request, destination);
                    954: 
                    955:            /* Add the entity callback function to provide the form data */
                    956:            HTRequest_setPostCallback(request, input);
                    957: 
                    958:            /* Now start the load normally */
                    959:            return launch_request(request, NO);
                    960:        }
                    961:     }
                    962:     return NO;
                    963: }
                    964: 
                    965: /*
                    966: **     After filter for handling PUT of document. We should now have the 
                    967: */
                    968: PRIVATE int HTSaveFilter (HTRequest * request, void * param, int status)
                    969: {
                    970:     HTPutContext * me = (HTPutContext *) param;
                    971:     if (APP_TRACE)
                    972:        HTTrace("Save Filter. Using context %p with state %c\n",
                    973:                me, me->state+0x30);
                    974: 
                    975:     /*
1.125     frystyk   976:     **  Just ignore authentication in the hope that some other filter will
                    977:     **  handle this.
                    978:     */
                    979:     if (status == HT_NO_ACCESS || status == HT_NO_PROXY_ACCESS) {
                    980:        if (APP_TRACE) HTTrace("Save Filter. Waiting for authentication\n");
                    981:        return HT_OK;
                    982:     }
                    983: 
                    984:     /*
1.124     frystyk   985:     **  If either the source or the destination has moved then ask the user
                    986:     **  what to do. If there is no user then stop
                    987:     */
                    988:     if (status == HT_TEMP_REDIRECT || status == HT_PERM_REDIRECT) {
                    989:        HTAlertCallback * prompt = HTAlert_find(HT_A_CONFIRM);
                    990:        HTAnchor * redirection = HTRequest_redirection(request);
                    991:        if (prompt && redirection) {
1.125     frystyk   992:            if (me->state == HT_LOAD_SOURCE) {
1.124     frystyk   993:                if ((*prompt)(request, HT_A_CONFIRM, HT_MSG_SOURCE_MOVED,
                    994:                              NULL, NULL, NULL) == YES) {
                    995:                    me->source = HTAnchor_parent(redirection);
1.128     frystyk   996:                } else {
                    997:                    /*
                    998:                    ** Make sure that the operation stops 
                    999:                    */
                   1000:                    me->state = HT_ABORT_SAVE;
1.124     frystyk  1001:                }
                   1002:            } else {
                   1003:                if ((*prompt)(request, HT_A_CONFIRM, HT_MSG_DESTINATION_MOVED,
                   1004:                              NULL, NULL, NULL) == YES) {
                   1005:                    me->destination = redirection;
1.128     frystyk  1006:                } else {
                   1007:                    /*
                   1008:                    ** Make sure that the operation stops 
                   1009:                    */
                   1010:                    me->state = HT_ABORT_SAVE;
1.124     frystyk  1011:                }
                   1012:            }
                   1013:        }
1.128     frystyk  1014:        return HT_OK;
1.124     frystyk  1015:     }
                   1016: 
                   1017:     /*
1.125     frystyk  1018:     ** If we succeeded getting the source then start the PUT itself. Otherwise
                   1019:     ** cleanup the mess
1.124     frystyk  1020:     */
1.125     frystyk  1021:     if (me->state == HT_LOAD_SOURCE && status == HT_LOADED &&
                   1022:        !HTError_hasSeverity(HTRequest_error(request), ERR_INFO)) {
1.124     frystyk  1023: 
                   1024:        /* Swap the document in the anchor with the new one */
                   1025:        me->placeholder = HTAnchor_document(me->source);
                   1026:        HTAnchor_setDocument(me->source, HTChunk_data(me->document));
                   1027: 
                   1028:        /* Set up the request object */
                   1029:        HTRequest_addGnHd(request, HT_G_DATE);
                   1030:        HTRequest_setEntityAnchor(request, me->source);
                   1031:        HTRequest_setMethod(request, METHOD_PUT);
                   1032:        HTRequest_setAnchor(request, me->destination);
                   1033:        HTRequest_setOutputFormat(request, me->format);
                   1034:        HTRequest_setOutputStream(request, me->target);
                   1035: 
1.125     frystyk  1036:        /* Turn progress notifications back on */
                   1037:        HTRequest_setInternal(request, NO);
                   1038: 
1.124     frystyk  1039:        /* Add the entity callback function to provide the form data */
                   1040:        HTRequest_setPostCallback(request, HTEntity_callback);
                   1041: 
                   1042:        /* Now start the load normally */
                   1043:        me->state = launch_request(request, NO) ?
1.125     frystyk  1044:            HT_SAVE_DEST : HT_LOAD_SOURCE;
1.124     frystyk  1045: 
                   1046:        /*
                   1047:        **  By returning HT_ERROR we make sure that this is the last handler to
                   1048:        **  be called. We do this as we don't want any other filter to delete
                   1049:        **  the request object now when we have just started a new one
                   1050:        **  ourselves
                   1051:        */      
                   1052:        return HT_ERROR;
                   1053: 
                   1054:     } else {
                   1055:        HTRequest_deleteAfter(request, HTSaveFilter);
                   1056:        HTAnchor_setDocument(me->source, me->placeholder);
                   1057:        HTChunk_delete(me->document);
                   1058:        HT_FREE(me);
                   1059:     }
                   1060:     return HT_OK;
                   1061: }
                   1062: 
                   1063: /*     Send an Anchor using PUT from absolute name
                   1064: **     -------------------------------------------
                   1065: **     Upload a document referenced by an absolute URL appended.
                   1066: **     The URL can NOT contain any fragment identifier!
                   1067: **     The list of form data must be given as an association list where 
                   1068: **     the name is the field name and the value is the value of the field.
                   1069: */
                   1070: PUBLIC BOOL HTPutDocumentAbsolute (HTParentAnchor *    source,
                   1071:                                   const char *         destination,
                   1072:                                   HTRequest *          request)
                   1073: {
                   1074:     if (source && destination && request) {
                   1075:        HTAnchor * dest = HTAnchor_findAddress(destination);
                   1076:        return HTPutDocumentAnchor(source, dest, request);
                   1077:     }
                   1078:     return NO;
                   1079: }
                   1080: 
                   1081: /*     Send an Anchor using PUT from relative name
                   1082: **     -------------------------------------------
                   1083: **     Upload a document referenced by a relative URL appended.
                   1084: **     The URL can NOT contain any fragment identifier!
                   1085: **     The list of form data must be given as an association list where 
                   1086: **     the name is the field name and the value is the value of the field.
                   1087: */
                   1088: PUBLIC BOOL HTPutDocumentRelative (HTParentAnchor *    source,
                   1089:                                   const char *         relative,
                   1090:                                   HTParentAnchor *     destination_base,
                   1091:                                   HTRequest *          request)
                   1092: {
                   1093:     if (source && relative && destination_base && request) {
                   1094:        BOOL status;
                   1095:        char * full_url = NULL;
                   1096:        char * base_url = HTAnchor_address((HTAnchor *) destination_base);
                   1097:        full_url=HTParse(relative, base_url,
                   1098:                         PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
                   1099:        status = HTPutDocumentAbsolute(source, full_url, request);
                   1100:        HT_FREE(full_url);
                   1101:        HT_FREE(base_url);
                   1102:        return status;
                   1103:     }
                   1104:     return NO;
                   1105: }
                   1106: 
                   1107: /*     Send an Anchor using PUT from an anchor
                   1108: **     ---------------------------------------
                   1109: **     Upload a document referenced by an anchor object appended
                   1110: **     The URL can NOT contain any fragment identifier!
                   1111: **     The source document is first loaded into memory and then the PUT
                   1112: **     to the remote server is done using the memory version
                   1113: */
                   1114: PUBLIC BOOL HTPutDocumentAnchor (HTParentAnchor *      source,
                   1115:                                 HTAnchor *             destination,
                   1116:                                 HTRequest *            request)
                   1117: {
                   1118:     HTParentAnchor * dest = HTAnchor_parent(destination);
                   1119:     if (source && dest && request) {
1.131   ! frystyk  1120:        if (setup_anchors(request, source, dest, METHOD_PUT) == YES) {
1.124     frystyk  1121:            HTPutContext * context = NULL;
                   1122: 
                   1123:            /*
                   1124:            **  First we register an AFTER filter that can check the result
                   1125:            **  of the source load if success then it can start the PUT
                   1126:            ** operation to the destination.
                   1127:            */
                   1128:            if (!(context=(HTPutContext *) HT_CALLOC(1, sizeof(HTPutContext))))
                   1129:                HT_OUTOFMEM("HTPutDocumentAnchor");
                   1130:            context->source = source;
                   1131:            context->destination = destination;
                   1132:            HTRequest_addAfter(request, HTSaveFilter, context, HT_ALL, NO);
1.125     frystyk  1133: 
                   1134:            /* Turn off progress notifications */
                   1135:            HTRequest_setInternal(request, YES);
1.124     frystyk  1136: 
                   1137:            /*
                   1138:            **  We make sure that we are not using a memory cached element.
                   1139:            **  It's OK to use a file cached element!
                   1140:            */
                   1141:            HTRequest_setReloadMode(request, HT_MEM_REFRESH);
                   1142: 
                   1143:            /*
                   1144:            ** Now we load the source document into a chunk. We specify that
                   1145:            ** we want the document ASIS from the source location. 
                   1146:            */
                   1147:            context->format = HTRequest_outputFormat(request);
                   1148:            context->target = HTRequest_outputStream(request);
                   1149:            HTRequest_setOutputFormat(request, WWW_SOURCE);
                   1150:            context->document = HTLoadAnchorToChunk((HTAnchor*)source,request);
                   1151:            if (context->document == NULL) {
                   1152:                if (APP_TRACE) HTTrace("Put Document No source\n");
                   1153:                HT_FREE(context);
                   1154:                return NO;
                   1155:            }
                   1156:            return YES;
                   1157:        }
                   1158:     }
                   1159:     return NO;
                   1160: }
                   1161: 
                   1162: /* ------------------------------------------------------------------------- */
1.70      frystyk  1163: 
1.90      frystyk  1164: /*     Copy an anchor
1.70      frystyk  1165: **     --------------
1.90      frystyk  1166: **     Fetch the URL (possibly local file URL) and send it using either PUT
                   1167: **     or POST to the remote destination using HTTP. The caller can decide the
                   1168: **     exact method used and which HTTP header fields to transmit by setting
                   1169: **     the user fields in the request structure.
1.92      frystyk  1170: **     If posting to NNTP then we can't dispatch at this level but must pass
                   1171: **     the source anchor to the news module that then takes all the refs
                   1172: **     to NNTP and puts into the "newsgroups" header
1.70      frystyk  1173: */
1.109     frystyk  1174: PUBLIC BOOL HTCopyAnchor (HTAnchor * src_anchor, HTRequest * main_dest)
1.80      frystyk  1175: { 
1.106     frystyk  1176:     HTRequest * src_req;
                   1177:     HTList * cur;
1.109     frystyk  1178:     if (!src_anchor || !main_dest) {
1.115     eric     1179:        if (WWWTRACE) HTTrace("Copy........ BAD ARGUMENT\n");
1.90      frystyk  1180:        return NO;
1.109     frystyk  1181:     }
1.70      frystyk  1182: 
1.112     frystyk  1183:     /* Set the source anchor */
                   1184:     main_dest->source_anchor = HTAnchor_parent(src_anchor);
                   1185: 
1.80      frystyk  1186:     /* Build the POST web if not already there */
1.109     frystyk  1187:     if (!main_dest->source) {
                   1188:        src_req = HTRequest_dupInternal(main_dest);       /* Get a duplicate */
1.80      frystyk  1189:        HTAnchor_clearHeader((HTParentAnchor *) src_anchor);
1.109     frystyk  1190:        src_req->method = METHOD_GET;
1.85      frystyk  1191:        src_req->reload = HT_MEM_REFRESH;
1.104     frystyk  1192:        src_req->output_stream = NULL;
1.80      frystyk  1193:        src_req->output_format = WWW_SOURCE;     /* We want source (for now) */
                   1194: 
                   1195:        /* Set up the main link in the source anchor */
                   1196:        {
1.106     frystyk  1197:            HTLink * main_link = HTAnchor_mainLink((HTAnchor *) src_anchor);
                   1198:            HTAnchor *main_anchor = HTLink_destination(main_link);
                   1199:            HTMethod method = HTLink_method(main_link);
1.85      frystyk  1200:            if (!main_link || method==METHOD_INVALID) {
1.91      frystyk  1201:                if (WWWTRACE)
1.115     eric     1202:                    HTTrace("Copy Anchor. No destination found or unspecified method\n");
1.80      frystyk  1203:                HTRequest_delete(src_req);
1.90      frystyk  1204:                return NO;
1.80      frystyk  1205:            }
1.109     frystyk  1206:            main_dest->GenMask |= HT_G_DATE;             /* Send date header */
                   1207:            main_dest->reload = HT_CACHE_REFRESH;
                   1208:            main_dest->method = method;
                   1209:            main_dest->input_format = WWW_SOURCE;
                   1210:            HTRequest_addDestination(src_req, main_dest);
                   1211:            if (HTLoadAnchor(main_anchor, main_dest) == NO)
                   1212:                return NO;
1.80      frystyk  1213:        }
1.78      frystyk  1214: 
1.80      frystyk  1215:        /* For all other links in the source anchor */
1.106     frystyk  1216:        if ((cur = HTAnchor_subLinks(src_anchor))) {
                   1217:            HTLink * pres;
1.109     frystyk  1218:            while ((pres = (HTLink *) HTList_nextObject(cur))) {
1.106     frystyk  1219:                HTAnchor *dest = HTLink_destination(pres);
                   1220:                HTMethod method = HTLink_method(pres);
1.80      frystyk  1221:                HTRequest *dest_req;
                   1222:                if (!dest || method==METHOD_INVALID) {
1.91      frystyk  1223:                    if (WWWTRACE)
1.115     eric     1224:                        HTTrace("Copy Anchor. Bad anchor setup %p\n",
1.80      frystyk  1225:                                dest);
1.90      frystyk  1226:                    return NO;
1.80      frystyk  1227:                }
1.109     frystyk  1228:                dest_req = HTRequest_dupInternal(main_dest);
1.107     frystyk  1229:                dest_req->GenMask |= HT_G_DATE;          /* Send date header */
1.85      frystyk  1230:                dest_req->reload = HT_CACHE_REFRESH;
1.80      frystyk  1231:                dest_req->method = method;
1.104     frystyk  1232:                dest_req->output_stream = NULL;
                   1233:                dest_req->output_format = WWW_SOURCE;
1.109     frystyk  1234:                HTRequest_addDestination(src_req, dest_req);
1.104     frystyk  1235: 
1.90      frystyk  1236:                if (HTLoadAnchor(dest, dest_req) == NO)
                   1237:                    return NO;
1.80      frystyk  1238:            }
                   1239:        }
                   1240:     } else {                    /* Use the existing Post Web and restart it */
1.109     frystyk  1241:        src_req = main_dest->source;
1.80      frystyk  1242:        if (src_req->mainDestination)
1.124     frystyk  1243:            if (launch_request(main_dest, NO) == NO)
1.90      frystyk  1244:                return NO;
1.80      frystyk  1245:        if (src_req->destinations) {
1.106     frystyk  1246:            HTRequest * pres;
                   1247:            cur = HTAnchor_subLinks(src_anchor);
1.80      frystyk  1248:            while ((pres = (HTRequest *) HTList_nextObject(cur)) != NULL) {
1.124     frystyk  1249:                if (launch_request(pres, NO) == NO)
1.90      frystyk  1250:                    return NO;
1.80      frystyk  1251:            }
                   1252:        }
1.78      frystyk  1253:     }
                   1254: 
1.80      frystyk  1255:     /* Now open the source */
                   1256:     return HTLoadAnchor(src_anchor, src_req);
1.70      frystyk  1257: }
                   1258: 
1.90      frystyk  1259: /*     Upload an Anchor
1.70      frystyk  1260: **     ----------------
1.111     frystyk  1261: **     This function can be used to send data along with a request to a remote
                   1262: **     server. It can for example be used to POST form data to a remote HTTP
                   1263: **     server - or it can be used to post a newsletter to a NNTP server. In
                   1264: **     either case, you pass a callback function which the request calls when
                   1265: **     the remote destination is ready to accept data. In this callback
                   1266: **     you get the current request object and a stream into where you can 
                   1267: **     write data. It is very important that you return the value returned
                   1268: **     by this stream to the Library so that it knows what to do next. The
                   1269: **     reason is that the outgoing stream might block or an error may
                   1270: **     occur and in that case the Library must know about it. The source
                   1271: **     anchor represents the data object in memory and it points to 
                   1272: **     the destination anchor by using the POSTWeb method. The source anchor
                   1273: **     contains metainformation about the data object in memory and the 
                   1274: **     destination anchor represents the reponse from the remote server.
1.90      frystyk  1275: **     Returns YES if request accepted, else NO
                   1276: */
1.111     frystyk  1277: PUBLIC BOOL HTUploadAnchor (HTAnchor *         source_anchor,
                   1278:                            HTRequest *         request,
                   1279:                            HTPostCallback *    callback)
                   1280: {
                   1281:     HTLink * link = HTAnchor_mainLink((HTAnchor *) source_anchor);
                   1282:     HTAnchor * dest_anchor = HTLink_destination(link);
                   1283:     HTMethod method = HTLink_method(link);
                   1284:     if (!link || method==METHOD_INVALID || !callback) {
                   1285:        if (WWWTRACE)
1.115     eric     1286:            HTTrace("Upload...... No destination found or unspecified method\n");
1.90      frystyk  1287:        return NO;
1.109     frystyk  1288:     }
1.111     frystyk  1289:     request->GenMask |= HT_G_DATE;                      /* Send date header */
                   1290:     request->reload = HT_CACHE_REFRESH;
                   1291:     request->method = method;
                   1292:     request->source_anchor = HTAnchor_parent(source_anchor);
                   1293:     request->PostCallback = callback;
                   1294:     return HTLoadAnchor(dest_anchor, request);
                   1295: }
                   1296: 
                   1297: /*     POST Callback Handler
                   1298: **     ---------------------
                   1299: **     If you do not want to handle the stream interface on your own, you
                   1300: **     can use this function which writes the source anchor hyperdoc to the
                   1301: **     target stream for the anchor upload and also handles the return value
                   1302: **     from the stream. If you don't want to write the source anchor hyperdoc
                   1303: **     then you can register your own callback function that can get the data
                   1304: **     you want.
                   1305: */
                   1306: PUBLIC int HTUpload_callback (HTRequest * request, HTStream * target)
                   1307: {
1.115     eric     1308:     if (WWWTRACE) HTTrace("Uploading... from callback function\n");
1.111     frystyk  1309:     if (!request || !request->source_anchor || !target) return HT_ERROR;
                   1310:     {
                   1311:        int status;
                   1312:        HTParentAnchor * source = request->source_anchor;
                   1313:        char * document = (char *) HTAnchor_document(request->source_anchor);
                   1314:        int len = HTAnchor_length(source);
                   1315:        if (len < 0) {
                   1316:            len = strlen(document);
                   1317:            HTAnchor_setLength(source, len);
                   1318:        }
                   1319:        status = (*target->isa->put_block)(target, document, len);
                   1320:        if (status == HT_OK)
                   1321:            return (*target->isa->flush)(target);
                   1322:        if (status == HT_WOULD_BLOCK) {
1.115     eric     1323:            if (PROT_TRACE)HTTrace("POST Anchor. Target WOULD BLOCK\n");
1.111     frystyk  1324:            return HT_WOULD_BLOCK;
                   1325:        } else if (status == HT_PAUSE) {
1.115     eric     1326:            if (PROT_TRACE) HTTrace("POST Anchor. Target PAUSED\n");
1.111     frystyk  1327:            return HT_PAUSE;
                   1328:        } else if (status > 0) {              /* Stream specific return code */
                   1329:            if (PROT_TRACE)
1.115     eric     1330:                HTTrace("POST Anchor. Target returns %d\n", status);
1.111     frystyk  1331:            return status;
1.120     eric     1332:        } else {                                     /* we have a real error */
1.115     eric     1333:            if (PROT_TRACE) HTTrace("POST Anchor. Target ERROR\n");
1.111     frystyk  1334:            return status;
                   1335:        }
1.70      frystyk  1336:     }
1.123     frystyk  1337: }
                   1338: 
                   1339: /* ------------------------------------------------------------------------- */
                   1340: /*                             HEAD METHOD                                  */
                   1341: /* ------------------------------------------------------------------------- */
                   1342: 
                   1343: /*     Request metainformation about a document from absolute name
                   1344: **     -----------------------------------------------------------
                   1345: **     Request a document referencd by an absolute URL.
                   1346: **     Returns YES if request accepted, else NO
                   1347: */
                   1348: PUBLIC BOOL HTHeadAbsolute (const char * url, HTRequest * request)
                   1349: {
                   1350:     if (url && request) {
                   1351:        HTAnchor * anchor = HTAnchor_findAddress(url);
1.124     frystyk  1352:        return HTHeadAnchor(anchor, request);
1.123     frystyk  1353:     }
                   1354:     return NO;
                   1355: }
                   1356: 
                   1357: /*     Request metainformation about a document from relative name
                   1358: **     -----------------------------------------------------------
                   1359: **     Request a document referenced by a relative URL. The relative URL is 
                   1360: **     made absolute by resolving it relative to the address of the 'base' 
                   1361: **     anchor.
                   1362: **     Returns YES if request accepted, else NO
                   1363: */
                   1364: PUBLIC BOOL HTHeadRelative (const char *       relative,
                   1365:                            HTParentAnchor *    base,
                   1366:                            HTRequest *         request)
                   1367: {
                   1368:     BOOL status = NO;
                   1369:     if (relative && base && request) {
                   1370:        char * rel = NULL;
                   1371:        char * full_url = NULL;
                   1372:        char * base_url = HTAnchor_address((HTAnchor *) base);
                   1373:        StrAllocCopy(rel, relative);
                   1374:        full_url = HTParse(HTStrip(rel), base_url,
                   1375:                         PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
1.124     frystyk  1376:        status = HTHeadAbsolute(full_url, request);
1.123     frystyk  1377:        HT_FREE(rel);
                   1378:        HT_FREE(full_url);
                   1379:        HT_FREE(base_url);
                   1380:     }
                   1381:     return status;
                   1382: }
                   1383: 
                   1384: /*     Request metainformation about an anchor
                   1385: **     --------------------------------------
                   1386: **     Request the document referenced by the anchor
                   1387: **     Returns YES if request accepted, else NO
                   1388: */
                   1389: PUBLIC BOOL HTHeadAnchor (HTAnchor * anchor, HTRequest * request)
                   1390: {
                   1391:     if (anchor && request) {
                   1392:        HTRequest_setAnchor(request, anchor);
1.130     frystyk  1393:        HTRequest_setOutputFormat(request, WWW_DEBUG);
1.123     frystyk  1394:        HTRequest_setMethod(request, METHOD_HEAD);
1.124     frystyk  1395:        return launch_request(request, NO);
1.123     frystyk  1396:     }
                   1397:     return NO;
                   1398: }
                   1399: 
                   1400: /* ------------------------------------------------------------------------- */
                   1401: /*                             DELETE METHOD                                */
                   1402: /* ------------------------------------------------------------------------- */
                   1403: 
                   1404: /*     Delete a document on a remote server
                   1405: **     ------------------------------------
                   1406: **     Request a document referencd by an absolute URL.
                   1407: **     Returns YES if request accepted, else NO
                   1408: */
                   1409: PUBLIC BOOL HTDeleteAbsolute (const char * url, HTRequest * request)
                   1410: {
                   1411:     if (url && request) {
                   1412:        HTAnchor * anchor = HTAnchor_findAddress(url);
1.124     frystyk  1413:        return HTDeleteAnchor(anchor, request);
1.123     frystyk  1414:     }
                   1415:     return NO;
                   1416: }
                   1417: 
                   1418: /*     Request metainformation about a document from relative name
                   1419: **     -----------------------------------------------------------
                   1420: **     Request a document referenced by a relative URL. The relative URL is 
                   1421: **     made absolute by resolving it relative to the address of the 'base' 
                   1422: **     anchor.
                   1423: **     Returns YES if request accepted, else NO
                   1424: */
                   1425: PUBLIC BOOL HTDeleteRelative (const char *     relative,
                   1426:                            HTParentAnchor *    base,
                   1427:                            HTRequest *         request)
                   1428: {
                   1429:     BOOL status = NO;
                   1430:     if (relative && base && request) {
                   1431:        char * rel = NULL;
                   1432:        char * full_url = NULL;
                   1433:        char * base_url = HTAnchor_address((HTAnchor *) base);
                   1434:        StrAllocCopy(rel, relative);
                   1435:        full_url = HTParse(HTStrip(rel), base_url,
                   1436:                         PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
1.124     frystyk  1437:        status = HTDeleteAbsolute(full_url, request);
1.123     frystyk  1438:        HT_FREE(rel);
                   1439:        HT_FREE(full_url);
                   1440:        HT_FREE(base_url);
                   1441:     }
                   1442:     return status;
                   1443: }
                   1444: 
                   1445: /*     Request metainformation about an anchor
                   1446: **     --------------------------------------
                   1447: **     Request the document referenced by the anchor
                   1448: **     Returns YES if request accepted, else NO
                   1449: */
                   1450: PUBLIC BOOL HTDeleteAnchor (HTAnchor * anchor, HTRequest * request)
                   1451: {
                   1452:     if (anchor && request) {
                   1453:        HTRequest_setAnchor(request, anchor);
                   1454:        HTRequest_setMethod(request, METHOD_DELETE);
1.124     frystyk  1455:        return launch_request(request, NO);
                   1456:     }
                   1457:     return NO;
                   1458: }
                   1459: 
                   1460: /* ------------------------------------------------------------------------- */
                   1461: /*                             OPTIONS METHOD                               */
                   1462: /* ------------------------------------------------------------------------- */
                   1463: 
                   1464: /*     Options availeble for document from absolute name
                   1465: **     -------------------------------------------------
                   1466: **     Request a document referencd by an absolute URL.
                   1467: **     Returns YES if request accepted, else NO
                   1468: */
                   1469: PUBLIC BOOL HTOptionsAbsolute (const char * url, HTRequest * request)
                   1470: {
                   1471:     if (url && request) {
                   1472:        HTAnchor * anchor = HTAnchor_findAddress(url);
                   1473:        return HTOptionsAnchor(anchor, request);
                   1474:     }
                   1475:     return NO;
                   1476: }
                   1477: 
                   1478: /*     Options available for document from relative name
                   1479: **     -------------------------------------------------
                   1480: **     Request a document referenced by a relative URL. The relative URL is 
                   1481: **     made absolute by resolving it relative to the address of the 'base' 
                   1482: **     anchor.
                   1483: **     Returns YES if request accepted, else NO
                   1484: */
                   1485: PUBLIC BOOL HTOptionsRelative (const char *    relative,
                   1486:                            HTParentAnchor *    base,
                   1487:                            HTRequest *         request)
                   1488: {
                   1489:     BOOL status = NO;
                   1490:     if (relative && base && request) {
                   1491:        char * rel = NULL;
                   1492:        char * full_url = NULL;
                   1493:        char * base_url = HTAnchor_address((HTAnchor *) base);
                   1494:        StrAllocCopy(rel, relative);
                   1495:        full_url = HTParse(HTStrip(rel), base_url,
                   1496:                         PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
                   1497:        status = HTOptionsAbsolute(full_url, request);
                   1498:        HT_FREE(rel);
                   1499:        HT_FREE(full_url);
                   1500:        HT_FREE(base_url);
                   1501:     }
                   1502:     return status;
                   1503: }
                   1504: 
                   1505: /*     Options available for document using Anchor
                   1506: **     -------------------------------------------
                   1507: **     Request the document referenced by the anchor
                   1508: **     Returns YES if request accepted, else NO
                   1509: */
                   1510: PUBLIC BOOL HTOptionsAnchor (HTAnchor * anchor, HTRequest * request)
                   1511: {
                   1512:     if (anchor && request) {
                   1513:        HTRequest_setAnchor(request, anchor);
                   1514:        HTRequest_setMethod(request, METHOD_OPTIONS);
                   1515:        return launch_request(request, NO);
1.123     frystyk  1516:     }
                   1517:     return NO;
1.1       timbl    1518: }
1.127     frystyk  1519: 
                   1520: /* ------------------------------------------------------------------------- */
                   1521: /*                             TRACE METHOD                                 */
                   1522: /* ------------------------------------------------------------------------- */
                   1523: 
                   1524: /*     Traces available for document from absolute name
                   1525: **     ------------------------------------------------
                   1526: **     Request a document referencd by an absolute URL.
                   1527: **     Returns YES if request accepted, else NO
                   1528: */
                   1529: PUBLIC BOOL HTTraceAbsolute (const char * url, HTRequest * request)
                   1530: {
                   1531:     if (url && request) {
                   1532:        HTAnchor * anchor = HTAnchor_findAddress(url);
                   1533:        return HTTraceAnchor(anchor, request);
                   1534:     }
                   1535:     return NO;
                   1536: }
                   1537: 
                   1538: /*     Traces available for document from relative name
                   1539: **     ------------------------------------------------
                   1540: **     Request a document referenced by a relative URL. The relative URL is 
                   1541: **     made absolute by resolving it relative to the address of the 'base' 
                   1542: **     anchor.
                   1543: **     Returns YES if request accepted, else NO
                   1544: */
                   1545: PUBLIC BOOL HTTraceRelative (const char *      relative,
                   1546:                             HTParentAnchor *   base,
                   1547:                             HTRequest *        request)
                   1548: {
                   1549:     BOOL status = NO;
                   1550:     if (relative && base && request) {
                   1551:        char * rel = NULL;
                   1552:        char * full_url = NULL;
                   1553:        char * base_url = HTAnchor_address((HTAnchor *) base);
                   1554:        StrAllocCopy(rel, relative);
                   1555:        full_url = HTParse(HTStrip(rel), base_url,
                   1556:                         PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
                   1557:        status = HTTraceAbsolute(full_url, request);
                   1558:        HT_FREE(rel);
                   1559:        HT_FREE(full_url);
                   1560:        HT_FREE(base_url);
                   1561:     }
                   1562:     return status;
                   1563: }
                   1564: 
                   1565: /*     Trace available for document using Anchor
                   1566: **     -------------------------------------------
                   1567: **     Request the document referenced by the anchor
                   1568: **     Returns YES if request accepted, else NO
                   1569: */
                   1570: PUBLIC BOOL HTTraceAnchor (HTAnchor * anchor, HTRequest * request)
                   1571: {
                   1572:     if (anchor && request) {
                   1573:        HTRequest_setAnchor(request, anchor);
                   1574:        HTRequest_setMethod(request, METHOD_TRACE);
                   1575:        return launch_request(request, NO);
                   1576:     }
                   1577:     return NO;
                   1578: }
                   1579: 

Webmaster