Annotation of libwww/Library/src/HTGopher.c, revision 1.1
1.1 ! timbl 1: /* GOPHER ACCESS HTGopher.c
! 2: ** =============
! 3: **
! 4: ** History:
! 5: ** 26 Sep 90 Adapted from other accesses (News, HTTP) TBL
! 6: ** 29 Nov 91 Downgraded to C, for portable implementation.
! 7: */
! 8:
! 9: #define GOPHER_PORT 70 /* See protocol spec */
! 10: #define BIG 1024 /* Bug */
! 11: #define LINE_LENGTH 256 /* Bug */
! 12:
! 13: /* Gopher entity types:
! 14: */
! 15: #define GOPHER_TEXT '0'
! 16: #define GOPHER_MENU '1'
! 17: #define GOPHER_CSO '2'
! 18: #define GOPHER_ERROR '3'
! 19: #define GOPHER_MACBINHEX '4'
! 20: #define GOPHER_PCBINHEX '5'
! 21: #define GOPHER_UUENCODED '6'
! 22: #define GOPHER_INDEX '7'
! 23: #define GOPHER_TELNET '8'
! 24: #define GOPHER_HTML 'h' /* HTML */
! 25: #define GOPHER_DUPLICATE '+'
! 26: #define GOPHER_WWW 'w' /* W3 address */
! 27:
! 28: #include <ctype.h>
! 29: #include "HTUtils.h" /* Coding convention macros */
! 30: #include "tcp.h"
! 31:
! 32: #include "HTGopher.h"
! 33:
! 34: #include "HText.h"
! 35: #include "HTParse.h"
! 36: #include "HTFormat.h"
! 37: #include "HTTCP.h"
! 38:
! 39: #ifdef NeXTStep
! 40: #include <appkit/defaults.h>
! 41: #define GOPHER_PROGRESS(foo)
! 42: #else
! 43: #define GOPHER_PROGRESS(foo) fprintf(stderr, "%s\n", (foo))
! 44: #endif
! 45:
! 46: extern HTStyleSheet * styleSheet;
! 47:
! 48: #define NEXT_CHAR HTGetChararcter()
! 49:
! 50:
! 51:
! 52: /* Module-wide variables
! 53: */
! 54: PRIVATE int s; /* Socket for GopherHost */
! 55: PRIVATE HText * HT; /* the new hypertext */
! 56: PRIVATE HTParentAnchor *node_anchor; /* Its anchor */
! 57: PRIVATE int diagnostic; /* level: 0=none 2=source */
! 58:
! 59: PRIVATE HTStyle *addressStyle; /* For address etc */
! 60: PRIVATE HTStyle *heading1Style; /* For heading level 1 */
! 61: PRIVATE HTStyle *textStyle; /* Text style */
! 62:
! 63:
! 64: /* Matrix of allowed characters in filenames
! 65: ** -----------------------------------------
! 66: */
! 67:
! 68: PRIVATE BOOL acceptable[256];
! 69: PRIVATE BOOL acceptable_inited = NO;
! 70:
! 71: PRIVATE void init_acceptable NOARGS
! 72: {
! 73: unsigned int i;
! 74: char * good =
! 75: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./-_$";
! 76: for(i=0; i<256; i++) acceptable[i] = NO;
! 77: for(;*good; good++) acceptable[(unsigned int)*good] = YES;
! 78: acceptable_inited = YES;
! 79: }
! 80:
! 81: PRIVATE CONST char hex[17] = "0123456789abcdef";
! 82:
! 83: /* Decdoe one hex character
! 84: */
! 85:
! 86: PRIVATE char from_hex ARGS1(char, c)
! 87: {
! 88: return (c>='0')&&(c<='9') ? c-'0'
! 89: : (c>='A')&&(c<='F') ? c-'A'+10
! 90: : (c>='a')&&(c<='f') ? c-'a'+10
! 91: : 0;
! 92: }
! 93:
! 94:
! 95:
! 96: /* Get Styles from stylesheet
! 97: ** --------------------------
! 98: */
! 99: PRIVATE void get_styles NOARGS
! 100: {
! 101: if (!heading1Style) heading1Style = HTStyleNamed(styleSheet, "Heading1");
! 102: if (!addressStyle) addressStyle = HTStyleNamed(styleSheet, "Address");
! 103: if (!textStyle) textStyle = HTStyleNamed(styleSheet, "Example");
! 104: }
! 105:
! 106:
! 107: /* Paste in an Anchor
! 108: ** ------------------
! 109: **
! 110: ** The title of the destination is set, as there is no way
! 111: ** of knowing what the title is when we arrive.
! 112: **
! 113: ** On entry,
! 114: ** HT is in append mode.
! 115: ** text points to the text to be put into the file, 0 terminated.
! 116: ** addr points to the hypertext refernce address 0 terminated.
! 117: */
! 118: PRIVATE void write_anchor ARGS2(CONST char *,text, CONST char *,addr)
! 119: {
! 120: HTChildAnchor *anchor;
! 121: HTParentAnchor *dest;
! 122:
! 123: HText_beginAnchor(HT,
! 124: anchor = HTAnchor_findChildAndLink(node_anchor, "", addr, 0));
! 125: dest = HTAnchor_parent(
! 126: HTAnchor_followMainLink((HTAnchor *)anchor));
! 127:
! 128: if (!HTAnchor_title(dest)) HTAnchor_setTitle(dest, text);
! 129:
! 130: HText_appendText(HT, text);
! 131: HText_endAnchor(HT);
! 132: }
! 133:
! 134:
! 135: /* Parse a Gopher Menu document
! 136: ** ============================
! 137: **
! 138: */
! 139:
! 140: PRIVATE void parse_menu ARGS2 (
! 141: CONST char *, arg,
! 142: HTParentAnchor *,anAnchor)
! 143: {
! 144: char gtype;
! 145: char ch;
! 146: char line[BIG];
! 147: char address[BIG];
! 148: char *name, *selector; /* Gopher menu fields */
! 149: char *host;
! 150: char *port;
! 151: char *p = line;
! 152:
! 153:
! 154: #define TAB '\t'
! 155: #define HEX_ESCAPE '%'
! 156:
! 157: if (!HTAnchor_title(anAnchor))
! 158: HTAnchor_setTitle(anAnchor, arg);/* Tell user something's happening */
! 159:
! 160: node_anchor = anAnchor;
! 161: HT = HText_new(anAnchor);
! 162:
! 163: HText_beginAppend(HT);
! 164: HText_appendText(HT, "Select one of:\n\n");
! 165:
! 166: while ((ch=NEXT_CHAR) != (char)EOF) {
! 167:
! 168: if (ch != '\n') {
! 169: *p = ch; /* Put character in line */
! 170: if (p< &line[BIG-1]) p++;
! 171:
! 172: } else {
! 173: *p++ = 0; /* Terminate line */
! 174: p = line; /* Scan it to parse it */
! 175: port = 0; /* Flag "not parsed" */
! 176: if (TRACE) fprintf(stderr, "HTGopher: Menu item: %s\n", line);
! 177: gtype = *p++;
! 178:
! 179: /* Break on line with a dot by itself */
! 180: if ((gtype=='.') && ((*p=='\r') || (*p==0))) break;
! 181:
! 182: if (gtype && *p) {
! 183: name = p;
! 184: selector = strchr(name, TAB);
! 185: if (selector) {
! 186: *selector++ = 0; /* Terminate name */
! 187: host = strchr(selector, TAB);
! 188: if (host) {
! 189: *host++ = 0; /* Terminate selector */
! 190: port = strchr(host, TAB);
! 191: if (port) {
! 192: char *junk;
! 193: port[0] = ':'; /* delimit host a la W3 */
! 194: junk = strchr(port, TAB);
! 195: if (junk) *junk++ = 0; /* Chop port */
! 196: if ((port[1]=='0') && (!port[2]))
! 197: port[0] = 0; /* 0 means none */
! 198: } /* no port */
! 199: } /* host ok */
! 200: } /* selector ok */
! 201: } /* gtype and name ok */
! 202:
! 203: if (gtype == GOPHER_WWW) { /* Gopher pointer to W3 */
! 204: write_anchor(name, selector);
! 205: HText_appendParagraph(HT);
! 206:
! 207: } else if (port) { /* Other types need port */
! 208: if (gtype == GOPHER_TELNET) {
! 209: if (*selector) sprintf(address, "telnet://%s@%s/",
! 210: selector, host);
! 211: else sprintf(address, "telnet://%s/", host);
! 212:
! 213: } else { /* If parsed ok */
! 214: char *q;
! 215: char *p;
! 216: sprintf(address, "//%s/%c", host, gtype);
! 217: q = address+ strlen(address);
! 218: for(p=selector; *p; p++) { /* Encode selector string */
! 219: if (acceptable[*p]) *q++ = *p;
! 220: else {
! 221: *q++ = HEX_ESCAPE; /* Means hex coming */
! 222: *q++ = hex[(TOASCII(*p)) >> 4];
! 223: *q++ = hex[(TOASCII(*p)) & 15];
! 224: }
! 225: }
! 226: *q++ = 0; /* terminate address */
! 227: }
! 228: HText_appendText(HT, " "); /* Prettier JW/TBL */
! 229: write_anchor(name, address);
! 230: HText_appendParagraph(HT);
! 231: } else { /* parse error */
! 232: if (TRACE) fprintf(stderr,
! 233: "HTGopher: Bad menu item.\n");
! 234: HText_appendText(HT, line);
! 235: HText_appendParagraph(HT);
! 236: } /* parse error */
! 237:
! 238: p = line; /* Start again at beginning of line */
! 239:
! 240: } /* if end of line */
! 241:
! 242: } /* Loop over characters */
! 243:
! 244: HText_endAppend(HT);
! 245: return;
! 246: }
! 247:
! 248: /* Display a Gopher Index document
! 249: ** -------------------------------
! 250: */
! 251:
! 252: PRIVATE void display_index ARGS2 (
! 253: CONST char *, arg,
! 254: HTParentAnchor *,anAnchor)
! 255: {
! 256: node_anchor = anAnchor;
! 257: HT = HText_new(anAnchor);
! 258: HText_beginAppend(HT);
! 259: HText_setStyle(HT, heading1Style);
! 260: HText_appendText(HT, arg);
! 261: HText_setStyle(HT, textStyle);
! 262: HText_appendText(HT, "\nThis is a searchable index.\n");
! 263:
! 264: if (!HTAnchor_title(anAnchor))
! 265: HTAnchor_setTitle(anAnchor, arg);/* Tell user something's happening */
! 266:
! 267: HText_endAppend(HT);
! 268: return;
! 269: }
! 270:
! 271:
! 272: /* De-escape a selector into a command
! 273: ** -----------------------------------
! 274: **
! 275: ** The % hex escapes are converted. Otheriwse, the string is copied.
! 276: */
! 277: PRIVATE void de_escape ARGS2(char *, command, CONST char *, selector)
! 278: {
! 279: CONST char * p = selector;
! 280: char * q = command;
! 281: if (command == NULL) outofmem(__FILE__, "HTLoadGopher");
! 282: while (*p) { /* Decode hex */
! 283: if (*p == HEX_ESCAPE) {
! 284: char c;
! 285: unsigned int b;
! 286: p++;
! 287: c = *p++;
! 288: b = from_hex(c);
! 289: c = *p++;
! 290: if (!c) break; /* Odd number of chars! */
! 291: *q++ = FROMASCII((b<<4) + from_hex(c));
! 292: } else {
! 293: *q++ = *p++; /* Record */
! 294: }
! 295: }
! 296: *q++ = 0; /* Terminate command */
! 297:
! 298: }
! 299:
! 300:
! 301: /* Load by name HTLoadGopher
! 302: ** ============
! 303: **
! 304: ** Bug: No decoding of strange data types as yet.
! 305: **
! 306: */
! 307: PUBLIC int HTLoadGopher ARGS3(
! 308: CONST char *,arg,
! 309: HTParentAnchor *,anAnchor,
! 310: int,diag)
! 311: {
! 312: char *command; /* The whole command */
! 313: int status; /* tcp return */
! 314: char gtype; /* Gopher Node type */
! 315: char * selector; /* Selector string */
! 316:
! 317: struct sockaddr_in soc_address; /* Binary network address */
! 318: struct sockaddr_in* sin = &soc_address;
! 319:
! 320: diagnostic = diag; /* set global flag */
! 321:
! 322: if (!acceptable_inited) init_acceptable();
! 323:
! 324: if (!arg) return -3; /* Bad if no name sepcified */
! 325: if (!*arg) return -2; /* Bad if name had zero length */
! 326:
! 327: if (TRACE) fprintf(stderr, "HTGopher: Looking for %s\n", arg);
! 328: get_styles();
! 329:
! 330:
! 331: /* Set up defaults:
! 332: */
! 333: sin->sin_family = AF_INET; /* Family, host order */
! 334: sin->sin_port = htons(GOPHER_PORT); /* Default: new port, */
! 335:
! 336: if (TRACE) fprintf(stderr, "HTTPAccess: Looking for %s\n", arg);
! 337:
! 338: /* Get node name and optional port number:
! 339: */
! 340: {
! 341: char *p1 = HTParse(arg, "", PARSE_HOST);
! 342: int status = HTParseInet(sin, p1);
! 343: free(p1);
! 344: if (status) return status; /* Bad */
! 345: }
! 346:
! 347: /* Get entity type, and selector string.
! 348: */
! 349: {
! 350: char * p1 = HTParse(arg, "", PARSE_PATH|PARSE_PUNCTUATION);
! 351: gtype = '1'; /* Default = menu */
! 352: selector = p1;
! 353: if ((*selector++=='/') && (*selector)) { /* Skip first slash */
! 354: gtype = *selector++; /* Pick up gtype */
! 355: }
! 356: if (gtype == GOPHER_INDEX) {
! 357: char * query;
! 358: HTAnchor_setIndex(anAnchor); /* Search is allowed */
! 359: query = strchr(selector, '?'); /* Look for search string */
! 360: if (!query || !query[1]) { /* No search required */
! 361: display_index(arg, anAnchor); /* Display "cover page" */
! 362: return 1; /* Local function only */
! 363: }
! 364: *query++ = 0; /* Skip '?' */
! 365: command = malloc(strlen(selector)+ 1 + strlen(query)+ 2 + 1);
! 366: if (command == NULL) outofmem(__FILE__, "HTLoadGopher");
! 367:
! 368: de_escape(command, selector); /* Bug fix TBL 921208 */
! 369:
! 370: strcat(command, "\t");
! 371:
! 372: { /* Remove plus signs 921006 */
! 373: char *p;
! 374: for (p=query; *p; p++) {
! 375: if (*p == '+') *p = ' ';
! 376: }
! 377: }
! 378: strcat(command, query);
! 379:
! 380: } else { /* Not index */
! 381: command = command = malloc(strlen(selector)+2+1);
! 382: de_escape(command, selector);
! 383: }
! 384: free(p1);
! 385: }
! 386:
! 387: strcat(command, "\r\n"); /* Include CR for telnet compat. */
! 388:
! 389:
! 390: /* Set up a socket to the server for the data:
! 391: */
! 392: s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
! 393: status = connect(s, (struct sockaddr*)&soc_address, sizeof(soc_address));
! 394: if (status<0){
! 395: if (TRACE) fprintf(stderr, "HTTPAccess: Unable to connect to remote host for `%s'.\n",
! 396: arg);
! 397: free(command);
! 398: return HTInetStatus("connect");
! 399: }
! 400:
! 401: HTInitInput(s); /* Set up input buffering */
! 402:
! 403: if (TRACE) fprintf(stderr, "HTGopher: Connected, writing command `%s' to socket %d\n", command, s);
! 404:
! 405: #ifdef NOT_ASCII
! 406: {
! 407: char * p;
! 408: for(p = command; *p; p++) {
! 409: *p = TOASCII(*p);
! 410: }
! 411: }
! 412: #endif
! 413:
! 414: status = NETWRITE(s, command, (int)strlen(command));
! 415: free(command);
! 416: if (status<0){
! 417: if (TRACE) fprintf(stderr, "HTGopher: Unable to send command.\n");
! 418: return HTInetStatus("send");
! 419: }
! 420:
! 421: /* Now read the data from the socket:
! 422: */
! 423: if (diagnostic==2) gtype = GOPHER_TEXT; /* Read as plain text anyway */
! 424:
! 425: switch (gtype) {
! 426:
! 427: case GOPHER_HTML :
! 428: HTParseFormat(WWW_HTML, anAnchor, s);
! 429: NETCLOSE(s);
! 430: return 1;
! 431:
! 432: case GOPHER_MENU :
! 433: case GOPHER_INDEX :
! 434: parse_menu(arg, anAnchor);
! 435: NETCLOSE(s);
! 436: return 1;
! 437:
! 438: case GOPHER_TEXT :
! 439: default: /* @@ parse as plain text */
! 440: HTParseFormat(WWW_PLAINTEXT, anAnchor, s);
! 441: NETCLOSE(s);
! 442: return 1;
! 443: } /* switch(gtype) */
! 444: /*NOTREACHED*/
! 445: }
! 446:
Webmaster