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