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