Annotation of libwww/Library/src/HTAccess.c, revision 1.32
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: */
52: PUBLIC char * HTClientHost = 0; /* Name of remote login host if any */
53: PUBLIC FILE * logfile = 0; /* File to which to output one-liners */
1.12 timbl 54: PUBLIC BOOL HTSecure = NO; /* Disable access for telnet users? */
1.27 luotonen 55: PUBLIC BOOL using_proxy = NO; /* are we using a proxy gateway? */
56: PUBLIC BOOL HTImServer = NO; /* cern_httpd sets this */
57: PUBLIC BOOL HTImProxy = NO; /* cern_httpd as a proxy? */
1.1 timbl 58:
1.2 timbl 59: /* To generate other things, play with these:
60: */
61:
1.15 timbl 62: /* PUBLIC HTFormat HTOutputFormat = NULL; use request->output_format */
63: /* PUBLIC HTStream* HTOutputStream = NULL; use request->output_stream */
1.1 timbl 64:
65: PRIVATE HTList * protocols = NULL; /* List of registered protocol descriptors */
66:
1.24 timbl 67: /* Superclass defn */
1.1 timbl 68:
1.24 timbl 69: struct _HTStream {
70: HTStreamClass * isa;
71: /* ... */
72: };
73:
1.15 timbl 74: /* Create a request structure
75: ** ---------------------------
76: */
77:
78: PUBLIC HTRequest * HTRequest_new NOARGS
79: {
1.28 luotonen 80: HTRequest * me = (HTRequest*) calloc(1, sizeof(*me)); /* zero fill */
1.15 timbl 81: if (!me) outofmem(__FILE__, "HTRequest_new()");
82:
1.20 luotonen 83: me->conversions = HTList_new(); /* No conversions registerd yet */
84: me->output_format = WWW_PRESENT; /* default it to present to user */
85:
1.15 timbl 86: return me;
87: }
88:
89:
1.20 luotonen 90: /* Delete a request structure
91: ** --------------------------
92: */
93: PUBLIC void HTRequest_delete ARGS1(HTRequest *, req)
94: {
95: if (req) {
1.23 luotonen 96: if (req->conversions) {
97: HTList *cur = req->conversions;
98: HTPresentation *pres;
1.20 luotonen 99:
1.23 luotonen 100: while ((pres = (HTPresentation*)HTList_nextObject(cur))) {
101: FREE(pres->command); /* Leak fixed AL 6 Feb 1994 */
102: free(pres);
103: }
104: HTList_delete(req->conversions); /* Leak fixed AL 6 Feb 1994 */
105: }
1.28 luotonen 106: FREE(req->authorization);
1.20 luotonen 107: free(req);
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.3 timbl 208: #ifdef DIRECT_WAIS
1.14 duns 209: GLOBALREF HTProtocol HTWAIS;
1.3 timbl 210: #endif
1.2 timbl 211: HTRegisterProtocol(&HTFTP);
212: HTRegisterProtocol(&HTNews);
213: HTRegisterProtocol(&HTGopher);
1.3 timbl 214: #ifdef DIRECT_WAIS
215: HTRegisterProtocol(&HTWAIS);
216: #endif
1.1 timbl 217: #endif
218:
1.2 timbl 219: HTRegisterProtocol(&HTTP);
220: HTRegisterProtocol(&HTFile);
221: HTRegisterProtocol(&HTTelnet);
222: HTRegisterProtocol(&HTTn3270);
223: HTRegisterProtocol(&HTRlogin);
1.1 timbl 224: }
225: #endif
226:
227:
1.2 timbl 228: /* Find physical name and access protocol
229: ** --------------------------------------
1.1 timbl 230: **
231: **
232: ** On entry,
233: ** addr must point to the fully qualified hypertext reference.
234: ** anchor a pareent anchor with whose address is addr
235: **
236: ** On exit,
1.2 timbl 237: ** returns HT_NO_ACCESS Error has occured.
238: ** HT_OK Success
1.1 timbl 239: **
240: */
1.21 luotonen 241: PRIVATE int get_physical ARGS1(HTRequest *, req)
242: {
1.1 timbl 243: char * access=0; /* Name of access method */
1.21 luotonen 244: char * addr = HTAnchor_address((HTAnchor*)req->anchor); /* free me */
1.27 luotonen 245:
1.2 timbl 246: #ifndef NO_RULES
1.27 luotonen 247: if (HTImServer) /* cern_httpd has already done its own translations */
248: HTAnchor_setPhysical(req->anchor, addr);
1.21 luotonen 249: else {
1.27 luotonen 250: char * physical = HTTranslate(addr);
1.21 luotonen 251: if (!physical) {
252: free(addr);
253: return HT_FORBIDDEN;
254: }
255: HTAnchor_setPhysical(req->anchor, physical);
256: free(physical); /* free our copy */
1.2 timbl 257: }
258: #else
1.21 luotonen 259: HTAnchor_setPhysical(req->anchor, addr);
1.2 timbl 260: #endif
261:
1.21 luotonen 262: access = HTParse(HTAnchor_physical(req->anchor),
1.27 luotonen 263: "file:", PARSE_ACCESS);
1.1 timbl 264:
265: /* Check whether gateway access has been set up for this
1.8 timbl 266: **
267: ** This function can be replaced by the rule system above.
1.1 timbl 268: */
1.8 timbl 269: #define USE_GATEWAYS
1.1 timbl 270: #ifdef USE_GATEWAYS
1.2 timbl 271: {
1.27 luotonen 272: char * gateway_parameter, *gateway, *proxy;
273:
1.2 timbl 274: gateway_parameter = (char *)malloc(strlen(access)+20);
275: if (gateway_parameter == NULL) outofmem(__FILE__, "HTLoad");
1.27 luotonen 276:
277: /* search for proxy gateways */
1.2 timbl 278: strcpy(gateway_parameter, "WWW_");
279: strcat(gateway_parameter, access);
280: strcat(gateway_parameter, "_GATEWAY");
281: gateway = (char *)getenv(gateway_parameter); /* coerce for decstation */
1.27 luotonen 282:
283: /* search for proxy servers */
284: strcpy(gateway_parameter, access);
285: strcat(gateway_parameter, "_proxy");
286: proxy = (char *)getenv(gateway_parameter);
287:
1.2 timbl 288: free(gateway_parameter);
1.27 luotonen 289:
290: if (TRACE && gateway)
291: fprintf(stderr,"Gateway found: %s\n",gateway);
292: if (TRACE && proxy)
293: fprintf(stderr,"Proxy server found: %s\n",proxy);
294:
1.8 timbl 295: #ifndef DIRECT_WAIS
1.9 timbl 296: if (!gateway && 0==strcmp(access, "wais")) {
1.8 timbl 297: gateway = DEFAULT_WAIS_GATEWAY;
298: }
299: #endif
1.27 luotonen 300: /* make sure the using_proxy variable is false */
301: using_proxy = NO;
302:
303: /* proxy servers have precedence over gateway servers */
304: if (proxy) {
305: char * gatewayed=0;
306:
307: StrAllocCopy(gatewayed,proxy);
308: StrAllocCat(gatewayed,addr);
309: using_proxy = YES;
310: HTAnchor_setPhysical(req->anchor, gatewayed);
311: free(gatewayed);
312: free(access);
313:
314: access = HTParse(HTAnchor_physical(req->anchor),
315: "http:", PARSE_ACCESS);
316: } else if (gateway) {
1.9 timbl 317: char * path = HTParse(addr, "",
318: PARSE_HOST + PARSE_PATH + PARSE_PUNCTUATION);
319: /* Chop leading / off to make host into part of path */
320: char * gatewayed = HTParse(path+1, gateway, PARSE_ALL);
321: free(path);
1.21 luotonen 322: HTAnchor_setPhysical(req->anchor, gatewayed);
1.9 timbl 323: free(gatewayed);
1.2 timbl 324: free(access);
1.9 timbl 325:
1.21 luotonen 326: access = HTParse(HTAnchor_physical(req->anchor),
1.8 timbl 327: "http:", PARSE_ACCESS);
1.2 timbl 328: }
329: }
1.1 timbl 330: #endif
331:
1.19 timbl 332: free(addr);
1.1 timbl 333:
334:
335: /* Search registered protocols to find suitable one
336: */
337: {
1.20 luotonen 338: HTList *cur;
339: HTProtocol *p;
1.1 timbl 340: #ifndef NO_INIT
1.2 timbl 341: if (!protocols) HTAccessInit();
1.1 timbl 342: #endif
1.20 luotonen 343: cur = protocols;
344: while ((p = (HTProtocol*)HTList_nextObject(cur))) {
1.2 timbl 345: if (strcmp(p->name, access)==0) {
1.21 luotonen 346: HTAnchor_setProtocol(req->anchor, p);
1.2 timbl 347: free(access);
348: return (HT_OK);
1.1 timbl 349: }
350: }
351: }
352:
353: free(access);
1.2 timbl 354: return HT_NO_ACCESS;
1.1 timbl 355: }
356:
357:
358: /* Load a document
359: ** ---------------
360: **
1.2 timbl 361: ** This is an internal routine, which has an address AND a matching
362: ** anchor. (The public routines are called with one OR the other.)
363: **
364: ** On entry,
365: ** addr must point to the fully qualified hypertext reference.
1.15 timbl 366: ** request->
367: ** anchor a parent anchor with whose address is addr
368: ** output_format valid
369: ** output_stream valid on NULL
1.2 timbl 370: **
371: ** On exit,
372: ** returns <0 Error has occured.
373: ** HT_LOADED Success
374: ** HT_NO_DATA Success, but no document loaded.
1.8 timbl 375: ** (telnet sesssion started etc)
1.2 timbl 376: **
377: */
1.15 timbl 378: PRIVATE int HTLoad ARGS2(
1.19 timbl 379: CONST char *, addr, /* not used */
1.15 timbl 380: HTRequest *, request)
1.2 timbl 381: {
1.25 frystyk 382: char *arg = NULL;
383: HTProtocol *p;
384: int status;
385:
1.22 luotonen 386: if (request->method == METHOD_INVALID)
387: request->method = METHOD_GET;
1.21 luotonen 388: status = get_physical(request);
1.2 timbl 389: if (status == HT_FORBIDDEN) {
1.21 luotonen 390: return HTLoadError(request, 500,
391: "Access forbidden by rule");
1.2 timbl 392: }
393: if (status < 0) return status; /* Can't resolve or forbidden */
1.25 frystyk 394:
395: if(!(arg = HTAnchor_physical(request->anchor)) || !*arg)
396: return (-1);
1.27 luotonen 397:
1.15 timbl 398: p = HTAnchor_protocol(request->anchor);
1.17 timbl 399: return (*(p->load))(request);
1.2 timbl 400: }
401:
402:
403: /* Get a save stream for a document
404: ** --------------------------------
405: */
1.19 timbl 406: PUBLIC HTStream *HTSaveStream ARGS1(HTRequest *, request)
1.15 timbl 407: {
408: HTProtocol * p;
1.19 timbl 409: int status;
1.22 luotonen 410: request->method = METHOD_PUT;
1.21 luotonen 411: status = get_physical(request);
1.19 timbl 412: if (status == HT_FORBIDDEN) {
1.21 luotonen 413: HTLoadError(request, 500,
414: "Access forbidden by rule");
1.19 timbl 415: return NULL; /* should return error status? */
416: }
417: if (status < 0) return NULL; /* @@ error. Can't resolve or forbidden */
418:
1.15 timbl 419: p = HTAnchor_protocol(request->anchor);
1.2 timbl 420: if (!p) return NULL;
421:
1.15 timbl 422: return (*p->saveStream)(request);
1.2 timbl 423:
424: }
425:
426:
427: /* Load a document - with logging etc
428: ** ----------------------------------
429: **
430: ** - Checks or documents already loaded
431: ** - Logs the access
432: ** - Allows stdin filter option
433: ** - Trace ouput and error messages
434: **
1.1 timbl 435: ** On Entry,
1.19 timbl 436: ** request->anchor valid for of the document to be accessed.
437: ** request->childAnchor optional anchor within doc to be selected
438: **
1.2 timbl 439: ** filter if YES, treat stdin as HTML
1.1 timbl 440: **
1.15 timbl 441: ** request->anchor is the node_anchor for the document
442: ** request->output_format is valid
443: **
1.1 timbl 444: ** On Exit,
445: ** returns YES Success in opening document
446: ** NO Failure
447: **
448: */
449:
1.19 timbl 450: PRIVATE BOOL HTLoadDocument ARGS1(HTRequest *, request)
1.1 timbl 451:
452: {
453: int status;
454: HText * text;
1.19 timbl 455: char * full_address = HTAnchor_address((HTAnchor*)request->anchor);
456:
1.1 timbl 457: if (TRACE) fprintf (stderr,
458: "HTAccess: loading document %s\n", full_address);
459:
1.18 timbl 460: request->using_cache = NULL;
461:
1.15 timbl 462: if (!request->output_format) request->output_format = WWW_PRESENT;
1.25 frystyk 463:
1.31 frystyk 464: if (!HTForceReload && (text=(HText *)HTAnchor_document(request->anchor)))
1.15 timbl 465: { /* Already loaded */
1.1 timbl 466: if (TRACE) fprintf(stderr, "HTAccess: Document already in memory.\n");
1.19 timbl 467: if (request->childAnchor) {
468: HText_selectAnchor(text, request->childAnchor);
469: } else {
470: HText_select(text);
471: }
472: free(full_address);
1.1 timbl 473: return YES;
474: }
1.17 timbl 475:
476: /* Check the Cache
477: */
478: /* Bug: for each format, we only check whether it is ok, we
479: don't check them all and chose the best */
480: if (request->anchor->cacheItems) {
481: HTList * list = request->anchor->cacheItems;
1.20 luotonen 482: HTList * cur = list;
483: HTCacheItem * item;
484:
485: while ((item = (HTCacheItem*)HTList_nextObject(cur))) {
1.18 timbl 486: HTStream * s;
487:
488: request->using_cache = item;
489:
490: s = HTStreamStack(item->format, request);
1.17 timbl 491: if (s) { /* format was suitable */
492: FILE * fp = fopen(item->filename, "r");
1.18 timbl 493: if (TRACE) fprintf(stderr, "Cache: HIT file %s for %s\n",
1.20 luotonen 494: item->filename,
495: full_address);
1.17 timbl 496: if (fp) {
497: HTFileCopy(fp, s);
1.24 timbl 498: (*s->isa->free)(s); /* close up pipeline */
1.17 timbl 499: fclose(fp);
1.19 timbl 500: free(full_address);
1.17 timbl 501: return YES;
502: } else {
503: fprintf(stderr, "***** Can't read cache file %s !\n",
1.20 luotonen 504: item->filename);
1.17 timbl 505: } /* file open ok */
506: } /* stream ok */
507: } /* next cache item */
508: } /* if cache available for this anchor */
1.1 timbl 509:
1.15 timbl 510: status = HTLoad(full_address, request);
1.2 timbl 511:
512:
1.1 timbl 513: /* Log the access if necessary
514: */
515: if (logfile) {
516: time_t theTime;
517: time(&theTime);
518: fprintf(logfile, "%24.24s %s %s %s\n",
519: ctime(&theTime),
520: HTClientHost ? HTClientHost : "local",
521: status<0 ? "FAIL" : "GET",
522: full_address);
523: fflush(logfile); /* Actually update it on disk */
524: if (TRACE) fprintf(stderr, "Log: %24.24s %s %s %s\n",
525: ctime(&theTime),
526: HTClientHost ? HTClientHost : "local",
527: status<0 ? "FAIL" : "GET",
528: full_address);
529: }
530:
531:
532: if (status == HT_LOADED) {
533: if (TRACE) {
534: fprintf(stderr, "HTAccess: `%s' has been accessed.\n",
535: full_address);
536: }
1.19 timbl 537: free(full_address);
1.1 timbl 538: return YES;
539: }
540:
541: if (status == HT_NO_DATA) {
542: if (TRACE) {
543: fprintf(stderr,
544: "HTAccess: `%s' has been accessed, No data left.\n",
545: full_address);
546: }
1.19 timbl 547: free(full_address);
1.1 timbl 548: return NO;
549: }
550:
1.2 timbl 551: if (status<0) { /* Failure in accessing a document */
1.1 timbl 552: #ifdef CURSES
553: user_message("Can't access `%s'", full_address);
554: #else
1.5 timbl 555: if (TRACE) fprintf(stderr,
556: "HTAccess: Can't access `%s'\n", full_address);
1.1 timbl 557: #endif
1.32 ! frystyk 558: /* This is done in the specific load procedures... Henrik 07/03-94 */
! 559: /* HTLoadError(request, 500, "Unable to access document."); */
1.19 timbl 560: free(full_address);
1.1 timbl 561: return NO;
562: }
1.9 timbl 563:
564: /* If you get this, then please find which routine is returning
565: a positive unrecognised error code! */
566:
1.1 timbl 567: fprintf(stderr,
1.2 timbl 568: "**** HTAccess: socket or file number returned by obsolete load routine!\n");
1.9 timbl 569: fprintf(stderr,
1.19 timbl 570: "**** HTAccess: Internal software error. Please mail www-bug@info.cern.ch quoting the version number of this software and the URL: %s!\n",
571: full_address);
572: free(full_address);
573:
1.1 timbl 574: exit(-6996);
1.20 luotonen 575: return NO; /* For gcc :-( */
1.2 timbl 576: } /* HTLoadDocument */
1.1 timbl 577:
578:
579:
580: /* Load a document from absolute name
581: ** ---------------
582: **
583: ** On Entry,
584: ** addr The absolute address of the document to be accessed.
585: ** filter if YES, treat document as HTML
586: **
587: ** On Exit,
588: ** returns YES Success in opening document
589: ** NO Failure
590: **
591: **
592: */
593:
1.15 timbl 594: PUBLIC BOOL HTLoadAbsolute ARGS2(CONST char *,addr, HTRequest*, request)
1.2 timbl 595: {
1.19 timbl 596: HTAnchor * anchor = HTAnchor_findAddress(addr);
597: request->anchor = HTAnchor_parent(anchor);
598: request->childAnchor = ((HTAnchor*)request->anchor == anchor) ?
599: NULL : (HTChildAnchor*) anchor;
600: return HTLoadDocument(request);
1.2 timbl 601: }
602:
603:
604: /* Load a document from absolute name to stream
605: ** --------------------------------------------
606: **
607: ** On Entry,
608: ** addr The absolute address of the document to be accessed.
1.15 timbl 609: ** request->output_stream if non-NULL, send data down this stream
1.2 timbl 610: **
611: ** On Exit,
612: ** returns YES Success in opening document
613: ** NO Failure
614: **
615: **
616: */
617:
618: PUBLIC BOOL HTLoadToStream ARGS3(
619: CONST char *, addr,
620: BOOL, filter,
1.15 timbl 621: HTRequest*, request)
1.1 timbl 622: {
1.19 timbl 623: HTAnchor * anchor = HTAnchor_findAddress(addr);
624: request->anchor = HTAnchor_parent(anchor);
625: request->childAnchor = ((HTAnchor*)request->anchor == anchor) ? NULL :
626: (HTChildAnchor*) anchor;
1.15 timbl 627: request->output_stream = request->output_stream;
1.19 timbl 628: return HTLoadDocument(request);
1.1 timbl 629: }
630:
631:
1.2 timbl 632:
633:
1.1 timbl 634: /* Load a document from relative name
635: ** ---------------
636: **
637: ** On Entry,
1.2 timbl 638: ** relative_name The relative address of the document
639: ** to be accessed.
1.1 timbl 640: **
641: ** On Exit,
642: ** returns YES Success in opening document
643: ** NO Failure
644: **
645: **
646: */
647:
1.15 timbl 648: PUBLIC BOOL HTLoadRelative ARGS3(
1.2 timbl 649: CONST char *, relative_name,
1.15 timbl 650: HTParentAnchor *, here,
1.20 luotonen 651: HTRequest *, request)
1.1 timbl 652: {
653: char * full_address = 0;
654: BOOL result;
655: char * mycopy = 0;
656: char * stripped = 0;
657: char * current_address =
1.2 timbl 658: HTAnchor_address((HTAnchor*)here);
1.1 timbl 659:
660: StrAllocCopy(mycopy, relative_name);
661:
662: stripped = HTStrip(mycopy);
663: full_address = HTParse(stripped,
664: current_address,
665: PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
1.15 timbl 666: result = HTLoadAbsolute(full_address, request);
1.1 timbl 667: free(full_address);
668: free(current_address);
669: free(mycopy); /* Memory leak fixed 10/7/92 -- JFG */
670: return result;
671: }
672:
673:
674: /* Load if necessary, and select an anchor
675: ** --------------------------------------
676: **
677: ** On Entry,
678: ** destination The child or parenet anchor to be loaded.
679: **
680: ** On Exit,
681: ** returns YES Success
682: ** NO Failure
683: **
684: */
685:
1.15 timbl 686: PUBLIC BOOL HTLoadAnchor ARGS2(HTAnchor*, anchor, HTRequest *, request)
1.1 timbl 687: {
1.15 timbl 688: if (!anchor) return NO; /* No link */
1.1 timbl 689:
1.15 timbl 690: request->anchor = HTAnchor_parent(anchor);
1.19 timbl 691: request->childAnchor = ((HTAnchor*)request->anchor == anchor) ? NULL
692: : (HTChildAnchor*) anchor;
1.1 timbl 693:
1.19 timbl 694: return HTLoadDocument(request) ? YES : NO;
1.1 timbl 695:
696: } /* HTLoadAnchor */
697:
698:
699: /* Search
700: ** ------
701: ** Performs a keyword search on word given by the user. Adds the keyword to
702: ** the end of the current address and attempts to open the new address.
703: **
704: ** On Entry,
705: ** *keywords space-separated keyword list or similar search list
1.2 timbl 706: ** here is anchor search is to be done on.
1.1 timbl 707: */
708:
1.2 timbl 709: PRIVATE char hex(i)
710: int i;
711: {
1.13 timbl 712: char * hexchars = "0123456789ABCDEF";
713: return hexchars[i];
1.2 timbl 714: }
1.1 timbl 715:
1.15 timbl 716: PUBLIC BOOL HTSearch ARGS3(
1.2 timbl 717: CONST char *, keywords,
1.15 timbl 718: HTParentAnchor *, here,
719: HTRequest *, request)
1.1 timbl 720: {
1.2 timbl 721:
722: #define acceptable \
723: "1234567890abcdefghijlkmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-_"
724:
725: char *q, *u;
726: CONST char * p, *s, *e; /* Pointers into keywords */
727: char * address = HTAnchor_address((HTAnchor*)here);
1.1 timbl 728: BOOL result;
1.2 timbl 729: char * escaped = malloc(strlen(keywords)*3+1);
730:
1.29 frystyk 731: /* static CONST BOOL isAcceptable[96] = */
732: /* static AND const is not good for a gnu compiler! Frystyk 25/02-94 */
1.30 luotonen 733: static BOOL isAcceptable[96] =
1.2 timbl 734: /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
735: { 0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,0, /* 2x !"#$%&'()*+,-./ */
736: 1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, /* 3x 0123456789:;<=>? */
737: 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 4x @ABCDEFGHIJKLMNO */
738: 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1, /* 5X PQRSTUVWXYZ[\]^_ */
739: 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 6x `abcdefghijklmno */
740: 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0 }; /* 7X pqrstuvwxyz{\}~ DEL */
741:
742: if (escaped == NULL) outofmem(__FILE__, "HTSearch");
743:
1.29 frystyk 744: /* Convert spaces to + and hex escape unacceptable characters */
1.2 timbl 745:
1.29 frystyk 746: for(s=keywords; *s && WHITE(*s); s++); /*scan */ /* Skip white space */
747: for(e = s + strlen(s); e>s && WHITE(*(e-1)) ; e--); /* Skip trailers */
748: for(q=escaped, p=s; p<e; p++) { /* scan stripped field */
1.2 timbl 749: int c = (int)TOASCII(*p);
750: if (WHITE(*p)) {
751: *q++ = '+';
1.29 frystyk 752: } else if (c>=32 && c<=127 && isAcceptable[c-32] != 0) {
1.13 timbl 753: *q++ = *p; /* 930706 TBL for MVS bug */
1.2 timbl 754: } else {
755: *q++ = '%';
756: *q++ = hex(c / 16);
757: *q++ = hex(c % 16);
758: }
759: } /* Loop over string */
1.1 timbl 760:
1.2 timbl 761: *q=0;
762: /* terminate escaped sctring */
763: u=strchr(address, '?'); /* Find old search string */
764: if (u) *u = 0; /* Chop old search off */
1.1 timbl 765:
766: StrAllocCat(address, "?");
1.2 timbl 767: StrAllocCat(address, escaped);
768: free(escaped);
1.15 timbl 769: result = HTLoadRelative(address, here, request);
1.1 timbl 770: free(address);
1.2 timbl 771:
1.1 timbl 772: return result;
1.2 timbl 773: }
774:
775:
776: /* Search Given Indexname
777: ** ------
778: ** Performs a keyword search on word given by the user. Adds the keyword to
779: ** the end of the current address and attempts to open the new address.
780: **
781: ** On Entry,
782: ** *keywords space-separated keyword list or similar search list
783: ** *addres is name of object search is to be done on.
784: */
785:
1.15 timbl 786: PUBLIC BOOL HTSearchAbsolute ARGS3(
1.2 timbl 787: CONST char *, keywords,
1.15 timbl 788: CONST char *, indexname,
789: HTRequest *, request)
1.2 timbl 790: {
791: HTParentAnchor * anchor =
792: (HTParentAnchor*) HTAnchor_findAddress(indexname);
1.15 timbl 793: return HTSearch(keywords, anchor, request);
1.2 timbl 794: }
795:
796:
797: /* Generate the anchor for the home page
798: ** -------------------------------------
799: **
800: ** As it involves file access, this should only be done once
801: ** when the program first runs.
1.10 timbl 802: ** This is a default algorithm -- browser don't HAVE to use this.
803: ** But consistency betwen browsers is STRONGLY recommended!
1.2 timbl 804: **
1.10 timbl 805: ** Priority order is:
806: **
807: ** 1 WWW_HOME environment variable (logical name, etc)
808: ** 2 ~/WWW/default.html
809: ** 3 /usr/local/bin/default.html
810: ** 4 http://info.cern.ch/default.html
811: **
1.2 timbl 812: */
813: PUBLIC HTParentAnchor * HTHomeAnchor NOARGS
814: {
1.12 timbl 815: char * my_home_document = NULL;
816: char * home = (char *)getenv(LOGICAL_DEFAULT);
1.2 timbl 817: char * ref;
818: HTParentAnchor * anchor;
1.1 timbl 819:
1.12 timbl 820: if (home) {
821: StrAllocCopy(my_home_document, home);
822:
823: /* Someone telnets in, they get a special home.
824: */
825: #define MAX_FILE_NAME 1024 /* @@@ */
826: } else if (HTClientHost) { /* Telnet server */
827: FILE * fp = fopen(REMOTE_POINTER, "r");
828: char * status;
829: if (fp) {
830: my_home_document = (char*) malloc(MAX_FILE_NAME);
831: status = fgets(my_home_document, MAX_FILE_NAME, fp);
832: if (!status) {
833: free(my_home_document);
834: my_home_document = NULL;
835: }
836: fclose(fp);
837: }
838: if (!my_home_document) StrAllocCopy(my_home_document, REMOTE_ADDRESS);
839: }
840:
841:
842:
1.2 timbl 843: #ifdef unix
1.12 timbl 844:
1.10 timbl 845: if (!my_home_document) {
846: FILE * fp = NULL;
847: CONST char * home = (CONST char*)getenv("HOME");
848: if (home) {
849: my_home_document = (char *)malloc(
850: strlen(home)+1+ strlen(PERSONAL_DEFAULT)+1);
851: if (my_home_document == NULL) outofmem(__FILE__, "HTLocalName");
852: sprintf(my_home_document, "%s/%s", home, PERSONAL_DEFAULT);
853: fp = fopen(my_home_document, "r");
854: }
855:
856: if (!fp) {
857: StrAllocCopy(my_home_document, LOCAL_DEFAULT_FILE);
858: fp = fopen(my_home_document, "r");
859: }
1.2 timbl 860: if (fp) {
861: fclose(fp);
862: } else {
863: if (TRACE) fprintf(stderr,
1.10 timbl 864: "HTBrowse: No local home document ~/%s or %s\n",
865: PERSONAL_DEFAULT, LOCAL_DEFAULT_FILE);
1.11 timbl 866: free(my_home_document);
867: my_home_document = NULL;
1.2 timbl 868: }
869: }
870: #endif
1.10 timbl 871: ref = HTParse( my_home_document ? my_home_document :
872: HTClientHost ? REMOTE_ADDRESS
873: : LAST_RESORT,
874: "file:",
1.2 timbl 875: PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
1.10 timbl 876: if (my_home_document) {
1.2 timbl 877: if (TRACE) fprintf(stderr,
878: "HTAccess: Using custom home page %s i.e. address %s\n",
1.10 timbl 879: my_home_document, ref);
880: free(my_home_document);
1.2 timbl 881: }
882: anchor = (HTParentAnchor*) HTAnchor_findAddress(ref);
883: free(ref);
884: return anchor;
1.1 timbl 885: }
1.26 frystyk 886:
887:
888: /* Bind an Anchor to the request structure
889: ** ---------------------------------------
890: **
891: ** On Entry,
892: ** anchor The child or parenet anchor to be binded
893: ** request The request sturcture
894: ** On Exit,
895: ** returns YES Success
896: ** NO Failure
897: **
898: ** Note: Actually the same as HTLoadAnchor() but DOES NOT do the loading
899: ** Henrik Frystyk 17/02-94
900: */
901:
902: PUBLIC BOOL HTBindAnchor ARGS2(HTAnchor*, anchor, HTRequest *, request)
903: {
904: if (!anchor) return NO; /* No link */
905:
906: request->anchor = HTAnchor_parent(anchor);
907: request->childAnchor = ((HTAnchor*)request->anchor == anchor) ? NULL
908: : (HTChildAnchor*) anchor;
909:
1.29 frystyk 910: return YES;
1.26 frystyk 911: } /* HTBindAnchor */
912:
Webmaster