Annotation of libwww/Library/src/HTAccess.c, revision 1.123
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.123 ! frystyk 6: ** @(#) $Id: HTAccess.c,v 1.122 1996/06/28 16:30:48 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.123 ! frystyk 41: /* --------------------------------------------------------------------------*/
! 42: /* THE GET METHOD */
! 43: /* --------------------------------------------------------------------------*/
1.33 luotonen 44:
1.90 frystyk 45: /* Request a document
46: ** -----------------
47: ** Private version that requests a document from the request manager
48: ** Returns YES if request accepted, else NO
1.88 frystyk 49: */
1.101 frystyk 50: PRIVATE BOOL HTLoadDocument (HTRequest * request, BOOL recursive)
1.88 frystyk 51: {
52: if (PROT_TRACE) {
1.90 frystyk 53: HTParentAnchor *anchor = HTRequest_anchor(request);
54: char * full_address = HTAnchor_address((HTAnchor *) anchor);
1.115 eric 55: HTTrace("HTAccess.... Accessing document %s\n", full_address);
1.114 frystyk 56: HT_FREE(full_address);
1.88 frystyk 57: }
1.96 frystyk 58: return HTLoad(request, recursive);
1.58 frystyk 59: }
1.1 timbl 60:
1.90 frystyk 61: /* Request a document from absolute name
62: ** -------------------------------------
63: ** Request a document referencd by an absolute URL.
64: ** Returns YES if request accepted, else NO
65: */
1.122 frystyk 66: PUBLIC BOOL HTLoadAbsolute (const char * url, HTRequest * request)
1.90 frystyk 67: {
68: if (url && request) {
69: HTAnchor * anchor = HTAnchor_findAddress(url);
70: HTRequest_setAnchor(request, anchor);
71: return HTLoadDocument(request, NO);
72: }
73: return NO;
74: }
75:
1.123 ! frystyk 76: /* Request a document from relative name
! 77: ** -------------------------------------
! 78: ** Request a document referenced by a relative URL. The relative URL is
! 79: ** made absolute by resolving it relative to the address of the 'base'
! 80: ** anchor.
! 81: ** Returns YES if request accepted, else NO
! 82: */
! 83: PUBLIC BOOL HTLoadRelative (const char * relative,
! 84: HTParentAnchor * base,
! 85: HTRequest * request)
! 86: {
! 87: BOOL status = NO;
! 88: if (relative && base && request) {
! 89: char * full_url = NULL;
! 90: char * base_url = HTAnchor_address((HTAnchor *) base);
! 91: full_url = HTParse(relative, base_url,
! 92: PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
! 93: status = HTLoadAbsolute(full_url, request);
! 94: HT_FREE(full_url);
! 95: HT_FREE(base_url);
! 96: }
! 97: return status;
! 98: }
1.90 frystyk 99:
100: /* Request a document from absolute name to stream
101: ** -----------------------------------------------
102: ** Request a document referencd by an absolute URL and sending the data
1.123 ! frystyk 103: ** down a stream.
! 104: ** Returns YES if request accepted, else NO
! 105: */
! 106: PUBLIC BOOL HTLoadToStream (const char * url, HTStream * output,
! 107: HTRequest * request)
! 108: {
! 109: if (url && output && request) {
! 110: HTRequest_setOutputStream(request, output);
! 111: return HTLoadAbsolute(url, request);
! 112: }
! 113: return NO;
! 114: }
! 115:
! 116: /* Load a document and save it ASIS in a local file
! 117: ** ------------------------------------------------
1.90 frystyk 118: ** Returns YES if request accepted, else NO
119: */
1.123 ! frystyk 120: PUBLIC BOOL HTLoadToFile (const char * url, HTRequest * request,
! 121: const char * filename)
1.90 frystyk 122: {
1.123 ! frystyk 123: if (url && filename && request) {
! 124: FILE * fp = NULL;
! 125:
! 126: /* Check if file exists. If so then ask user if we can replace it */
! 127: if (access(filename, F_OK) != -1) {
! 128: HTAlertCallback * prompt = HTAlert_find(HT_A_CONFIRM);
! 129: if (prompt) {
! 130: if ((*prompt)(request, HT_A_CONFIRM, HT_MSG_FILE_REPLACE, NULL,
! 131: NULL, NULL) != YES)
! 132: return NO;
! 133: }
! 134: }
! 135:
! 136: /* If replace then open the file */
! 137: if ((fp = fopen(filename, "wb")) == NULL) {
! 138: HTRequest_addError(request, ERR_NON_FATAL, NO, HTERR_NO_FILE,
! 139: (char *) filename, strlen(filename),
! 140: "HTLoadToFile");
! 141: return NO;
! 142: }
! 143:
! 144: /* Set the output stream and start the request */
! 145: HTRequest_setOutputFormat(request, WWW_SOURCE);
! 146: HTRequest_setOutputStream(request, HTFWriter_new(request, fp, NO));
! 147: return HTLoadAbsolute(url, request);
! 148: }
! 149: return NO;
1.90 frystyk 150: }
151:
1.122 frystyk 152: /*
153: ** Load a URL to a mem buffer
154: ** --------------------------
155: ** Load a request and store the result in a memory buffer.
156: ** Returns chunk if OK - else NULL
157: */
158: PUBLIC HTChunk * HTLoadToChunk (const char * url, HTRequest * request)
159: {
160: if (url && request) {
161: HTChunk * chunk = NULL;
162: HTStream * target = HTStreamToChunk(request, &chunk, 0);
163: HTAnchor * anchor = HTAnchor_findAddress(url);
164: HTRequest_setAnchor(request, anchor);
165: HTRequest_setOutputStream(request, target);
166: if (HTLoadDocument(request, NO) == YES)
167: return chunk;
168: else {
169: HTChunk_delete(chunk);
170: return NULL;
171: }
172: }
173: return NULL;
174: }
1.90 frystyk 175:
176: /* Request an anchor
177: ** -----------------
178: ** Request the document referenced by the anchor
179: ** Returns YES if request accepted, else NO
180: */
181: PUBLIC BOOL HTLoadAnchor (HTAnchor * anchor, HTRequest * request)
182: {
183: if (anchor && request) {
184: HTRequest_setAnchor(request, anchor);
185: return HTLoadDocument(request, NO);
186: }
187: return NO;
188: }
189:
190: /* Request an anchor
191: ** -----------------
192: ** Same as HTLoadAnchor but any information in the Error Stack in the
193: ** request object is kept, so that any error messages in one
1.52 frystyk 194: ** This function is almost identical to HTLoadAnchor, but it doesn't
195: ** clear the error stack so that the information in there is kept.
1.90 frystyk 196: ** Returns YES if request accepted, else NO
197: */
198: PUBLIC BOOL HTLoadAnchorRecursive (HTAnchor * anchor, HTRequest * request)
199: {
200: if (anchor && request) {
201: HTRequest_setAnchor(request, anchor);
202: return HTLoadDocument(request, YES);
203: }
204: return NO;
205: }
206:
1.122 frystyk 207: /*
208: ** Load a URL to a mem buffer
209: ** --------------------------
210: ** Load a request and store the result in a memory buffer.
211: ** Returns chunk if OK - else NULL
212: */
213: PUBLIC HTChunk * HTLoadAnchorToChunk (HTAnchor * anchor, HTRequest * request)
214: {
215: if (anchor && request) {
216: HTChunk * chunk = NULL;
217: HTStream * target = HTStreamToChunk(request, &chunk, 0);
218: HTRequest_setAnchor(request, anchor);
219: HTRequest_setOutputStream(request, target);
220: if (HTLoadDocument(request, NO) == YES)
221: return chunk;
222: else {
223: HTChunk_delete(chunk);
224: return NULL;
225: }
226: }
227: return NULL;
228: }
1.90 frystyk 229:
1.123 ! frystyk 230: /*
! 231: ** Load a Rule File
! 232: ** ----------------
! 233: ** Load a rule find with the URL specified and add the set of rules to
! 234: ** the existing set.
! 235: */
! 236: PUBLIC BOOL HTLoadRules (const char * url)
! 237: {
! 238: BOOL status = NO;
! 239: if (url) {
! 240: HTList * list = HTList_new();
! 241: HTRequest * request = HTRequest_new();
! 242: HTRequest_setPreemptive(request, YES);
! 243: HTAlert_setInteractive(NO);
! 244: HTConversion_add(list, "application/x-www-rules", "*/*", HTRules,
! 245: 1.0, 0.0, 0.0);
! 246: HTRequest_setConversion(request, list, YES);
! 247: status = HTLoadAbsolute(url, request);
! 248: HTConversion_deleteAll(list);
! 249: HTRequest_delete(request);
! 250: }
! 251: return status;
! 252: }
! 253:
! 254: /* --------------------------------------------------------------------------*/
! 255: /* GET WITH KEYWORDS (SEARCH) */
! 256: /* --------------------------------------------------------------------------*/
! 257:
! 258: /*
! 259: ** This function creates a URL with a searh part as defined by RFC 1866
! 260: ** Both the baseurl and the keywords must be escaped.
! 261: **
! 262: ** 1. The form field names and values are escaped: space
! 263: ** characters are replaced by `+', and then reserved characters
! 264: ** are escaped as per [URL]; that is, non-alphanumeric
! 265: ** characters are replaced by `%HH', a percent sign and two
! 266: ** hexadecimal digits representing the ASCII code of the
! 267: ** character. Line breaks, as in multi-line text field values,
! 268: ** are represented as CR LF pairs, i.e. `%0D%0A'.
! 269: **
! 270: ** 2. The fields are listed in the order they appear in the
! 271: ** document with the name separated from the value by `=' and
! 272: ** the pairs separated from each other by `&'. Fields with null
! 273: ** values may be omitted. In particular, unselected radio
! 274: ** buttons and checkboxes should not appear in the encoded
! 275: ** data, but hidden fields with VALUE attributes present
! 276: ** should.
! 277: **
! 278: ** NOTE - The URI from a query form submission can be
! 279: ** used in a normal anchor style hyperlink.
! 280: ** Unfortunately, the use of the `&' character to
! 281: ** separate form fields interacts with its use in SGML
! 282: ** attribute values as an entity reference delimiter.
! 283: ** For example, the URI `http://host/?x=1&y=2' must be
! 284: ** written `<a href="http://host/?x=1&y=2"' or `<a
! 285: ** href="http://host/?x=1&y=2">'.
! 286: **
! 287: ** HTTP server implementors, and in particular, CGI
! 288: ** implementors are encouraged to support the use of
! 289: ** `;' in place of `&' to save users the trouble of
! 290: ** escaping `&' characters this way.
! 291: */
! 292: PRIVATE char * query_url_encode (const char * baseurl, HTChunk * keywords)
! 293: {
! 294: char * fullurl = NULL;
! 295: if (baseurl && keywords && HTChunk_size(keywords)) {
! 296: int len = strlen(baseurl);
! 297: fullurl = (char *) HT_MALLOC(len + HTChunk_size(keywords) + 2);
! 298: sprintf(fullurl, "%s?%s", baseurl, HTChunk_data(keywords));
! 299: {
! 300: char * ptr = fullurl+len;
! 301: while (*ptr) {
! 302: if (*ptr == ' ') *ptr = '+';
! 303: ptr++;
! 304: }
! 305: }
! 306: }
! 307: return fullurl;
! 308: }
! 309:
! 310: PRIVATE char * form_url_encode (const char * baseurl, HTAssocList * formdata)
! 311: {
! 312: if (formdata) {
! 313: BOOL first = YES;
! 314: int cnt = HTList_count((HTList *) formdata);
! 315: HTChunk * fullurl = HTChunk_new(128);
! 316: HTAssoc * pres;
! 317: if (baseurl) HTChunk_puts(fullurl, baseurl);
! 318: while (cnt > 0) {
! 319: pres = (HTAssoc *) HTList_objectAt((HTList *) formdata, --cnt);
! 320: if (first)
! 321: first = NO;
! 322: else
! 323: HTChunk_putc(fullurl, '&'); /* Could use ';' instead */
! 324: HTChunk_puts(fullurl, HTAssoc_name(pres));
! 325: HTChunk_putc(fullurl, '=');
! 326: HTChunk_puts(fullurl, HTAssoc_value(pres));
! 327: }
! 328: return HTChunk_toCString(fullurl);
! 329: }
! 330: return NULL;
! 331: }
! 332:
! 333: /* Search a document from absolute name
! 334: ** ------------------------------------
! 335: ** Request a document referencd by an absolute URL appended with the
! 336: ** keywords given. The URL can NOT contain any fragment identifier!
! 337: ** The list of keywords must be a space-separated list and spaces will
! 338: ** be converted to '+' before the request is issued.
! 339: ** Returns YES if request accepted, else NO
! 340: */
! 341: PUBLIC BOOL HTSearchAbsolute (HTChunk * keywords,
! 342: const char * base,
! 343: HTRequest * request)
! 344: {
! 345: if (keywords && base && request) {
! 346: char * full = query_url_encode(base, keywords);
! 347: if (full) {
! 348: HTAnchor * anchor = HTAnchor_findAddress(full);
! 349: HTRequest_setAnchor(request, anchor);
! 350: HT_FREE(full);
! 351: return HTLoadDocument(request, NO);
! 352: }
! 353: }
! 354: return NO;
! 355: }
! 356:
! 357: /* Search a document from relative name
! 358: ** -------------------------------------
! 359: ** Request a document referenced by a relative URL. The relative URL is
! 360: ** made absolute by resolving it relative to the address of the 'base'
! 361: ** anchor.
! 362: ** Returns YES if request accepted, else NO
! 363: */
! 364: PUBLIC BOOL HTSearchRelative (HTChunk * keywords,
! 365: const char * relative,
! 366: HTParentAnchor * base,
! 367: HTRequest * request)
! 368: {
! 369: BOOL status = NO;
! 370: if (keywords && relative && base && request) {
! 371: char * full_url = NULL;
! 372: char * base_url = HTAnchor_address((HTAnchor *) base);
! 373: full_url = HTParse(relative, base_url,
! 374: PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
! 375: status = HTSearchAbsolute(keywords, full_url, request);
! 376: HT_FREE(full_url);
! 377: HT_FREE(base_url);
! 378: }
! 379: return status;
! 380: }
! 381:
! 382: /*
! 383: ** Search a string
! 384: ** ---------------
! 385: ** This is the same as HTSearchAbsolute but instead of using a chunk
! 386: ** you can pass a string.
! 387: */
! 388: PUBLIC BOOL HTSearchString (const char * keywords,
! 389: HTAnchor * anchor,
! 390: HTRequest * request)
! 391: {
! 392: BOOL status = NO;
! 393: if (keywords && anchor && request) {
! 394: char * base_url = HTAnchor_address((HTAnchor *) anchor);
! 395: HTChunk * chunk = HTChunk_new(strlen(keywords)+2);
! 396: HTChunk_puts(chunk, keywords);
! 397: status = HTSearchAbsolute(chunk, base_url, request);
! 398: HT_FREE(base_url);
! 399: HTChunk_delete(chunk);
! 400: }
! 401: return status;
! 402: }
! 403:
1.90 frystyk 404: /* Search an Anchor
405: ** ----------------
406: ** Performs a keyword search on word given by the user. Adds the keyword
407: ** to the end of the current address and attempts to open the new address.
408: ** The list of keywords must be a space-separated list and spaces will
409: ** be converted to '+' before the request is issued.
410: ** Search can also be performed by HTLoadAbsolute() etc.
411: ** Returns YES if request accepted, else NO
412: */
1.123 ! frystyk 413: PUBLIC BOOL HTSearchAnchor (HTChunk * keywords,
! 414: HTAnchor * anchor,
! 415: HTRequest * request)
1.90 frystyk 416: {
1.99 frystyk 417: BOOL status = NO;
1.123 ! frystyk 418: if (keywords && anchor && request) {
! 419: char * base_url = HTAnchor_address(anchor);
! 420: status = HTSearchAbsolute(keywords, base_url, request);
1.114 frystyk 421: HT_FREE(base_url);
1.90 frystyk 422: }
1.99 frystyk 423: return status;
1.2 timbl 424: }
425:
1.123 ! frystyk 426: /* --------------------------------------------------------------------------*/
! 427: /* HANDLING FORMS USING GET AND POST */
! 428: /* --------------------------------------------------------------------------*/
1.2 timbl 429:
1.123 ! frystyk 430: /* Send a Form request using GET from absolute name
! 431: ** ------------------------------------------------
1.90 frystyk 432: ** Request a document referencd by an absolute URL appended with the
1.123 ! frystyk 433: ** formdata given. The URL can NOT contain any fragment identifier!
! 434: ** The list of form data must be given as an association list where
! 435: ** the name is the field name and the value is the value of the field.
! 436: ** Returns YES if request accepted, else NO
! 437: */
! 438: PUBLIC BOOL HTGetFormAbsolute (HTAssocList * formdata,
! 439: const char * base,
! 440: HTRequest * request)
! 441: {
! 442: if (formdata && base && request) {
! 443: char * full = form_url_encode(base, formdata);
! 444: if (full) {
! 445: HTAnchor * anchor = HTAnchor_findAddress(full);
! 446: HTRequest_setAnchor(request, anchor);
! 447: HT_FREE(full);
! 448: return HTLoadDocument(request, NO);
! 449: }
! 450: }
! 451: return NO;
! 452: }
! 453:
! 454: /* Send a Form request using GET from relative name
! 455: ** ------------------------------------------------
! 456: ** Request a document referencd by a relative URL appended with the
! 457: ** formdata given. The URL can NOT contain any fragment identifier!
! 458: ** The list of form data must be given as an association list where
! 459: ** the name is the field name and the value is the value of the field.
! 460: ** Returns YES if request accepted, else NO
! 461: */
! 462: PUBLIC BOOL HTGetFormRelative (HTAssocList * formdata,
! 463: const char * relative,
! 464: HTParentAnchor * base,
! 465: HTRequest * request)
! 466: {
! 467: BOOL status = NO;
! 468: if (formdata && relative && base && request) {
! 469: char * full_url = NULL;
! 470: char * base_url = HTAnchor_address((HTAnchor *) base);
! 471: full_url=HTParse(relative, base_url,
! 472: PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
! 473: status = HTGetFormAbsolute(formdata, full_url, request);
! 474: HT_FREE(full_url);
! 475: HT_FREE(base_url);
! 476: }
! 477: return status;
! 478: }
! 479:
! 480: /* Send a Form request using GET from an anchor
! 481: ** --------------------------------------------
! 482: ** Request a document referencd by an anchor object appended with the
! 483: ** formdata given. The URL can NOT contain any fragment identifier!
! 484: ** The list of form data must be given as an association list where
! 485: ** the name is the field name and the value is the value of the field.
1.90 frystyk 486: ** Returns YES if request accepted, else NO
487: */
1.123 ! frystyk 488: PUBLIC BOOL HTGetFormAnchor (HTAssocList * formdata,
! 489: HTAnchor * anchor,
! 490: HTRequest * request)
! 491: {
! 492: BOOL status = NO;
! 493: if (formdata && anchor && request) {
! 494: char * base_url = HTAnchor_address((HTAnchor *) anchor);
! 495: status = HTGetFormAbsolute(formdata, base_url, request);
! 496: HT_FREE(base_url);
! 497: }
! 498: return status;
! 499: }
! 500:
! 501: PRIVATE int HTEntity_callback (HTRequest * request, HTStream * target)
! 502: {
! 503: HTParentAnchor * entity = HTRequest_entityAnchor(request);
! 504: if (WWWTRACE) HTTrace("Posting Form from callback function\n");
! 505: if (!request || !entity || !target) return HT_ERROR;
! 506: {
! 507: int status;
! 508: char * document = (char *) HTAnchor_document(entity);
! 509: int len = HTAnchor_length(entity);
! 510: status = (*target->isa->put_block)(target, document, len);
! 511: if (status == HT_OK)
! 512: return (*target->isa->flush)(target);
! 513: if (status == HT_WOULD_BLOCK) {
! 514: if (PROT_TRACE)HTTrace("Posting Form Target WOULD BLOCK\n");
! 515: return HT_WOULD_BLOCK;
! 516: } else if (status == HT_PAUSE) {
! 517: if (PROT_TRACE) HTTrace("Posting Form. Target PAUSED\n");
! 518: return HT_PAUSE;
! 519: } else if (status > 0) { /* Stream specific return code */
! 520: if (PROT_TRACE)
! 521: HTTrace("Posting Form. Target returns %d\n", status);
! 522: return status;
! 523: } else { /* we have a real error */
! 524: if (PROT_TRACE) HTTrace("Posting Form Target ERROR\n");
! 525: return status;
! 526: }
! 527: }
! 528: }
! 529:
! 530: /* Send a Form request using POST from absolute name
! 531: ** -------------------------------------------------
! 532: ** Request a document referencd by an absolute URL appended with the
! 533: ** formdata given. The URL can NOT contain any fragment identifier!
! 534: ** The list of form data must be given as an association list where
! 535: ** the name is the field name and the value is the value of the field.
! 536: */
! 537: PUBLIC HTParentAnchor * HTPostFormAbsolute (HTAssocList * formdata,
! 538: const char * base,
! 539: HTRequest * request)
! 540: {
! 541: if (formdata && base && request) {
! 542: HTAnchor * anchor = HTAnchor_findAddress(base);
! 543: return HTPostFormAnchor(formdata, anchor, request);
! 544: }
! 545: return NULL;
! 546: }
! 547:
! 548: /* Send a Form request using POST from relative name
! 549: ** -------------------------------------------------
! 550: ** Request a document referencd by a relative 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 * HTPostFormRelative (HTAssocList * formdata,
! 556: const char * relative,
! 557: HTParentAnchor * base,
! 558: HTRequest * request)
1.90 frystyk 559: {
1.123 ! frystyk 560: HTParentAnchor * postanchor = NULL;
! 561: if (formdata && relative && base && request) {
! 562: char * full_url = NULL;
! 563: char * base_url = HTAnchor_address((HTAnchor *) base);
! 564: full_url=HTParse(relative, base_url,
! 565: PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
! 566: postanchor = HTPostFormAbsolute(formdata, full_url, request);
! 567: HT_FREE(full_url);
! 568: HT_FREE(base_url);
! 569: }
! 570: return postanchor;
! 571: }
! 572:
! 573: /* Send a Form request using POST from an anchor
! 574: ** ---------------------------------------------
! 575: ** Request a document referencd by an anchor object appended with the
! 576: ** formdata given. The URL can NOT contain any fragment identifier!
! 577: ** The list of form data must be given as an association list where
! 578: ** the name is the field name and the value is the value of the field.
! 579: */
! 580: PUBLIC HTParentAnchor * HTPostFormAnchor (HTAssocList * formdata,
! 581: HTAnchor * anchor,
! 582: HTRequest * request)
! 583: {
! 584: HTParentAnchor * postanchor = NULL;
! 585: if (formdata && anchor && request) {
! 586: HTUserProfile * up = HTRequest_userProfile(request);
! 587: char * tmpfile = HTGetTmpFileName(HTUserProfile_tmp(up));
! 588: char * tmpurl = HTParse(tmpfile, "file:", PARSE_ALL);
! 589: char * form_encoded = form_url_encode(NULL, formdata);
! 590: if (form_encoded) {
! 591:
! 592: /*
! 593: ** Now create a new anchor for the post data and set up
! 594: ** the rest of the metainformation we know about this anchor. The
! 595: ** tmp anchor may actually already exist from previous postings.
! 596: */
! 597: postanchor = (HTParentAnchor *) HTAnchor_findAddress(tmpurl);
! 598: HTAnchor_clearHeader(postanchor);
! 599: HTAnchor_setDocument(postanchor, form_encoded);
! 600: HTAnchor_setLength(postanchor, strlen(form_encoded));
! 601: HTAnchor_setFormat(postanchor, WWW_FORM);
! 602:
! 603: /*
! 604: ** Bind the temporary anchor to the anchor that will contain the
! 605: ** response
! 606: */
! 607: HTLink_removeAll((HTAnchor *) postanchor);
! 608: HTLink_add((HTAnchor *) postanchor, (HTAnchor *) anchor,
! 609: NULL, METHOD_POST);
! 610:
! 611: /* Set up the request object */
! 612: HTRequest_addGnHd(request, HT_G_DATE); /* Send date header */
! 613: HTRequest_setAnchor(request, anchor);
! 614: HTRequest_setEntityAnchor(request, postanchor);
! 615: HTRequest_setMethod(request, METHOD_POST);
! 616:
! 617: /* Add the post form callback function to provide the form data */
! 618: HTRequest_setPostCallback(request, HTEntity_callback);
! 619:
! 620: /* Now start the load normally */
! 621: HTLoadDocument(request, NO);
! 622: }
! 623: HT_FREE(tmpfile);
! 624: HT_FREE(tmpurl);
1.90 frystyk 625: }
1.123 ! frystyk 626: return postanchor;
1.57 howcome 627: }
628:
1.70 frystyk 629: /* --------------------------------------------------------------------------*/
1.123 ! frystyk 630: /* PUT AND POST METHODS */
1.70 frystyk 631: /* --------------------------------------------------------------------------*/
632:
1.90 frystyk 633: /* Copy an anchor
1.70 frystyk 634: ** --------------
1.90 frystyk 635: ** Fetch the URL (possibly local file URL) and send it using either PUT
636: ** or POST to the remote destination using HTTP. The caller can decide the
637: ** exact method used and which HTTP header fields to transmit by setting
638: ** the user fields in the request structure.
1.92 frystyk 639: ** If posting to NNTP then we can't dispatch at this level but must pass
640: ** the source anchor to the news module that then takes all the refs
641: ** to NNTP and puts into the "newsgroups" header
1.70 frystyk 642: */
1.109 frystyk 643: PUBLIC BOOL HTCopyAnchor (HTAnchor * src_anchor, HTRequest * main_dest)
1.80 frystyk 644: {
1.106 frystyk 645: HTRequest * src_req;
646: HTList * cur;
1.109 frystyk 647: if (!src_anchor || !main_dest) {
1.115 eric 648: if (WWWTRACE) HTTrace("Copy........ BAD ARGUMENT\n");
1.90 frystyk 649: return NO;
1.109 frystyk 650: }
1.70 frystyk 651:
1.112 frystyk 652: /* Set the source anchor */
653: main_dest->source_anchor = HTAnchor_parent(src_anchor);
654:
1.80 frystyk 655: /* Build the POST web if not already there */
1.109 frystyk 656: if (!main_dest->source) {
657: src_req = HTRequest_dupInternal(main_dest); /* Get a duplicate */
1.80 frystyk 658: HTAnchor_clearHeader((HTParentAnchor *) src_anchor);
1.109 frystyk 659: src_req->method = METHOD_GET;
1.85 frystyk 660: src_req->reload = HT_MEM_REFRESH;
1.104 frystyk 661: src_req->output_stream = NULL;
1.80 frystyk 662: src_req->output_format = WWW_SOURCE; /* We want source (for now) */
663:
664: /* Set up the main link in the source anchor */
665: {
1.106 frystyk 666: HTLink * main_link = HTAnchor_mainLink((HTAnchor *) src_anchor);
667: HTAnchor *main_anchor = HTLink_destination(main_link);
668: HTMethod method = HTLink_method(main_link);
1.85 frystyk 669: if (!main_link || method==METHOD_INVALID) {
1.91 frystyk 670: if (WWWTRACE)
1.115 eric 671: HTTrace("Copy Anchor. No destination found or unspecified method\n");
1.80 frystyk 672: HTRequest_delete(src_req);
1.90 frystyk 673: return NO;
1.80 frystyk 674: }
1.109 frystyk 675: main_dest->GenMask |= HT_G_DATE; /* Send date header */
676: main_dest->reload = HT_CACHE_REFRESH;
677: main_dest->method = method;
678: main_dest->input_format = WWW_SOURCE;
679: HTRequest_addDestination(src_req, main_dest);
680: if (HTLoadAnchor(main_anchor, main_dest) == NO)
681: return NO;
1.80 frystyk 682: }
1.78 frystyk 683:
1.80 frystyk 684: /* For all other links in the source anchor */
1.106 frystyk 685: if ((cur = HTAnchor_subLinks(src_anchor))) {
686: HTLink * pres;
1.109 frystyk 687: while ((pres = (HTLink *) HTList_nextObject(cur))) {
1.106 frystyk 688: HTAnchor *dest = HTLink_destination(pres);
689: HTMethod method = HTLink_method(pres);
1.80 frystyk 690: HTRequest *dest_req;
691: if (!dest || method==METHOD_INVALID) {
1.91 frystyk 692: if (WWWTRACE)
1.115 eric 693: HTTrace("Copy Anchor. Bad anchor setup %p\n",
1.80 frystyk 694: dest);
1.90 frystyk 695: return NO;
1.80 frystyk 696: }
1.109 frystyk 697: dest_req = HTRequest_dupInternal(main_dest);
1.107 frystyk 698: dest_req->GenMask |= HT_G_DATE; /* Send date header */
1.85 frystyk 699: dest_req->reload = HT_CACHE_REFRESH;
1.80 frystyk 700: dest_req->method = method;
1.104 frystyk 701: dest_req->output_stream = NULL;
702: dest_req->output_format = WWW_SOURCE;
1.109 frystyk 703: HTRequest_addDestination(src_req, dest_req);
1.104 frystyk 704:
1.90 frystyk 705: if (HTLoadAnchor(dest, dest_req) == NO)
706: return NO;
1.80 frystyk 707: }
708: }
709: } else { /* Use the existing Post Web and restart it */
1.109 frystyk 710: src_req = main_dest->source;
1.80 frystyk 711: if (src_req->mainDestination)
1.109 frystyk 712: if (HTLoadDocument(main_dest, NO) == NO)
1.90 frystyk 713: return NO;
1.80 frystyk 714: if (src_req->destinations) {
1.106 frystyk 715: HTRequest * pres;
716: cur = HTAnchor_subLinks(src_anchor);
1.80 frystyk 717: while ((pres = (HTRequest *) HTList_nextObject(cur)) != NULL) {
1.90 frystyk 718: if (HTLoadDocument(pres, NO) == NO)
719: return NO;
1.80 frystyk 720: }
721: }
1.78 frystyk 722: }
723:
1.80 frystyk 724: /* Now open the source */
725: return HTLoadAnchor(src_anchor, src_req);
1.70 frystyk 726: }
727:
1.90 frystyk 728: /* Upload an Anchor
1.70 frystyk 729: ** ----------------
1.111 frystyk 730: ** This function can be used to send data along with a request to a remote
731: ** server. It can for example be used to POST form data to a remote HTTP
732: ** server - or it can be used to post a newsletter to a NNTP server. In
733: ** either case, you pass a callback function which the request calls when
734: ** the remote destination is ready to accept data. In this callback
735: ** you get the current request object and a stream into where you can
736: ** write data. It is very important that you return the value returned
737: ** by this stream to the Library so that it knows what to do next. The
738: ** reason is that the outgoing stream might block or an error may
739: ** occur and in that case the Library must know about it. The source
740: ** anchor represents the data object in memory and it points to
741: ** the destination anchor by using the POSTWeb method. The source anchor
742: ** contains metainformation about the data object in memory and the
743: ** destination anchor represents the reponse from the remote server.
1.90 frystyk 744: ** Returns YES if request accepted, else NO
745: */
1.111 frystyk 746: PUBLIC BOOL HTUploadAnchor (HTAnchor * source_anchor,
747: HTRequest * request,
748: HTPostCallback * callback)
749: {
750: HTLink * link = HTAnchor_mainLink((HTAnchor *) source_anchor);
751: HTAnchor * dest_anchor = HTLink_destination(link);
752: HTMethod method = HTLink_method(link);
753: if (!link || method==METHOD_INVALID || !callback) {
754: if (WWWTRACE)
1.115 eric 755: HTTrace("Upload...... No destination found or unspecified method\n");
1.90 frystyk 756: return NO;
1.109 frystyk 757: }
1.111 frystyk 758: request->GenMask |= HT_G_DATE; /* Send date header */
759: request->reload = HT_CACHE_REFRESH;
760: request->method = method;
761: request->source_anchor = HTAnchor_parent(source_anchor);
762: request->PostCallback = callback;
763: return HTLoadAnchor(dest_anchor, request);
764: }
765:
766: /* POST Callback Handler
767: ** ---------------------
768: ** If you do not want to handle the stream interface on your own, you
769: ** can use this function which writes the source anchor hyperdoc to the
770: ** target stream for the anchor upload and also handles the return value
771: ** from the stream. If you don't want to write the source anchor hyperdoc
772: ** then you can register your own callback function that can get the data
773: ** you want.
774: */
775: PUBLIC int HTUpload_callback (HTRequest * request, HTStream * target)
776: {
1.115 eric 777: if (WWWTRACE) HTTrace("Uploading... from callback function\n");
1.111 frystyk 778: if (!request || !request->source_anchor || !target) return HT_ERROR;
779: {
780: int status;
781: HTParentAnchor * source = request->source_anchor;
782: char * document = (char *) HTAnchor_document(request->source_anchor);
783: int len = HTAnchor_length(source);
784: if (len < 0) {
785: len = strlen(document);
786: HTAnchor_setLength(source, len);
787: }
788: status = (*target->isa->put_block)(target, document, len);
789: if (status == HT_OK)
790: return (*target->isa->flush)(target);
791: if (status == HT_WOULD_BLOCK) {
1.115 eric 792: if (PROT_TRACE)HTTrace("POST Anchor. Target WOULD BLOCK\n");
1.111 frystyk 793: return HT_WOULD_BLOCK;
794: } else if (status == HT_PAUSE) {
1.115 eric 795: if (PROT_TRACE) HTTrace("POST Anchor. Target PAUSED\n");
1.111 frystyk 796: return HT_PAUSE;
797: } else if (status > 0) { /* Stream specific return code */
798: if (PROT_TRACE)
1.115 eric 799: HTTrace("POST Anchor. Target returns %d\n", status);
1.111 frystyk 800: return status;
1.120 eric 801: } else { /* we have a real error */
1.115 eric 802: if (PROT_TRACE) HTTrace("POST Anchor. Target ERROR\n");
1.111 frystyk 803: return status;
804: }
1.70 frystyk 805: }
1.123 ! frystyk 806: }
! 807:
! 808: /* ------------------------------------------------------------------------- */
! 809: /* HEAD METHOD */
! 810: /* ------------------------------------------------------------------------- */
! 811:
! 812: /* Request metainformation about a document from absolute name
! 813: ** -----------------------------------------------------------
! 814: ** Request a document referencd by an absolute URL.
! 815: ** Returns YES if request accepted, else NO
! 816: */
! 817: PUBLIC BOOL HTHeadAbsolute (const char * url, HTRequest * request)
! 818: {
! 819: if (url && request) {
! 820: HTAnchor * anchor = HTAnchor_findAddress(url);
! 821: HTRequest_setAnchor(request, anchor);
! 822: HTRequest_setMethod(request, METHOD_HEAD);
! 823: return HTLoadDocument(request, NO);
! 824: }
! 825: return NO;
! 826: }
! 827:
! 828: /* Request metainformation about a document from relative name
! 829: ** -----------------------------------------------------------
! 830: ** Request a document referenced by a relative URL. The relative URL is
! 831: ** made absolute by resolving it relative to the address of the 'base'
! 832: ** anchor.
! 833: ** Returns YES if request accepted, else NO
! 834: */
! 835: PUBLIC BOOL HTHeadRelative (const char * relative,
! 836: HTParentAnchor * base,
! 837: HTRequest * request)
! 838: {
! 839: BOOL status = NO;
! 840: if (relative && base && request) {
! 841: char * rel = NULL;
! 842: char * full_url = NULL;
! 843: char * base_url = HTAnchor_address((HTAnchor *) base);
! 844: StrAllocCopy(rel, relative);
! 845: full_url = HTParse(HTStrip(rel), base_url,
! 846: PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
! 847: HTRequest_setMethod(request, METHOD_HEAD);
! 848: status = HTLoadAbsolute(full_url, request);
! 849: HT_FREE(rel);
! 850: HT_FREE(full_url);
! 851: HT_FREE(base_url);
! 852: }
! 853: return status;
! 854: }
! 855:
! 856: /* Request metainformation about an anchor
! 857: ** --------------------------------------
! 858: ** Request the document referenced by the anchor
! 859: ** Returns YES if request accepted, else NO
! 860: */
! 861: PUBLIC BOOL HTHeadAnchor (HTAnchor * anchor, HTRequest * request)
! 862: {
! 863: if (anchor && request) {
! 864: HTRequest_setAnchor(request, anchor);
! 865: HTRequest_setMethod(request, METHOD_HEAD);
! 866: return HTLoadDocument(request, NO);
! 867: }
! 868: return NO;
! 869: }
! 870:
! 871: /* ------------------------------------------------------------------------- */
! 872: /* DELETE METHOD */
! 873: /* ------------------------------------------------------------------------- */
! 874:
! 875: /* Delete a document on a remote server
! 876: ** ------------------------------------
! 877: ** Request a document referencd by an absolute URL.
! 878: ** Returns YES if request accepted, else NO
! 879: */
! 880: PUBLIC BOOL HTDeleteAbsolute (const char * url, HTRequest * request)
! 881: {
! 882: if (url && request) {
! 883: HTAnchor * anchor = HTAnchor_findAddress(url);
! 884: HTRequest_setAnchor(request, anchor);
! 885: HTRequest_setMethod(request, METHOD_DELETE);
! 886: return HTLoadDocument(request, NO);
! 887: }
! 888: return NO;
! 889: }
! 890:
! 891: /* Request metainformation about a document from relative name
! 892: ** -----------------------------------------------------------
! 893: ** Request a document referenced by a relative URL. The relative URL is
! 894: ** made absolute by resolving it relative to the address of the 'base'
! 895: ** anchor.
! 896: ** Returns YES if request accepted, else NO
! 897: */
! 898: PUBLIC BOOL HTDeleteRelative (const char * relative,
! 899: HTParentAnchor * base,
! 900: HTRequest * request)
! 901: {
! 902: BOOL status = NO;
! 903: if (relative && base && request) {
! 904: char * rel = NULL;
! 905: char * full_url = NULL;
! 906: char * base_url = HTAnchor_address((HTAnchor *) base);
! 907: StrAllocCopy(rel, relative);
! 908: full_url = HTParse(HTStrip(rel), base_url,
! 909: PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
! 910: HTRequest_setMethod(request, METHOD_DELETE);
! 911: status = HTLoadAbsolute(full_url, request);
! 912: HT_FREE(rel);
! 913: HT_FREE(full_url);
! 914: HT_FREE(base_url);
! 915: }
! 916: return status;
! 917: }
! 918:
! 919: /* Request metainformation about an anchor
! 920: ** --------------------------------------
! 921: ** Request the document referenced by the anchor
! 922: ** Returns YES if request accepted, else NO
! 923: */
! 924: PUBLIC BOOL HTDeleteAnchor (HTAnchor * anchor, HTRequest * request)
! 925: {
! 926: if (anchor && request) {
! 927: HTRequest_setAnchor(request, anchor);
! 928: HTRequest_setMethod(request, METHOD_DELETE);
! 929: return HTLoadDocument(request, NO);
! 930: }
! 931: return NO;
1.1 timbl 932: }
Webmaster