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