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