1 /********************************************************************
2  * $Author: jgoerzen $
3  * $Revision: 1.10 $
4  * $Date: 2002/04/26 14:59:54 $
5  * $Source: /home/jgoerzen/tmp/gopher-umn/gopher/head/gopher/gopher.c,v $
6  * $State: Exp $
7  *
8  * Paul Lindner, University of Minnesota CIS.
9  *
10  * Copyright 1991, 1992 by the Regents of the University of Minnesota
11  * see the file "Copyright" in the distribution for conditions of use.
12  *********************************************************************
13  * MODULE: gopher.c
14  * Main functions for the gopher client
15  *********************************************************************
16  * Revision History:
17  * $Log: gopher.c,v $
18  * Revision 1.10  2002/04/26 14:59:54  jgoerzen
19  * Preparations for 3.0.5.
20  *
21  * Revision 1.9  2002/01/11 04:29:30  jgoerzen
22  * Another attempt at the strerror fix -- maybe #elsif isn't so hot an idea...
23  *
24  * Revision 1.8  2002/01/10 14:44:03  jgoerzen
25  * Updated
26  *
27  * Revision 1.7  2002/01/08 21:35:49  jgoerzen
28  * Many changes:
29  *  Revved the version number
30  *  updated greeting
31  *  updated copyright notices
32  *
33  * Revision 1.6  2002/01/08 20:57:26  jgoerzen
34  * Small logic error fixed
35  *
36  * Revision 1.5  2002/01/08 20:30:35  jgoerzen
37  *     * gopher.c: Modified to use HAVE_STRERROR
38  *
39  * Revision 1.4  2002/01/08 17:36:14  jgoerzen
40  * Finally builds!
41  *
42  * Changes:
43  *
44  *   * config.h.in: rebuilt by autoheader
45  *
46  *   * configure: rebuilt by autoconf
47  *
48  *   * configure.in:
49  *     * Added REGEXLIBS test for NetBSD -- look for re_comp in -lcompat
50  *     * Added checkes for term.h, re_comp.h, regex.h
51  *
52  *   * gopher/ourutils.c, gopher/CURcurses.c: Use term.h check
53  *
54  *   * gopher/Makefile.in, gopherd/Makefile.in, gophfilt/Makefile.in:
55  *     * Use REGEXLIBS
56  *
57  *   * gopher/globals.h, gopher/gopher.c: Remove sys_errlist
58  *
59  *   * object/GSgopherobj.c: Removed <regex.h> #include, now use
60  *     "Regex.h" that has proper regex determining logic
61  *
62  *   * object/Regex.h: Moved regex.h include to here.  Make it conditional
63  *     based on configure test.  Add conditional re_comp.h include.
64  *
65  * Revision 1.3  2000/12/27 21:22:46  s2mdalle
66  * Added a few #include's to get at prototypes for implicitly defined
67  * functions
68  *
69  * Revision 1.2  2000/08/19 01:18:16  jgoerzen
70  * fix sys_errlist
71  *
72  * Revision 1.1.1.1  2000/08/19 00:28:56  jgoerzen
73  * Import from UMN Gopher 2.3.1 after GPLization
74  *
75  * Revision 3.112  1996/01/04  18:28:53  lindner
76  * Updates for autoconf
77  *
78  * Revision 3.111  1995/11/03  21:18:18  lindner
79  * ANSIfication
80  *
81  * Revision 3.110  1995/11/03  20:50:48  lindner
82  * Coen: fixes..
83  *
84  * Revision 3.109  1995/06/08  05:21:47  lindner
85  * Fixed conflict for message 221, fixed problem with DECC
86  *
87  * Revision 3.108  1995/04/15  07:09:02  lindner
88  * New control C methods, CTRL-X mods
89  *
90  * Revision 3.107  1995/02/27  17:09:49  lindner
91  * Add Pacbell NOBANNER patch
92  *
93  * Revision 3.106  1995/01/20  04:52:24  lindner
94  * Modifications for SOCKS
95  * Use VARIABLE length records for text on VMS
96  * Don't add the default gopher server when using the -b option.
97  *
98  * Revision 3.105  1994/12/07  07:43:22  lindner
99  * Fix for URLs on the command line from F.Macrdes
100  *
101  * Revision 3.104  1994/11/25  17:40:35  lindner
102  * Fix for folks with non gopher port of 70 in conf.h
103  *
104  * Revision 3.103  1994/11/16  18:52:52  lindner
105  * Fix for adding a text file to bookmarks
106  *
107  * Revision 3.102  1994/10/21  04:40:34  lindner
108  * IETF url format, plus ANSI printer
109  *
110  * Revision 3.101  1994/10/13  05:30:17  lindner
111  * Compiler complaint fixes
112  *
113  * Revision 3.100  1994/08/19  16:52:38  lindner
114  * Fixes for many gripe problems...
115  *
116  * Revision 3.99  1994/08/03  03:25:12  lindner
117  * Fix for osf
118  *
119  * Revision 3.98  1994/08/01  21:55:27  lindner
120  * Add back garbled flag
121  *
122  * Revision 3.97  1994/07/25  02:53:30  lindner
123  * Skip over type 'i' items, secure mode mods
124  *
125  * Revision 3.96  1994/07/19  20:20:00  lindner
126  * Gripe options added, telnet tracer etc.
127  *
128  * Revision 3.95  1994/07/08  05:55:28  lindner
129  * Fix to allow urls in place of hosts..
130  *
131  * Revision 3.94  1994/07/07  01:51:49  lindner
132  * Fix bug
133  *
134  * Revision 3.93  1994/07/06  15:36:27  lindner
135  * These changes are from F. Macrides:
136  *
137  * Moved declaration and initialization of setlocale_LangDir to
138  * globals.h.
139  *
140  * Note that setlocale() doesn't yet work with VMS Alphas, so it
141  * presently just returns without doing anything and the "hardcoded"
142  * English text is used.  It all works fine with VAXen.
143  *
144  * Added code to delete the temporary file when gopher is called with the
145  * -p or the new -u switch.
146  *
147  * Added J. Lance Wilkinson's (JLW@psulias.psu.edu) internationalization
148  * port to VMS and: 'L' operation for changing language on the fly.
149  *
150  * Pause after telnet/tn3270 to permit reading of any error messages.
151  * Added telnet and tn3270 to prompt for a URL via the 'w' command.
152  *
153  * Modified Alan's dialog box title and prompt for the 'w' command.
154  *
155  * NEVERSETOPTIONS compilation symbol disables ability to set options via
156  * SetOptions() and issues an appropriate message.
157  *
158  * NEVERSPAWN compilation symbol disables the spawn command and issues an
159  * appropriate message.
160  *
161  * On VMS, the NEVERSETOPTIONS and NEVERSPAWN symbols can be used to
162  * create a gopher image for CAPTIVE accounts which still permits
163  * functions that would be disabled by the -s or -S switch, but issues an
164  * appropriate message (rather than a DCL error message) on spawn attempt
165  * and precludes "creative" modifications of the display software and
166  * print command mappings.
167  *
168  * Added missing ! for NoShellMode under binary and and encoded save to
169  * disk if().
170  *
171  * Revision 3.92  1994/07/03  23:11:24  lindner
172  * Add internal download feature
173  *
174  * Revision 3.91  1994/06/29  05:20:37  lindner
175  * Check for new gopher.rc files, add support for A_APP items
176  *
177  * Revision 3.90  1994/06/09  22:13:37  lindner
178  * More language conversions
179  *
180  * Revision 3.89  1994/06/09  04:16:28  lindner
181  * Added option to allow 'd'elete only for bookmarks via a
182  * DELETE_BOOKMARKS_ONLY compilation symbol.
183  *
184  * Revision 3.88  1994/06/09  03:45:02  lindner
185  * More attempts to use sunos 4.1.3 setlocale
186  *
187  * Revision 3.87  1994/06/03  05:58:58  lindner
188  * Use LC_MESSAGES variable instead of LANG
189  *
190  * Revision 3.86  1994/05/27  18:08:51  lindner
191  * Fix for SetOptions() on VMS
192  *
193  * Revision 3.85  1994/05/27  02:22:23  lindner
194  * One more fix for International defs
195  *
196  * Revision 3.84  1994/05/26  19:36:45  lindner
197  * Fix bugs for ginternational
198  *
199  * Revision 3.83  1994/05/26  19:15:22  lindner
200  * Add GINTERNATIONAL stuff
201  *
202  * Revision 3.82  1994/05/25  21:46:35  lindner
203  * Fix for suck_sound
204  *
205  * Revision 3.81  1994/05/25  21:38:17  lindner
206  * Fix for mem prob..
207  *
208  * Revision 3.80  1994/05/25  20:59:42  lindner
209  * Fix for play command forking
210  *
211  * Revision 3.79  1994/05/24  07:00:38  lindner
212  * Don't allow users with rsh to specify a shell as an app..
213  *
214  * Revision 3.78  1994/05/24  05:50:10  lindner
215  * Fix for bad free() in SetOptions()
216  *
217  * Revision 3.77  1994/05/19  15:53:34  lindner
218  * Fix for vms
219  *
220  * Revision 3.76  1994/05/19  14:07:25  lindner
221  * use fast malloc on VMS VAXC
222  *
223  * Revision 3.75  1994/05/18  03:59:25  lindner
224  * Change to FIOsystem() for VMS
225  *
226  * Revision 3.74  1994/05/17  05:47:57  lindner
227  * Massive internationalization change
228  *
229  * Revision 3.73  1994/05/14  04:13:41  lindner
230  * Internationalization...
231  *
232  * Revision 3.72  1994/04/25  04:05:24  lindner
233  * FIOsystem() return values were reversed for VMS.
234  *
235  * Added code for mapping telnet and/or tn3270 commands to "- none -" for
236  * disabling them and having the client issue "Sorry, not implemented"
237  * messages on efforts to use Type 8 or T tuples.
238  *
239  * Added FIOsystem() failure messages for both VMS and Unix. (all from
240  * F.Macrides)
241  *
242  * Revision 3.71  1994/04/25  03:37:38  lindner
243  * Modifications for Debug() and mismatched NULL arguments, added Debugmsg
244  *
245  * Revision 3.70  1994/04/13  19:12:50  lindner
246  * Fix for ASK block bug
247  *
248  * Revision 3.69  1994/04/12  21:02:51  lindner
249  * Move file declaration inside
250  *
251  * Revision 3.68  1994/03/30  20:32:58  lindner
252  * Fix for deleting last item in a directory
253  *
254  * Revision 3.67  1994/03/08  15:55:09  lindner
255  * gcc -Wall fixes
256  *
257  * Revision 3.66  1994/03/08  03:25:55  lindner
258  * Fix for big bad telnet bug, additions for secure process i/o
259  *
260  * Revision 3.65  1994/03/04  23:41:17  lindner
261  * Better gripe, more signal handling, better forms
262  *
263  * Revision 3.64  1994/02/20  16:34:22  lindner
264  * Patch for weird gopher+ servers, add anon-ftp key, and localtime key
265  *
266  * Revision 3.63  1994/01/10  03:29:49  lindner
267  * fix for changes in GDdeleteGS
268  *
269  * Revision 3.62  1993/11/29  01:11:25  lindner
270  * 'v' no longer does anything if user is already viewing bookmarks.
271  * (Beckett)
272  *
273  * Add new routine, do_movie(), which will eventually allow viewing of
274  * movies.  For now, just display an error message.  In process_request(),
275  * add a switch case for the A_MOVIE type.  (Wolfe, Macrides)
276  *
277  * Fix the 'm' command so that it deletes the current menu's cache files.
278  * Also prevent 'm' from destroying bookmarks. (Beckett)
279  *
280  * In describe_gopher(), add TRUE argument to GStoLink() so that the Admin
281  * and ModDate information are displayed ('=' and '^').  (Macrides)
282  *
283  * Prevent describe_gopher() from destroying the cache file for the current
284  * item.  (Beckett, Wolfe)
285  *
286  * Revision 3.61  1993/11/03  03:55:12  lindner
287  * More stable file caching, subject line chopping in Gripe
288  *
289  * Revision 3.60  1993/11/02  21:17:36  lindner
290  * Better client side logging code
291  *
292  * Revision 3.59  1993/10/27  18:52:14  lindner
293  * Simplify VMS fixed/variable file code
294  *
295  * Revision 3.58  1993/10/26  18:45:09  lindner
296  * Move screen refresh stuff to CURwgetch()
297  *
298  * Revision 3.57  1993/10/26  18:01:01  lindner
299  * Allow saving to variable length record VMS files, remove Interrupted msg
300  *
301  * Revision 3.56  1993/10/22  20:24:14  lindner
302  * Fixes for VMS tempnam() (Fote)
303  *
304  * Revision 3.55  1993/10/22  20:06:37  lindner
305  * Add optional client logging
306  *
307  * Revision 3.54  1993/10/20  03:24:55  lindner
308  * Cleanup on a SIGHUP signal
309  *
310  * Revision 3.53  1993/10/13  16:47:23  lindner
311  * Fixes for empty bookmark addition problem
312  *
313  * Revision 3.52  1993/10/11  17:16:15  lindner
314  * Fote's mods for display applications
315  *
316  * Revision 3.51  1993/10/11  04:54:36  lindner
317  * Fix for segvs when exiting early
318  *
319  * Revision 3.50  1993/10/07  05:09:28  lindner
320  * Don't redraw the screen if we attempt to go up a directory level from
321  * the root level.
322  *
323  * In the 'o' command, allocate memory for the form based on screen width.
324  * Fix memory leak.
325  *
326  * In Gripe(), allocate memory for the form based on screen width.  Fix
327  * email parsing under VMS.  Fix problem where gopher did not delete mail
328  * temp files under VMS.
329  *
330  * In process_request(), make the status "Receiving xxxx..." a bit more
331  * descriptive for images, MIME files, and other files.
332  *
333  * Add NULL as third argument to Save_File in a couple of places
334  *
335  * Revision 3.49  1993/09/30  22:42:04  lindner
336  * Add option for bolding of searched words
337  *
338  * Revision 3.48  1993/09/29  20:50:57  lindner
339  * Always cleanupandexit instead of exit, allow deleting of last bookmark
340  *
341  * Revision 3.47  1993/09/26  09:19:44  lindner
342  * Changed errormsg wording
343  *
344  * Revision 3.46  1993/09/22  04:14:22  lindner
345  * Fix core dumps when exiting from bookmark screen, various cleanups
346  *
347  * Revision 3.45  1993/09/22  03:56:47  lindner
348  * Add support for tn3270 on oddball ports..
349  *
350  * Revision 3.44  1993/09/22  03:09:59  lindner
351  * Add command-line searching
352  *
353  * Revision 3.43  1993/09/22  01:15:49  lindner
354  * Add support for DEC HELP key/KEY_HELP
355  *
356  * Revision 3.42  1993/09/21  01:50:43  lindner
357  * Fix for caching of alternate views
358  *
359  * Revision 3.41  1993/09/18  04:40:47  lindner
360  * Additions to fix caching of Multiple view items
361  *
362  * Revision 3.40  1993/09/18  03:28:30  lindner
363  * Remove extra CURexit()
364  *
365  * Revision 3.39  1993/09/11  04:46:45  lindner
366  * Don't allow null applications to be added with the options code, Beep on network error
367  *
368  * Revision 3.38  1993/09/08  05:22:30  lindner
369  * Lobotomized the bolding code
370  *
371  * Revision 3.37  1993/09/08  01:11:51  lindner
372  * Added URL stuff to describe_gopher
373  *
374  * Revision 3.36  1993/09/03  03:33:45  lindner
375  * Inform user about mal-configuration if view has Unix piping on VMS.
376  *
377  * Deal with the problem of the gopher+ code setting view to NULL or ""
378  * here and there as if they were equivalent.
379  *
380  * Added code for mailing gripes from VMS systems, and fixed memory leak
381  * in the Unix code.
382  *
383  * Added v1.12b 'S' command for saving titles in a directory.
384  *
385  * Added titles to CURGetOneOption() calls.
386  *
387  * Revision 3.35  1993/08/25  02:57:52  lindner
388  * Fix for Fote mod to 'o' command
389  *
390  * Revision 3.34  1993/08/23  02:32:28  lindner
391  * Don't connect if nothing typed in 'o'
392  *
393  * Revision 3.33  1993/08/19  20:31:10  lindner
394  * remove excess variable
395  *
396  * Revision 3.32  1993/08/19  20:22:49  lindner
397  * Mitra's Debug patch
398  *
399  * Revision 3.31  1993/08/16  18:10:19  lindner
400  * Temporary code to fix DEC Alphas screen clearing, exit handler for VMS
401  *
402  * Revision 3.30  1993/08/16  17:58:20  lindner
403  * Removed REMOTEUSER ifdefs
404  *
405  * Revision 3.29  1993/08/09  20:28:04  lindner
406  * Mods for VMS for telnet dialog, argv[0]
407  *
408  * Revision 3.28  1993/08/09  20:17:59  lindner
409  * Fixes for CMULIB and NETLIB for VMS
410  *
411  * Revision 3.27  1993/08/05  03:24:21  lindner
412  * Fix for control-c on startup
413  *
414  * Revision 3.26  1993/08/04  22:08:47  lindner
415  * Fix for problems with '=' and '?' and /bin/mail Gripe mods
416  *
417  * Revision 3.25  1993/08/03  20:48:27  lindner
418  * Audio file fix from jqj
419  *
420  * Revision 3.24  1993/08/03  20:26:50  lindner
421  * Don't allow securemode types to use o
422  *
423  * Revision 3.23  1993/08/03  20:24:18  lindner
424  * Bigger Better Badder Options, inspired by jqj
425  *
426  * Revision 3.22  1993/08/03  04:43:56  lindner
427  * Fix for VMS unresolved variables
428  *
429  * Revision 3.21  1993/07/30  17:37:24  lindner
430  * SecureMode fix from Mitra
431  *
432  * Revision 3.20  1993/07/30  14:19:29  lindner
433  * Mitra autoexit patch
434  *
435  * Revision 3.19  1993/07/30  14:12:18  lindner
436  * Allow non-gplus stuff to cache
437  *
438  * Revision 3.18  1993/07/29  17:24:53  lindner
439  * Removed dead variable, $ and ! are synonomous now
440  *
441  * Revision 3.17  1993/07/27  05:28:48  lindner
442  * Mondo Debug overhaul from Mitra
443  *
444  * Revision 3.16  1993/07/27  02:02:22  lindner
445  * More comments, GDdelete method
446  *
447  * Revision 3.15  1993/07/26  20:29:40  lindner
448  * fix memory usage
449  *
450  * Revision 3.14  1993/07/26  15:35:56  lindner
451  * fix for longjmp params
452  *
453  * Revision 3.13  1993/07/23  04:42:58  lindner
454  * error checking and longjmp'ing
455  *
456  * Revision 3.12  1993/07/20  23:15:35  lindner
457  * Mods to cache askdata..
458  *
459  * Revision 3.11  1993/07/07  19:42:59  lindner
460  * fix for SGIs
461  *
462  * Revision 3.10  1993/06/29  06:18:09  lindner
463  * Prettier error msg for VMS
464  *
465  * Revision 3.9  1993/06/22  06:13:28  lindner
466  * took back fix for perror.h etc.
467  *
468  * Revision 3.8  1993/06/11  16:25:43  lindner
469  * Many bug fixes, open new gopher, shell escape, etc.
470  *
471  * Revision 3.7  1993/04/30  16:04:48  lindner
472  * Cleared gripe memory, fixed bug for long names in  check_sock
473  *
474  * Revision 3.6  1993/04/13  04:56:54  lindner
475  * New code for REMOTEUSERS from Mitra
476  * Better error messages for socket connections from jqj
477  *
478  * Revision 3.5  1993/03/24  16:59:54  lindner
479  * Major changes to the way things are displayed
480  *
481  * Revision 3.4  1993/03/18  23:32:07  lindner
482  * commented out forkoff stuff for now...
483  *
484  * Revision 3.3  1993/02/19  21:08:04  lindner
485  * Updated most routines to get defaults from gopher+ attribute types,
486  * Allow commands to be pipelines and almost have forking working :-)
487  *
488  * Fixed problems with bookmarks.  Still need to work on g+ bookmarks
489  *
490  * Revision 3.2  1993/02/11  18:25:45  lindner
491  * Fixed helpfile display.
492  *
493  *********************************************************************/
494 
495 
496 #include "gopher.h"
497 #if defined(VMS) && defined(A_LANGUAGE)
498 static	CursesObj	*LangScreen = NULL;
499 #endif
500 #include "Stdlib.h"
501 #include "Debug.h"
502 
503 #include <errno.h>
504 #include "fileio.h"
505 #include "Malloc.h"
506 #include "patchlevel.h"
507 
508 #ifdef HAVE_TIME_H
509 #  include <time.h>
510 #endif /* HAVE_TIME_H */
511 
512 void describe_gopher(char *banner, GopherStruct *ZeGopher, FILE *gripefile);
513 extern int  twirl(void);
514 
515 
516 /*
517 ** Open a connection to another host using telnet or tn3270
518 */
519 
520 void
do_tel_3270(GopherStruct * ZeGopher)521 do_tel_3270(GopherStruct *ZeGopher)
522 {
523      char *Dialogmess[9];
524 
525      char sMessage1[128];
526      char sMessage2[128];
527 
528      char sTelCmd[128];
529 
530      char *cp;
531 
532      /* retrieve the gopher information for the telnet command*/
533 
534      clear();
535 #ifdef VMS
536      refresh();
537 #endif
538      Dialogmess[0] = Gtxt("Warning!!!!!, you are about to leave the Internet",34);
539      Dialogmess[1] = Gtxt("Gopher program and connect to another host. If",35);
540      Dialogmess[2] = Gtxt("you get stuck press the control key and the",36);
541 #if defined(VMS) && defined(MULTINET)
542      Dialogmess[3] = Gtxt("^ key, and then type q.",38);
543 #else
544      Dialogmess[3] = Gtxt("] key, and then type quit",37);
545 #endif
546      Dialogmess[4] = "";
547 
548      if (GSgetPort(ZeGopher) != 0)
549 	  sprintf(sMessage1,Gtxt("Connecting to %.40s, port %d using %s.",39),
550 		  GSgetHost(ZeGopher),GSgetPort(ZeGopher),
551 		  (GSgetType(ZeGopher) == A_TN3270) ? "tn3270" : "telnet");
552      else
553 	  sprintf(sMessage1, Gtxt("Connecting to %.40s using %s.",40),
554 		  GSgetHost(ZeGopher),
555 		  (GSgetType(ZeGopher) == A_TN3270) ? "tn3270" : "telnet");
556 
557      Dialogmess[5] = sMessage1;
558 
559      cp = GSgetPath(ZeGopher);
560      if (*cp != '\0')
561 	  sprintf(sMessage2,
562 		  Gtxt("Use the account name \"%.40s\" to log in",41),
563 		  GSgetPath(ZeGopher));
564      else
565 	  sMessage2[0] = '\0';
566 
567      Dialogmess[6] = "";
568      Dialogmess[7] = sMessage2;
569      Dialogmess[8] = NULL;
570 
571      if (CURDialog(CursesScreen, GSgetTitle(ZeGopher), Dialogmess) <0)
572 	  return;
573 
574      CURexit(CursesScreen);
575 
576      if (GSgetType(ZeGopher) == 'T') {
577 	  /**** A TN3270 connection ****/
578 	  RCdisplayCommand(GlobalRC, "Terminal/tn3270", GSgetHost(ZeGopher),
579 			   sTelCmd);
580 
581      } else {
582 
583 	  RCdisplayCommand(GlobalRC, "Terminal/telnet", GSgetHost(ZeGopher),
584 			   sTelCmd);
585      }
586 
587      if (GSgetPort(ZeGopher) != 0 && GSgetPort(ZeGopher) != 23)
588 #if defined(VMS) && (defined(MULTINET) || defined(CMUIP))
589 	  sprintf(sTelCmd+strlen(sTelCmd),
590 		  "/PORT=%d", GSgetPort(ZeGopher));
591 #else
592      sprintf(sTelCmd+strlen(sTelCmd), " %d", GSgetPort(ZeGopher));
593 #endif
594 
595      CURexit(CursesScreen);
596 
597 #ifdef Telnet_Trace
598      Telnet_Trace(sTelCmd, ZeGopher);
599 #endif
600      FIOsystem(sTelCmd);
601      printf("\n\n");
602      printf(Gtxt("\nPress <RETURN> to continue",120));
603      fflush(stdout);
604      fflush(stdin);
605      fflush(stdin);
606      (void) getchar();
607      CURenter(CursesScreen);
608      return;
609 }
610 
611 
612 #if defined(VMS) && defined(TELNET_TRACE)
613 
614 /*
615 ** Some sites are really paranoid about attempts to connect from their site
616 ** to specific telnet hosts (e.g., MUDDs, etc.) -- so provide a log facility
617 ** to track specific connectsions just to see how clients are finding them.
618 */
619 #include <descrip.h>
620 #include <lnmdef.h>
621 #include <ssdef.h>
622 #include "syslog.h"
623 
624 /*
625 **  Like logrequest() but should use a different log file just for this...
626 */
627 
628 int
trace_telnet(msg,gs)629 trace_telnet(msg, gs)
630   char *msg;
631   GopherObj *gs;
632 {
633      static boolean inited = FALSE;
634      char *url = "";
635      char *name = "";
636 
637      if (!gs)
638 	return(0);
639 
640      if (inited == FALSE) {
641 	  openlog("gopher", (LOG_PID), LOG_LOCAL7); /* MULTINET allows LOCAL7 */
642 	  setlogmask(LOG_UPTO(LOG_INFO));
643 	  inited = TRUE;
644      }
645      if (gs != NULL) {
646 	  url = GSgetURL(gs, "");
647 	  name = GSgetTitle(gs);
648      }
649 
650      syslog(LOG_INFO, "%s %s%s%s%s", msg, url, strlen(name)?" \"":"",
651 						    name, strlen(name)?"\"":"");
652 
653      return(0);
654 }
655 
656 int
Telnet_Trace(char * sTelCmd,GopherStruct * ZeGopher)657 Telnet_Trace(char *sTelCmd, GopherStruct *ZeGopher)
658 {
659 	char	text[255];
660         int	l;
661 static	int	buf_len;
662 static	int	i;
663 static	int	junk;
664 static	int	max_len;
665 
666 	int	no_case = LNM$M_CASE_BLIND;
667 static	$DESCRIPTOR(lname_desc,"GOPHER_TELNET_LOG");
668 static	$DESCRIPTOR(tabnam,"LNM$SYSTEM_TABLE");
669 struct itmlst
670     {
671 	short int buf_len;
672 	short int item_code;
673 	char *buffer;
674 	int *buf_length;
675     };
676 static
677     struct itmlst
678 		item_max[] = { {4,LNM$_MAX_INDEX,(char *)&max_len,&buf_len},
679 					{0,0,0,0}};
680 static
681     struct itmlst
682 		item_str[] = { {4,LNM$_INDEX,(char *)&i,&junk},
683 					{0,LNM$_STRING,0,&buf_len},
684 					{0,0,0,0}};
685 
686   if (SS$_NORMAL != SYS$TRNLNM(&no_case,&tabnam,&lname_desc,0,&item_max))
687 	return;
688   item_str[1].buffer = text;
689   for (i=0; i<=max_len; i++) {
690     item_str[1].buf_len = 255;
691     if (SS$_NORMAL==SYS$TRNLNM(&no_case,&tabnam,&lname_desc,0,&item_str)) {
692 	text[buf_len] = '\0';
693 	if (strcmp(text,GSgetHost(ZeGopher))==0) {
694 	    /* Log this session */
695 	    text[sprintf(text," *-%.*s>",iLevel,
696 			"-------------------------------")]='\0';
697 	    trace_telnet(text,ZeGopher);
698 	    if(GDgetLocation(CurrentDir) != NULL) {
699 		text[sprintf(text," + %.*s ",iLevel,
700 			    "                               ")]='\0';
701 		trace_telnet(text,GDgetLocation(CurrentDir));
702 	    }
703 	    for (l = iLevel; l>0; l--) {
704 		text[sprintf(text," +%.*s>",l,
705 			"-------------------------------")]='\0';
706 		trace_telnet(text, GDgetEntry(OldDirs[l-1],
707 				    GDgetCurrentItem(OldDirs[l-1])-1));
708 	    }
709 	    return;
710 	}
711     }
712   }
713     return;
714 }
715 #endif
716 
717 
718 void
Gripe(GopherObj * gs)719 Gripe(GopherObj *gs)
720 {
721      char *gripeprompt[15];
722      char *gripemess[15];
723      char *cp = NULL;
724      char  email[128], version[128];
725      int   i, j;
726      char *mailargv[3];
727      FileIO *fio;
728 
729 #ifdef VMS
730      char mailcmd[256],
731      FILE *f;
732      char MailAddress[512], *tmpfilename;
733 #endif
734 
735      GSgetginfo(gs, TRUE);
736 
737      if (GSisGplus(gs) && (GSgetAdmin(gs) != NULL))
738 	  cp = strchr(GSgetAdmin(gs),'<');
739 
740      /* at this point cp is NULL if (and hopefully only if)
741       * the item is not Gopher+, there is no Admin for the item,
742       * or there is no e-mail address in the admin definition
743       */
744      if (cp == NULL) {
745 
746 #ifdef LOCAL_GRIPE_ADMINISTRATOR
747 # ifdef DOMAIN_FOR_LOCAL_GRIPES
748 	  if (strstr(GSgetHost(gs),DOMAIN_FOR_LOCAL_GRIPES) != NULL )
749 # endif
750 	       cp = LOCAL_GRIPE_ADMINISTRATOR;
751 #endif
752 
753 	  /* if the local gripe admin won't take it, either let the user
754 	   * edit it, or put up an error message
755 	   */
756 	  if (cp == NULL) {
757 #ifdef MODIFIABLE_GRIPE_TO
758 	       cp = "<>";
759 #else
760 	       CursesErrorMsg(
761 		 Gtxt("Can't find an administrator for this item, sorry!",48));
762 	       return;
763 #endif
764 	  }
765      }
766 
767      strncpy(email, cp+1, sizeof(email));
768      cp = strrchr(email, '>');
769      if (cp != NULL)
770 	  *cp = '\0';
771 
772 #define ACHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789%.@!-_"
773 
774      if (SecureMode || NoShellMode)
775 	  if ( strspn(email, ACHARS) != strlen(email)||
776 	      *email == '-') {
777 		  CursesErrorMsg("Can't find an administrator for this item, sorry!");
778 		  return;
779 	  }
780 
781      /** Empty out the array **/
782      for (i=3; i< 15; i++) {
783 	  cp = (char *) malloc(sizeof(char) * COLS);
784 	  bzero(cp,COLS*sizeof(char));
785 	  gripeprompt[i] = "";
786 	  gripemess[i] = cp;
787      }
788 
789      for (i=0; i < 3; i++)
790 	  gripemess[i] = NULL;
791 
792 
793 
794      gripeprompt[0] = Gtxt("Hit the Tab key at the end of each line you type.",49);
795 #ifdef CONTROLX
796      gripeprompt[1] = Gtxt("Press Control-X to send your message.",50);
797 #else
798      gripeprompt[1] = Gtxt("Hit the Enter key to send your message.",50);
799 #endif
800 
801 
802 #ifdef MODIFIABLE_GRIPE_TO
803      gripeprompt[2] = Gtxt("To",230);
804      gripemess[2] = email;
805      gripeprompt[13] = Gtxt("<Last Line>",231);
806 #else
807      gripeprompt[2] = "";
808 #endif
809      gripeprompt[3] = Gtxt("Subject",51);
810      gripeprompt[4] = Gtxt("Problem",52);
811      gripeprompt[14] = NULL;
812 
813      if (CURRequest(CursesScreen, GSgetAdmin(gs), gripeprompt, gripemess)!=0) {
814 	  return;
815      }
816      if (strlen(gripemess[3]) == 0) {
817 	  CursesErrorMsg(Gtxt("Cannot send, no subject...",228));
818 	  return;
819      }
820      for (i=4, j=0; i<14; i++)
821 	  j += strlen(gripemess[i]);
822      if (j < 20) {
823 	  CursesErrorMsg(Gtxt("Cannot send, message too small...",228));
824 	  return;
825      }
826 
827 #ifdef MODIFIABLE_GRIPE_TO
828      if (strlen(strcpy(email,gripemess[2])) == 0) {
829 	CursesErrorMsg(Gtxt("Cannot send mail to null address...",228));
830 	return;
831      }
832 #endif
833 #ifdef VMS
834      if ((strchr(email, '@') != NULL) && (strchr(email, '\"') == NULL))
835 	   sprintf(MailAddress, MAIL_ADRS, email);
836      else
837 	   strcpy(MailAddress, email);
838 
839      tmpfilename = tempnam(NULL,NULL);
840 
841      if ((f = fopen(tmpfilename, "w")) == NULL) {
842 	  CursesErrorMsg(Gtxt("Cannot send mail...",53));
843 	  return;
844      }
845 
846      sprintf(mailcmd, "%s/subject=\"%.70s\" %s %s",
847            MAIL_COMMAND, gripemess[3], tmpfilename, MailAddress);
848      free(gripemess[3]);
849 #ifdef DESCRIBE_GOPHER_GRIPE
850      fprintf(f, Gtxt("Regarding the following Gopher item:\n",229));
851      describe_gopher(" ",
852 		    GDgetEntry(CurrentDir, GDgetCurrentItem(CurrentDir)-1),
853 			f);
854      fprintf(f, "\n");
855 #endif
856      for (i=14; i>3 && strlen(gripemess[i])==0; i--) {
857 	  free(gripemess[i]);
858 	  gripemess[i] = NULL;
859      }
860 
861      for (i=4; (i< 15) && gripemess[i]; i++) {
862 	  fprintf(f, "%s\n", gripemess[i]);
863 	  free(gripemess[i]);
864      }
865      fprintf(f, "\n[Sent from within the ");
866      fprintf(f, Gtxt("Internet Gopher Information Client v%s.%s.%d",102),
867 		GOPHER_MAJOR_VERSION, GOPHER_MINOR_VERSION, PATCHLEVEL);
868      fprintf(f, "]\n");
869 
870      fclose(f);
871 
872      CURexit(CursesScreen);
873      printf(Gtxt("Mailing gripe to %s...",54), MailAddress);
874      FIOsystem(mailcmd);
875      unlink(tmpfilename);
876      free(tmpfilename);
877      CURenter(CursesScreen);
878 #else
879 
880      mailargv[0] = MAIL_COMMAND;
881      mailargv[1] = email;
882      mailargv[2] = NULL;
883 
884      fio = FIOopenProcess(mailargv[0], mailargv, "w");
885 
886      if (fio == NULL) {
887 	  CursesErrorMsg(Gtxt("Cannot send mail...",53));
888 	  return;
889      }
890 
891      *(gripemess[3] + 71) = '\0';
892      FIOwritestring(fio, "Subject: ");
893      FIOwritestring(fio, gripemess[3]);
894      FIOwritestring(fio, "\n\n");
895 
896      free(gripemess[3]);
897 
898 #ifdef DESCRIBE_GOPHER_GRIPE
899     FIOwritestring(fio, Gtxt("Regarding the following Gopher item:\n",229));
900     GStoLink(GDgetEntry(CurrentDir, GDgetCurrentItem(CurrentDir)-1),
901 	     FIOgetfd(fio), TRUE);
902     FIOwritestring(fio, "\n");
903 #endif
904 
905 
906      for (i=14; i>3 && strlen(gripemess[i])==0; i--) {
907 	    free(gripemess[i]);
908 	    gripemess[i] = NULL;
909      }
910 
911      for (i=4; i< 15 && gripemess[i]; i++) {
912 	  FIOwritestring(fio, gripemess[i]);
913 	  FIOwritestring(fio, "\n");
914 	  free(gripemess[i]);
915      }
916 
917      FIOwritestring(fio, "\n[Sent from within the ");
918      sprintf(version, Gtxt("Internet Gopher Information Client v%s.%s.%d",102),
919 	     GOPHER_MAJOR_VERSION, GOPHER_MINOR_VERSION, PATCHLEVEL);
920      FIOwritestring(fio, version);
921      FIOwritestring(fio, "]\n");
922 
923      FIOclose(fio);
924 #endif
925 }
926 
927 /*
928 ** do_index gets keywords from the user to search for.  It returns
929 ** it to the calling process.  This storage is volotile. Callers should
930 ** make a copy if they want to call do_index multiple times.
931 */
932 
do_index(GopherObj * ZeGopher)933 char* do_index(GopherObj *ZeGopher)
934 {
935      static char *inputline = NULL;
936      static char *prompt[2];
937      static char *response[2];
938 
939      if (inputline == NULL) {
940 	  inputline = (char *) malloc(sizeof(char)*256);
941 	  if (inputline == NULL)
942 	       perror("Out of memory"), CleanupandExit(-1);
943 	  *inputline = '\0';
944      }
945 
946      prompt[0] = Gtxt("Words to search for",55);
947      prompt[1] = NULL;
948 
949      response[0] = inputline;
950      response[1] = NULL;
951 
952      if (CURRequest(CursesScreen, GSgetTitle(ZeGopher),prompt, response) == -1 )
953 	  return(NULL);
954 
955      if (*inputline == '\0')
956 	  return(NULL);
957      else
958 	  return(inputline);
959 }
960 
961 
962 /*
963  * this procedure just retrieves binary data from the socket and
964  * pumps it into a "play" process.
965  */
966 
967 #define BUFSIZE 1400  /* A pretty good value for ethernet */
968 
969 #ifndef VMS
970 void
suck_sound(int sockfd)971 suck_sound(int sockfd)
972 {
973      FileIO *Play;
974      int j;
975      char buf[BUFSIZE];
976      char playCmd[BUFSIZE];
977 
978      if (!RCdisplayCommand(GlobalRC, "Audio/basic", "", playCmd) ||
979          !strncasecmp(playCmd, "- none -", 8) ||
980 	 playCmd == NULL || playCmd[0] == '\0') {
981 	  /*** Hey! no play command, bummer ***/
982 	  /** We're forked at this point, and we can't display anything.. **/
983 	  /*	  CursesErrorMsg(Gtxt("Sorry, this machine doesn't support sounds",159));*/
984 
985 	  return;
986      }
987 
988      Play = FIOopenCmdline(playCmd, "w");
989 
990 
991      while(1) {
992           j = read(sockfd, buf, BUFSIZE);
993 
994 	  if (j == 0)
995 	       break;
996 
997 	  FIOwriten(Play, buf, j);
998      }
999 }
1000 #endif
1001 
1002 /*
1003  * fork off a sound process to siphon the data across the net.
1004  * So the user can listen to tunage while browsing the directories.
1005  */
1006 
1007 void
do_sound(GopherStruct * ZeGopher)1008 do_sound(GopherStruct *ZeGopher)
1009 {
1010 #ifdef VMS
1011      CursesErrorMsg(Gtxt("Sorry, this machine doesn't support sounds",159));
1012 #else
1013      int sockfd;
1014      BOOLEAN Waitforchld = FALSE;
1015 
1016      if ((sockfd = GSconnect(ZeGopher)) <0) {
1017 	  check_sock(sockfd, GSgetHost(ZeGopher), GSgetPort(ZeGopher));
1018 	  return;
1019      }
1020 
1021      /** Send out the request **/
1022      GStransmit(ZeGopher, sockfd, NULL, NULL, NULL);
1023 
1024      /** Okay, it's cool, we can fork off **/
1025 
1026      if (SOUNDCHILD != 0)
1027 	  Waitforchld = TRUE;
1028 
1029      if ( (SOUNDCHILD = fork()) < 0)
1030 	  ;/* Fork Error */
1031 
1032      else if (SOUNDCHILD == 0) {  /* Child Process */
1033 	  suck_sound(sockfd);
1034 	  exit(0);
1035      }
1036 
1037      /* Parent Process */
1038 
1039      closenet(sockfd);
1040      return;
1041 #endif  /* not VMS */
1042 }
1043 
1044 
1045 
1046 
1047 /**
1048 *** Show file takes a gopher text thing, writes it to a file
1049 *** and passes it to your favorite pager.
1050 **/
1051 
1052 void
showfile(GopherObj * ZeGopher)1053 showfile(GopherObj *ZeGopher)
1054 {
1055      char    *tmpfilename = NULL;
1056      FILE    *tmpfile;
1057      char    inputline[512];
1058      char    *view = NULL;
1059      boolean WritePipe = FALSE,
1060              ForkOff   = FALSE;
1061      boolean GS2FileSucceeded = TRUE;
1062      int     Child;
1063 
1064      DebugGSplusPrint(ZeGopher, "showfile:start");
1065 
1066      view = Choose_View(ZeGopher);
1067 
1068      Debug("showfile:Choose_View returned view=%s\n",view);
1069 
1070      if ((view == NULL) || (*view == '\0'))
1071 	  return;
1072 
1073      if (!RCdisplayCommand(GlobalRC, view, "", inputline) ||
1074          !strncasecmp(inputline, "- none -", 8) ||
1075 	 inputline == NULL || inputline[0] == '\0') {
1076           CursesErrorMsg(Gtxt("No display command is mapped to this view!",113));
1077 	  return;
1078      }
1079      Debug("showfile:inputline=%s\n",inputline);
1080 
1081      if (GSgetLocalFile(ZeGopher) == NULL ||
1082 	 GSgetLocalView(ZeGopher) == NULL ||
1083 	 strcmp(GSgetLocalView(ZeGopher), view)!= 0) {
1084 	  /*** We need to retrieve the file ***/
1085 
1086 	  if (*inputline == '|') {
1087 #ifdef VMS
1088 	       CursesErrorMsg(Gtxt("Unix piping requested.  Check your configuration!",176));
1089 	       return;
1090 #endif
1091 	       if ((tmpfile = popen(inputline+1, "w")) == NULL) {
1092                    char buf[75];
1093                    snprintf(buf, sizeof(buf) - 1,
1094                             Gtxt("Coultn't execute %s", 81), inputline);
1095                    CursesErrorMsg(buf);
1096                    return;
1097                }
1098 	       WritePipe = TRUE;
1099 	       CURexit(CursesScreen);
1100 	  } else {
1101 	       /** Open a temporary file **/
1102 #ifdef VMS
1103 	       tmpfilename = tempnam(NULL,NULL);
1104 #else
1105 	       tmpfilename = tempnam("/tmp","gopher");
1106 #endif
1107 
1108 #if defined(VMS)  && defined(VMSRecords)
1109 	       if (GSisText(ZeGopher, view))
1110 		    /*** Use VARIABLE length records for text on VMS ***/
1111 		    tmpfile = fopen_VAR(tmpfilename, "w");
1112 	       else
1113 		    /*** Use FIXED 512 records for binaries on VMS ***/
1114 	            tmpfile = fopen_FIX(tmpfilename, "w");
1115 
1116 	       if (tmpfile == NULL) {
1117 #else
1118 	       if ((tmpfile = fopen(tmpfilename, "w")) == NULL) {
1119 #endif
1120 		    CURexit(CursesScreen);
1121 		    fprintf(stderr, Gtxt("Couldn't make a tmp file!\n",83)),
1122 		    CleanupandExit(-1);
1123 	       }
1124 	       if (GSgetLocalFile(ZeGopher) != NULL &&
1125 		   GSgetLocalView(ZeGopher) != NULL &&
1126 		   strcmp(GSgetLocalView(ZeGopher), view) != 0)
1127 		    unlink(GSgetLocalFile(ZeGopher));
1128 	       GSsetLocalFile(ZeGopher, tmpfilename);
1129 	  } /* | */
1130 
1131 	  if (inputline[strlen(inputline)-1] == '&') {
1132 	       inputline[strlen(inputline)-1] = '\0';
1133 	       ForkOff = TRUE;
1134 	  }
1135 
1136 #ifndef VMS
1137 	  /* This is the child process, so exit.. */
1138 	  if (ForkOff) {
1139 	       if ((Child = fork()) < 0) {
1140 		    ;/** Fork Error ***/
1141 		    CursesErrorMsg("Fork Error!");
1142 	       }
1143 	       else if (Child >0) {
1144 		    /*** Parent Process ***/
1145 		    ;
1146 		    CURenter(CursesScreen);
1147 		    return;
1148 	       }
1149 	  }  /* Forkoff */
1150 #endif
1151 
1152  	  Debug("\n\rshowfile: view=%s ",view);
1153  	  Debug("\n\rcommand=%s ",inputline);
1154  	  Debug("\n\rfile=%s",tmpfilename);
1155  	  Debug("\n\r/%s\n",GSgetLocalFile(ZeGopher));
1156 
1157 	  GS2FileSucceeded = GStoFile(ZeGopher, tmpfile, view, twirl);
1158 
1159 	  if (!GS2FileSucceeded) {
1160 	       GSsetLocalFile(ZeGopher, NULL);
1161 	       GSsetLocalView(ZeGopher, NULL);
1162 	  }
1163 	  else
1164 	       GSsetLocalView(ZeGopher, view);
1165 
1166 
1167 	  if (WritePipe)
1168 	       pclose(tmpfile);  /* data went down pipe - displayed there */
1169 	  else
1170 	       fclose(tmpfile);  /* data is in tmpfile, will display below */
1171 
1172      } /* GopehrPluss || LocalFile = NULL */
1173 
1174      logrequest("ARRIVED AT", ZeGopher);
1175 
1176      if (!WritePipe && GS2FileSucceeded)
1177 	  GSdisplay(ZeGopher);
1178      if (!GS2FileSucceeded)
1179           unlink(tmpfilename);
1180 
1181 /*     if (!ForkOff) {
1182 	  printf("\nPress <RETURN> to continue: ");
1183 	  fflush(stdin);
1184 	  fflush(stdin);
1185 	  getchar();
1186 	  CURenter(CursesScreen);
1187      }*/
1188 
1189 
1190      if (tmpfilename!=NULL) free(tmpfilename);
1191 
1192      if (ForkOff)
1193 	  exit(-1);
1194 
1195      CURenter(CursesScreen);
1196      return;
1197 }
1198 
1199 
1200 /*
1201  * Eventually this routine should allow viewing of the movie type by
1202  * forking off a movie process to siphon the data across the net.
1203  */
1204 
1205 void
1206 do_movie(GopherStruct *ZeGopher)
1207 {
1208      showfile(ZeGopher);
1209 }
1210 
1211 
1212 /*
1213 ** Pushgopher takes a GopherThing pointer and adds it to it's stack.
1214 **
1215 ** Ick this must be fixed!
1216 */
1217 
1218 void
1219 pushgopher(GopherDirObj *ZeDir)
1220 {
1221 
1222      OldDirs[iLevel]= ZeDir;
1223      iLevel ++;
1224 }
1225 
1226 /*
1227 ** If the stack is empty, popgopher returns a -1
1228 */
1229 
1230 int
1231 popgopher(GopherDirObj **ZeDir)
1232 {
1233 
1234      if (iLevel == 0)
1235 	  return(-1);
1236 
1237      iLevel --;
1238 
1239      *ZeDir =  OldDirs[iLevel];
1240 
1241      return(0);
1242 }
1243 
1244 
1245 #ifdef VMS
1246 #include <perror.h>
1247 #else
1248 extern int h_errno;
1249 #ifndef HAVE_STRERROR
1250 extern int sys_nerr;
1251 extern char *sys_errlist[];
1252 #endif
1253 extern int  errno;
1254 #endif
1255 
1256 void check_sock(int sockfd, char *host, int port)
1257 {
1258      char DispString[WHOLELINE];
1259      char DispString2[WHOLELINE];
1260      char *DispStrings[4];
1261 
1262      /* NULL DispStrings entries here, so can override below */
1263      DispStrings[3] = NULL;
1264 
1265      if (sockfd <0) {
1266 	  sprintf(DispString,
1267 		  Gtxt("Cannot connect to host %.40s, port %d.",72),
1268 		  host, port);
1269 
1270 	  switch (sockfd) {
1271 	  case -2:
1272 	       DispStrings[2] = Gtxt("Hostname is unknown.",100);
1273 	       break;
1274 	  case -3:
1275 	       DispStrings[2] = Gtxt("Unable to allocate a socket.",175);
1276 	       break;
1277 	  case -4:
1278 #if defined(VMS) && defined(MULTINET)
1279 	       sprintf(DispString2, "%.78s.", vms_errno_string());
1280 	       DispStrings[2] = DispString2;
1281 #else
1282 #ifdef __DECC
1283 	       if (errno > 0 && errno <= 65) {
1284 #else
1285 #ifdef HAVE_STRERROR
1286                if (1) {
1287 #else
1288 	       if (errno > 0 && errno <= sys_nerr) {
1289 #endif
1290 #endif
1291 		    sprintf(DispString2, Gtxt("Connection failed: %s.",78),
1292 #if defined(VMS) || defined(HAVE_STRERROR)
1293 			    strerror(errno));
1294 #else
1295 		    sys_errlist[errno]);
1296 #endif
1297 	            DispStrings[2] = DispString2;
1298 	       } else
1299 		    DispStrings[2] =
1300 			 Gtxt("Connection to remote host failed.",79);
1301 #endif
1302 
1303 	  default:
1304 	       DispStrings[2] = Gtxt("Unknown error.",177);
1305 	  }
1306 	  DispStrings[0] = DispString;
1307 	  DispStrings[1] = "";
1308           DispStrings[3] = NULL;
1309 
1310           CURBeep(CursesScreen);
1311 
1312 	  CURDialog(CursesScreen, Gtxt("Network Error",110), DispStrings);
1313      }
1314 }
1315 
1316 
1317 BOOLEAN
1318 ReallyQuit(void)
1319 {
1320      char yesno[3];
1321 
1322      yesno[0] = 'y';
1323      yesno[1] = '\0';
1324 
1325      CURgetYesorNo(CursesScreen, Gtxt("Really quit (y/n) ?",124), yesno);
1326      if (*yesno == 'y') {
1327 	  return(TRUE);
1328      }
1329 
1330      return(FALSE);
1331 }
1332 
1333 
1334 RETSIGTYPE
1335 CleanupandExit(int exitval)
1336 {
1337      GopherDirObj *gd;
1338 #ifdef VMS
1339      extern boolean DidCleanup;
1340 #endif
1341 
1342 #ifdef SIGWINCH
1343      signal(SIGWINCH, SIG_IGN);
1344 #endif
1345      signal(SIGINT, SIG_IGN);
1346      signal(SIGQUIT, SIG_IGN);
1347      signal(SIGPIPE, SIG_IGN);
1348      signal(SIGTERM, SIG_IGN);
1349      signal(SIGHUP, SIG_IGN);
1350 
1351 
1352      if (CursesScreen)
1353 	  CURexit(CursesScreen);
1354 
1355      if (ChangedDefs)
1356 	  RCtoFile(GlobalRC);
1357 
1358      do {
1359 	  if (CurrentDir != NULL && CurrentDir != RCgetBookmarkDir(GlobalRC))
1360 	       GDdestroy(CurrentDir);
1361      }
1362      while (popgopher(&CurrentDir) != -1);
1363 
1364      if (GlobalRC) {
1365 	  gd = RCgetBookmarkDir(GlobalRC);
1366 	  if (gd != NULL)
1367 	       GDdestroy(gd);
1368      }
1369      logrequest("EXIT", NULL);
1370 #ifdef VMS
1371      DidCleanup = TRUE;
1372 #endif
1373      exit(exitval);
1374 }
1375 
1376 
1377 
1378 /**************
1379 ** This bit of code catches control-c's, it cleans up the curses stuff.
1380 */
1381 
1382 RETSIGTYPE
1383 controlc_old(int sig)
1384 {
1385 
1386 #ifdef VMS
1387      if (!CursesScreen->inCurses) {
1388           /** Reprime the signal and set flag **/
1389           if (signal(SIGINT, controlc) == SIG_ERR)
1390 	       perror("signal died:\n"), CleanupandExit(-1);
1391  	  HadVMSInt = TRUE;
1392           return;
1393      }
1394 #endif
1395 
1396      if (CurrentDir == NULL || GDgetNumitems(CurrentDir) <= 0) {
1397           CURexit(CursesScreen);
1398 	  fprintf(stderr,Gtxt("gopher: Nothing received for main menu, can't continue\n",60));
1399 	  CleanupandExit(1);
1400      }
1401 
1402      if (ReallyQuit())
1403      {
1404 	  CleanupandExit(0);
1405      }
1406      else {
1407 	  CURresize(CursesScreen);
1408 	  /* scline(-1, 1, CurrentDir); */
1409 	  /** Interrupt search, go back a level?? **/
1410      }
1411 
1412      /*
1413       * Reprime the signals...
1414       */
1415 
1416      if (signal(SIGINT, controlc) == SIG_ERR)
1417 	  perror("signal died:\n"), CleanupandExit(-1);
1418 
1419 
1420      /** Really should be siglongjmp **/
1421      longjmp(Jmpenv,1);
1422 }
1423 
1424 
1425 /**************
1426 ** This bit of code catches window size change signals
1427 **/
1428 
1429 RETSIGTYPE
1430 sizechange(int sig)
1431 {
1432      int lines, cols;
1433 
1434 #ifdef  TIOCGWINSZ
1435      static struct      winsize zewinsize;        /* 4.3 BSD window sizing */
1436 #endif
1437 
1438      lines = LINES;
1439      cols  = COLS;
1440 
1441 #ifdef  TIOCGWINSZ
1442      if (ioctl(0, TIOCGWINSZ, (char *) &zewinsize) == 0) {
1443 	  lines = zewinsize.ws_row;
1444 	  cols  = zewinsize.ws_col;
1445      } else {
1446 #endif
1447 	  /* code here to use sizes from termcap/terminfo, not yet... */
1448 	  ;
1449 #ifdef  TIOCGWINSZ
1450      }
1451 
1452      if (lines != LINES || cols != COLS) {
1453 	  LINES = lines;
1454 	  COLS  = cols;
1455 	  CURresize(CursesScreen);
1456 
1457 	  scline(-1, 1, CurrentDir);
1458      }
1459 
1460      if (signal(SIGWINCH, sizechange)==SIG_ERR)
1461 	  perror("signal died:\n"), CleanupandExit(-1);
1462 
1463 
1464 #endif
1465 
1466 }
1467 
1468 
1469 
1470 /**********
1471 **
1472 ** Set up all the global variables.
1473 **
1474 ***********/
1475 
1476 void
1477 Initialize(void)
1478 {
1479 
1480      Debugmsg("Initialize\n")
1481      GlobalRC        = RCnew();
1482 
1483      /** get defaults from the rc file **/
1484 
1485      RCfromUser(GlobalRC);
1486 
1487 
1488      /*** Set up the curses environment ***/
1489 
1490      CursesScreen = CURnew();
1491 
1492      if (strcmp(CURgetTerm(CursesScreen), "unknown")==0)
1493 	  fprintf(stderr, Gtxt("I don't understand your terminal type\n",101)),
1494 	  CleanupandExit(-1);
1495 
1496 
1497      /*** Make a signal handler for window size changes ***/
1498 
1499 #ifdef SIGWINCH
1500      CURsetSIGWINCH(CursesScreen, sizechange);
1501      if (signal(SIGWINCH, sizechange)==SIG_ERR)
1502 	  perror("signal died:\n"),
1503 	  CleanupandExit(-1);
1504 
1505 #endif
1506 
1507      if (signal(SIGINT, controlc) == SIG_ERR)
1508 	  perror("signal died:\n"),
1509 	  CleanupandExit(-1);
1510 
1511      if (signal(SIGQUIT, CleanupandExit) == SIG_ERR)
1512 	  perror("signal died:\n"),
1513 	  CleanupandExit(-1);
1514 
1515      if (signal(SIGPIPE, CleanupandExit) == SIG_ERR)
1516 	  perror("signal died:\n"),
1517 	  CleanupandExit(-1);
1518 
1519      if (signal(SIGTERM, CleanupandExit) == SIG_ERR)
1520 	  perror("signal died:\n"),
1521 	  CleanupandExit(-1);
1522 
1523      if (signal(SIGHUP, CleanupandExit) == SIG_ERR)
1524 	  perror("signal died:\n"),
1525 	  CleanupandExit(-1);
1526 
1527 
1528 
1529      /*** Init MainWindow ****/
1530      CURenter(CursesScreen);
1531 
1532 
1533      /*** Check for new global configuration file ***/
1534      if (RCisGlobalNew()) {
1535 	  int result;
1536 	  char *mess[2];
1537 	  mess[0] =  Gtxt("A new configuration is available.  Load it?",218);
1538 	  mess[1] = NULL;
1539 
1540 	  /*** Ask if the user wants to reload values from globalrc ***/
1541 	  result = CURDialog(CursesScreen, "", mess);
1542 
1543 	  if (result == 0) {
1544 	       /*** Do it...  ****/
1545 	       RCreadGlobalRC(GlobalRC);
1546 	  }
1547 	  ChangedDefs = TRUE;
1548 
1549      }
1550 }
1551 
1552 
1553 
1554 /*
1555  * This stuff will set the options in a nice way...
1556  */
1557 
1558 void
1559 SetOptions(void)
1560 {
1561      int choice, restricted = FALSE;
1562      char     *shell;
1563 
1564      static char **OptionNewApp = NULL, **OptionsMenu = NULL;
1565      static char **noyes = NULL;
1566      static char **printchoice = NULL;
1567 
1568      if (OptionNewApp == NULL) {
1569 	  OptionNewApp = (char**) malloc(sizeof(char*) * 4);
1570 	  OptionNewApp[0] = Gtxt("Content-Type Name",27);
1571 	  OptionNewApp[1] = Gtxt("Display Application",28);
1572 	  OptionNewApp[2] = Gtxt("Printing Application",29),
1573 	  OptionNewApp[3] = NULL;
1574      }
1575 
1576      if (OptionsMenu == NULL) {
1577 	  OptionsMenu = (char **) malloc(sizeof(char*) * 5);
1578 	  OptionsMenu[0] = Gtxt("General Options",30);
1579 	  OptionsMenu[1] = Gtxt("Configure Display Applications",31);
1580 	  OptionsMenu[2] = Gtxt("Configure Printing Applications",32);
1581 	  OptionsMenu[3] = Gtxt("Define New Content-Type",33);
1582 	  OptionsMenu[4] = NULL;
1583      }
1584      if (noyes == NULL) {
1585 	  noyes = (char**) malloc(sizeof(char*) * 3);
1586 	  noyes[0] = Gtxt("No",111);
1587 	  noyes[1] = Gtxt("Yes",180);
1588 	  noyes[2] = NULL;
1589      }
1590      if (printchoice == NULL) {
1591 	  printchoice = (char**) malloc(sizeof(char*) * 3);
1592 	  printchoice[0] = Gtxt("Default System Printer", 219);
1593 	  printchoice[1] = Gtxt("ANSI attached Printer", 220);
1594 	  printchoice[2] = NULL;
1595      }
1596 
1597      /*
1598       * If the user is running a restricted shell, don't allow him/her to
1599       * set applications, because he/she can specify a shell.  This is
1600       * independent of -s and -S because rsh users may have a shell.
1601       */
1602 #ifndef VMS
1603      restricted = (shell = getenv("SHELL")) == NULL
1604 	  || strcmp(shell+strlen(shell) - 3, "rsh") == 0;
1605 
1606 #endif
1607 
1608      if (SecureMode || NoShellMode || restricted) {
1609 	  CursesErrorMsg(Gtxt("Sorry, you are not allowed to set options in secure mode.",163));
1610 	  return;
1611      }
1612 
1613      /** Display Configuration Menu and allow user to choose **/
1614      choice = CURChoice(CursesScreen, Gtxt("Gopher Options",94), OptionsMenu,
1615 			Gtxt("Your Choice",181), -1);
1616 
1617      if (choice == -1)
1618 	  return;
1619 
1620      switch (choice) {
1621 
1622      case 0:
1623      {
1624 	  /** General Options **/
1625 
1626 	  Requestitem *items[3];
1627 	  Requestitem bold;
1628 	  Requestitem printer;
1629 
1630 	  bold.prompt  = Gtxt("Bold Search words in Builtin Pager",66);
1631 	  bold.thing   = CUR_CHOICE;
1632 	  bold.choices = noyes;
1633 	  bold.chooseitem = RCsearchBolding(GlobalRC);
1634 
1635 	  printer.prompt  = Gtxt("Print to",221);
1636 	  printer.thing   = CUR_CHOICE;
1637 	  printer.choices = printchoice;
1638 	  printer.chooseitem = RCuseANSIprinter(GlobalRC);
1639 
1640 
1641 	  items[0] = &bold;
1642 	  items[1] = &printer;
1643 	  items[2] = NULL;
1644 
1645 	  if (CURrequester(CursesScreen, Gtxt("General Options",30), items)==0) {
1646 	       RCsetSearchBolding(GlobalRC, items[0]->chooseitem);
1647 	       RCsetANSIprinter(GlobalRC,  items[1]->chooseitem);
1648 	       ChangedDefs = TRUE;
1649 	  }
1650 
1651 	  break;
1652      }
1653 
1654      case 1:
1655      case 2:
1656      {
1657 	  /** Display/Print Applications **/
1658 
1659 	  char     **gplusviews;
1660 	  int      i;
1661 	  int      numoptions;
1662 	  RCMapObj *rcm;
1663 	  char     **Responses;
1664 	  char     *zetitle;
1665 
1666 	  numoptions = RCMAgetNumEntries(GlobalRC->commands);
1667 	  gplusviews = (char **) malloc(sizeof(char *) * (numoptions + 1));
1668 	  Responses = (char **) malloc(sizeof(char *) * (numoptions + 1));
1669 
1670 	  for (i = 0 ; i < numoptions; i++) {
1671 	       rcm = RCMAgetEntry(GlobalRC->commands, i);
1672 	       gplusviews[i] = RCMgetView(rcm);
1673 	       Responses[i] = (char *) malloc(sizeof(char) * MAXSTR);
1674 	       if (choice == 1)
1675 		    strcpy(Responses[i], RCMgetDisplaycmd(rcm));
1676 	       else
1677 		    strcpy(Responses[i], RCMgetPrintcmd(rcm));
1678 	  }
1679 	  gplusviews[i] = NULL;
1680 	  Responses[i] = NULL;
1681 
1682 	  if (choice == 1)
1683 	       zetitle = Gtxt("Configure Display Applications",31);
1684 	  else
1685 	       zetitle = Gtxt("Configure Printing Applications",32);
1686 
1687 	  if (CURRequest(CursesScreen, zetitle, gplusviews,
1688 			 Responses) == 0) {
1689 	       char tmpstr[512];
1690 
1691 	       while (i-- > 0) {
1692 		    rcm = RCMAgetEntry(GlobalRC->commands, i);
1693 		    if (choice == 1)
1694 			 /** Display Applications **/
1695 			 sprintf(tmpstr, "%s,%s,%s", gplusviews[i],
1696 				 Responses[i], RCMgetPrintcmd(rcm));
1697 		    else
1698 			 /** Print Applications **/
1699 			 sprintf(tmpstr, "%s,%s,%s", gplusviews[i],
1700 				 RCMgetDisplaycmd(rcm), Responses[i]);
1701 
1702 		    RCMAfromLine(GlobalRC->commands, tmpstr);
1703 	       }
1704 	       ChangedDefs = TRUE;
1705 	  }
1706 
1707 	  for (i = 0 ; i <= numoptions; i++)
1708 	       if (Responses[i])
1709 		    free(Responses[i]);
1710 	  free(Responses);
1711 	  free(gplusviews);
1712 
1713 	  break;
1714      }
1715 
1716      case 3:
1717      {
1718 	  /** Add a new Content-Type and define **/
1719 	  /** its Display and Printing applications **/
1720 
1721 	  int  i;
1722 	  char *Responses[4];
1723 
1724 	  for (i = 0; i < 3; i++) {
1725 	       Responses[i] = (char *) malloc(sizeof(char) * MAXSTR);
1726 	       Responses[i][0] = '\0';
1727 	  }
1728 	  Responses[3] = NULL;
1729 
1730 	  if ((CURRequest(CursesScreen, Gtxt("Define New Content-Type",33),
1731 	       OptionNewApp, Responses) == 0) && (*Responses[0] != '\0')) {
1732 	       char tmpstr[512];
1733 
1734 	       sprintf(tmpstr, "%s,%s,%s", Responses[0], Responses[1],
1735 		       Responses[2]);
1736 	       RCMAfromLine(GlobalRC->commands, tmpstr);
1737 	       ChangedDefs = TRUE;
1738 	  }
1739 
1740 	  for (i = 0; i < 3; i++)
1741 	       free(Responses[i]);
1742           break;
1743      }
1744 
1745      }  /** End of switch on option type **/
1746 }
1747 
1748 
1749 
1750 static char *search_string = NULL;
1751 
1752 int
1753 main(int argc, char **argv)
1754 {
1755      BOOLEAN bDone = FALSE;
1756      char sTmp[80];
1757      GopherStruct *RootGophers[2];
1758      int numhosts = 2;
1759      int TypedChar;
1760      /*** for getopt processing ***/
1761      int c;
1762      extern char *optarg;
1763      extern int optind;
1764      int errflag =0, i;
1765      int curitem;
1766      int Garbled = TRUE;
1767      boolean Bkmarksfirst = FALSE;
1768      boolean startAtHome = TRUE;
1769 
1770 
1771      /*** Initialize international languages ***/
1772      Gtxtlocale(LC_ALL, "");
1773 
1774 
1775 #if defined(GINTERNATIONAL) && !defined(VMS)
1776 
1777 #ifdef LC_CTYPE
1778      Gtxtlocale(LC_CTYPE, "iso_8859_1");
1779 #endif
1780 
1781 #ifdef LC_MESSAGES
1782      if (getenv("LC_MESSAGES") != NULL)
1783 	 Gtxtlocale(LC_MESSAGES, getenv("LC_MESSAGES"));
1784 #endif /* LC_MESSAGES */
1785 
1786 #ifndef __osf__
1787      Gtxtopen("gopher", 0);  /** OSF seems to mess this up... **/
1788 #endif
1789 
1790      if (Gcatd == (nl_catd) -1) {
1791 	  /** Try finding it elsewhere.. **/
1792 	  char fname[256];
1793 	  if (getenv("LC_MESSAGES") != NULL) {
1794 	       sprintf(fname, "%s/gophernls/%s.cat", GOPHERLIB, getenv("LC_MESSAGES"));
1795 	       Gtxtopen(fname, 0);
1796 	  }
1797      }
1798 #endif	  /* GINTERNATIONAL & !VMS */
1799 
1800 #ifdef VMS
1801      argv[0] = "gopher";
1802 #endif
1803 
1804      for (i=0; i<2; i++) {
1805 	  RootGophers[i] = GSnew();
1806 	  GSsetType (RootGophers[i], A_DIRECTORY);
1807 	  GSsetPath (RootGophers[i],"");
1808 	  GSsetGplus(RootGophers[i], TRUE);
1809      }
1810 
1811      /** Should generalize this to >2 hosts .... Sigh... ***/
1812      GSsetHost (RootGophers[0], CLIENT1_HOST);
1813      GSsetPath (RootGophers[0], CLIENT1_PATH);
1814      GSsetPort (RootGophers[0], CLIENT1_PORT);
1815 
1816      GSsetHost (RootGophers[1], CLIENT2_HOST);
1817      GSsetHost (RootGophers[1], CLIENT2_PATH);
1818      GSsetPort (RootGophers[1], CLIENT2_PORT);
1819 
1820      if (CLIENT2_PORT == 0)
1821 	  numhosts = 1;
1822 
1823      sTmp[0] = '\0';
1824 
1825 
1826      while ((c = getopt(argc, argv, "DsSbrp:t:T:i:")) != -1)
1827 	  switch (c) {
1828 	  case 's':
1829 	       SecureMode = TRUE;
1830 	       break;
1831 	  case 'S':	/* Similar to secure, but assumes own Unix account */
1832 			/* No change in behaviour if this not set */
1833 		NoShellMode = TRUE;
1834 		break;
1835 	  case 'i':
1836 	       search_string = optarg;
1837 	       break;
1838 
1839 	  case 'p':
1840 	       GSsetPath(RootGophers[0], optarg);
1841 	       GSsetPath(RootGophers[1], optarg);
1842 	       GSsetGplus(RootGophers[0], FALSE);
1843 	       GSsetGplus(RootGophers[1], FALSE);
1844 
1845 	       startAtHome = FALSE;
1846 	       break;
1847 	  case 'T':
1848 	       GSsetType(RootGophers[0], *optarg);
1849 	       GSsetType(RootGophers[1], *optarg);
1850 	       if (optarg[1] == '+') {
1851 		    GSsetGplus(RootGophers[0], TRUE);
1852 		    GSsetGplus(RootGophers[1], TRUE);
1853 	       } else if (optarg[1] == '?') {
1854 		    GSsetAsk(RootGophers[0], TRUE);
1855 		    GSsetAsk(RootGophers[1], TRUE);
1856 	       }
1857 	       startAtHome = FALSE;
1858 	       break;
1859 	  case 't':
1860 	       GSsetTitle(RootGophers[0], optarg);
1861 	       GSsetTitle(RootGophers[1], optarg);
1862 	       break;
1863 	  case 'D':
1864 	       DEBUG = TRUE;
1865 	       Debugmsg("gopher starting - debug on\n")
1866 	       break;
1867 	  case 'r':
1868 	       RemoteUser = TRUE;
1869 	       break;
1870 	  case 'b':
1871 	       Bkmarksfirst = TRUE;
1872 	       break;
1873 	  case '?':
1874 	       errflag++;
1875 	  }
1876 
1877 
1878      if (errflag) {
1879 
1880 	  fprintf(stderr, Gtxt("Usage: %s [-sSbDr] [-T type] [-p path] [-t title] [hostname port]+\n",9), argv[0]);
1881 	  fprintf(stderr,
1882 	    Gtxt("     -s      secure mode, users without own account\n",10));
1883 	  fprintf(stderr,
1884 	    Gtxt("     -S      secure mode, users with own account\n",11));
1885 	  fprintf(stderr,
1886 	    Gtxt("     -p path specify path to initial item\n",12));
1887 	  fprintf(stderr,
1888             Gtxt("     -T type Type of initial item\n",13));
1889 	  fprintf(stderr,
1890             Gtxt("     -i      Search argument (for -T 7)\n",14));
1891 	  fprintf(stderr,
1892 	    Gtxt("     -b      Bookmarks first\n",15));
1893 	  fprintf(stderr,
1894             Gtxt("     -r      Remote user\n",16));
1895 	  fprintf(stderr,
1896 	    Gtxt("     -D      Debug mode\n",17));
1897 	  CleanupandExit(-1);
1898      }
1899 
1900      /**** Get host #1 from the command line ****/
1901 
1902      if (optind < argc) {
1903 
1904 	  if (strchr(argv[optind], ':') != NULL) {
1905 	       GopherObj *tempGopher = GSnew();
1906 	       int doneflags = 0;
1907 
1908 	       doneflags = GSfromURL(tempGopher, argv[optind],
1909 	       			     AFTP_HOST, AFTP_PORT, doneflags);
1910 	       GScpy(RootGophers[0], tempGopher);
1911 	       GScpy(RootGophers[1], tempGopher);
1912 	       GSdestroy(tempGopher);
1913 	       startAtHome = FALSE;
1914 	       optind++;
1915 	  } else {
1916 	       GSsetPort(RootGophers[0], 70);  /* Just in case the default */
1917 	       GSsetPort(RootGophers[1], 70);  /* isn't 70 */
1918 
1919                /* Restore blank path if a host was specified but -p was not */
1920 
1921 	       if (startAtHome)
1922 	           GSsetPath(RootGophers[0], "");
1923 
1924 	       GSsetHost(RootGophers[0], argv[optind]);
1925 	       GSsetHost(RootGophers[1], "");  /** Nuke the 2nd alternative host **/
1926 	       GSsetPort(RootGophers[1], 0);
1927 	       numhosts = 1;
1928 	       startAtHome = FALSE;
1929 	       optind++;
1930 	  }
1931      }
1932      if (optind < argc) {
1933 	  GSsetPort(RootGophers[0], atoi(argv[optind]));
1934 	  optind++;
1935      }
1936 
1937      /*** Get host #2 from the command line... ***/
1938      if (optind < argc) {
1939 	  GSsetHost(RootGophers[1], argv[optind]);
1940 	  numhosts = 2;
1941 	  optind++;
1942      }
1943      if (optind < argc) {
1944 	  GSsetPort(RootGophers[1], atoi(argv[optind]));
1945 	  optind++;
1946      }
1947 
1948      /*** If the title hasn't been set, then add a default title **/
1949      if (GSgetTitle(RootGophers[0]) == NULL) {
1950 	  sprintf(sTmp, Gtxt("Home Gopher server: %.59s",97),
1951 		  GSgetHost(RootGophers[0]));
1952 	  GSsetTitle(RootGophers[0], sTmp);
1953 	  sprintf(sTmp, Gtxt("Home Gopher server: %.59s",97),
1954 		  GSgetHost(RootGophers[1]));
1955 	  GSsetTitle(RootGophers[1], sTmp);
1956      }
1957 
1958      /*** Set up global variables, etc. ***/
1959 
1960 #ifdef SOCKS
1961      SOCKSinit(argv[0]);
1962 #endif
1963 
1964      Initialize();
1965 
1966      if (startAtHome && (RCgetHome(GlobalRC) != NULL)) {
1967 	  GScpy(RootGophers[0], RCgetHome(GlobalRC));
1968 	  GScpy(RootGophers[1], RCgetHome(GlobalRC));
1969      }
1970 
1971      if (Bkmarksfirst) {
1972 	  CurrentDir = RCgetBookmarkDir(GlobalRC);
1973 
1974      } else {
1975 	  int rnum;
1976 
1977 	  srand(time(NULL));
1978 	  rnum = rand() % numhosts;
1979 	  process_request(RootGophers[rnum]);
1980 
1981           /* just process the command line entry */
1982           if( GSgetType(RootGophers[0]) != A_DIRECTORY &&
1983 	      GSgetType(RootGophers[0]) != A_INDEX) {
1984 	       refresh();
1985 	       if( GSgetLocalFile(RootGophers[0]) != NULL)
1986 	           unlink(GSgetLocalFile(RootGophers[0]));
1987 	       CleanupandExit(0);
1988           }
1989      }
1990 
1991 #ifdef DEBUGGING
1992 	if (CurrentDir == NULL)  {
1993 	     Debugmsg("CurrentDir=NULL\n");
1994 	}
1995 	else
1996 	     if (GDgetNumitems(CurrentDir) <=0) {
1997 		  Debugmsg("GDgetNumitems<=0\n");
1998 	     }
1999 #endif
2000 
2001      if (CurrentDir == NULL || GDgetNumitems(CurrentDir) <= 0) {
2002 	  /*
2003 	   * We didn't get anything from that gopher server.  Either
2004 	   * it is down, doesn't exist, or is empty or otherwise
2005 	   * busted.
2006            *
2007            * Try any alternative hosts that are available..
2008 	   */
2009 
2010 	  while (optind < argc && CurrentDir == NULL) {
2011 	       Debugmsg("Trying alternate site\n");
2012 	       GSsetHost(RootGophers[0], argv[optind]);
2013 	       optind++;
2014 		   if (optind < argc) {
2015 			   GSsetPort(RootGophers[0], atoi(argv[optind]));
2016 			   optind++;
2017 		   }
2018 	       process_request(RootGophers[0]);
2019 	  }
2020      }
2021 
2022      if (CurrentDir == NULL || GDgetNumitems(CurrentDir) <= 0) {
2023 #ifdef DEBUGGING
2024 	if (CurrentDir == NULL)  {
2025 	     Debugmsg("B: CurrentDir=NULL\n");
2026 	}
2027 	else if (GDgetNumitems(CurrentDir) <=0) {
2028 	     Debugmsg("B: GDgetNumitems<=0\n");
2029 	}
2030 #endif
2031 
2032 	CURexit(CursesScreen);
2033 	fprintf(stderr,
2034 		Gtxt("%s: Nothing received for main menu, can't continue\n",188)
2035 		, argv[0]);
2036 	CleanupandExit(1);
2037    }
2038 
2039      while (bDone == FALSE)
2040      {
2041 	  GetMenu(CurrentDir, &TypedChar, Garbled);
2042 	  Garbled = TRUE;
2043 #ifdef DESCRIBE_GOPHER
2044 #undef DESCRIBE_GOPHER
2045 #endif
2046 #ifdef DESCRIBE_GOPHER_GRIPE
2047 #define	DESCRIBE_GOPHER(a,b)	describe_gopher((a),(b), NULL)
2048 #else
2049 #define	DESCRIBE_GOPHER(a,b)	describe_gopher((a),(b))
2050 #endif
2051 
2052 	  switch(TypedChar)
2053 	  {
2054 	  case '\r':
2055 	  case '\n':
2056 	       /*** Select the designated item ***/
2057 		   if ((curitem = GDgetCurrentItem(CurrentDir)) <= 0)
2058 			   curitem = 1;
2059 	       if (process_request(GDgetEntry(CurrentDir, curitem-1))==1)
2060 		    ;  /** Don't do anything if we failed, I guess **/
2061 #if defined(VMS) && defined(A_LANGUAGE)
2062 	       if (CurrentDir == setlocale_LangDir) {
2063 		    sprintf(sTmp, Gtxt("Home Gopher server: %.59s",97),
2064 						    GSgetHost(RootGophers[0]));
2065 		    GDsetTitle(OldDirs[0], sTmp);
2066 		    GSsetTitle(RootGophers[0], sTmp);
2067 		    Garbled = TRUE;
2068 		    goto Backup_Resetlocale;
2069 	       }
2070 #endif
2071 	       break;
2072 
2073 	  case '\0':
2074 	       /*** What the heck? ***/
2075 	       CursesErrorMsg("Strange Error occurred!");
2076 	       break;
2077 
2078 	  case 'u':
2079 	  case 'U':
2080 #ifdef AUTOEXITONU
2081 	  case 'q':
2082 #endif
2083 #if defined(VMS) && defined(A_LANGUAGE)
2084 	  Backup_Resetlocale:
2085 #endif
2086 	  {
2087 	       GopherDirObj *tempGdir;
2088 
2089 	       logrequest("PREVIOUS DIRECTORY", NULL);
2090 
2091 	       /*** Don't highlight texts which aren't search hits ***/
2092 	       Searchstring = NULL;
2093 
2094 	       /*** Go up a directory level ***/
2095 	       tempGdir = CurrentDir;
2096 	       /** Don't destroy root level directory, or bookmarks **/
2097 	       if (popgopher(&CurrentDir)==0 && tempGdir != CurrentDir) {
2098 		    if (tempGdir != RCgetBookmarkDir(GlobalRC))
2099 #if defined(VMS) && defined(A_LANGUAGE)
2100 		      if (tempGdir != setlocale_LangDir)
2101 #endif
2102 			 GDdestroy(tempGdir);
2103 #ifdef AUTOEXITONU
2104 	       } else {
2105 	       		bDone = TRUE;
2106 	       		CURexit(CursesScreen);
2107 #else
2108 	       } else {
2109 		    Garbled = FALSE;
2110 #endif
2111 	       }
2112 	  }
2113 	       break;
2114 
2115 	  case 'g':
2116 	       /*** Gripe! ***/
2117 #ifdef NOGRIPE_SECURE
2118 	       if (SecureMode || NoShellMode) {
2119 		    CursesErrorMsg(Gtxt
2120 				   ("Sorry, can't submit gripes in securemode",
2121 							227));
2122 		    break;
2123 	       }
2124 #endif
2125 	       Gripe(GDgetEntry(CurrentDir,GDgetCurrentItem(CurrentDir)-1));
2126 	       break;
2127 
2128 	  case 's':  /*** Save a file directly ***/
2129 	       if (SecureMode || NoShellMode) {
2130 		    CursesErrorMsg(Gtxt("Sorry, can't save files with this account",152));
2131 		    break;
2132 	       }
2133 	       Save_file(GDgetEntry(CurrentDir,GDgetCurrentItem(CurrentDir)-1), NULL, NULL);
2134 	       break;
2135 
2136           case 'S':   /*** Save list of menu items (objects) to a file ***/
2137 	       if (SecureMode || NoShellMode) {
2138 		    CursesErrorMsg(Gtxt("Sorry, can't save files with this account",152));
2139 		    break;
2140 	       }
2141 	       Save_list(CurrentDir);
2142 	       break;
2143 
2144 	  case 'D':
2145 	       Download_file(GDgetEntry(CurrentDir,GDgetCurrentItem(CurrentDir)-1));
2146 	       break;
2147 
2148 	  case 'o': /** Open a new session **/
2149 	  {
2150 	       GopherObj *tempgs = GSnew();
2151 	       char *prompts[4];
2152 	       char *responses[4];
2153 	       int i;
2154 
2155 	       if (SecureMode) {
2156 		    CursesErrorMsg(Gtxt("Sorry, you're not allowed to do this",164));
2157 		    break;
2158 	       }
2159 
2160 	       prompts[0]=Gtxt("Hostname",99);
2161 	       prompts[1]=Gtxt("Port",118);
2162 	       prompts[2]=Gtxt("Selector (Optional)",144);
2163 	       prompts[3]=NULL;
2164 
2165 	       for (i=0; i<3; i++) {
2166 		    responses[i] = (char*)malloc(COLS*sizeof(char));
2167 		    *responses[i] = '\0';
2168 	       }
2169 	       strcpy(responses[1], "70");
2170 
2171 	       responses[3] = NULL;
2172 
2173 	       if ((CURRequest(CursesScreen,
2174 			       Gtxt("Connect to a new Gopher Server",75),
2175 			       prompts, responses) != -1)
2176 		   && *responses[0]!='\0') {
2177 
2178 		    GSsetType(tempgs, A_DIRECTORY);
2179 		    GSsetTitle(tempgs, responses[0]);
2180 		    GSsetHost(tempgs, responses[0]);
2181 		    GSsetPort(tempgs, atoi(responses[1]));
2182 		    GSsetPath(tempgs, responses[2]);
2183 
2184 		    Load_Dir(tempgs);
2185 	       }
2186 
2187 	       GSdestroy(tempgs);
2188 	       for (i=0; i<3; i++)
2189 		    free(responses[i]);
2190 	       break;
2191 	  }
2192 
2193 
2194 	  case 'w': /** Open a new session from a URL **/
2195 	  {
2196 	       GopherObj *tempgs = GSnew();
2197 	       char *prompt[2];
2198 	       char *response[2];
2199 	       int doneflags = 0;
2200 
2201 	       if (SecureMode) {
2202 		    CursesErrorMsg("Sorry, you're not allowed to do this");
2203 		    break;
2204 	       }
2205 
2206 	       prompt[0]=
2207 	           "http, gopher, ftp, telnet or tn3270 URL to connect to:";
2208 	       prompt[1]=NULL;
2209 
2210 	        response[0] = (char*)malloc(COLS*sizeof(char));
2211 	       *response[0] = '\0';
2212 		response[1] = NULL;
2213 
2214 	       if ((CURRequest(CursesScreen, "Connect to a new server",
2215 			  prompt, response) != -1) && *response[0]!='\0') {
2216 
2217 		    doneflags = GSfromURL(tempgs, response[0],
2218 		    			  AFTP_HOST, AFTP_PORT, doneflags);
2219 		    if (doneflags & G_HOST) {
2220 			 if (!(doneflags & G_PATH))
2221 			      GSsetPath(tempgs, "");
2222 			 if (!(doneflags & G_TYPE))
2223 			      GSsetType(tempgs, A_DIRECTORY);
2224 			 if (!(doneflags & G_NAME))
2225 			      GSsetTitle(tempgs, response[0]);
2226 
2227 			 process_request(tempgs);
2228 		    }
2229 	       }
2230 
2231 	       GSdestroy(tempgs);
2232 	       free(response[0]);
2233 	       break;
2234 	  }
2235 
2236 	  case 'f': /** Open an aFTP session **/
2237 	  {
2238 	       GopherObj *tempgs = GSnew();
2239 	       char *prompts[3];
2240 	       char *responses[3];
2241 	       char FTPpath[256];
2242 	       int i;
2243 
2244 	       if (SecureMode) {
2245 		    CursesErrorMsg(Gtxt("Sorry, you're not allowed to do this",164));
2246 		    break;
2247 	       }
2248 
2249 	       prompts[0]=Gtxt("anonymous FTP Host",187);
2250 	       prompts[1]=Gtxt("Selector (Optional)",144);
2251 	       prompts[2]=NULL;
2252 
2253 	       for (i=0; i<2; i++) {
2254 		    responses[i] = (char *) malloc(sizeof(char)*COLS);
2255 		    *responses[i] = '\0';
2256 	       }
2257 
2258 	       responses[2] = NULL;
2259 
2260 	       if ((CURRequest(CursesScreen,
2261 		   Gtxt("Connect to an anonymous FTP Server via the Gopher gateway",76),
2262 			   prompts, responses) == -1) || *responses[0] == '\0')
2263 		    break;
2264 
2265 	       GSsetType(tempgs, A_DIRECTORY);
2266 	       GSsetTitle(tempgs, responses[0]);
2267 	       GSsetHost(tempgs, AFTP_HOST);
2268 	       GSsetPort(tempgs, AFTP_PORT);
2269 	       if (responses[1][strlen(responses[1])-1] != '/')
2270 	            strcat(responses[1], "/");
2271 	       sprintf(FTPpath, "ftp:%s@%s%s", responses[0],
2272 		              (*responses[1] == '/') ? "" : "/", responses[1]);
2273 	       GSsetPath(tempgs, FTPpath);
2274 
2275 	       Load_Dir(tempgs);
2276 
2277 	       GSdestroy(tempgs);
2278 	       for (i=0; i<2; i++)
2279 		    free(responses[i]);
2280 	       break;
2281 	  }
2282 
2283 	  case 'r':  /** Go to root menu of current item **/
2284 	  {
2285 	       char GSType;
2286 	       GopherObj *tempgs;
2287 
2288 	       if (SecureMode) {
2289 		    CursesErrorMsg(Gtxt("Sorry, you're not allowed to do this",164));
2290 		    break;
2291 	       }
2292 
2293 		   if ((curitem = GDgetCurrentItem(CurrentDir)) <= 0)
2294 			   curitem = 1;
2295 	       GSType = GSgetType(GDgetEntry(CurrentDir, curitem-1));
2296 
2297 	       tempgs = GSnew();
2298 
2299 	       /** These Gopher objects don't necessarily point
2300 		   to machines running Gopher servers **/
2301 	       if (GSType == A_CSO || GSType == A_TELNET ||
2302 		   GSType == A_TN3270 || GSType == A_ERROR ||
2303 		   GSType == A_INFO) {
2304 
2305 		    /** Try the parent instead **/
2306 		    if(GDgetLocation(CurrentDir) != NULL)
2307 			 GScpy(tempgs, GDgetLocation(CurrentDir));
2308 		    else
2309 			 GScpy(tempgs, GDgetEntry(OldDirs[iLevel-1],
2310 				     GDgetCurrentItem(OldDirs[iLevel-1])-1));
2311 	       }
2312 	       else
2313 		    if ((curitem = GDgetCurrentItem(OldDirs[iLevel-1])) <= 0)
2314 			 curitem = 1;
2315 		    GScpy(tempgs, GDgetEntry(CurrentDir,
2316 			  GDgetCurrentItem(CurrentDir)-1));
2317 
2318 	       GSsetType(tempgs, A_DIRECTORY);
2319 	       GSsetTitle(tempgs, Gtxt("Root menu: ",135));
2320 	       STRcat(tempgs->Title, GSgetHost(tempgs));
2321 	       GSsetPath(tempgs, NULL);
2322 
2323 	       Load_Dir(tempgs);
2324 	       GSdestroy(tempgs);
2325 	       break;
2326 	  }
2327 
2328 	  case 'R':  /** Go to root menu of current menu **/
2329 	  {
2330 	       GopherObj *tempgs;
2331 
2332 	       if (SecureMode) {
2333 		    CursesErrorMsg(Gtxt("Sorry, you're not allowed to do this",164));
2334 		    break;
2335 	       }
2336 
2337 	       if (iLevel == 0) {
2338 		    Garbled = FALSE;
2339 		    break;
2340 	       }
2341 
2342 	       tempgs = GSnew();
2343 
2344 	       if (GDgetLocation(CurrentDir) != NULL)
2345 		    GScpy(tempgs, GDgetLocation(CurrentDir));
2346 	       else
2347 		    GScpy(tempgs, GDgetEntry(OldDirs[iLevel-1],
2348 				   GDgetCurrentItem(OldDirs[iLevel-1])-1));
2349 
2350 	       GSsetType(tempgs, A_DIRECTORY);
2351 	       GSsetTitle(tempgs, Gtxt("Root menu: ",135));
2352 	       STRcat(tempgs->Title, GSgetHost(tempgs));
2353 	       GSsetPath(tempgs, NULL);
2354 
2355 	       Load_Dir(tempgs);
2356 	       GSdestroy(tempgs);
2357 	       break;
2358 	  }
2359 
2360 	  case 'v':  /** View bookmark list **/
2361 	  {
2362 	       logrequest("VIEW BOOKMARKS", NULL);
2363 
2364 	       if (RCgetBookmarkDir(GlobalRC) == NULL) {
2365 		    CursesErrorMsg(Gtxt("No bookmarks are defined",112));
2366 		    break;
2367 	       }
2368 
2369 	       /** Don't do anything if we are already viewing bookmarks **/
2370 	       if (CurrentDir == RCgetBookmarkDir(GlobalRC)) {
2371 		    Garbled = FALSE;
2372 		    break;
2373 	       }
2374 
2375 	       /** Don't push an empty gopher directory... **/
2376 	       if (CurrentDir != NULL)
2377 		    pushgopher(CurrentDir);
2378 
2379 	       CurrentDir = RCgetBookmarkDir(GlobalRC);
2380 
2381 	       break;
2382 	  }
2383 
2384 	  case 'a': /** add current item as a bookmark **/
2385 	  {
2386 	       GopherObj *tmpgs;
2387 	       char newtitle[256];
2388 
2389 	       if (RCgetBookmarkDir(GlobalRC) == NULL) {
2390  		    RCsetBookmarkDir(GlobalRC,GDnew(32));
2391 		    GDsetTitle(RCgetBookmarkDir(GlobalRC), Gtxt("Bookmarks",67));
2392 	       }
2393 
2394 	       tmpgs = GSnew();
2395 	       GScpy(tmpgs, GDgetEntry(CurrentDir, GDgetCurrentItem(CurrentDir)-1));
2396 
2397 	       strcpy(newtitle, GSgetTitle(tmpgs));
2398 	       if (CURGetOneOption(CursesScreen, newtitle,
2399 	       		   Gtxt("Name for this bookmark:",109), newtitle)==0) {
2400 		    if (*newtitle != '\0') {
2401 			 GSsetTitle(tmpgs, newtitle);
2402 			 GDaddGS(RCgetBookmarkDir(GlobalRC), tmpgs);
2403 			 ChangedDefs = TRUE;
2404 		    }
2405 	       }
2406 
2407 	       logrequest("ADD BOOKMARK", tmpgs);
2408 
2409                /* Clear the local file name so the file isn't deleted */
2410                GSsetLocalFile(tmpgs, NULL);
2411 
2412 	       GSdestroy(tmpgs);
2413 
2414 	       break;
2415 	  }
2416 
2417 	  case 'A': /*** Add current directory/search as a bookmark **/
2418 	  {
2419 	       GopherObj *tmpgs;
2420 	       char newtitle[256];
2421 		   char *tmps;
2422 
2423 	       if (RCgetBookmarkDir(GlobalRC) == NULL) {
2424 		    RCsetBookmarkDir(GlobalRC,GDnew(32));
2425 		    GDsetTitle(RCgetBookmarkDir(GlobalRC), Gtxt("Bookmarks",67));
2426 	       }
2427 
2428 	       if (CurrentDir == RCgetBookmarkDir(GlobalRC)) {
2429 		    CursesErrorMsg(Gtxt("Sorry, can't make a bookmark of bookmarks",151));
2430 		    break;
2431 	       }
2432 
2433 	       tmpgs = GSnew();
2434 	       if (GDgetLocation(CurrentDir) != NULL)
2435 		    GScpy(tmpgs, GDgetLocation(CurrentDir));
2436 	       else if (iLevel == 0)
2437 		    GScpy(tmpgs, RootGophers[0]);
2438 	       else
2439 		    GScpy(tmpgs, GDgetEntry(OldDirs[iLevel-1],
2440 					    GDgetCurrentItem(OldDirs[iLevel-1])-1));
2441 
2442 		   if ((GSgetType(tmpgs) == '7') &&
2443 			   ((tmps = GSgetPath(tmpgs)) == NULL ||
2444 				*tmps == '\0' ||
2445 				Searchstring == NULL ||
2446 				*Searchstring== '\0')) {
2447 			   CursesErrorMsg("Sorry, can't make a bookmark of this search");
2448 			   GSdestroy(tmpgs);
2449 			   break;
2450            }
2451 	       strcpy(newtitle, GDgetTitle(CurrentDir));
2452 	       if (CURGetOneOption(CursesScreen, newtitle,
2453 	       		Gtxt("Name for this bookmark:",109), newtitle)==0) {
2454 
2455 		    if (*newtitle != '\0') {
2456 			 GSsetTitle(tmpgs, newtitle);
2457 
2458 			 /*** Freeze the search, if there was one. ***/
2459 			 if (GSgetType(tmpgs) == '7') {
2460 			      char ickypath[512];
2461 			      strcpy(ickypath, GSgetPath(tmpgs));
2462 			      strcat(ickypath, "\t");
2463 			      strcat(ickypath, Searchstring);
2464 			      GSsetPath(tmpgs, ickypath);
2465 			      GSsetType(tmpgs, '1');
2466 			 }
2467 
2468 			 GDaddGS(RCgetBookmarkDir(GlobalRC), tmpgs);
2469 			 ChangedDefs = TRUE;
2470 		    }
2471 	       }
2472 	       logrequest("ADD BOOKMARK", tmpgs);
2473 
2474 	       GSdestroy(tmpgs);
2475 	       break;
2476 	  }
2477 
2478 	  case 'd':  /*** Delete a bookmark ***/
2479 	  {
2480 	       GopherDirObj *tempgd;
2481 
2482 
2483 #ifdef DELETE_BOOKMARKS_ONLY
2484               if (CurrentDir != RCgetBookmarkDir(GlobalRC)) {
2485                    CursesErrorMsg(Gtxt("The 'd'elete command is only for bookmarks.",189));
2486                   break;
2487              }
2488 #endif
2489  	       if (CurrentDir != RCgetBookmarkDir(GlobalRC)) {
2490 		    Download_file(GDgetEntry(CurrentDir,GDgetCurrentItem(CurrentDir)-1));
2491 		    break;
2492 	       }
2493 
2494 
2495 	       logrequest("DELETE BOOKMARK", GDgetEntry(CurrentDir, GDgetCurrentItem(CurrentDir)-1));
2496 
2497 	       if (GDgetNumitems(CurrentDir) == 1)
2498 		    tempgd = NULL;
2499 	       else
2500 		    tempgd=GDdeleteGS(CurrentDir,(GDgetCurrentItem(CurrentDir)-1));
2501 
2502 	       if (CurrentDir == RCgetBookmarkDir(GlobalRC))
2503 		    RCsetBookmarkDir(GlobalRC, tempgd);
2504 
2505 	       if (tempgd == NULL) {
2506 		    Debugmsg("Last item - pip up a directory")
2507 		    tempgd = CurrentDir;
2508 		    if (popgopher(&CurrentDir)==0 && tempgd != CurrentDir)
2509 			 GDdestroy(tempgd);
2510 		    else {
2511 			 CursesErrorMsg(Gtxt("Sorry, can't delete top level directory.",149));
2512 			 scline(-1, 1, CurrentDir);
2513 		    }
2514 		    ChangedDefs = TRUE;
2515 	       } else {
2516 		     CurrentDir = tempgd;
2517 
2518 		     ChangedDefs = TRUE;
2519 	       }
2520 	       break;
2521 	  }
2522 
2523 #if defined(VMS) && defined(A_LANGUAGE)
2524 	  case 'L':  /** add language change	**/
2525 	       if (CurrentDir == setlocale_LangDir)
2526 	            break;
2527 	       if (setlocale_LangDir== NULL) {
2528  		    CursesErrorMsg(Gtxt("Sorry, no language choice available",
2529 					239));
2530 	       }
2531 	       else
2532 	       {
2533 		   /** Don't push an empty gopher directory... **/
2534 		   if (CurrentDir != NULL)
2535 			pushgopher(CurrentDir);
2536 
2537 		   CurrentDir = setlocale_LangDir;
2538 	       }
2539 	       break;
2540 #endif
2541 
2542 	  case '$':
2543 	  case '!':
2544 #ifdef NEVERSPAWN
2545 	       CursesErrorMsg(Gtxt("Sorry, can't spawn in with this account",153));
2546 	       break;
2547 #else
2548 	       if (SecureMode || NoShellMode) {
2549 		    CursesErrorMsg(Gtxt("Sorry, can't spawn in with this account",153));
2550 		    break;
2551 	       }
2552 	       /*** Spawn to shell or DCL ***/
2553 	       CURexit(CursesScreen);
2554 #ifdef VMS
2555 	       printf(Gtxt("Spawning DCL subprocess.  Logout to return to Gopher.\n",169));
2556 	       fflush(stdout);
2557 	       i = FIOsystem("");
2558 #else
2559 	       printf(Gtxt("Spawning your default shell.  Type 'exit' to return to Gopher.\n\n",170));
2560 	       fflush(stdout);
2561 	       i = FIOsystem(getenv("SHELL"));
2562 #endif
2563 	       CURenter(CursesScreen);
2564 	       if (i < 0)
2565 #ifdef VMS
2566 		    CursesErrorMsg(Gtxt("Spawn to DCL failed!",167));
2567 #else
2568 		    CursesErrorMsg(Gtxt("Spawn to default shell failed!",168));
2569 #endif
2570 #endif	  /* NEVERSPAWN */
2571 	       break;
2572 
2573 
2574 	       /*** Pop back to the main menu ***/
2575 	  case 'M':
2576 	  case 'm':
2577 	  {
2578 	       GopherDirObj *tempGdir;
2579 
2580 	       tempGdir = CurrentDir;
2581 	       while (popgopher(&CurrentDir) != -1) {
2582 		    if (tempGdir != NULL && tempGdir != RCgetBookmarkDir(GlobalRC))
2583 			 GDdestroy(tempGdir);
2584 		    tempGdir = CurrentDir;
2585 	       }
2586 
2587 	  }
2588 
2589 	       break;
2590 
2591 #ifndef AUTOEXITONU
2592 	  case 'q':
2593 	       /*** Quit the program ***/
2594 	       if (TRUE == ReallyQuit()) {
2595 		    bDone = TRUE;
2596 		    CURexit(CursesScreen);
2597 		    break;
2598 	       }
2599 
2600 	       break;
2601 #endif /*AUTOEXITONU*/
2602 #ifdef VMS
2603 	  case '\032': /* ^Z */
2604 #endif
2605 	  case 'Q':
2606 	       /*** Quit the program, don't ask ***/
2607 	       bDone = TRUE;
2608 	       CURexit(CursesScreen);
2609 	       break;
2610 
2611 	  case 'O':
2612 	       /*** Change various program things ***/
2613 #ifdef NEVERSETOPTIONS
2614 	       CursesErrorMsg(Gtxt("Sorry, you're not allowed to do this",164));
2615 #else
2616 	       SetOptions();
2617 #endif
2618 	       break;
2619 
2620 	  case '=':
2621 	       DESCRIBE_GOPHER(Gtxt("Gopher Item Information",93),
2622 			       GDgetEntry(CurrentDir, GDgetCurrentItem(CurrentDir)-1));
2623 	       break;
2624 
2625 	  case '^':
2626 	  {
2627 	       if (GDgetLocation(CurrentDir) != NULL)
2628 		    DESCRIBE_GOPHER(Gtxt("Gopher Directory Information",89),
2629 				    GDgetLocation(CurrentDir));
2630 	       else if (iLevel == 0)
2631 		    DESCRIBE_GOPHER(Gtxt("Gopher Directory Information",89),
2632 				    RootGophers[0]);
2633 	       else
2634 		    DESCRIBE_GOPHER(Gtxt("Gopher Directory Information",89),
2635 				    GDgetEntry(OldDirs[iLevel-1],
2636 					       GDgetCurrentItem(OldDirs[iLevel-1])-1));
2637 	       break;
2638 	  }
2639 
2640 
2641 	  case '?':
2642 	  case KEY_HELP:
2643 	  {
2644 	       /*** Display help file ***/
2645 	       GopherObj *tmpgs;
2646 	       char *tmpurl = (char *)malloc(17+255);
2647 	       char line[80];
2648 
2649 	       tmpgs = GSnew();
2650 	       GSsetType(tmpgs, A_FILE);
2651 	       sprintf(line, Gtxt("Internet Gopher Information Client v%s.%s.%d",102),
2652 		       GOPHER_MAJOR_VERSION, GOPHER_MINOR_VERSION, PATCHLEVEL);
2653 
2654 /*	       GSsetTitle(tmpgs, Gtxt("Gopher Help File",92));*/
2655 	       GSsetTitle(tmpgs, line);
2656 
2657 #ifdef VMS
2658 	       GSsetLocalFile(tmpgs, Gtxt(GOPHERHELP,223));
2659 #else
2660 	       GSsetLocalFile(tmpgs, Gtxt(GOPHERHELP,225));
2661 #endif
2662 #ifdef GOPHERHELP_SECURE
2663 	       if (SecureMode)
2664 #ifdef VMS
2665 		  GSsetLocalFile(tmpgs, Gtxt(GOPHERHELP_SECURE,224));
2666 #else
2667 		  GSsetLocalFile(tmpgs, Gtxt(GOPHERHELP_SECURE,226));
2668 #endif
2669 #endif
2670 	       GSsetLocalView(tmpgs, "text/plain");
2671 
2672 	       if (tmpurl != NULL) {
2673 		    /* set a URL for logrequest() in showfile() */
2674 		    strcpy(tmpurl, "file://localhost");
2675 #ifdef VMS
2676 		    strcat(tmpurl, Gtxt(GOPHERHELP,223));
2677 #else
2678 		    strcat(tmpurl, Gtxt(GOPHERHELP,225));
2679 #endif
2680 #ifdef GOPHERHELP_SECURE
2681 		    if (SecureMode)
2682 #ifdef VMS
2683 			strcat(tmpurl, Gtxt(GOPHERHELP_SECURE,224));
2684 #else
2685 			strcat(tmpurl, Gtxt(GOPHERHELP_SECURE,226));
2686 #endif
2687 #endif
2688 		    GSsetURL(tmpgs, tmpurl);
2689 		    free(tmpurl);
2690 	       }
2691 
2692 	       showfile(tmpgs);
2693 
2694 	       GSsetLocalFile(tmpgs, NULL);
2695 	       GSdestroy(tmpgs);
2696 	       break;
2697 	  }
2698 
2699 	  case 20:	/* Control-T */
2700 	  {
2701 	       /*** Show host's local date and time ***/
2702 	       GopherObj *tempgs = GSnew();
2703 	       char *prompts[2];
2704 	       char *responses[2];
2705 
2706 	       prompts[0]=Gtxt("Host to query:",98);
2707 	       prompts[1]=NULL;
2708 
2709 	       /*** Offer current gopher host as default ***/
2710 	       responses[0] = (char *) malloc(sizeof(char)*COLS);
2711 	       if (GDgetLocation(CurrentDir) != NULL)
2712 		    strcpy(responses[0], GSgetHost(GDgetLocation(CurrentDir)));
2713 	       else if (iLevel == 0)
2714 		    strcpy(responses[0], GSgetHost(RootGophers[0]));
2715 	       else
2716 		    strcpy(responses[0],
2717 		           GSgetHost(GDgetEntry(OldDirs[iLevel-1],
2718 			             GDgetCurrentItem(OldDirs[iLevel-1])-1)));
2719 	       responses[1] = NULL;
2720 
2721 	       if ((CURRequest(CursesScreen,
2722 			       Gtxt("Show host's local date and time",145),
2723 			   prompts, responses) == -1) || *responses[0] == '\0')
2724 		    break;
2725 
2726 	       GSsetType(tempgs, A_FILE);
2727 	       GSsetTitle(tempgs, Gtxt("Local Date and Time",104));
2728 	       GSsetHost(tempgs, responses[0]);
2729 	       GSsetPort(tempgs, 13);
2730 	       GSsetPath(tempgs, NULL);
2731 
2732 	       showfile(tempgs);
2733 
2734 	       GSdestroy(tempgs);
2735 	       free(responses[0]);
2736 	       break;
2737 	  }
2738 
2739 	  default:
2740 	       Debug("main beep %d\r\n",TypedChar)
2741 	       CURBeep(CursesScreen);
2742 	       Garbled = FALSE;
2743 	       break;
2744 
2745 	  } /* switch */
2746 
2747 #ifdef DESCRIBE_GOPHER
2748 #undef DESCRIBE_GOPHER
2749 #endif
2750 
2751      }      /* while */
2752 
2753      CleanupandExit(0);
2754 
2755      exit(0); /* Never get here... */
2756 }
2757 
2758 
2759 #if defined(VMS) && defined(A_LANGUAGE)
2760 /*
2761  *  This procedure displays the available language menu and reports the
2762  *	user's language selection
2763  */
2764 
2765 int
2766 setlocale_screen()
2767 {
2768     int status;
2769 
2770     if (!LangScreen)
2771 	LangScreen = CURnew();
2772     CURenter(LangScreen);
2773     GetMenu(setlocale_LangDir, &status, 1);
2774     status = GDgetCurrentItem(setlocale_LangDir)-1;
2775     CURexit(LangScreen);
2776     return(status);
2777 }
2778 #endif
2779 
2780 
2781 int
2782 Load_Index(GopherObj *ZeGopher)
2783 {
2784      Draw_Status(Gtxt("Searching...",143));
2785      refresh();
2786 
2787      return(Load_Index_or_Dir(ZeGopher, Searchstring));
2788 }
2789 
2790 int
2791 Load_Dir(GopherObj *ZeGopher)
2792 {
2793      Searchstring= NULL;
2794      return(Load_Index_or_Dir(ZeGopher, NULL));
2795 }
2796 
2797 
2798 int
2799 Load_Index_or_Dir(GopherObj *ZeGopher, char *Searchmungestr)
2800 {
2801      int failed = 0;
2802      int sockfd;
2803      int i, numbytes;
2804      static char DirTitle[512];
2805      GopherDirObj *NewDir = NULL;
2806 
2807      DebugGSplusPrint(ZeGopher,"Load_Index_or_Dir");
2808      NewDir = GDnew(32);
2809 
2810 
2811      Draw_Status(Gtxt("Connecting...",77)); refresh();
2812 
2813 
2814      if ((sockfd = GSconnect(ZeGopher)) <0) {
2815 
2816 	  check_sock(sockfd, GSgetHost(ZeGopher), GSgetPort(ZeGopher));
2817 	  failed = 1;
2818      }
2819      else {
2820 	  if (GSgetType(ZeGopher) == A_DIRECTORY) {
2821 	       Draw_Status(Gtxt("Retrieving Directory...",133)); refresh();
2822 	       GStransmit(ZeGopher, sockfd, NULL, "$", NULL);
2823 	  }
2824 	  else if (GSgetType(ZeGopher) == A_INDEX) {
2825 	       Draw_Status(Gtxt("Searching...",143)); refresh();
2826 	       GStransmit(ZeGopher, sockfd, Searchmungestr, "$", NULL);
2827 	  }
2828 
2829 	  numbytes = GSrecvHeader(ZeGopher, sockfd);
2830 
2831 	  if (numbytes == 0) {
2832 
2833 	       Gplus_Error(sockfd);
2834 	       failed = 1;
2835 	       return(failed);
2836 	  }
2837 
2838 	  if (GSisGplus(ZeGopher))
2839 	       GDplusfromNet(NewDir, sockfd, twirl);
2840 	  else
2841 	       GDfromNet(NewDir, sockfd, twirl);
2842 
2843 	  logrequest("ARRIVED AT", ZeGopher);
2844 
2845 
2846 	  if (GDgetNumitems(NewDir) <= 0) {
2847 	       CursesErrorMsg(Gtxt("Nothing available.", 214));
2848 	       failed = 1;
2849 	       GDdestroy(NewDir);
2850 	       closenet(sockfd);
2851 	       return(failed);
2852 	  }
2853 
2854 	  if (GSgetType(ZeGopher) == A_INDEX) {
2855 	       sprintf(DirTitle, "%s: %s", GSgetTitle(ZeGopher), Searchmungestr);
2856 	       GDsetTitle(NewDir, DirTitle);
2857 	  }
2858           else
2859 	       GDsetTitle(NewDir, GSgetTitle(ZeGopher));
2860 
2861 	  GDsetLocation(NewDir, ZeGopher);
2862 
2863 	  /** Don't push an empty gopher directory... **/
2864 	  if (CurrentDir != NULL)
2865 	       pushgopher(CurrentDir);
2866 
2867 	  CurrentDir = NewDir;
2868 
2869      }
2870      i = closenet(sockfd);
2871      return(failed);
2872 }
2873 
2874 /*
2875  * This dispatches the appropriate fcns depending on what the object is..
2876  */
2877 
2878 int
2879 process_request(GopherObj *ZeGopher)
2880 {
2881      int failed=0;
2882      char ImageCmd[128];
2883      char MovieCmd[128];
2884 
2885      if (GSisAsk(ZeGopher)) {
2886 	  char **askdata = AskBlock(ZeGopher);
2887 
2888 	  if (askdata == NULL)
2889 	       return(1);
2890 	  GSsetAskdata(ZeGopher, askdata);
2891      }
2892 
2893      /** Decide what to do with it.. **/
2894 
2895      Debug("process_request type=%c\n",GSgetType(ZeGopher));
2896      logrequest("GOING TO", ZeGopher);
2897 
2898      switch(GSgetType(ZeGopher)) {
2899      case -1:
2900 	  break;
2901 
2902 #if defined(VMS) && defined(A_LANGUAGE)
2903      case A_LANGUAGE:
2904 	  rsetlocale(GSgetPort(ZeGopher));
2905 	  break;
2906 #endif
2907 
2908      case A_INFO:
2909 	  /** Do nothing **/
2910 	  break;
2911 
2912      case A_FILE:
2913 	  Draw_Status(Gtxt("Receiving Information...",128));
2914 	  refresh();
2915 	  showfile(ZeGopher);
2916 	  break;
2917 
2918      case A_GIF:
2919      case A_IMAGE:
2920           if (!RCdisplayCommand(GlobalRC, "Image", "", ImageCmd) ||
2921 	      !strncasecmp(ImageCmd, "- none -", 8) ||
2922 	      ImageCmd == NULL || ImageCmd == '\0') {
2923 	       CursesErrorMsg(Gtxt("Sorry, this machine doesn't support images",158));
2924 	       break;
2925 	  }
2926 	  Draw_Status(Gtxt("Receiving Image...",132));
2927 	  refresh();
2928 	  if (RemoteUser)
2929 		Download_file(ZeGopher);
2930 	  else
2931 		showfile(ZeGopher);
2932 	  break;
2933 
2934      case A_MIME:
2935 	  if (!SecureMode) {
2936 	       Draw_Status(Gtxt("Receiving MIME File...",129));
2937 	       refresh();
2938 	       showfile(ZeGopher);
2939 	  }
2940 	  else
2941 	       CursesErrorMsg(Gtxt("Sorry, cannot display this file",148));
2942 	  break;
2943 
2944      case A_MACHEX:
2945      case A_PCBIN:
2946      case A_UNIXBIN:
2947 	if (RemoteUser || SecureMode || NoShellMode) {
2948 		Draw_Status(Gtxt("Receiving File...",126));
2949 		refresh();
2950 		Download_file(ZeGopher);
2951 	}
2952 	else
2953 	       Save_file(ZeGopher, NULL, NULL);
2954 	  break;
2955 
2956      case A_DIRECTORY:
2957 	  Draw_Status(Gtxt("Receiving Directory...",125));
2958 	  refresh();
2959 	  failed = Load_Dir(ZeGopher);
2960 	  break;
2961 
2962      case A_TELNET:
2963      case A_TN3270:
2964           {
2965 	      char type, buf[512];
2966 
2967 	      if ((type=GSgetType(ZeGopher)) == A_TELNET &&
2968 	          (!RCdisplayCommand(GlobalRC, "Terminal/telnet", "", buf) ||
2969 		   !strncasecmp(buf, "- none -", 8) ||
2970 	 	   buf == NULL || buf[0] == '\0')) {
2971 	          CursesErrorMsg(Gtxt("Sorry, telnet is not available.",157));
2972 		  break;
2973 	      }
2974 	      else if (type == A_TN3270 &&
2975 	          (!RCdisplayCommand(GlobalRC, "Terminal/tn3270", "", buf) ||
2976 		   !strncasecmp(buf, "- none -", 8) ||
2977 	 	   buf == NULL || buf[0] == '\0')) {
2978 	          CursesErrorMsg(Gtxt("Sorry, tn3270 is not available.",160));
2979 		  break;
2980 	      }
2981 	      else
2982 	          do_tel_3270(ZeGopher);
2983 	  }
2984 	  break;
2985 
2986      case A_INDEX:
2987 	  refresh();
2988 	  if (search_string) { /* search_string in commandline */
2989 	       Searchstring = search_string;
2990 	       search_string = NULL;
2991 	  } else
2992 	       Searchstring = do_index(ZeGopher);
2993 
2994 	  Draw_Status(Gtxt("Searching Text...",142));
2995 	  if (Searchstring != NULL)
2996 	       failed=Load_Index(ZeGopher);
2997 	  else
2998 	       failed = 1;
2999 	  break;
3000 
3001      case A_CSO:
3002 	  do_cso(ZeGopher);
3003 	  break;
3004 
3005      case A_SOUND:
3006 	  Draw_Status(Gtxt("Receiving Sound...",131));
3007 	  refresh();
3008 	  do_sound(ZeGopher);
3009 	  break;
3010 
3011       case A_MOVIE:
3012 	  if (!RCdisplayCommand(GlobalRC, "video", "", MovieCmd) ||
3013 	      !strncasecmp(MovieCmd, "- none -", 8) ||
3014 	      MovieCmd == NULL || MovieCmd == '\0') {
3015 		CursesErrorMsg(Gtxt("Sorry, this machine doesn't support movies",165));
3016 		break;
3017 	  }
3018 	  Draw_Status(Gtxt("Receiving Movie...",130));
3019 	  refresh();
3020 	  if (RemoteUser)
3021 	        Download_file(ZeGopher);
3022 	  else
3023 	        do_movie(ZeGopher);
3024 	  break;
3025 
3026      case A_HTML:
3027 	  Draw_Status(Gtxt("Receiving HTML page...",127));
3028 	  refresh();
3029 	  do_html(ZeGopher);
3030 	  break;
3031      }
3032      if (GSisAsk(ZeGopher) && GSgetLocalFile(ZeGopher) != NULL) {
3033 	  unlink(GSgetLocalFile(ZeGopher));
3034 	  GSsetLocalFile(ZeGopher, NULL);
3035      }
3036      (void)GSsetAskdata(ZeGopher, NULL);
3037      return(failed);
3038 }
3039 
3040 
3041 /*
3042  * Provide information about a gopher item...
3043  * Should use curses...  But....
3044  */
3045 
3046 void
3047 #ifdef DESCRIBE_GOPHER_GRIPE
3048 describe_gopher(char *banner, GopherStruct *ZeGopher, FILE *gripefile)
3049 
3050 #else
3051 
3052 #endif
3053 
3054 
3055 {
3056      char *tmpfilename;
3057      FILE *tmpfile;
3058      Blockobj *bl;
3059      int i,j, views;
3060      GopherObj *infogs;
3061 
3062      infogs = GSnew();
3063      GSsetType(infogs, A_FILE);
3064      GSsetTitle(infogs, Gtxt("Link Info",103));
3065 
3066      GSgetginfo(ZeGopher, TRUE);
3067 
3068 #ifdef DESCRIBE_GOPHER_GRIPE
3069      if (!gripefile)
3070 	goto normal_describe;
3071      tmpfile = gripefile;
3072      GSdestroy(infogs);
3073      goto format_description;
3074 
3075 normal_describe:
3076 #endif
3077 #ifdef VMS
3078      Gopenfile = tmpfilename = tempnam(NULL,NULL);
3079 #else
3080      Gopenfile = tmpfilename = tempnam("/tmp", "gopher");
3081 #endif
3082      GSsetLocalFile(infogs, tmpfilename);
3083      GSsetLocalView(infogs, "text/plain");
3084      if ((tmpfile = fopen(tmpfilename, "w")) == NULL) {
3085 	  CURexit(CursesScreen);
3086 	  fprintf(stderr, Gtxt("Couldn't make a tmp file!\n",83)),
3087 	  CleanupandExit(-1);
3088      }
3089 
3090      free(tmpfilename);
3091 
3092 #ifdef DESCRIBE_GOPHER_GRIPE
3093 format_description:
3094 #endif
3095 
3096      GStoLink(ZeGopher, fileno(tmpfile), TRUE);
3097 
3098      fprintf(tmpfile, "<URL:%s>\n\n", GSgetURL(ZeGopher,""));
3099      fflush(tmpfile);
3100 
3101      if (GSisGplus(ZeGopher)) {
3102 	  GopherObj *server;
3103 
3104 	  server = GSnew();
3105 	  GScpy(server, ZeGopher);
3106 	  GSsetPath(server, "");
3107 	  GSsetLocalFile(server, "");
3108 
3109 	  fputc('\n', tmpfile);
3110 	  /** Search out for specific blocks **/
3111 	  for (i=0; i<GSgetNumBlocks(ZeGopher); i++) {
3112 	       if (strcmp(BLgetName(GSgetBlock(ZeGopher, i)),"ABSTRACT")==0){
3113 		    bl = GSgetBlock(ZeGopher, i);
3114 		    fprintf(tmpfile, "%s\n---------\n\n",BLgetName(bl));
3115 		    for (j=0; j < BLgetNumLines(bl); j++) {
3116 			 fprintf(tmpfile, "%s\n", BLgetLine(bl, j));
3117 		    }
3118 		    fprintf(tmpfile, "\n");
3119 		    break;
3120 	       }
3121 	  }
3122 
3123 	  fprintf(tmpfile, Gtxt("Size       Language      Document Type\n",146));
3124 	  fprintf(tmpfile, Gtxt("---------- ------------- ----------------------------\n",147));
3125 
3126 	  for (views=0; views< GSgetNumViews(ZeGopher); views++) {
3127 	       char *cp;
3128 	       VIewobj *ZeView = GSgetView(ZeGopher, views);
3129 
3130 	       if (ZeView == NULL)
3131 		    continue;
3132 
3133 	       cp = VIgetSize(ZeView);
3134 	       if (cp != NULL)
3135 		    fprintf(tmpfile, "%s", cp);
3136 	       else
3137 		    cp = "";
3138 
3139 	       for (i=strlen(cp); i<11; i++)
3140 		    fputc(' ', tmpfile);
3141 
3142 
3143 	       cp = VIprettyLang(ZeView, "En_US");
3144 	       if (cp != NULL)
3145 		    fprintf(tmpfile, "%s", cp);
3146 	       else
3147 		    cp = VIgetLang(ZeView);
3148 	       for (i=(cp?strlen(cp):0); i<14; i++)
3149 		    fputc(' ', tmpfile);
3150 
3151 	       cp = VIgetType(ZeView);
3152 	       fprintf(tmpfile, "%s\n", cp);
3153 	  }
3154 
3155 
3156 	  for (i=0; i<GSgetNumBlocks(ZeGopher); i++) {
3157 	       bl = GSgetBlock(ZeGopher, i);
3158 	       if (strcmp(BLgetName(bl),"ADMIN")==0) {
3159 		    fprintf(tmpfile, Gtxt("\n\nServer Information\n",186));
3160 		    fprintf(tmpfile, "------------------\n");
3161 		    for (j=0; j < BLgetNumLines(bl); j++) {
3162 			 char *cp = BLgetLine(bl, j);
3163 
3164 			 if ( (strncmp(cp, "Admin:", 5) != 0) &&
3165 			      (strncmp(cp, "Mod-Date:", 9) != 0) &&
3166 			      (strncmp(cp, "TTL:", 4) != 0) ) {
3167 			      fprintf(tmpfile, "%s\n", cp);
3168 			 }
3169 		    }
3170 		    fprintf(tmpfile, "\n");
3171 		    break;
3172 	       }
3173 	  }
3174 	  GSdestroy(server);
3175      }
3176 
3177 #ifdef DESCRIBE_GOPHER_GRIPE
3178      if (gripefile)
3179 	return;
3180 #endif
3181      fclose(tmpfile);
3182 
3183      showfile(infogs);
3184 
3185      CURenter(CursesScreen); /* do this after unlink fails */
3186 
3187      GSdestroy(infogs);
3188      return;
3189 }
3190