/********************************************************************
* lindner
* 3.106
* 1995/01/20 04:52:24
* /home/arcwelder/GopherSrc/CVS/gopher+/gopher/gopher.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: gopher.c
* Main functions for the gopher client
*********************************************************************
* Revision History:
* gopher.c,v
* Revision 3.106 1995/01/20 04:52:24 lindner
* Modifications for SOCKS
* Use VARIABLE length records for text on VMS
* Don't add the default gopher server when using the -b option.
*
* Revision 3.105 1994/12/07 07:43:22 lindner
* Fix for URLs on the command line from F.Macrdes
*
* Revision 3.104 1994/11/25 17:40:35 lindner
* Fix for folks with non gopher port of 70 in conf.h
*
* Revision 3.103 1994/11/16 18:52:52 lindner
* Fix for adding a text file to bookmarks
*
* Revision 3.102 1994/10/21 04:40:34 lindner
* IETF url format, plus ANSI printer
*
* Revision 3.101 1994/10/13 05:30:17 lindner
* Compiler complaint fixes
*
* Revision 3.100 1994/08/19 16:52:38 lindner
* Fixes for many gripe problems...
*
* Revision 3.99 1994/08/03 03:25:12 lindner
* Fix for osf
*
* Revision 3.98 1994/08/01 21:55:27 lindner
* Add back garbled flag
*
* Revision 3.97 1994/07/25 02:53:30 lindner
* Skip over type 'i' items, secure mode mods
*
* Revision 3.96 1994/07/19 20:20:00 lindner
* Gripe options added, telnet tracer etc.
*
* Revision 3.95 1994/07/08 05:55:28 lindner
* Fix to allow urls in place of hosts..
*
* Revision 3.94 1994/07/07 01:51:49 lindner
* Fix bug
*
* Revision 3.93 1994/07/06 15:36:27 lindner
* These changes are from F. Macrides:
*
* Moved declaration and initialization of setlocale_LangDir to
* globals.h.
*
* Note that setlocale() doesn't yet work with VMS Alphas, so it
* presently just returns without doing anything and the "hardcoded"
* English text is used. It all works fine with VAXen.
*
* Added code to delete the temporary file when gopher is called with the
* -p or the new -u switch.
*
* Added J. Lance Wilkinson's (
[email protected]) internationalization
* port to VMS and: 'L' operation for changing language on the fly.
*
* Pause after telnet/tn3270 to permit reading of any error messages.
* Added telnet and tn3270 to prompt for a URL via the 'w' command.
*
* Modified Alan's dialog box title and prompt for the 'w' command.
*
* NEVERSETOPTIONS compilation symbol disables ability to set options via
* SetOptions() and issues an appropriate message.
*
* NEVERSPAWN compilation symbol disables the spawn command and issues an
* appropriate message.
*
* On VMS, the NEVERSETOPTIONS and NEVERSPAWN symbols can be used to
* create a gopher image for CAPTIVE accounts which still permits
* functions that would be disabled by the -s or -S switch, but issues an
* appropriate message (rather than a DCL error message) on spawn attempt
* and precludes "creative" modifications of the display software and
* print command mappings.
*
* Added missing ! for NoShellMode under binary and and encoded save to
* disk if().
*
* Revision 3.92 1994/07/03 23:11:24 lindner
* Add internal download feature
*
* Revision 3.91 1994/06/29 05:20:37 lindner
* Check for new gopher.rc files, add support for A_APP items
*
* Revision 3.90 1994/06/09 22:13:37 lindner
* More language conversions
*
* Revision 3.89 1994/06/09 04:16:28 lindner
* Added option to allow 'd'elete only for bookmarks via a
* DELETE_BOOKMARKS_ONLY compilation symbol.
*
* Revision 3.88 1994/06/09 03:45:02 lindner
* More attempts to use sunos 4.1.3 setlocale
*
* Revision 3.87 1994/06/03 05:58:58 lindner
* Use LC_MESSAGES variable instead of LANG
*
* Revision 3.86 1994/05/27 18:08:51 lindner
* Fix for SetOptions() on VMS
*
* Revision 3.85 1994/05/27 02:22:23 lindner
* One more fix for International defs
*
* Revision 3.84 1994/05/26 19:36:45 lindner
* Fix bugs for ginternational
*
* Revision 3.83 1994/05/26 19:15:22 lindner
* Add GINTERNATIONAL stuff
*
* Revision 3.82 1994/05/25 21:46:35 lindner
* Fix for suck_sound
*
* Revision 3.81 1994/05/25 21:38:17 lindner
* Fix for mem prob..
*
* Revision 3.80 1994/05/25 20:59:42 lindner
* Fix for play command forking
*
* Revision 3.79 1994/05/24 07:00:38 lindner
* Don't allow users with rsh to specify a shell as an app..
*
* Revision 3.78 1994/05/24 05:50:10 lindner
* Fix for bad free() in SetOptions()
*
* Revision 3.77 1994/05/19 15:53:34 lindner
* Fix for vms
*
* Revision 3.76 1994/05/19 14:07:25 lindner
* use fast malloc on VMS VAXC
*
* Revision 3.75 1994/05/18 03:59:25 lindner
* Change to FIOsystem() for VMS
*
* Revision 3.74 1994/05/17 05:47:57 lindner
* Massive internationalization change
*
* Revision 3.73 1994/05/14 04:13:41 lindner
* Internationalization...
*
* Revision 3.72 1994/04/25 04:05:24 lindner
* FIOsystem() return values were reversed for VMS.
*
* Added code for mapping telnet and/or tn3270 commands to "- none -" for
* disabling them and having the client issue "Sorry, not implemented"
* messages on efforts to use Type 8 or T tuples.
*
* Added FIOsystem() failure messages for both VMS and Unix. (all from
* F.Macrides)
*
* Revision 3.71 1994/04/25 03:37:38 lindner
* Modifications for Debug() and mismatched NULL arguments, added Debugmsg
*
* Revision 3.70 1994/04/13 19:12:50 lindner
* Fix for ASK block bug
*
* Revision 3.69 1994/04/12 21:02:51 lindner
* Move file declaration inside
*
* Revision 3.68 1994/03/30 20:32:58 lindner
* Fix for deleting last item in a directory
*
* Revision 3.67 1994/03/08 15:55:09 lindner
* gcc -Wall fixes
*
* Revision 3.66 1994/03/08 03:25:55 lindner
* Fix for big bad telnet bug, additions for secure process i/o
*
* Revision 3.65 1994/03/04 23:41:17 lindner
* Better gripe, more signal handling, better forms
*
* Revision 3.64 1994/02/20 16:34:22 lindner
* Patch for weird gopher+ servers, add anon-ftp key, and localtime key
*
* Revision 3.63 1994/01/10 03:29:49 lindner
* fix for changes in GDdeleteGS
*
* Revision 3.62 1993/11/29 01:11:25 lindner
* 'v' no longer does anything if user is already viewing bookmarks.
* (Beckett)
*
* Add new routine, do_movie(), which will eventually allow viewing of
* movies. For now, just display an error message. In process_request(),
* add a switch case for the A_MOVIE type. (Wolfe, Macrides)
*
* Fix the 'm' command so that it deletes the current menu's cache files.
* Also prevent 'm' from destroying bookmarks. (Beckett)
*
* In describe_gopher(), add TRUE argument to GStoLink() so that the Admin
* and ModDate information are displayed ('=' and '^'). (Macrides)
*
* Prevent describe_gopher() from destroying the cache file for the current
* item. (Beckett, Wolfe)
*
* Revision 3.61 1993/11/03 03:55:12 lindner
* More stable file caching, subject line chopping in Gripe
*
* Revision 3.60 1993/11/02 21:17:36 lindner
* Better client side logging code
*
* Revision 3.59 1993/10/27 18:52:14 lindner
* Simplify VMS fixed/variable file code
*
* Revision 3.58 1993/10/26 18:45:09 lindner
* Move screen refresh stuff to CURwgetch()
*
* Revision 3.57 1993/10/26 18:01:01 lindner
* Allow saving to variable length record VMS files, remove Interrupted msg
*
* Revision 3.56 1993/10/22 20:24:14 lindner
* Fixes for VMS tempnam() (Fote)
*
* Revision 3.55 1993/10/22 20:06:37 lindner
* Add optional client logging
*
* Revision 3.54 1993/10/20 03:24:55 lindner
* Cleanup on a SIGHUP signal
*
* Revision 3.53 1993/10/13 16:47:23 lindner
* Fixes for empty bookmark addition problem
*
* Revision 3.52 1993/10/11 17:16:15 lindner
* Fote's mods for display applications
*
* Revision 3.51 1993/10/11 04:54:36 lindner
* Fix for segvs when exiting early
*
* Revision 3.50 1993/10/07 05:09:28 lindner
* Don't redraw the screen if we attempt to go up a directory level from
* the root level.
*
* In the 'o' command, allocate memory for the form based on screen width.
* Fix memory leak.
*
* In Gripe(), allocate memory for the form based on screen width. Fix
* email parsing under VMS. Fix problem where gopher did not delete mail
* temp files under VMS.
*
* In process_request(), make the status "Receiving xxxx..." a bit more
* descriptive for images, MIME files, and other files.
*
* Add NULL as third argument to Save_File in a couple of places
*
* Revision 3.49 1993/09/30 22:42:04 lindner
* Add option for bolding of searched words
*
* Revision 3.48 1993/09/29 20:50:57 lindner
* Always cleanupandexit instead of exit, allow deleting of last bookmark
*
* Revision 3.47 1993/09/26 09:19:44 lindner
* Changed errormsg wording
*
* Revision 3.46 1993/09/22 04:14:22 lindner
* Fix core dumps when exiting from bookmark screen, various cleanups
*
* Revision 3.45 1993/09/22 03:56:47 lindner
* Add support for tn3270 on oddball ports..
*
* Revision 3.44 1993/09/22 03:09:59 lindner
* Add command-line searching
*
* Revision 3.43 1993/09/22 01:15:49 lindner
* Add support for DEC HELP key/KEY_HELP
*
* Revision 3.42 1993/09/21 01:50:43 lindner
* Fix for caching of alternate views
*
* Revision 3.41 1993/09/18 04:40:47 lindner
* Additions to fix caching of Multiple view items
*
* Revision 3.40 1993/09/18 03:28:30 lindner
* Remove extra CURexit()
*
* Revision 3.39 1993/09/11 04:46:45 lindner
* Don't allow null applications to be added with the options code, Beep on network error
*
* Revision 3.38 1993/09/08 05:22:30 lindner
* Lobotomized the bolding code
*
* Revision 3.37 1993/09/08 01:11:51 lindner
* Added URL stuff to describe_gopher
*
* Revision 3.36 1993/09/03 03:33:45 lindner
* Inform user about mal-configuration if view has Unix piping on VMS.
*
* Deal with the problem of the gopher+ code setting view to NULL or ""
* here and there as if they were equivalent.
*
* Added code for mailing gripes from VMS systems, and fixed memory leak
* in the Unix code.
*
* Added v1.12b 'S' command for saving titles in a directory.
*
* Added titles to CURGetOneOption() calls.
*
* Revision 3.35 1993/08/25 02:57:52 lindner
* Fix for Fote mod to 'o' command
*
* Revision 3.34 1993/08/23 02:32:28 lindner
* Don't connect if nothing typed in 'o'
*
* Revision 3.33 1993/08/19 20:31:10 lindner
* remove excess variable
*
* Revision 3.32 1993/08/19 20:22:49 lindner
* Mitra's Debug patch
*
* Revision 3.31 1993/08/16 18:10:19 lindner
* Temporary code to fix DEC Alphas screen clearing, exit handler for VMS
*
* Revision 3.30 1993/08/16 17:58:20 lindner
* Removed REMOTEUSER ifdefs
*
* Revision 3.29 1993/08/09 20:28:04 lindner
* Mods for VMS for telnet dialog, argv[0]
*
* Revision 3.28 1993/08/09 20:17:59 lindner
* Fixes for CMULIB and NETLIB for VMS
*
* Revision 3.27 1993/08/05 03:24:21 lindner
* Fix for control-c on startup
*
* Revision 3.26 1993/08/04 22:08:47 lindner
* Fix for problems with '=' and '?' and /bin/mail Gripe mods
*
* Revision 3.25 1993/08/03 20:48:27 lindner
* Audio file fix from jqj
*
* Revision 3.24 1993/08/03 20:26:50 lindner
* Don't allow securemode types to use o
*
* Revision 3.23 1993/08/03 20:24:18 lindner
* Bigger Better Badder Options, inspired by jqj
*
* Revision 3.22 1993/08/03 04:43:56 lindner
* Fix for VMS unresolved variables
*
* Revision 3.21 1993/07/30 17:37:24 lindner
* SecureMode fix from Mitra
*
* Revision 3.20 1993/07/30 14:19:29 lindner
* Mitra autoexit patch
*
* Revision 3.19 1993/07/30 14:12:18 lindner
* Allow non-gplus stuff to cache
*
* Revision 3.18 1993/07/29 17:24:53 lindner
* Removed dead variable, $ and ! are synonomous now
*
* Revision 3.17 1993/07/27 05:28:48 lindner
* Mondo Debug overhaul from Mitra
*
* Revision 3.16 1993/07/27 02:02:22 lindner
* More comments, GDdelete method
*
* Revision 3.15 1993/07/26 20:29:40 lindner
* fix memory usage
*
* Revision 3.14 1993/07/26 15:35:56 lindner
* fix for longjmp params
*
* Revision 3.13 1993/07/23 04:42:58 lindner
* error checking and longjmp'ing
*
* Revision 3.12 1993/07/20 23:15:35 lindner
* Mods to cache askdata..
*
* Revision 3.11 1993/07/07 19:42:59 lindner
* fix for SGIs
*
* Revision 3.10 1993/06/29 06:18:09 lindner
* Prettier error msg for VMS
*
* Revision 3.9 1993/06/22 06:13:28 lindner
* took back fix for perror.h etc.
*
* Revision 3.8 1993/06/11 16:25:43 lindner
* Many bug fixes, open new gopher, shell escape, etc.
*
* Revision 3.7 1993/04/30 16:04:48 lindner
* Cleared gripe memory, fixed bug for long names in check_sock
*
* Revision 3.6 1993/04/13 04:56:54 lindner
* New code for REMOTEUSERS from Mitra
* Better error messages for socket connections from jqj
*
* Revision 3.5 1993/03/24 16:59:54 lindner
* Major changes to the way things are displayed
*
* Revision 3.4 1993/03/18 23:32:07 lindner
* commented out forkoff stuff for now...
*
* Revision 3.3 1993/02/19 21:08:04 lindner
* Updated most routines to get defaults from gopher+ attribute types,
* Allow commands to be pipelines and almost have forking working :-)
*
* Fixed problems with bookmarks. Still need to work on g+ bookmarks
*
* Revision 3.2 1993/02/11 18:25:45 lindner
* Fixed helpfile display.
*
*********************************************************************/
#include "gopher.h"
#if defined(VMS) && defined(A_LANGUAGE)
static CursesObj *LangScreen = NULL;
#endif
#include "Stdlib.h"
#include "Debug.h"
#include <errno.h>
#include "fileio.h"
#include "Malloc.h"
#include "patchlevel.h"
void describe_gopher();
extern int twirl();
/*
** Open a connection to another host using telnet or tn3270
*/
void
do_tel_3270(ZeGopher)
GopherStruct *ZeGopher;
{
char *Dialogmess[9];
char sMessage1[128];
char sMessage2[128];
char sTelCmd[128];
char *cp;
/* retrieve the gopher information for the telnet command*/
clear();
#ifdef VMS
refresh();
#endif
Dialogmess[0] = Gtxt("Warning!!!!!, you are about to leave the Internet",34);
Dialogmess[1] = Gtxt("Gopher program and connect to another host. If",35);
Dialogmess[2] = Gtxt("you get stuck press the control key and the",36);
#if defined(VMS) && defined(MULTINET)
Dialogmess[3] = Gtxt("^ key, and then type q.",38);
#else
Dialogmess[3] = Gtxt("] key, and then type quit",37);
#endif
Dialogmess[4] = "";
if (GSgetPort(ZeGopher) != 0)
sprintf(sMessage1,Gtxt("Connecting to %.40s, port %d using %s.",39),
GSgetHost(ZeGopher),GSgetPort(ZeGopher),
(GSgetType(ZeGopher) == A_TN3270) ? "tn3270" : "telnet");
else
sprintf(sMessage1, Gtxt("Connecting to %.40s using %s.",40),
GSgetHost(ZeGopher),
(GSgetType(ZeGopher) == A_TN3270) ? "tn3270" : "telnet");
Dialogmess[5] = sMessage1;
cp = GSgetPath(ZeGopher);
if (*cp != '\0')
sprintf(sMessage2,
Gtxt("Use the account name \"%.40s\" to log in",41),
GSgetPath(ZeGopher));
else
sMessage2[0] = '\0';
Dialogmess[6] = "";
Dialogmess[7] = sMessage2;
Dialogmess[8] = NULL;
if (CURDialog(CursesScreen, GSgetTitle(ZeGopher), Dialogmess) <0)
return;
CURexit(CursesScreen);
if (GSgetType(ZeGopher) == 'T') {
/**** A TN3270 connection ****/
RCdisplayCommand(GlobalRC, "Terminal/tn3270", GSgetHost(ZeGopher),
sTelCmd);
} else {
RCdisplayCommand(GlobalRC, "Terminal/telnet", GSgetHost(ZeGopher),
sTelCmd);
}
if (GSgetPort(ZeGopher) != 0 && GSgetPort(ZeGopher) != 23)
#if defined(VMS) && (defined(MULTINET) || defined(CMUIP))
sprintf(sTelCmd+strlen(sTelCmd),
"/PORT=%d", GSgetPort(ZeGopher));
#else
sprintf(sTelCmd+strlen(sTelCmd), " %d", GSgetPort(ZeGopher));
#endif
CURexit(CursesScreen);
#ifdef Telnet_Trace
Telnet_Trace(sTelCmd, ZeGopher);
#endif
FIOsystem(sTelCmd);
printf("\n\n");
printf(Gtxt("\nPress <RETURN> to continue",120));
fflush(stdout);
fflush(stdin);
fflush(stdin);
(void) getchar();
CURenter(CursesScreen);
return;
}
#if defined(VMS) && defined(TELNET_TRACE)
/*
** Some sites are really paranoid about attempts to connect from their site
** to specific telnet hosts (e.g., MUDDs, etc.) -- so provide a log facility
** to track specific connectsions just to see how clients are finding them.
*/
#include <descrip.h>
#include <lnmdef.h>
#include <ssdef.h>
#include "syslog.h"
/*
** Like logrequest() but should use a different log file just for this...
*/
int
trace_telnet(msg, gs)
char *msg;
GopherObj *gs;
{
static boolean inited = FALSE;
char *url = "";
char *name = "";
if (!gs)
return(0);
if (inited == FALSE) {
openlog("gopher", (LOG_PID), LOG_LOCAL7); /* MULTINET allows LOCAL7 */
setlogmask(LOG_UPTO(LOG_INFO));
inited = TRUE;
}
if (gs != NULL) {
url = GSgetURL(gs);
name = GSgetTitle(gs);
}
syslog(LOG_INFO, "%s %s%s%s%s", msg, url, strlen(name)?" \"":"",
name, strlen(name)?"\"":"");
return(0);
}
int
Telnet_Trace(char *sTelCmd, GopherStruct *ZeGopher)
{
char text[255];
int l;
static int buf_len;
static int i;
static int junk;
static int max_len;
int no_case = LNM$M_CASE_BLIND;
static $DESCRIPTOR(lname_desc,"GOPHER_TELNET_LOG");
static $DESCRIPTOR(tabnam,"LNM$SYSTEM_TABLE");
struct itmlst
{
short int buf_len;
short int item_code;
char *buffer;
int *buf_length;
};
static
struct itmlst
item_max[] = { {4,LNM$_MAX_INDEX,(char *)&max_len,&buf_len},
{0,0,0,0}};
static
struct itmlst
item_str[] = { {4,LNM$_INDEX,(char *)&i,&junk},
{0,LNM$_STRING,0,&buf_len},
{0,0,0,0}};
if (SS$_NORMAL != SYS$TRNLNM(&no_case,&tabnam,&lname_desc,0,&item_max))
return;
item_str[1].buffer = text;
for (i=0; i<=max_len; i++) {
item_str[1].buf_len = 255;
if (SS$_NORMAL==SYS$TRNLNM(&no_case,&tabnam,&lname_desc,0,&item_str)) {
text[buf_len] = '\0';
if (strcmp(text,GSgetHost(ZeGopher))==0) {
/* Log this session */
text[sprintf(text," *-%.*s>",iLevel,
"-------------------------------")]='\0';
trace_telnet(text,ZeGopher);
if(GDgetLocation(CurrentDir) != NULL) {
text[sprintf(text," + %.*s ",iLevel,
" ")]='\0';
trace_telnet(text,GDgetLocation(CurrentDir));
}
for (l = iLevel; l>0; l--) {
text[sprintf(text," +%.*s>",l,
"-------------------------------")]='\0';
trace_telnet(text, GDgetEntry(OldDirs[l-1],
GDgetCurrentItem(OldDirs[l-1])-1));
}
return;
}
}
}
return;
}
#endif
void
Gripe(gs)
GopherObj *gs;
{
char *gripeprompt[15];
char *gripemess[15];
char *cp = NULL;
char email[128], mailcmd[256], version[128];
int i;
char *mailargv[3];
FileIO *fio;
#ifdef VMS
FILE *f;
char MailAddress[512], *tmpfilename;
#endif
GSgetginfo(gs, TRUE);
if (GSisGplus(gs) && (GSgetAdmin(gs) != NULL))
cp = strchr(GSgetAdmin(gs),'<');
/* at this point cp is NULL if (and hopefully only if)
* the item is not Gopher+, there is no Admin for the item,
* or there is no e-mail address in the admin definition
*/
if (cp == NULL) {
#ifdef LOCAL_GRIPE_ADMINISTRATOR
# ifdef DOMAIN_FOR_LOCAL_GRIPES
if (strstr(GSgetHost(gs),DOMAIN_FOR_LOCAL_GRIPES) != NULL )
# endif
cp = LOCAL_GRIPE_ADMINISTRATOR;
#endif
/* if the local gripe admin won't take it, either let the user
* edit it, or put up an error message
*/
if (cp == NULL) {
#ifdef MODIFIABLE_GRIPE_TO
cp = "<>";
#else
CursesErrorMsg(
Gtxt("Can't find an administrator for this item, sorry!",48));
return;
#endif
}
}
strncpy(email, cp+1, sizeof(email));
cp = strrchr(email, '>');
if (cp != NULL)
*cp = '\0';
#define ACHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789%.@!-_"
if (SecureMode || NoShellMode)
if ( strspn(email, ACHARS) != strlen(email)||
*email == '-') {
CursesErrorMsg("Can't find an administrator for this item, sorry!");
return;
}
/** Empty out the array **/
for (i=3; i< 15; i++) {
cp = (char *) malloc(sizeof(char) * COLS);
bzero(cp,COLS*sizeof(char));
gripeprompt[i] = "";
gripemess[i] = cp;
}
for (i=0; i < 3; i++)
gripemess[i] = NULL;
gripeprompt[0] = Gtxt("Hit the Tab key at the end of each line you type.",49);
gripeprompt[1] = Gtxt("Hit the Enter key to send your message.",50);
#ifdef MODIFIABLE_GRIPE_TO
gripeprompt[2] = Gtxt("To",230);
gripemess[2] = email;
gripeprompt[13] = Gtxt("<Last Line>",231);
#else
gripeprompt[2] = "";
#endif
gripeprompt[3] = Gtxt("Subject",51);
gripeprompt[4] = Gtxt("Problem",52);
gripeprompt[14] = NULL;
if (CURRequest(CursesScreen, GSgetAdmin(gs), gripeprompt, gripemess)!=0) {
return;
}
#ifdef MODIFIABLE_GRIPE_TO
if (strlen(strcpy(email,gripemess[2])) == 0) {
CursesErrorMsg(Gtxt("Cannot send mail to null address...",228));
return;
}
#endif
#ifdef VMS
if ((strchr(email, '@') != NULL) && (strchr(email, '\"') == NULL))
sprintf(MailAddress, MAIL_ADRS, email);
else
strcpy(MailAddress, email);
tmpfilename = tempnam(NULL,NULL);
if ((f = fopen(tmpfilename, "w")) == NULL) {
CursesErrorMsg(Gtxt("Cannot send mail...",53));
return;
}
sprintf(mailcmd, "%s/subject=\"%.70s\" %s %s",
MAIL_COMMAND, gripemess[3], tmpfilename, MailAddress);
free(gripemess[3]);
#ifdef DESCRIBE_GOPHER_GRIPE
fprintf(f, Gtxt("Regarding the following Gopher item:\n",229));
describe_gopher(" ",
GDgetEntry(CurrentDir, GDgetCurrentItem(CurrentDir)-1),
f);
fprintf(f, "\n");
#endif
for (i=14; i>3 && strlen(gripemess[i])==0; i--) {
free(gripemess[i]);
gripemess[i] = NULL;
}
for (i=4; (i< 15) && gripemess[i]; i++) {
fprintf(f, "%s\n", gripemess[i]);
free(gripemess[i]);
}
fprintf(f, "\n[Sent from within the ");
fprintf(f, Gtxt("Internet Gopher Information Client v%s.%s.%d",102),
GOPHER_MAJOR_VERSION, GOPHER_MINOR_VERSION, PATCHLEVEL);
fprintf(f, "]\n");
fclose(f);
CURexit(CursesScreen);
printf(Gtxt("Mailing gripe to %s...",54), MailAddress);
FIOsystem(mailcmd);
unlink(tmpfilename);
free(tmpfilename);
CURenter(CursesScreen);
#else
mailargv[0] = MAIL_COMMAND;
mailargv[1] = email;
mailargv[2] = NULL;
fio = FIOopenProcess(mailargv[0], mailargv, "w");
if (fio == NULL) {
CursesErrorMsg(Gtxt("Cannot send mail...",53));
return;
}
*(gripemess[3] + 71) = '\0';
FIOwritestring(fio, "Subject: ");
FIOwritestring(fio, gripemess[3]);
FIOwritestring(fio, "\n\n");
free(gripemess[3]);
#ifdef DESCRIBE_GOPHER_GRIPE
FIOwritestring(fio, Gtxt("Regarding the following Gopher item:\n",229));
GStoLink(GDgetEntry(CurrentDir, GDgetCurrentItem(CurrentDir)-1),
FIOgetfd(fio), TRUE);
FIOwritestring(fio, "\n");
#endif
for (i=14; i>3 && strlen(gripemess[i])==0; i--) {
free(gripemess[i]);
gripemess[i] = NULL;
}
for (i=4; i< 15 && gripemess[i]; i++) {
FIOwritestring(fio, gripemess[i]);
FIOwritestring(fio, "\n");
free(gripemess[i]);
}
FIOwritestring(fio, "\n[Sent from within the ");
sprintf(version, Gtxt("Internet Gopher Information Client v%s.%s.%d",102),
GOPHER_MAJOR_VERSION, GOPHER_MINOR_VERSION, PATCHLEVEL);
FIOwritestring(fio, version);
FIOwritestring(fio, "]\n");
FIOclose(fio);
#endif
}
/*
** do_index gets keywords from the user to search for. It returns
** it to the calling process. This storage is volotile. Callers should
** make a copy if they want to call do_index multiple times.
*/
char* do_index(ZeGopher)
GopherObj *ZeGopher;
{
static char *inputline = NULL;
static char *prompt[2];
static char *response[2];
if (inputline == NULL) {
inputline = (char *) malloc(sizeof(char)*256);
if (inputline == NULL)
perror("Out of memory"), CleanupandExit(-1);
*inputline = '\0';
}
prompt[0] = Gtxt("Words to search for",55);
prompt[1] = NULL;
response[0] = inputline;
response[1] = NULL;
if (CURRequest(CursesScreen, GSgetTitle(ZeGopher),prompt, response) == -1 )
return(NULL);
if (*inputline == '\0')
return(NULL);
else
return(inputline);
}
/*
* this procedure just retrieves binary data from the socket and
* pumps it into a "play" process.
*/
#define BUFSIZE 1400 /* A pretty good value for ethernet */
#ifndef VMS
void
suck_sound(sockfd)
int sockfd;
{
FileIO *Play;
int j;
char buf[BUFSIZE];
char playCmd[BUFSIZE];
if (!RCdisplayCommand(GlobalRC, "Audio/basic", "", playCmd) ||
!strncasecmp(playCmd, "- none -", 8) ||
playCmd == NULL || playCmd[0] == '\0') {
/*** Hey! no play command, bummer ***/
/** We're forked at this point, and we can't display anything.. **/
/* CursesErrorMsg(Gtxt("Sorry, this machine doesn't support sounds",159));*/
return;
}
Play = FIOopenCmdline(playCmd, "w");
while(1) {
j = read(sockfd, buf, BUFSIZE);
if (j == 0)
break;
FIOwriten(Play, buf, j);
}
}
#endif
/*
* fork off a sound process to siphon the data across the net.
* So the user can listen to tunage while browsing the directories.
*/
void
do_sound(ZeGopher)
GopherStruct *ZeGopher;
{
#ifdef VMS
CursesErrorMsg(Gtxt("Sorry, this machine doesn't support sounds",159));
#else
int sockfd;
BOOLEAN Waitforchld = FALSE;
if ((sockfd = GSconnect(ZeGopher)) <0) {
check_sock(sockfd, GSgetHost(ZeGopher), GSgetPort(ZeGopher));
return;
}
/** Send out the request **/
GStransmit(ZeGopher, sockfd, NULL, NULL, NULL);
/** Okay, it's cool, we can fork off **/
if (SOUNDCHILD != 0)
Waitforchld = TRUE;
if ( (SOUNDCHILD = fork()) < 0)
;/* Fork Error */
else if (SOUNDCHILD == 0) { /* Child Process */
suck_sound(sockfd);
exit(0);
}
/* Parent Process */
closenet(sockfd);
return;
#endif /* not VMS */
}
/**
*** Show file takes a gopher text thing, writes it to a file
*** and passes it to your favorite pager.
**/
void
showfile(ZeGopher)
GopherObj *ZeGopher;
{
char *tmpfilename = NULL;
FILE *tmpfile;
char inputline[512];
char *view = NULL;
boolean WritePipe = FALSE,
ForkOff = FALSE;
boolean GS2FileSucceeded = TRUE;
int Child;
DebugGSplusPrint(ZeGopher, "showfile:start");
view = Choose_View(ZeGopher);
Debug("showfile:Choose_View returned view=%s\n",view);
if ((view == NULL) || (*view == '\0'))
return;
if (!RCdisplayCommand(GlobalRC, view, "", inputline) ||
!strncasecmp(inputline, "- none -", 8) ||
inputline == NULL || inputline[0] == '\0') {
CursesErrorMsg(Gtxt("No display command is mapped to this view!",113));
return;
}
Debug("showfile:inputline=%s\n",inputline);
if (GSgetLocalFile(ZeGopher) == NULL ||
GSgetLocalView(ZeGopher) == NULL ||
strcmp(GSgetLocalView(ZeGopher), view)!= 0) {
/*** We need to retrieve the file ***/
if (*inputline == '|') {
#ifdef VMS
CursesErrorMsg(Gtxt("Unix piping requested. Check your configuration!",176));
return;
#endif
if ((tmpfile = popen(inputline+1, "w")) == NULL)
fprintf(stderr, Gtxt("Couldn't execute %s\n",81)
,inputline), CleanupandExit(-1);
WritePipe = TRUE;
CURexit(CursesScreen);
} else {
/** Open a temporary file **/
#ifdef VMS
tmpfilename = tempnam(NULL,NULL);
#else
tmpfilename = tempnam("/tmp","gopher");
#endif
#if defined(VMS) && defined(VMSRecords)
if (GSisText(ZeGopher, view))
/*** Use VARIABLE length records for text on VMS ***/
tmpfile = fopen_VAR(tmpfilename, "w");
else
/*** Use FIXED 512 records for binaries on VMS ***/
tmpfile = fopen_FIX(tmpfilename, "w");
if (tmpfile == NULL) {
#else
if ((tmpfile = fopen(tmpfilename, "w")) == NULL) {
#endif
CURexit(CursesScreen);
fprintf(stderr, Gtxt("Couldn't make a tmp file!\n",83)),
CleanupandExit(-1);
}
if (GSgetLocalFile(ZeGopher) != NULL &&
GSgetLocalView(ZeGopher) != NULL &&
strcmp(GSgetLocalView(ZeGopher), view) != 0)
unlink(GSgetLocalFile(ZeGopher));
GSsetLocalFile(ZeGopher, tmpfilename);
} /* | */
if (inputline[strlen(inputline)-1] == '&') {
inputline[strlen(inputline)-1] = '\0';
ForkOff = TRUE;
}
#ifndef VMS
/* This is the child process, so exit.. */
if (ForkOff) {
if ((Child = fork()) < 0) {
;/** Fork Error ***/
CursesErrorMsg("Fork Error!");
}
else if (Child >0) {
/*** Parent Process ***/
;
CURenter(CursesScreen);
return;
}
} /* Forkoff */
#endif
Debug("showfile: view=%s ",view);
Debug("command=%s ",inputline);
Debug("file=%s",tmpfilename);
Debug("/%s\n",GSgetLocalFile(ZeGopher));
GS2FileSucceeded = GStoFile(ZeGopher, tmpfile, view, twirl);
if (!GS2FileSucceeded) {
GSsetLocalFile(ZeGopher, NULL);
GSsetLocalView(ZeGopher, NULL);
}
else
GSsetLocalView(ZeGopher, view);
if (WritePipe)
pclose(tmpfile); /* data went down pipe - displayed there */
else
fclose(tmpfile); /* data is in tmpfile, will display below */
} /* GopehrPluss || LocalFile = NULL */
logrequest("ARRIVED AT", ZeGopher);
if (!WritePipe && GS2FileSucceeded)
GSdisplay(ZeGopher);
if (!GS2FileSucceeded)
unlink(tmpfilename);
/* if (!ForkOff) {
printf("\nPress <RETURN> to continue: ");
fflush(stdin);
fflush(stdin);
getchar();
CURenter(CursesScreen);
}*/
if (tmpfilename!=NULL) free(tmpfilename);
if (ForkOff)
exit(-1);
CURenter(CursesScreen);
return;
}
/*
* Eventually this routine should allow viewing of the movie type by
* forking off a movie process to siphon the data across the net.
*/
void
do_movie(ZeGopher)
GopherStruct *ZeGopher;
{
showfile(ZeGopher);
}
/*
** Pushgopher takes a GopherThing pointer and adds it to it's stack.
**
** Ick this must be fixed!
*/
void
pushgopher(ZeDir)
GopherDirObj *ZeDir;
{
OldDirs[iLevel]= ZeDir;
iLevel ++;
}
/*
** If the stack is empty, popgopher returns a -1
*/
int
popgopher(ZeDir)
GopherDirObj **ZeDir;
{
if (iLevel == 0)
return(-1);
iLevel --;
*ZeDir = OldDirs[iLevel];
return(0);
}
#ifdef VMS
#include <perror.h>
#else
extern int h_errno;
extern int sys_nerr;
extern char *sys_errlist[];
extern int errno;
#endif
void check_sock(sockfd, host, port)
int sockfd;
char *host;
int port;
{
char DispString[WHOLELINE];
char DispString2[WHOLELINE];
char *DispStrings[4];
/* NULL DispStrings entries here, so can override below */
DispStrings[3] = NULL;
if (sockfd <0) {
sprintf(DispString,
Gtxt("Cannot connect to host %.40s, port %d.",72),
host, port);
switch (sockfd) {
case -2:
DispStrings[2] = Gtxt("Hostname is unknown.",100);
break;
case -3:
DispStrings[2] = Gtxt("Unable to allocate a socket.",175);
break;
case -4:
#if defined(VMS) && defined(MULTINET)
sprintf(DispString2, "%.78s.", vms_errno_string());
DispStrings[2] = DispString2;
#else
if (errno > 0 && errno <= sys_nerr) {
sprintf(DispString2, Gtxt("Connection failed: %s.",78),
#ifdef VMS
strerror(errno));
#else
sys_errlist[errno]);
#endif
DispStrings[2] = DispString2;
} else
DispStrings[2] =
Gtxt("Connection to remote host failed.",79);
#endif
break;
default:
DispStrings[2] = Gtxt("Unknown error.",177);
}
DispStrings[0] = DispString;
DispStrings[1] = "";
DispStrings[3] = NULL;
CURBeep(CursesScreen);
CURDialog(CursesScreen, Gtxt("Network Error",110), DispStrings);
}
}
BOOLEAN
ReallyQuit()
{
char yesno[3];
yesno[0] = 'y';
yesno[1] = '\0';
CURgetYesorNo(CursesScreen, Gtxt("Really quit (y/n) ?",124), yesno);
if (*yesno == 'y') {
return(TRUE);
}
return(FALSE);
}
void
CleanupandExit(exitval)
int exitval;
{
GopherDirObj *gd;
#ifdef VMS
extern boolean DidCleanup;
#endif
if (CursesScreen)
CURexit(CursesScreen);
if (ChangedDefs)
RCtoFile(GlobalRC);
do {
if (CurrentDir != NULL && CurrentDir != RCgetBookmarkDir(GlobalRC))
GDdestroy(CurrentDir);
}
while (popgopher(&CurrentDir) != -1);
if (GlobalRC) {
gd = RCgetBookmarkDir(GlobalRC);
if (gd != NULL)
GDdestroy(gd);
}
logrequest("EXIT", NULL);
#ifdef VMS
DidCleanup = TRUE;
#endif
exit(exitval);
}
/**************
** This bit of code catches control-c's, it cleans up the curses stuff.
*/
void
controlc(sig)
int sig;
{
#ifdef VMS
if (!CursesScreen->inCurses) {
/** Reprime the signal and set flag **/
if (signal(SIGINT, controlc) == SIG_ERR)
perror("signal died:\n"), CleanupandExit(-1);
HadVMSInt = TRUE;
return;
}
#endif
if (CurrentDir == NULL || GDgetNumitems(CurrentDir) <= 0) {
CURexit(CursesScreen);
fprintf(stderr,Gtxt("gopher: Nothing received for main menu, can't continue\n",60));
CleanupandExit(1);
}
if (ReallyQuit())
{
CleanupandExit(0);
}
else {
CURresize(CursesScreen);
/* scline(-1, 1, CurrentDir); */
/** Interrupt search, go back a level?? **/
}
/*
* Reprime the signals...
*/
if (signal(SIGINT, controlc) == SIG_ERR)
perror("signal died:\n"), CleanupandExit(-1);
/** Really should be siglongjmp **/
longjmp(Jmpenv,1);
}
/**************
** This bit of code catches window size change signals
**/
void
sizechange(sig)
int sig;
{
int lines, cols;
#ifdef TIOCGWINSZ
static struct winsize zewinsize; /* 4.3 BSD window sizing */
#endif
lines = LINES;
cols = COLS;
#ifdef TIOCGWINSZ
if (ioctl(0, TIOCGWINSZ, (char *) &zewinsize) == 0) {
lines = zewinsize.ws_row;
cols = zewinsize.ws_col;
} else {
#endif
/* code here to use sizes from termcap/terminfo, not yet... */
;
#ifdef TIOCGWINSZ
}
if (lines != LINES || cols != COLS) {
LINES = lines;
COLS = cols;
CURresize(CursesScreen);
scline(-1, 1, CurrentDir);
}
if (signal(SIGWINCH, sizechange)==SIG_ERR)
perror("signal died:\n"), CleanupandExit(-1);
#endif
}
/**********
**
** Set up all the global variables.
**
***********/
void
Initialize()
{
Debugmsg("Initialize\n")
GlobalRC = RCnew();
/** get defaults from the rc file **/
RCfromUser(GlobalRC);
/*** Set up the curses environment ***/
CursesScreen = CURnew();
if (strcmp(CURgetTerm(CursesScreen), "unknown")==0)
fprintf(stderr, Gtxt("I don't understand your terminal type\n",101)),
CleanupandExit(-1);
/*** Make a signal handler for window size changes ***/
#ifdef SIGWINCH
CURsetSIGWINCH(CursesScreen, sizechange);
if (signal(SIGWINCH, sizechange)==SIG_ERR)
perror("signal died:\n"),
CleanupandExit(-1);
#endif
if (signal(SIGINT, controlc) == SIG_ERR)
perror("signal died:\n"),
CleanupandExit(-1);
if (signal(SIGQUIT, CleanupandExit) == SIG_ERR)
perror("signal died:\n"),
CleanupandExit(-1);
if (signal(SIGPIPE, CleanupandExit) == SIG_ERR)
perror("signal died:\n"),
CleanupandExit(-1);
if (signal(SIGTERM, CleanupandExit) == SIG_ERR)
perror("signal died:\n"),
CleanupandExit(-1);
if (signal(SIGHUP, CleanupandExit) == SIG_ERR)
perror("signal died:\n"),
CleanupandExit(-1);
/*** Init MainWindow ****/
CURenter(CursesScreen);
/*** Check for new global configuration file ***/
if (RCisGlobalNew()) {
int result;
char *mess[2];
mess[0] = Gtxt("A new configuration is available. Load it?",218);
mess[1] = NULL;
/*** Ask if the user wants to reload values from globalrc ***/
result = CURDialog(CursesScreen, "", mess);
if (result == 0) {
/*** Do it... ****/
RCreadGlobalRC(GlobalRC);
}
ChangedDefs = TRUE;
}
}
/*
* This stuff will set the options in a nice way...
*/
void
SetOptions()
{
int choice, restricted = FALSE;
char *shell;
static char **OptionNewApp = NULL, **OptionsMenu = NULL;
static char **noyes = NULL;
static char **printchoice = NULL;
if (OptionNewApp == NULL) {
OptionNewApp = (char**) malloc(sizeof(char*) * 4);
OptionNewApp[0] = Gtxt("Content-Type Name",27);
OptionNewApp[1] = Gtxt("Display Application",28);
OptionNewApp[2] = Gtxt("Printing Application",29),
OptionNewApp[3] = NULL;
}
if (OptionsMenu == NULL) {
OptionsMenu = (char **) malloc(sizeof(char*) * 5);
OptionsMenu[0] = Gtxt("General Options",30);
OptionsMenu[1] = Gtxt("Configure Display Applications",31);
OptionsMenu[2] = Gtxt("Configure Printing Applications",32);
OptionsMenu[3] = Gtxt("Define New Content-Type",33);
OptionsMenu[4] = NULL;
}
if (noyes == NULL) {
noyes = (char**) malloc(sizeof(char*) * 3);
noyes[0] = Gtxt("No",111);
noyes[1] = Gtxt("Yes",180);
noyes[2] = NULL;
}
if (printchoice == NULL) {
printchoice = (char**) malloc(sizeof(char*) * 3);
printchoice[0] = Gtxt("Default System Printer", 219);
printchoice[1] = Gtxt("ANSI attached Printer", 220);
printchoice[2] = NULL;
}
/*
* If the user is running a restricted shell, don't allow him/her to
* set applications, because he/she can specify a shell. This is
* independent of -s and -S because rsh users may have a shell.
*/
#ifndef VMS
restricted = (shell = getenv("SHELL")) == NULL
|| strcmp(shell+strlen(shell) - 3, "rsh") == 0;
#endif
if (SecureMode || NoShellMode || restricted) {
CursesErrorMsg(Gtxt("Sorry, you are not allowed to set options in secure mode.",163));
return;
}
/** Display Configuration Menu and allow user to choose **/
choice = CURChoice(CursesScreen, Gtxt("Gopher Options",94), OptionsMenu,
Gtxt("Your Choice",181), -1);
if (choice == -1)
return;
switch (choice) {
case 0:
{
/** General Options **/
Requestitem *items[3];
Requestitem bold;
Requestitem printer;
bold.prompt = Gtxt("Bold Search words in Builtin Pager",66);
bold.thing = CUR_CHOICE;
bold.choices = noyes;
bold.chooseitem = RCsearchBolding(GlobalRC);
printer.prompt = Gtxt("Print to",221);
printer.thing = CUR_CHOICE;
printer.choices = printchoice;
printer.chooseitem = RCuseANSIprinter(GlobalRC);
items[0] = &bold;
items[1] = &printer;
items[2] = NULL;
if (CURrequester(CursesScreen, Gtxt("General Options",30), items)==0) {
RCsetSearchBolding(GlobalRC, items[0]->chooseitem);
RCsetANSIprinter(GlobalRC, items[1]->chooseitem);
ChangedDefs = TRUE;
}
break;
}
case 1:
case 2:
{
/** Display/Print Applications **/
char **gplusviews;
int i;
int numoptions;
RCMapObj *rcm;
char **Responses;
char *zetitle;
numoptions = RCMAgetNumEntries(GlobalRC->commands);
gplusviews = (char **) malloc(sizeof(char *) * (numoptions + 1));
Responses = (char **) malloc(sizeof(char *) * (numoptions + 1));
for (i = 0 ; i < numoptions; i++) {
rcm = RCMAgetEntry(GlobalRC->commands, i);
gplusviews[i] = RCMgetView(rcm);
Responses[i] = (char *) malloc(sizeof(char) * MAXSTR);
if (choice == 1)
strcpy(Responses[i], RCMgetDisplaycmd(rcm));
else
strcpy(Responses[i], RCMgetPrintcmd(rcm));
}
gplusviews[i] = NULL;
Responses[i] = NULL;
if (choice == 1)
zetitle = Gtxt("Configure Display Applications",31);
else
zetitle = Gtxt("Configure Printing Applications",32);
if (CURRequest(CursesScreen, zetitle, gplusviews,
Responses) == 0) {
char tmpstr[512];
while (i-- > 0) {
rcm = RCMAgetEntry(GlobalRC->commands, i);
if (choice == 1)
/** Display Applications **/
sprintf(tmpstr, "%s,%s,%s", gplusviews[i],
Responses[i], RCMgetPrintcmd(rcm));
else
/** Print Applications **/
sprintf(tmpstr, "%s,%s,%s", gplusviews[i],
RCMgetDisplaycmd(rcm), Responses[i]);
RCMAfromLine(GlobalRC->commands, tmpstr);
}
ChangedDefs = TRUE;
}
for (i = 0 ; i <= numoptions; i++)
if (Responses[i])
free(Responses[i]);
free(Responses);
free(gplusviews);
break;
}
case 3:
{
/** Add a new Content-Type and define **/
/** its Display and Printing applications **/
int i;
char *Responses[4];
for (i = 0; i < 3; i++) {
Responses[i] = (char *) malloc(sizeof(char) * MAXSTR);
Responses[i][0] = '\0';
}
Responses[3] = NULL;
if ((CURRequest(CursesScreen, Gtxt("Define New Content-Type",33),
OptionNewApp, Responses) == 0) && (*Responses[0] != '\0')) {
char tmpstr[512];
sprintf(tmpstr, "%s,%s,%s", Responses[0], Responses[1],
Responses[2]);
RCMAfromLine(GlobalRC->commands, tmpstr);
ChangedDefs = TRUE;
}
for (i = 0; i < 3; i++)
free(Responses[i]);
break;
}
} /** End of switch on option type **/
}
static char *search_string = NULL;
int
main(argc, argv)
int argc;
char *argv[];
{
BOOLEAN bDone = FALSE;
char sTmp[80];
GopherStruct *RootGophers[2];
int numhosts = 2;
int TypedChar;
/*** for getopt processing ***/
int c;
extern char *optarg;
extern int optind;
int errflag =0, i;
int curitem;
int Garbled = TRUE;
boolean Bkmarksfirst = FALSE;
boolean startAtHome = TRUE;
/*** Initialize international languages ***/
Gtxtlocale(LC_ALL, "");
#if defined(GINTERNATIONAL) && !defined(VMS)
#ifdef LC_CTYPE
Gtxtlocale(LC_CTYPE, "iso_8859_1");
#endif
#ifdef LC_MESSAGES
if (getenv("LC_MESSAGES") != NULL)
Gtxtlocale(LC_MESSAGES, getenv("LC_MESSAGES"));
#endif /* LC_MESSAGES */
#ifndef __osf__
Gtxtopen("gopher", 0); /** OSF seems to mess this up... **/
#endif
if (Gcatd == (nl_catd) -1) {
/** Try finding it elsewhere.. **/
char fname[256];
if (getenv("LC_MESSAGES") != NULL) {
sprintf(fname, "%s/gophernls/%s.cat", GOPHERLIB, getenv("LC_MESSAGES"));
Gtxtopen(fname, 0);
}
}
#endif /* GINTERNATIONAL & !VMS */
#ifdef VMS
argv[0] = "gopher";
#endif
for (i=0; i<2; i++) {
RootGophers[i] = GSnew();
GSsetType (RootGophers[i], A_DIRECTORY);
GSsetPath (RootGophers[i],"");
}
/** Should generalize this to >2 hosts .... Sigh... ***/
GSsetHost (RootGophers[0], CLIENT1_HOST);
GSsetPort (RootGophers[0], CLIENT1_PORT);
GSsetHost (RootGophers[1], CLIENT2_HOST);
GSsetPort (RootGophers[1], CLIENT2_PORT);
if (CLIENT2_PORT == 0)
numhosts = 1;
sTmp[0] = '\0';
while ((c = getopt(argc, argv, "DsSbrp:t:T:i:")) != -1)
switch (c) {
case 's':
SecureMode = TRUE;
break;
case 'S': /* Similar to secure, but assumes own Unix account */
/* No change in behaviour if this not set */
NoShellMode = TRUE;
break;
case 'i':
search_string = optarg;
break;
case 'p':
GSsetPath(RootGophers[0], optarg);
GSsetPath(RootGophers[1], optarg);
startAtHome = FALSE;
break;
case 'T':
GSsetType(RootGophers[0], *optarg);
GSsetType(RootGophers[1], *optarg);
if (optarg[1] == '+') {
GSsetGplus(RootGophers[0], TRUE);
GSsetGplus(RootGophers[1], TRUE);
} else if (optarg[1] == '?') {
GSsetAsk(RootGophers[0], TRUE);
GSsetAsk(RootGophers[1], TRUE);
}
startAtHome = FALSE;
break;
case 't':
GSsetTitle(RootGophers[0], optarg);
GSsetTitle(RootGophers[1], optarg);
break;
case 'D':
DEBUG = TRUE;
Debugmsg("gopher starting - debug on\n")
break;
case 'r':
RemoteUser = TRUE;
break;
case 'b':
Bkmarksfirst = TRUE;
break;
case '?':
errflag++;
}
if (errflag) {
fprintf(stderr, Gtxt("Usage: %s [-sSbDr] [-T type] [-p path] [-t title] [hostname port]+\n",9), argv[0]);
fprintf(stderr,
Gtxt(" -s secure mode, users without own account\n",10));
fprintf(stderr,
Gtxt(" -S secure mode, users with own account\n",11));
fprintf(stderr,
Gtxt(" -p path specify path to initial item\n",12));
fprintf(stderr,
Gtxt(" -T type Type of initial item\n",13));
fprintf(stderr,
Gtxt(" -i Search argument (for -T 7)\n",14));
fprintf(stderr,
Gtxt(" -b Bookmarks first\n",15));
fprintf(stderr,
Gtxt(" -r Remote user\n",16));
fprintf(stderr,
Gtxt(" -D Debug mode\n",17));
CleanupandExit(-1);
}
/**** Get host #1 from the command line ****/
if (optind < argc) {
if (strchr(argv[optind], ':') != NULL) {
GopherObj *tempGopher = GSnew();
int doneflags = 0;
doneflags = GSfromURL(tempGopher, argv[optind],
AFTP_HOST, AFTP_PORT, doneflags);
GScpy(RootGophers[0], tempGopher);
GScpy(RootGophers[1], tempGopher);
GSdestroy(tempGopher);
startAtHome = FALSE;
optind++;
} else {
GSsetPort(RootGophers[0], 70); /* Just in case the default */
GSsetPort(RootGophers[1], 70); /* isn't 70 */
GSsetHost(RootGophers[0], argv[optind]);
GSsetHost(RootGophers[1], ""); /** Nuke the 2nd alternative host **/
GSsetPort(RootGophers[1], 0);
numhosts = 1;
startAtHome = FALSE;
optind++;
}
}
if (optind < argc) {
GSsetPort(RootGophers[0], atoi(argv[optind]));
optind++;
}
/*** Get host #2 from the command line... ***/
if (optind < argc) {
GSsetHost(RootGophers[1], argv[optind]);
numhosts = 2;
optind++;
}
if (optind < argc) {
GSsetPort(RootGophers[1], atoi(argv[optind]));
optind++;
}
/*** If the title hasn't been set, then add a default title **/
if (GSgetTitle(RootGophers[0]) == NULL) {
sprintf(sTmp, Gtxt("Home Gopher server: %.59s",97),
GSgetHost(RootGophers[0]));
GSsetTitle(RootGophers[0], sTmp);
sprintf(sTmp, Gtxt("Home Gopher server: %.59s",97),
GSgetHost(RootGophers[1]));
GSsetTitle(RootGophers[1], sTmp);
}
/*** Set up global variables, etc. ***/
#ifdef SOCKS
SOCKSinit(argv[0]);
#endif
Initialize();
if (startAtHome && (RCgetHome(GlobalRC) != NULL)) {
GScpy(RootGophers[0], RCgetHome(GlobalRC));
GScpy(RootGophers[1], RCgetHome(GlobalRC));
}
if (Bkmarksfirst) {
CurrentDir = RCgetBookmarkDir(GlobalRC);
} else {
int rnum;
srand(time(NULL));
rnum = rand() % numhosts;
process_request(RootGophers[rnum]);
/* just process the command line entry */
if( GSgetType(RootGophers[0]) != A_DIRECTORY &&
GSgetType(RootGophers[0]) != A_INDEX) {
refresh();
if( GSgetLocalFile(RootGophers[0]) != NULL)
unlink(GSgetLocalFile(RootGophers[0]));
CleanupandExit(0);
}
}
#ifdef DEBUGGING
if (CurrentDir == NULL) {
Debugmsg("CurrentDir=NULL\n");
}
else
if (GDgetNumitems(CurrentDir) <=0) {
Debugmsg("GDgetNumitems<=0\n");
}
#endif
if (CurrentDir == NULL || GDgetNumitems(CurrentDir) <= 0) {
/*
* We didn't get anything from that gopher server. Either
* it is down, doesn't exist, or is empty or otherwise
* busted.
*
* Try any alternative hosts that are available..
*/
while (optind < argc && CurrentDir == NULL) {
Debugmsg("Trying alternate site\n");
GSsetHost(RootGophers[0], argv[optind]);
optind++;
if (optind < argc) {
GSsetPort(RootGophers[0], atoi(argv[optind]));
optind++;
}
process_request(RootGophers[0]);
}
}
if (CurrentDir == NULL || GDgetNumitems(CurrentDir) <= 0) {
#ifdef DEBUGGING
if (CurrentDir == NULL) {
Debugmsg("B: CurrentDir=NULL\n");
}
else if (GDgetNumitems(CurrentDir) <=0) {
Debugmsg("B: GDgetNumitems<=0\n");
}
#endif
CURexit(CursesScreen);
fprintf(stderr,
Gtxt("%s: Nothing received for main menu, can't continue\n",188)
, argv[0]);
CleanupandExit(1);
}
while (bDone == FALSE)
{
/* Set up a longjmp for control-cs **/
if (setjmp(Jmpenv) != 0) {
/* This part only runs when returning from a longjmp */
Garbled = TRUE;
}
GetMenu(CurrentDir, &TypedChar, Garbled);
Garbled = TRUE;
#ifdef DESCRIBE_GOPHER
#undef DESCRIBE_GOPHER
#endif
#ifdef DESCRIBE_GOPHER_GRIPE
#define DESCRIBE_GOPHER(a,b) describe_gopher((a),(b), NULL)
#else
#define DESCRIBE_GOPHER(a,b) describe_gopher((a),(b))
#endif
switch(TypedChar)
{
case '\r':
case '\n':
/*** Select the designated item ***/
if ((curitem = GDgetCurrentItem(CurrentDir)) <= 0)
curitem = 1;
if (process_request(GDgetEntry(CurrentDir, curitem-1))==1)
; /** Don't do anything if we failed, I guess **/
#if defined(VMS) && defined(A_LANGUAGE)
if (CurrentDir == setlocale_LangDir) {
sprintf(sTmp, Gtxt("Home Gopher server: %.59s",97),
GSgetHost(RootGophers[0]));
GDsetTitle(OldDirs[0], sTmp);
GSsetTitle(RootGophers[0], sTmp);
Garbled = TRUE;
goto Backup_Resetlocale;
}
#endif
break;
case '\0':
/*** What the heck? ***/
CursesErrorMsg("Strange Error occurred!");
break;
case 'u':
case 'U':
#ifdef AUTOEXITONU
case 'q':
#endif
#if defined(VMS) && defined(A_LANGUAGE)
Backup_Resetlocale:
#endif
{
GopherDirObj *tempGdir;
logrequest("PREVIOUS DIRECTORY", NULL);
/*** Don't highlight texts which aren't search hits ***/
Searchstring = NULL;
/*** Go up a directory level ***/
tempGdir = CurrentDir;
/** Don't destroy root level directory, or bookmarks **/
if (popgopher(&CurrentDir)==0 && tempGdir != CurrentDir) {
if (tempGdir != RCgetBookmarkDir(GlobalRC))
#if defined(VMS) && defined(A_LANGUAGE)
if (tempGdir != setlocale_LangDir)
#endif
GDdestroy(tempGdir);
#ifdef AUTOEXITONU
} else {
bDone = TRUE;
CURexit(CursesScreen);
#else
} else {
Garbled = FALSE;
#endif
}
}
break;
case 'g':
/*** Gripe! ***/
#ifdef NOGRIPE_SECURE
if (SecureMode || NoShellMode) {
CursesErrorMsg(Gtxt
("Sorry, can't submit gripes in securemode",
227));
break;
}
#endif
Gripe(GDgetEntry(CurrentDir,GDgetCurrentItem(CurrentDir)-1));
break;
case 's': /*** Save a file directly ***/
if (SecureMode || NoShellMode) {
CursesErrorMsg(Gtxt("Sorry, can't save files with this account",152));
break;
}
Save_file(GDgetEntry(CurrentDir,GDgetCurrentItem(CurrentDir)-1), NULL, NULL);
break;
case 'S': /*** Save list of menu items (objects) to a file ***/
if (SecureMode || NoShellMode) {
CursesErrorMsg(Gtxt("Sorry, can't save files with this account",152));
break;
}
Save_list(CurrentDir);
break;
case 'D':
Download_file(GDgetEntry(CurrentDir,GDgetCurrentItem(CurrentDir)-1));
break;
case 'o': /** Open a new session **/
{
GopherObj *tempgs = GSnew();
char *prompts[4];
char *responses[4];
int i;
if (SecureMode) {
CursesErrorMsg(Gtxt("Sorry, you're not allowed to do this",164));
break;
}
prompts[0]=Gtxt("Hostname",99);
prompts[1]=Gtxt("Port",118);
prompts[2]=Gtxt("Selector (Optional)",144);
prompts[3]=NULL;
for (i=0; i<3; i++) {
responses[i] = (char*)malloc(COLS*sizeof(char));
*responses[i] = '\0';
}
strcpy(responses[1], "70");
responses[3] = NULL;
if ((CURRequest(CursesScreen,
Gtxt("Connect to a new Gopher Server",75),
prompts, responses) != -1)
&& *responses[0]!='\0') {
GSsetType(tempgs, A_DIRECTORY);
GSsetTitle(tempgs, responses[0]);
GSsetHost(tempgs, responses[0]);
GSsetPort(tempgs, atoi(responses[1]));
GSsetPath(tempgs, responses[2]);
Load_Dir(tempgs);
}
GSdestroy(tempgs);
for (i=0; i<3; i++)
free(responses[i]);
break;
}
case 'w': /** Open a new session from a URL **/
{
GopherObj *tempgs = GSnew();
char *prompt[2];
char *response[2];
int doneflags = 0;
if (SecureMode) {
CursesErrorMsg("Sorry, you're not allowed to do this");
break;
}
prompt[0]=
"http, gopher, ftp, telnet or tn3270 URL to connect to:";
prompt[1]=NULL;
response[0] = (char*)malloc(COLS*sizeof(char));
*response[0] = '\0';
response[1] = NULL;
if ((CURRequest(CursesScreen, "Connect to a new server",
prompt, response) != -1) && *response[0]!='\0') {
doneflags = GSfromURL(tempgs, response[0],
AFTP_HOST, AFTP_PORT, doneflags);
if (doneflags & G_HOST) {
if (!(doneflags & G_PATH))
GSsetPath(tempgs, "");
if (!(doneflags & G_TYPE))
GSsetType(tempgs, A_DIRECTORY);
if (!(doneflags & G_NAME))
GSsetTitle(tempgs, response[0]);
process_request(tempgs);
}
}
GSdestroy(tempgs);
free(response[0]);
break;
}
case 'f': /** Open an aFTP session **/
{
GopherObj *tempgs = GSnew();
char *prompts[3];
char *responses[3];
char FTPpath[256];
int i;
if (SecureMode) {
CursesErrorMsg(Gtxt("Sorry, you're not allowed to do this",164));
break;
}
prompts[0]=Gtxt("anonymous FTP Host",187);
prompts[1]=Gtxt("Selector (Optional)",144);
prompts[2]=NULL;
for (i=0; i<2; i++) {
responses[i] = (char *) malloc(sizeof(char)*COLS);
*responses[i] = '\0';
}
responses[2] = NULL;
if ((CURRequest(CursesScreen,
Gtxt("Connect to an anonymous FTP Server via the Gopher gateway",76),
prompts, responses) == -1) || *responses[0] == '\0')
break;
GSsetType(tempgs, A_DIRECTORY);
GSsetTitle(tempgs, responses[0]);
GSsetHost(tempgs, AFTP_HOST);
GSsetPort(tempgs, AFTP_PORT);
if (responses[1][strlen(responses[1])-1] != '/')
strcat(responses[1], "/");
sprintf(FTPpath, "ftp:%s@%s%s", responses[0],
(*responses[1] == '/') ? "" : "/", responses[1]);
GSsetPath(tempgs, FTPpath);
Load_Dir(tempgs);
GSdestroy(tempgs);
for (i=0; i<2; i++)
free(responses[i]);
break;
}
case 'r': /** Go to root menu of current item **/
{
char GSType;
GopherObj *tempgs;
if (SecureMode) {
CursesErrorMsg(Gtxt("Sorry, you're not allowed to do this",164));
break;
}
if ((curitem = GDgetCurrentItem(CurrentDir)) <= 0)
curitem = 1;
GSType = GSgetType(GDgetEntry(CurrentDir, curitem-1));
tempgs = GSnew();
/** These Gopher objects don't necessarily point
to machines running Gopher servers **/
if (GSType == A_CSO || GSType == A_TELNET ||
GSType == A_TN3270 || GSType == A_ERROR ||
GSType == A_INFO) {
/** Try the parent instead **/
if(GDgetLocation(CurrentDir) != NULL)
GScpy(tempgs, GDgetLocation(CurrentDir));
else
GScpy(tempgs, GDgetEntry(OldDirs[iLevel-1],
GDgetCurrentItem(OldDirs[iLevel-1])-1));
}
else
if ((curitem = GDgetCurrentItem(OldDirs[iLevel-1])) <= 0)
curitem = 1;
GScpy(tempgs, GDgetEntry(CurrentDir,
GDgetCurrentItem(CurrentDir)-1));
GSsetType(tempgs, A_DIRECTORY);
GSsetTitle(tempgs, Gtxt("Root menu: ",135));
STRcat(tempgs->Title, GSgetHost(tempgs));
GSsetPath(tempgs, NULL);
Load_Dir(tempgs);
GSdestroy(tempgs);
break;
}
case 'R': /** Go to root menu of current menu **/
{
GopherObj *tempgs;
if (SecureMode) {
CursesErrorMsg(Gtxt("Sorry, you're not allowed to do this",164));
break;
}
if (iLevel == 0) {
Garbled = FALSE;
break;
}
tempgs = GSnew();
if (GDgetLocation(CurrentDir) != NULL)
GScpy(tempgs, GDgetLocation(CurrentDir));
else
GScpy(tempgs, GDgetEntry(OldDirs[iLevel-1],
GDgetCurrentItem(OldDirs[iLevel-1])-1));
GSsetType(tempgs, A_DIRECTORY);
GSsetTitle(tempgs, Gtxt("Root menu: ",135));
STRcat(tempgs->Title, GSgetHost(tempgs));
GSsetPath(tempgs, NULL);
Load_Dir(tempgs);
GSdestroy(tempgs);
break;
}
case 'v': /** View bookmark list **/
{
logrequest("VIEW BOOKMARKS", NULL);
if (RCgetBookmarkDir(GlobalRC) == NULL) {
CursesErrorMsg(Gtxt("No bookmarks are defined",112));
break;
}
/** Don't do anything if we are already viewing bookmarks **/
if (CurrentDir == RCgetBookmarkDir(GlobalRC)) {
Garbled = FALSE;
break;
}
/** Don't push an empty gopher directory... **/
if (CurrentDir != NULL)
pushgopher(CurrentDir);
CurrentDir = RCgetBookmarkDir(GlobalRC);
break;
}
case 'a': /** add current item as a bookmark **/
{
GopherObj *tmpgs;
char newtitle[256];
if (RCgetBookmarkDir(GlobalRC) == NULL) {
RCsetBookmarkDir(GlobalRC,GDnew(32));
GDsetTitle(RCgetBookmarkDir(GlobalRC), Gtxt("Bookmarks",67));
}
tmpgs = GSnew();
GScpy(tmpgs, GDgetEntry(CurrentDir, GDgetCurrentItem(CurrentDir)-1));
strcpy(newtitle, GSgetTitle(tmpgs));
if (CURGetOneOption(CursesScreen, newtitle,
Gtxt("Name for this bookmark:",109), newtitle)==0) {
if (*newtitle != '\0') {
GSsetTitle(tmpgs, newtitle);
GDaddGS(RCgetBookmarkDir(GlobalRC), tmpgs);
ChangedDefs = TRUE;
}
}
logrequest("ADD BOOKMARK", tmpgs);
/* Clear the local file name so the file isn't deleted */
GSsetLocalFile(tmpgs, NULL);
GSdestroy(tmpgs);
break;
}
case 'A': /*** Add current directory/search as a bookmark **/
{
GopherObj *tmpgs;
char newtitle[256];
char *tmps;
if (RCgetBookmarkDir(GlobalRC) == NULL) {
RCsetBookmarkDir(GlobalRC,GDnew(32));
GDsetTitle(RCgetBookmarkDir(GlobalRC), Gtxt("Bookmarks",67));
}
if (CurrentDir == RCgetBookmarkDir(GlobalRC)) {
CursesErrorMsg(Gtxt("Sorry, can't make a bookmark of bookmarks",151));
break;
}
tmpgs = GSnew();
if (GDgetLocation(CurrentDir) != NULL)
GScpy(tmpgs, GDgetLocation(CurrentDir));
else if (iLevel == 0)
GScpy(tmpgs, RootGophers[0]);
else
GScpy(tmpgs, GDgetEntry(OldDirs[iLevel-1],
GDgetCurrentItem(OldDirs[iLevel-1])-1));
if ((GSgetType(tmpgs) == '7') &&
((tmps = GSgetPath(tmpgs)) == NULL ||
*tmps == '\0' ||
Searchstring == NULL ||
*Searchstring== '\0')) {
CursesErrorMsg("Sorry, can't make a bookmark of this search");
GSdestroy(tmpgs);
break;
}
strcpy(newtitle, GDgetTitle(CurrentDir));
if (CURGetOneOption(CursesScreen, newtitle,
Gtxt("Name for this bookmark:",109), newtitle)==0) {
if (*newtitle != '\0') {
GSsetTitle(tmpgs, newtitle);
/*** Freeze the search, if there was one. ***/
if (GSgetType(tmpgs) == '7') {
char ickypath[512];
strcpy(ickypath, GSgetPath(tmpgs));
strcat(ickypath, "\t");
strcat(ickypath, Searchstring);
GSsetPath(tmpgs, ickypath);
GSsetType(tmpgs, '1');
}
GDaddGS(RCgetBookmarkDir(GlobalRC), tmpgs);
ChangedDefs = TRUE;
}
}
logrequest("ADD BOOKMARK", tmpgs);
GSdestroy(tmpgs);
break;
}
case 'd': /*** Delete a bookmark ***/
{
GopherDirObj *tempgd;
#ifdef DELETE_BOOKMARKS_ONLY
if (CurrentDir != RCgetBookmarkDir(GlobalRC)) {
CursesErrorMsg(Gtxt("The 'd'elete command is only for bookmarks.",189));
break;
}
#endif
logrequest("DELETE BOOKMARK", GDgetEntry(CurrentDir, GDgetCurrentItem(CurrentDir)-1));
if (GDgetNumitems(CurrentDir) == 1)
tempgd = NULL;
else
tempgd=GDdeleteGS(CurrentDir,(GDgetCurrentItem(CurrentDir)-1));
if (CurrentDir == RCgetBookmarkDir(GlobalRC))
RCsetBookmarkDir(GlobalRC, tempgd);
if (tempgd == NULL) {
Debugmsg("Last item - pip up a directory")
tempgd = CurrentDir;
if (popgopher(&CurrentDir)==0 && tempgd != CurrentDir)
GDdestroy(tempgd);
else {
CursesErrorMsg(Gtxt("Sorry, can't delete top level directory.",149));
scline(-1, 1, CurrentDir);
}
ChangedDefs = TRUE;
} else {
CurrentDir = tempgd;
ChangedDefs = TRUE;
}
break;
}
#if defined(VMS) && defined(A_LANGUAGE)
case 'L': /** add language change **/
if (CurrentDir == setlocale_LangDir)
break;
if (setlocale_LangDir== NULL) {
CursesErrorMsg(Gtxt("Sorry, no language choice available",
221));
}
else
{
/** Don't push an empty gopher directory... **/
if (CurrentDir != NULL)
pushgopher(CurrentDir);
CurrentDir = setlocale_LangDir;
}
break;
#endif
case '$':
case '!':
#ifdef NEVERSPAWN
CursesErrorMsg(Gtxt("Sorry, can't spawn in with this account",153));
break;
#else
if (SecureMode || NoShellMode) {
CursesErrorMsg(Gtxt("Sorry, can't spawn in with this account",153));
break;
}
/*** Spawn to shell or DCL ***/
CURexit(CursesScreen);
#ifdef VMS
printf(Gtxt("Spawning DCL subprocess. Logout to return to Gopher.\n",169));
fflush(stdout);
i = FIOsystem("");
#else
printf(Gtxt("Spawning your default shell. Type 'exit' to return to Gopher.\n\n",170));
fflush(stdout);
i = FIOsystem(getenv("SHELL"));
#endif
CURenter(CursesScreen);
if (i < 0)
#ifdef VMS
CursesErrorMsg(Gtxt("Spawn to DCL failed!",167));
#else
CursesErrorMsg(Gtxt("Spawn to default shell failed!",168));
#endif
#endif /* NEVERSPAWN */
break;
/*** Pop back to the main menu ***/
case 'M':
case 'm':
{
GopherDirObj *tempGdir;
tempGdir = CurrentDir;
while (popgopher(&CurrentDir) != -1) {
if (tempGdir != NULL && tempGdir != RCgetBookmarkDir(GlobalRC))
GDdestroy(tempGdir);
tempGdir = CurrentDir;
}
}
break;
#ifndef AUTOEXITONU
case 'q':
/*** Quit the program ***/
if (TRUE == ReallyQuit()) {
bDone = TRUE;
CURexit(CursesScreen);
break;
}
break;
#endif /*AUTOEXITONU*/
#ifdef VMS
case '\032': /* ^Z */
#endif
case 'Q':
/*** Quit the program, don't ask ***/
bDone = TRUE;
CURexit(CursesScreen);
break;
case 'O':
/*** Change various program things ***/
#ifdef NEVERSETOPTIONS
CursesErrorMsg(Gtxt("Sorry, you're not allowed to do this",164));
#else
SetOptions();
#endif
break;
case '=':
DESCRIBE_GOPHER(Gtxt("Gopher Item Information",93),
GDgetEntry(CurrentDir, GDgetCurrentItem(CurrentDir)-1));
break;
case '^':
{
if (GDgetLocation(CurrentDir) != NULL)
DESCRIBE_GOPHER(Gtxt("Gopher Directory Information",89),
GDgetLocation(CurrentDir));
else if (iLevel == 0)
DESCRIBE_GOPHER(Gtxt("Gopher Directory Information",89),
RootGophers[0]);
else
DESCRIBE_GOPHER(Gtxt("Gopher Directory Information",89),
GDgetEntry(OldDirs[iLevel-1],
GDgetCurrentItem(OldDirs[iLevel-1])-1));
break;
}
case '?':
case KEY_HELP:
{
/*** Display help file ***/
GopherObj *tmpgs;
char *tmpurl = (char *)malloc(17+255);
tmpgs = GSnew();
GSsetType(tmpgs, A_FILE);
GSsetTitle(tmpgs, Gtxt("Gopher Help File",92));
#ifdef VMS
GSsetLocalFile(tmpgs, Gtxt(GOPHERHELP,223));
#else
GSsetLocalFile(tmpgs, Gtxt(GOPHERHELP,225));
#endif
#ifdef GOPHERHELP_SECURE
if (SecureMode)
#ifdef VMS
GSsetLocalFile(tmpgs, Gtxt(GOPHERHELP_SECURE,224));
#else
GSsetLocalFile(tmpgs, Gtxt(GOPHERHELP_SECURE,226));
#endif
#endif
GSsetLocalView(tmpgs, "text/plain");
if (tmpurl != NULL) {
/* set a URL for logrequest() in showfile() */
strcpy(tmpurl, "file://localhost");
#ifdef VMS
strcat(tmpurl, Gtxt(GOPHERHELP,223));
#else
strcat(tmpurl, Gtxt(GOPHERHELP,225));
#endif
#ifdef GOPHERHELP_SECURE
if (SecureMode)
#ifdef VMS
strcat(tmpurl, Gtxt(GOPHERHELP_SECURE,224));
#else
strcat(tmpurl, Gtxt(GOPHERHELP_SECURE,226));
#endif
#endif
GSsetURL(tmpgs, tmpurl);
free(tmpurl);
}
showfile(tmpgs);
GSsetLocalFile(tmpgs, NULL);
GSdestroy(tmpgs);
break;
}
case 20: /* Control-T */
{
/*** Show host's local date and time ***/
GopherObj *tempgs = GSnew();
char *prompts[2];
char *responses[2];
prompts[0]=Gtxt("Host to query:",98);
prompts[1]=NULL;
/*** Offer current gopher host as default ***/
responses[0] = (char *) malloc(sizeof(char)*COLS);
if (GDgetLocation(CurrentDir) != NULL)
strcpy(responses[0], GSgetHost(GDgetLocation(CurrentDir)));
else if (iLevel == 0)
strcpy(responses[0], GSgetHost(RootGophers[0]));
else
strcpy(responses[0],
GSgetHost(GDgetEntry(OldDirs[iLevel-1],
GDgetCurrentItem(OldDirs[iLevel-1])-1)));
responses[1] = NULL;
if ((CURRequest(CursesScreen,
Gtxt("Show host's local date and time",145),
prompts, responses) == -1) || *responses[0] == '\0')
break;
GSsetType(tempgs, A_FILE);
GSsetTitle(tempgs, Gtxt("Local Date and Time",104));
GSsetHost(tempgs, responses[0]);
GSsetPort(tempgs, 13);
GSsetPath(tempgs, NULL);
showfile(tempgs);
GSdestroy(tempgs);
free(responses[0]);
break;
}
default:
Debug("main beep %d\r\n",TypedChar)
CURBeep(CursesScreen);
Garbled = FALSE;
break;
} /* switch */
#ifdef DESCRIBE_GOPHER
#undef DESCRIBE_GOPHER
#endif
} /* while */
CleanupandExit(0);
exit(0); /* Never get here... */
}
#if defined(VMS) && defined(A_LANGUAGE)
/*
* This procedure displays the available language menu and reports the
* user's language selection
*/
int
setlocale_screen()
{
int status;
if (!LangScreen)
LangScreen = CURnew();
CURenter(LangScreen);
GetMenu(setlocale_LangDir, &status, 1);
status = GDgetCurrentItem(setlocale_LangDir)-1;
CURexit(LangScreen);
return(status);
}
#endif
int
Load_Index(ZeGopher)
GopherObj *ZeGopher;
{
Draw_Status(Gtxt("Searching...",143));
refresh();
return(Load_Index_or_Dir(ZeGopher, Searchstring));
}
int
Load_Dir(ZeGopher)
GopherObj *ZeGopher;
{
Searchstring= NULL;
return(Load_Index_or_Dir(ZeGopher, NULL));
}
int
Load_Index_or_Dir(ZeGopher, Searchmungestr)
GopherObj *ZeGopher;
char *Searchmungestr;
{
int failed = 0;
int sockfd;
int i, numbytes;
static char DirTitle[512];
GopherDirObj *NewDir = NULL;
DebugGSplusPrint(ZeGopher,"Load_Index_or_Dir");
NewDir = GDnew(32);
Draw_Status(Gtxt("Connecting...",77)); refresh();
if ((sockfd = GSconnect(ZeGopher)) <0) {
check_sock(sockfd, GSgetHost(ZeGopher), GSgetPort(ZeGopher));
failed = 1;
}
else {
if (GSgetType(ZeGopher) == A_DIRECTORY) {
Draw_Status(Gtxt("Retrieving Directory...",133)); refresh();
GStransmit(ZeGopher, sockfd, NULL, "$", NULL);
}
else if (GSgetType(ZeGopher) == A_INDEX) {
Draw_Status(Gtxt("Searching...",143)); refresh();
GStransmit(ZeGopher, sockfd, Searchmungestr, "$", NULL);
}
numbytes = GSrecvHeader(ZeGopher, sockfd);
if (numbytes == 0) {
Gplus_Error(sockfd);
failed = 1;
return(failed);
}
if (GSisGplus(ZeGopher))
GDplusfromNet(NewDir, sockfd, twirl);
else
GDfromNet(NewDir, sockfd, twirl);
logrequest("ARRIVED AT", ZeGopher);
if (GDgetNumitems(NewDir) <= 0) {
CursesErrorMsg(Gtxt("Nothing available.", 214));
failed = 1;
GDdestroy(NewDir);
closenet(sockfd);
return(failed);
}
if (GSgetType(ZeGopher) == A_INDEX) {
sprintf(DirTitle, "%s: %s", GSgetTitle(ZeGopher), Searchmungestr);
GDsetTitle(NewDir, DirTitle);
}
else
GDsetTitle(NewDir, GSgetTitle(ZeGopher));
GDsetLocation(NewDir, ZeGopher);
/** Don't push an empty gopher directory... **/
if (CurrentDir != NULL)
pushgopher(CurrentDir);
CurrentDir = NewDir;
}
i = closenet(sockfd);
return(failed);
}
/*
* This dispatches the appropriate fcns depending on what the object is..
*/
int
process_request(ZeGopher)
GopherObj *ZeGopher;
{
int failed=0;
char ImageCmd[128];
char MovieCmd[128];
if (GSisAsk(ZeGopher)) {
char **askdata = AskBlock(ZeGopher);
if (askdata == NULL)
return(1);
GSsetAskdata(ZeGopher, askdata);
}
/** Decide what to do with it.. **/
Debug("process_request type=%c\n",GSgetType(ZeGopher));
logrequest("GOING TO", ZeGopher);
switch(GSgetType(ZeGopher)) {
case -1:
break;
#if defined(VMS) && defined(A_LANGUAGE)
case A_LANGUAGE:
rsetlocale(GSgetPort(ZeGopher));
break;
#endif
case A_INFO:
/** Do nothing **/
break;
case A_FILE:
Draw_Status(Gtxt("Receiving Information...",128));
refresh();
showfile(ZeGopher);
break;
case A_GIF:
case A_IMAGE:
if (!RCdisplayCommand(GlobalRC, "Image", "", ImageCmd) ||
!strncasecmp(ImageCmd, "- none -", 8) ||
ImageCmd == NULL || ImageCmd == '\0') {
CursesErrorMsg(Gtxt("Sorry, this machine doesn't support images",158));
break;
}
Draw_Status(Gtxt("Receiving Image...",132));
refresh();
if (RemoteUser)
Download_file(ZeGopher);
else
showfile(ZeGopher);
break;
case A_MIME:
if (!SecureMode) {
Draw_Status(Gtxt("Receiving MIME File...",129));
refresh();
showfile(ZeGopher);
}
else
CursesErrorMsg(Gtxt("Sorry, cannot display this file",148));
break;
case A_MACHEX:
case A_PCBIN:
case A_UNIXBIN:
if (RemoteUser || SecureMode || NoShellMode) {
Draw_Status(Gtxt("Receiving File...",126));
refresh();
Download_file(ZeGopher);
}
else
Save_file(ZeGopher, NULL, NULL);
break;
case A_DIRECTORY:
Draw_Status(Gtxt("Receiving Directory...",125));
refresh();
failed = Load_Dir(ZeGopher);
break;
case A_TELNET:
case A_TN3270:
{
char type, buf[512];
if ((type=GSgetType(ZeGopher)) == A_TELNET &&
(!RCdisplayCommand(GlobalRC, "Terminal/telnet", "", buf) ||
!strncasecmp(buf, "- none -", 8) ||
buf == NULL || buf[0] == '\0')) {
CursesErrorMsg(Gtxt("Sorry, telnet is not available.",157));
break;
}
else if (type == A_TN3270 &&
(!RCdisplayCommand(GlobalRC, "Terminal/tn3270", "", buf) ||
!strncasecmp(buf, "- none -", 8) ||
buf == NULL || buf[0] == '\0')) {
CursesErrorMsg(Gtxt("Sorry, tn3270 is not available.",160));
break;
}
else
do_tel_3270(ZeGopher);
}
break;
case A_INDEX:
refresh();
if (search_string) { /* search_string in commandline */
Searchstring = search_string;
search_string = NULL;
} else
Searchstring = do_index(ZeGopher);
Draw_Status(Gtxt("Searching Text...",142));
if (Searchstring != NULL)
failed=Load_Index(ZeGopher);
else
failed = 1;
break;
case A_CSO:
do_cso(ZeGopher);
break;
case A_SOUND:
Draw_Status(Gtxt("Receiving Sound...",131));
refresh();
do_sound(ZeGopher);
break;
case A_MOVIE:
if (!RCdisplayCommand(GlobalRC, "video", "", MovieCmd) ||
!strncasecmp(MovieCmd, "- none -", 8) ||
MovieCmd == NULL || MovieCmd == '\0') {
CursesErrorMsg(Gtxt("Sorry, this machine doesn't support movies",165));
break;
}
Draw_Status(Gtxt("Receiving Movie...",130));
refresh();
if (RemoteUser)
Download_file(ZeGopher);
else
do_movie(ZeGopher);
break;
case A_HTML:
Draw_Status(Gtxt("Receiving HTML page...",127));
refresh();
do_html(ZeGopher);
break;
}
if (GSisAsk(ZeGopher) && GSgetLocalFile(ZeGopher) != NULL) {
unlink(GSgetLocalFile(ZeGopher));
GSsetLocalFile(ZeGopher, NULL);
}
(void)GSsetAskdata(ZeGopher, NULL);
return(failed);
}
/*
* Provide information about a gopher item...
* Should use curses... But....
*/
void
#ifdef DESCRIBE_GOPHER_GRIPE
describe_gopher(banner, ZeGopher, gripefile)
FILE *gripefile;
#else
describe_gopher(banner, ZeGopher)
#endif
char *banner;
GopherStruct *ZeGopher;
{
char *tmpfilename;
FILE *tmpfile;
Blockobj *bl;
int i,j, views;
GopherObj *infogs;
infogs = GSnew();
GSsetType(infogs, A_FILE);
GSsetTitle(infogs, Gtxt("Link Info",103));
GSgetginfo(ZeGopher, TRUE);
#ifdef DESCRIBE_GOPHER_GRIPE
if (!gripefile)
goto normal_describe;
tmpfile = gripefile;
GSdestroy(infogs);
goto format_description;
normal_describe:
#endif
#ifdef VMS
Gopenfile = tmpfilename = tempnam(NULL,NULL);
#else
Gopenfile = tmpfilename = tempnam("/tmp", "gopher");
#endif
GSsetLocalFile(infogs, tmpfilename);
GSsetLocalView(infogs, "text/plain");
if ((tmpfile = fopen(tmpfilename, "w")) == NULL) {
CURexit(CursesScreen);
fprintf(stderr, Gtxt("Couldn't make a tmp file!\n",83)),
CleanupandExit(-1);
}
free(tmpfilename);
#ifdef DESCRIBE_GOPHER_GRIPE
format_description:
#endif
GStoLink(ZeGopher, fileno(tmpfile), TRUE);
fprintf(tmpfile, "<URL:%s>\n\n", GSgetURL(ZeGopher));
fflush(tmpfile);
if (GSisGplus(ZeGopher)) {
GopherObj *server;
server = GSnew();
GScpy(server, ZeGopher);
GSsetPath(server, "");
GSsetLocalFile(server, "");
fputc('\n', tmpfile);
/** Search out for specific blocks **/
for (i=0; i<GSgetNumBlocks(ZeGopher); i++) {
if (strcmp(BLgetName(GSgetBlock(ZeGopher, i)),"ABSTRACT")==0){
bl = GSgetBlock(ZeGopher, i);
fprintf(tmpfile, "%s\n---------\n\n",BLgetName(bl));
for (j=0; j < BLgetNumLines(bl); j++) {
fprintf(tmpfile, "%s\n", BLgetLine(bl, j));
}
fprintf(tmpfile, "\n");
break;
}
}
fprintf(tmpfile, Gtxt("Size Language Document Type\n",146));
fprintf(tmpfile, Gtxt("---------- ------------- ----------------------------\n",147));
for (views=0; views< GSgetNumViews(ZeGopher); views++) {
char *cp;
VIewobj *ZeView = GSgetView(ZeGopher, views);
if (ZeView == NULL)
continue;
cp = VIgetSize(ZeView);
if (cp != NULL)
fprintf(tmpfile, "%s", cp);
else
cp = "";
for (i=strlen(cp); i<11; i++)
fputc(' ', tmpfile);
cp = VIprettyLang(ZeView, "En_US");
if (cp != NULL)
fprintf(tmpfile, "%s", cp);
else
cp = VIgetLang(ZeView);
for (i=strlen(cp); i<14; i++)
fputc(' ', tmpfile);
cp = VIgetType(ZeView);
fprintf(tmpfile, "%s\n", cp);
}
for (i=0; i<GSgetNumBlocks(ZeGopher); i++) {
bl = GSgetBlock(ZeGopher, i);
if (strcmp(BLgetName(bl),"ADMIN")==0) {
fprintf(tmpfile, Gtxt("\n\nServer Information\n",186));
fprintf(tmpfile, "------------------\n");
for (j=0; j < BLgetNumLines(bl); j++) {
char *cp = BLgetLine(bl, j);
if ( (strncmp(cp, "Admin:", 5) != 0) &&
(strncmp(cp, "Mod-Date:", 9) != 0) &&
(strncmp(cp, "TTL:", 4) != 0) ) {
fprintf(tmpfile, "%s\n", cp);
}
}
fprintf(tmpfile, "\n");
break;
}
}
GSdestroy(server);
}
#ifdef DESCRIBE_GOPHER_GRIPE
if (gripefile)
return;
#endif
fclose(tmpfile);
showfile(infogs);
CURenter(CursesScreen); /* do this after unlink fails */
GSdestroy(infogs);
return;
}