1 /*
2 Copyright (C) 2000 Masanao Izumo <mo@goice.co.jp>
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19 #include "config.h"
20 #include <stdio.h>
21 #include <stdlib.h>
22 #ifdef HAVE_UNISTD_H
23 #include <unistd.h>
24 #endif /* HAVE_UNISTD_H */
25 #ifndef NO_STRING_H
26 #include <string.h>
27 #else
28 #include <strings.h>
29 #endif
30 #include <signal.h> /* for SIGALRM */
31
32 #include "libarc/url.h"
33 #include "net.h"
34
35 #define NNTP_OK_ID '2'
36 #define MAX_LINE_BUFF 1024
37 #define ALARM_TIMEOUT 10
38 /* #define DEBUG */
39
40 #ifdef URL_NEWS_XOVER_SUPPORT
41 static char *xover_commands[] = {URL_NEWS_XOVER_SUPPORT, NULL};
42 #else
43 static char *xover_commands[] = {NULL};
44 #endif /* URL_NEWS_XOVER_SUPPORT */
45
46 static VOLATILE int timeout_flag = 1;
47
48 typedef struct _URL_newsgroup
49 {
50 char common[sizeof(struct _URL)];
51
52 FILE *fp;
53 SOCKET fd;
54 int first, last;
55 int minID, maxID;
56 int xover;
57 int eof;
58 char *name;
59 } URL_newsgroup;
60
61 static int name_newsgroup_check(char *url_string);
62 static long url_newsgroup_read(URL url, void *buff, long n);
63 static char *url_newsgroup_gets(URL url, char *buff, int n);
64 static void url_newsgroup_close(URL url);
65
66 struct URL_module URL_module_newsgroup =
67 {
68 URL_newsgroup_t,
69 name_newsgroup_check,
70 NULL,
71 url_newsgroup_open,
72 NULL
73 };
74
name_newsgroup_check(char * s)75 static int name_newsgroup_check(char *s)
76 {
77 if(strncmp(s, "news://", 7) == 0 && strchr(s, '@') == NULL)
78 return 1;
79 return 0;
80 }
81
82 /*ARGSUSED*/
timeout(int sig)83 static void timeout(int sig)
84 {
85 timeout_flag = 1;
86 }
87
url_newsgroup_open(char * name)88 URL url_newsgroup_open(char *name)
89 {
90 URL_newsgroup *url;
91 SOCKET fd;
92 char *host, *p, *urlname;
93 unsigned short port;
94 char buff[BUFSIZ], group[256], *range;
95 int n;
96
97 #ifdef DEBUG
98 printf("url_newsgroup_open(%s)\n", name);
99 #endif /* DEBUG */
100
101 if((urlname = strdup(name)) == NULL)
102 return NULL;
103 n = strlen(urlname);
104 while(n > 0 && urlname[n - 1] == '/')
105 urlname[--n] = '\0';
106
107 url = (URL_newsgroup *)alloc_url(sizeof(URL_newsgroup));
108 if(url == NULL)
109 {
110 url_errno = errno;
111 free(urlname);
112 errno = url_errno;
113 return NULL;
114 }
115
116 /* common members */
117 URLm(url, type) = URL_newsgroup_t;
118 URLm(url, url_read) = url_newsgroup_read;
119 URLm(url, url_gets) = url_newsgroup_gets;
120 URLm(url, url_fgetc) = NULL;
121 URLm(url, url_seek) = NULL;
122 URLm(url, url_tell) = NULL;
123 URLm(url, url_close) = url_newsgroup_close;
124
125 /* private members */
126 url->fd = (SOCKET)-1;
127 url->fp = NULL;
128 url->xover = -1;
129 url->eof = 0;
130 url->first = url->last = 0;
131 url->minID = url->maxID = 0;
132 url->name = urlname;
133
134 if(strncmp(name, "news://", 7) == 0)
135 name += 7;
136
137 strncpy(buff, name, sizeof(buff) - 1);
138 buff[sizeof(buff) - 1] = '\0';
139
140 host = buff;
141 for(p = host; *p && *p != ':' && *p != '/'; p++)
142 ;
143 if(*p == ':')
144 {
145 *p++ = '\0'; /* terminate `host' string */
146 port = atoi(p);
147 p = strchr(p, '/');
148 if(p == NULL)
149 {
150 url_errno = URLERR_CANTOPEN;
151 errno = ENOENT;
152 url_newsgroup_close((URL)url);
153 return NULL;
154 }
155 }
156 else
157 port = 119;
158 *p++ = '\0'; /* terminate `host' string */
159 strncpy(group, p, sizeof(group) - 1);
160 group[sizeof(group) - 1] = '\0';
161
162 if((range = strchr(group, '/')) != NULL)
163 *range++ = '\0';
164
165 #ifdef DEBUG
166 printf("group: %s\n", group);
167 #endif /* DEBUG */
168
169 #ifdef DEBUG
170 printf("open(host=`%s', port=`%d')\n", host, port);
171 #endif /* DEBUG */
172
173 #ifdef __W32__
174 timeout_flag = 0;
175 fd = open_socket(host, port);
176 #else
177 timeout_flag = 0;
178 signal(SIGALRM, timeout);
179 alarm(ALARM_TIMEOUT);
180 url->fd = fd = open_socket(host, port);
181 alarm(0);
182 signal(SIGALRM, SIG_DFL);
183 #endif /* __W32__ */
184
185 if(fd == (SOCKET)-1)
186 {
187 VOLATILE_TOUCH(timeout_flag);
188 #ifdef ETIMEDOUT
189 if(timeout_flag)
190 errno = ETIMEDOUT;
191 #endif /* ETIMEDOUT */
192 if(errno)
193 url_errno = errno;
194 else
195 {
196 url_errno = URLERR_CANTOPEN;
197 errno = ENOENT;
198 }
199 url_newsgroup_close((URL)url);
200 return NULL;
201 }
202
203 if((url->fp = socket_fdopen(fd, "rb")) == NULL)
204 {
205 url_errno = errno;
206 closesocket(fd);
207 url_newsgroup_close((URL)url);
208 errno = url_errno;
209 return NULL;
210 }
211
212 if(socket_fgets(buff, sizeof(buff), url->fp) == NULL)
213 {
214 url_newsgroup_close((URL)url);
215 return NULL;
216 }
217
218 #ifdef DEBUG
219 printf("Connect status: %s", buff);
220 #endif /* DEBUG */
221
222 if(buff[0] != NNTP_OK_ID)
223 {
224 url_newsgroup_close((URL)url);
225 url_errno = URLERR_CANTOPEN;
226 errno = ENOENT;
227 return NULL;
228 }
229
230 sprintf(buff, "GROUP %s\r\n", group);
231
232 #ifdef DEBUG
233 printf("CMD> %s", buff);
234 #endif /* DEBUG */
235
236 socket_write(fd, buff, (long)strlen(buff));
237 if(socket_fgets(buff, sizeof(buff), url->fp) == NULL)
238 {
239 url_newsgroup_close((URL)url);
240 url_errno = URLERR_CANTOPEN;
241 errno = ENOENT;
242 return NULL;
243 }
244
245 #ifdef DEBUG
246 printf("CMD< %s", buff);
247 #endif /* DEBUG */
248
249 if(buff[0] != NNTP_OK_ID)
250 {
251 url_newsgroup_close((URL)url);
252 url_errno = URLERR_CANTOPEN;
253 errno = ENOENT;
254 return NULL;
255 }
256
257 p = buff + 4;
258 if(*p == '0') /* No article */
259 url->eof = 1;
260 p++;
261 while('0' <= *p && *p <= '9')
262 p++;
263 while(*p == ' ')
264 p++;
265 url->first = url->minID = atoi(p);
266 while('0' <= *p && *p <= '9')
267 p++;
268 while(*p == ' ')
269 p++;
270 url->last = url->maxID = atoi(p);
271
272 if(range != NULL)
273 {
274 if('0' <= *range && *range <= '9')
275 {
276 url->first = atoi(range);
277 if(url->first < url->minID)
278 url->first = url->minID;
279 }
280 if((range = strchr(range, '-')) != NULL)
281 {
282 range++;
283 if('0' <= *range && *range <= '9')
284 {
285 url->last = atoi(range);
286 if(url->last > url->maxID)
287 url->last = url->maxID;
288 }
289 }
290 }
291
292 return (URL)url;
293 }
294
url_newsgroup_name(URL url)295 char *url_newsgroup_name(URL url)
296 {
297 if(url->type != URL_newsgroup_t)
298 return NULL;
299 return ((URL_newsgroup *)url)->name;
300 }
301
url_newsgroup_close(URL url)302 static void url_newsgroup_close(URL url)
303 {
304 URL_newsgroup *urlp = (URL_newsgroup *)url;
305 int save_errno = errno;
306 if(urlp->fd != (SOCKET)-1)
307 {
308 socket_write(urlp->fd, "QUIT\r\n", 6);
309 closesocket(urlp->fd);
310 }
311 if(urlp->fp != NULL)
312 socket_fclose(urlp->fp);
313 if(urlp->name != NULL)
314 free(urlp->name);
315 free(url);
316 errno = save_errno;
317 }
318
url_newsgroup_read(URL url,void * buff,long n)319 static long url_newsgroup_read(URL url, void *buff, long n)
320 {
321 char *p;
322
323 p = url_newsgroup_gets(url, (char *)buff, n);
324 if(p == NULL)
325 return 0;
326 return (long)strlen(p);
327 }
328
url_newsgroup_gets(URL url,char * buff,int n)329 static char *url_newsgroup_gets(URL url, char *buff, int n)
330 {
331 URL_newsgroup *urlp = (URL_newsgroup *)url;
332 char linebuff[MAX_LINE_BUFF], *p, numbuf[32];
333 int i, j, nump;
334 int find_first;
335
336 if(urlp->eof || n <= 0)
337 return NULL;
338 if(n == 1)
339 {
340 buff[0] = '\0';
341 return buff;
342 }
343
344 find_first = 0;
345 if(urlp->xover == -1)
346 {
347 urlp->xover = 0;
348 for(i = 0; xover_commands[i] != NULL; i++)
349 {
350 sprintf(linebuff, "%s %d-%d\r\n", xover_commands[i],
351 urlp->first, urlp->last);
352 #ifdef DEBUG
353 printf("CMD> %s", linebuff);
354 #endif /* DEBUG */
355 socket_write(urlp->fd, linebuff, (long)strlen(linebuff));
356 if(socket_fgets(linebuff, sizeof(linebuff), urlp->fp) == NULL)
357 {
358 urlp->eof = 1;
359 return NULL;
360 }
361 #ifdef DEBUG
362 printf("CMD< %s", linebuff);
363 #endif /* DEBUG */
364 if(linebuff[0] == NNTP_OK_ID)
365 {
366 urlp->xover = 1;
367 break;
368 }
369 }
370 if(!urlp->xover)
371 find_first = 1;
372 }
373 else if(!urlp->xover)
374 socket_write(urlp->fd, "NEXT\r\n", 6);
375
376 next_read:
377 if(find_first)
378 {
379 for(i = urlp->first; i <= urlp->last; i++)
380 {
381 sprintf(linebuff, "STAT %d\r\n", i);
382 #ifdef DEBUG
383 printf("CMD> %s", linebuff);
384 #endif /* DEBUG */
385 socket_write(urlp->fd, linebuff, (long)strlen(linebuff));
386 if(socket_fgets(linebuff, sizeof(linebuff), urlp->fp) == NULL)
387 {
388 urlp->eof = 1;
389 return NULL;
390 }
391 #ifdef DEBUG
392 printf("CMD< %s", linebuff);
393 #endif /* DEBUG */
394 if(atoi(linebuff) != 423)
395 break;
396 }
397 if(i > urlp->last)
398 {
399 urlp->eof = 1;
400 return NULL;
401 }
402 find_first = 0;
403 }
404 else
405 {
406 if(socket_fgets(linebuff, sizeof(linebuff), urlp->fp) == NULL)
407 {
408 urlp->eof = 1;
409 return NULL;
410 }
411
412 i = strlen(linebuff);
413 if(i > 0 && linebuff[i - 1] != '\n')
414 {
415 int c;
416
417 do
418 {
419 c = socket_fgetc(urlp->fp);
420 } while(c != '\n' && c != EOF);
421 }
422 }
423 p = linebuff;
424 #ifdef DEBUG
425 printf("line: %s", linebuff);
426 #endif /* DEBUG */
427
428 if(urlp->xover == 0)
429 {
430 if(p[0] != '2')
431 {
432 if(strncmp(p, "421", 3) == 0)
433 {
434 urlp->eof = 1;
435 return NULL;
436 }
437
438 socket_write(urlp->fd, "NEXT\r\n", 6);
439 goto next_read;
440 }
441
442 p += 3;
443 while(*p == ' ' || *p == '\t')
444 p++;
445 nump = 0;
446 i = atoi(p);
447 if(i > urlp->last)
448 {
449 urlp->eof = 1;
450 return NULL;
451 }
452 if(i == urlp->last)
453 urlp->eof = 1;
454 while('0' <= *p && *p <= '9' && nump < sizeof(numbuf))
455 numbuf[nump++] = *p++;
456 if(nump == 0)
457 {
458 socket_write(urlp->fd, "NEXT\r\n", 6);
459 goto next_read;
460 }
461
462 if((p = strchr(linebuff, '<')) == NULL)
463 {
464 socket_write(urlp->fd, "NEXT\r\n", 6);
465 goto next_read;
466 }
467 }
468 else
469 {
470 int i;
471
472 if(linebuff[0] == '.')
473 {
474 urlp->eof = 1;
475 return NULL;
476 }
477
478 nump = 0;
479 while('0' <= linebuff[nump] && linebuff[nump] <= '9'
480 && nump < sizeof(numbuf))
481 {
482 numbuf[nump] = linebuff[nump];
483 nump++;
484 }
485
486 for(i = 0; i < 4; i++)
487 {
488 p = strchr(p, '\t');
489 if(p == NULL)
490 goto next_read;
491 p++;
492 }
493 }
494
495 if(*p == '<')
496 p++;
497
498 i = j = 0;
499 while(j < n - 2 && j < nump)
500 {
501 buff[j] = numbuf[j];
502 j++;
503 }
504
505 buff[j++] = ' ';
506 while(j < n - 1 && p[i] && p[i] != '>' && p[i] != ' ' && p[i] != '\t')
507 {
508 buff[j] = p[i];
509 i++;
510 j++;
511 }
512 buff[j] = '\0';
513 return buff;
514 }
515
516 #ifdef NEWSGROUP_MAIN
safe_malloc(int n)517 void *safe_malloc(int n) { return malloc(n); }
safe_realloc(void * p,int n)518 void *safe_realloc(void *p, int n) { return realloc(p, n); }
main(int argc,char ** argv)519 void main(int argc, char **argv)
520 {
521 URL url;
522 char buff[BUFSIZ];
523
524 if(argc != 2)
525 {
526 fprintf(stderr, "Usage: %s news-URL\n", argv[0]);
527 exit(1);
528 }
529 if((url = url_newsgroup_open(argv[1])) == NULL)
530 {
531 fprintf(stderr, "Can't open news group: %s\n", argv[1]);
532 exit(1);
533 }
534
535 while(url_gets(url, buff, sizeof(buff)) != NULL)
536 puts(buff);
537 url_close(url);
538 exit(0);
539 }
540 #endif /* NEWSGROUP_MAIN */
541