/* GOPHER ACCESS HTGopher.c
** =============
**
** History:
** 26 Sep 90 Adapted from other accesses (News, HTTP) TBL
** 29 Nov 91 Downgraded to C, for portable implementation.
** 10 Mar 96 Foteos Macrides (macrides@sci.wfbr.edu). Added a
** form-based CSO/PH gateway. Can be invoked via a
** "cso://host[:port]/" or "gopher://host:105/2"
** URL. If a gopher URL is used with a query token
** ('?'), the old ISINDEX procedure will be used
** instead of the form-based gateway.
** 15 Mar 96 Foteos Macrides (macrides@sci.wfbr.edu). Pass
** port 79, gtype 0 gopher URLs to the finger
** gateway.
*/
#include "HTUtils.h" /* Coding convention macros */
#include "tcp.h"
#include "HTAlert.h"
#include "HTParse.h"
#include "HTTCP.h"
#include "HTFinger.h"
/* Implements:
*/
#include "HTGopher.h"
#define HT_EM_SPACE ((char)2) /* For now */
#define GOPHER_PORT 70 /* See protocol spec */
#define CSO_PORT 105 /* See protocol spec */
#define BIG 1024 /* Bug */
#define LINE_LENGTH 256 /* Bug */
/* Gopher entity types:
*/
#define GOPHER_TEXT '0'
#define GOPHER_MENU '1'
#define GOPHER_CSO '2'
#define GOPHER_ERROR '3'
#define GOPHER_MACBINHEX '4'
#define GOPHER_PCBINARY '5'
#define GOPHER_UUENCODED '6'
#define GOPHER_INDEX '7'
#define GOPHER_TELNET '8'
#define GOPHER_BINARY '9'
#define GOPHER_GIF 'g'
#define GOPHER_HTML 'h' /* HTML */
#define GOPHER_CHTML 'H' /* HTML */
#define GOPHER_SOUND 's'
#define GOPHER_WWW 'w' /* W3 address */
#define GOPHER_IMAGE 'I'
#define GOPHER_TN3270 'T'
#define GOPHER_INFO 'i'
#define GOPHER_DUPLICATE '+'
#define GOPHER_PLUS_IMAGE ':' /* Addition from Gopher Plus */
#define GOPHER_PLUS_MOVIE ';'
#define GOPHER_PLUS_SOUND '<'
#define GOPHER_PLUS_PDF 'P'
#include <ctype.h>
#include "HTParse.h"
#include "HTFormat.h"
#include "HTTCP.h"
#define FREE(x) if (x) {free(x); x = NULL;}
/* Hypertext object building machinery
*/
#include "HTML.h"
#include "LYLeaks.h"
#define PUTC(c) (*targetClass.put_character)(target, c)
#define PUTS(s) (*targetClass.put_string)(target, s)
#define START(e) (*targetClass.start_element)(target, e, 0, 0, 0)
#define END(e) (*targetClass.end_element)(target, e, 0)
#define FREE_TARGET (*targetClass._free)(target)
#define GOPHER_PROGRESS(foo) HTAlert(foo)
#define NEXT_CHAR HTGetCharacter()
/* Module-wide variables
*/
PRIVATE int s; /* Socket for gopher or CSO host */
struct _HTStructured {
CONST HTStructuredClass * isa; /* For gopher streams */
/* ... */
};
PRIVATE HTStructured *target; /* the new gopher hypertext */
PRIVATE HTStructuredClass targetClass; /* Its action routines */
struct _HTStream
{
HTStreamClass * isa; /* For form-based CSO gateway - FM */
};
typedef struct _CSOfield_info { /* For form-based CSO gateway - FM */
struct _CSOfield_info * next;
char * name;
char * attributes;
char * description;
int id;
int lookup;
int indexed;
int url;
int max_size;
int defreturn;
int explicit_return;
int reserved;
int public;
char name_buf[16]; /* Avoid malloc if we can */
char desc_buf[32]; /* Avoid malloc if we can */
char attr_buf[80]; /* Avoid malloc if we can */
} CSOfield_info;
PRIVATE CSOfield_info *CSOfields = NULL; /* For form-based CSO gateway - FM */
typedef struct _CSOformgen_context { /* For form-based CSO gateway - FM */
char * host;
char * seek;
CSOfield_info * fld;
int port;
int cur_line;
int cur_off;
int rep_line;
int rep_off;
int public_override;
int field_select;
} CSOformgen_context;
/* Matrix of allowed characters in filenames
** =========================================
*/
PRIVATE BOOL acceptable[256];
PRIVATE BOOL acceptable_inited = NO;
PRIVATE void init_acceptable NOARGS
{
unsigned int i;
char * good =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./-_$";
for(i = 0; i < 256; i++)
acceptable[i] = NO;
for(; *good; good++)
acceptable[(unsigned int)*good] = YES;
acceptable_inited = YES;
}
/* Decode one hex character
** ========================
*/
PRIVATE CONST char hex[17] = "0123456789abcdef";
PRIVATE char from_hex ARGS1(char, c)
{
return (c>='0')&&(c<='9') ? c-'0'
: (c>='A')&&(c<='F') ? c-'A'+10
: (c>='a')&&(c<='f') ? c-'a'+10
: 0;
}
/* Paste in an Anchor
** ==================
**
** The title of the destination is set, as there is no way
** of knowing what the title is when we arrive.
**
** On entry,
** HT is in append mode.
** text points to the text to be put into the file, 0 terminated.
** addr points to the hypertext refernce address 0 terminated.
*/
PUBLIC BOOLEAN HT_Is_Gopher_URL=FALSE;
PRIVATE void write_anchor ARGS2(CONST char *,text, CONST char *,addr)
{
BOOL present[HTML_A_ATTRIBUTES];
CONST char * value[HTML_A_ATTRIBUTES];
int i;
for (i = 0; i < HTML_A_ATTRIBUTES; i++)
present[i] = 0;
present[HTML_A_HREF] = YES;
((CONST char **)value)[HTML_A_HREF] = addr;
present[HTML_A_TITLE] = YES;
((CONST char **)value)[HTML_A_TITLE] = text;
if(TRACE)
fprintf(stderr,"HTGopher: adding URL: %s\n",addr);
HT_Is_Gopher_URL = TRUE; /* tell HTML.c that this is a Gopher URL */
(*targetClass.start_element)(target, HTML_A, present,
(CONST char **)value, 0);
PUTS(text);
END(HTML_A);
}
/* Parse a Gopher Menu document
** ============================
*/
PRIVATE void parse_menu ARGS2(
CONST char *, arg,
HTParentAnchor *, anAnchor)
{
char gtype;
char ch;
char line[BIG];
char address[BIG];
char *name = NULL, *selector = NULL; /* Gopher menu fields */
char *host = NULL;
char *port;
char *p = line;
CONST char *title;
int bytes = 0;
int BytesReported = 0;
char buffer[128];
extern int interrupted_in_htgetcharacter;
#define TAB '\t'
#define HEX_ESCAPE '%'
START(HTML_HTML);
PUTS("\n");
START(HTML_HEAD);
PUTS("\n");
START(HTML_TITLE);
if ((title = HTAnchor_title(anAnchor)))
PUTS(title);
else
PUTS("Gopher Menu");
END(HTML_TITLE);
PUTS("\n");
END(HTML_HEAD);
PUTS("\n");
START(HTML_BODY);
PUTS("\n");
START(HTML_H1);
if ((title = HTAnchor_title(anAnchor)))
PUTS(title);
else
PUTS("Gopher Menu");
END(HTML_H1);
PUTS("\n");
START(HTML_PRE);
while ((ch=NEXT_CHAR) != (char)EOF) {
if (interrupted_in_htgetcharacter) {
if (TRACE)
fprintf(stderr,
"HTGopher: Interrupted in HTGetCharacter, apparently.\n");
goto end_html;
}
if (ch != LF) {
*p = ch; /* Put character in line */
if (p< &line[BIG-1]) p++;
} else {
*p++ = '\0'; /* Terminate line */
bytes += p-line; /* add size */
p = line; /* Scan it to parse it */
port = 0; /* Flag "not parsed" */
if (TRACE)
fprintf(stderr, "HTGopher: Menu item: %s\n", line);
gtype = *p++;
if (bytes > BytesReported + 1024) {
sprintf(buffer, "Transferred %d bytes", bytes);
HTProgress(buffer);
BytesReported = bytes;
}
/* Break on line with a dot by itself */
if ((gtype=='.') && ((*p=='\r') || (*p==0)))
break;
if (gtype && *p) {
name = p;
selector = strchr(name, TAB);
if (selector) {
*selector++ = '\0'; /* Terminate name */
/*
* Gopher+ Type=0+ objects can be binary, and will
* have 9 or 5 beginning their selector. Make sure
* we don't trash the terminal by treating them as
* text. - FM
*/
if (gtype == GOPHER_TEXT && (*selector == GOPHER_BINARY ||
*selector == GOPHER_PCBINARY))
gtype = *selector;
host = strchr(selector, TAB);
if (host) {
*host++ = '\0'; /* Terminate selector */
port = strchr(host, TAB);
if (port) {
char *junk;
port[0] = ':'; /* delimit host a la W3 */
junk = strchr(port, TAB);
if (junk) *junk++ = '\0'; /* Chop port */
if ((port[1]=='0') && (!port[2]))
port[0] = '\0'; /* 0 means none */
} /* no port */
} /* host ok */
} /* selector ok */
} /* gtype and name ok */
/* Nameless files are a separator line */
if (gtype == GOPHER_TEXT) {
int i = strlen(name)-1;
while (name[i] == ' ' && i >= 0)
name[i--] = '\0';
if (i < 0)
gtype = GOPHER_INFO;
}
if (gtype == GOPHER_WWW) { /* Gopher pointer to W3 */
PUTS("(HTML) ");
write_anchor(name, selector);
} else if (gtype == GOPHER_INFO) {
/* Information or separator line */
PUTS(" ");
PUTS(name);
} else if (port) { /* Other types need port */
if (gtype == GOPHER_TELNET) {
PUTS(" (TEL) ");
if (*selector) sprintf(address, "telnet://%s@%s/",
selector, host);
else sprintf(address, "telnet://%s/", host);
}
else if (gtype == GOPHER_TN3270)
{
PUTS("(3270) ");
if (*selector)
sprintf(address, "tn3270://%s@%s/",
selector, host);
else
sprintf(address, "tn3270://%s/", host);
}
else { /* If parsed ok */
char *q;
char *p;
switch(gtype) {
case GOPHER_TEXT:
PUTS("(FILE) ");
break;
case GOPHER_MENU:
PUTS(" (DIR) ");
break;
case GOPHER_CSO:
PUTS(" (CSO) ");
break;
case GOPHER_PCBINARY:
PUTS(" (BIN) ");
break;
case GOPHER_UUENCODED:
PUTS(" (UUE) ");
break;
case GOPHER_INDEX:
PUTS(" (?) ");
break;
case GOPHER_BINARY:
PUTS(" (BIN) ");
break;
case GOPHER_GIF:
case GOPHER_IMAGE:
case GOPHER_PLUS_IMAGE:
PUTS(" (IMG) ");
break;
case GOPHER_SOUND:
case GOPHER_PLUS_SOUND:
PUTS(" (SND) ");
break;
case GOPHER_MACBINHEX:
PUTS(" (HQX) ");
break;
case GOPHER_HTML:
case GOPHER_CHTML:
PUTS("(HTML) ");
break;
case 'm':
PUTS("(MIME) ");
break;
case GOPHER_PLUS_MOVIE:
PUTS(" (MOV) ");
break;
case GOPHER_PLUS_PDF:
PUTS(" (PDF) ");
break;
default:
PUTS("(UNKN) ");
break;
}
sprintf(address, "//%s/%c", host, gtype);
q = address+ strlen(address);
for(p=selector; *p; p++) { /* Encode selector string */
if (acceptable[(unsigned char)*p]) *q++ = *p;
else {
*q++ = HEX_ESCAPE; /* Means hex coming */
*q++ = hex[(TOASCII(*p)) >> 4];
*q++ = hex[(TOASCII(*p)) & 15];
}
}
*q++ = '\0'; /* terminate address */
}
/* Error response from Gopher doesn't deserve to
be a hyperlink. */
if (strcmp (address, "gopher://error.host:1/0"))
write_anchor(name, address);
else
PUTS(name);
} else { /* parse error */
if (TRACE)
fprintf(stderr, "HTGopher: Bad menu item.\n");
PUTS(line);
} /* parse error */
PUTS("\n");
p = line; /* Start again at beginning of line */
} /* if end of line */
} /* Loop over characters */
end_html:
END(HTML_PRE);
PUTS("\n");
END(HTML_BODY);
PUTS("\n");
END(HTML_HTML);
PUTS("\n");
FREE_TARGET;
return;
}
/* Parse a Gopher CSO document from and ISINDEX query.
** ===================================================
**
** Accepts an open socket to a CSO server waiting to send us
** data and puts it on the screen in a reasonable manner.
**
** Perhaps this data can be automatically linked to some
** other source as well???
**
** Taken from hacking by Lou Montulli@ukanaix.cc.ukans.edu
** on XMosaic-1.1, and put on libwww 2.11 by Arthur Secret,
** secret@dxcern.cern.ch .
*/
PRIVATE void parse_cso ARGS2(
CONST char *, arg,
HTParentAnchor *, anAnchor)
{
char ch;
char line[BIG];
char *p = line;
char *second_colon, last_char='\0';
CONST char *title;
START(HTML_HEAD);
PUTS("\n");
START(HTML_TITLE);
if ((title = HTAnchor_title(anAnchor)))
PUTS(title);
else
PUTS("CSO Search Results");
END(HTML_TITLE);
PUTS("\n");
END(HTML_HEAD);
PUTS("\n");
START(HTML_H1);
if ((title = HTAnchor_title(anAnchor)))
PUTS(title);
else {
PUTS(arg);
PUTS(" Search Results");
}
END(HTML_H1);
PUTS("\n");
START(HTML_PRE);
/* start grabbing chars from the network */
while ((ch=NEXT_CHAR) != (char)EOF)
{
if (ch != LF)
{
*p = ch; /* Put character in line */
if (p< &line[BIG-1]) p++;
}
else
{
*p = '\0'; /* Terminate line */
p = line; /* Scan it to parse it */
/* OK we now have a line in 'p' lets parse it and
print it */
/* Break on line that begins with a 2. It's the end of
* data.
*/
if (*p == '2')
break;
/* lines beginning with 5 are errors,
* print them and quit
*/
if (*p == '5') {
START(HTML_H2);
PUTS(p+4);
END(HTML_H2);
break;
}
if(*p == '-') {
/* data lines look like -200:#:
* where # is the search result number and can be
* multiple digits (infinate?)
* 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.
* make this line an <h2>
*/
/* find the second_colon */
second_colon = strchr( strchr(p,':')+1, ':');
if(second_colon != NULL) { /* error check */
if (*(second_colon-1) != last_char)
/* print seperator */
{
END(HTML_PRE);
START(HTML_H2);
}
/* right now the record appears with the alias
* (first line)
* as the header and the rest as <pre> text
* It might look better with the name as the
* header and the rest as a <ul> with <li> tags
* 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)
* A <dl> with the first line as the <DT> and
* the rest as some form of <DD> might good also?
*/
/* print data */
PUTS(second_colon+1);
PUTS("\n");
if (*(second_colon-1) != last_char)
/* end seperator */
{
END(HTML_H2);
START(HTML_PRE);
}
/* save the char before the second colon
* for comparison on the next pass
*/
last_char = *(second_colon-1) ;
} /* end if second_colon */
} /* end if *p == '-' */
} /* if end of line */
} /* Loop over characters */
/* end the text block */
PUTS("\n");
END(HTML_PRE);
PUTS("\n");
FREE_TARGET;
return; /* all done */
} /* end of procedure */
/* Display a Gopher CSO ISINDEX cover page
** =======================================
*/
PRIVATE void display_cso ARGS2(
CONST char *, arg,
HTParentAnchor *, anAnchor)
{
CONST char * title;
START(HTML_HEAD);
PUTS("\n");
START(HTML_TITLE);
if ((title = HTAnchor_title(anAnchor)))
PUTS(title);
else
PUTS("CSO index");
END(HTML_TITLE);
PUTS("\n");
START(HTML_ISINDEX);
PUTS("\n");
END(HTML_HEAD);
PUTS("\n");
START(HTML_H1);
if ((title = HTAnchor_title(anAnchor)))
PUTS(title);
else {
PUTS(arg);
PUTS(" index");
}
END(HTML_H1);
PUTS("\nThis is a searchable index of a CSO database.\n");
START(HTML_P);
PUTS("\nPress the 's' key and enter search keywords.\n");
START(HTML_P);
PUTS("\nThe keywords that you enter will allow you to search on a");
PUTS(" person's name in the database.\n");
if (!HTAnchor_title(anAnchor))
HTAnchor_setTitle(anAnchor, arg);
FREE_TARGET;
return;
}
/* Display a Gopher Index document
** ===============================
*/
PRIVATE void display_index ARGS2(
CONST char *, arg,
HTParentAnchor *,anAnchor)
{
CONST char * title;
START(HTML_HEAD);
PUTS("\n");
PUTS("\n");
START(HTML_TITLE);
if ((title = HTAnchor_title(anAnchor)))
PUTS(title);
else
PUTS("Gopher index");
END(HTML_TITLE);
PUTS("\n");
START(HTML_ISINDEX);
PUTS("\n");
END(HTML_HEAD);
PUTS("\n");
START(HTML_H1);
if ((title = HTAnchor_title(anAnchor)))
PUTS(title);
else {
PUTS(arg);
PUTS(" index");
}
END(HTML_H1);
PUTS("\nThis is a searchable Gopher index.\n");
START(HTML_P);
PUTS("\nPlease enter search keywords.\n");
if (!HTAnchor_title(anAnchor))
HTAnchor_setTitle(anAnchor, arg);
FREE_TARGET;
return;
}
/* De-escape a selector into a command
** ===================================
**
** The % hex escapes are converted. Otheriwse, the string is copied.
*/
PRIVATE void de_escape ARGS2(char *, command, CONST char *, selector)
{
CONST char * p = selector;
char * q = command;
if (command == NULL)
outofmem(__FILE__, "HTLoadGopher");
while (*p) { /* Decode hex */
if (*p == HEX_ESCAPE) {
char c;
unsigned int b;
p++;
c = *p++;
b = from_hex(c);
c = *p++;
if (!c) break; /* Odd number of chars! */
*q++ = FROMASCII((b<<4) + from_hex(c));
} else {
*q++ = *p++; /* Record */
}
}
*q++ = '\0'; /* Terminate command */
}
/* Free the CSOfields structures. - FM
** ===================================
*/
PRIVATE void free_CSOfields NOPARAMS
{
CSOfield_info *cur = CSOfields;
CSOfield_info *prev;
while (cur) {
if (cur->name != cur->name_buf)
FREE(cur->name);
if (cur->attributes != cur->attr_buf)
FREE(cur->attributes);
if (cur->description != cur->desc_buf)
FREE(cur->description);
prev = cur;
cur = cur->next;
FREE(prev);
}
return;
}
/* Interpret CSO/PH form template keys. - FM
** =========================================
*/
PRIVATE int interpret_cso_key ARGS5(
char *, key,
char *, buf,
int *, length,
CSOformgen_context *, ctx,
HTStream *, Target)
{
CSOfield_info *fld;
if (fld = ctx->fld) {
/*
* Most substitutions only recognized inside of loops.
*/
int error = 0;
if (0 == strncmp(key, "$(FID)", 6)) {
sprintf(buf, "%d", fld->id);
} else if (0 == strncmp(key, "$(FDESC)", 8)) {
sprintf(buf, "%s%s%s", fld->description,
ctx->public_override ? /***" "***/"" : "",
ctx->public_override ? /***fld->attributes***/"" : "");
} else if (0 == strncmp(key, "$(FDEF)", 7)) {
strcpy(buf, fld->defreturn ? " checked" : "");
} else if (0 == strncmp(key, "$(FNDX)", 7)) {
strcpy(buf, fld->indexed ? "*" : "");
} else if (0 == strncmp(key, "$(FSIZE)", 8)) {
sprintf(buf, " size=%d maxlength=%d",
fld->max_size > 55 ? 55 : fld->max_size,
fld->max_size);
} else if (0 == strncmp(key, "$(FSIZE2)", 9)) {
sprintf(buf, " maxlength=%d", fld->max_size);
} else {
error = 1;
}
if (!error) {
*length = strlen(buf);
return -1;
}
}
/*
*/
buf[0] = '\0';
if (0 == strncmp(key, "$(NEXTFLD)", 10)) {
if (!ctx->fld)
fld = CSOfields;
else
fld = ctx->fld->next;
switch (ctx->field_select) {
case 0:
/* 'Query' fields, public and lookup attributes */
for (; fld; fld = fld->next)
if (fld->public && (fld->lookup==1))
break;
break;
case 1:
/* 'Query' fields, accept lookup attribute */
for (; fld; fld = fld->next)
if (fld->lookup == 1)
break;
break;
case 2:
/* 'Return' fields, public only */
for (; fld; fld = fld->next)
if (fld->public)
break;
break;
case 3:
/* all fields */
break;
}
if (fld) {
ctx->cur_line = ctx->rep_line;
ctx->cur_off = ctx->rep_off;
}
ctx->fld = fld;
} else if ((0 == strncmp(key, "$(QFIELDS)", 10)) ||
(0 == strncmp(key, "$(RFIELDS)", 10))) {
/*
* Begin interation sequence.
*/
ctx->rep_line = ctx->cur_line;
ctx->rep_off = ctx->cur_off;
ctx->fld = (CSOfield_info *) 0;
ctx->seek = "$(NEXTFLD)";
ctx->field_select = (key[2] == 'Q') ? 0 : 2;
if (ctx->public_override)
ctx->field_select++;
} else if (0 == strncmp(key, "$(NAMEFLD)", 10)) {
/*
* Special, locate name field. Flag lookup so QFIELDS will skip it.
*/
for (fld = CSOfields; fld; fld = fld->next)
if (strcmp(fld->name, "name") == 0 ||
strcmp(fld->name, "Name") == 0) {
if (fld->lookup)
fld->lookup = 2;
break;
}
ctx->fld = fld;
} else if (0 == strncmp (key, "$(HOST)", 7)) {
strcpy (buf, ctx->host);
} else if (0 == strncmp (key, "$(PORT)", 7)) {
sprintf(buf, "%d", ctx->port);
} else {
/*
* No match, dump key to buffer so client sees it for debugging.
*/
int out = 0;
while (*key && (*key != ')')) {
buf[out++] = (*key++);
if (out > sizeof(buf)-2) {
buf[out] = '\0';
(*Target->isa->put_block)(Target, buf, strlen(buf));
out = 0;
}
}
buf[out++] = ')';
buf[out] = '\0';
*length = strlen(buf);
return -1;
}
*length = strlen(buf);
return 0;
}
/* Parse the elements in a CSO/PH fields structure. - FM
** =====================================================
*/
PRIVATE int parse_cso_field_info ARGS1(
CSOfield_info *, blk)
{
int i;
char *info, *max_spec;
/*
* initialize all fields to default values.
*/
blk->indexed = blk->lookup = blk->reserved = blk->max_size = blk->url = 0;
blk->defreturn = blk->explicit_return = blk->public = 0;
/*
* Search for keywords in info string and set values. Attributes
* are converted to all lower-case for comparison.
*/
info = blk->attributes;
for (i = 0; info[i]; i++)
info[i] = TOLOWER(info[i]);
if (strstr(info, "indexed "))
blk->indexed = 1;
if (strstr(info, "default "))
blk->defreturn = 1;
if (strstr(info, "public "))
blk->public = 1;
if (strstr(info, "lookup "))
blk->lookup = 1;
if (strstr(info, "url ")) {
blk->url = 1;
blk->defreturn = 1;
}
max_spec = strstr(info, "max ");
if (max_spec) {
sscanf(&max_spec[4], "%d", &blk->max_size);
} else {
blk->max_size = 32;
}
return 0;
}
/* Parse a reply from a CSO/PH fields request. - FM
** ================================================
*/
PRIVATE int parse_cso_fields ARGS2(
char *, buf,
int, size)
{
char ch;
char *p = buf;
int i, code, prev_code, alen;
char *index, *name;
CSOfield_info *last, *new;
extern int interrupted_in_htgetcharacter;
last = CSOfields = (CSOfield_info *) 0;
prev_code = -2555;
buf[0] = '\0';
/* start grabbing chars from the network */
while ((ch=NEXT_CHAR) != (char)EOF) {
if (interrupted_in_htgetcharacter) {
if (TRACE) {
fprintf(stderr,
"HTLoadCSO: Interrupted in HTGetCharacter, apparently.\n");
}
free_CSOfields();
buf[0] = '\0';
return HT_INTERRUPTED;
}
if (ch != LF) {
*p = ch; /* Put character in buffer */
if (p < &buf[size-1]) {
p++;
}
} else {
*p = '\0'; /* Terminate line */
p = buf; /* Scan it to parse it */
/* OK we now have a line in 'p' lets parse it.
*/
/* Break on line that begins with a 2.
* It's the end of data.
*/
if (*p == '2')
break;
/* lines beginning with 5 are errors,
* print them and quit
*/
if (*p == '5') {
strcpy (buf, p);
return 5;
}
if (*p == '-') {
/* data lines look like -200:#:
* where # is the search result number and can be
* multiple digits (infinite?).
*/
/*
* Check status, ignore any non-success.
*/
if (p[1] != '2' )
continue;
/*
* Parse fields within returned line into status, ndx, name, data.
*/
index = NULL;
name = NULL;
for (i = 0; p[i]; i++)
if (p[i] == ':' ) {
p[i] = '\0';
if (!index) {
index = (char *)&p[i+1];
code = atoi (index);
} else if (!name) {
name = (char *)&p[i+1];
} else {
i++;
break;
}
}
/*
* Add data to field structure.
*/
if (name) {
if (code == prev_code) {
/*
* Remaining data is description,
* save in current info block.
*/
alen = strlen((char *)&p[i]) + 1;
if (alen > sizeof(last->desc_buf)) {
if (last->description != last->desc_buf)
FREE(last->description);
if (!(last->description = (char *)malloc(alen))) {
outofmem(__FILE__, "HTLoadCSO");
}
}
strcpy(last->description, (char *)&p[i]);
} else {
/*
* Initialize new block, append to end of list
* to preserve order.
*/
new = (CSOfield_info *)calloc(1, sizeof(CSOfield_info));
if (!new) {
outofmem(__FILE__, "HTLoadCSO");
}
if (last)
last->next = new;
else
CSOfields = new;
last = new;
new->next = (CSOfield_info *) 0;
new->name = new->name_buf;
alen = strlen(name) + 1;
if (alen > sizeof(new->name_buf)) {
if (!(new->name = (char *)malloc(alen))) {
outofmem(__FILE__, "HTLoadCSO");
}
}
strcpy (new->name, name);
new->attributes = new->attr_buf;
alen = strlen((char *)&p[i]) + 2;
if (alen > sizeof(new->attr_buf)) {
if (!(new->attributes = (char *)malloc(alen))) {
outofmem(__FILE__, "HTLoadCSO");
}
}
strcpy(new->attributes, (char *)&p[i]);
strcpy((char *)&new->attributes[alen-2], " ");
new->description = new->desc_buf;
new->desc_buf[0] = '\0';
new->id = atoi(index);
/*
* Scan for keywords.
*/
parse_cso_field_info(new);
}
prev_code = code;
} else
break;
} /* end if *p == '-' */
} /* if end of line */
} /* Loop over characters */
/* end the text block */
if (buf[0] == '\0') {
return -1; /* no response */
}
buf[0] = '\0';
return 0; /* all done */
} /* end of procedure */
/* Generate a form for submitting CSO/PH searches. - FM
** ====================================================
*/
PRIVATE int generate_cso_form ARGS4(
char *, host,
int, port,
char *, buf,
HTStream *, Target)
{
int i, j, length, out;
int full_flag = 1;
char *key, *line;
CSOformgen_context ctx;
static char *template[] = {
"<HEAD>\n<TITLE>CSO/PH Query Form for $(HOST)</TITLE>\n</HEAD>\n<BODY>",
"<H2><I>CSO/PH Query Form</I> for <EM>$(HOST)</EM></H2>",
"To search the database for a name, fill in one or more of the fields",
"in the form below and activate the 'Submit query' button. At least",
"one of the entered fields must be flagged as indexed.",
"<HR><FORM method=\"POST\" action=\"cso://$(HOST)/\">",
"[ <input type=\"submit\" value=\"Submit query\"> | ",
"<input type=\"reset\" value=\"Clear fields\"> ]",
"<P><DL>",
" <DT>Search parameters (* indicates indexed field):",
" <DD>", "$(NAMEFLD) <DL COMPACT>\n <DT><I>$(FDESC)</I>$(FNDX)",
" <DD>Last: <input name=\"q_$(FID)\" type=\"text\" size=49$(FSIZE2)>",
" <DD>First: <input name=\"q_$(FID)\" type=\"text\" size=48$(FSIZE2)>",
"$(QFIELDS) <DT><I>$(FDESC)</I>$(FNDX)",
" <DD><input name=\"q_$(FID)\" type=\"text\" $(FSIZE)>\n$(NEXTFLD)",
" </DL>",
" </DL>\n<P><DL>",
" <DT>Output format:",
" <DD>Returned data option: <select name=\"return\">",
" <option>default<option selected>all<option>selected</select><BR>",
"$(RFIELDS) <input type=\"checkbox\" name=\"r_$(FID)\"$(FDEF)> $(FDESC)<BR>",
"$(NEXTFLD) ",
" </DL></FORM><HR>\n</BODY>\n</HTML>",
(char *) 0
};
out = 0;
ctx.host = host;
ctx.seek = (char *) 0;
ctx.port = port;
ctx.fld = (CSOfield_info *) 0;
ctx.public_override = full_flag;
/*
* Parse the strings in the template array to produce HTML document
* to send to client. First line is skipped for 'full' lists.
*/
out = 0;
buf[out] = '\0';
for (i = full_flag ? /***1***/ 0 : 0; template[i]; i++) {
/*
* Search the current string for substitution, flagged by $(
*/
for (line=template[i], j = 0; line[j]; j++) {
if ((line[j] == '$') && (line[j+1] == '(')) {
/*
* Command detected, flush output buffer and find closing ')'
* that delimits the command.
*/
buf[out] = '\0';
if (out > 0)
(*Target->isa->put_block)(Target, buf, strlen(buf));
out = 0;
for (key = &line[j]; line[j+1] && (line[j] != ')'); j++)
;
/*
* Save context, interpet command and restore updated context.
*/
ctx.cur_line = i;
ctx.cur_off = j;
interpret_cso_key(key, buf, &length, &ctx, Target);
i = ctx.cur_line;
j = ctx.cur_off;
line = template[i];
out = length;
if (ctx.seek) {
/*
* command wants us to skip (forward) to indicated token.
* Start at current position.
*/
int slen = strlen(ctx.seek);
for (; template[i]; i++) {
for (line = template[i]; line[j]; j++) {
if (line[j] == '$')
if (0 == strncmp(ctx.seek, &line[j], slen)) {
if (j == 0)
j = strlen(template[--i])-1;
else
--j;
line = template[i];
ctx.seek = (char *) 0;
break;
}
}
if (!ctx.seek)
break;
j = 0;
}
if (ctx.seek) {
char *temp = (char *)malloc(strlen(ctx.seek) + 20);
if (temp) {
outofmem(__FILE__, "HTLoadCSO");
}
sprintf(temp, "Seek fail on %s\n", ctx.seek);
(*Target->isa->put_block)(Target, temp, strlen(temp));
FREE(temp);
}
}
} else {
/*
* Non-command text, add to output buffer.
*/
buf[out++] = line[j];
if (out > (sizeof(buf)-3)) {
buf[out] = '\0';
(*Target->isa->put_block)(Target, buf, strlen(buf));
out = 0;
}
}
}
buf[out++] = '\n';
buf[out] = '\0';
}
if (out > 0)
(*Target->isa->put_block)(Target, buf, strlen(buf));
return 0;
}
/* Generate a results report for CSO/PH form-based searches. - FM
** ==============================================================
*/
PRIVATE int generate_cso_report ARGS2(
char *, buf,
HTStream *, Target)
{
char ch;
char line[BIG];
char *p = line, *href = NULL;
int len, i, prev_ndx, ndx;
char *rcode, *ndx_str, *fname, *fvalue, *l;
CSOfield_info *fld;
BOOL stop = FALSE;
extern int interrupted_in_htgetcharacter;
/*
* Read lines until non-negative status.
*/
prev_ndx = -100;
/* start grabbing chars from the network */
while (!stop && (ch=NEXT_CHAR) != (char)EOF) {
if (interrupted_in_htgetcharacter) {
buf[0] = '\0';
if (TRACE) {
fprintf(stderr,
"HTLoadCSO: Interrupted in HTGetCharacter, apparently.\n");
}
_HTProgress ("Connection interrupted.");
goto end_CSOreport;
}
if (ch != LF) {
*p = ch; /* Put character in line */
if (p < &line[BIG-1]) {
p++;
}
} else {
*p = '\0'; /* Terminate line */
/*
* OK we now have a line.
* Load it as 'p' and parse it.
*/
p = line;
if (p[0] != '-' && p[0] != '1') {
stop = TRUE;
}
rcode = (p[0] == '-') ? &p[1] : p;
ndx_str = fname = NULL;
len = strlen(p);
for (i = 0; i < len; i++) {
if (p[i] == ':') {
p[i] = '\0';
if (!ndx_str) {
fname = ndx_str = &p[i+1];
} else {
fname = &p[i+1];
break;
}
}
}
if (ndx_str) {
ndx = atoi(ndx_str);
if (prev_ndx != ndx) {
if (prev_ndx != -100) {
strcpy(buf, "</DL></DL>\n");
(*Target->isa->put_block)(Target, buf, strlen(buf));
}
if (ndx == 0) {
strcpy(buf,
"<HR><DL><DT>Information/status<DD><DL><DT>\n");
(*Target->isa->put_block)(Target, buf, strlen(buf));
} else {
sprintf(buf,
"<HR><DL><DT>Entry %d:<DD><DL COMPACT><DT>\n", ndx);
(*Target->isa->put_block)(Target, buf, strlen(buf));
}
prev_ndx = ndx;
}
} else {
sprintf(buf, "<DD>%s\n", rcode);
(*Target->isa->put_block)(Target, buf, strlen(buf));
continue;
}
if ((*rcode >= '2') && (*rcode <= '5') && (fname != ndx_str)) {
while (*fname == ' ') {
fname++; /* trim leading spaces */
}
for (fvalue = fname; *fvalue; fvalue++) {
if (*fvalue == ':') {
*fvalue++ = '\0';
i = strlen(fname) - 1;
while (i >= 0 && fname[i] == ' ') {
fname[i--] = '\0'; /* trim trailing */
}
break;
}
}
if (fvalue) {
while (*fvalue == ' ') {
fvalue++; /* trim leading spaces */
}
}
if (*fname) {
for (fld = CSOfields; fld; fld = fld->next) {
if (!strcmp(fld->name, fname)) {
if (fld->description) {
fname = fld->description;
}
break;
}
}
if (fld && fld->url) {
sprintf(buf,
"<DT><I>%s</I><DD><A HREF=\"%s\">%s</A>\n",
fname, fvalue, fvalue);
(*Target->isa->put_block)(Target, buf, strlen(buf));
} else {
sprintf(buf, "<DT><I>%s</I><DD>", fname);
(*Target->isa->put_block)(Target, buf, strlen(buf));
i = 0;
buf[i] = '\0';
l = fvalue;
while (*l) {
if (*l == '<') {
strcat(buf, "<");
l++;
i += 4;
buf[i] = '\0';
} else if (*l == '>') {
strcat(buf, ">");
l++;
i += 4;
buf[i] = '\0';
} else if (strncmp (l, "news:", 5) &&
strncmp (l, "snews://", 8) &&
strncmp (l, "nntp://", 7) &&
strncmp (l, "ftp://", 6) &&
strncmp (l, "file:/", 6) &&
strncmp (l, "finger://", 9) &&
strncmp (l, "http://", 7) &&
strncmp (l, "https://", 8) &&
strncmp (l, "wais://", 7) &&
strncmp (l, "mailto:", 7) &&
strncmp (l, "cso://", 6) &&
strncmp (l, "gopher://", 9)) {
buf[i++] = *l++;
buf[i] = '\0';
} else {
strcat(buf, "<a href=\"");
i += 9;
buf[i] = '\0';
StrAllocCopy(href, l);
strcat(buf, strtok(href, " \r\n\t,>)\""));
strcat(buf, "\">");
i = strlen(buf);
while (*l && !strchr(" \r\n\t,>)\"", *l)) {
buf[i++] = *l++;
}
buf[i] = '\0';
strcat(buf, "</a>");
i += 4;
FREE(href);
}
}
strcat(buf, "\n");
(*Target->isa->put_block)(Target, buf, strlen(buf));
}
} else {
sprintf(buf, "<DD>");
(*Target->isa->put_block)(Target, buf, strlen(buf));
i = 0;
buf[i] = '\0';
l = fvalue;
while (*l) {
if (*l == '<') {
strcat(buf, "<");
l++;
i += 4;
buf[i] = '\0';
} else if (*l == '>') {
strcat(buf, ">");
l++;
i += 4;
buf[i] = '\0';
} else if (strncmp (l, "news:", 5) &&
strncmp (l, "snews://", 8) &&
strncmp (l, "nntp://", 7) &&
strncmp (l, "ftp://", 6) &&
strncmp (l, "file:/", 6) &&
strncmp (l, "finger://", 9) &&
strncmp (l, "http://", 7) &&
strncmp (l, "https://", 8) &&
strncmp (l, "wais://", 7) &&
strncmp (l, "mailto:", 7) &&
strncmp (l, "cso://", 6) &&
strncmp (l, "gopher://", 9)) {
buf[i++] = *l++;
buf[i] = '\0';
} else {
strcat(buf, "<a href=\"");
i += 9;
buf[i] = '\0';
StrAllocCopy(href, l);
strcat(buf, strtok(href, " \r\n\t,>)\""));
strcat(buf, "\">");
i = strlen(buf);
while (*l && !strchr(" \r\n\t,>)\"", *l)) {
buf[i++] = *l++;
}
buf[i] = '\0';
strcat(buf, "</a>");
i += 4;
FREE(href);
}
}
strcat(buf, "\n");
(*Target->isa->put_block)(Target, buf, strlen(buf));
}
} else {
sprintf(buf, "<DD>%s\n", fname ? fname : rcode );
(*Target->isa->put_block)(Target, buf, strlen(buf));
}
}
}
end_CSOreport:
if (prev_ndx != -100) {
sprintf(buf, "</DL></DL>\n");
(*Target->isa->put_block)(Target, buf, strlen(buf));
}
return 0;
}
/* CSO/PH form-based search gateway - FM HTLoadCSO
** =====================================
*/
PUBLIC int HTLoadCSO ARGS4(
CONST char *, arg,
HTParentAnchor *, anAnchor,
HTFormat, format_out,
HTStream*, sink)
{
char *host, *cp;
int port = CSO_PORT;
int status; /* tcp return */
char *command = NULL;
char *content = NULL;
int len, i, j, start, finish, flen, ndx, clen;
int return_type, has_indexed;
CSOfield_info *fld;
char buf[2048];
HTFormat format_in = WWW_HTML;
HTStream *Target = NULL;
struct sockaddr_in soc_address; /* Binary network address */
struct sockaddr_in* sin = &soc_address;
if (!acceptable_inited)
init_acceptable();
if (!arg)
return -3; /* Bad if no name sepcified */
if (!*arg)
return -2; /* Bad if name had zero length */
if (TRACE)
fprintf(stderr, "HTLoadCSO: Looking for %s\n", arg);
/* Set up defaults:
*/
sin->sin_family = AF_INET; /* Family, host order */
sin->sin_port = htons(CSO_PORT); /* Default: new port, */
/* Get node name and optional port number:
*/
{
char *p1 = HTParse(arg, "", PARSE_HOST);
int status = HTParseInet(sin, p1);
FREE(p1);
if (status) {
return status; /* Bad */
}
}
/* Set up a socket to the server for the data:
*/
status = HTDoConnect (arg, "cso", CSO_PORT, &s);
if (status == HT_INTERRUPTED) {
/* Interrupt cleanly. */
if (TRACE)
fprintf(stderr,
"HTLoadCSO: Interrupted on connect; recovering cleanly.\n");
_HTProgress ("Connection interrupted.");
return HT_INTERRUPTED;
}
if (status < 0) {
if (TRACE)
fprintf(stderr,
"HTLoadCSO: Unable to connect to remote host for `%s'.\n",
arg);
return HTInetStatus("connect");
}
HTInitInput(s); /* Set up input buffering */
if ((command = (char *)malloc(12)) == NULL)
outofmem(__FILE__, "HTLoadCSO");
sprintf(command, "fields%c%c", CR, LF);
if (TRACE)
fprintf(stderr,
"HTLoadCSO: Connected, writing command `%s' to socket %d\n",
command, s);
_HTProgress ("Sending CSO/PH request.");
status = NETWRITE(s, command, (int)strlen(command));
FREE(command);
if (status < 0) {
if (TRACE)
fprintf(stderr, "HTLoadCSO: Unable to send command.\n");
return HTInetStatus("send");
}
_HTProgress ("CSO/PH request sent; waiting for response.");
/* Now read the data from the socket:
*/
status = parse_cso_fields(buf, sizeof(buf));
if (status) {
NETCLOSE(s);
if (status = HT_INTERRUPTED) {
_HTProgress ("Connection interrupted.");
} else if (buf[0] != '\0') {
HTAlert(buf);
} else {
HTAlert("No response from server!");
}
return HT_NOT_LOADED;
}
Target = HTStreamStack(format_in,
format_out,
sink, anAnchor);
if (!Target || Target == NULL) {
char *temp = (char *)malloc(256);
if (!temp) {
outofmem(__FILE__, "HTLoadCSO");
}
sprintf(temp, "Sorry, no known way of converting %s to %s.",
HTAtom_name(format_in), HTAtom_name(format_out));
HTAlert(temp);
FREE(temp);
NETCLOSE(s);
return HT_NOT_LOADED;
}
host = HTParse(arg, "", PARSE_HOST);
if ((cp=strchr(host, ':')) != NULL) {
if (cp[1] >= '0' && cp[1] <= '9') {
port = atoi((cp+1));
if (port == CSO_PORT) {
*cp = '\0';
}
}
}
if (!(anAnchor->post_data && *anAnchor->post_data)) {
generate_cso_form(host, port, buf, Target);
(*Target->isa->_free)(Target);
FREE(host);
NETCLOSE(s);
free_CSOfields();
return HT_LOADED;
}
sprintf(buf,
"<HTML>\n<HEAD>\n<TITLE>CSO/PH Results on %s</TITLE>\n</HEAD>\n<BODY>\n",
host);
(*Target->isa->put_block)(Target, buf, strlen(buf));
FREE(host);
StrAllocCopy(content, anAnchor->post_data);
if (content[strlen(content)-1] != '&')
StrAllocCat(content, "&");
len = strlen(content);
for (i = 0; i < len; i++) {
if (content[i] == '+') {
content[i] = ' ';
}
}
HTUnEscape(content);
len = strlen(content);
return_type = 0;
has_indexed = 0;
start = finish = clen = 0;
for (i = 0; i < len; i++) {
if (!content[i] || content[i] == '&') {
/*
* Value parsed. Unescape characters and look for first '='
* to delimit field name from value.
*/
flen = i - start;
finish = start + flen;
content[finish] = '\0';
for (j = start; j < finish; j++) {
if (content[j] == '=') {
/*
* content[start..j-1] is field name,
* [j+1..finish-1] is value.
*/
if ((content[start+1] == '_') &&
((content[start] == 'r') || (content[start] == 'q'))) {
/*
* Decode fields number and lookup field info.
*/
sscanf (&content[start+2], "%d=", &ndx);
for (fld = CSOfields; fld; fld = fld->next) {
if (ndx==fld->id) {
if ((j+1) >= finish)
break; /* ignore nulls */
if (content[start] == 'q') {
/*
* Append field to query line.
*/
if (fld->lookup) {
if (fld->indexed)
has_indexed = 1;
if (clen == 0) {
StrAllocCopy(command, "query ");
clen = 6;
} else {
StrAllocCat(command, " ");
clen++;
}
sprintf(buf, "%s=\"%s\"",
fld->name, &content[j+1]);
StrAllocCat(command, buf);
clen += strlen(buf);
} else {
strcpy(buf,
"Warning: non-lookup field ignored<BR>\n");
(*Target->isa->put_block)(Target,
buf,
strlen(buf));
}
} else if (content[start] == 'r') {
fld->explicit_return = 1;
}
break;
}
}
} else if (!strncmp(&content[start],"return=",7)) {
if (!strcmp(&content[start+7],"all")) {
return_type = 1;
} else if (!strcmp(&content[start+7],"selected")) {
return_type = 2;
}
}
}
}
start = i + 1;
}
}
FREE(content);
if ((clen == 0) || !has_indexed) {
NETCLOSE(s);
strcpy(buf,
"<EM>Error:</EM> At least one indexed field value must be specified!\n");
(*Target->isa->put_block)(Target, buf, strlen(buf));
strcpy(buf, "</BODY>\n</HTML>\n");
(*Target->isa->put_block)(Target, buf, strlen(buf));
(*Target->isa->_free)(Target);
free_CSOfields();
return HT_LOADED;
}
/*
* Append return fields.
*/
if (return_type == 1) {
StrAllocCat(command, " return all");
clen += 11;
} else if (return_type == 2) {
StrAllocCat(command, " return");
clen += 7;
for (fld = CSOfields; fld; fld = fld->next) {
if (fld->explicit_return) {
sprintf(buf, " %s", fld->name);
StrAllocCat(command, buf);
clen += strlen(buf);
}
}
}
sprintf(buf, "%c%c", CR, LF);
StrAllocCat(command, buf);
clen += strlen(buf);
strcpy(buf, "<H2>\n<EM>CSO/PH command:</EM> ");
(*Target->isa->put_block)(Target, buf, strlen(buf));
(*Target->isa->put_block)(Target, command, clen);
strcpy(buf, "</H2>\n");
(*Target->isa->put_block)(Target, buf, strlen(buf));
if (TRACE)
fprintf(stderr,
"HTLoadCSO: Writing command `%s' to socket %d\n",
command, s);
status = NETWRITE(s, command, clen);
FREE(command);
if (status < 0) {
if (TRACE)
fprintf(stderr, "HTLoadCSO: Unable to send command.\n");
free_CSOfields();
return HTInetStatus("send");
}
generate_cso_report(buf, Target);
NETCLOSE(s);
strcpy(buf, "</BODY>\n</HTML>\n");
(*Target->isa->put_block)(Target, buf, strlen(buf));
(*Target->isa->_free)(Target);
FREE(host);
free_CSOfields();
return HT_LOADED;
}
/* Load by name HTLoadGopher
** ============
**
** Bug: No decoding of strange data types as yet.
**
*/
PUBLIC int HTLoadGopher ARGS4(
CONST char *, arg,
HTParentAnchor *, anAnchor,
HTFormat, format_out,
HTStream*, sink)
{
char *command; /* The whole command */
int status; /* tcp return */
char gtype; /* Gopher Node type */
char * selector; /* Selector string */
struct sockaddr_in soc_address; /* Binary network address */
struct sockaddr_in* sin = &soc_address;
if (!acceptable_inited)
init_acceptable();
if (!arg)
return -3; /* Bad if no name sepcified */
if (!*arg)
return -2; /* Bad if name had zero length */
if (TRACE)
fprintf(stderr, "HTGopher: Looking for %s\n", arg);
/* If it's a port 105 GOPHER_CSO gtype with no ISINDEX token ('?'),
** use the form-based CSO gateway (otherwise, return an ISINDEX
** cover page or do the ISINDEX search). - FM
*/
{
int len;
if ((len = strlen(arg)) > 5) {
if (0 == strcmp((char *)&arg[len-6], ":105/2")) {
/* Use CSO gateway. */
if (TRACE)
fprintf(stderr, "HTGopher: Passing to CSO/PH gateway.\n");
return HTLoadCSO(arg, anAnchor, format_out, sink);
}
}
}
/* If it's a port 79/0[/...] URL, use the finger gateway. - FM
*/
if (strstr(arg, ":79/0") != NULL) {
if (TRACE)
fprintf(stderr, "HTGopher: Passing to finger gateway.\n");
return HTLoadFinger(arg, anAnchor, format_out, sink);
}
/* Set up defaults:
*/
sin->sin_family = AF_INET; /* Family, host order */
sin->sin_port = htons(GOPHER_PORT); /* Default: new port, */
/* Get node name and optional port number:
*/
{
char *p1 = HTParse(arg, "", PARSE_HOST);
int status = HTParseInet(sin, p1);
FREE(p1);
if (status)
return status; /* Bad */
}
/* Get entity type, and selector string.
*/
{
char * p1 = HTParse(arg, "", PARSE_PATH|PARSE_PUNCTUATION);
gtype = '1'; /* Default = menu */
selector = p1;
if ((*selector++=='/') && (*selector)) { /* Skip first slash */
gtype = *selector++; /* Pick up gtype */
}
if (gtype == GOPHER_INDEX) {
char * query;
/* Search is allowed */
HTAnchor_setIndex(anAnchor, anAnchor->address);
query = strchr(selector, '?'); /* Look for search string */
if (!query || !query[1]) { /* No search required */
target = HTML_new(anAnchor, format_out, sink);
targetClass = *target->isa;
display_index(arg, anAnchor); /* Display "cover page" */
return HT_LOADED; /* Local function only */
}
*query++ = '\0'; /* Skip '?' */
command =
(char *)malloc(strlen(selector)+ 1 + strlen(query)+ 2 + 1);
if (command == NULL)
outofmem(__FILE__, "HTLoadGopher");
de_escape(command, selector); /* Bug fix TBL 921208 */
strcat(command, "\t");
{ /* Remove plus signs 921006 */
char *p;
for (p=query; *p; p++) {
if (*p == '+') *p = ' ';
}
}
de_escape(&command[strlen(command)], query);/* bug fix LJM 940415 */
} else if (gtype == GOPHER_CSO) {
char * query;
/* Search is allowed */
query = strchr(selector, '?'); /* Look for search string */
if (!query || !query[1]) { /* No search required */
target = HTML_new(anAnchor, format_out, sink);
targetClass = *target->isa;
display_cso(arg, anAnchor); /* Display "cover page" */
return HT_LOADED; /* Local function only */
}
HTAnchor_setIndex(anAnchor, anAnchor->address);
*query++ = '\0'; /* Skip '?' */
command = (char *)malloc(strlen("query")+1 + strlen(query)+2+1);
if (command == NULL)
outofmem(__FILE__, "HTLoadGopher");
de_escape(command, selector); /* Bug fix TBL 921208 */
strcpy(command, "query ");
{ /* Remove plus signs 921006 */
char *p;
for (p=query; *p; p++) {
if (*p == '+') *p = ' ';
}
}
de_escape(&command[strlen(command)], query);/* bug fix LJM 940415 */
} else { /* Not index */
command = (char *)malloc(strlen(selector)+2+1);
de_escape(command, selector);
}
FREE(p1);
}
{
char * p = command + strlen(command);
*p++ = CR; /* Macros to be correct on Mac */
*p++ = LF;
*p++ = '\0';
/* strcat(command, "\r\n"); */ /* CR LF, as in rfc 977 */
}
/* Set up a socket to the server for the data:
*/
status = HTDoConnect (arg, "gopher", GOPHER_PORT, &s);
if (status == HT_INTERRUPTED)
{
/* Interrupt cleanly. */
if (TRACE)
fprintf(stderr,
"HTGopher: Interrupted on connect; recovering cleanly.\n");
_HTProgress ("Connection interrupted.");
return HT_INTERRUPTED;
}
if (status < 0) {
if (TRACE)
fprintf(stderr,
"HTGopher: Unable to connect to remote host for `%s'.\n",
arg);
FREE(command);
return HTInetStatus("connect");
}
HTInitInput(s); /* Set up input buffering */
if (TRACE)
fprintf(stderr,
"HTGopher: Connected, writing command `%s' to socket %d\n",
command, s);
#ifdef NOT_ASCII
{
char * p;
for(p = command; *p; p++) {
*p = TOASCII(*p);
}
}
#endif
_HTProgress ("Sending Gopher request.");
status = NETWRITE(s, command, (int)strlen(command));
FREE(command);
if (status < 0) {
if (TRACE)
fprintf(stderr, "HTGopher: Unable to send command.\n");
return HTInetStatus("send");
}
_HTProgress ("Gopher request sent; waiting for response.");
/* Now read the data from the socket:
*/
switch (gtype) {
case GOPHER_TEXT :
HTParseSocket(WWW_PLAINTEXT, format_out, anAnchor, s, sink);
break;
case GOPHER_HTML :
case GOPHER_CHTML :
HTParseSocket(WWW_HTML, format_out, anAnchor, s, sink);
break;
case GOPHER_GIF:
case GOPHER_IMAGE:
case GOPHER_PLUS_IMAGE:
HTParseSocket(HTAtom_for("image/gif"),
format_out, anAnchor, s, sink);
break;
case GOPHER_MENU :
case GOPHER_INDEX :
target = HTML_new(anAnchor, format_out, sink);
targetClass = *target->isa;
parse_menu(arg, anAnchor);
break;
case GOPHER_CSO:
target = HTML_new(anAnchor, format_out, sink);
targetClass = *target->isa;
parse_cso(arg, anAnchor);
break;
case GOPHER_SOUND :
case GOPHER_PLUS_SOUND :
HTParseSocket(WWW_AUDIO, format_out, anAnchor, s, sink);
break;
case GOPHER_PLUS_MOVIE:
HTParseSocket(HTAtom_for("video/mpeg"), format_out, anAnchor, s, sink);
break;
case GOPHER_PLUS_PDF:
HTParseSocket(HTAtom_for("application/pdf"), format_out, anAnchor,
s, sink);
break;
case GOPHER_MACBINHEX:
case GOPHER_PCBINARY:
case GOPHER_UUENCODED:
case GOPHER_BINARY:
default:
/* Specifying WWW_UNKNOWN forces dump to local disk. */
HTParseSocket (WWW_UNKNOWN, format_out, anAnchor, s, sink);
break;
} /* switch(gtype) */
NETCLOSE(s);
return HT_LOADED;
}
#ifdef GLOBALDEF_IS_MACRO
#define _HTGOPHER_C_1_INIT { "gopher", HTLoadGopher, NULL }
GLOBALDEF (HTProtocol, HTGopher, _HTGOPHER_C_1_INIT);
#define _HTCSO_C_1_INIT { "cso", HTLoadCSO, NULL }
GLOBALDEF (HTProtocol, HTCSO, _HTCSO_C_1_INIT);
#else
GLOBALDEF PUBLIC HTProtocol HTGopher = { "gopher", HTLoadGopher, NULL };
GLOBALDEF PUBLIC HTProtocol HTCSO = { "cso", HTLoadCSO, NULL };
#endif /* GLOBALDEF_IS_MACRO */