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