/*****************************************************************************
* JLWilkinson 21-Sep-1995 Moved syslog() routines to [-.object]compatible.c
* so they're available for OpenVMS Server use too.
* M.Dunnett 09-Mar-1995 Also invoke GSprompter() when using callable TPU
* pagers.
* F.Macrides 08-Mar-1995 Use strncasecmp() for getting the AskLs value in
* AskBlock().
* F.Macrides 02-Mar-1995 Restore use of strncasecmp(command, "builtin", 7)
* when checking the PAGER_COMMAND. It's not
* indended to include a %s, and doesn't in the
* conf.h definition, but users are likely to include
* %s when setting the pager in their RC file, and
* cause the check to fail. Apparently was changed
* to strcasecmp() during a recent upgrade, because
* in *theory* that's adequate, but in *practice* it
* isn't. (Reported by Hunter Goatley.)
*/
/********************************************************************
* lindner
* 3.72
* 1995/01/25 23:10:07
* /home/arcwelder/GopherSrc/CVS/gopher+/gopher/ourutils.c,v
* Exp
*
* Paul Lindner, University of Minnesota CIS.
*
* Copyright 1991, 1992,1993 by the Regents of the University of Minnesota
* see the file "Copyright" in the distribution for conditions of use.
*********************************************************************
* MODULE: ourutils.c
* stuff that doesn't really fit anywhere else.
*********************************************************************
* Revision History:
* ourutils.c,v
* Revision 3.72 1995/01/25 23:10:07 lindner
* Remove hard limit of 5 AskL items per form
*
* Revision 3.71 1994/12/03 01:55:43 lindner
* JL Wilkinson 16-Jul-1994 Merged in TGV's syslog() functions to allow
* the CLIENT_LOGGER and TELNET_TRACE functionality to work with
* Multinet. Not tested against other VMS TCP/IP agents, and *NOT
* SUPPORTED* thru TGV -- please don't anybody ask TGV for help in using
* this. Somebody who wants to use it can Email me or the
*
[email protected] group and I'll try to assist them for
* MULTINET only (
[email protected]). Basic documentation is in
*
* Added Lance's post-pager RETURN synonyms.
*
* Indicate 'D'ownload as an option on exit from the pager in SecureMode
* and NoShellMode.
*
* Revision 3.70 1994/10/24 22:14:38 lindner
* Add pdf type
*
* Revision 3.69 1994/10/13 05:30:38 lindner
* Compiler complaint fixes
*
* Revision 3.68 1994/09/29 19:25:57 lindner
* More restrictive Unix file name filter
*
* Revision 3.67 1994/08/19 16:53:29 lindner
* Add casts for malloc calls
*
* Revision 3.66 1994/07/25 02:55:17 lindner
* Secure mode mods
*
* Revision 3.65 1994/07/19 20:18:36 lindner
* Remove CLIENT_LOGGER, Add default language choice
*
* Revision 3.64 1994/07/03 23:11:26 lindner
* Add internal download feature
*
* Revision 3.63 1994/06/29 07:04:41 lindner
* Mods for A_APP and builtin upload
*
* Revision 3.62 1994/06/09 04:11:07 lindner
* Added
[email protected] patch for off-by-one line
* counts in AskL blocks.
*
* Revision 3.61 1994/06/03 06:11:46 lindner
* Fix for big bad cso dashes
*
* Revision 3.60 1994/05/19 14:08:04 lindner
* use fast malloc on VMS VAXC
*
* Revision 3.59 1994/05/17 05:48:03 lindner
* Massive internationalization change
*
* Revision 3.58 1994/05/14 04:13:46 lindner
* Internationalization...
*
* Revision 3.57 1994/04/29 15:13:17 lindner
* Fix for items with multiple AskL items
*
* Revision 3.56 1994/04/25 18:40:05 lindner
* Fix for uninited variable i
*
* Revision 3.55 1994/04/25 03:37:43 lindner
* Modifications for Debug() and mismatched NULL arguments, added Debugmsg
*
* Revision 3.54 1994/04/13 19:14:03 lindner
* AskL modifications
*
* Revision 3.53 1994/03/08 20:38:28 lindner
* Fix for VMS compilation
*
* Revision 3.52 1994/03/08 15:55:16 lindner
* gcc -Wall fixes
*
* Revision 3.51 1994/03/08 03:25:58 lindner
* Fix for big bad telnet bug, additions for secure process i/o
*
* Revision 3.50 1994/03/04 23:31:32 lindner
* Fix for askblock from beckett
*
* Revision 3.49 1994/02/20 16:32:58 lindner
* Modify routines to use fileio routines for reading text files
*
* Revision 3.48 1993/12/28 17:33:00 lindner
* filter out more VMS characters
*
* Revision 3.47 1993/11/29 01:12:26 lindner
* In Save_file(), disallow saving of <CSO> type since this is fetched as a
* set of indexed fields instead of as a savable file. Also disallow
* saving of info and error types since there's nothing to save. (Wolfe,
* Beckett, Macrides)
*
* In Choose_View(), add case for A_MOVIE. (Macrides)
*
* Revision 3.46 1993/11/03 03:50:01 lindner
* More fixes for VMSrecords
*
* Revision 3.45 1993/11/02 21:17:44 lindner
* Better client side logging code
*
* Revision 3.44 1993/10/27 18:52:17 lindner
* Simplify VMS fixed/variable file code
*
* Revision 3.43 1993/10/26 18:22:43 lindner
* Mucho changes for VMS variable length records
*
* Revision 3.42 1993/10/22 20:33:17 lindner
* Fixed bad code in Save_file() for choosing view (Fote)
*
* Revision 3.41 1993/10/22 20:06:46 lindner
* Add optional client logging
*
* Revision 3.40 1993/10/11 17:20:43 lindner
* Fix for data/time stripping, vms multidot files, no case comparision for builtin
*
* Revision 3.39 1993/10/07 05:08:22 lindner
* In Choose_View(), remove unreachable statement.
*
* In AskBlock(), allocate memory for the form based on screen width.
*
* In Save_file(), choose a view BEFORE opening the file -- user may
* change mind and cancel, which left a zero-length file previously.
*
* In Save_file(), check to see if a view has already been chosen. This
* change requires that Save_file() have a third argument. Any call to
* Save_file() will now need a third argument. The third argument should
* be a pointer to the chosen view or NULL to indicate that one has yet to
* be chosen.
*
* In GSdisplay(), add "view" as the third argument to the two "new"
* Save_file() calls to prevent Save_file() from asking the user to choose
* a view. It is redundant in both cases.
*
* Revision 3.38 1993/09/29 22:47:21 lindner
* Fix for AskBlock memory cruft
*
* Revision 3.37 1993/09/29 21:30:29 lindner
* Fix for memory probs with ask blocks
*
* Revision 3.36 1993/09/29 20:49:42 lindner
* Remove dead code, fix for vms mail
*
* Revision 3.35 1993/09/26 09:20:54 lindner
* Change to use GSmail()
*
* Revision 3.34 1993/09/22 19:57:52 lindner
* fix for off by one and Select:
*
* Revision 3.33 1993/09/21 01:46:02 lindner
* Implement all remaining ASK block items..
*
* Revision 3.32 1993/09/18 04:44:23 lindner
* Additions to fix caching of Multiple view items
*
* Revision 3.31 1993/09/18 03:29:02 lindner
* Mucho fixes for ask block parsing..
*
* Revision 3.30 1993/09/11 05:12:41 lindner
* Many ask block fixes
*
* Revision 3.29 1993/09/08 05:23:19 lindner
* Implement Builtin pager..
*
* Revision 3.28 1993/09/08 01:12:33 lindner
* Add support for HTML and MIME on the menu displays
*
* Revision 3.27 1993/09/01 21:47:37 lindner
* Fix for compiler error on Alpha
*
* Revision 3.26 1993/08/19 20:51:52 lindner
* secure patch from mitra
*
* Revision 3.25 1993/08/19 20:30:39 lindner
* Fix problem with selecting ask item more than once
*
* Revision 3.24 1993/08/19 20:22:56 lindner
* Mitra's Debug patch
*
* Revision 3.23 1993/08/16 18:01:10 lindner
* Fix for VMSfile, mods for DEC alpha VMS
*
* Revision 3.22 1993/08/16 17:58:24 lindner
* Removed REMOTEUSER ifdefs
*
* Revision 3.21 1993/08/06 14:39:57 lindner
* One more small security patch
*
* Revision 3.20 1993/08/05 20:40:43 lindner
* Added warning message..
*
* Revision 3.19 1993/08/04 22:07:23 lindner
* Use /bin/mail instead of ucbmail
*
* Revision 3.18 1993/08/03 04:43:59 lindner
* Fix for VMS unresolved variables
*
* Revision 3.17 1993/07/30 17:31:56 lindner
* Mods to support AskP:, AskL:
*
* Revision 3.16 1993/07/29 17:20:19 lindner
* eliminate non-used variables
*
* Revision 3.15 1993/07/27 06:14:17 lindner
* Bug fix, use readrecvbuf, not readn
*
* Revision 3.14 1993/07/27 05:28:57 lindner
* Mondo Debug overhaul from Mitra
*
* Revision 3.13 1993/07/27 02:04:13 lindner
* More comments
*
* Revision 3.12 1993/07/26 20:30:05 lindner
* fix memory usage
*
* Revision 3.11 1993/07/26 15:35:28 lindner
* Fix for Unixfile to remove parens
*
* Revision 3.10 1993/07/23 04:38:43 lindner
* Mods to make client view selection pretty
*
* Revision 3.9 1993/07/20 23:14:59 lindner
* Mods to cache askdata..
*
* Revision 3.8 1993/07/07 19:43:42 lindner
* Added TPU support for pager in VMS
*
* Revision 3.7 1993/06/29 06:18:52 lindner
* recasting MIME types
*
* Revision 3.6 1993/04/15 21:27:26 lindner
* NOMAIL option, Remote user stuff, and bug in Ask
*
* Revision 3.5 1993/03/26 19:44:36 lindner
* Fix for CSO stuff and saving binary files
*
* Revision 3.4 1993/03/24 17:01:27 lindner
* Major changes to the way things are displayed/saved
*
* Revision 3.3 1993/03/18 23:32:41 lindner
* Added Note: support and default values to AskBlock
*
* Revision 3.2 1993/02/19 21:12:16 lindner
* Generalized on gopher+ stuff, strip long filenames to reasonable length
*
* Revision 3.1.1.1 1993/02/11 18:02:58 lindner
* Gopher+1.2beta release
*
* Revision 2.1 1993/02/09 22:37:50 lindner
* Much new in the way of display routines.
*
*********************************************************************/
#include "gopher.h"
#include "Stdlib.h"
#include "util.h"
#ifndef VMS
#include <pwd.h>
#else
#include <descrip.h>
#endif
#ifdef mips
char *getenv();
#endif
#include "Debug.h"
#include "fileio.h"
#include "Malloc.h"
#include "Locale.h"
#include "String.h"
void GSprompter();
int
outchar(c)
char c;
{
/** output the given character. From tputs... **/
/** Note: this CANNOT be a macro! **/
putc(c, stdout);
return(c);
}
/*
* Function that twirls on the screen..
*
*/
int
twirl()
{
static int twirlnum = 0;
static char *twirls = "-/|\\";
addch('\b');
addch(*(twirls + (twirlnum++ % 4 )));
refresh();
return(0);
}
/*
** This procedure sends a file through the mail.
*/
void
GSmail(gs)
GopherObj *gs;
{
char *Filename = GSgetLocalFile(gs);
char *Realname = GSgetTitle(gs);
static char *SaveName = NULL;
char command[512];
FileIO *infile, *mailit;
char buf[512];
char MailAddress[512];
if (SaveName==NULL) {
if ((SaveName = (char *) malloc(sizeof(char)*256)) == NULL)
perror("Out of memory!");
*SaveName = '\0';
}
if (CURGetOneOption(CursesScreen, Realname,
Gtxt("Mail current document to:",105),
SaveName)<0)
return;
if (*SaveName == '\0')
return;
/*
* You should be very careful when you add characters to this list..
*
* It could create a nasty security hole!
*/
#define ACHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789%.@!-_"
if (SecureMode || NoShellMode)
if ( strspn(SaveName, ACHARS) != strlen(SaveName)||
*SaveName == '-') {
Debugmsg("mail_file - beep - bad char")
puts(CURgetBell(CursesScreen));
fflush(stdout);
Draw_Status(Gtxt("Address syntax rejected...",65));
refresh();
return;
}
if (strchr(Realname, '\"') != NULL)
Realname = Gtxt("Gopher File",91);
Draw_Status(Gtxt("Mailing File...",106));
refresh();
#ifdef VMS
/* encase any address with '@' in double-quotes and add transport */
/* if the user hasn't done it (based on code from Earl Fogel) */
if ((strchr(SaveName, '@') != NULL) && (strchr(SaveName, '\"') == NULL))
sprintf(MailAddress, MAIL_ADRS, SaveName);
else
strcpy(MailAddress, SaveName);
sprintf(command, "%s/subject=\"%.70s\" %s %s",
MAIL_COMMAND, Realname, Filename, MailAddress);
CURexit(CursesScreen);
printf(Gtxt("Mailing file to %s...",107), MailAddress);
FIOsystem(command);
CURenter(CursesScreen);
#else
sprintf(command, "%s %s",
MAIL_COMMAND, SaveName);
infile = FIOopenUFS(Filename, O_RDONLY, 0);
mailit = FIOopenCmdline(command, "w");
if (mailit == NULL || infile == NULL) {
CursesErrorMsg(Gtxt("System problem, unable to mail",173));
return;
}
if (strlen(Realname) > 70)
Realname[70] = '\0';
FIOwritestring(mailit, "Subject: ");
FIOwritestring(mailit, Realname);
FIOwritestring(mailit, "\n\n");
while (FIOreadline(infile, buf, sizeof(buf))) {
twirl();
FIOwritestring(mailit, buf);
}
FIOclose(mailit);
FIOclose(infile);
#endif
}
/*
* This fcn allows one to choose a view out of a unlimited size list
* returns false if fails
*/
#ifndef DEFAULT_LANG /* should be set in conf.h, but just in case */
#define DEFAULT_LANG "En_US" /* English (US) */
#endif
char *
Choose_View(gs)
GopherObj *gs;
{
int viewno;
static char viewnlang[64];
char *view;
if (GSisGplus(gs)) {
GSgetginfo(gs, TRUE);
if (GSgetNumViews(gs) >1) {
int choice, default_choice = -1;
int langChoice = -1, viewChoice = -1;
char *default_lang = Gtxt(DEFAULT_LANG, 222);
char **views = (char **) malloc((1 + GSgetNumViews(gs)) * sizeof(char *));
char **choices = (char **) malloc((1 + GSgetNumViews(gs)) * sizeof(char *));
if ( (views == NULL) || (choices == NULL) )
return NULL;
for (viewno=0; viewno <GSgetNumViews(gs); viewno++) {
VIewobj *thisView = GSgetView(gs, viewno);
views[viewno] = strdup(VIgetViewnLang(thisView, viewnlang));
choices[viewno] = strdup(VIgetPrettyView(thisView,viewnlang));
if (default_choice < 0) {
int langMatch = !(strcasecmp(VIgetLang(thisView), default_lang));
int viewMatch = !(strcasecmp(VIgetType(thisView), "text/plain"));
if (langMatch && viewMatch)
default_choice = viewno;
else {
if (langMatch && (langChoice < 1))
langChoice = viewno;
if (viewMatch && (viewChoice < 1))
viewChoice = viewno;
}
}
}
if (default_choice < 0) {
if (viewChoice >= 0)
default_choice = viewChoice;
else if (langChoice >= 0)
default_choice = langChoice;
else
default_choice = 0;
}
choices[viewno] = NULL;
choice = CURChoice(CursesScreen, GSgetTitle(gs), choices,
Gtxt("Choose a document type",73),
default_choice);
Debug("Choice was %d\n", choice);
if (choice == -1)
return(NULL);
sprintf(viewnlang, "%s", views[choice]);
for (viewno = 0; viewno <GSgetNumViews(gs); viewno++) {
free(views[viewno]);
free(choices[viewno]);
}
free(views);
free(choices);
return(viewnlang);
}
else if (GSgetNumViews(gs) == 1)
return(VIgetViewnLang(GSgetView(gs, 0), viewnlang));
else
return(NULL);
}
else {
switch (GSgetType(gs)) {
case A_FILE:
case A_CSO:
view = "text/plain";
break;
case A_MIME:
view = "message/rfc822";
break;
case A_MACHEX:
view = "application/mac-binhex40";
break;
case A_HTML:
view = "text/html";
break;
case A_GIF:
view = "image/gif";
break;
case A_IMAGE:
view = "image";
break;
case A_MOVIE:
view = "video";
break;
case A_SOUND:
view = "audio/basic";
break;
case A_TN3270:
view = "terminal/tn3270";
break;
case A_TELNET:
view = "terminal/telnet";
break;
case A_UNIXBIN:
case A_PCBIN:
view = "application/octet-stream";
break;
case A_PDF:
view = "application/pdf";
break;
default:
return(NULL);
}
return(view);
}
}
Requestitem *
REQitemnew()
{
Requestitem *temp;
temp = (Requestitem*) (malloc(sizeof(Requestitem)));
if (temp == NULL)
return(NULL);
temp->stowage = (char *) malloc(sizeof(char) * COLS);
temp->stowage[0] = '\0';
temp->choices = NULL;
temp->prompt = NULL;
return(temp);
}
void
REQitemdestroy(req)
Requestitem *req;
{
int j;
if (req->stowage != (char*) NULL)
free(req->stowage);
if (req->prompt != (char*) NULL)
free(req->prompt);
if (req->choices != (char**) NULL) {
for (j=0; req->choices[j] != (char*) NULL; j++)
free(req->choices[j]);
free(req->choices);
}
free(req);
}
/*
* Crude implementation of the ASK block stuff.
*/
char **
AskBlock(gs)
GopherObj *gs;
{
int Asknum, reqnum, AskLs = 0;
Blockobj *bl;
Requestitem **items;
char **asktypes;
char **responses = NULL;
char askline[256];
char *defaultval;
int i;
GSgetginfo(gs, TRUE);
bl = GSfindBlock(gs, "ASK");
if (bl == NULL)
return(NULL);
/** Find out how many ASKL items we have... **/
for (Asknum=0; Asknum < BLgetNumLines(bl); Asknum++) {
if (strncasecmp(BLgetLine(bl, Asknum), "AskL:", 5) == 0)
AskLs++;
}
/** Can't guess how many askl things we'll have.. **/
items = (Requestitem **) malloc(sizeof(Requestitem*)*
(BLgetNumLines(bl) + (AskLs * 10) + 1));
asktypes = (char **) malloc(sizeof(char*) *
(BLgetNumLines(bl) + (AskLs * 10) + 1));
reqnum = 0;
for (Asknum=0; Asknum <BLgetNumLines(bl); Asknum++, reqnum++) {
char *askprompt, *cp;
strcpy(askline, BLgetLine(bl, Asknum));
items[reqnum] = REQitemnew();
/*** find the type of question ***/
askprompt = strchr(askline, ':');
if (askprompt == NULL) {
/* Empty line crashes CURRequester unless we do this*/
items[reqnum]->prompt = strdup("");
items[reqnum]->thing = CUR_LABEL;
asktypes[reqnum] = strdup("Note:");
continue;
}
*(askprompt+1) = '\0';
askprompt+=2;
/*** Zap the tabs, and load it up.. ***/
cp = strchr(askprompt, '\t');
if (cp != NULL) {
defaultval = cp+1;
*cp = '\0';
} else
defaultval = NULL;
asktypes[reqnum] = strdup(askline);
items[reqnum]->prompt = strdup(askprompt);
if (strncasecmp(askline, "Note:", 5) == 0)
items[reqnum]->thing = CUR_LABEL;
else if (strncasecmp(askline, "Choose:", 7) == 0) {
int cnum = 0;
items[reqnum]->thing = CUR_CHOICE;
items[reqnum]->chooseitem = 0;
/*** CURChoice() has a limit of 99 choices ***/
items[reqnum]->choices = (char**) malloc(sizeof(char*)*100);
/*** add list of choices to struct **/
while ((cp = strchr(defaultval, '\t')) != NULL) {
*cp = '\0';
items[reqnum]->choices[cnum++] = strdup(defaultval);
defaultval = cp+1;
}
if (defaultval != NULL)
items[reqnum]->choices[cnum++] = strdup(defaultval);
items[reqnum]->choices[cnum] = NULL;
}
else if (strncasecmp(askline, "Select:", 7) == 0) {
items[reqnum]->thing = CUR_CHOICE;
items[reqnum]->chooseitem = 0;
items[reqnum]->choices = (char**) malloc(sizeof(char*)*3);
cp = strrchr(items[reqnum]->prompt, ':');
if (cp != NULL) {
*cp = '\0';
cp++;
if (*cp == '1')
items[reqnum]->chooseitem = 1;
}
items[reqnum]->choices[0] = strdup(Gtxt("No",111));
items[reqnum]->choices[1] = strdup(Gtxt("Yes",180));
items[reqnum]->choices[2] = NULL;
}
else if (strncasecmp(askline, "AskP:", 5)==0)
items[reqnum]->thing = CUR_PASSWD;
else if (strncasecmp(askline, "Choosef:", 8) == 0)
items[reqnum]->thing = CUR_FNAME;
else if (strncasecmp(askline, "AskL:", 5)==0){
int x;
items[reqnum]->thing = CUR_LABEL;
/** Add a bunch more... **/
for (x=0; x < 10; x++) {
reqnum++;
items[reqnum] = REQitemnew();
items[reqnum]->prompt = strdup("");
items[reqnum]->thing = CUR_ASKL;
asktypes[reqnum] = NULL;
}
}
else
items[reqnum]->thing = CUR_PROMPT;
if (defaultval != NULL)
strncat(items[reqnum]->stowage, defaultval, COLS-1);
}
items[reqnum] = NULL;
if (!CURrequester(CursesScreen, GSgetTitle(gs), items)) {
int respnum = 0;
char *choosefile = NULL;
responses = (char**) malloc(sizeof(char*)*(10+reqnum));
for (i=0; i <reqnum; i++) {
if (strcasecmp(asktypes[i], "AskL:")==0) {
int alnum, extra;
char numlinestr[16];
/** Okay, figure out how many lines were typed **/
for (alnum=9; alnum >= 0; alnum--) {
if (*(items[alnum+i]->stowage) != '\0')
break;
}
alnum++;
sprintf(numlinestr, "%d", alnum);
responses[respnum++] = strdup(numlinestr);
extra = 10 - alnum;
while (alnum) {
responses[respnum++] = strdup(items[++i]->stowage);
alnum--;
}
i += extra;
} else if (strcasecmp(asktypes[i], "Select:")==0) {
if (items[i]->chooseitem == 0)
responses[respnum++] = strdup("0");
else
responses[respnum++] = strdup("1");
} else if (items[i]->thing == CUR_CHOICE) {
responses[respnum++] = strdup(items[i]->choices[items[i]->chooseitem]);
}
else if (items[i]->thing == CUR_PROMPT ||
items[i]->thing == CUR_PASSWD)
responses[respnum++] = strdup(items[i]->stowage);
}
responses[respnum++] = NULL;
}
/*** Free memory ***/
for (i=0; i<Asknum; i++) {
REQitemdestroy(items[i]);
items[i] = NULL;
if (asktypes[i]!=NULL)
free(asktypes[i]);
}
free(items);
free(asktypes);
return(responses);
}
/*
* Handle a gplus error
*/
#define GERRMSGSIZE 25
void
Gplus_Error(sockfd)
int sockfd;
{
char inputline[256];
char *errmsg[GERRMSGSIZE];
int i;
Debug("Gplus_Error on %d...", sockfd);
for (i=0; i < GERRMSGSIZE;i++) {
char *cp;
if (readline(sockfd, inputline, sizeof(inputline)) <=0)
break;
ZapCRLF(inputline);
if (strcmp(inputline, ".")==0)
break;
cp = inputline;
while ((cp = strchr(cp, '\t')) != NULL)
*cp = ' ';
errmsg[i] = strdup(inputline);
}
errmsg[i] = NULL;
CURDialog(CursesScreen, Gtxt("Gopher Transmission Error",95), errmsg);
for (i=0; ; i++) {
if (errmsg[i] == NULL)
break;
else
free(errmsg[i]);
}
}
/*
* This fcn transfers a file from a gopher server into f.
*
*/
boolean
GStoFile(gs, f, view, twirlfn)
GopherObj *gs;
FILE *f;
char *view;
int (*twirlfn)();
{
int numread, sockfd;
char buf[1024];
int bytemethod;
int line = 0, i;
/*** Check for gopher+ and multiple views ***/
Debug("GStoFile, view %s",view);
if ((sockfd = GSconnect(gs)) <0) {
check_sock(sockfd, GSgetHost(gs), GSgetPort(gs));
return(FALSE);
}
/** Send out the request **/
GStransmit(gs, sockfd, NULL, "+", view);
bytemethod = GSrecvHeader(gs, sockfd);
if (bytemethod == 0) {
Gplus_Error(sockfd);
return(FALSE);
}
Draw_Status(Gtxt("Receiving File...",126));
refresh();
if (GSisText(gs, view)) {
char cso_click = '\0';
while (readline(sockfd, buf, sizeof(buf)) > 0) {
ZapCRLF(buf);
line ++;
if (*buf == '.' && *(buf+1) == '\0')
break;
if (GSgetType(gs) == A_CSO) {
if (*buf == '2')
break;
if ((*buf >= '3') && (*buf <= '9')) {
fprintf(f, "%s\n", GSgetPath(gs));
fprintf(f, "%s\n", buf+4);
break;
}
if (*buf == '-') {
if (buf[1] >= '3' && buf[1] <= '9') {
fprintf(f, "%s\n", GSgetPath(gs));
fprintf(f, "%s\n", buf+5);
}
else {
char *colonpos = strchr(buf+5,':');
i = '\0';
if (colonpos != NULL && *(colonpos-1) != cso_click) {
fprintf(f, "-------------------------------------------------------\n");
cso_click = *(colonpos-1);
}
fputs((colonpos ? colonpos+1 : buf+6), f);
fputc('\n', f);
}
}
}
else {
fputs(buf, f);
/** Don't cut long lines... **/
if (strlen(buf) < sizeof(buf))
putc('\n', f);
}
if ((line % 25) == 0)
twirlfn();
}
}
else {
#if defined(VMS) && defined(VMSRecords)
while((numread = readrecvbuf(sockfd, buf, 512)) > 0) {
if (numread < 512)
for (i = numread; i < 512; i++) buf[i] = 0;
if (fwrite(buf, 512, 1, f) == 0) {
#else
while ((numread = readrecvbuf(sockfd, buf, sizeof buf)) > 0) {
if (fwrite(buf, numread, 1, f) == 0) {
#endif
CursesErrorMsg(Gtxt("Problems Writing",123));
closenet(sockfd);
return(FALSE);
}
twirlfn();
}
}
if (GSgetType(gs) == A_CSO)
writestring(sockfd, "quit\r\n");
closenet(sockfd);
return(TRUE);
}
/*
* This function makes a nice vms filename.
*/
void
VMSfile(fname)
char *fname;
{
#ifndef VMS
return;
#else
char *cp, *dot, *end;
int j, k;
/*** Trim off date-size suffix, if present ***/
end = fname + strlen(fname);
if ((*(end - 1) == ']') && ((cp = strrchr(fname, '[')) != NULL) &&
(cp > fname) && *(--cp) == ' ')
*cp = '\0';
/*** Replace illegal or problem characters ***/
dot = fname + strlen(fname);
for (cp = fname; cp < dot; cp++) {
/** Replace with underscores **/
if (*cp == ' ' || *cp == '/' || *cp == ':' ||
*cp == '[' || *cp == ']' || *cp == '{' ||
*cp == '}')
*cp = '_';
/** Replace with dashes **/
else if (*cp == '!' || *cp == '?' || *cp == '\'' ||
*cp == ',' || *cp == ':' || *cp == '\"' ||
*cp == '+' || *cp == '@' || *cp == '\\' ||
*cp == '(' || *cp == ')' || *cp == '=' ||
*cp == '<' || *cp == '>' || *cp == '#' ||
*cp == '%' || *cp == '*' || *cp == '`' ||
*cp == '~' || *cp == '^' || *cp == '|' ||
*cp == '/')
*cp = '-';
}
/** Collapse any serial underscores **/
cp = fname + 1;
j = 0;
while (cp < dot) {
if (fname[j] == '_' && *cp == '_')
cp++;
else
fname[++j] = *cp++;
}
fname[++j] = '\0';
/** Collapse any serial dashes **/
dot = fname + (strlen(fname));
cp = fname + 1;
j = 0;
while (cp < dot) {
if (fname[j] == '-' && *cp == '-')
cp++;
else
fname[++j] = *cp++;
}
fname[++j] = '\0';
/** Trim any trailing or leading **/
/** underscrores or dashes **/
cp = fname + (strlen(fname)) - 1;
while (*cp == '_' || *cp == '-')
*cp-- = '\0';
if (fname[0] == '_' || fname[0] == '-') {
dot = fname + (strlen(fname));
cp = fname;
while ((*cp == '_' || *cp == '-') && cp < dot)
cp++;
j = 0;
while (cp < dot)
fname[j++] = *cp++;
fname[j] = '\0';
}
/** Replace all but the last period with _'s, or second **/
/** to last if last is followed by a terminal Z or z, **/
/** e.g., convert foo.tar.Z to **/
/** foo.tar_Z **/
j = strlen(fname) - 1;
if ((dot = strrchr(fname, '.')) != NULL) {
if (((fname[j] == 'Z' || fname[j] == 'z') && fname[j-1] == '.') &&
(((cp = strchr(fname, '.')) != NULL) && cp < dot)) {
*dot = '_';
dot = strrchr(fname, '.');
}
cp = fname;
while ((cp = strchr(cp, '.')) != NULL && cp < dot)
*cp = '_';
/** But if the root is > 39 characters, move **/
/** the period appropriately to the left **/
while (dot - fname > 39) {
*dot = '\0';
if ((cp = strrchr(fname, '_')) != NULL) {
*cp = '.';
*dot = '_';
}
else if ((cp = strrchr(fname, '-')) != NULL) {
*cp = '.';
*dot = '_';
}
else {
*dot = '_';
j = strlen(fname);
fname[j+1] = '\0';
while (j > 39)
fname[j--] = fname[j];
fname[j] = '.';
}
dot = strrchr(fname, '.');
}
/** Make sure the extension is < 40 characters **/
if ((fname + strlen(fname) - dot) > 39)
*(dot+40) = '\0';
/** Trim trailing dashes or underscores **/
j = strlen(fname) - 1;
while (fname[j] == '_' || fname[j] == '-')
fname[j--] = '\0';
}
else {
/** No period, so put one on the end, or after **/
/** the 39th character, trimming trailing dashes **/
/** or underscrores **/
if (strlen(fname) > 39)
fname[39] = '\0';
j = strlen(fname) - 1;
while ((fname[j] == '_') || (fname[j] == '-'))
j--;
fname[++j] = '.';
fname[++j] = '\0';
}
/** Make sure the rest of the original string in nulled, **/
/** to avoid problems with the CURwgetstr() line editor **/
cp = fname + strlen(fname);
while (cp < end)
*cp++ = '\0';
#endif
}
/*
* This function makes a nice UNIX filename
*/
void
UNIXfile(fname)
char *fname;
{
char *cp;
for (cp = fname; *cp != '\0'; cp++) {
switch (*cp) {
case '\'':
case '\"':
case '/':
case ' ':
case '(':
case ')':
case '&':
*cp = '-';
break;
case '[':
case ']':
case '{':
case '}':
case '|':
case '!':
case '?':
case '*':
case '`':
case '\\':
case '<':
case '>':
*cp = '_';
break;
}
}
if (strlen(fname) >50) {
fname[50] = '\0';
}
/*** Don't allow '-' or '.' as first character of filename ***/
if (fname[0] == '-' || fname[0] == '.')
fname[0] = '_';
}
/*
* This procedure prompts the user for a name, checks for pipe characters
* and ~ characters in the filename and saves the file to disk.
*
* If infile is NULL then contact the server and get the file.
*
* If saveto is non-NULL then don't prompt the user for a filename
* to save into, just "do it".
*/
void
Save_file(gs, saveto, view)
GopherObj *gs;
char *saveto, *view;
{
char Title[128], Userfilename[128];
char *cp;
char buf[1024];
boolean Openpipe = FALSE;
boolean GS2FileSucceeded = TRUE;
FILE *f;
switch (GSgetType(gs)) {
case A_CSO:
case A_DIRECTORY:
case A_ERROR:
case A_INDEX:
case A_INFO:
case A_TELNET:
case A_TN3270:
if (saveto == NULL) {
CursesErrorMsg(Gtxt("Sorry, can't save that item to a file!",154));
return;
}
}
if (view == NULL)
view = Choose_View(gs);
if (view == NULL || view == "")
return;
/*** Get the Title ***/
strcpy(Title, GSgetTitle(gs));
/*** Construct a nice default filename ***/
if (saveto == NULL) {
/* It shouldnt ever come here if Secure, but I've noticed calls
to this in the code */
if (NoShellMode || SecureMode) {
CursesErrorMsg(Gtxt("Sorry, you are not allowed to do this", 64));
return;
}
/*** Let them use the Title ***/
strcpy(Userfilename, Title);
#ifdef VMS
VMSfile(Userfilename);
#else
UNIXfile(Userfilename);
#endif
if (CURGetOneOption(CursesScreen, Title, Gtxt("Save in file:",136),
Userfilename)<0)
return;
saveto = Userfilename;
}
if (*saveto == '\0')
return;
if (view == NULL)
view = Choose_View(gs);
if (view == NULL || view == "")
return;
#ifndef VMS
if (*saveto == '|') {
/** Open a pipe! **/
Openpipe = TRUE;
saveto++;
}
if (*saveto == '~') {
/*** Save in our home directory ***/
if (*(saveto + 1) == '/' && (cp = getenv("HOME")) != NULL) {
/*** Expand ~ to the home directory ***/
strcpy(buf,cp);
buf[strlen(cp)]='/';
strcpy(buf+strlen(cp) +1, saveto+2);
strcpy(saveto, buf);
} else {
/*** Save in someone else's home directory ***/
struct passwd *pass;
cp = strchr(saveto,'/');
*cp = '\0';
pass = getpwnam(saveto+1);
if (pass != NULL) {
/** align in prep for the home dir **/
strcpy(buf,pass->pw_dir);
buf[strlen(pass->pw_dir)]='/';
strcpy(buf+strlen(pass->pw_dir)+1,cp+1);
strcpy(saveto,buf);
} else {
char tmpstr[256];
sprintf(tmpstr, Gtxt("No such user '%s'",114), saveto+1);
CursesErrorMsg(tmpstr);
return;
}
}
}
#endif
#if defined(VMS) && defined(VMSRecords)
if (GSisText(gs, view))
/*** Save ASCII files as VARIABLE length records for VMS ***/
f = fopen_VAR(saveto, "w+");
else
/*** Save binary files as FIXED 512 records for VMS ***/
f = fopen_FIX(saveto, "w+");
#else
if (Openpipe)
f = popen(saveto, "w+");
else
f = fopen(saveto, "w+");
#endif
if (f == NULL) {
char tempstr[128];
sprintf(tempstr, Gtxt("Couldn't create '%s'",80), saveto);
CursesErrorMsg(tempstr);
return;
}
if (GSgetLocalFile(gs) != NULL && strcmp(view, GSgetLocalView(gs))==0) {
/** We've already retrieved it, copy it over... **/
int oldfd, cc;
oldfd = open(GSgetLocalFile(gs), O_RDONLY, 0);
if (oldfd <0) {
CursesErrorMsg(Gtxt("Can't open old file..",68));
if (Openpipe)
pclose(f);
else
fclose(f);
return;
}
while ( (cc=read(oldfd, buf, sizeof buf)) > 0) {
if (fwrite(buf, cc, 1, f) <= 0)
CursesErrorMsg(Gtxt("Problems Writing",123));
twirl();
}
if (Openpipe)
pclose(f);
else
fclose(f);
close(oldfd);
return;
} else {
/** We don't have the file yet, let's get it... **/
GS2FileSucceeded = GStoFile(gs, f, view, twirl); /* false if fails */
if (Openpipe)
pclose(f);
else
fclose(f);
if (!GS2FileSucceeded)
unlink(saveto);
}
}
/*
* This procedure saves a directory (menu) list to a file.
* It prompts the user for a filename, checks for pipe characters
* and ~ characters in the filename and saves the directory name
* and the list of items in the file to disk.
*
* This is useful after searches for which the titles of hits
* themselves contain the information one was seeking.
*/
void
Save_list(gd)
GopherDirObj *gd;
{
char Title[256], Userfilename[128];
char *cp, *saveto;
int numitems, j;
char buf[128];
boolean Openpipe = FALSE;
FILE *f;
*Userfilename = '\0';
sprintf(Title,"%s", GDgetTitle(gd));
if (CURGetOneOption(CursesScreen, Title,
Gtxt("Save list of items in file:",137),
Userfilename)<0)
return;
saveto = Userfilename;
if (*saveto == '\0')
return;
#ifndef VMS
if (*saveto == '|') {
/** Open a pipe! **/
Openpipe = TRUE;
saveto++;
}
if (*saveto == '~') {
/*** Save in our home directory ***/
if (*(saveto + 1) == '/' && (cp = getenv("HOME")) != NULL) {
/*** Expand ~ to the home directory ***/
strcpy(buf,cp);
buf[strlen(cp)]='/';
strcpy(buf+strlen(cp) +1, saveto+2);
strcpy(saveto, buf);
} else {
/*** Save in someone else's home directory ***/
struct passwd *pass;
cp = strchr(saveto,'/');
*cp = '\0';
pass = getpwnam(saveto+1);
if (pass != NULL) {
/** align in prep for the home dir **/
strcpy(buf,pass->pw_dir);
buf[strlen(pass->pw_dir)]='/';
strcpy(buf+strlen(pass->pw_dir)+1,cp+1);
strcpy(saveto,buf);
} else {
char tmpstr[256];
sprintf(tmpstr, Gtxt("No such user '%s'",114), saveto+1);
CursesErrorMsg(tmpstr);
return;
}
}
}
#endif
#if defined(VMS) && defined(VMSRecords)
f = fopen_VAR(saveto, "w+");
#else
if (Openpipe)
f = popen(saveto, "w+");
else
f = fopen(saveto, "w+");
#endif
if (f == NULL) {
char tempstr[128];
sprintf(tempstr, Gtxt("Couldn't create '%s'",80), saveto);
/** Should give better error messages here **/
CursesErrorMsg(tempstr);
return;
}
/*** Write the Title, and put the Searchstring, ***/
/*** if present, on the next line. ***/
sprintf(buf," %s\n\n", GDgetTitle(gd));
if (Searchstring != NULL)
if ((cp = strstr(buf, Searchstring)) != NULL)
*(cp-1) = '\n';
if (fwrite(buf, strlen(buf), 1, f) <= 0)
CursesErrorMsg(Gtxt("Problems Writing",123));
twirl();
/*** Write the Items ***/
numitems = GDgetNumitems(gd);
for (j = 0; j < numitems; j++) {
sprintf(buf, "%s\n", GSgetTitle(GDgetEntry(gd, j)));
if (fwrite(buf, strlen(buf), 1, f) <= 0) {
CursesErrorMsg(Gtxt("Problems Writing",123));
}
twirl();
}
if (Openpipe)
pclose(f);
else
fclose(f);
}
/*
** This procedure exits out of the curses environment and
** displays the file indicated by pathname to the screen
** using a pager command of some sort
*/
void
GSdisplay(gs)
GopherObj *gs;
{
char command[MAXSTR];
int ch;
char *file = GSgetLocalFile(gs);
char *view = GSgetLocalView(gs);
#ifdef VMS
char *cp;
int return_status;
/** Keep DECC from complaining **/
struct dsc$descriptor_s command_desc;
command_desc.dsc$b_class = DSC$K_CLASS_S;
command_desc.dsc$b_dtype = DSC$K_DTYPE_T;
#endif
/** Search for a helper application **/
RCdisplayCommand(GlobalRC, view, file, command);
Debug("Displaying view %s ", view);
Debug("command %s ", command);
Debug("file %s", file);
Debug("/%s \n", GSgetLocalFile(gs));
if (strlen(command) == 0) {
if (SecureMode || NoShellMode)
CursesErrorMsg(Gtxt("Sorry, no viewer for the file....",166));
else
Save_file(gs, NULL, view);
return;
}
#ifdef VMS
if (strncasecmp("TPU",command,3) == 0) {
CURexit(CursesScreen);
/** Make sure we invoke TPU in /READ_ONLY/NOJOURNAL mode **/
for (cp = command; *cp; cp++)
*cp = _toupper(*cp);
if (strstr(command, "/READ") == NULL)
strcat(command,"/READ");
if (strstr(command, "/NOJOU") == NULL)
strcat(command,"/NOJOU");
/** Load the descriptor and call TPU **/
command_desc.dsc$w_length = strlen(command);
command_desc.dsc$a_pointer = command;
return_status = TPU$TPU(&command_desc);
if (!return_status&1) {
CURenter(CursesScreen);
CursesErrorMsg(Gtxt("Error invoking callable TPU as a pager!!!",85));
return;
}
if (GSgetType(gs) != A_APP) {
GSprompter(gs, view, command);
}
} else
#endif
if (strncasecmp(command, "builtin-upload", 14) == 0) {
BuiltinUploadfile();
return;
}
else if (strncasecmp(command, "builtin-download", 14) == 0) {
BuiltinDownload(getenv("HOME"));
return;
}
else if (strncasecmp(command, "builtin", 7) == 0) {
PagerBuiltin(CursesScreen, gs);
return;
}
else {
CURexit(CursesScreen);
FIOsystem(command);
if (GSgetType(gs) != A_APP) {
GSprompter(gs, view, command);
}
}
}
/*
* Display the prompt at the end of viewing a document
*/
void
GSprompter(gs, view, command)
GopherObj *gs;
char *view, *command;
{
int ch;
#ifdef VMS
printf(Gtxt("\nPress %sRETURN%s to continue",184),
CURgetHighon(CursesScreen), CURgetHighoff(CursesScreen));
if (!SecureMode && !NoShellMode) {
if (COLS < 78)
printf(",\n ");
else
printf(", ");
printf(Gtxt("%sm%s to mail, %sD%s to download, %ss%s to save, or %sp%s to print:",61),
CURgetHighon(CursesScreen), CURgetHighoff(CursesScreen),
CURgetHighon(CursesScreen), CURgetHighoff(CursesScreen),
CURgetHighon(CursesScreen), CURgetHighoff(CursesScreen),
CURgetHighon(CursesScreen), CURgetHighoff(CursesScreen));
}
#ifndef NOMAIL
else {
printf(Gtxt(", %sm%s to mail, %sD%s to download:",62),
CURgetHighon(CursesScreen), CURgetHighoff(CursesScreen),
CURgetHighon(CursesScreen), CURgetHighoff(CursesScreen));
}
#else
else {
printf(Gtxt(", %sD%s to download:",237),
CURgetHighon(CursesScreen), CURgetHighoff(CursesScreen));
}
#endif
#else /*** Unix: ****/
printf(Gtxt("\nPress <RETURN> to continue",185));
if (!SecureMode && !NoShellMode)
printf(Gtxt(",\n <m> to mail, <D> to download, <s> to save, or <p> to print:",64));
#ifndef NOMAIL
else
printf(Gtxt(", <m> to mail, <D> to download:",63));
#else
else
printf(Gtxt(", <D> to download:",238));
#endif
#endif
fflush(stdin);
fflush(stdout);
noecho();
cbreak();
ch = 0;
while (ch == 0) {
ch=getchar();
#ifdef VMS
if (HadVMSInt)
break;
#endif
if (SecureMode || NoShellMode) {
switch (ch) {
case '\n':
case '\r':
case KEY_ENTER:
case ' ':
case 'q' :
case 'Q' :
#ifdef VMS
case '\032': /* ^Z */
#endif
break;
#ifndef NOMAIL
case 'm':
#ifdef VMS
(void) getchar();
#endif
CURenter(CursesScreen);
GSmail(gs);
break;
#endif
case 'D':
#ifdef VMS
(void) getchar();
#endif
CURenter(CursesScreen);
Download_file(gs);
break;
default:
puts(CURgetBell(CursesScreen));
fflush(stdout);
ch=0;
break;
}
}
else {
switch(ch) {
case '\n':
case '\r':
case ' ' :
case 'q' :
case 'Q' :
#ifdef VMS
case '\032': /* ^Z */
#endif
break;
case 's':
#ifdef VMS
(void) getchar();
#endif
CURenter(CursesScreen);
Draw_Status(Gtxt("Saving File...",138));
refresh();
if (!(SecureMode || NoShellMode))
Save_file(gs,NULL, view);
break;
case 'p':
#ifdef VMS
(void) getchar();
#endif
if (!RCprintCommand(GlobalRC, view, GSgetLocalFile(gs), command)) {
CursesErrorMsg(Gtxt("Sorry, cannot print this document",155));
return;
}
FIOsystem(command);
break;
case 'm':
#ifdef VMS
(void) getchar();
#endif
CURenter(CursesScreen);
GSmail(gs);
break;
case 'D':
#ifdef VMS
(void) getchar();
#endif
CURenter(CursesScreen);
Download_file(gs);
break;
default:
Debugmsg("GSdisplayAfter - beep\r\n");
puts(CURgetBell(CursesScreen));
fflush(stdout);
ch=0;
break;
}
}
}
tputs(CURgetCLS(CursesScreen),1,outchar);
fflush(stdout);
}
/*
* This displays a single line message using CUR routines
*/
void
CursesMsg(Message, title)
char *Message;
char *title;
{
char *mess[2];
mess[0] = Message;
mess[1] = NULL;
Debugmsg("CursesMsg - beep\r\n")
CURBeep(CursesScreen);
CURDialog(CursesScreen, title,mess);
return;
}
/*
* This does the same as above, but puts a window title on the mess
*/
void
CursesErrorMsg(Message)
char *Message;
{
CursesMsg(Message, Gtxt("Gopher Error",90));
}
#ifdef CLIENT_LOGGER
#if defined(VMS) && defined(MULTINET)
#include "syslog.h"
#else
#include <syslog.h>
#endif
#endif
int
logrequest(msg, gs)
char *msg;
GopherObj *gs;
{
#ifdef CLIENT_LOGGER
static boolean inited = FALSE;
char *url = "";
if (inited == FALSE) {
openlog("gopher", (LOG_PID), LOG_DAEMON);
setlogmask(LOG_UPTO(LOG_INFO));
inited = TRUE;
}
if (gs != NULL)
url = GSgetURL(gs);
syslog(LOG_INFO, "%s %s", msg, url ? url : "");
#endif
return(0);
}