1 /*
2  * $LynxId: HTFinger.c,v 1.30 2013/01/04 21:40:19 tom Exp $
3  *
4  *			FINGER ACCESS				HTFinger.c
5  *			=============
6  * Authors:
7  *  ARB  Andrew Brooks
8  *
9  * History:
10  *	21 Apr 94   First version (ARB, from HTNews.c by TBL)
11  *	12 Mar 96   Made the URL and command buffering secure from
12  *		     stack modifications, beautified the HTLoadFinger()
13  *		     and response() functions, and added support for the
14  *		     following URL formats for sending a "", "/w",
15  *		     "username[@host]", or "/w username[@host]" command
16  *		     to the server:
17  *			finger://host
18  *			finger://host/
19  *			finger://host/%2fw
20  *			finger://host/%2fw%20username[@host]
21  *			finger://host/w/username[@host]
22  *			finger://host/username[@host]
23  *			finger://host/username[@host]/w
24  *			finger://username@host
25  *			finger://username@host/
26  *			finger://username@host/w
27  *	15 Mar 96   Added support for port 79 gtype 0 gopher URLs
28  *		     relayed from HTLoadGopher. - FM
29  */
30 
31 #include <HTUtils.h>
32 
33 #ifndef DISABLE_FINGER
34 
35 #include <HTAlert.h>
36 #include <HTML.h>
37 #include <HTParse.h>
38 #include <HTFormat.h>
39 #include <HTTCP.h>
40 #include <HTString.h>
41 #include <HTFinger.h>
42 
43 #include <LYUtils.h>
44 #include <LYLeaks.h>
45 
46 #define FINGER_PORT 79		/* See rfc742 */
47 #define BIG 1024		/* Bug */
48 
49 #define PUTC(c) (*targetClass.put_character)(target, c)
50 #define PUTS(s) (*targetClass.put_string)(target, s)
51 #define START(e) (*targetClass.start_element)(target, e, 0, 0, -1, 0)
52 #define END(e) (*targetClass.end_element)(target, e, 0)
53 #define FREE_TARGET (*targetClass._free)(target)
54 #define NEXT_CHAR HTGetCharacter()
55 
56 /*	Module-wide variables
57 */
58 static int finger_fd;		/* Socket for FingerHost */
59 
60 struct _HTStructured {
61     const HTStructuredClass *isa;	/* For gopher streams */
62     /* ... */
63 };
64 
65 static HTStructured *target;	/* The output sink */
66 static HTStructuredClass targetClass;	/* Copy of fn addresses */
67 
68 /*	Initialisation for this module
69  *	------------------------------
70  */
71 static BOOL initialized = NO;
initialize(void)72 static BOOL initialize(void)
73 {
74     finger_fd = -1;		/* Disconnected */
75     return YES;
76 }
77 
78 /*	Start anchor element
79  *	--------------------
80  */
start_anchor(const char * href)81 static void start_anchor(const char *href)
82 {
83     BOOL present[HTML_A_ATTRIBUTES];
84     const char *value[HTML_A_ATTRIBUTES];
85 
86     {
87 	int i;
88 
89 	for (i = 0; i < HTML_A_ATTRIBUTES; i++)
90 	    present[i] = (BOOL) (i == HTML_A_HREF);
91     }
92     ((const char **) value)[HTML_A_HREF] = href;
93     (*targetClass.start_element) (target, HTML_A, present,
94 				  (const char **) value, -1, 0);
95 
96 }
97 
98 /*	Send Finger Command line to remote host & Check Response
99  *	--------------------------------------------------------
100  *
101  * On entry,
102  *	command	points to the command to be sent, including CRLF, or is null
103  *		pointer if no command to be sent.
104  * On exit,
105  *	Negative status indicates transmission error, socket closed.
106  *	Positive status is a Finger status.
107  */
108 
response(char * command,char * sitename,HTParentAnchor * anAnchor,HTFormat format_out,HTStream * sink)109 static int response(char *command,
110 		    char *sitename,
111 		    HTParentAnchor *anAnchor,
112 		    HTFormat format_out,
113 		    HTStream *sink)
114 {
115     int status;
116     int length = (int) strlen(command);
117     int ch, i;
118     char line[BIG], *l, *cmd = NULL;
119     char *p = line, *href = NULL;
120 
121     if (length == 0)
122 	return (-1);
123 
124     /* Set up buffering.
125      */
126     HTInitInput(finger_fd);
127 
128     /* Send the command.
129      */
130     CTRACE((tfp, "HTFinger command to be sent: %s", command));
131     status = (int) NETWRITE(finger_fd, (char *) command, (unsigned) length);
132     if (status < 0) {
133 	CTRACE((tfp, "HTFinger: Unable to send command. Disconnecting.\n"));
134 	NETCLOSE(finger_fd);
135 	finger_fd = -1;
136 	return status;
137     }
138     /* if bad status */
139     /* Make a hypertext object with an anchor list.
140      */
141     target = HTML_new(anAnchor, format_out, sink);
142     targetClass = *target->isa;	/* Copy routine entry points */
143 
144     /* Create the results report.
145      */
146     CTRACE((tfp, "HTFinger: Reading finger information\n"));
147     START(HTML_HTML);
148     PUTC('\n');
149     START(HTML_HEAD);
150     PUTC('\n');
151     START(HTML_TITLE);
152     PUTS("Finger server on ");
153     PUTS(sitename);
154     END(HTML_TITLE);
155     PUTC('\n');
156     END(HTML_HEAD);
157     PUTC('\n');
158     START(HTML_BODY);
159     PUTC('\n');
160     START(HTML_H1);
161     PUTS("Finger server on ");
162     START(HTML_EM);
163     PUTS(sitename);
164     END(HTML_EM);
165     PUTS(": ");
166     StrAllocCopy(cmd, command);
167     for (i = ((int) strlen(cmd) - 1); i >= 0; i--) {
168 	if (cmd[i] == LF || cmd[i] == CR) {
169 	    cmd[i] = '\0';
170 	} else {
171 	    break;
172 	}
173     }
174     PUTS(cmd);
175     FREE(cmd);
176     END(HTML_H1);
177     PUTC('\n');
178     START(HTML_PRE);
179 
180     while ((ch = NEXT_CHAR) != EOF) {
181 
182 	if (interrupted_in_htgetcharacter) {
183 	    CTRACE((tfp,
184 		    "HTFinger: Interrupted in HTGetCharacter, apparently.\n"));
185 	    _HTProgress(CONNECTION_INTERRUPTED);
186 	    goto end_html;
187 	}
188 
189 	if (ch != LF) {
190 	    *p = (char) ch;	/* Put character in line */
191 	    if (p < &line[BIG - 1]) {
192 		p++;
193 	    }
194 	} else {
195 	    *p = '\0';		/* Terminate line */
196 	    /*
197 	     * OK we now have a line.
198 	     * Load it as 'l' and parse it.
199 	     */
200 	    p = l = line;
201 	    while (*l) {
202 		if (StrNCmp(l, STR_NEWS_URL, LEN_NEWS_URL) &&
203 		    StrNCmp(l, "snews://", 8) &&
204 		    StrNCmp(l, "nntp://", 7) &&
205 		    StrNCmp(l, "snewspost:", 10) &&
206 		    StrNCmp(l, "snewsreply:", 11) &&
207 		    StrNCmp(l, "newspost:", 9) &&
208 		    StrNCmp(l, "newsreply:", 10) &&
209 		    StrNCmp(l, "ftp://", 6) &&
210 		    StrNCmp(l, "file:/", 6) &&
211 		    StrNCmp(l, "finger://", 9) &&
212 		    StrNCmp(l, "http://", 7) &&
213 		    StrNCmp(l, "https://", 8) &&
214 		    StrNCmp(l, "wais://", 7) &&
215 		    StrNCmp(l, STR_MAILTO_URL, LEN_MAILTO_URL) &&
216 		    StrNCmp(l, "cso://", 6) &&
217 		    StrNCmp(l, "gopher://", 9))
218 		    PUTC(*l++);
219 		else {
220 		    StrAllocCopy(href, l);
221 		    start_anchor(strtok(href, " \r\n\t,>)\""));
222 		    while (*l && !strchr(" \r\n\t,>)\"", *l))
223 			PUTC(*l++);
224 		    END(HTML_A);
225 		    FREE(href);
226 		}
227 	    }
228 	    PUTC('\n');
229 	}
230     }
231     NETCLOSE(finger_fd);
232     finger_fd = -1;
233 
234   end_html:
235     END(HTML_PRE);
236     PUTC('\n');
237     END(HTML_BODY);
238     PUTC('\n');
239     END(HTML_HTML);
240     PUTC('\n');
241     FREE_TARGET;
242     return (0);
243 }
244 
245 /*		Load by name					HTLoadFinger
246  *		============
247  */
HTLoadFinger(const char * arg,HTParentAnchor * anAnchor,HTFormat format_out,HTStream * stream)248 int HTLoadFinger(const char *arg,
249 		 HTParentAnchor *anAnchor,
250 		 HTFormat format_out,
251 		 HTStream *stream)
252 {
253     static char empty[1];
254 
255     char *username, *sitename;	/* Fields extracted from URL */
256     char *slash, *at_sign;	/* Fields extracted from URL */
257     char *command, *str, *param;	/* Buffers */
258     int port;			/* Port number from URL */
259     int status;			/* tcp return */
260     int result = HT_LOADED;
261     BOOL IsGopherURL = FALSE;
262     const char *p1 = arg;
263 
264     CTRACE((tfp, "HTFinger: Looking for %s\n", (arg ? arg : "NULL")));
265 
266     if (!(arg && *arg)) {
267 	HTAlert(COULD_NOT_LOAD_DATA);
268 	return HT_NOT_LOADED;	/* Ignore if no name */
269     }
270 
271     if (!initialized)
272 	initialized = initialize();
273     if (!initialized) {
274 	HTAlert(gettext("Could not set up finger connection."));
275 	return HT_NOT_LOADED;	/* FAIL */
276     }
277 
278     /*  Set up the host and command fields.
279      */
280     if (!strncasecomp(arg, "finger://", 9)) {
281 	p1 = arg + 9;		/* Skip "finger://" prefix */
282     } else if (!strncasecomp(arg, "gopher://", 9)) {
283 	p1 = arg + 9;		/* Skip "gopher://" prefix */
284 	IsGopherURL = TRUE;
285     }
286 
287     param = 0;
288     sitename = StrAllocCopy(param, p1);
289     if (param == 0) {
290 	HTAlert(COULD_NOT_LOAD_DATA);
291 	return HT_NOT_LOADED;
292     } else if ((slash = strchr(sitename, '/')) != NULL) {
293 	*slash++ = '\0';
294 	HTUnEscape(slash);
295 	if (IsGopherURL) {
296 	    if (*slash != '0') {
297 		HTAlert(COULD_NOT_LOAD_DATA);
298 		return HT_NOT_LOADED;	/* FAIL */
299 	    }
300 	    *slash++ = '\0';
301 	}
302     }
303 
304     if ((at_sign = strchr(sitename, '@')) != NULL) {
305 	if (IsGopherURL) {
306 	    HTAlert(COULD_NOT_LOAD_DATA);
307 	    return HT_NOT_LOADED;	/* FAIL */
308 	} else {
309 	    *at_sign++ = '\0';
310 	    username = sitename;
311 	    sitename = at_sign;
312 	    HTUnEscape(username);
313 	}
314     } else if (slash) {
315 	username = slash;
316     } else {
317 	username = empty;
318     }
319 
320     if (*sitename == '\0') {
321 	HTAlert(gettext("Could not load data (no sitename in finger URL)"));
322 	result = HT_NOT_LOADED;	/* Ignore if no name */
323     } else if (HTParsePort(sitename, &port) != NULL) {
324 	if (port != 79) {
325 	    HTAlert(gettext("Invalid port number - will only use port 79!"));
326 	    result = HT_NOT_LOADED;	/* Ignore if wrong port */
327 	}
328     }
329 
330     if (result == HT_LOADED) {
331 	/* Load the string for making a connection/
332 	 */
333 	str = 0;
334 	HTSprintf0(&str, "lose://%s/", sitename);
335 
336 	/* Load the command for the finger server.
337 	 */
338 	command = 0;
339 	if (at_sign && slash) {
340 	    if (*slash == 'w' || *slash == 'W') {
341 		HTSprintf0(&command, "/w %s%c%c", username, CR, LF);
342 	    } else {
343 		HTSprintf0(&command, "%s%c%c", username, CR, LF);
344 	    }
345 	} else if (at_sign) {
346 	    HTSprintf0(&command, "%s%c%c", username, CR, LF);
347 	} else if (*username == '/') {
348 	    if ((slash = strchr((username + 1), '/')) != NULL) {
349 		*slash = ' ';
350 	    }
351 	    HTSprintf0(&command, "%s%c%c", username, CR, LF);
352 	} else if ((*username == 'w' || *username == 'W') &&
353 		   *(username + 1) == '/') {
354 	    if (*username + 2 != '\0') {
355 		*(username + 1) = ' ';
356 	    } else {
357 		*(username + 1) = '\0';
358 	    }
359 	    HTSprintf0(&command, "/%s%c%c", username, CR, LF);
360 	} else if ((*username == 'w' || *username == 'W') &&
361 		   *(username + 1) == '\0') {
362 	    HTSprintf0(&command, "/%s%c%c", username, CR, LF);
363 	} else if ((slash = strchr(username, '/')) != NULL) {
364 	    *slash++ = '\0';
365 	    if (*slash == 'w' || *slash == 'W') {
366 		HTSprintf0(&command, "/w %s%c%c", username, CR, LF);
367 	    } else {
368 		HTSprintf0(&command, "%s%c%c", username, CR, LF);
369 	    }
370 	} else {
371 	    HTSprintf0(&command, "%s%c%c", username, CR, LF);
372 	}
373 
374 	/* Now, let's get a stream setup up from the FingerHost:
375 	 * CONNECTING to finger host
376 	 */
377 	CTRACE((tfp, "HTFinger: doing HTDoConnect on '%s'\n", str));
378 	status = HTDoConnect(str, "finger", FINGER_PORT, &finger_fd);
379 	CTRACE((tfp, "HTFinger: Done DoConnect; status %d\n", status));
380 
381 	if (status == HT_INTERRUPTED) {
382 	    /* Interrupt cleanly */
383 	    CTRACE((tfp,
384 		    "HTFinger: Interrupted on connect; recovering cleanly.\n"));
385 	    HTProgress(CONNECTION_INTERRUPTED);
386 	    result = HT_NOT_LOADED;
387 	} else if (status < 0) {
388 	    NETCLOSE(finger_fd);
389 	    finger_fd = -1;
390 	    CTRACE((tfp, "HTFinger: Unable to connect to finger host.\n"));
391 	    HTAlert(gettext("Could not access finger host."));
392 	    result = HT_NOT_LOADED;	/* FAIL */
393 	} else {
394 	    CTRACE((tfp, "HTFinger: Connected to finger host '%s'.\n", str));
395 
396 	    /* Send the command, and process response if successful.
397 	     */
398 	    if (response(command, sitename, anAnchor, format_out, stream) != 0) {
399 		HTAlert(gettext("No response from finger server."));
400 		result = HT_NOT_LOADED;
401 	    }
402 	}
403 	FREE(str);
404 	FREE(command);
405     }
406     FREE(param);
407     return result;
408 }
409 
410 #ifdef GLOBALDEF_IS_MACRO
411 #define _HTFINGER_C_1_INIT { "finger", HTLoadFinger, NULL }
412 GLOBALDEF(HTProtocol, HTFinger, _HTFINGER_C_1_INIT);
413 #else
414 GLOBALDEF HTProtocol HTFinger =
415 {"finger", HTLoadFinger, NULL};
416 #endif /* GLOBALDEF_IS_MACRO */
417 
418 #endif /* not DISABLE_FINGER */
419