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