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