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