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