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&#38;y=2"' or `<a
360 **	    href="http://host/?x=1&amp;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