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
30 #ifdef HAVE_UNISTD_H
31 #include <unistd.h>
32 #endif /* HAVE_UNISTD_H */
33
34 #ifndef NO_STRING_H
35 #include <string.h>
36 #else
37 #include <strings.h>
38 #endif
39 #include <signal.h> /* for SIGALRM */
40
41 #include "timidity.h"
42 #include "common.h"
43 #include "url.h"
44 #include "net.h"
45
46 typedef struct _NewsConnection
47 {
48 char *host;
49 unsigned short port;
50 FILE *fp;
51 SOCKET fd;
52 struct _NewsConnection *next;
53 int status; /* -1, 0, 1 */
54 } NewsConnection;
55
56 #define NNTP_OK_ID '2'
57 #define ALARM_TIMEOUT 10
58 /* #define DEBUG */
59
60 static VOLATILE int timeout_flag = 1;
61 static NewsConnection *connection_cache;
62 static int connection_cache_flag = URL_NEWS_CONN_NO_CACHE;
63
64 typedef struct _URL_news
65 {
66 char common[sizeof(struct _URL)];
67
68 NewsConnection *news;
69 int status; /* for detection '\r?\n.\r?\n'
70 * 1 2 34 5
71 */
72 int eof;
73 } URL_news;
74
75 enum
76 {
77 ARTICLE_STATUS_0,
78 ARTICLE_STATUS_1,
79 ARTICLE_STATUS_2,
80 ARTICLE_STATUS_3,
81 ARTICLE_STATUS_4
82 };
83
84 static int name_news_check(char *url_string);
85 static long url_news_read(URL url, void *buff, long n);
86 static int url_news_fgetc(URL url);
87 static void url_news_close(URL url);
88
89 struct URL_module URL_module_news =
90 {
91 URL_news_t,
92 name_news_check,
93 NULL,
94 url_news_open,
95 NULL
96 };
97
name_news_check(char * s)98 static int name_news_check(char *s)
99 {
100 if(strncmp(s, "news://", 7) == 0 && strchr(s, '@') != NULL)
101 return 1;
102 return 0;
103 }
104
105 /*ARGSUSED*/
timeout(int sig)106 static void timeout(int sig)
107 {
108 timeout_flag = 1;
109 }
110
close_news_server(NewsConnection * news)111 static void close_news_server(NewsConnection *news)
112 {
113 if(news->fd != (SOCKET)-1)
114 {
115 socket_write(news->fd, "QUIT\r\n", 6);
116 closesocket(news->fd);
117 }
118 if(news->fp != NULL)
119 socket_fclose(news->fp);
120 free(news->host);
121 news->status = -1;
122 }
123
open_news_server(char * host,unsigned short port)124 static NewsConnection *open_news_server(char *host, unsigned short port)
125 {
126 NewsConnection *p;
127 char buff[512];
128
129 for(p = connection_cache; p != NULL; p = p->next)
130 {
131 if(p->status == 0 && strcmp(p->host, host) == 0 && p->port == port)
132 {
133 p->status = 1;
134 return p;
135 }
136 }
137 for(p = connection_cache; p != NULL; p = p->next)
138 if(p->status == -1)
139 break;
140 if(p == NULL)
141 {
142 if((p = (NewsConnection *)safe_malloc(sizeof(NewsConnection))) == NULL)
143 return NULL;
144 p->next = connection_cache;
145 connection_cache = p;
146 p->status = -1;
147 }
148
149 if((p->host = safe_strdup(host)) == NULL)
150 return NULL;
151 p->port = port;
152
153 #ifdef __W32__
154 timeout_flag = 0;
155 p->fd = open_socket(host, port);
156 #else
157 timeout_flag = 0;
158 signal(SIGALRM, timeout);
159 alarm(ALARM_TIMEOUT);
160 p->fd = open_socket(host, port);
161 alarm(0);
162 signal(SIGALRM, SIG_DFL);
163 #endif /* __W32__ */
164
165 if(p->fd == (SOCKET)-1)
166 {
167 int save_errno;
168
169 VOLATILE_TOUCH(timeout_flag);
170 #ifdef ETIMEDOUT
171 if(timeout_flag)
172 errno = ETIMEDOUT;
173 #endif /* ETIMEDOUT */
174 if(errno)
175 url_errno = errno;
176 else
177 {
178 url_errno = URLERR_CANTOPEN;
179 errno = ENOENT;
180 }
181 #ifdef DEBUG
182 perror(host);
183 #endif /* DEBUG */
184
185 save_errno = errno;
186 free(p->host);
187 errno = save_errno;
188
189 return NULL;
190 }
191
192 if((p->fp = socket_fdopen(p->fd, "rb")) == NULL)
193 {
194 url_errno = errno;
195 closesocket(p->fd);
196 free(p->host);
197 errno = url_errno;
198 return NULL;
199 }
200
201 buff[0] = '\0';
202 if(socket_fgets(buff, sizeof(buff), p->fp) == NULL)
203 {
204 url_errno = errno;
205 closesocket(p->fd);
206 socket_fclose(p->fp);
207 free(p->host);
208 errno = url_errno;
209 return NULL;
210 }
211
212 #ifdef DEBUG
213 fprintf(stderr, "Connect status: %s", buff);
214 #endif /* DEBUG */
215
216 if(buff[0] != NNTP_OK_ID)
217 {
218 closesocket(p->fd);
219 socket_fclose(p->fp);
220 free(p->host);
221 url_errno = URLERR_CANTOPEN;
222 errno = ENOENT;
223 return NULL;
224 }
225 p->status = 1;
226 return p;
227 }
228
url_news_connection_cache(int flag)229 int url_news_connection_cache(int flag)
230 {
231 NewsConnection *p;
232 int oldflag;
233
234 oldflag = connection_cache_flag;
235
236 switch(flag)
237 {
238 case URL_NEWS_CONN_NO_CACHE:
239 case URL_NEWS_CONN_CACHE:
240 connection_cache_flag = flag;
241 break;
242 case URL_NEWS_CLOSE_CACHE:
243 for(p = connection_cache; p != NULL; p = p->next)
244 if(p->status == 0)
245 close_news_server(p);
246 break;
247 case URL_NEWS_GET_FLAG:
248 break;
249 }
250 return oldflag;
251 }
252
url_news_open(char * name)253 URL url_news_open(char *name)
254 {
255 URL_news *url;
256 char *host, *p;
257 unsigned short port;
258 char buff[BUFSIZ], messageID[256];
259 int check_timeout;
260 int i;
261
262 #ifdef DEBUG
263 fprintf(stderr, "url_news_open(%s)\n", name);
264 #endif /* DEBUG */
265
266 url = (URL_news *)alloc_url(sizeof(URL_news));
267 if(url == NULL)
268 {
269 url_errno = errno;
270 return NULL;
271 }
272
273 /* common members */
274 URLm(url, type) = URL_news_t;
275 URLm(url, url_read) = url_news_read;
276 URLm(url, url_gets) = NULL;
277 URLm(url, url_fgetc) = url_news_fgetc;
278 URLm(url, url_seek) = NULL;
279 URLm(url, url_tell) = NULL;
280 URLm(url, url_close) = url_news_close;
281
282 /* private members */
283 url->news = NULL;
284 url->status = ARTICLE_STATUS_2;
285 url->eof = 0;
286
287 if(strncmp(name, "news://", 7) == 0)
288 name += 7;
289
290 strncpy(buff, name, sizeof(buff) - 1);
291 buff[sizeof(buff) - 1] = '\0';
292
293 host = buff;
294 if (host[0] == '[')
295 {
296 if (!(p = strchr(host, ']')))
297 return NULL;
298 *p = '\0';
299 ++host;
300 ++p;
301 } else
302 for(p = host; *p && *p != ':' && *p != '/'; p++)
303 ;
304
305 if(*p == ':')
306 {
307 *p++ = '\0'; /* terminate `host' string */
308 port = atoi(p);
309 p = strchr(p, '/');
310 if(p == NULL)
311 {
312 url_errno = URLERR_CANTOPEN;
313 errno = ENOENT;
314 url_news_close((URL)url);
315 return NULL;
316 }
317 }
318 else
319 port = 119;
320 *p++ = '\0'; /* terminate `host' string */
321 if(*p == '<')
322 p++;
323 strncpy(messageID, p, sizeof(messageID) - 1);
324 messageID[sizeof(messageID) - 1] = '\0';
325 i = strlen(messageID);
326 if(i > 0 && messageID[i - 1] == '>')
327 messageID[i - 1] = '\0';
328
329 #ifdef DEBUG
330 fprintf(stderr, "messageID: <%s>\n", messageID);
331 #endif /* DEBUG */
332
333 #ifdef DEBUG
334 fprintf(stderr, "open(host=`%s', port=`%d')\n", host, port);
335 #endif /* DEBUG */
336
337 if((url->news = open_news_server(host, port)) == NULL)
338 {
339 url_news_close((URL)url);
340 return NULL;
341 }
342
343 check_timeout = 1;
344 retry_article:
345
346 sprintf(buff, "ARTICLE <%s>\r\n", messageID);
347
348 #ifdef DEBUG
349 fprintf(stderr, "CMD> %s", buff);
350 #endif /* DEBUG */
351
352 socket_write(url->news->fd, buff, (long)strlen(buff));
353 buff[0] = '\0';
354 if(socket_fgets(buff, sizeof(buff), url->news->fp) == NULL)
355 {
356 if(check_timeout)
357 {
358 check_timeout = 0;
359 close_news_server(url->news);
360 if((url->news = open_news_server(host, port)) != NULL)
361 goto retry_article;
362 }
363 url_news_close((URL)url);
364 url_errno = URLERR_CANTOPEN;
365 errno = ENOENT;
366 return NULL;
367 }
368
369 #ifdef DEBUG
370 fprintf(stderr, "CMD< %s", buff);
371 #endif /* DEBUG */
372
373 if(buff[0] != NNTP_OK_ID)
374 {
375 if(check_timeout && strncmp(buff, "503", 3) == 0)
376 {
377 check_timeout = 0;
378 close_news_server(url->news);
379 if((url->news = open_news_server(host, port)) != NULL)
380 goto retry_article;
381 }
382 url_news_close((URL)url);
383 url_errno = errno = ENOENT;
384 return NULL;
385 }
386 return (URL)url;
387 }
388
url_news_close(URL url)389 static void url_news_close(URL url)
390 {
391 URL_news *urlp = (URL_news *)url;
392 NewsConnection *news = urlp->news;
393 int save_errno = errno;
394
395 if(news != NULL)
396 {
397 if(connection_cache_flag == URL_NEWS_CONN_CACHE)
398 news->status = 0;
399 else
400 close_news_server(news);
401 }
402 free(url);
403
404 errno = save_errno;
405 }
406
url_news_read(URL url,void * buff,long size)407 static long url_news_read(URL url, void *buff, long size)
408 {
409 char *p = (char *)buff;
410 long n;
411 int c;
412
413 n = 0;
414 while(n < size)
415 {
416 if((c = url_news_fgetc(url)) == EOF)
417 break;
418 p[n++] = c;
419 }
420 return n;
421 }
422
url_news_fgetc(URL url)423 static int url_news_fgetc(URL url)
424 {
425 URL_news *urlp = (URL_news *)url;
426 NewsConnection *news = urlp->news;
427 int c;
428
429 if(urlp->eof)
430 return EOF;
431 if((c = socket_fgetc(news->fp)) == EOF)
432 {
433 urlp->eof = 1;
434 return EOF;
435 }
436
437 switch(urlp->status)
438 {
439 case ARTICLE_STATUS_0:
440 if(c == '\r')
441 urlp->status = ARTICLE_STATUS_1;
442 else if(c == '\n')
443 urlp->status = ARTICLE_STATUS_2;
444 break;
445
446 case ARTICLE_STATUS_1:
447 if(c == '\n')
448 urlp->status = ARTICLE_STATUS_2;
449 else
450 urlp->status = ARTICLE_STATUS_0;
451 break;
452
453 case ARTICLE_STATUS_2:
454 if(c == '.')
455 urlp->status = ARTICLE_STATUS_3;
456 else
457 urlp->status = ARTICLE_STATUS_0;
458 break;
459
460 case ARTICLE_STATUS_3:
461 if(c == '\r')
462 urlp->status = ARTICLE_STATUS_4;
463 else if(c == '\n')
464 urlp->eof = 1;
465 else
466 urlp->status = ARTICLE_STATUS_0;
467 break;
468
469 case ARTICLE_STATUS_4:
470 if(c == '\n')
471 urlp->eof = 1;
472 break;
473 }
474
475 return c;
476 }
477
478 #ifdef NEWS_MAIN
main(int argc,char ** argv)479 void main(int argc, char **argv)
480 {
481 URL url;
482 char buff[BUFSIZ];
483 int c;
484
485 if(argc != 2)
486 {
487 fprintf(stderr, "Usage: %s news-URL\n", argv[0]);
488 exit(1);
489 }
490 if((url = url_news_open(argv[1])) == NULL)
491 {
492 fprintf(stderr, "Can't open news group: %s\n", argv[1]);
493 exit(1);
494 }
495
496 #if NEWS_MAIN
497 while((c = url_getc(url)) != EOF)
498 putchar(c);
499 #else
500 while((c = url_read(url, buff, sizeof(buff))) > 0)
501 write(1, buff, c);
502 #endif
503 url_close(url);
504 exit(0);
505 }
506 #endif /* NEWS_MAIN */
507