?? htgopher.c
字號:
/* HTGopher.c** GOPHER ACCESS**** (c) COPYRIGHT MIT 1995.** Please first read the full copyright statement in the file COPYRIGH.**** History:** 26 Sep 90 Adapted from other accesses (News, HTTP) TBL** 29 Nov 91 Downgraded to C, for portable implementation.** 28 Apr 94 target no more global and icons implemented** HF, frystyk@w3.org** 2 May 94 Fixed possible security hole when the URL contains** a newline, that could cause multiple commands to be** sent to a Gopher server. AL, luotonen@www.cern.ch** 12 May 94 Checked and made ready for multi-threads, Frystyk** 8 Jul 94 FM Insulate free() from _free structure element.** Sep 95 HFN Made non-blocking and state stream oriented*//* Library include files */#include "wwwsys.h"#include "WWWUtil.h"#include "WWWCore.h"#include "WWWHTML.h"#include "WWWDir.h"#include "WWWTrans.h"#include "HTNetMan.h"#include "HTGopher.h" /* Implemented here *//* Macros and other defines */#ifndef GOPHER_PORT#define GOPHER_PORT 70 /* See protocol spec */#endif/* Hypertext object building machinery */#define PUTC(c) (*target->isa->put_character)(target, c)#define PUTS(s) (*target->isa->put_string)(target, s)#define START(e) (*target->isa->start_element)(target, e, 0, 0)#define END(e) (*target->isa->end_element)(target, e)/* Type definitions and global variables etc. local to this module */typedef enum _HTGopherType { GT_TEXT = '0', GT_MENU = '1', GT_CSO = '2', GT_ERROR = '3', GT_MACBINHEX = '4', GT_PCBINHEX = '5', GT_UUENCODED = '6', GT_INDEX = '7', GT_TELNET = '8', GT_BINARY = '9', GT_GIF = 'g', GT_HTML = 'h', /* HTML */ GT_INFO = 'i', GT_SOUND = 's', GT_WWW = 'w', /* W3 address */ GT_IMAGE = 'I', GT_TN3270 = 'T', GT_DUPLICATE = '+', GT_PLUS_IMAGE = ':', /* Addition from Gopher Plus */ GT_PLUS_MOVIE = ';', GT_PLUS_SOUND = '<', GT_EOF = '.'} HTGopherType;/* Final states have negative value */typedef enum _GopherState { GOPHER_ERROR = -3, GOPHER_NO_DATA = -2, GOPHER_GOT_DATA = -1, GOPHER_BEGIN = 0, GOPHER_NEED_CONNECTION, GOPHER_NEED_REQUEST, GOPHER_NEED_RESPONSE} GopherState;/* This is the context structure for the this module */typedef struct _gopher_info { HTGopherType type; /* Gopher item type */ GopherState state; char * cmd; HTNet * net;} gopher_info;#define MAX_GOPHER_LINE 256struct _HTStructured { const HTStructuredClass * isa;};struct _HTStream { const HTStreamClass * isa; HTStructured * target; HTRequest * request; HTEOLState state; char * url; BOOL pre; /* Preformatted mode? */ BOOL junk; /* For too long lines */ BOOL CSO; char cso_rec[10]; /* CSO record number */ char buffer[MAX_GOPHER_LINE+1]; int buflen;};struct _HTInputStream { const HTInputStreamClass * isa;};PRIVATE HTDirShow dir_show = HT_DS_ICON;/* ------------------------------------------------------------------------- *//* GopherIcon** ----------** This function finds an appopriate icon for the item in the gopher** list. Actually it is only a shell build upon HTIcon_find().*/PRIVATE HTIconNode *GopherIcon (HTGopherType type){ HTFormat content_type = NULL; HTEncoding content_encoding = NULL; HTFileMode mode = HT_IS_FILE; switch(type) { case GT_MENU: mode = HT_IS_DIR; case GT_TEXT: content_type = HTAtom_for("text/void"); break; case GT_IMAGE: case GT_PLUS_IMAGE: case GT_GIF: content_type = HTAtom_for("image/void"); break; case GT_WWW: case GT_HTML: content_type = HTAtom_for("text/void"); break; case GT_SOUND: case GT_PLUS_SOUND: content_type = HTAtom_for("audio/void"); break; case GT_PLUS_MOVIE: content_type = HTAtom_for("video/void"); break; case GT_INDEX: content_type = HTAtom_for("application/x-gopher-index"); break; case GT_CSO: content_type = HTAtom_for("application/x-gopher-cso"); break; case GT_TELNET: content_type = HTAtom_for("application/x-gopher-telnet"); break; case GT_TN3270: content_type = HTAtom_for("application/x-gopher-tn3270"); break; case GT_DUPLICATE: content_type = HTAtom_for("application/x-gopher-duplicate"); break; case GT_ERROR: content_type = HTAtom_for("www/unknown"); break; case GT_BINARY: content_type = WWW_BINARY; break; default: content_type = HTAtom_for("www/unknown"); break; } return HTIcon_find(mode, content_type, content_encoding);}/* ------------------------------------------------------------------------- *//* STREAMS *//* ------------------------------------------------------------------------- *//* GopherTitle** -----------** Create the top part of the page*/PRIVATE BOOL GopherTitle (HTStream *me){ HTStructured *target = me->target; char *str = NULL; StrAllocCopy(str, me->CSO ? "CSO Search " : "GopherMenu"); START(HTML_HTML); START(HTML_HEAD); START(HTML_TITLE); if (me->CSO) { char *keyword = strchr(me->url, '?'); if (keyword) { StrAllocCat(str, "for "); StrAllocCat(str, ++keyword); } } PUTS(str); END(HTML_TITLE); END(HTML_HEAD); START(HTML_BODY); START(HTML_H1); PUTS(str); END(HTML_H1); HT_FREE(str); return YES;}/* GopherBottom** ------------** Create the bottom part of the page*/PRIVATE BOOL GopherBottom (HTStream *me){ HTStructured *target = me->target; if (me->pre) END(HTML_PRE); END(HTML_BODY); END(HTML_HTML); return YES;}/* GopherMenuLine** --------------** Parses a Gopher Menu Line** Return YES if more data else NO*/PRIVATE BOOL GopherMenuLine (HTStream *me, char *line){ HTStructured *target = me->target; HTGopherType gtype = (HTGopherType) *line++; HTTRACE(PROT_TRACE, "HTGopher.... Menu line: `%s\'\n" _ line); if (gtype == GT_INFO) { char *stop = strchr(line, '\t'); if (stop) *stop = '\0'; PUTS(line); } else if (gtype == GT_ERROR) { char *stop = strchr(line, '\t'); if (stop) *stop = '\0'; PUTS(line); } else if ((strstr(line, "error.host") || strstr(line, "errorhost"))) { char *stop = strchr(line, '\t'); /* Chop off error.host */ if (stop) *stop = '\0'; PUTS(line); } else if (gtype == GT_EOF) { return NO; } else { /* Parse normal gopher menu line */ char *name = line; /* Get link information */ char *selector = strchr(name, '\t'); char *host = NULL; char *port = NULL; if (selector) { *selector++ = '\0'; if ((host = strchr(selector, '\t'))) { *host++ = '\0'; if ((port = strchr(host, '\t'))) { char *junk; *port = ':'; /* delimit host a la W3 */ if ((junk = strchr(port, '\t')) != NULL) *junk = '\0'; /* Chop port */ if (*(port+1) == '0' && !*(port+2)) *port = '\0'; } } } if (!me->pre) { /* For now we use preformatted listing */ START(HTML_PRE); me->pre = YES; } if (dir_show & HT_DS_ICON) { /* Put out the icon */ HTIconNode *icon = GopherIcon(gtype); if (icon) { char * alt = HTIcon_alternative(icon, YES); HTMLPutImg(target, HTIcon_url(icon), alt, NULL); HT_FREE(alt); PUTC(' '); } } if (gtype == GT_WWW) { /* Gopher pointer to W3 */ char *escaped = NULL; escaped = HTEscape(selector, URL_PATH); HTStartAnchor(target, NULL, escaped); PUTS(name); END(HTML_A); HT_FREE(escaped); } else if (port) { /* Other types need port */ char *escaped = NULL; char *address = NULL; int addr_len; /* Calculate the length of the WWW-address */ if (selector && *selector) { escaped = HTEscape(selector, URL_PATH); addr_len = 15 + strlen(escaped) + strlen(host) + 1; } else { addr_len = 15 + strlen(host) + 1; } if ((address = (char *) HT_MALLOC(addr_len)) == NULL) HT_OUTOFMEM("GopherMenuLine"); *address = '\0'; if (gtype == GT_TELNET) { if (escaped) sprintf(address, "telnet://%s@%s/", escaped, host); else sprintf(address, "telnet://%s/", host); } else if (gtype == GT_TN3270) { if (escaped) sprintf(address, "tn3270://%s@%s/", escaped, host); else sprintf(address, "tn3270://%s/", host); } else { if (escaped) sprintf(address, "//%s/%c%s", host, gtype, escaped); else sprintf(address, "//%s/%c", host, gtype); } HTStartAnchor(target, NULL, address); PUTS(name); END(HTML_A); HT_FREE(address); HT_FREE(escaped); PUTC('\n'); } else { /* If parse error */ HTTRACE(PROT_TRACE, "HTGopher.... Bad menu item, `%s\'\n" _ line); } } return YES;}/* GopherCSOLine** --------------** Parses a Gopher Menu Line** Return YES if more data else NO*/PRIVATE BOOL GopherCSOLine (HTStream *me, char *line){ HTStructured *target = me->target; if (*line == '1') { /* Information line */ char *start = strchr(line, ':'); if (start) start++; else start=line; PUTS(start); } else if (*line == '2') { /* Transfer complete */ return NO; } else if (*line == '5') { /* Error */ char *start = strchr(line, ':'); if (start) start++; else start=line; PUTS(start); } else if (*line == '-') { /* data */ /* data lines look like '-200:code:field:value' * where code is the search result number and can be * multiple digits (infinte?) * find the second colon and check the digit to the * left of it to see if they are diferent * if they are then a different person is starting. */ char *code; char *field; if ((code = strchr(line, ':')) && (field = strchr(++code, ':'))) { BOOL newrec = YES; *field++ = '\0'; if (!*me->cso_rec) { /* Header of first record */ START(HTML_DL); } else if (strcmp(me->cso_rec, code)) { /* Another new record */ START(HTML_B); } else newrec = NO; START(HTML_DT); /* I'm not sure whether the name field comes in any * special order or if its even required in a * record, so for now the first line is the header * no matter what it is (it's almost always the * alias) */ { char *value = strchr(field, ':'); if (!value) value = "Empty value"; else *value++ = '\0'; { char *strip = HTStrip(field); PUTS(strip); START(HTML_DD); strip = HTStrip(value); if (newrec) { PUTS(strip); END(HTML_B); } else PUTS(strip); } /* Save the code for comparison on the next pass */ strcpy(me->cso_rec, code); } } } else { /* Unknown line */ char *start = strchr(line, ':'); if (start) start++; else start=line; PUTS(start); } return YES;}/*** Searches for Gopher line until buffer fills up or a CRLF or LF is found*/PRIVATE int GopherMenu_put_block (HTStream * me, const char * b, int l){ while (l-- > 0) { if (me->state == EOL_FCR) { if (*b == LF && me->buflen) { if (!me->junk) { BOOL cont; *(me->buffer+me->buflen) = '\0';
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -