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

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

Webmaster