/******************************************************************** * wilkinson * 3.75VMS * 1995/09/25 11:20 * gopher_root1:[gopher.g2.vms2_13.object]GSgopherobj.c,v * Exp * * Paul Lindner, University of Minnesota CIS. * * Copyright 1991, 1992 by the Regents of the University of Minnesota * see the file "Copyright" in the distribution for conditions of use. ********************************************************************* * MODULE: GSgopherobj.c * Implement gopher directory functions. ********************************************************************* * Revision History: * GSgopherobj.c,v * Revision 3.75VMS 1995/09/25 11:20 wilkinson * Consolodate VMS/Unix source code for server as well as client * - add create/modify date display flag (default is modify) * * Revision 3.75 1995/02/22 05:30:25 lindner * reintroduce gsinit to gsnew * * Revision 3.74 1995/02/17 18:29:37 lindner * Abstract display support * * Revision 3.73 1995/02/16 22:32:44 lindner * HTML icon support * * Revision 3.72 1995/02/07 07:12:55 lindner * oops! * * Revision 3.71 1995/02/07 07:06:58 lindner * performance fixes * * Revision 3.70 1995/02/06 22:13:52 lindner * Performance fixes * * Revision 3.69 1995/02/02 17:14:46 lindner * Fix for memory leaks and accesses * * Revision 3.68 1995/02/01 22:08:02 lindner * Put back GSaddmerge * * Revision 3.67 1995/02/01 21:44:41 lindner * Remove GSmerge fcns, Add message/rfc822 and application/gopher to GSisText * * Revision 3.66 1994/12/15 17:30:49 lindner * Allow multi-line abstracts in link files, Fix for ftp URL generation * * Revision 3.65 1994/12/05 22:39:55 lindner * Fix Name and Path settings in GSfromURL * * Revision 3.64 1994/11/17 06:33:58 lindner * Fixes for VMS internationalization * * Revision 3.63 1994/11/13 06:30:48 lindner * Better GSfromURL * * Revision 3.62 1994/10/24 22:15:53 lindner * Add PDF type * * Revision 3.61 1994/10/18 21:37:03 lindner * Make sure buf is set * * Revision 3.60 1994/10/13 05:27:18 lindner * Compiler complaint fixes * * Revision 3.59 1994/09/29 19:26:45 lindner * Fix for debug messages and NULL values * * Revision 3.58 1994/08/19 16:13:09 lindner * mktime() etc, fixes from Alan Coopersmith * * Revision 3.57 1994/08/03 20:03:44 lindner * Fix for adding ask stuff * * Revision 3.56 1994/07/31 04:57:20 lindner * Fix for .names messup... * * Revision 3.55 1994/07/21 22:29:18 lindner * misc hacks * * Revision 3.54 1994/07/06 19:21:34 lindner * use strftime to get localized date and time * * Revision 3.53 1994/06/29 06:51:21 lindner * ifdef GINTERNATIONAL, use strftime to localize the date string * returned by GSgetModDate() * * in GSplusfromNet(), if there's anything in the ADMIN block other than * Admin, ModDate, or TTL entries, save it for the client's showinfo() * Server Information * * Move GSfromURL() return code definitions to header so other files can * call it * * Fix GStoNetURL() & HTML display of directories * * Round sizes in GSaddView * * Make GSfromURL() lie and say all http: is HTML so the client * passes it off to a http-speaking program anyway (assuming * all HTML viewers speak http, that is) (Coopersmith) * * Revision 3.52 1994/06/29 05:45:56 lindner * Mods to pump tickets to the net * * Revision 3.51 1994/05/25 20:57:46 lindner * Remove unused var * * Revision 3.50 1994/05/06 02:29:46 lindner * Fix from Robert Beckett for binhex files * * Revision 3.49 1994/04/25 03:36:58 lindner * Modifications for Debug() and mismatched NULL arguments, added Debugmsg * * Revision 3.48 1994/04/25 02:26:30 lindner * Fix for url problems * * Revision 3.47 1994/04/25 02:16:31 lindner * Fix for |= mistype * * Revision 3.46 1994/04/22 06:41:35 lindner * More pacbell hacks for Domain= * * Revision 3.45 1994/04/22 03:26:15 lindner * pacbell hack * * Revision 3.44 1994/04/19 14:32:29 lindner * Change GD and GSfromLink routines to use FIO * * Revision 3.43 1994/04/13 19:16:17 lindner * Fix for ASK block bug * * Revision 3.42 1994/04/08 20:05:52 lindner * gcc -Wall fixes * * Revision 3.41 1994/04/07 17:27:09 lindner * Fix for pyramids * * Revision 3.40 1994/04/01 04:38:06 lindner * Fix for conditional macros * * Revision 3.39 1994/03/31 22:48:55 lindner * fix for Hgopher and ask requests * * Revision 3.38 1994/03/31 21:05:11 lindner * More debug code, and fix for Socket return values * * Revision 3.37 1994/03/17 04:37:41 lindner * Fix for spinning clients * * Revision 3.36 1994/03/08 15:56:17 lindner * gcc -Wall fixes * * Revision 3.35 1994/03/04 17:59:16 lindner * More URL fixes from A.C. * * Revision 3.34 1994/02/20 16:27:44 lindner * Remove dead code for GDtoNetHTML * * Revision 3.33 1994/01/25 06:51:21 lindner * Many additions for URL/HTML support, removed insidious dot at end of host.. * * Revision 3.32 1994/01/10 03:28:12 lindner * Allow bangs in Domain= lines, to negate classes of hosts * * Revision 3.31 1994/01/06 06:10:53 lindner * fix for cvs entries * * * Revision 3.30 1994/01/06 05:43:46 lindner * Fix for type=1 and ask blocks. * * Revision 3.29 1993/12/27 16:25:39 lindner * Add fix for appending a period to domain names * * Revision 3.28 1993/11/29 01:07:23 lindner * In GSfromLink(), disable handling of "Domain_pat:" for VMS to keep the * compiler from complaining about the lack of re_comp() and re_exec(). * "Domain_pat:" handling isn't needed in the client anyway. (Wolfe) * * Add "AddInfo" argument to GStoLink(). This option decides whether to * add the Admin and ModDate fields to the basic information about the * gopher object. Bookmarks should not include them, but requested * technical information ('=' and '^') should. This change requires * changes to gopher/gopher.c and object/GDgopherdir.c; see below. * (Macrides) * * Revision 3.27 1993/11/05 07:24:46 lindner * Allow Type=1? etc.. in .link files * * Revision 3.26 1993/11/04 04:50:50 lindner * Add quotes around HREFs * * Revision 3.25 1993/11/04 02:10:47 lindner * Added Domain_pat= line to check for a regexp domains * * Revision 3.24 1993/11/03 15:35:40 lindner * Fix problems with bookmarks of gopher+ items * * Revision 3.23 1993/11/02 06:15:24 lindner * HTML additions * * Revision 3.22 1993/10/26 17:49:50 lindner * Fix for NULL view in GSisText() * * Revision 3.21 1993/10/22 20:02:33 lindner * Add Movie (;) and Info (i) type support * * Revision 3.20 1993/09/18 04:44:41 lindner * Additions to fix caching of Multiple view items * * Revision 3.19 1993/09/11 06:41:08 lindner * Fix for picky compilers * * Revision 3.18 1993/09/11 06:32:52 lindner * URL support * * Revision 3.17 1993/09/01 21:51:59 lindner * Remove GSnewSet, better initialization in GSnew() * * Revision 3.16 1993/08/19 20:24:11 lindner * Mitra's Debug patch * * Revision 3.15 1993/07/29 20:02:55 lindner * Removed dead variables * * Revision 3.14 1993/07/27 20:17:25 lindner * Fix improper bracketed debug output * * Revision 3.13 1993/07/27 05:30:23 lindner * Mondo Debug overhaul from Mitra * * Revision 3.12 1993/07/27 00:30:09 lindner * plus patch from Mitra * * Revision 3.11 1993/07/23 04:50:17 lindner * Mods to allow abstract/admin setting in .names files * * Revision 3.10 1993/07/21 03:31:09 lindner * Askdata can be stored locally, plus GSfromLink doesn't core dump with * mangled items * * Revision 3.9 1993/07/14 20:37:11 lindner * Negative numbering patches * * Revision 3.8 1993/07/07 19:30:12 lindner * Split off socket based fcns to Sockets.c * * Revision 3.7 1993/07/06 20:22:40 lindner * Added listener and accept fcns * * Revision 3.6 1993/06/22 06:07:14 lindner * Added Domain= hacks.. * * Revision 3.5 1993/04/15 17:44:52 lindner * Fixed link processing, mods from Mitra * * Revision 3.4 1993/03/26 19:50:46 lindner * Mitra fixes for better/clearer fromNet code * * Revision 3.3 1993/03/24 17:05:33 lindner * Additions for Localfile for each GopherObj * * Revision 3.2 1993/03/18 22:15:50 lindner * filtering, memory leaks fixed, GSmerge problems * *********************************************************************/ #ifdef VMS_SERVER #define GSGOPHEROBJ_C #include "GopherD.h" #endif #include "GSgopherobj.h" #if defined(mips) && defined(ultrix) /*** Gross hack, yuck! ***/ #define _SIZE_T #endif #include "String.h" #include "STRstring.h" #include #include "compatible.h" #include #include "Malloc.h" #include "Sockets.h" #include "util.h" #include "Debug.h" #include "fileio.h" #include #ifdef pyr unsigned long errno; #endif /* * Make a new gopherobj... Should reuse destroyed GopherObjs... */ GopherObj * GSnew() { GopherObj *temp; temp = (GopherObj *) malloc(sizeof(GopherObj)); temp->Selstr = STRnew(); temp->Title = STRnew(); temp->Host = STRnew(); temp->Localfile = STRnew(); temp->Localview = STRnew(); temp->gplus = NULL; temp->isask = FALSE; temp->url = NULL; #ifdef VMS_SERVER temp->date_cr = FALSE; temp->lookaside = FALSE; temp->Access = NULL; temp->Defaccess=ACC_FULL; temp->Head = STRnew(); temp->Foot = STRnew(); temp->RHead = STRnew(); temp->RFoot = STRnew(); #endif GSinit(temp); return(temp); } /* * Initialize the gopherplus components of the object * (Only called for a gplus item) */ void GSplusnew(gs) GopherObj *gs; { if (gs->gplus == NULL) { gs->gplus = (GplusObj *) malloc(sizeof(GplusObj)); } gs->gplus->Admin = STRnew(); gs->gplus->ModDate = STRnew(); gs->gplus->Views = VIAnew(10); gs->gplus->OtherBlocks = BLAnew(5); gs->gplus->Askdata = NULL; } /*** Destroy gopher object ***/ void GSdestroy(gs) GopherObj *gs; { STRdestroy(gs->Selstr); STRdestroy(gs->Title); STRdestroy(gs->Host); if (GSgetLocalFile(gs) != NULL) unlink(GSgetLocalFile(gs)); STRdestroy(gs->Localfile); STRdestroy(gs->Localview); GSplusdestroy(gs); if (gs->url != NULL) URLdestroy(gs->url); #ifdef VMS_SERVER if (gs->Access != NULL) SiteArrDestroy(gs->Access); STRdestroy(gs->Head); STRdestroy(gs->Foot); STRdestroy(gs->RHead); STRdestroy(gs->RFoot); #endif free(gs); } /*** Destroy Gopher+ attributes ***/ void GSplusdestroy(gs) GopherObj *gs; { if (gs->gplus != NULL) { STRdestroy(gs->gplus->Admin); STRdestroy(gs->gplus->ModDate); VIAdestroy(gs->gplus->Views); BLAdestroy(gs->gplus->OtherBlocks); (void)GSsetAskdata(gs, NULL); free(gs->gplus); gs->gplus = NULL; } } /* * Clear out all the crud */ void GSinit(gs) GopherObj *gs; { GSsetType(gs, '\0'); STRinit(gs->Title); STRinit(gs->Selstr); STRinit(gs->Host); if (GSgetLocalFile(gs) != NULL) unlink(GSgetLocalFile(gs)); STRinit(gs->Localfile); STRinit(gs->Localview); gs->ttl = -1; gs->iPort = 0; GSsetNum(gs, 0); GSsetWeight(gs, 0); GSsetGplus(gs,FALSE); /** Default is no gplus **/ GSsetAsk(gs, FALSE); if (gs->url != NULL) URLdestroy(gs->url); gs->url = NULL; GSplusInit(gs); #ifdef VMS_SERVER gs->date_cr = FALSE; gs->lookaside = FALSE; if (gs->Access != NULL) SiteArrDestroy(gs->Access); gs->Access = NULL; gs->Defaccess = ACC_FULL; STRinit(gs->Head); STRinit(gs->Foot); STRinit(gs->RHead); STRinit(gs->RFoot); #endif } /* * Clear out gopher+ crud if it exists */ void GSplusInit(gs) GopherObj *gs; { if (gs->gplus != NULL) { STRinit(gs->gplus->Admin); STRinit(gs->gplus->ModDate); VIAinit(gs->gplus->Views); STAinit(gs->gplus->OtherBlocks); } } /* * Set a URL for the gopherobject.. */ void GSsetURL(gs, url) GopherObj *gs; char *url; { if (gs->url == NULL) gs->url = URLnew(); URLset(gs->url, url); } int GSgetNumBlocks(gs) GopherObj *gs; { if (gs->gplus == NULL) return(-1); return(BLAgetTop(gs->gplus->OtherBlocks)); } Blockobj* GSgetBlock(gs, bnum) GopherObj *gs; int bnum; { if (gs->gplus == NULL) return(NULL); return(BLAgetEntry(gs->gplus->OtherBlocks, bnum)); } /* * Return the number of views if item is gopher+ */ int GSgetNumViews(gs) GopherObj *gs; { if (gs->gplus == NULL) return(0); return(VIAgetTop(gs->gplus->Views)); } /* * Find out what a specific view is */ VIewobj * GSgetView(gs, viewnum) GopherObj *gs; int viewnum; { if (gs->gplus == NULL) return(NULL); return(VIAgetEntry(gs->gplus->Views, viewnum)); } char * GSgetURL(gs) GopherObj *gs; { if (gs->url == NULL) { gs->url = URLnew(); URLfromGS(gs->url, gs); } return(URLget(gs->url)); } char * GSgetURLhtml(gs) GopherObj *gs; { char *cp = GSgetURL(gs); char *cp2; int views; if ((cp != NULL) && (strncmp(cp, "gopher://", 9) == 0)) { if ( GSgplusInited(gs) ) { for (views=0; views< GSgetNumViews(gs); views++) { if (!(strncasecmp(VIgetType(GSgetView(gs,views)), "text/html", 9))) { cp = strdup(cp); /** find the type character **/ cp2 = strchr(cp+10, '/'); if (cp2 != NULL) { *(++cp2) = 'h'; *(++cp2) = 'h'; } } } } } return cp; } char * GStoNetURL(gs,url) GopherObj *gs; char *url; { char *path, *ftphost, *ftppath; *url = '\0'; path = GSgetPath(gs); /** This is a server specific hack for now.. **/ if ( (path != NULL) && (strncmp(path, "ftp:", 4) == 0) ) { ftphost = path+4; ftppath = strchr(ftphost, '@'); if (ftppath != NULL) { sprintf(url, "ftp://%.*s/", ftppath-ftphost, ftphost); ftppath++; /*** The rest is the file/path ***/ if (*ftppath == '/') ftppath++; strcat(url, ftppath); return(url); } } strcpy(url,GSgetURLhtml(gs)); return(url); } void GSsetstringAsk(gs,ask) GopherObj *gs; char *ask; { GSsetBlock(gs,"ASK",ask,TRUE); GSsetAsk(gs,TRUE); } Blockobj* GSfindBlock(gs, blockname) GopherObj *gs; char *blockname; { int x; x = BLAsearch(GSgetOtherBlocks(gs), blockname); Debug("GSfind num is %d\n", x); if (x < 0) return(NULL); else return(GSgetBlock(gs, x)); } void GSsetBlock(gs, blockname, text, appendIfPoss) GopherObj *gs; char *blockname; char *text; boolean appendIfPoss; { Blockobj *bl = NULL; boolean newbl = FALSE; Debug("GSsetBlock:%s;",blockname); Debug("%s\r\n", text); if (appendIfPoss) bl = GSfindBlock(gs, blockname); if (bl == NULL) { newbl = TRUE; bl = BLnew(); } BLsetName(bl, blockname); BLaddText(bl, text); #ifdef DEBUGGING if (DEBUG) BLtoNet(bl, fileno(stderr), TRUE); #endif if (gs->gplus == NULL) GSplusnew(gs); if (newbl) { BLApush(gs->gplus->OtherBlocks, bl); BLdestroy(bl); } } /* * Set the Askdata, destroy if necessary.. */ char ** GSsetAskdata(gs, askdata) GopherObj *gs; char **askdata; { if (!GSgplusInited(gs)) return(NULL); /** Destroy data if necessary **/ if (gs->gplus->Askdata != NULL) { int i=0; char **Askmoo = gs->gplus->Askdata; while (Askmoo[i] != NULL) { free(Askmoo[i++]); } free(Askmoo); } gs->gplus->Askdata = askdata; return(askdata); } /* * Set the administrator line as defined in Gopher+ protocol * Name */ /* Converts the gopher+ moddate from to a struct tm ptr */ struct tm * GSgetModDateTM(gs) GopherObj *gs ; { static struct tm time; char *cp; #ifndef NO_MKTIME time_t converted; #endif if ( (gs->gplus == NULL) || (STRget(gs->gplus->ModDate) == NULL) ) return NULL; cp = strchr(STRget(gs->gplus->ModDate), '<'); if (cp == NULL) return NULL; #define ASCII_TO_INT(a) (a - '0') /* Should really do some sanity checking on the input here */ time.tm_year = ((ASCII_TO_INT(cp[1]) * 1000) + (ASCII_TO_INT(cp[2]) * 100) + (ASCII_TO_INT(cp[3]) * 10) + ASCII_TO_INT(cp[4]) ) - 1900; time.tm_mon = (ASCII_TO_INT(cp[5]) * 10) + ASCII_TO_INT(cp[6]) - 1; time.tm_mday = (ASCII_TO_INT(cp[7]) * 10) + ASCII_TO_INT(cp[8]); time.tm_hour = (ASCII_TO_INT(cp[9]) * 10) + ASCII_TO_INT(cp[10]); time.tm_min = (ASCII_TO_INT(cp[11]) * 10) + ASCII_TO_INT(cp[12]); time.tm_sec = (ASCII_TO_INT(cp[13]) * 10) + ASCII_TO_INT(cp[14]); time.tm_isdst = -1; /* make the system figure it out */ #ifndef NO_MKTIME /* if mktime() is present, let it do sanity checking and day of week setting for us */ converted = mktime(&time); if (converted != -1) return (localtime(&converted)); #endif return (&time); } #if defined(__VMS) || (defined(GINTERNATIONAL) && !defined(NO_STRFTIME)) /* In internationalized environments, convert the ModDate string to the * native language when it's requested. * If i18n is not on, the macro from GSgopherobj.h is used. * (The i18n definition's are turned on or off in Locale.h) */ char * GSgetModDate(gs) GopherObj *gs; { char dateBuf[256]; char *datetime; struct tm *modTime; if ( (gs->gplus == NULL) || (STRget(gs->gplus->ModDate) == NULL) ) return NULL; modTime = GSgetModDateTM(gs); if ( (modTime != NULL) && strftime(dateBuf, sizeof(dateBuf) - 16, "%c ", modTime) ) { datetime = strchr(STRget(gs->gplus->ModDate), '<'); if (datetime != NULL) strncat(dateBuf, datetime, 16); STRset(gs->gplus->ModDate, dateBuf); } return STRget(gs->gplus->ModDate); } #endif /* GINTERNATIONAL */ void GSsetAdmin(gs, admin) GopherObj *gs; char *admin; { if (gs->gplus == NULL) GSplusnew(gs); STRset(gs->gplus->Admin, admin); } void GSsetModDate(gs, str) GopherObj *gs; char *str; { if (gs->gplus == NULL) GSplusnew(gs); STRset(gs->gplus->ModDate, str); } /** Add a view for a specific gopherobj **/ void GSaddView(gs, view, language, estsize) GopherObj *gs; char *view; char *language; int estsize; { char tmpstr[64]; VIewobj *temp; if (estsize > 960) sprintf(tmpstr, "%dk", (estsize + 512)/1024); else sprintf(tmpstr, ".%dk", ((estsize+64)*10)/1024); temp = VInew(); VIsetType(temp, view); VIsetLang(temp, language); VIsetSize(temp, tmpstr); VIApush(gs->gplus->Views, temp); VIdestroy(temp); } void GSaddBlock(gs, Blockname, file) GopherObj *gs; char *Blockname; char *file; { Blockobj *bl; bl = BLnew(); BLsetName(bl, Blockname); BLsetFile(bl, file); ; if (gs->gplus == NULL) GSplusnew(gs); BLApush(gs->gplus->OtherBlocks, bl); BLdestroy(bl); } /* * Send a gopher reference into an fd */ void GStoNet(gs, sockfd, fmt, ticket) GopherObj *gs; int sockfd; GSformat fmt; char *ticket; { char buf[1024]; static char *nullword = "(NULL)"; if (ticket == NULL) ticket = ""; buf[0] = GSgetType(gs); if (buf[0] == '\0') /* For example a .names with no Type */ buf[0] = '3'; if (fmt == GSFORM_G0) { sprintf(buf + 1, "%s\t%s%s\t%s\t%d", GSgetTitle(gs) ? GSgetTitle(gs) : nullword, ticket ? ticket : nullword, GSgetPath(gs) ? GSgetPath(gs) : nullword, GSgetHost(gs) ? GSgetHost(gs) : nullword, GSgetPort(gs)); if (GSisAsk(gs)) strcat(buf, "\t?\r\n"); else if (GSisGplus(gs)) strcat(buf, "\t+\r\n"); else strcat(buf, "\r\n"); writestring(sockfd, buf); Debug("GStoNet:%s", buf); } else if (fmt == GSFORM_GPLUS) { ; } else if (fmt == GSFORM_HTML) { char url[256]; if (GSgetType(gs) == A_INFO) { writestring(sockfd, GSgetTitle(gs)); return; } GStoNetURL(gs, url); if (url[0] == '\0') return; sprintf(buf, "%s\r\n", url, GSgetTitle(gs)); writestring(sockfd, buf); if (GSgplusInited(gs)) { int j; Blockobj *bl; bl = GSfindBlock(gs, "ABSTRACT"); if (bl != NULL) { writestring(sockfd, "
"); BLtoNet(bl, sockfd, FALSE); writestring(sockfd, "\r\n"); } } if (GSgetWeight(gs) != 0) { sprintf(buf, "
Score: %d\r\n", GSgetWeight(gs)); writestring(buf); } } } /* * Send a long gopher data descriptor * * filter is a character array of blocks to send */ void GSplustoNet(gs, sockfd, filter, ticket) GopherObj *gs; int sockfd; char **filter; char *ticket; { int i; char tmpstr[256]; boolean sendviews, sendadmin, sendothers; #ifdef VMS_SERVER char *admin; char *moddate; #endif if (filter == NULL) sendviews = sendadmin = sendothers = TRUE; else { sendviews = sendadmin = sendothers = FALSE; for (i=0; filter[i] != NULL ;i++) { if (strcasecmp(filter[i], "VIEWS")==0) sendviews = TRUE; else if (strcasecmp(filter[i], "ADMIN")==0) sendadmin = TRUE; else sendothers = TRUE; } } /** Send out the old style INFO stuff **/ writestring(sockfd, "+INFO: "); GStoNet(gs,sockfd, GSFORM_G0, ticket); /** Only write out "interesting" URLs **/ if (GSgetURL(gs) != NULL) { if (strncmp(GSgetURL(gs), "gopher:", 7) != 0) { writestring(sockfd, "+URL:\r\n "); writestring(sockfd, GSgetURL(gs)); writestring(sockfd, "\r\n"); } } if (GSgplusInited(gs)) { /*** Should special case for local filename.... ***/ #ifndef VMS_SERVER if (GSgetAdmin(gs) != NULL && GSgetModDate(gs) != NULL && sendadmin){ writestring(sockfd, "+ADMIN:\r\n Admin: "); writestring(sockfd, GSgetAdmin(gs)); writestring(sockfd, "\r\n Mod-Date: "); writestring(sockfd, GSgetModDate(gs)); if (GSgetTTL(gs) > -1) { writestring(sockfd, "\r\n TTL: "); sprintf(tmpstr, "%d", GSgetTTL(gs)); writestring(sockfd, tmpstr); } writestring(sockfd, "\r\n"); } #else if (sendadmin) { admin=GSgetAdmin(gs); moddate=GSgetModDate(gs); if (admin || moddate || (GSgetTTL(gs) > -1)) { writestring(sockfd, "+ADMIN:\r\n"); if (admin) { writestring(sockfd, " Admin: "); writestring(sockfd, admin); writestring(sockfd, "\r\n"); } if (moddate){ writestring(sockfd, " Mod-Date: "); writestring(sockfd, moddate); writestring(sockfd, "\r\n"); } if (GSgetTTL(gs) > -1){ writestring(sockfd, " TTL: "); sprintf(tmpstr, "%d", GSgetTTL(gs)); writestring(sockfd, tmpstr); writestring(sockfd, "\r\n"); } } } #endif if (GSgetNumViews(gs) > 0 && sendviews) { writestring(sockfd, "+VIEWS:\r\n"); for (i=0 ; i< GSgetNumViews(gs); i++) { VItoLine(GSgetView(gs, i), tmpstr); writestring(sockfd, tmpstr); writestring(sockfd, "\r\n"); } } if (sendothers) { for (i=0; i< GSgetNumBlocks(gs); i++) BLtoNet(GSgetBlock(gs, i),sockfd, TRUE); } } } /*** GSplusfromnet() assumes that the leading +INFO text has been read from the file descriptor. It returns 1 if there's more data (and another INFO block) 0 if there's EOF SOFTERROR caller can keep reading HARDERROR caller should stop reading ***/ int GSplusfromNet(gs, fd) GopherObj *gs; int fd; { int result,readok,i; boolean nextinfo = FALSE; char plusfield; Blockobj *bl; char inputline[512]; if (gs->gplus == NULL) GSplusnew(gs); bl = BLnew(); /** get the gopher-data descriptor **/ readok = GSfromNet(gs, fd); if (readok == HARDERROR) return(HARDERROR); /** If readok is softerror we still need to look for blocks to throw away any data before next item **/ /** Now start looking for blocks. Process blocks if we know how. **/ /** State: _GotGREF_ **/ if ((result = readrecvbuf(fd, &plusfield, 1))<0) return(HARDERROR); while (!nextinfo) { if (result == 0) { /*** We're done ***/ BLdestroy(bl); if (readok == SOFTERROR) return(SOFTERROR); else return(FOUNDEOF); } switch (plusfield) { case '.': readline(fd, inputline, sizeof(inputline)); result = FOUNDEOF; break; case '+': /*** State _NewBlock_ ***/ if (readtoken(fd,inputline, sizeof(inputline), ':') <= 0) return(HARDERROR); if (strcasecmp(inputline, "INFO")==0) { /** Get rid of the space. **/ readrecvbuf(fd, &plusfield,1); BLdestroy(bl); if (readok == SOFTERROR) return(SOFTERROR); else return(MORECOMING); } /** Specialized fromNets here **/ BLinit(bl); if ((result=BLfromNet(bl, fd, inputline)) <0) { /** Bad read **/ BLdestroy(bl); return(HARDERROR); } else if (result == FOUNDEOF) /** EOF, block read ok **/ nextinfo = TRUE; else nextinfo = FALSE; /*** See what the block is. Transform it if necessary ***/ if (strcasecmp(BLgetName(bl), "VIEWS")==0) { VIAfromBL(gs->gplus->Views, bl); } else if (strcasecmp(BLgetName(bl), "ADMIN")==0) { int saveAdminBlock = 0; for (i=0; igplus->OtherBlocks, bl); } else if (strcasecmp(BLgetName(bl), "URL")==0) { char *cp; cp = BLgetLine(bl, 0); if (cp != NULL) GSsetURL(gs, cp); } else BLApush(gs->gplus->OtherBlocks, bl); break; /** '+' **/ default: /*** Hmmm plusfield wasn't a plus or '.' **/ return(HARDERROR); } /** switch plusfield **/ } /** While **/ BLdestroy(bl); return(FOUNDEOF); } /* GSfromNet - no comments on the original, so this is my (Mitra's) guess GSfromNet reads a gopher style line, it is called from: GDfromNet - in which case the gopher line is the whole item or; GSplusfromNet - in which case it is the part after the +INFO. It should return after reading the whole line, however in gopher+1.2b2 this is not always the case, especially if it sees a Type=3 returns: 1 blank line (I think?) 0 ok -1 HARDERROR readfield etc error - give up -2 SOFTERROR unrecognized or unhandleable type - skip on to next one */ extern int readfield(); extern int readline(); int GSfromNet(gs, sockfd) GopherObj *gs; int sockfd; { char foo[1024], *cp; if (readtoken(sockfd, foo, 1024, '\t')<= 0) { /* EOF or error */ return(HARDERROR); } GSsetType(gs, foo[0]); /** Get the kind of file from the first character **/ /** Filter out files that we can't deal with **/ switch (GSgetType(gs)) { case A_PDF: case A_FILE: case A_DIRECTORY: case A_MACHEX: case A_PCBIN: case A_CSO: case A_INDEX: case A_TELNET: case A_SOUND: case A_UNIXBIN: case A_GIF: case A_HTML: case A_TN3270: case A_MIME: case A_IMAGE: case A_INFO: case A_MOVIE: case A_APP: #ifdef VMS_SERVER case A_UUENCODE: #endif break; case A_ERROR: GSsetPath(gs, ""); GSsetHost(gs, ""); GSsetGplus(gs, FALSE); ZapCRLF(foo+1); cp = foo + strlen(foo+1); while (*cp == '.' && (*(cp-1) == '\n' || *(cp-1) == '\r')) { *cp = '\0'; ZapCRLF(foo+1); } break; case A_EOI: if (foo[1] == '\r' && foo[2] == '\n') return(1); default: /** Can't handle this type **/ readline(sockfd, foo, 1024);/** Cleanup **/ return(SOFTERROR); } /** Suck off the User Displayable name **/ cp = foo+1; while (*cp == '\n' || *cp == '\r') cp++; GSsetTitle(gs, cp); /** Suck off the Pathname **/ if (readtoken(sockfd, foo, 1024, '\t') <= 0) return(GSgetType(gs)==A_ERROR?0:HARDERROR); GSsetPath(gs, foo); /** Suck off the hostname **/ if (readtoken(sockfd, foo, 1024, '\t') <= 0) return(GSgetType(gs)==A_ERROR?0:HARDERROR); GSsetHost(gs, foo); if (readline(sockfd, foo, 1024)<=0) return(GSgetType(gs)==A_ERROR?0:HARDERROR); GSsetPort(gs, 0); /** Get the port number **/ if ((cp = strchr(foo, '\t')) != NULL) { *cp = '\0'; switch (*(cp+1)) { case '?': GSsetAsk(gs, TRUE); case '+': GSsetGplus(gs, TRUE); break; } } GSsetPort(gs, atoi(foo)); DebugGSplusPrint(gs,"GSfromNet end:"); return(0); } /** Copy a GopherObj ***/ void GScpy(dest, orig) GopherObj *dest, *orig; { dest->sFileType = orig->sFileType; dest->iPort = orig->iPort; dest->Itemnum = orig->Itemnum; #ifdef VMS_SERVER dest->date_cr = orig->date_cr; dest->lookaside = orig->lookaside; if (dest->Access != NULL) SiteArrDestroy(dest->Access); dest->Access = NULL; if (orig->Access != NULL) { Site *temp; int i; dest->Access = SiteArrayNew(); for (i=0; i< DAgetTop(orig->Access); i++) { temp = SiteArrgetEntry(orig->Access, i); SiteArrayAdd(dest->Access,temp->domain,temp->Level); } } dest->Defaccess = orig->Defaccess; GSsetHeader(dest, GSgetHeader(orig)); GSsetFooter(dest, GSgetFooter(orig)); GSsetRHeader(dest, GSgetRHeader(orig)); GSsetRFooter(dest, GSgetRFooter(orig)); #endif GSsetTitle(dest, GSgetTitle(orig)); GSsetPath(dest, GSgetPath(orig)); GSsetHost(dest, GSgetHost(orig)); GSsetGplus(dest, GSisGplus(orig)); GSsetAsk(dest, GSisAsk(orig)); GSsetURL(dest, GSgetURL(orig)); GSsetTTL(dest, GSgetTTL(orig)); STRinit(dest->Localfile); STRinit(dest->Localview); GSpluscpy(dest, orig); } void GSpluscpy(dest, orig) GopherObj *dest, *orig; { if (GSgplusInited(orig)) { if (!GSgplusInited(dest)) GSplusnew(dest); GSsetAdmin(dest, GSgetAdmin(orig)); GSsetModDate(dest, GSgetModDate(orig)); VIAcpy(dest->gplus->Views, orig->gplus->Views); BLAcpy(dest->gplus->OtherBlocks, orig->gplus->OtherBlocks); (void)GSsetAskdata(dest, GSgetAskdata(orig)); } } /** GSmerge combines 2 gopher objects overwriting fields defined in overlay **/ /* Called by GDaddGSmerge to merge gs into directory, that is only called by function to do this from a link */ void GSmerge(gs, overlay) GopherObj *gs, *overlay; { char *tempstr; DebugGSplusPrint(gs,"GSmerge: Original"); DebugGSplusPrint(overlay,"GSmerge: Overlay"); if (GSgetHost(overlay) != NULL) { if (GSgetType(overlay) != '\0') { /* Just setting Type wont work, since they interelated with Path * so set first char of path as well */ GSsetType(gs, GSgetType(overlay)); tempstr = GSgetPath(gs); if (*tempstr != A_DIRECTORY) { tempstr[0] = GSgetType(overlay); GSsetPath(gs, tempstr); } if (GSisAsk(overlay) && GSgetType(overlay) == A_DIRECTORY) GSaddView(gs, "application/gopher+-menu", "En_US", 0); /* lang==GDCgetLang(Config), 0 == dummy size here */ /* need this patch for proper protocol & Hgopher, which ignores gopher0 type in favor of +VIEW */ } if (GSgetTitle(overlay) != NULL) GSsetTitle(gs, GSgetTitle(overlay)); /* Don't set path - that is the key to the merge, and in the overlay most probably has the first char set to ' ' ????*/ if (GSgetHost(overlay) != NULL) GSsetHost(gs, GSgetHost(overlay)); if (GSgetPort(overlay) != 0) GSsetPort(gs, GSgetPort(overlay)); if (GSgetNum(overlay) != -1) GSsetNum(gs, GSgetNum(overlay)); if (GSgetWeight(overlay) != 0) GSsetWeight(gs, GSgetWeight(overlay)); if (GSgetAdmin(overlay) != NULL) GSsetAdmin(gs, GSgetAdmin(overlay)); #ifdef VMS_SERVER if (GSisCreateDate(overlay)) GSsetCreateDate(gs); if (overlay->Access != NULL) { Site *temp; int i; if (gs->Access != NULL) { SiteArrDestroy(gs->Access); gs->Access = NULL; } gs->Access = SiteArrayNew(); for (i=0; i< DAgetTop(overlay->Access); i++) { temp = SiteArrgetEntry(overlay->Access, i); SiteArrayAdd(gs->Access,temp->domain,temp->Level); } } if (GSgetHeader(overlay) != NULL) GSsetHeader(gs, GSgetHeader(overlay)); if (GSgetFooter(overlay) != NULL) GSsetFooter(gs, GSgetFooter(overlay)); if (GSgetRHeader(overlay) != NULL) GSsetRHeader(gs, GSgetRHeader(overlay)); if (GSgetRFooter(overlay) != NULL) GSsetRFooter(gs, GSgetRFooter(overlay)); #endif if (overlay->gplus != NULL) { if (!GSgplusInited(gs)) GSplusnew(gs); BLAcpy(gs->gplus->OtherBlocks, overlay->gplus->OtherBlocks); } } DebugGSplusPrint(gs,"GSmerge: Result"); } /** Compare two GopherObjs ***/ int GScmp(gs1, gs2) GopherObj *gs1, *gs2; { if (GSgetTitle(gs1) == NULL) return(1); if (GSgetTitle(gs2) == NULL) return(-1); return(strcasecmp(GSgetTitle(gs1), GSgetTitle(gs2))); } /*********** The following functions implement the gopher/gopher+ protocol, mostly GSconnect(), then GStransmit(), GSsendHeader() GSrecvHeader(); ************/ /* GSconnect performs a connection to socket 'service' on host * 'host'. Host can be a hostname or ip-address. If 'host' is null, the * local host is assumed. The parameter full_hostname will, on return, * contain the expanded hostname (if possible). Note that full_hostname is a * pointer to a char *, and is allocated by connect_to_gopher() * * returns Errors : ErrSocket* defined in Sockets.h or socket * */ int GSconnect(gs) GopherObj *gs; { int sockfd; Debug("GSconnect: Host=%s",GSgetHost(gs)); Debug("Port=%d\r\n",GSgetPort(gs)); sockfd = SOCKconnect(GSgetHost(gs), GSgetPort(gs)); return(sockfd); } /* * GStransmit sends the request from the client to the server * * All parameters are optional except for gs and sockfd. * * the rest pertain to gopher+ transmission. */ void GStransmit(gs, sockfd, search, command, view) GopherObj *gs; int sockfd; char *search; char *command; char *view; { char *cp; char **ask = GSgetAskdata(gs); int i; writestring(sockfd, GSgetPath(gs)); if (search != NULL) { writestring(sockfd, "\t"); writestring(sockfd, search); } /** Only send if gplus **/ if (!GSisGplus(gs)) { writestring(sockfd, "\r\n"); return; } if (command != NULL) { writestring(sockfd, "\t"); writestring(sockfd, command); } if (view != NULL) { writestring(sockfd, view); } if (ask == NULL) writestring(sockfd, "\r\n"); else { writestring(sockfd, "\t1\r\n"); GSsendHeader(sockfd, -1); for (i=0; ;i++) { cp = ask[i]; if (cp == NULL) break; writestring(sockfd, cp); writestring(sockfd, "\r\n"); } writestring(sockfd, ".\r\n"); } } /* * GSsendHeader generates an appropriate header on sockfd * */ void GSsendHeader(sockfd, size) int sockfd; int size; { char sizestr[64]; sprintf(sizestr, "+%d\r\n", size); writestring(sockfd, sizestr); } /** GSsendErrorHeader sends an error message header/message to the client **/ void GSsendErrorHeader(gs,sockfd,errortype,errormsg) GopherObj *gs; int sockfd; int errortype; char *errormsg; { char tmpstr[512]; sprintf(tmpstr, "-%d %s\r\n", errortype, errormsg); writestring(sockfd, tmpstr); } /* * GSrecvHeader will retrieve a gopher+ header, if it exists * * It returns the expected number of bytes that are headed our * way, if it can. * * Otherwise it returns -1 to indicate to read until \r\n.\r\n * or -2 to indicate to read to EOF * * If it encounters an error, it returns 0 and sets errno to the * gopher error class. */ int GSrecvHeader(gs, sockfd) GopherObj *gs; int sockfd; { char headerline[256]; Debugmsg("GSrecvHeader\n"); if (GSisGplus(gs)) { if (readline(sockfd, headerline, sizeof(headerline))<=0) return(0); ZapCRLF(headerline); if (*headerline == '+') { if (*(headerline+1) == '-') return(- (atoi(headerline+2))); else return(atoi(headerline+1)); } else if (*headerline == '-') { /*** Oh no! an error! ***/ errno = atoi(headerline+1); return(0); } } /*** Guess if we're running old style gopher ***/ else { switch (GSgetType(gs)) { case A_SOUND: case A_IMAGE: case A_GIF: case A_UNIXBIN: case A_PCBIN: return(-2); break; default: return(-1); } } return(-1); /** Should never get here **/ } /* * This routine will load up the item information from a gopher item * if the item hasn't transferred it already... * * The savename param, if TRUE will keep the current name and type */ void GSgetginfo(gs, savename) GopherObj *gs; boolean savename; { int sockfd, bytes; char inputline[256]; String *tempname = NULL; char temptype; if (!GSisGplus(gs)) return; /** Try not to overwrite stuff unnecessarily.. **/ if (GSgplusInited(gs)) { if (GSgetAdmin(gs)!=NULL) return; if (GSgetAskdata(gs) != NULL) return; } if (savename) { tempname = STRnew(); STRset(tempname, GSgetTitle(gs)); temptype = GSgetType(gs); } GSplusnew(gs); /** Send out the request **/ if ((sockfd = GSconnect(gs)) <0) { /*check_sock(sockfd, GSgetHost(gs), GSgetPort(gs));*/ return; } GStransmit(gs, sockfd, NULL, "!", NULL, NULL); bytes = GSrecvHeader(gs,sockfd); if (bytes == 0) return; /*** Read off the first info block ***/ readtoken(sockfd, inputline, sizeof(inputline), ' '); GSplusfromNet(gs, sockfd); if (savename) { GSsetTitle(gs, STRget(tempname)); GSsetType(gs, temptype); STRdestroy(tempname); } } /* * GSfromLink takes an FIO structure and starts reading from it. * * It reads until it finds a line it recognizes, then * * It keeps going until it finds * eof, a non-recognized line, as long as there is a valid Path= line * * returns -1 on an error, 0 for EOF, 1 for success */ int GSfromLink(gs, fio, host, port, directory, peer) GopherObj *gs; FileIO *fio; char *host; int port; char *directory; char *peer; { int doneflags = 0; char buf[1024]; int bytesread; boolean DomainDefault = TRUE; /** Default for using domain stuff yes/no */ boolean BadDomain = FALSE; /** For use with the Domain= line **/ boolean DidDomain = FALSE; /** Needed to make Domain= lines into logical or's **/ buf[0] = '\0'; Debugmsg("GSfromLink...\n"); while ((bytesread = FIOreadlinezap(fio, buf, sizeof(buf)))>0) { if (buf[0] == '#') { if (doneflags & G_PATH) break; /* comment */ else continue; } ZapCRLF(buf); if (strncasecmp(buf, GS_TYPE, strlen(GS_TYPE))==0) { GSsetType(gs, buf[strlen(GS_TYPE)]); if (buf[strlen(GS_TYPE)+1] == '+') GSsetGplus(gs, TRUE); if (buf[strlen(GS_TYPE)+1] == '?') GSsetAsk(gs, TRUE); doneflags |= G_TYPE; } else if (strncasecmp(buf, GS_NAME, strlen(GS_NAME))==0) { GSsetTitle(gs, buf+strlen(GS_NAME)); doneflags |= G_NAME; } else if (strncasecmp(buf, GS_PATH, strlen(GS_PATH))==0) { #ifndef VMS_SERVER if (strncmp(buf+strlen(GS_PATH), "~/",2) == 0 || strncmp(buf+strlen(GS_PATH), "./",2) == 0) { char tmpstr[256]; *tmpstr = '.'; strcpy(tmpstr+1, directory); if (directory[strlen(directory)-1] == '/') strcat(tmpstr, buf+strlen(GS_PATH)+2); else strcat(tmpstr, buf+strlen(GS_PATH)+1); GSsetPath(gs, tmpstr); } else GSsetPath(gs, buf+strlen(GS_PATH)); #else char *bp = buf+strlen(GS_PATH); String *acp = STRnew(); while (*(bp + strlen(bp)-1) == GDCcontinue && bytesread >0) { /* A continuation line */ *(bp + strlen(bp)-1) = '\0'; STRcat(acp,bp); bytesread = FIOreadlinezap(fio, bp=buf, sizeof(buf)); } STRcat(acp, bp); GSsetPath(gs, STRget(acp)); STRdestroy(acp); #endif doneflags |= G_PATH; } else if (strncasecmp(buf, GS_HOST, strlen(GS_HOST))==0) { if (buf[strlen(GS_HOST)] == '+' && buf[strlen(GS_HOST)+1] == '\0') GSsetHost(gs, host); else GSsetHost(gs, buf+strlen(GS_HOST)); doneflags |= G_HOST; } else if (strncasecmp(buf, GS_PORT, strlen(GS_PORT))==0) { if (buf[strlen(GS_PORT)] == '+' && buf[strlen(GS_PORT)+1] == '\0') GSsetPort(gs, port); else GSsetPort(gs, atoi(buf+strlen(GS_PORT))); doneflags |= G_PORT; } else if (strncasecmp(buf, GS_NUMB, strlen(GS_NUMB))==0) GSsetNum(gs, atoi(buf+strlen(GS_NUMB))); else if (strncasecmp(buf, GS_ABSTRACT, strlen(GS_ABSTRACT))==0) { char *acp; acp = buf+strlen(GS_ABSTRACT); while (*(buf + strlen(buf)-1) == GDCcontinue && bytesread >0) { /* A continuation line */ *(buf + strlen(buf)-1) = '\0'; GSsetAbstract(gs, acp); bytesread = FIOreadlinezap(fio, buf, sizeof(buf)); acp = buf; } GSsetAbstract(gs, acp); } else if (strncasecmp(buf, GS_ADMIN, strlen(GS_ADMIN)) == 0) GSsetAdmin(gs, buf +strlen(GS_ADMIN)); else if (strncasecmp(buf, GS_URL, strlen(GS_URL)) == 0) doneflags |= GSfromURL(gs, buf + strlen(GS_URL), host, port); #ifndef VMS_SERVER else if (strncmp(buf, "Domaindef=", 10)==0 && peer != NULL) { DomainDefault = (strcasecmp(buf+10, "no")!=0); Debug("Default Domain is %s\n", buf+10); } else if (strncmp(buf, "Domain=", 7) ==0 && peer != NULL) { /** Check to see if the peer matches the domain **/ int peerlen,domainlen; boolean TestResult = !DomainDefault; char *host = buf+7; if (*host == '!') { TestResult = TRUE; host++; } peerlen = strlen(peer); domainlen = strlen(host); if (DidDomain == TRUE && BadDomain == FALSE) break; if (domainlen > peerlen) { BadDomain = !TestResult; } else if (strncasecmp(buf+7, peer + peerlen - domainlen, domainlen)== 0) { /** Domains match, do it! **/ BadDomain = TestResult; } else BadDomain = !TestResult; DidDomain = TRUE; } #endif #ifndef __VMS else if (strncmp(buf, "Domain_pat=", 11) ==0 && peer != NULL) { char *host = buf+11; boolean TestResult = !DomainDefault; if (DidDomain == TRUE && BadDomain == FALSE) break; if (*host == '!') { host++; TestResult = TRUE; } /** Check for domain using regexps **/ if (re_comp(host) != NULL) break; if (re_exec(peer) == 1) BadDomain = TestResult; else BadDomain = !TestResult; DidDomain = TRUE; } #endif else if (strncasecmp(buf, GS_TTL, strlen(GS_TTL)) == 0) { GSsetTTL(gs, atoi(buf+strlen(GS_TTL))); } #ifdef VMS_SERVER else if (strncasecmp(buf, GS_CREATE, strlen(GS_CREATE))==0) GSsetCreateDate(gs); else if (strncasecmp(buf, GS_MODIFY, strlen(GS_MODIFY))==0) GSsetModifyDate(gs); else if (strncasecmp(buf, GS_HDDN, strlen(GS_HDDN))==0) GSsetNum(gs, -99); else if (strncasecmp(buf, GS_ACCS, strlen(GS_ACCS))==0) { if (GSgetAccess(gs) == NULL) GSsetAccess(gs, SiteArrayNew()); GSsetAccessSite(gs, buf+strlen(GS_ACCS)); GSsetDefAcc(gs,SiteDefAccess(gs->Access)); if (GSgetDefAcc(gs) == ACC_UNKNOWN) GSsetDefAcc(gs,ACC_FULL); } else if (strncasecmp(buf, GS_HEAD, strlen(GS_HEAD))==0) GSsetHeader(gs, buf+strlen(GS_HEAD)); else if (strncasecmp(buf, GS_FOOT, strlen(GS_FOOT))==0) GSsetFooter(gs, buf+strlen(GS_FOOT)); else if (strncasecmp(buf, GS_RHEAD, strlen(GS_RHEAD))==0) GSsetRHeader(gs, buf+strlen(GS_RHEAD)); else if (strncasecmp(buf, GS_RFOOT, strlen(GS_RFOOT))==0) GSsetRFooter(gs, buf+strlen(GS_RFOOT)); #endif else break; /*** Unknown name/item ***/ } Debugmsg("Done with this link item\n"); if (BadDomain) return(SOFTERROR); if (bytesread == 0) { if (doneflags & G_PATH) return(FOUNDEOF); /** Found the eof, plus there's a g item **/ else return(HARDERROR); /** Mangled item, plus eof, stop the game **/ } if (doneflags & G_PATH) return(MORECOMING); /** Found item, more coming. **/ else return(SOFTERROR); /** Mangled item, more coming.. **/ } /* * Fill in a GopherObj, given a URL */ int GSfromURL(gs, urltxt, host, port, doneflags) GopherObj *gs; char *urltxt; char *host; int port; int doneflags; { char tempbuf[256]; Url *url; UrlServiceType serviceType; url = URLnew(); URLset(url, urltxt); Debug("GSfromURL: %s\r\n",urltxt); GSsetURL(gs, urltxt); serviceType = URLgetService(url); switch (serviceType) { case http: if (! (doneflags & G_TYPE)) { /* It may not be HTML, but we lie and say it is so the */ /* client passes it off to a http-speaking program */ GSsetType(gs, A_HTML); doneflags |= G_TYPE; } if (! (doneflags & G_PATH)) { sprintf(tempbuf, "GET /%s", URLgetPath(url)); GSsetPath(gs, tempbuf); } doneflags |= G_PATH; break; case ftp: if (! (doneflags & G_HOST)) { GSsetHost(gs, host); doneflags |= G_HOST; } if (! (doneflags & G_PORT)) { GSsetPort(gs, port); doneflags |= G_PORT; } break; case telnet: if (! (doneflags & G_TYPE)) { GSsetType(gs, A_TELNET); doneflags |= G_TYPE; } case tn3270: if (! (doneflags & G_TYPE)) { GSsetType(gs, A_TN3270); doneflags |= G_TYPE; } break; case gopher: if (! (doneflags & G_TYPE)) { GSsetType(gs, URLgetGophType(url)); doneflags |= G_TYPE; } break; default: /* A type we can't deal with... */ return(doneflags); } if (! (doneflags & G_NAME)) { GSsetTitle(gs, URLget(url)); doneflags |= G_NAME; } /* Use login & password if needed & present */ switch (serviceType) { case telnet: case tn3270: if (URLgetUser(url) != NULL) GSsetPath(gs, URLgetUser(url)); else GSsetPath(gs, ""); doneflags |= G_PATH; break; } if (serviceType == ftp) { if (!(doneflags & G_PATH)) { if (URLgetPath(url) != NULL && *URLgetPath(url) != '\0') sprintf(tempbuf, "ftp:%s@/%s", URLgetHost(url), URLgetPath(url)); else sprintf(tempbuf, "ftp:%s@/", URLgetHost(url)); GSsetPath(gs, tempbuf); doneflags |= G_PATH; } if (! (doneflags & G_TYPE)) { if ((*(URLgetPath(url)) == '\0') || *(URLgetPath(url) + strlen(URLgetPath(url))-1) == '/') GSsetType(gs, A_DIRECTORY); else GSsetType(gs, A_FILE); doneflags |= G_TYPE; } } else { if (! (doneflags & G_HOST)) { GSsetHost(gs, URLgetHost(url)); doneflags |= G_HOST; } if (! (doneflags & G_PORT)) { GSsetPort(gs, URLgetPort(url)); doneflags |= G_PORT; } } if (! (doneflags & G_PATH) || GSgetPath(gs) == NULL) { char *cp; GSsetPath(gs, (cp=URLgetPath(url)) ? cp : ""); doneflags |= G_PATH; } return doneflags; } void GStoLink(gs, fd, AddInfo) GopherObj *gs; int fd; BOOLEAN AddInfo; { char gtype[2]; char portnum[16]; gtype[0] = GSgetType(gs); gtype[1] = '\0'; writestring(fd, "#"); writestring(fd, "\nType="); writestring(fd, gtype); if (GSisGplus(gs)) writestring(fd, "+"); writestring(fd, "\nName="); writestring(fd, GSgetTitle(gs)); writestring(fd, "\nPath="); writestring(fd, GSgetPath(gs)); writestring(fd, "\nHost="); writestring(fd, GSgetHost(gs)); writestring(fd, "\nPort="); sprintf(portnum, "%d", GSgetPort(gs)); writestring(fd, portnum); writestring(fd, "\n"); if (GSisGplus(gs) && GSgplusInited(gs) && AddInfo) { writestring(fd, "Admin="); writestring(fd, GSgetAdmin(gs)); writestring(fd, "\nModDate="); writestring(fd, GSgetModDate(gs)); writestring(fd, "\n"); } } boolean GSisText(gs, view) GopherObj *gs; char *view; { if (view == NULL) { switch (GSgetType(gs)) { case A_DIRECTORY: case A_FILE: case A_MIME: case A_CSO: case A_MACHEX: case A_HTML: return(TRUE); default: return(FALSE); } } else { char viewstowage[64], *cp; strcpy(viewstowage, view); if ((cp=strchr(viewstowage, ' '))!=NULL) { *cp = '\0'; view = viewstowage; } if (strncasecmp(view, "Text",4) == 0 || strncasecmp(view, "message/rfc822", 14)==0 || strncasecmp(view, "application/postscript", 21)==0 || strncasecmp(view, "application/mac-binhex40", 24)==0 || strncasecmp(view, "application/rtf", 15) == 0 || strncasecmp(view, "application/gopher", 18) == 0) return(TRUE); else return(FALSE); } } #ifdef DEBUGGING void GSplusPrint(gs,head) GopherObj *gs; char *head; { int i; int oldDebug = DEBUG; DEBUG=FALSE; fprintf(stderr,"%s: Type=%c,Title=%s,Path=%s,Host=%s,Port=%d,Num=%d,Weight=%d,Plus=%d,Ask=%d\r\n", head, GSgetType(gs), GSgetTitle(gs), GSgetPath(gs), GSgetHost(gs), GSgetPort(gs), GSgetNum(gs), GSgetWeight(gs), GSisGplus(gs), GSisAsk(gs) ); #ifdef VMS_SERVER fprintf(stderr,"%*c%s,Lookaside=%d,Access=%d,Head=%s,Foot=%s,RHead=%s,RFoot=%s\r\n", strlen(head)+2,' ', (gs->date_cr)?"Display=Create":"Display=Modify", (gs->lookaside), (gs->Access)?1:0, GSgetHeader(gs), GSgetFooter(gs), GSgetRHeader(gs), GSgetRFooter(gs) ); #endif if (GSgplusInited(gs)) for (i=0; i< GSgetNumBlocks(gs); i++) { BLtoNet(GSgetBlock(gs, i), fileno(stderr), TRUE); } fprintf(stderr,"===============\r\n"); DEBUG = oldDebug; } #endif #ifdef VMS_SERVER /* * This tests to see if the current Gopher Object has an access specification * matches the current client's address, which allows the specified access. */ AccessResult GScanAccess(int sockfd, GopherObj *gs, int access) { AccessResult test; if (GSgetAccess(gs) == NULL) return(SITE_OK); Debug("GScanAccess: Testing %s/", CurrentPeerName); Debug("%s ", CurrentPeerIP); Debug("for %s\n", (access==ACC_SEARCH)?"searching": (access==ACC_READ)?"reading": (access==ACC_BROWSE)?"browsing": (access==ACC_FTP)?"ftping": (access==ACC_EXEC)?"script execution": "some unexpected access"); test = SiteAccess(GSgetAccess(gs), CurrentPeerName, CurrentPeerIP, access, 0); if (test != SITE_OK) if ((GSgetDefAcc(gs) & access) == access) return(SITE_OK); return(test); } /* * Fill in a GopherObj, given an HREF= link from a WWW anchor.. * So far only works with http */ void GSfromHREF(gs, href) GopherObj *gs; char *href; { char *cp; char *host; char path[1024]; Debug("GSfromHREF %s\r\n",href) if (strncasecmp(href, "http://", 7)==0) { host = href +7; cp = strchr(href+7, '/'); if (cp == NULL) return; *cp = '\0'; strcpy(path, "GET "); strcat(path, cp+1); GSsetPath(gs, path); GSsetType(gs, A_HTML); GSsetPath(gs, path); cp = strchr(host, ':'); if (cp==NULL) GSsetPort(gs, 80); /** default WWW port **/ else { GSsetPort(gs, atoi(cp+1)); *cp = '\0'; } GSsetHost(gs, host); } } void GStoNetHTML(gs, sockfd) GopherObj *gs; int sockfd; { static char buf[1024]; static char pathbuf[1024]; buf[0] = '\0'; pathbuf[0] = '\0'; /** Convert Path so that spaces are %20 **/ Tohexstr(GSgetPath(gs), pathbuf); sprintf(buf, "%s", GSgetHost(gs), GSgetPort(gs), pathbuf, GSgetTitle(gs)); writestring(sockfd, buf); Debug("HTML: %s\n", buf); if (GSgetWeight(gs) != 0) { sprintf(buf, "Score: %d\r\n", GSgetWeight(gs)); writestring(sockfd, buf); } else writestring(sockfd, "\r\n"); } #endif .