1 /********************************************************************
2  * $Author: jgoerzen $
3  * $Revision: 1.8 $
4  * $Date: 2002/03/19 20:07:16 $
5  * $Source: /home/jgoerzen/tmp/gopher-umn/gopher/head/object/GSgopherobj.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: GSgopherobj.c
14  * Implement gopher directory functions.
15  *********************************************************************
16  * Revision History:
17  * $Log: GSgopherobj.c,v $
18  * Revision 1.8  2002/03/19 20:07:16  jgoerzen
19  * GSgopherobj.c: Moved Regex.h include up.
20  *
21  * Regex.c, Regex.h: continuing full rewrites.
22  *
23  * Revision 1.7  2002/01/08 17:36:14  jgoerzen
24  * Finally builds!
25  *
26  * Changes:
27  *
28  *   * config.h.in: rebuilt by autoheader
29  *
30  *   * configure: rebuilt by autoconf
31  *
32  *   * configure.in:
33  *     * Added REGEXLIBS test for NetBSD -- look for re_comp in -lcompat
34  *     * Added checkes for term.h, re_comp.h, regex.h
35  *
36  *   * gopher/ourutils.c, gopher/CURcurses.c: Use term.h check
37  *
38  *   * gopher/Makefile.in, gopherd/Makefile.in, gophfilt/Makefile.in:
39  *     * Use REGEXLIBS
40  *
41  *   * gopher/globals.h, gopher/gopher.c: Remove sys_errlist
42  *
43  *   * object/GSgopherobj.c: Removed <regex.h> #include, now use
44  *     "Regex.h" that has proper regex determining logic
45  *
46  *   * object/Regex.h: Moved regex.h include to here.  Make it conditional
47  *     based on configure test.  Add conditional re_comp.h include.
48  *
49  * Revision 1.6  2001/01/17 21:48:05  jgoerzen
50  * Many fixes and tune-ups.  Now compiles cleanly with -Wall -Werror!
51  *
52  * Revision 1.5  2001/01/17 21:16:35  jgoerzen
53  * More psinrtf -> snprintf changes
54  *
55  * Revision 1.4  2000/12/26 22:48:12  s2mdalle
56  *
57  * Changed GSsendHeader(int, int) => GSsendHeader(int, long) because the
58  * second argument is often the file size, and an int may not be
59  * sufficient depending on platform.  Most calls use canned values of -1
60  * and -2 anyway.
61  *
62  * Revision 1.3  2000/12/20 01:19:20  jgoerzen
63  * Added patches from David Allen <s2mdalle@titan.vcu.edu>
64  *
65  * Revision 1.2  2000/08/23 00:18:41  jgoerzen
66  * Various bugfixes
67  *
68  * Revision 1.1.1.1  2000/08/19 00:28:56  jgoerzen
69  * Import from UMN Gopher 2.3.1 after GPLization
70  *
71  * Revision 3.85  1995/11/10  22:26:26  lindner
72  * Fix for wierd relative gopher URLs
73  *
74  * Revision 3.84  1995/11/03  18:00:45  lindner
75  * Coen: changes
76  *
77  * Revision 3.83  1995/11/03  05:37:13  lindner
78  * Convert ------ to <HR>
79  *
80  * Revision 3.82  1995/10/04  22:30:45  lindner
81  * Add <BR> to the end of info lines
82  *
83  * Revision 3.81  1995/09/28  22:59:25  lindner
84  * Move nullword
85  *
86  * Revision 3.80  1995/09/25  22:07:18  lindner
87  * Ansification
88  *
89  * Revision 3.79  1995/08/29  07:13:06  lindner
90  * Add tickets to urls
91  *
92  * Revision 3.78  1995/06/12  15:33:41  lindner
93  * Add onlyhtml support
94  *
95  * Revision 3.77  1995/05/02  04:03:51  lindner
96  * Remove unused variable
97  *
98  * Revision 3.76  1995/04/15  07:07:19  lindner
99  * Fix for fcn prototype, check for error
100  *
101  * Revision 3.75  1995/02/22  05:30:25  lindner
102  * reintroduce gsinit to gsnew
103  *
104  * Revision 3.74  1995/02/17  18:29:37  lindner
105  * Abstract display support
106  *
107  * Revision 3.73  1995/02/16  22:32:44  lindner
108  * HTML icon support
109  *
110  * Revision 3.72  1995/02/07  07:12:55  lindner
111  * oops!
112  *
113  * Revision 3.71  1995/02/07  07:06:58  lindner
114  * performance fixes
115  *
116  * Revision 3.70  1995/02/06  22:13:52  lindner
117  * Performance fixes
118  *
119  * Revision 3.69  1995/02/02  17:14:46  lindner
120  * Fix for memory leaks and accesses
121  *
122  * Revision 3.68  1995/02/01  22:08:02  lindner
123  * Put back GSaddmerge
124  *
125  * Revision 3.67  1995/02/01  21:44:41  lindner
126  * Remove GSmerge fcns, Add message/rfc822 and application/gopher to GSisText
127  *
128  * Revision 3.66  1994/12/15  17:30:49  lindner
129  * Allow multi-line abstracts in link files, Fix for ftp URL generation
130  *
131  * Revision 3.65  1994/12/05  22:39:55  lindner
132  * Fix Name and Path settings in GSfromURL
133  *
134  * Revision 3.64  1994/11/17  06:33:58  lindner
135  * Fixes for VMS internationalization
136  *
137  * Revision 3.63  1994/11/13  06:30:48  lindner
138  * Better GSfromURL
139  *
140  * Revision 3.62  1994/10/24  22:15:53  lindner
141  * Add PDF type
142  *
143  * Revision 3.61  1994/10/18  21:37:03  lindner
144  * Make sure buf is set
145  *
146  * Revision 3.60  1994/10/13  05:27:18  lindner
147  * Compiler complaint fixes
148  *
149  * Revision 3.59  1994/09/29  19:26:45  lindner
150  * Fix for debug messages and NULL values
151  *
152  * Revision 3.58  1994/08/19  16:13:09  lindner
153  * mktime() etc, fixes from Alan Coopersmith
154  *
155  * Revision 3.57  1994/08/03  20:03:44  lindner
156  * Fix for adding ask stuff
157  *
158  * Revision 3.56  1994/07/31  04:57:20  lindner
159  * Fix for .names messup...
160  *
161  * Revision 3.55  1994/07/21  22:29:18  lindner
162  * misc hacks
163  *
164  * Revision 3.54  1994/07/06  19:21:34  lindner
165  * use strftime to get localized date and time
166  *
167  * Revision 3.53  1994/06/29  06:51:21  lindner
168  * ifdef GINTERNATIONAL, use strftime to localize the date string
169  * returned by GSgetModDate()
170  *
171  * in GSplusfromNet(), if there's anything in the ADMIN block other than
172  * Admin, ModDate, or TTL entries, save it for the client's showinfo()
173  * Server Information
174  *
175  * Move GSfromURL() return code definitions to header so other files can
176  * call it
177  *
178  * Fix GStoNetURL() & HTML display of directories
179  *
180  * Round sizes in GSaddView
181  *
182  * Make GSfromURL() lie and say all http: is HTML so the client
183  * passes it off to a http-speaking program anyway (assuming
184  * all HTML viewers speak http, that is)  (Coopersmith)
185  *
186  * Revision 3.52  1994/06/29  05:45:56  lindner
187  * Mods to pump tickets to the net
188  *
189  * Revision 3.51  1994/05/25  20:57:46  lindner
190  * Remove unused var
191  *
192  * Revision 3.50  1994/05/06  02:29:46  lindner
193  * Fix from Robert Beckett for binhex files
194  *
195  * Revision 3.49  1994/04/25  03:36:58  lindner
196  * Modifications for Debug() and mismatched NULL arguments, added Debugmsg
197  *
198  * Revision 3.48  1994/04/25  02:26:30  lindner
199  * Fix for url problems
200  *
201  * Revision 3.47  1994/04/25  02:16:31  lindner
202  * Fix for |= mistype
203  *
204  * Revision 3.46  1994/04/22  06:41:35  lindner
205  * More pacbell hacks for Domain=
206  *
207  * Revision 3.45  1994/04/22  03:26:15  lindner
208  * pacbell hack
209  *
210  * Revision 3.44  1994/04/19  14:32:29  lindner
211  * Change GD and GSfromLink routines to use FIO
212  *
213  * Revision 3.43  1994/04/13  19:16:17  lindner
214  * Fix for ASK block bug
215  *
216  * Revision 3.42  1994/04/08  20:05:52  lindner
217  * gcc -Wall fixes
218  *
219  * Revision 3.41  1994/04/07  17:27:09  lindner
220  * Fix for pyramids
221  *
222  * Revision 3.40  1994/04/01  04:38:06  lindner
223  * Fix for conditional macros
224  *
225  * Revision 3.39  1994/03/31  22:48:55  lindner
226  * fix for Hgopher and ask requests
227  *
228  * Revision 3.38  1994/03/31  21:05:11  lindner
229  * More debug code, and fix for Socket return values
230  *
231  * Revision 3.37  1994/03/17  04:37:41  lindner
232  * Fix for spinning clients
233  *
234  * Revision 3.36  1994/03/08  15:56:17  lindner
235  * gcc -Wall fixes
236  *
237  * Revision 3.35  1994/03/04  17:59:16  lindner
238  * More URL fixes from A.C.
239  *
240  * Revision 3.34  1994/02/20  16:27:44  lindner
241  * Remove dead code for GDtoNetHTML
242  *
243  * Revision 3.33  1994/01/25  06:51:21  lindner
244  * Many additions for URL/HTML support, removed insidious dot at end of host..
245  *
246  * Revision 3.32  1994/01/10  03:28:12  lindner
247  * Allow bangs in Domain= lines, to negate classes of hosts
248  *
249  * Revision 3.31  1994/01/06  06:10:53  lindner
250  * fix for cvs entries
251  *
252  *
253  * Revision 3.30  1994/01/06  05:43:46 lindner
254  * Fix for type=1 and ask blocks.
255  *
256  * Revision 3.29  1993/12/27  16:25:39 lindner
257  * Add fix for appending a period to domain names
258  *
259  * Revision 3.28 1993/11/29  01:07:23 lindner
260  * In GSfromLink(), disable handling of "Domain_pat:" for VMS to keep the
261  * compiler from complaining about the lack of re_comp() and re_exec().
262  * "Domain_pat:" handling isn't needed in the client anyway.  (Wolfe)
263  *
264  * Add "AddInfo" argument to GStoLink().  This option decides whether to
265  * add the Admin and ModDate fields to the basic information about the
266  * gopher object.  Bookmarks should not include them, but requested
267  * technical information ('=' and '^') should.  This change requires
268  * changes to gopher/gopher.c and object/GDgopherdir.c; see below.
269  * (Macrides)
270  *
271  * Revision 3.27  1993/11/05  07:24:46 lindner
272  * Allow Type=1? etc.. in .link files
273  *
274  * Revision 3.26  1993/11/04  04:50:50 lindner
275  * Add quotes around HREFs
276  *
277  * Revision 3.25  1993/11/04  02:10:47 lindner
278  * Added Domain_pat= line to check for a regexp domains
279  *
280  * Revision 3.24  1993/11/03  15:35:40 lindner
281  * Fix problems with bookmarks of gopher+ items
282  *
283  * Revision 3.23  1993/11/02  06:15:24 lindner
284  * HTML additions
285  *
286  * Revision 3.22  1993/10/26  17:49:50 lindner
287  * Fix for NULL view in GSisText()
288  *
289  * Revision 3.21  1993/10/22  20:02:33 lindner
290  * Add Movie (;) and Info (i) type support
291  *
292  * Revision 3.20  1993/09/18  04:44:41 lindner
293  * Additions to fix caching of Multiple view items
294  *
295  * Revision 3.19  1993/09/11  06:41:08 lindner
296  * Fix for picky compilers
297  *
298  * Revision 3.18  1993/09/11  06:32:52  lindner
299  * URL support
300  *
301  * Revision 3.17  1993/09/01  21:51:59  lindner
302  * Remove GSnewSet, better initialization in GSnew()
303  *
304  * Revision 3.16  1993/08/19  20:24:11  lindner
305  * Mitra's Debug patch
306  *
307  * Revision 3.15  1993/07/29  20:02:55  lindner
308  * Removed dead variables
309  *
310  * Revision 3.14  1993/07/27  20:17:25  lindner
311  * Fix improper bracketed debug output
312  *
313  * Revision 3.13  1993/07/27  05:30:23  lindner
314  * Mondo Debug overhaul from Mitra
315  *
316  * Revision 3.12  1993/07/27  00:30:09  lindner
317  * plus patch from Mitra
318  *
319  * Revision 3.11  1993/07/23  04:50:17  lindner
320  * Mods to allow abstract/admin setting in .names files
321  *
322  * Revision 3.10  1993/07/21  03:31:09  lindner
323  * Askdata can be stored locally, plus GSfromLink doesn't core dump with
324  * mangled items
325  *
326  * Revision 3.9  1993/07/14  20:37:11  lindner
327  * Negative numbering patches
328  *
329  * Revision 3.8  1993/07/07  19:30:12  lindner
330  * Split off socket based fcns to Sockets.c
331  *
332  * Revision 3.7  1993/07/06  20:22:40  lindner
333  * Added listener and accept fcns
334  *
335  * Revision 3.6  1993/06/22  06:07:14  lindner
336  * Added Domain= hacks..
337  *
338  * Revision 3.5  1993/04/15  17:44:52  lindner
339  * Fixed link processing, mods from Mitra
340  *
341  * Revision 3.4  1993/03/26  19:50:46  lindner
342  * Mitra fixes for better/clearer fromNet code
343  *
344  * Revision 3.3  1993/03/24  17:05:33  lindner
345  * Additions for Localfile for each GopherObj
346  *
347  * Revision 3.2  1993/03/18  22:15:50  lindner
348  * filtering, memory leaks fixed, GSmerge problems
349  *
350  *********************************************************************/
351 
352 #include "GSgopherobj.h"
353 
354 #if defined(mips) && defined(ultrix)   /*** Gross hack, yuck! ***/
355 #define _SIZE_T
356 #endif
357 
358 #include "String.h"
359 #include "STRstring.h"
360 #ifndef __DragonFly__
361 #include "Regex.h"
362 #endif
363 #include <stdio.h>
364 #include "compatible.h"
365 #include <errno.h>
366 #include "Malloc.h"
367 #include "Sockets.h"
368 #include "util.h"
369 #include "Debug.h"
370 #include "fileio.h"
371 #include <time.h>
372 
373 #ifdef pyr
374 unsigned long errno;
375 #endif
376 
377 /*
378  * Make a new gopherobj...  Should reuse destroyed GopherObjs...
379  */
380 
381 GopherObj *
GSnew()382 GSnew()
383 {
384      GopherObj *temp;
385 
386      temp = (GopherObj *) malloc(sizeof(GopherObj));
387 
388      temp->Selstr    = STRnew();
389      temp->Title     = STRnew();
390      temp->Host      = STRnew();
391      temp->Localfile = STRnew();
392      temp->Localview = STRnew();
393      temp->gplus     = NULL;
394      temp->isask     = FALSE;
395      temp->url       = NULL;
396 
397      GSinit(temp);
398 
399      return(temp);
400 }
401 
402 
403 /*
404  * Initialize the gopherplus components of the object
405  * (Only called for a gplus item)
406  */
407 
408 void
GSplusnew(GopherObj * gs)409 GSplusnew(GopherObj *gs)
410 {
411      if (gs->gplus == NULL) {
412 	  gs->gplus = (GplusObj *) malloc(sizeof(GplusObj));
413      }
414 
415      gs->gplus->Admin    = STRnew();
416      gs->gplus->ModDate  = STRnew();
417 
418      gs->gplus->Views    = VIAnew(10);
419      gs->gplus->OtherBlocks = BLAnew(5);
420      gs->gplus->Askdata  = NULL;
421 }
422 
423 
424 /*** Destroy gopher object ***/
425 
426 void
GSdestroy(GopherObj * gs)427 GSdestroy(GopherObj *gs)
428 {
429      STRdestroy(gs->Selstr);
430      STRdestroy(gs->Title);
431      STRdestroy(gs->Host);
432      if (GSgetLocalFile(gs) != NULL)
433 	  unlink(GSgetLocalFile(gs));
434      STRdestroy(gs->Localfile);
435      STRdestroy(gs->Localview);
436 
437      GSplusdestroy(gs);
438      if (gs->url != NULL)
439 	  URLdestroy(gs->url);
440      free(gs);
441 }
442 
443 
444 /*** Destroy Gopher+ attributes ***/
445 
446 void
GSplusdestroy(GopherObj * gs)447 GSplusdestroy(GopherObj *gs)
448 {
449      if (gs->gplus != NULL) {
450 	  STRdestroy(gs->gplus->Admin);
451 	  STRdestroy(gs->gplus->ModDate);
452 	  VIAdestroy(gs->gplus->Views);
453 	  BLAdestroy(gs->gplus->OtherBlocks);
454 
455 	  (void)GSsetAskdata(gs, NULL);
456 
457 	  free(gs->gplus);
458 	  gs->gplus = NULL;
459      }
460 }
461 
462 
463 /*
464  * Clear out all the crud
465  */
466 
467 void
GSinit(GopherObj * gs)468 GSinit(GopherObj *gs)
469 {
470      GSsetType(gs, '\0');
471 
472      STRinit(gs->Title);
473      STRinit(gs->Selstr);
474      STRinit(gs->Host);
475      if (GSgetLocalFile(gs) != NULL)
476 	  unlink(GSgetLocalFile(gs));
477      STRinit(gs->Localfile);
478      STRinit(gs->Localview);
479 
480      gs->ttl = -1;
481      gs->iPort = 0;
482      GSsetNum(gs, 0);
483      GSsetWeight(gs, 0);
484 
485      GSsetGplus(gs,FALSE);  /** Default is no gplus **/
486      GSsetAsk(gs, FALSE);
487 
488      if (gs->url != NULL)
489 	  URLdestroy(gs->url);
490      gs->url = NULL;
491      GSplusInit(gs);
492 }
493 
494 
495 /*
496  * Clear out gopher+ crud if it exists
497  */
498 
499 void
GSplusInit(GopherObj * gs)500 GSplusInit(GopherObj *gs)
501 {
502      if (gs->gplus != NULL) {
503 	  STRinit(gs->gplus->Admin);
504 	  STRinit(gs->gplus->ModDate);
505 	  VIAinit(gs->gplus->Views);
506 	  STAinit(gs->gplus->OtherBlocks);
507 
508      }
509 }
510 
511 
512 /*
513  * Set a URL for the gopherobject..
514  */
515 
516 void
GSsetURL(gs,url)517 GSsetURL(gs, url)
518   GopherObj *gs;
519   char      *url;
520 {
521      if (gs->url == NULL)
522 	  gs->url = URLnew();
523 
524      URLset(gs->url, url);
525 }
526 
527 int
GSgetNumBlocks(GopherObj * gs)528 GSgetNumBlocks(GopherObj *gs)
529 {
530      if (gs->gplus == NULL)
531 	  return(-1);
532 
533      return(BLAgetTop(gs->gplus->OtherBlocks));
534 }
535 
536 Blockobj*
GSgetBlock(GopherObj * gs,int bnum)537 GSgetBlock(GopherObj *gs, int bnum)
538 {
539      if (gs->gplus == NULL || bnum < 0)
540 	  return(NULL);
541 
542      return(BLAgetEntry(gs->gplus->OtherBlocks, bnum));
543 }
544 
545 /*
546  * Return the number of views if item is gopher+
547  */
548 
549 int
GSgetNumViews(GopherObj * gs)550 GSgetNumViews(GopherObj *gs)
551 {
552      if (gs->gplus == NULL)
553 	  return(0);
554 
555      return(VIAgetTop(gs->gplus->Views));
556 }
557 
558 /*
559  * Find out what a specific view is
560  */
561 
562 VIewobj *
GSgetView(GopherObj * gs,int viewnum)563 GSgetView(GopherObj *gs, int viewnum)
564 {
565      if (gs->gplus == NULL)
566 	  return(NULL);
567 
568      return(VIAgetEntry(gs->gplus->Views, viewnum));
569 }
570 
571 
572 char *
GSgetURL(GopherObj * gs,char * ticket)573 GSgetURL(GopherObj *gs, char *ticket)
574 {
575      if (gs->url == NULL) {
576 	  gs->url = URLnew();
577 	  URLfromGS(gs->url, gs, ticket);
578      }
579 
580      return(URLget(gs->url));
581 }
582 
583 
584 char *
GSgetURLhtml(GopherObj * gs,char * ticket)585 GSgetURLhtml(GopherObj *gs, char *ticket)
586 {
587      char *cp = GSgetURL(gs, ticket);
588      char *cp2;
589      int   views;
590 
591      if ((cp != NULL) && (strncmp(cp, "gopher://", 9) == 0)) {
592 	  if ( GSgplusInited(gs) ) {
593 	       for (views=0; views< GSgetNumViews(gs); views++) {
594 		    if (!(strncasecmp(VIgetType(GSgetView(gs,views)),
595 				      "text/html", 9))) {
596 
597 			 cp = strdup(cp);
598 
599 			 /** find the type character **/
600 			 cp2 = strchr(cp+10, '/');
601 			 if (cp2 != NULL) {
602 			      *(++cp2) = 'h';
603 			      cp2++;
604 			      if (strncmp(cp2, "%2a",3)==0) {
605 				   /** validated item, skip forward... **/
606 				   cp2 = strstr(cp2, "%20");
607 				   if (cp2 != NULL) {
608 					cp2 +=3;
609 					cp2 = strstr(cp2, "%20");
610 					if (cp2 != NULL) {
611 					     cp2+=3;
612 					     *cp2 = 'h';
613 					}
614 				   }
615 			       } else
616 				    *cp2 = 'h';
617      			 }
618 		     }
619 	       }
620 	  }
621 	  if (strstr(cp, "/1validate%201") != NULL) {
622 	       cp = strdup(cp);
623 	       cp2 = strchr(cp+10, '/');
624 	       if (cp2 != NULL) {
625 		    *(++cp2) = 'h';
626 		    *(++cp2) = 'h';
627 		    *(cp2+11) = 'h';
628 	       }
629 	  }
630 	  else if (strstr(cp, "/hhalidate%201") != NULL) {
631 	       cp = strdup(cp);
632 	       cp2 = strchr(cp+10, '/');
633 	       *(cp2+13) = 'h';
634 	  } else if (*(GSgetPath(gs)) == '*') {
635 	       /** This is a validated item.. **/
636 	       ;
637 	  }
638      }
639 
640      return cp;
641 
642 }
643 
644 static char *
GStoNetURL(GopherObj * gs,char * url,char * ticket)645 GStoNetURL(GopherObj *gs, char *url,  char *ticket)
646 {
647      char *path, *ftphost, *ftppath;
648 
649      *url = '\0';
650      path  = GSgetPath(gs);
651 
652      /** This is a server specific hack for now..  **/
653      if (path == NULL)
654 	  return(NULL);
655 
656      if ((path != NULL) && (strncmp(path, "ftp://", 6) == 0)) {
657 	  strcpy(url, path);
658 	  return(url);
659      }
660      else if ((path != NULL) && (strncmp(path, "ftp:", 4) == 0) ) {
661 	  ftphost = path+4;
662 	  ftppath = strchr(ftphost, '@');
663 
664 	  if (ftppath != NULL)
665 	  {
666 	       sprintf(url, "ftp://%.*s/", (int) (ftppath - ftphost), ftphost);
667 
668 	       ftppath++;
669 
670 	       /*** The rest is the file/path ***/
671 	       if (*ftppath == '/')
672 		    ftppath++;
673 
674 	       strcat(url, ftppath);
675 	       return(url);
676 	  }
677      }
678      strcpy(url,GSgetURLhtml(gs,ticket));
679      return(url);
680 }
681 
682 
683 Blockobj*
GSfindBlock(GopherObj * gs,char * blockname)684 GSfindBlock(GopherObj *gs, char *blockname)
685 {
686      int x;
687 
688      x = BLAsearch(GSgetOtherBlocks(gs), blockname);
689 
690      Debug("GSfind num is %d\n", x);
691      if (x < 0)
692 	  return(NULL);
693      else
694 	  return(GSgetBlock(gs, x));
695 }
696 
697 void
GSsetBlock(GopherObj * gs,char * blockname,char * text,boolean appendIfPoss)698 GSsetBlock(GopherObj *gs, char *blockname, char *text, boolean appendIfPoss)
699 {
700      Blockobj *bl = NULL;
701      boolean newbl = FALSE;
702 
703      Debug("GSsetBlock:%s;",blockname);
704      Debug("%s\r\n", text);
705 
706      if (appendIfPoss)
707           bl = GSfindBlock(gs, blockname);
708 
709      if (bl == NULL) {
710           newbl = TRUE;
711           bl = BLnew();
712      }
713 
714      BLsetName(bl, blockname);
715      BLaddText(bl, text);
716 
717 #ifdef DEBUGGING
718      if (DEBUG)
719 	  BLtoNet(bl, fileno(stderr), TRUE);
720 #endif
721      if (gs->gplus == NULL)
722 	  GSplusnew(gs);
723 
724      if (newbl) {
725           BLApush(gs->gplus->OtherBlocks, bl);
726           BLdestroy(bl);
727      }
728 
729 }
730 
731 /*
732  * Set the Askdata, destroy if necessary..
733  */
734 
735 char **
GSsetAskdata(GopherObj * gs,char ** askdata)736 GSsetAskdata(GopherObj *gs, char **askdata)
737 {
738      if (!GSgplusInited(gs))
739 	  return(NULL);
740 
741      /** Destroy data if necessary **/
742      if (gs->gplus->Askdata != NULL) {
743 	  int i=0;
744 	  char **Askmoo = gs->gplus->Askdata;
745 
746 	  while (Askmoo[i] != NULL) {
747 	       free(Askmoo[i++]);
748 	  }
749 	  free(Askmoo);
750      }
751 
752      gs->gplus->Askdata = askdata;
753 
754      return(askdata);
755 
756 }
757 
758 
759 /*
760  * Set the administrator line as defined in Gopher+ protocol
761  * Name <email>
762  */
763 
764 
765 /* Converts the gopher+ moddate from <YYYYMMDDHHMMSS> to a struct tm ptr */
766 struct tm *
GSgetModDateTM(GopherObj * gs)767 GSgetModDateTM(GopherObj *gs)
768 {
769      static struct tm time;
770      char *cp;
771 #ifndef NO_MKTIME
772      time_t converted;
773 #endif
774 
775      if ( (gs->gplus == NULL) || (STRget(gs->gplus->ModDate) == NULL) )
776           return NULL;
777 
778      cp = strchr(STRget(gs->gplus->ModDate), '<');
779      if (cp == NULL)
780           return NULL;
781 
782 #define ASCII_TO_INT(a)     (a - '0')
783      /* Should really do some sanity checking on the input here */
784      time.tm_year = ((ASCII_TO_INT(cp[1]) * 1000) + (ASCII_TO_INT(cp[2]) * 100)
785                    + (ASCII_TO_INT(cp[3]) * 10) + ASCII_TO_INT(cp[4]) ) - 1900;
786      time.tm_mon  = (ASCII_TO_INT(cp[5])  * 10) + ASCII_TO_INT(cp[6]) - 1;
787      time.tm_mday = (ASCII_TO_INT(cp[7])  * 10) + ASCII_TO_INT(cp[8]);
788      time.tm_hour = (ASCII_TO_INT(cp[9])  * 10) + ASCII_TO_INT(cp[10]);
789      time.tm_min  = (ASCII_TO_INT(cp[11]) * 10) + ASCII_TO_INT(cp[12]);
790      time.tm_sec  = (ASCII_TO_INT(cp[13]) * 10) + ASCII_TO_INT(cp[14]);
791      time.tm_isdst = -1;      /* make the system figure it out */
792 
793 #ifndef NO_MKTIME
794      /* if mktime() is present, let it do sanity checking and day of
795 	  week setting for us */
796      converted = mktime(&time);
797      if (converted != -1)
798 	  return (localtime(&converted));
799 #endif
800      return (&time);
801 
802 }
803 
804 
805 #if defined(VMS) || (defined(GINTERNATIONAL) && !defined(NO_STRFTIME))
806 /* In internationalized environments, convert the ModDate string to the
807  *  native language when it's requested.
808  * If i18n is not on, the macro from GSgopherobj.h is used.
809  *  (The i18n definition's are turned on or off in Locale.h)
810  */
811 char *
GSgetModDate(GopherObj * gs)812 GSgetModDate(GopherObj *gs)
813 {
814      char	    dateBuf[256];
815      char	    *datetime;
816      struct tm	    *modTime;
817 
818      if ( (gs->gplus == NULL) || (STRget(gs->gplus->ModDate) == NULL) )
819           return NULL;
820 
821      modTime = GSgetModDateTM(gs);
822 
823      if ( (modTime != NULL) &&
824 	  strftime(dateBuf, sizeof(dateBuf) - 16, "%c ", modTime) )
825      {
826           datetime = strchr(STRget(gs->gplus->ModDate), '<');
827           if (datetime != NULL)
828                strncat(dateBuf, datetime, 16);
829 
830           STRset(gs->gplus->ModDate, dateBuf);
831      }
832 
833      return STRget(gs->gplus->ModDate);
834 }
835 #endif /* GINTERNATIONAL */
836 
837 
838 
839 void
GSsetAdmin(GopherObj * gs,char * admin)840 GSsetAdmin(GopherObj *gs, char *admin)
841 {
842      if (gs->gplus == NULL)
843 	  GSplusnew(gs);
844 
845      STRset(gs->gplus->Admin, admin);
846 }
847 
848 
849 
850 void
GSsetModDate(GopherObj * gs,char * str)851 GSsetModDate(GopherObj *gs, char *str)
852 {
853      if (gs->gplus == NULL) GSplusnew(gs);
854      STRset(gs->gplus->ModDate, str);
855 }
856 
857 
858 
859 /** Add a view for a specific gopherobj **/
860 
861 void
GSaddView(GopherObj * gs,char * view,char * language,int estsize)862 GSaddView(GopherObj *gs, char *view, char *language, int estsize)
863 {
864      char tmpstr[64];
865      VIewobj *temp;
866 
867      if (estsize > 960)
868 	  snprintf(tmpstr, sizeof(tmpstr), "%dk", (estsize + 512)/1024);
869      else
870       	  snprintf(tmpstr, sizeof(tmpstr), ".%dk", ((estsize+64)*10)/1024);
871 
872      temp = VInew();
873 
874      VIsetType(temp, view);
875      VIsetLang(temp, language);
876      VIsetSize(temp, tmpstr);
877 
878      VIApush(gs->gplus->Views, temp);
879      VIdestroy(temp);
880 }
881 
882 void
GSdelTopView(GopherObj * gs)883 GSdelTopView(GopherObj *gs)
884 {
885 #define VIApop(a) (DApop((DynArray*)(a)))
886      VIApop(gs->gplus->Views);
887 }
888 
889 void
GSaddBlock(GopherObj * gs,char * Blockname,char * file)890 GSaddBlock(GopherObj *gs, char *Blockname, char *file)
891 {
892      Blockobj *bl;
893 
894      bl = BLnew();
895      BLsetName(bl, Blockname);
896      BLsetFile(bl, file);
897      ;
898      if (gs->gplus == NULL)
899 	  GSplusnew(gs);
900 
901      BLApush(gs->gplus->OtherBlocks, bl);
902 
903      BLdestroy(bl);
904 }
905 
906 
907 
908 /*
909  * Send a gopher reference into an fd
910  */
911 
912 static char *nullword = "(NULL)";
913 
914 void
GStoNet(GopherObj * gs,int sockfd,GSformat fmt,char * ticket)915 GStoNet(GopherObj *gs, int sockfd, GSformat fmt, char *ticket)
916 {
917      char buf[1024];
918      int i;
919      Blockobj *bl;
920 
921      if (ticket == NULL)
922 	  ticket = "";
923 
924      buf[0] = GSgetType(gs);
925 
926      if (buf[0] == '\0')	/* For example a .names with no Type */
927 	buf[0] = '3';
928 
929      if (fmt == GSFORM_G0) {
930 	  for (i=0; i< GSgetNumBlocks(gs); i++) {
931 	       bl = GSgetBlock(gs, i);
932 	       if (strcasecmp(BLgetName(bl), "ONLYHTML")==0)
933 		    return;
934 	  }
935 	  snprintf(buf + 1,
936 		   sizeof(buf) - 1,
937 		   "%s\t%s%s\t%s\t%d",
938 		   GSgetTitle(gs) ? GSgetTitle(gs) : nullword,
939 		   ticket         ? ticket : nullword,
940 		   GSgetPath(gs)  ? GSgetPath(gs) : nullword,
941 		   GSgetHost(gs)  ? GSgetHost(gs) : nullword,
942 		   GSgetPort(gs));
943 
944 	  if (GSisAsk(gs))
945 	       strcat(buf, "\t?\r\n");
946 	  else if (GSisGplus(gs))
947 	       strcat(buf, "\t+\r\n");
948 	  else
949 	       strcat(buf, "\r\n");
950 
951 	  writestring(sockfd, buf);
952 	  Debug("GStoNet:%s", buf);
953      }
954      else if (fmt == GSFORM_GPLUS) {
955 	  ;
956      }
957      else if (fmt == GSFORM_HTML) {
958 	  char url[256];
959 	  for (i=0; i < GSgetNumBlocks(gs); i++) {
960 	       bl = GSgetBlock(gs, i);
961 	       if (strcmp(BLgetName(bl), "NOHTML") == 0)
962 		    return;
963 	  }
964 
965 	  if (GSgetType(gs) == A_INFO) {
966 	       writestring(sockfd, "<DT>");
967 	       if (strncmp(GSgetTitle(gs), "----------------",16)==0) {
968 		    writestring(sockfd, "<HR>");
969 	       } else
970 		    writestring(sockfd, GSgetTitle(gs));
971 	       writestring(sockfd, "<BR>");
972 	       return;
973 	  }
974 	  GStoNetURL(gs, url, ticket);
975 	  if (url[0] == '\0')
976 	       return;
977 	  snprintf(buf, sizeof(buf),
978 		   "<A HREF=\"%s\">%s</A>\r\n", url, GSgetTitle(gs));
979 	  writestring(sockfd, buf);
980 
981 	  if (GSgplusInited(gs)) {
982 	       Blockobj *bl;
983 
984 	       bl = GSfindBlock(gs, "ABSTRACT");
985 	       if (bl != NULL) {
986 		    writestring(sockfd, "<DD>");
987 		    BLtoNet(bl, sockfd, FALSE);
988 		    writestring(sockfd, "\r\n");
989 	       }
990 	  }
991 
992 	  if (GSgetWeight(gs) != 0) {
993 	       snprintf(buf, sizeof(buf),
994 			"<DD>Score: %d\r\n", GSgetWeight(gs));
995 	       writestring(sockfd, buf);
996 	  }
997      }
998 
999 }
1000 
1001 
1002 /*
1003  * Send a long gopher data descriptor
1004  *
1005  * filter is a character array of blocks to send
1006  */
1007 
1008 void
GSplustoNet(GopherObj * gs,int sockfd,char ** filter,char * ticket)1009 GSplustoNet(GopherObj *gs, int sockfd, char **filter, char *ticket)
1010 {
1011      int      i;
1012      char     tmpstr[256];
1013      boolean  sendviews, sendadmin, sendothers;
1014 
1015      for (i=0; i < GSgetNumBlocks(gs); i++) {
1016 	  if (strcmp(BLgetName(GSgetBlock(gs, i)), "ONLYHTML") == 0)
1017 	       return;
1018      }
1019 
1020      if (filter == NULL)
1021 	  sendviews = sendadmin = sendothers = TRUE;
1022      else {
1023 	  sendviews = sendadmin = sendothers = FALSE;
1024 	  for (i=0; filter[i] != NULL ;i++) {
1025 	       if (strcasecmp(filter[i], "VIEWS")==0)
1026 		    sendviews = TRUE;
1027 	       else if (strcasecmp(filter[i], "ADMIN")==0)
1028 		    sendadmin = TRUE;
1029 	       else
1030 		    sendothers = TRUE;
1031 	  }
1032      }
1033 
1034 
1035      /** Send out the old style INFO stuff **/
1036      writestring(sockfd, "+INFO: ");
1037      GStoNet(gs,sockfd, GSFORM_G0, ticket);
1038 
1039 
1040      /** Only write out "interesting" URLs **/
1041 
1042      if (GSgetURL(gs,ticket) != NULL) {
1043 	  if (strncmp(GSgetURL(gs,ticket), "gopher:", 7) != 0) {
1044 	       writestring(sockfd, "+URL:\r\n ");
1045 	       writestring(sockfd, GSgetURL(gs,ticket));
1046 	       writestring(sockfd, "\r\n");
1047 	  }
1048      }
1049 
1050 
1051      if (GSgplusInited(gs)) {
1052 	  /*** Should special case for local filename.... ***/
1053 
1054 	  if (GSgetAdmin(gs) != NULL && GSgetModDate(gs) != NULL && sendadmin){
1055 	       writestring(sockfd, "+ADMIN:\r\n Admin: ");
1056 	       writestring(sockfd, GSgetAdmin(gs));
1057 	       writestring(sockfd, "\r\n Mod-Date: ");
1058 	       writestring(sockfd, GSgetModDate(gs));
1059 	       if (GSgetTTL(gs) > -1) {
1060 		    writestring(sockfd, "\r\n TTL: ");
1061 		    snprintf(tmpstr, sizeof(tmpstr), "%d", GSgetTTL(gs));
1062 		    writestring(sockfd, tmpstr);
1063 	       }
1064 	       writestring(sockfd, "\r\n");
1065 	  }
1066 	  if (GSgetNumViews(gs) > 0 && sendviews) {
1067 	       writestring(sockfd, "+VIEWS:\r\n");
1068 	       for (i=0 ; i< GSgetNumViews(gs); i++) {
1069 		    VItoLine(GSgetView(gs, i), tmpstr);
1070 		    writestring(sockfd, tmpstr);
1071 		    writestring(sockfd, "\r\n");
1072 	       }
1073 	  }
1074 
1075 	  if (sendothers) {
1076 
1077 	       for (i=0; i< GSgetNumBlocks(gs); i++)
1078 		    BLtoNet(GSgetBlock(gs, i),sockfd, TRUE);
1079 	  }
1080      }
1081 }
1082 
1083 
1084 /*** GSplusfromnet() assumes that the leading +INFO text has been read from
1085      the file descriptor.
1086 
1087      It returns 1  if there's more data (and another INFO block)
1088                 0  if there's EOF
1089 		SOFTERROR caller can keep reading
1090 		HARDERROR caller should stop reading
1091  ***/
1092 
1093 int
GSplusfromNet(GopherObj * gs,int fd)1094 GSplusfromNet(GopherObj *gs, int fd)
1095 {
1096      int result,readok,i;
1097      boolean nextinfo = FALSE;
1098      char plusfield;
1099      Blockobj *bl;
1100      char inputline[512];
1101 
1102      if (gs->gplus == NULL)
1103 	  GSplusnew(gs);
1104 
1105      bl = BLnew();
1106 
1107      /** get the gopher-data descriptor **/
1108      readok = GSfromNet(gs, fd);
1109 
1110      if (readok == HARDERROR)
1111 	  return(HARDERROR);
1112 
1113      /** If readok is softerror we still need to look for blocks to throw
1114          away any data before next item **/
1115 
1116      /** Now start looking for blocks.  Process blocks if we know how.  **/
1117      /** State: _GotGREF_ **/
1118 
1119      if ((result = readrecvbuf(fd, &plusfield, 1))<0)
1120 	  return(HARDERROR);
1121 
1122      while (!nextinfo) {
1123 	  if (result == 0) { 	       /*** We're done ***/
1124 	       BLdestroy(bl);
1125 
1126 	       if (readok == SOFTERROR)
1127 		    return(SOFTERROR);
1128 	       else
1129 		    return(FOUNDEOF);
1130 	  }
1131 	  switch (plusfield) {
1132 
1133 	  case '.':
1134 	       readline(fd, inputline, sizeof(inputline));
1135 	       result = FOUNDEOF;
1136 	       break;
1137 
1138 	  case '+':
1139 	       /*** State _NewBlock_ ***/
1140 
1141 	       if (readtoken(fd,inputline, sizeof(inputline), ':') <= 0)
1142 		    return(HARDERROR);
1143 
1144 	       if (strcasecmp(inputline, "INFO")==0) {
1145 		    /** Get rid of the space. **/
1146 		    if (readrecvbuf(fd, &plusfield,1) <= 0)
1147 			 return(HARDERROR);
1148 		    BLdestroy(bl);
1149 		    if (readok == SOFTERROR)
1150 			 return(SOFTERROR);
1151 		    else
1152 			 return(MORECOMING);
1153 	       }
1154 
1155 	       /** Specialized fromNets here **/
1156 
1157 	       BLinit(bl);
1158 	       if ((result=BLfromNet(bl, fd, inputline)) <0) {
1159 		    /** Bad read **/
1160 		    BLdestroy(bl);
1161 		    return(HARDERROR);
1162 	       }
1163 	       else if (result == FOUNDEOF)   /** EOF, block read ok **/
1164 		    nextinfo = TRUE;
1165 	       else
1166 		    nextinfo = FALSE;
1167 
1168 
1169 	       /*** See what the block is. Transform it if necessary ***/
1170 	       if (strcasecmp(BLgetName(bl), "VIEWS")==0) {
1171 		    VIAfromBL(gs->gplus->Views, bl);
1172 	       } else if (strcasecmp(BLgetName(bl), "ADMIN")==0) {
1173 		    int	 saveAdminBlock = 0;
1174 
1175 		    for (i=0; i<BLgetNumLines(bl); i++) {
1176 			 char *cp;
1177 
1178 			 cp = BLgetLine(bl, i);
1179 
1180 			 if (strncasecmp(cp, "Admin: ",7)==0)
1181 			      GSsetAdmin(gs, cp+7);
1182 			 else if (strncasecmp(cp, "Mod-Date: ", 10)==0)
1183 			      GSsetModDate(gs, cp+10);
1184 			 else if (strncasecmp(cp, "TTL: ", 5) == 0)
1185 			      GSsetTTL(gs, atoi(cp+5));
1186 			 else
1187 			      saveAdminBlock = 1;
1188 		    }
1189 		    if (saveAdminBlock)
1190 			 BLApush(gs->gplus->OtherBlocks, bl);
1191 
1192 	       } else if (strcasecmp(BLgetName(bl), "URL")==0) {
1193 		    char *cp;
1194 
1195 		    cp = BLgetLine(bl, 0);
1196 		    if (cp != NULL)
1197 			 GSsetURL(gs, cp);
1198 	       }
1199 	       else
1200 		    BLApush(gs->gplus->OtherBlocks, bl);
1201 
1202 	       break;  /** '+' **/
1203 
1204 	  default: /*** Hmmm plusfield wasn't a plus or '.' **/
1205 	       return(HARDERROR);
1206 
1207 	  } /** switch plusfield **/
1208 
1209      }   /** While **/
1210 
1211      BLdestroy(bl);
1212 
1213      return(FOUNDEOF);
1214 }
1215 
1216 
1217 
1218 /* GSfromNet - no comments on the original, so this is my (Mitra's) guess
1219    GSfromNet reads a gopher style line, it is called from:
1220    GDfromNet - in which case the gopher line is the whole item or;
1221    GSplusfromNet - in which case it is the part after the +INFO.
1222    It should return after reading the whole line, however in gopher+1.2b2
1223    this is not always the case, especially if it sees a Type=3
1224    returns:
1225        1      blank line (I think?)
1226        0      ok
1227       -1 HARDERROR    readfield etc error - give up
1228       -2 SOFTERROR    unrecognized or unhandleable type - skip on to next one
1229 */
1230 
1231 
1232 
1233 
1234 extern int readfield();
1235 extern int readline();
1236 
1237 #if 0
1238 static void
1239 GSprocessObject(GopherObj *gs)
1240 {
1241  	if (DEBUG)
1242 		return;
1243 #ifdef GOPHERTOFTP_HOST
1244     if (strncmp(GSgetPath(gs),"ftp:",4) == 0) {
1245 	GSsetPort(gs,GOPHERTOFTP_PORT);
1246  	GSsetHost(gs,GOPHERTOFTP_HOST);
1247      }
1248 #endif
1249 }
1250 #endif /* 0 */
1251 
1252 
1253 
1254 int
GSfromNet(GopherObj * gs,int sockfd)1255 GSfromNet(GopherObj *gs, int sockfd)
1256 {
1257      char foo[1024], *cp;
1258 
1259      if (readtoken(sockfd, foo, 1024, '\t')<= 0) {
1260 	  /* EOF or error */
1261 	  return(HARDERROR);
1262      }
1263 
1264      GSsetType(gs, foo[0]);
1265 
1266      /** Get the kind of file from the first character **/
1267      /** Filter out files that we can't deal with **/
1268 
1269      switch (GSgetType(gs)) {
1270        case A_PDF:
1271        case A_FILE:
1272        case A_DIRECTORY:
1273        case A_MACHEX:
1274        case A_PCBIN:
1275        case A_CSO:
1276        case A_INDEX:
1277        case A_TELNET:
1278        case A_SOUND:
1279        case A_UNIXBIN:
1280        case A_GIF:
1281        case A_HTML:
1282        case A_TN3270:
1283        case A_MIME:
1284        case A_IMAGE:
1285        case A_INFO:
1286        case A_MOVIE:
1287        case A_APP:
1288 	  break;
1289      case A_ERROR:
1290 	  GSsetPath(gs, "");
1291 	  GSsetHost(gs, "");
1292 	  GSsetGplus(gs, FALSE);
1293 	  ZapCRLF(foo+1);
1294 	  cp = foo + strlen(foo+1);
1295 	  while (*cp == '.' && (*(cp-1) == '\n' || *(cp-1) == '\r')) {
1296 	       *cp = '\0';
1297 	       ZapCRLF(foo+1);
1298 	  }
1299 	  break;
1300      case A_EOI:
1301 	  if (foo[1] == '\r' && foo[2] == '\n')
1302 	       return(1);
1303      default:
1304 	  /** Can't handle this type **/
1305 	  readline(sockfd, foo, 1024);/** Cleanup **/
1306 	  return(SOFTERROR);
1307      }
1308 
1309      /** Suck off the User Displayable name **/
1310      cp = foo+1;
1311      while (*cp == '\n' || *cp == '\r')
1312 	    cp++;
1313      GSsetTitle(gs, cp);
1314 
1315      /** Suck off the Pathname **/
1316      if (readtoken(sockfd, foo, 1024, '\t') <= 0)
1317 	  return(GSgetType(gs)==A_ERROR?0:HARDERROR);
1318      GSsetPath(gs, foo);
1319 
1320      /** Suck off the hostname **/
1321      if (readtoken(sockfd, foo, 1024, '\t') <= 0)
1322 	  return(GSgetType(gs)==A_ERROR?0:HARDERROR);
1323 
1324      GSsetHost(gs, foo);
1325 
1326      if (readline(sockfd, foo, 1024)<=0)
1327 	  return(GSgetType(gs)==A_ERROR?0:HARDERROR);
1328 
1329      GSsetPort(gs, 0);
1330 
1331      /** Get the port number **/
1332      if ((cp = strchr(foo, '\t')) != NULL) {
1333 	  *cp = '\0';
1334 	  switch (*(cp+1)) {
1335 	  case '?':
1336 	       GSsetAsk(gs, TRUE);
1337 	  case '+':
1338 	       GSsetGplus(gs, TRUE);
1339 	       break;
1340 	  }
1341      }
1342 
1343      GSsetPort(gs, atoi(foo));
1344      DebugGSplusPrint(gs,"GSfromNet end:");
1345 
1346      return(0);
1347 }
1348 
1349 
1350 /** Copy a GopherObj ***/
1351 
1352 void
GScpy(GopherObj * dest,GopherObj * orig)1353 GScpy(GopherObj *dest, GopherObj *orig)
1354 {
1355      dest->sFileType = orig->sFileType;
1356      dest->iPort     = orig->iPort;
1357      dest->Itemnum   = orig->Itemnum;
1358 
1359      GSsetTitle(dest, GSgetTitle(orig));
1360      GSsetPath(dest, GSgetPath(orig));
1361      GSsetHost(dest, GSgetHost(orig));
1362 
1363      GSsetGplus(dest, GSisGplus(orig));
1364      GSsetAsk(dest, GSisAsk(orig));
1365      if (orig->url != NULL)
1366 	  GSsetURL(dest, GSgetURL(orig, ""));
1367      GSsetTTL(dest, GSgetTTL(orig));
1368 
1369      STRinit(dest->Localfile);
1370      STRinit(dest->Localview);
1371      GSpluscpy(dest, orig);
1372 
1373 }
1374 
1375 void
GSpluscpy(GopherObj * dest,GopherObj * orig)1376 GSpluscpy(GopherObj *dest, GopherObj *orig)
1377 {
1378      if (GSgplusInited(orig)) {
1379 	  if (!GSgplusInited(dest))
1380 	       GSplusnew(dest);
1381 	  GSsetAdmin(dest, GSgetAdmin(orig));
1382 	  GSsetModDate(dest, GSgetModDate(orig));
1383 	  VIAcpy(dest->gplus->Views, orig->gplus->Views);
1384 	  BLAcpy(dest->gplus->OtherBlocks, orig->gplus->OtherBlocks);
1385 	  (void)GSsetAskdata(dest, GSgetAskdata(orig));
1386      }
1387 }
1388 
1389 /** GSmerge combines 2 gopher objects overwriting fields defined in overlay **/
1390 /* Called by GDaddGSmerge to merge gs into directory, that is only called
1391    by function to do this from a link */
1392 void
GSmerge(GopherObj * gs,GopherObj * overlay)1393 GSmerge(GopherObj *gs, GopherObj *overlay)
1394 {
1395      char *tempstr;
1396      char oldFileType;
1397 
1398      DebugGSplusPrint(gs,"GSmerge: Original");
1399      DebugGSplusPrint(overlay,"GSmerge: Overlay");
1400 
1401      if (GSgetHost(overlay) != NULL) {
1402 	  oldFileType = GSgetType(gs);
1403 
1404 	  if (GSgetType(overlay) != '\0') {
1405 	       /* Just setting Type wont work, since they interelated with Path
1406 	        * so set first char of path as well */
1407 	       GSsetType(gs, GSgetType(overlay));
1408 	       tempstr = GSgetPath(gs);
1409 
1410 	       if (*tempstr != A_DIRECTORY) {
1411 		    tempstr[0] = GSgetType(overlay);
1412 		    GSsetPath(gs, tempstr);
1413 	       }
1414 	       if (GSisAsk(overlay) && GSgetType(overlay) == A_DIRECTORY)
1415 		    GSaddView(gs, "application/gopher+-menu", "En_US", 0);
1416 	       /* lang==GDCgetLang(Config), 0 == dummy size here */
1417 	       /* need this patch for proper protocol & Hgopher,
1418 		  which ignores gopher0 type in favor of +VIEW */
1419 	  }
1420 
1421 	  if (GSgetTitle(overlay) != NULL)
1422 	       GSsetTitle(gs, GSgetTitle(overlay));
1423 	  /* Don't set path - that is the key to the merge, and in the overlay
1424 	     most probably has the first char set to ' '  ????*/
1425 	  if (GSgetHost(overlay) != NULL)
1426 	       GSsetHost(gs, GSgetHost(overlay));
1427 	  if (GSgetPort(overlay) != 0)
1428 	       GSsetPort(gs, GSgetPort(overlay));
1429 	  if (GSgetNum(overlay) != -1)
1430 	       GSsetNum(gs, GSgetNum(overlay));
1431 	  if (GSgetWeight(overlay) != 0)
1432 	       GSsetWeight(gs, GSgetWeight(overlay));
1433 
1434 	  if (GSgplusInited(overlay)) {
1435 	       if (!GSgplusInited(gs))
1436 		    GSplusnew(gs);
1437 	       BLAcpy(gs->gplus->OtherBlocks,
1438 		      overlay->gplus->OtherBlocks);
1439 	  }
1440 
1441 	  if (GSgetAdmin(overlay) != NULL)
1442                GSsetAdmin(gs, GSgetAdmin(overlay));
1443 
1444 
1445  	  if (GSgetModDate(overlay) != NULL)
1446 	       GSsetModDate(gs, GSgetModDate(overlay));
1447  	  if (GSgetNumViews(gs) && oldFileType == A_FILE
1448 	      && GSgetType(gs) != A_FILE) {
1449 	       char *vs, vsize[64], *vc, vcomments[256];
1450 	       VIewobj *temp;
1451 	       if ((vs = VIgetSize(GSgetView(gs, 0))))
1452 		    strcpy(vsize, vs);
1453 	       if ((vc = VIgetComments(GSgetView(gs, 0))))
1454 		    strcpy(vcomments, vc);
1455 	       VIAdestroy(gs->gplus->Views);
1456 	       gs->gplus->Views    = VIAnew(10);
1457 	       temp = VInew();
1458 	       VIsetType(temp, "");
1459 	       VIsetLang(temp, "");
1460 	       VIsetSize(temp, vsize);
1461 	       VIsetComments(temp, vcomments);
1462 	       VIApush(gs->gplus->Views, temp);
1463 	       VIdestroy(temp);
1464  	  }
1465  	  if (GSgetNumViews(overlay))
1466 	       BLAcpy(gs->gplus->Views, overlay->gplus->Views);
1467      }
1468 
1469      DebugGSplusPrint(gs,"GSmerge: Result");
1470 }
1471 
1472 
1473 /** Compare two GopherObjs ***/
1474 
1475 #if 0 /* unused */
1476 static int
1477 GScmp(GopherObj *gs1, GopherObj *gs2)
1478 {
1479      if (GSgetTitle(gs1) == NULL)
1480 	  return(1);
1481      if (GSgetTitle(gs2) == NULL)
1482 	  return(-1);
1483 
1484      return(strcmp(GSgetTitle(gs1), GSgetTitle(gs2)));
1485 }
1486 #endif /* 0 */
1487 
1488 /*********** The following functions implement the gopher/gopher+
1489   protocol, mostly
1490   GSconnect(), then GStransmit(), GSsendHeader() GSrecvHeader();
1491 ************/
1492 
1493 
1494 
1495 /* GSconnect performs a connection to socket 'service' on host
1496  * 'host'.  Host can be a hostname or ip-address.  If 'host' is null, the
1497  * local host is assumed.   The parameter full_hostname will, on return,
1498  * contain the expanded hostname (if possible).  Note that full_hostname is a
1499  * pointer to a char *, and is allocated by connect_to_gopher()
1500  *
1501  * returns Errors : ErrSocket* defined in Sockets.h or socket
1502  *
1503  */
1504 
1505 int
GSconnect(GopherObj * gs)1506 GSconnect(GopherObj *gs)
1507 {
1508      int sockfd;
1509 
1510      Debug("GSconnect: Host=%s",GSgetHost(gs));
1511      Debug("Port=%d\r\n",GSgetPort(gs));
1512 
1513      sockfd = SOCKconnect(GSgetHost(gs), GSgetPort(gs));
1514 
1515      return(sockfd);
1516 }
1517 
1518 
1519 /*
1520  * GStransmit sends the request from the client to the server
1521  *
1522  * All parameters are optional except for gs and sockfd.
1523  *
1524  * the rest pertain to gopher+ transmission.
1525  */
1526 
1527 void
GStransmit(GopherObj * gs,int sockfd,char * search,char * command,char * view)1528 GStransmit(GopherObj *gs, int sockfd, char *search, char *command, char *view)
1529 {
1530      char *cp;
1531      char **ask = GSgetAskdata(gs);
1532      int i;
1533 
1534      writestring(sockfd, GSgetPath(gs));
1535      if (search != NULL) {
1536 	  writestring(sockfd, "\t");
1537 	  writestring(sockfd, search);
1538      }
1539 
1540      /** Only send if gplus **/
1541      if  (!GSisGplus(gs)) {
1542 	  writestring(sockfd, "\r\n");
1543 	  return;
1544      }
1545 
1546      if (command != NULL) {
1547 	  writestring(sockfd, "\t");
1548 	  writestring(sockfd, command);
1549      }
1550 
1551      if (view != NULL) {
1552 	  writestring(sockfd, view);
1553      }
1554      if (ask == NULL)
1555 	  writestring(sockfd, "\r\n");
1556      else {
1557 	  writestring(sockfd, "\t1\r\n");
1558 
1559 	  GSsendHeader(sockfd, -1);
1560 
1561 	  for (i=0; ;i++) {
1562 	       cp = ask[i];
1563 
1564 	       if (cp == NULL)
1565 		    break;
1566 
1567 	       writestring(sockfd, cp);
1568 	       writestring(sockfd, "\r\n");
1569 	  }
1570 	  writestring(sockfd, ".\r\n");
1571      }
1572 }
1573 
1574 
1575 
1576 /*
1577  * GSsendHeader generates an appropriate header on sockfd
1578  *
1579  */
1580 void
GSsendHeader(int sockfd,long size)1581 GSsendHeader(int sockfd, long size)
1582 {
1583      char sizestr[64];
1584 
1585      snprintf(sizestr, sizeof(sizestr), "+%ld\r\n", size);
1586      writestring(sockfd, sizestr);
1587 }
1588 
1589 
1590 /** GSsendErrorHeader sends an error message header/message to the client **/
1591 void
GSsendErrorHeader(GopherObj * gs,int sockfd,int errortype,char * errormsg)1592 GSsendErrorHeader(GopherObj *gs, int sockfd, int errortype, char *errormsg)
1593 {
1594      char tmpstr[512];
1595 
1596      snprintf(tmpstr, sizeof(tmpstr), "-%d %s\r\n", errortype, errormsg);
1597      writestring(sockfd, tmpstr);
1598 }
1599 
1600 
1601 
1602 /*
1603  * GSrecvHeader will retrieve a gopher+ header, if it exists
1604  *
1605  * It returns the expected number of bytes that are headed our
1606  * way, if it can.
1607  *
1608  * Otherwise it returns -1 to indicate to read until \r\n.\r\n
1609  * or -2 to indicate to read to EOF
1610  *
1611  * If it encounters an error, it returns 0 and sets errno to the
1612  * gopher error class.
1613  */
1614 
1615 int
GSrecvHeader(GopherObj * gs,int sockfd)1616 GSrecvHeader(GopherObj *gs, int sockfd)
1617 {
1618      char headerline[256];
1619 
1620      Debugmsg("GSrecvHeader\n");
1621      if (GSisGplus(gs)) {
1622 	  if (readline(sockfd, headerline, sizeof(headerline))<=0)
1623 		  return(0);
1624 	  ZapCRLF(headerline);
1625 	  if (*headerline == '+') {
1626 	       if (*(headerline+1) == '-')
1627 		    return(- (atoi(headerline+2)));
1628 	       else
1629 		    return(atoi(headerline+1));
1630 	  }
1631 	  else if (*headerline == '-') {
1632 	       /*** Oh no! an error! ***/
1633 	       errno = atoi(headerline+1);
1634 	       return(0);
1635 	  }
1636      }
1637      /*** Guess if we're running old style gopher ***/
1638      else {
1639 	  switch (GSgetType(gs)) {
1640 	  case A_SOUND:
1641 	  case A_IMAGE:
1642 	  case A_GIF:
1643 	  case A_UNIXBIN:
1644 	  case A_PCBIN:
1645 	       return(-2);
1646 	       break;
1647 	  default:
1648 	       return(-1);
1649 	  }
1650      }
1651      return(-1); /** Should never get here **/
1652 }
1653 
1654 
1655 /*
1656  * This routine will load up the item information from a gopher item
1657  * if the item hasn't transferred it already...
1658  *
1659  * The savename param, if TRUE will keep the current name and type
1660  */
1661 
1662 void
GSgetginfo(GopherObj * gs,boolean savename)1663 GSgetginfo(GopherObj *gs, boolean   savename)
1664 {
1665      int    sockfd, bytes;
1666      char   inputline[256];
1667      String *tempname = NULL;
1668      char   temptype='\0';
1669 
1670      if (!GSisGplus(gs))
1671 	  return;
1672 
1673      /** Try not to overwrite stuff unnecessarily.. **/
1674 
1675      if (GSgplusInited(gs)) {
1676 	  if (GSgetAdmin(gs)!=NULL)
1677 	       return;
1678 	  if (GSgetAskdata(gs) != NULL)
1679 	       return;
1680      }
1681 
1682      if (savename) {
1683 	  tempname = STRnew();
1684 	  STRset(tempname, GSgetTitle(gs));
1685 	  temptype = GSgetType(gs);
1686      }
1687 
1688      GSplusnew(gs);
1689 
1690      /** Send out the request **/
1691      if ((sockfd = GSconnect(gs)) <0) {
1692 	  /*check_sock(sockfd, GSgetHost(gs), GSgetPort(gs));*/
1693 	  return;
1694      }
1695 
1696      GStransmit(gs, sockfd, NULL, "!", NULL);
1697      bytes = GSrecvHeader(gs,sockfd);
1698 
1699      if (bytes == 0)
1700 	  return;
1701 
1702      /***  Read off the first info block ***/
1703      readtoken(sockfd, inputline, sizeof(inputline), ' ');
1704 
1705      GSplusfromNet(gs, sockfd);
1706 
1707      if (savename) {
1708 	  GSsetTitle(gs, STRget(tempname));
1709 	  GSsetType(gs, temptype);
1710 	  STRdestroy(tempname);
1711      }
1712 
1713 }
1714 
1715 
1716 /*
1717  * GSfromLink takes an FIO structure and starts reading from it.
1718  *
1719  * It reads until it finds a line it recognizes, then
1720  *
1721  * It keeps going until it finds
1722  *   eof, a non-recognized line, as long as there is a valid Path= line
1723  *
1724  * returns -1 on an error, 0 for EOF, 1 for success
1725  */
1726 
1727 int
GSfromLink(GopherObj * gs,FileIO * fio,char * host,int port,char * directory,char * peer)1728 GSfromLink(
1729   GopherObj *gs,
1730   FileIO    *fio,
1731   char      *host,
1732   int       port,
1733   char      *directory,
1734   char      *peer)
1735 {
1736      int doneflags = 0;
1737      char buf[1024];
1738      int bytesread;
1739 
1740      boolean DomainDefault = TRUE; /** Default for using domain stuff yes/no */
1741      boolean BadDomain = FALSE;	   /** For use with the Domain= line **/
1742      boolean DidDomain = FALSE;    /** Needed to make Domain= lines
1743 				      into logical or's **/
1744      buf[0] = '\0';
1745 
1746 
1747      Debugmsg("GSfromLink...\n");
1748      while ((bytesread = FIOreadlinezap(fio, buf, sizeof(buf)))>0) {
1749 	  if (buf[0] == '#') {
1750 	       if (doneflags & G_PATH)
1751 		    break;   /* comment */
1752 	       else
1753 		    continue;
1754 	  }
1755 
1756 	  ZapCRLF(buf);
1757 
1758 	  if (strncmp(buf, "Type=", 5)==0) {
1759 	       GSsetType(gs, buf[5]);
1760 
1761 	       if (buf[6] == '+')
1762 		    GSsetGplus(gs, TRUE);
1763 	       if (buf[6] == '?')
1764 		    GSsetAsk(gs, TRUE);
1765 	       doneflags |= G_TYPE;
1766 	  }
1767 
1768 	  else if (strncmp(buf, "Name=", 5)==0) {
1769 	       GSsetTitle(gs, buf+5);
1770 	       doneflags |= G_NAME;
1771 	  }
1772 
1773 	  else if (strncmp(buf, "Path=", 5)==0) {
1774 	       if (strncmp(buf+5, "~/",2) == 0 ||
1775 		   strncmp(buf+5, "./",2) == 0) {
1776 		    char tmpstr[256];
1777 
1778 		    *tmpstr = '.';
1779 		    strcpy(tmpstr+1, directory);
1780 		    if (directory[strlen(directory)-1] == '/')
1781 			 strcat(tmpstr, buf+7);
1782 		    else
1783 			 strcat(tmpstr, buf+6);
1784 		    GSsetPath(gs, tmpstr);
1785 		    GSsetHost(gs, host);
1786 	       } else
1787 		    GSsetPath(gs, buf+5);
1788 	       doneflags |= G_PATH;
1789 	  }
1790 
1791 	  else if (strncmp(buf, "Host=", 5)==0) {
1792 	       if (buf[5] == '+' && buf[6] == '\0')
1793 		    GSsetHost(gs, host);
1794 	       else
1795 		    GSsetHost(gs, buf+5);
1796 
1797 	       doneflags |= G_HOST;
1798 	  }
1799 
1800 	  else if (strncmp(buf, "Port=", 5)==0) {
1801 	       if (buf[5] == '+' && buf[6] == '\0')
1802 		    GSsetPort(gs, port);
1803 	       else
1804 		    GSsetPort(gs, atoi(buf+5));
1805 
1806 	       doneflags |= G_PORT;
1807 	  }
1808 
1809 	  else if (strncmp(buf, "Numb=", 5)==0)
1810 	       GSsetNum(gs, atoi(buf+5));
1811 
1812 	  else if (strncmp(buf, "Abstract=", 9)==0) {
1813 	       char *acp;
1814 
1815 	       acp = buf+9;
1816 
1817 	       while (*(buf + strlen(buf)-1) == '\\' && bytesread >0) {
1818 		    /* A continuation line */
1819 		    *(buf + strlen(buf)-1) = '\0';
1820 		    GSsetAbstract(gs, acp);
1821 		    bytesread = FIOreadlinezap(fio, buf, sizeof(buf));
1822 		    acp = buf;
1823 	       }
1824 	       GSsetAbstract(gs, acp);
1825 	  }
1826 	  else if (strncmp(buf, "Admin=", 6) == 0)
1827 	       GSsetAdmin(gs, buf +6);
1828 	  else if (strncmp(buf, "URL=", 4) == 0)
1829 	       doneflags |= GSfromURL(gs, buf + 4, host, port, doneflags);
1830 	  else if (strncmp(buf, "Domaindef=", 10)==0 && peer != NULL) {
1831 	       DomainDefault = (strcasecmp(buf+10, "no")!=0);
1832 	       Debug("Default Domain is %s\n", buf+10);
1833 	  }
1834 	  else if (strncmp(buf, "Domain=", 7) ==0 && peer != NULL) {
1835 	       /** Check to see if the peer matches the domain **/
1836 	       int     peerlen,domainlen;
1837 	       boolean TestResult = !DomainDefault;
1838 	       char    *host = buf+7;
1839 
1840 	       if (*host == '!') {
1841 		    TestResult = TRUE;
1842 		    host++;
1843 	       }
1844 
1845 	       peerlen = strlen(peer);
1846 	       domainlen = strlen(host);
1847 
1848 	       if (DidDomain == TRUE && BadDomain == FALSE)
1849 		    break;
1850 
1851 	       if (domainlen > peerlen) {
1852 		    BadDomain = !TestResult;
1853 	       } else if (strncasecmp(buf+7, peer + peerlen - domainlen, domainlen)== 0) {
1854 		    /** Domains match, do it! **/
1855 		    BadDomain = TestResult;
1856 	       } else
1857 		    BadDomain = !TestResult;
1858 
1859 	       DidDomain = TRUE;
1860 	  }
1861 #ifndef VMS
1862 	  else if (strncmp(buf, "Domain_pat=", 11) ==0 && peer != NULL) {
1863 	       char    *host = buf+11;
1864 	       boolean TestResult = !DomainDefault;
1865 
1866 	       if (DidDomain == TRUE && BadDomain == FALSE)
1867 		    break;
1868 
1869 	       if (*host == '!') {
1870 		    host++;
1871 		    TestResult = TRUE;
1872 	       }
1873 
1874 	       /** Check for domain using regexps **/
1875 	       if (re_comp(host))
1876 		    break;
1877 	       if (re_exec(peer) == 1)
1878 		    BadDomain = TestResult;
1879 	       else
1880 		    BadDomain = !TestResult;
1881 
1882 	       DidDomain = TRUE;
1883 	  }
1884 #endif
1885 	  else if (strncmp(buf, "TTL=", 4) == 0) {
1886 	       GSsetTTL(gs, atoi(buf+4));
1887 	  }
1888 	  else
1889 	       break;  /*** Unknown name/item ***/
1890      }
1891 
1892      Debugmsg("Done with this link item\n");
1893 
1894      if (BadDomain)
1895 	  return(SOFTERROR);
1896 
1897      if (bytesread == 0) {
1898 	  if (doneflags & G_PATH)
1899 	       return(FOUNDEOF);  /** Found the eof, plus there's a g item **/
1900 	  else
1901 	       return(HARDERROR); /** Mangled item, plus eof, stop the game **/
1902      }
1903 
1904      if (doneflags & G_PATH)
1905 	  return(MORECOMING);  /** Found item, more coming. **/
1906      else
1907 	  return(SOFTERROR);   /** Mangled item, more coming.. **/
1908 }
1909 
1910 
1911 
1912 /*
1913  * Fill in a GopherObj, given a URL
1914  */
1915 
1916 
1917 int
GSfromURL(GopherObj * gs,char * urltxt,char * host,int port,int doneflags)1918 GSfromURL(GopherObj *gs, char *urltxt, char *host, int port, int doneflags)
1919 {
1920      char tempbuf[256];
1921      Url  *url;
1922      UrlServiceType serviceType;
1923 
1924      url = URLnew();
1925      URLset(url, urltxt);
1926 
1927      Debug("GSfromURL: %s\r\n",urltxt);
1928      GSsetURL(gs, urltxt);
1929      serviceType = URLgetService(url);
1930 
1931      switch (serviceType) {
1932      case http:
1933 	  if (! (doneflags & G_TYPE)) {
1934 	       /* It may not be HTML, but we lie and say it is so the  */
1935 	       /* client passes it off to a http-speaking program */
1936 	       GSsetType(gs, A_HTML);
1937 	       doneflags |= G_TYPE;
1938 	  }
1939 
1940 	  if (! (doneflags & G_PATH)) {
1941 	       snprintf(tempbuf, sizeof(tempbuf), "GET /%s", URLgetPath(url));
1942 	       GSsetPath(gs, tempbuf);
1943 	  }
1944 	  doneflags |= G_PATH;
1945 
1946 	  break;
1947 
1948      case ftp:
1949 	  if (! (doneflags & G_HOST)) {
1950 	       GSsetHost(gs, host);
1951 	       doneflags |= G_HOST;
1952 	  }
1953 	  if (! (doneflags & G_PORT)) {
1954 	       GSsetPort(gs, port);
1955 	       doneflags |= G_PORT;
1956 	  }
1957 	  break;
1958 
1959      case telnet:
1960 	  if (! (doneflags & G_TYPE)) {
1961 	       GSsetType(gs, A_TELNET);
1962 	       doneflags |= G_TYPE;
1963 	  }
1964 
1965      case tn3270:
1966 	  if (! (doneflags & G_TYPE)) {
1967 	       GSsetType(gs, A_TN3270);
1968 	       doneflags |= G_TYPE;
1969 	  }
1970 	  break;
1971 
1972      case gopher:
1973 
1974 	  if (! (doneflags & G_TYPE)) {
1975 	       GSsetType(gs, URLgetGophType(url));
1976 	       doneflags |= G_TYPE;
1977 	  }
1978 
1979 	  break;
1980 
1981      default:
1982 	  /* A type we can't deal with... */
1983 	  return(doneflags);
1984      }
1985 
1986      if (! (doneflags & G_NAME)) {
1987 	  GSsetTitle(gs, URLget(url));
1988 	  doneflags |= G_NAME;
1989      }
1990 
1991      /* Use login & password if needed & present */
1992      switch (serviceType) {
1993      case telnet:
1994      case tn3270:
1995 	  if (URLgetUser(url) != NULL)
1996 	       GSsetPath(gs, URLgetUser(url));
1997 	  else
1998 	       GSsetPath(gs, "");
1999 	  doneflags |= G_PATH;
2000 	  break;
2001 /* Unhandled cases... */
2002      case gopher:
2003      case http:
2004      case ftp:
2005      case news:
2006      case unset:
2007      case unknown:
2008      break;}
2009 
2010      if (serviceType == ftp) {
2011 	  if (!(doneflags & G_PATH)) {
2012 	       if (URLgetPath(url) != NULL && *URLgetPath(url) != '\0')
2013 		    snprintf(tempbuf, sizeof(tempbuf),
2014 			     "ftp:%s@/%s", URLgetHost(url),
2015 			     URLgetPath(url));
2016 	       else
2017 		    snprintf(tempbuf, sizeof(tempbuf),
2018 			     "ftp:%s@/", URLgetHost(url));
2019 
2020 	       GSsetPath(gs, tempbuf);
2021 	       doneflags |= G_PATH;
2022 	  }
2023 	  if (! (doneflags & G_TYPE)) {
2024 	       if ((*(URLgetPath(url)) == '\0') ||
2025 	            *(URLgetPath(url) + strlen(URLgetPath(url))-1) == '/')
2026 		    GSsetType(gs, A_DIRECTORY);
2027 	       else
2028 		    GSsetType(gs, A_FILE);
2029 
2030 	       doneflags |= G_TYPE;
2031 	  }
2032      } else {
2033 	  if (! (doneflags & G_HOST)) {
2034 	       GSsetHost(gs, URLgetHost(url));
2035 	       doneflags |= G_HOST;
2036 	  }
2037 	  if (! (doneflags & G_PORT)) {
2038 	       GSsetPort(gs, URLgetPort(url));
2039 	       doneflags |= G_PORT;
2040 	  }
2041      }
2042 
2043 
2044      if (! (doneflags & G_PATH) || GSgetPath(gs) == NULL) {
2045           char *cp;
2046 	  GSsetPath(gs, (cp=URLgetPath(url)) ? cp : "");
2047 	  doneflags |= G_PATH;
2048      }
2049 
2050      return doneflags;
2051 }
2052 
2053 
2054 
2055 
2056 void
GStoLink(GopherObj * gs,int fd,BOOLEAN AddInfo)2057 GStoLink(GopherObj *gs, int fd, BOOLEAN AddInfo)
2058 {
2059      char gtype[2];
2060      char portnum[16];
2061 
2062      gtype[0] = GSgetType(gs);
2063      gtype[1] = '\0';
2064 
2065      writestring(fd, "#");
2066      writestring(fd, "\nType=");
2067      writestring(fd, gtype);
2068      if (GSisGplus(gs))
2069 	  writestring(fd, "+");
2070      writestring(fd, "\nName=");
2071      writestring(fd, GSgetTitle(gs));
2072      writestring(fd, "\nPath=");
2073      writestring(fd, GSgetPath(gs));
2074      writestring(fd, "\nHost=");
2075      writestring(fd, GSgetHost(gs));
2076      writestring(fd, "\nPort=");
2077      snprintf(portnum, sizeof(portnum), "%d", GSgetPort(gs));
2078      writestring(fd, portnum);
2079      writestring(fd, "\n");
2080      if (GSisGplus(gs) && GSgplusInited(gs) && AddInfo) {
2081 	  writestring(fd, "Admin=");
2082 	  writestring(fd, GSgetAdmin(gs));
2083 	  writestring(fd, "\nModDate=");
2084 	  writestring(fd, GSgetModDate(gs));
2085 	  writestring(fd, "\n");
2086      }
2087 }
2088 
2089 
2090 boolean
GSisText(GopherObj * gs,char * view)2091 GSisText(GopherObj *gs, char *view)
2092 {
2093      if (view == NULL) {
2094 	  switch (GSgetType(gs)) {
2095 	  case A_DIRECTORY:
2096 	  case A_FILE:
2097 	  case A_MIME:
2098 	  case A_CSO:
2099 	  case A_MACHEX:
2100 	       return(TRUE);
2101 
2102 	  case A_HTML:		/* For goofy relative urls, ugh! */
2103 	  default:
2104 	       return(FALSE);
2105 	  }
2106      }
2107      else {
2108 	  char viewstowage[64], *cp;
2109 
2110 	  strcpy(viewstowage, view);
2111 	  if ((cp=strchr(viewstowage, ' '))!=NULL) {
2112 	       *cp = '\0';
2113 	       view = viewstowage;
2114 	  }
2115 
2116 	  if (strncasecmp(view, "Text",4) == 0 ||
2117 	      strncasecmp(view, "message/rfc822", 14)==0 ||
2118 	      strncasecmp(view, "application/postscript", 21)==0 ||
2119 	      strncasecmp(view, "application/mac-binhex40", 24)==0 ||
2120 	      strncasecmp(view, "application/rtf", 15) == 0 ||
2121 	      strncasecmp(view, "application/gopher", 18) == 0)
2122 
2123 
2124 
2125 	       return(TRUE);
2126 	  else
2127 	       return(FALSE);
2128      }
2129 }
2130 
2131 #ifdef DEBUGGING
2132 void
GSplusPrint(GopherObj * gs,char * head)2133 GSplusPrint(GopherObj *gs, char *head)
2134 {
2135      int i;
2136      int oldDebug = DEBUG;
2137      DEBUG=FALSE;
2138      fprintf(stderr,"%s: Type=%c,Title=%s,Path=%s,Host=%s,Port=%d,Num=%d,Weight=%d,Plus=%d,Ask=%d\r\n",
2139 	     head,
2140 	     GSgetType(gs),
2141 	     GSgetTitle(gs),
2142 	     GSgetPath(gs),
2143 	     GSgetHost(gs),
2144 	     GSgetPort(gs),
2145 	     GSgetNum(gs),
2146 	     GSgetWeight(gs),
2147 	     GSisGplus(gs),
2148 	     GSisAsk(gs)
2149 	     );
2150 
2151      if (GSgplusInited(gs))
2152 	  for (i=0; i< GSgetNumBlocks(gs); i++) {
2153 	       BLtoNet(GSgetBlock(gs, i), fileno(stderr), TRUE);
2154 	  }
2155      fprintf(stderr,"===============\r\n");
2156      DEBUG = oldDebug;
2157 }
2158 #endif
2159 
2160 
2161 
2162