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