Annotation of libwww/Library/src/HTAccess.c, revision 1.48
1.1 timbl 1: /* Access Manager HTAccess.c
2: ** ==============
3: **
4: ** Authors
5: ** TBL Tim Berners-Lee timbl@info.cern.ch
1.4 timbl 6: ** JFG Jean-Francois Groff jfg@dxcern.cern.ch
1.1 timbl 7: ** DD Denis DeLaRoca (310) 825-4580 <CSP1DWD@mvs.oac.ucla.edu>
8: ** History
9: ** 8 Jun 92 Telnet hopping prohibited as telnet is not secure TBL
10: ** 26 Jun 92 When over DECnet, suppressed FTP, Gopher and News. JFG
1.42 frystyk 11: ** 6 Oct 92 Moved HTClientHost and HTlogfile into here. TBL
1.1 timbl 12: ** 17 Dec 92 Tn3270 added, bug fix. DD
1.2 timbl 13: ** 4 Feb 93 Access registration, Search escapes bad chars TBL
1.9 timbl 14: ** PARAMETERS TO HTSEARCH AND HTLOADRELATIVE CHANGED
15: ** 28 May 93 WAIS gateway explicit if no WAIS library linked in.
1.19 timbl 16: ** Dec 93 Bug change around, more reentrant, etc
1.42 frystyk 17: ** 09 May 94 logfile renamed to HTlogfile to avoid clash with WAIS
1.2 timbl 18: ** Bugs
19: ** This module assumes that that the graphic object is hypertext, as it
1.9 timbl 20: ** needs to select it when it has been loaded. A superclass needs to be
1.2 timbl 21: ** defined which accepts select and select_anchor.
1.1 timbl 22: */
23:
1.9 timbl 24: #ifndef DEFAULT_WAIS_GATEWAY
1.8 timbl 25: #define DEFAULT_WAIS_GATEWAY "http://info.cern.ch:8001/"
1.9 timbl 26: #endif
1.8 timbl 27:
1.1 timbl 28: /* Implements:
29: */
30: #include "HTAccess.h"
31:
32: /* Uses:
33: */
34:
35: #include "HTParse.h"
36: #include "HTUtils.h"
1.4 timbl 37: #include "HTML.h" /* SCW */
1.2 timbl 38:
39: #ifndef NO_RULES
40: #include "HTRules.h"
41: #endif
42:
1.1 timbl 43: #include <stdio.h>
44:
1.2 timbl 45: #include "HTList.h"
46: #include "HText.h" /* See bugs above */
47: #include "HTAlert.h"
1.17 timbl 48: #include "HTFWriter.h" /* for cache stuff */
49: #include "HTTee.h"
1.46 frystyk 50: #include "HTError.h"
1.2 timbl 51:
1.1 timbl 52: /* These flags may be set to modify the operation of this module
53: */
1.34 frystyk 54: PUBLIC char * HTCacheDir = 0; /* Root for cached files or 0 for no cache */
55: PUBLIC char * HTSaveLocallyDir = SAVE_LOCALLY_HOME_DIR; /* Save & exe files */
1.1 timbl 56: PUBLIC char * HTClientHost = 0; /* Name of remote login host if any */
1.42 frystyk 57: PUBLIC FILE * HTlogfile = 0; /* File to which to output one-liners */
1.41 luotonen 58:
1.34 frystyk 59: PUBLIC BOOL HTForceReload = NO; /* Force reload from cache or net */
1.12 timbl 60: PUBLIC BOOL HTSecure = NO; /* Disable access for telnet users? */
1.27 luotonen 61: PUBLIC BOOL using_proxy = NO; /* are we using a proxy gateway? */
1.43 luotonen 62: PUBLIC char * HTImServer = NULL;/* cern_httpd sets this to the translated URL*/
1.27 luotonen 63: PUBLIC BOOL HTImProxy = NO; /* cern_httpd as a proxy? */
1.1 timbl 64:
1.43 luotonen 65:
1.2 timbl 66: /* To generate other things, play with these:
67: */
68:
1.15 timbl 69: /* PUBLIC HTFormat HTOutputFormat = NULL; use request->output_format */
70: /* PUBLIC HTStream* HTOutputStream = NULL; use request->output_stream */
1.1 timbl 71:
72: PRIVATE HTList * protocols = NULL; /* List of registered protocol descriptors */
73:
1.24 timbl 74: /* Superclass defn */
1.1 timbl 75:
1.24 timbl 76: struct _HTStream {
77: HTStreamClass * isa;
78: /* ... */
79: };
80:
1.15 timbl 81: /* Create a request structure
82: ** ---------------------------
83: */
84:
85: PUBLIC HTRequest * HTRequest_new NOARGS
86: {
1.28 luotonen 87: HTRequest * me = (HTRequest*) calloc(1, sizeof(*me)); /* zero fill */
1.15 timbl 88: if (!me) outofmem(__FILE__, "HTRequest_new()");
89:
1.20 luotonen 90: me->conversions = HTList_new(); /* No conversions registerd yet */
91: me->output_format = WWW_PRESENT; /* default it to present to user */
92:
1.15 timbl 93: return me;
94: }
95:
96:
1.20 luotonen 97: /* Delete a request structure
98: ** --------------------------
99: */
100: PUBLIC void HTRequest_delete ARGS1(HTRequest *, req)
101: {
102: if (req) {
1.34 frystyk 103: HTFormatDelete(req->conversions);
1.46 frystyk 104: HTErrorFree(req);
1.34 frystyk 105: HTAACleanup(req);
1.37 luotonen 106: FREE(req->from);
1.34 frystyk 107: FREE(req);
1.20 luotonen 108: }
109: }
110:
111:
1.22 luotonen 112: PRIVATE char * method_names[(int)MAX_METHODS + 1] =
113: {
114: "INVALID-METHOD",
115: "GET",
116: "HEAD",
117: "POST",
118: "PUT",
119: "DELETE",
120: "CHECKOUT",
121: "CHECKIN",
122: "SHOWMETHOD",
123: "LINK",
124: "UNLINK",
125: NULL
126: };
127:
128: /* Get method enum value
129: ** ---------------------
130: */
131: PUBLIC HTMethod HTMethod_enum ARGS1(char *, name)
132: {
133: if (name) {
134: int i;
135: for (i=1; i < (int)MAX_METHODS; i++)
136: if (!strcmp(name, method_names[i]))
137: return (HTMethod)i;
138: }
139: return METHOD_INVALID;
140: }
141:
142:
143: /* Get method name
144: ** ---------------
145: */
146: PUBLIC char * HTMethod_name ARGS1(HTMethod, method)
147: {
148: if ((int)method > (int)METHOD_INVALID &&
149: (int)method < (int)MAX_METHODS)
150: return method_names[(int)method];
151: else
152: return method_names[(int)METHOD_INVALID];
153: }
154:
155:
156: /* Is method in a list of method names?
157: ** -----------------------------------
158: */
159: PUBLIC BOOL HTMethod_inList ARGS2(HTMethod, method,
160: HTList *, list)
161: {
162: char * method_name = HTMethod_name(method);
163: HTList *cur = list;
164: char *item;
165:
166: while (NULL != (item = (char*)HTList_nextObject(cur))) {
167: CTRACE(stderr, " %s", item);
168: if (0==strcasecomp(item, method_name))
169: return YES;
170: }
171: return NO; /* Not found */
172: }
173:
174:
175:
176:
1.20 luotonen 177:
1.1 timbl 178: /* Register a Protocol HTRegisterProtocol
179: ** -------------------
180: */
181:
182: PUBLIC BOOL HTRegisterProtocol(protocol)
183: HTProtocol * protocol;
184: {
185: if (!protocols) protocols = HTList_new();
186: HTList_addObject(protocols, protocol);
187: return YES;
188: }
189:
190:
191: /* Register all known protocols
192: ** ----------------------------
193: **
194: ** Add to or subtract from this list if you add or remove protocol modules.
195: ** This routine is called the first time the protocol list is needed,
196: ** unless any protocols are already registered, in which case it is not called.
197: ** Therefore the application can override this list.
198: **
199: ** Compiling with NO_INIT prevents all known protocols from being forced
200: ** in at link time.
201: */
202: #ifndef NO_INIT
203: PRIVATE void HTAccessInit NOARGS /* Call me once */
204: {
1.14 duns 205: GLOBALREF HTProtocol HTTP, HTFile, HTTelnet, HTTn3270, HTRlogin;
1.1 timbl 206: #ifndef DECNET
1.14 duns 207: GLOBALREF HTProtocol HTFTP, HTNews, HTGopher;
1.42 frystyk 208:
209: /* This is the replacement when HTWhoIs gets a complete protocol module */
210: /* GLOBALREF HTProtocol HTFTP, HTNews, HTGopher, HTWhoIs; */
211: /* -------------------------------------------------------------------- */
212:
1.3 timbl 213: #ifdef DIRECT_WAIS
1.14 duns 214: GLOBALREF HTProtocol HTWAIS;
1.3 timbl 215: #endif
1.2 timbl 216: HTRegisterProtocol(&HTFTP);
217: HTRegisterProtocol(&HTNews);
218: HTRegisterProtocol(&HTGopher);
1.42 frystyk 219:
220: /* This should be added when HTWhoIs gets a complete protocol module */
221: /* HTRegisterProtocol(&HTWhoIs); */
222: /* ----------------------------------------------------------------- */
1.3 timbl 223: #ifdef DIRECT_WAIS
224: HTRegisterProtocol(&HTWAIS);
225: #endif
1.1 timbl 226: #endif
227:
1.2 timbl 228: HTRegisterProtocol(&HTTP);
229: HTRegisterProtocol(&HTFile);
230: HTRegisterProtocol(&HTTelnet);
231: HTRegisterProtocol(&HTTn3270);
232: HTRegisterProtocol(&HTRlogin);
1.1 timbl 233: }
234: #endif
235:
236:
1.33 luotonen 237:
238: /* override_proxy()
239: **
240: ** Check the no_proxy environment variable to get the list
241: ** of hosts for which proxy server is not consulted.
242: **
243: ** no_proxy is a comma- or space-separated list of machine
244: ** or domain names, with optional :port part. If no :port
245: ** part is present, it applies to all ports on that domain.
246: **
247: ** Example:
248: ** no_proxy="cern.ch,some.domain:8001"
249: **
250: */
251: PRIVATE BOOL override_proxy ARGS1(CONST char *, addr)
252: {
253: CONST char * no_proxy = getenv("no_proxy");
254: char * p = NULL;
255: char * host = NULL;
256: int port = 0;
257: int h_len = 0;
258:
259: if (!no_proxy || !addr || !(host = HTParse(addr, "", PARSE_HOST)))
260: return NO;
261: if (!*host) { free(host); return NO; }
262:
1.34 frystyk 263: if ((p = strchr(host, ':')) != NULL) { /* Port specified */
1.33 luotonen 264: *p++ = 0; /* Chop off port */
265: port = atoi(p);
266: }
267: else { /* Use default port */
268: char * access = HTParse(addr, "", PARSE_ACCESS);
269: if (access) {
270: if (!strcmp(access,"http")) port = 80;
271: else if (!strcmp(access,"gopher")) port = 70;
272: else if (!strcmp(access,"ftp")) port = 21;
273: free(access);
274: }
275: }
276: if (!port) port = 80; /* Default */
277: h_len = strlen(host);
278:
279: while (*no_proxy) {
280: CONST char * end;
281: CONST char * colon = NULL;
282: int templ_port = 0;
283: int t_len;
284:
285: while (*no_proxy && (WHITE(*no_proxy) || *no_proxy==','))
286: no_proxy++; /* Skip whitespace and separators */
287:
288: end = no_proxy;
289: while (*end && !WHITE(*end) && *end != ',') { /* Find separator */
290: if (*end==':') colon = end; /* Port number given */
291: end++;
292: }
293:
294: if (colon) {
295: templ_port = atoi(colon+1);
296: t_len = colon - no_proxy;
297: }
298: else {
299: t_len = end - no_proxy;
300: }
301:
302: if ((!templ_port || templ_port == port) &&
303: (t_len > 0 && t_len <= h_len &&
304: !strncmp(host + h_len - t_len, no_proxy, t_len))) {
305: free(host);
306: return YES;
307: }
308: if (*end) no_proxy = end+1;
309: else break;
310: }
311:
312: free(host);
313: return NO;
314: }
315:
316:
317:
1.2 timbl 318: /* Find physical name and access protocol
319: ** --------------------------------------
1.1 timbl 320: **
321: **
322: ** On entry,
323: ** addr must point to the fully qualified hypertext reference.
324: ** anchor a pareent anchor with whose address is addr
325: **
326: ** On exit,
1.2 timbl 327: ** returns HT_NO_ACCESS Error has occured.
328: ** HT_OK Success
1.1 timbl 329: **
330: */
1.21 luotonen 331: PRIVATE int get_physical ARGS1(HTRequest *, req)
332: {
1.1 timbl 333: char * access=0; /* Name of access method */
1.21 luotonen 334: char * addr = HTAnchor_address((HTAnchor*)req->anchor); /* free me */
1.27 luotonen 335:
1.35 luotonen 336: /*
337: ** This HACK is here until we have redirection implemented.
338: ** This is used when we are recursively calling HTLoad().
339: ** We then take the physical address, because currently the
340: ** virtual address is kept in a hash table so it can't be
341: ** changed -- otherwise it wouldn't be found anymore.
342: */
1.36 luotonen 343: if (HTAnchor_physical(req->anchor))
344: StrAllocCopy(addr, HTAnchor_physical(req->anchor));
1.35 luotonen 345:
1.2 timbl 346: #ifndef NO_RULES
1.47 luotonen 347: if (HTImServer) { /* cern_httpd has already done its own translations */
1.45 luotonen 348: HTAnchor_setPhysical(req->anchor, HTImServer);
1.47 luotonen 349: StrAllocCopy(addr, HTImServer); /* Oops, queries thru many proxies */
350: /* didn't work without this -- AL */
351: }
1.21 luotonen 352: else {
1.27 luotonen 353: char * physical = HTTranslate(addr);
1.21 luotonen 354: if (!physical) {
1.47 luotonen 355: free(addr);
1.21 luotonen 356: return HT_FORBIDDEN;
357: }
358: HTAnchor_setPhysical(req->anchor, physical);
359: free(physical); /* free our copy */
1.2 timbl 360: }
361: #else
1.21 luotonen 362: HTAnchor_setPhysical(req->anchor, addr);
1.2 timbl 363: #endif
364:
1.21 luotonen 365: access = HTParse(HTAnchor_physical(req->anchor),
1.27 luotonen 366: "file:", PARSE_ACCESS);
1.1 timbl 367:
368: /* Check whether gateway access has been set up for this
1.8 timbl 369: **
370: ** This function can be replaced by the rule system above.
1.1 timbl 371: */
1.8 timbl 372: #define USE_GATEWAYS
1.1 timbl 373: #ifdef USE_GATEWAYS
1.39 luotonen 374:
375: /* make sure the using_proxy variable is false */
376: using_proxy = NO;
377:
1.33 luotonen 378: if (!override_proxy(addr)) {
1.27 luotonen 379: char * gateway_parameter, *gateway, *proxy;
380:
1.2 timbl 381: gateway_parameter = (char *)malloc(strlen(access)+20);
382: if (gateway_parameter == NULL) outofmem(__FILE__, "HTLoad");
1.27 luotonen 383:
384: /* search for proxy gateways */
1.2 timbl 385: strcpy(gateway_parameter, "WWW_");
386: strcat(gateway_parameter, access);
387: strcat(gateway_parameter, "_GATEWAY");
388: gateway = (char *)getenv(gateway_parameter); /* coerce for decstation */
1.27 luotonen 389:
390: /* search for proxy servers */
391: strcpy(gateway_parameter, access);
392: strcat(gateway_parameter, "_proxy");
393: proxy = (char *)getenv(gateway_parameter);
394:
1.2 timbl 395: free(gateway_parameter);
1.27 luotonen 396:
397: if (TRACE && gateway)
398: fprintf(stderr,"Gateway found: %s\n",gateway);
399: if (TRACE && proxy)
400: fprintf(stderr,"Proxy server found: %s\n",proxy);
401:
1.8 timbl 402: #ifndef DIRECT_WAIS
1.9 timbl 403: if (!gateway && 0==strcmp(access, "wais")) {
1.8 timbl 404: gateway = DEFAULT_WAIS_GATEWAY;
405: }
406: #endif
1.27 luotonen 407:
408: /* proxy servers have precedence over gateway servers */
409: if (proxy) {
410: char * gatewayed=0;
411:
412: StrAllocCopy(gatewayed,proxy);
413: StrAllocCat(gatewayed,addr);
414: using_proxy = YES;
415: HTAnchor_setPhysical(req->anchor, gatewayed);
416: free(gatewayed);
417: free(access);
418:
419: access = HTParse(HTAnchor_physical(req->anchor),
420: "http:", PARSE_ACCESS);
421: } else if (gateway) {
1.9 timbl 422: char * path = HTParse(addr, "",
423: PARSE_HOST + PARSE_PATH + PARSE_PUNCTUATION);
424: /* Chop leading / off to make host into part of path */
425: char * gatewayed = HTParse(path+1, gateway, PARSE_ALL);
426: free(path);
1.21 luotonen 427: HTAnchor_setPhysical(req->anchor, gatewayed);
1.9 timbl 428: free(gatewayed);
1.2 timbl 429: free(access);
1.9 timbl 430:
1.21 luotonen 431: access = HTParse(HTAnchor_physical(req->anchor),
1.8 timbl 432: "http:", PARSE_ACCESS);
1.2 timbl 433: }
434: }
1.1 timbl 435: #endif
436:
1.19 timbl 437: free(addr);
1.1 timbl 438:
439:
440: /* Search registered protocols to find suitable one
441: */
442: {
1.20 luotonen 443: HTList *cur;
444: HTProtocol *p;
1.1 timbl 445: #ifndef NO_INIT
1.2 timbl 446: if (!protocols) HTAccessInit();
1.1 timbl 447: #endif
1.20 luotonen 448: cur = protocols;
449: while ((p = (HTProtocol*)HTList_nextObject(cur))) {
1.2 timbl 450: if (strcmp(p->name, access)==0) {
1.21 luotonen 451: HTAnchor_setProtocol(req->anchor, p);
1.2 timbl 452: free(access);
453: return (HT_OK);
1.1 timbl 454: }
455: }
456: }
457:
458: free(access);
1.2 timbl 459: return HT_NO_ACCESS;
1.1 timbl 460: }
461:
462:
463: /* Load a document
464: ** ---------------
465: **
1.2 timbl 466: ** This is an internal routine, which has an address AND a matching
467: ** anchor. (The public routines are called with one OR the other.)
468: **
469: ** On entry,
1.15 timbl 470: ** request->
1.35 luotonen 471: ** anchor a parent anchor with fully qualified
472: ** hypertext reference as its address set
1.15 timbl 473: ** output_format valid
474: ** output_stream valid on NULL
1.2 timbl 475: **
476: ** On exit,
477: ** returns <0 Error has occured.
478: ** HT_LOADED Success
479: ** HT_NO_DATA Success, but no document loaded.
1.8 timbl 480: ** (telnet sesssion started etc)
1.2 timbl 481: **
482: */
1.35 luotonen 483: PUBLIC int HTLoad ARGS1(HTRequest *, request)
1.2 timbl 484: {
1.25 frystyk 485: char *arg = NULL;
486: HTProtocol *p;
487: int status;
488:
1.22 luotonen 489: if (request->method == METHOD_INVALID)
490: request->method = METHOD_GET;
1.21 luotonen 491: status = get_physical(request);
1.2 timbl 492: if (status == HT_FORBIDDEN) {
1.21 luotonen 493: return HTLoadError(request, 500,
494: "Access forbidden by rule");
1.2 timbl 495: }
496: if (status < 0) return status; /* Can't resolve or forbidden */
1.25 frystyk 497:
498: if(!(arg = HTAnchor_physical(request->anchor)) || !*arg)
499: return (-1);
1.27 luotonen 500:
1.15 timbl 501: p = HTAnchor_protocol(request->anchor);
1.17 timbl 502: return (*(p->load))(request);
1.2 timbl 503: }
504:
505:
506: /* Get a save stream for a document
507: ** --------------------------------
508: */
1.19 timbl 509: PUBLIC HTStream *HTSaveStream ARGS1(HTRequest *, request)
1.15 timbl 510: {
511: HTProtocol * p;
1.19 timbl 512: int status;
1.22 luotonen 513: request->method = METHOD_PUT;
1.21 luotonen 514: status = get_physical(request);
1.19 timbl 515: if (status == HT_FORBIDDEN) {
1.21 luotonen 516: HTLoadError(request, 500,
517: "Access forbidden by rule");
1.19 timbl 518: return NULL; /* should return error status? */
519: }
520: if (status < 0) return NULL; /* @@ error. Can't resolve or forbidden */
521:
1.15 timbl 522: p = HTAnchor_protocol(request->anchor);
1.2 timbl 523: if (!p) return NULL;
524:
1.15 timbl 525: return (*p->saveStream)(request);
1.2 timbl 526:
527: }
528:
529:
530: /* Load a document - with logging etc
531: ** ----------------------------------
532: **
533: ** - Checks or documents already loaded
534: ** - Logs the access
535: ** - Allows stdin filter option
536: ** - Trace ouput and error messages
537: **
1.1 timbl 538: ** On Entry,
1.19 timbl 539: ** request->anchor valid for of the document to be accessed.
540: ** request->childAnchor optional anchor within doc to be selected
541: **
1.2 timbl 542: ** filter if YES, treat stdin as HTML
1.1 timbl 543: **
1.15 timbl 544: ** request->anchor is the node_anchor for the document
545: ** request->output_format is valid
546: **
1.1 timbl 547: ** On Exit,
548: ** returns YES Success in opening document
549: ** NO Failure
550: **
551: */
552:
1.19 timbl 553: PRIVATE BOOL HTLoadDocument ARGS1(HTRequest *, request)
1.1 timbl 554:
555: {
556: int status;
557: HText * text;
1.19 timbl 558: char * full_address = HTAnchor_address((HTAnchor*)request->anchor);
559:
1.1 timbl 560: if (TRACE) fprintf (stderr,
561: "HTAccess: loading document %s\n", full_address);
562:
1.18 timbl 563: request->using_cache = NULL;
564:
1.15 timbl 565: if (!request->output_format) request->output_format = WWW_PRESENT;
1.25 frystyk 566:
1.31 frystyk 567: if (!HTForceReload && (text=(HText *)HTAnchor_document(request->anchor)))
1.15 timbl 568: { /* Already loaded */
1.1 timbl 569: if (TRACE) fprintf(stderr, "HTAccess: Document already in memory.\n");
1.19 timbl 570: if (request->childAnchor) {
571: HText_selectAnchor(text, request->childAnchor);
572: } else {
573: HText_select(text);
574: }
575: free(full_address);
1.1 timbl 576: return YES;
577: }
1.17 timbl 578:
1.34 frystyk 579: /* Check the Cache */
580: /* Caching is ONLY done if (char*) HTCacheDir is set. Henrik 09/03-94 */
1.17 timbl 581: /* Bug: for each format, we only check whether it is ok, we
582: don't check them all and chose the best */
1.38 timbl 583: if (/* HTCacheDir && */ request->anchor->cacheItems) {
1.17 timbl 584: HTList * list = request->anchor->cacheItems;
1.20 luotonen 585: HTList * cur = list;
586: HTCacheItem * item;
587:
588: while ((item = (HTCacheItem*)HTList_nextObject(cur))) {
1.18 timbl 589: HTStream * s;
590:
591: request->using_cache = item;
592:
1.37 luotonen 593: s = HTStreamStack(item->format, request, NO);
1.17 timbl 594: if (s) { /* format was suitable */
595: FILE * fp = fopen(item->filename, "r");
1.18 timbl 596: if (TRACE) fprintf(stderr, "Cache: HIT file %s for %s\n",
1.20 luotonen 597: item->filename,
598: full_address);
1.17 timbl 599: if (fp) {
600: HTFileCopy(fp, s);
1.24 timbl 601: (*s->isa->free)(s); /* close up pipeline */
1.17 timbl 602: fclose(fp);
1.19 timbl 603: free(full_address);
1.17 timbl 604: return YES;
605: } else {
606: fprintf(stderr, "***** Can't read cache file %s !\n",
1.20 luotonen 607: item->filename);
1.17 timbl 608: } /* file open ok */
609: } /* stream ok */
610: } /* next cache item */
611: } /* if cache available for this anchor */
1.1 timbl 612:
1.35 luotonen 613: status = HTLoad(request);
1.2 timbl 614:
615:
1.1 timbl 616: /* Log the access if necessary
617: */
1.42 frystyk 618: if (HTlogfile) {
1.1 timbl 619: time_t theTime;
620: time(&theTime);
1.42 frystyk 621: fprintf(HTlogfile, "%24.24s %s %s %s\n",
1.1 timbl 622: ctime(&theTime),
623: HTClientHost ? HTClientHost : "local",
624: status<0 ? "FAIL" : "GET",
625: full_address);
1.42 frystyk 626: fflush(HTlogfile); /* Actually update it on disk */
1.1 timbl 627: if (TRACE) fprintf(stderr, "Log: %24.24s %s %s %s\n",
628: ctime(&theTime),
629: HTClientHost ? HTClientHost : "local",
630: status<0 ? "FAIL" : "GET",
631: full_address);
632: }
633:
634: if (status == HT_LOADED) {
635: if (TRACE) {
636: fprintf(stderr, "HTAccess: `%s' has been accessed.\n",
637: full_address);
638: }
1.19 timbl 639: free(full_address);
1.1 timbl 640: return YES;
641: }
642:
643: if (status == HT_NO_DATA) {
644: if (TRACE) {
645: fprintf(stderr,
646: "HTAccess: `%s' has been accessed, No data left.\n",
647: full_address);
648: }
1.19 timbl 649: free(full_address);
1.1 timbl 650: return NO;
651: }
652:
1.34 frystyk 653: /* Bug fix thanks to Lou Montulli. Henrik 10/03-94 */
654: if (status<=0) { /* Failure in accessing a document */
1.1 timbl 655: #ifdef CURSES
656: user_message("Can't access `%s'", full_address);
657: #else
1.5 timbl 658: if (TRACE) fprintf(stderr,
659: "HTAccess: Can't access `%s'\n", full_address);
1.1 timbl 660: #endif
1.32 frystyk 661: /* This is done in the specific load procedures... Henrik 07/03-94 */
1.48 ! luotonen 662: #if 0
1.47 luotonen 663: if (request->old_error_stack)
1.39 luotonen 664: HTLoadError(request, 500, "Unable to access document.");
1.48 ! luotonen 665: #endif
! 666: if (request->error_stack)
! 667: HTErrorMsg(request);
1.19 timbl 668: free(full_address);
1.1 timbl 669: return NO;
670: }
1.9 timbl 671:
672: /* If you get this, then please find which routine is returning
673: a positive unrecognised error code! */
674:
1.1 timbl 675: fprintf(stderr,
1.2 timbl 676: "**** HTAccess: socket or file number returned by obsolete load routine!\n");
1.9 timbl 677: fprintf(stderr,
1.19 timbl 678: "**** HTAccess: Internal software error. Please mail www-bug@info.cern.ch quoting the version number of this software and the URL: %s!\n",
679: full_address);
680: free(full_address);
681:
1.1 timbl 682: exit(-6996);
1.20 luotonen 683: return NO; /* For gcc :-( */
1.2 timbl 684: } /* HTLoadDocument */
1.1 timbl 685:
686:
687:
688: /* Load a document from absolute name
689: ** ---------------
690: **
691: ** On Entry,
692: ** addr The absolute address of the document to be accessed.
693: ** filter if YES, treat document as HTML
694: **
695: ** On Exit,
696: ** returns YES Success in opening document
697: ** NO Failure
698: **
699: **
700: */
701:
1.15 timbl 702: PUBLIC BOOL HTLoadAbsolute ARGS2(CONST char *,addr, HTRequest*, request)
1.2 timbl 703: {
1.19 timbl 704: HTAnchor * anchor = HTAnchor_findAddress(addr);
705: request->anchor = HTAnchor_parent(anchor);
706: request->childAnchor = ((HTAnchor*)request->anchor == anchor) ?
707: NULL : (HTChildAnchor*) anchor;
708: return HTLoadDocument(request);
1.2 timbl 709: }
710:
711:
712: /* Load a document from absolute name to stream
713: ** --------------------------------------------
714: **
715: ** On Entry,
716: ** addr The absolute address of the document to be accessed.
1.15 timbl 717: ** request->output_stream if non-NULL, send data down this stream
1.2 timbl 718: **
719: ** On Exit,
720: ** returns YES Success in opening document
721: ** NO Failure
722: **
723: **
724: */
725:
726: PUBLIC BOOL HTLoadToStream ARGS3(
727: CONST char *, addr,
728: BOOL, filter,
1.15 timbl 729: HTRequest*, request)
1.1 timbl 730: {
1.19 timbl 731: HTAnchor * anchor = HTAnchor_findAddress(addr);
732: request->anchor = HTAnchor_parent(anchor);
733: request->childAnchor = ((HTAnchor*)request->anchor == anchor) ? NULL :
734: (HTChildAnchor*) anchor;
1.15 timbl 735: request->output_stream = request->output_stream;
1.19 timbl 736: return HTLoadDocument(request);
1.1 timbl 737: }
738:
739:
1.2 timbl 740:
741:
1.1 timbl 742: /* Load a document from relative name
743: ** ---------------
744: **
745: ** On Entry,
1.2 timbl 746: ** relative_name The relative address of the document
747: ** to be accessed.
1.1 timbl 748: **
749: ** On Exit,
750: ** returns YES Success in opening document
751: ** NO Failure
752: **
753: **
754: */
755:
1.15 timbl 756: PUBLIC BOOL HTLoadRelative ARGS3(
1.2 timbl 757: CONST char *, relative_name,
1.15 timbl 758: HTParentAnchor *, here,
1.20 luotonen 759: HTRequest *, request)
1.1 timbl 760: {
761: char * full_address = 0;
762: BOOL result;
763: char * mycopy = 0;
764: char * stripped = 0;
765: char * current_address =
1.2 timbl 766: HTAnchor_address((HTAnchor*)here);
1.1 timbl 767:
768: StrAllocCopy(mycopy, relative_name);
769:
770: stripped = HTStrip(mycopy);
771: full_address = HTParse(stripped,
772: current_address,
773: PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
1.15 timbl 774: result = HTLoadAbsolute(full_address, request);
1.1 timbl 775: free(full_address);
776: free(current_address);
777: free(mycopy); /* Memory leak fixed 10/7/92 -- JFG */
778: return result;
779: }
780:
781:
782: /* Load if necessary, and select an anchor
783: ** --------------------------------------
784: **
785: ** On Entry,
786: ** destination The child or parenet anchor to be loaded.
787: **
788: ** On Exit,
789: ** returns YES Success
790: ** NO Failure
791: **
792: */
793:
1.15 timbl 794: PUBLIC BOOL HTLoadAnchor ARGS2(HTAnchor*, anchor, HTRequest *, request)
1.1 timbl 795: {
1.15 timbl 796: if (!anchor) return NO; /* No link */
1.1 timbl 797:
1.15 timbl 798: request->anchor = HTAnchor_parent(anchor);
1.19 timbl 799: request->childAnchor = ((HTAnchor*)request->anchor == anchor) ? NULL
800: : (HTChildAnchor*) anchor;
1.1 timbl 801:
1.19 timbl 802: return HTLoadDocument(request) ? YES : NO;
1.1 timbl 803:
804: } /* HTLoadAnchor */
805:
806:
807: /* Search
808: ** ------
809: ** Performs a keyword search on word given by the user. Adds the keyword to
810: ** the end of the current address and attempts to open the new address.
811: **
812: ** On Entry,
813: ** *keywords space-separated keyword list or similar search list
1.2 timbl 814: ** here is anchor search is to be done on.
1.1 timbl 815: */
816:
1.2 timbl 817: PRIVATE char hex(i)
818: int i;
819: {
1.13 timbl 820: char * hexchars = "0123456789ABCDEF";
821: return hexchars[i];
1.2 timbl 822: }
1.1 timbl 823:
1.15 timbl 824: PUBLIC BOOL HTSearch ARGS3(
1.2 timbl 825: CONST char *, keywords,
1.15 timbl 826: HTParentAnchor *, here,
827: HTRequest *, request)
1.1 timbl 828: {
1.2 timbl 829:
830: #define acceptable \
831: "1234567890abcdefghijlkmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-_"
832:
833: char *q, *u;
834: CONST char * p, *s, *e; /* Pointers into keywords */
835: char * address = HTAnchor_address((HTAnchor*)here);
1.1 timbl 836: BOOL result;
1.2 timbl 837: char * escaped = malloc(strlen(keywords)*3+1);
838:
1.29 frystyk 839: /* static CONST BOOL isAcceptable[96] = */
840: /* static AND const is not good for a gnu compiler! Frystyk 25/02-94 */
1.30 luotonen 841: static BOOL isAcceptable[96] =
1.2 timbl 842: /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
843: { 0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,0, /* 2x !"#$%&'()*+,-./ */
844: 1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, /* 3x 0123456789:;<=>? */
845: 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 4x @ABCDEFGHIJKLMNO */
846: 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1, /* 5X PQRSTUVWXYZ[\]^_ */
847: 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 6x `abcdefghijklmno */
848: 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0 }; /* 7X pqrstuvwxyz{\}~ DEL */
849:
850: if (escaped == NULL) outofmem(__FILE__, "HTSearch");
851:
1.29 frystyk 852: /* Convert spaces to + and hex escape unacceptable characters */
1.2 timbl 853:
1.29 frystyk 854: for(s=keywords; *s && WHITE(*s); s++); /*scan */ /* Skip white space */
855: for(e = s + strlen(s); e>s && WHITE(*(e-1)) ; e--); /* Skip trailers */
856: for(q=escaped, p=s; p<e; p++) { /* scan stripped field */
1.2 timbl 857: int c = (int)TOASCII(*p);
858: if (WHITE(*p)) {
859: *q++ = '+';
1.29 frystyk 860: } else if (c>=32 && c<=127 && isAcceptable[c-32] != 0) {
1.13 timbl 861: *q++ = *p; /* 930706 TBL for MVS bug */
1.2 timbl 862: } else {
863: *q++ = '%';
864: *q++ = hex(c / 16);
865: *q++ = hex(c % 16);
866: }
867: } /* Loop over string */
1.1 timbl 868:
1.2 timbl 869: *q=0;
870: /* terminate escaped sctring */
871: u=strchr(address, '?'); /* Find old search string */
872: if (u) *u = 0; /* Chop old search off */
1.1 timbl 873:
874: StrAllocCat(address, "?");
1.2 timbl 875: StrAllocCat(address, escaped);
876: free(escaped);
1.15 timbl 877: result = HTLoadRelative(address, here, request);
1.1 timbl 878: free(address);
1.2 timbl 879:
1.1 timbl 880: return result;
1.2 timbl 881: }
882:
883:
884: /* Search Given Indexname
885: ** ------
886: ** Performs a keyword search on word given by the user. Adds the keyword to
887: ** the end of the current address and attempts to open the new address.
888: **
889: ** On Entry,
890: ** *keywords space-separated keyword list or similar search list
891: ** *addres is name of object search is to be done on.
892: */
893:
1.15 timbl 894: PUBLIC BOOL HTSearchAbsolute ARGS3(
1.2 timbl 895: CONST char *, keywords,
1.15 timbl 896: CONST char *, indexname,
897: HTRequest *, request)
1.2 timbl 898: {
899: HTParentAnchor * anchor =
900: (HTParentAnchor*) HTAnchor_findAddress(indexname);
1.15 timbl 901: return HTSearch(keywords, anchor, request);
1.2 timbl 902: }
903:
904:
905: /* Generate the anchor for the home page
906: ** -------------------------------------
907: **
908: ** As it involves file access, this should only be done once
909: ** when the program first runs.
1.10 timbl 910: ** This is a default algorithm -- browser don't HAVE to use this.
911: ** But consistency betwen browsers is STRONGLY recommended!
1.2 timbl 912: **
1.10 timbl 913: ** Priority order is:
914: **
915: ** 1 WWW_HOME environment variable (logical name, etc)
916: ** 2 ~/WWW/default.html
917: ** 3 /usr/local/bin/default.html
918: ** 4 http://info.cern.ch/default.html
919: **
1.2 timbl 920: */
921: PUBLIC HTParentAnchor * HTHomeAnchor NOARGS
922: {
1.12 timbl 923: char * my_home_document = NULL;
924: char * home = (char *)getenv(LOGICAL_DEFAULT);
1.2 timbl 925: char * ref;
926: HTParentAnchor * anchor;
1.1 timbl 927:
1.12 timbl 928: if (home) {
929: StrAllocCopy(my_home_document, home);
930:
931: /* Someone telnets in, they get a special home.
932: */
933: #define MAX_FILE_NAME 1024 /* @@@ */
934: } else if (HTClientHost) { /* Telnet server */
935: FILE * fp = fopen(REMOTE_POINTER, "r");
936: char * status;
937: if (fp) {
938: my_home_document = (char*) malloc(MAX_FILE_NAME);
939: status = fgets(my_home_document, MAX_FILE_NAME, fp);
940: if (!status) {
941: free(my_home_document);
942: my_home_document = NULL;
943: }
944: fclose(fp);
945: }
946: if (!my_home_document) StrAllocCopy(my_home_document, REMOTE_ADDRESS);
947: }
948:
949:
950:
1.2 timbl 951: #ifdef unix
1.12 timbl 952:
1.10 timbl 953: if (!my_home_document) {
954: FILE * fp = NULL;
955: CONST char * home = (CONST char*)getenv("HOME");
956: if (home) {
957: my_home_document = (char *)malloc(
958: strlen(home)+1+ strlen(PERSONAL_DEFAULT)+1);
959: if (my_home_document == NULL) outofmem(__FILE__, "HTLocalName");
960: sprintf(my_home_document, "%s/%s", home, PERSONAL_DEFAULT);
961: fp = fopen(my_home_document, "r");
962: }
963:
964: if (!fp) {
965: StrAllocCopy(my_home_document, LOCAL_DEFAULT_FILE);
966: fp = fopen(my_home_document, "r");
967: }
1.2 timbl 968: if (fp) {
969: fclose(fp);
970: } else {
971: if (TRACE) fprintf(stderr,
1.10 timbl 972: "HTBrowse: No local home document ~/%s or %s\n",
973: PERSONAL_DEFAULT, LOCAL_DEFAULT_FILE);
1.11 timbl 974: free(my_home_document);
975: my_home_document = NULL;
1.2 timbl 976: }
977: }
978: #endif
1.10 timbl 979: ref = HTParse( my_home_document ? my_home_document :
980: HTClientHost ? REMOTE_ADDRESS
981: : LAST_RESORT,
982: "file:",
1.2 timbl 983: PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
1.10 timbl 984: if (my_home_document) {
1.2 timbl 985: if (TRACE) fprintf(stderr,
986: "HTAccess: Using custom home page %s i.e. address %s\n",
1.10 timbl 987: my_home_document, ref);
988: free(my_home_document);
1.2 timbl 989: }
990: anchor = (HTParentAnchor*) HTAnchor_findAddress(ref);
991: free(ref);
992: return anchor;
1.1 timbl 993: }
1.26 frystyk 994:
995:
996: /* Bind an Anchor to the request structure
997: ** ---------------------------------------
998: **
999: ** On Entry,
1000: ** anchor The child or parenet anchor to be binded
1001: ** request The request sturcture
1002: ** On Exit,
1003: ** returns YES Success
1004: ** NO Failure
1005: **
1006: ** Note: Actually the same as HTLoadAnchor() but DOES NOT do the loading
1007: ** Henrik Frystyk 17/02-94
1008: */
1009:
1010: PUBLIC BOOL HTBindAnchor ARGS2(HTAnchor*, anchor, HTRequest *, request)
1011: {
1012: if (!anchor) return NO; /* No link */
1013:
1014: request->anchor = HTAnchor_parent(anchor);
1015: request->childAnchor = ((HTAnchor*)request->anchor == anchor) ? NULL
1016: : (HTChildAnchor*) anchor;
1017:
1.29 frystyk 1018: return YES;
1.26 frystyk 1019: } /* HTBindAnchor */
1.39 luotonen 1020:
1021:
1022:
1023: /*
1024: * Error diagnostics
1025: */
1026: PUBLIC void HTAddError ARGS2(HTRequest *, req,
1.40 luotonen 1027: CONST char *, msg)
1.39 luotonen 1028: {
1029: HTAddError2(req,msg,NULL);
1030: }
1031:
1032: PUBLIC void HTAddError2 ARGS3(HTRequest *, req,
1.40 luotonen 1033: CONST char *, msg,
1034: CONST char *, param)
1.39 luotonen 1035: {
1036: int mlen = msg ? strlen(msg) : 0;
1037: int plen = param ? strlen(param) : 0;
1038: char * str;
1039:
1040: if (!req) return;
1.47 luotonen 1041: if (!req->old_error_stack) req->old_error_stack = HTList_new();
1.39 luotonen 1042:
1043: str = (char*)malloc(mlen + plen + 2);
1044: if (!str) outofmem(__FILE__,"HTAddError2");
1045:
1046: if (msg) strcpy(str,msg);
1047: strcpy(str+mlen," ");
1048: if (param) strcpy(str+mlen+1,param);
1049:
1.47 luotonen 1050: HTList_addObject(req->old_error_stack, (void*)str);
1.39 luotonen 1051: CTRACE(stderr, "libwww error: %s\n", str);
1052: }
1053:
1054: PUBLIC void HTAddErrorN ARGS3(HTRequest *, req,
1.40 luotonen 1055: CONST char *, msg,
1.39 luotonen 1056: int, num)
1057: {
1058: char buf[20];
1059: sprintf(buf,"%d",num);
1060: HTAddError2(req,msg,buf);
1061: }
1062:
1063: PUBLIC void HTClearErrors ARGS1(HTRequest *, req)
1064: {
1.47 luotonen 1065: if (req && req->old_error_stack) {
1066: HTList * cur = req->old_error_stack;
1.39 luotonen 1067: char * str;
1068: while ((str = (char*)HTList_nextObject(cur)))
1069: free(str);
1.47 luotonen 1070: HTList_delete(req->old_error_stack);
1071: req->old_error_stack = NULL;
1.39 luotonen 1072: }
1073: }
1.26 frystyk 1074:
Webmaster