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;
/*** 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);
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; i<BLgetNumLines(bl); i++) {
char *cp;
cp = BLgetLine(bl, i);
if (strncasecmp(cp, "Admin: ",7)==0)
GSsetAdmin(gs, cp+7);
else if (strncasecmp(cp, "Mod-Date: ", 10)==0)
GSsetModDate(gs, cp+10);
else if (strncasecmp(cp, "TTL: ", 5) == 0)
GSsetTTL(gs, atoi(cp+5));
else
saveAdminBlock = 1;
}
if (saveAdminBlock)
BLApush(gs->gplus->OtherBlocks, bl);
} else if (strcasecmp(BLgetName(bl), "URL")==0) {
char *cp;
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;
}
}
/** 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;
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);
/*********** 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
*
*/
/*
* 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.
*/
/** 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];
/*
* 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
*/
/*** 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;
}
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;
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;
}
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);
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;