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 #ifndef NO_STRING_H
23 #include <string.h>
24 #else
25 #include <strings.h>
26 #endif
27 #ifdef HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif /* HAVE_UNISTD_H */
30 #include <signal.h> /* for SIGALRM */
31 
32 #include "libarc/url.h"
33 #include "net.h"
34 
35 /* supported PASV mode only */
36 
37 #define ALARM_TIMEOUT 10
38 static VOLATILE int timeout_flag = 0;
39 
40 #ifdef FTP_PROXY_HOST
41 char *url_ftp_proxy_host = FTP_PROXY_HOST
42 unsigned short url_ftp_proxy_port = FTP_PROXY_PORT
43 #else
44 char *url_ftp_proxy_host = NULL;
45 unsigned short url_ftp_proxy_port;
46 #endif /* FTP_PROXY_HOST */
47 
48 
49 typedef struct _URL_ftp
50 {
51     char common[sizeof(struct _URL)];
52     FILE *datafp;
53     FILE *ctlifp;
54     FILE *ctlofp;
55     int abor;
56 } URL_ftp;
57 
58 static int name_ftp_check(char *url_string);
59 static long url_ftp_read(URL url, void *buff, long size);
60 static char *url_ftp_gets(URL url, char *buff, int n);
61 static int url_ftp_fgetc(URL url);
62 static void url_ftp_close(URL url);
63 static int guess_errno(char *msg);
64 
65 struct URL_module URL_module_ftp =
66 {
67     URL_ftp_t,
68     name_ftp_check,
69     NULL,
70     url_ftp_open,
71     NULL
72 };
73 
name_ftp_check(char * s)74 static int name_ftp_check(char *s)
75 {
76     if(strncmp(s, "ftp://", 6) == 0)
77 	return 1;
78     return 0;
79 }
80 
ftp_cmd(URL_ftp * url,char * buff,char * rspns)81 static int ftp_cmd(URL_ftp *url, char *buff, char *rspns)
82 {
83 #ifdef DEBUG
84     printf("FTP<%s", buff);
85 #endif
86     errno = 0;
87     if(socket_fwrite(buff, (long)strlen(buff), url->ctlofp) <= 0)
88     {
89 	url_ftp_close((URL)url);
90 	if(errno)
91 	    url_errno = errno;
92 	else
93 	    url_errno = errno = ENOENT;
94 	return -1;
95     }
96     socket_fflush(url->ctlofp);
97     do
98     {
99 	errno = 0;
100 	if(socket_fgets(buff, BUFSIZ, url->ctlifp) == NULL)
101 	{
102 	    url_ftp_close((URL)url);
103 	    if(errno)
104 		url_errno = errno;
105 	    else
106 		url_errno = errno = ENOENT;
107 	    return -1;
108 	}
109 #ifdef DEBUG
110 	printf("FTP>%s", buff);
111 #endif
112 	if(strncmp(buff, rspns, 3) != 0)
113 	{
114 	    url_ftp_close((URL)url);
115 	    url_errno = errno = guess_errno(buff);
116 	    return -1;
117 	}
118     } while(buff[3] == '-');
119     return 0;
120 }
121 
122 /*ARGSUSED*/
timeout(int sig)123 static void timeout(int sig)
124 {
125     timeout_flag = 1;
126 }
127 
guess_errno(char * msg)128 static int guess_errno(char *msg)
129 {
130     if(strncmp(msg, "550", 3) != 0)
131 	return ENOENT;
132     if((msg = strchr(msg, ':')) == NULL)
133 	return ENOENT;
134     msg++;
135     if(*msg == ' ')
136 	msg++;
137     if(strncmp(msg, "No such file or directory", 25) == 0)
138 	return ENOENT;
139     if(strncmp(msg, "Permission denied", 17) == 0)
140 	return EACCES;
141     if(strncmp(msg, "HTTP/1.0 500", 12) == 0) /* Proxy Error */
142 	return ENOENT;
143     return ENOENT;
144 }
145 
url_ftp_open(char * name)146 URL url_ftp_open(char *name)
147 {
148     URL_ftp *url;
149     SOCKET fd;
150     char *p, *host, *path;
151     unsigned short port;
152     char buff[BUFSIZ];
153     char path_buff[1024], host_buff[1024];
154     int n;
155     char *passwd;
156     char *user;
157 
158 #ifdef DEBUG
159     printf("url_ftp_open(%s)\n", name);
160 #endif /* DEBUG */
161 
162     passwd = user_mailaddr;
163     user   = "anonymous";
164 
165     url = (URL_ftp *)alloc_url(sizeof(URL_ftp));
166     if(url == NULL)
167     {
168 	url_errno = errno;
169 	return NULL;
170     }
171 
172     /* common members */
173     URLm(url, type)      = URL_ftp_t;
174     URLm(url, url_read)  = url_ftp_read;
175     URLm(url, url_gets)  = url_ftp_gets;
176     URLm(url, url_fgetc) = url_ftp_fgetc;
177     URLm(url, url_seek)  = NULL;
178     URLm(url, url_tell)  = NULL;
179     URLm(url, url_close) = url_ftp_close;
180 
181     /* private members */
182     url->datafp = NULL;
183     url->ctlifp = NULL;
184     url->ctlofp = NULL;
185     url->abor = 0;
186 
187     if(url_ftp_proxy_host != NULL)
188     {
189 	/* proxy */
190 	host = url_ftp_proxy_host;
191 	port = url_ftp_proxy_port;
192     }
193     else
194     {
195 	/* not proxy */
196 	if(strncmp(name, "ftp://", 6) == 0)
197 	    name += 6;
198 	strncpy(buff, name, sizeof(buff));
199 	buff[sizeof(buff) - 1] = '\0';
200 
201 	strncpy(host_buff, buff, sizeof(host_buff));
202 	host_buff[sizeof(host_buff) - 1] = '\0';
203 	host = host_buff;
204 
205 	if((p = strchr(host, '/')) == NULL)
206 	{
207 	    url_ftp_close((URL)url);
208 	    url_errno = URLERR_IURLF;
209 	    errno = ENOENT;
210 	    return NULL;
211 	}
212 
213 	port = 21;
214 	*p = '\0';
215 	strncpy(path_buff, name + strlen(host), sizeof(path_buff));
216 	path_buff[sizeof(path_buff) - 1] = '\0';
217 	path = path_buff;
218 
219 	/* check user:password@host */
220 	p = strchr(host, '@');
221 	if(p != NULL)
222 	{
223 	    user = host;
224 	    host = p;
225 	    *host++ = '\0';
226 	    if((passwd = strchr(user, ':')) == NULL)
227 		passwd = user_mailaddr;
228 	    else
229 		*passwd++ = '\0';
230 	}
231 
232 #ifdef DEBUG
233 	printf("open(host=`%s', port=`%d')\n", host, port);
234 #endif /* DEBUG */
235 
236 #ifdef __W32__
237 	timeout_flag = 0;
238 	fd = open_socket(host, port);
239 #else
240 	timeout_flag = 0;
241 	signal(SIGALRM, timeout);
242 	alarm(ALARM_TIMEOUT);
243 	fd = open_socket(host, port);
244 	alarm(0);
245 	signal(SIGALRM, SIG_DFL);
246 #endif /* __W32__ */
247 
248 	if(fd < 0)
249 	{
250 	    VOLATILE_TOUCH(timeout_flag);
251 #ifdef ETIMEDOUT
252 	    if(timeout_flag)
253 		errno = ETIMEDOUT;
254 #endif /* ETIMEDOUT */
255 
256 	    if(errno)
257 		url_errno = errno;
258 	    else
259 	    {
260 		url_errno = URLERR_CANTOPEN;
261 		errno = ENOENT;
262 	    }
263 	    url_ftp_close((URL)url);
264 	    return NULL;
265 	}
266 
267 	if((url->ctlifp = socket_fdopen(fd, "rb")) == NULL)
268 	{
269 	    url_ftp_close((URL)url);
270 	    url_errno = errno;
271 	    return NULL;
272 	}
273 
274 	if((url->ctlofp = socket_fdopen(fd, "wb")) == NULL)
275 	{
276 	    url_ftp_close((URL)url);
277 	    url_errno = errno;
278 	    return NULL;
279 	}
280 
281 	if(socket_fgets(buff, BUFSIZ, url->ctlifp) == NULL)
282 	{
283 	    url_ftp_close((URL)url);
284 	    url_errno = URLERR_CANTOPEN;
285 	    errno = ENOENT;
286 	    return NULL;
287 	}
288 
289 	if(strncmp(buff, "220 ", 4) != 0)
290 	{
291 	    url_ftp_close((URL)url);
292 	    url_errno = URLERR_CANTOPEN;
293 	    errno = ENOENT;
294 	    return NULL;
295 	}
296 
297 	/* login */
298 	sprintf(buff, "USER %s\r\n", user);
299 	if(ftp_cmd(url, buff, "331") < 0)
300 	    return NULL;
301 
302 	/* password */
303 	if(passwd == NULL)
304 	    sprintf(buff, "PASS Unknown@liburl.a\r\n");
305 	else
306 	    sprintf(buff, "PASS %s\r\n", passwd);
307 	if(ftp_cmd(url, buff, "230") < 0)
308 	    return NULL;
309 
310 	/* CWD */
311 	if(path[1] == '0')
312 	    /* Here is root */;
313 	else
314 	{
315 	    path++; /* skip '/' */
316 	    while((p = strchr(path, '/')) != NULL)
317 	    {
318 		*p = '\0';
319 		sprintf(buff, "CWD %s\r\n", path);
320 		if(ftp_cmd(url, buff, "250") < 0)
321 		    return NULL;
322 		path = p + 1;
323 	    }
324 	    if(!*path)
325 	    {
326 		url_ftp_close((URL)url);
327 		url_errno = URLERR_IURLF;
328 		errno = ENOENT;
329 		return NULL;
330 	    }
331 	}
332 
333 	/* TYPE I */
334 	strcpy(buff, "TYPE I\r\n");
335 	if(ftp_cmd(url, buff, "200") < 0)
336 	    return NULL;
337 
338 	/* PASV */
339 	strcpy(buff, "PASV\r\n");
340 	if(ftp_cmd(url, buff, "227") < 0)
341 	    return NULL;
342 
343 	/* Parse PASV
344 	 * 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2)
345 	 */
346 	p = buff + 4;
347 
348 	while(*p && (*p < '0' || *p > '9'))
349 	    p++;
350 	if(*p == '\0')
351 	{
352 	    url_ftp_close((URL)url);
353 	    url_errno = URLERR_CANTOPEN;
354 	    errno = ENOENT;
355 	    return NULL;
356 	}
357 	host = p;
358 	n = 0; /* number of commas */
359 	while(n < 4)
360 	{
361 	    if((p = strchr(p, ',')) == NULL)
362 	    {
363 		url_ftp_close((URL)url);
364 		url_errno = URLERR_CANTOPEN;
365 		errno = ENOENT;
366 		return NULL;
367 	    }
368 	    *p = '.';
369 	    n++;
370 	}
371 	*p++ = '\0';
372 
373 	port = atoi(p) * 256;
374 	if((p = strchr(p, ',')) == NULL)
375 	{
376 	    url_ftp_close((URL)url);
377 	    url_errno = URLERR_CANTOPEN;
378 	    errno = ENOENT;
379 	    return NULL;
380 	}
381 	port += atoi(p + 1);
382 
383 	/* RETR */
384 	socket_fwrite("RETR ", 5, url->ctlofp);
385 	socket_fwrite(path, (long)strlen(path), url->ctlofp);
386 	socket_fwrite("\r\n", 2, url->ctlofp);
387 	socket_fflush(url->ctlofp);
388 
389 #ifdef DEBUG
390 	printf("FTP>RETR %s\r\n", path);
391 #endif /* DEBUG */
392     }
393 
394     /* Open data connection. */
395 #ifdef DEBUG
396     printf("open(host=`%s', port=`%d')\n", host, port);
397 #endif /* DEBUG */
398 
399     if((fd = open_socket(host, port)) < 0)
400     {
401 	url_ftp_close((URL)url);
402 	if(errno)
403 	    url_errno = errno;
404 	else
405 	    url_errno = errno = ENOENT;
406 	return NULL;
407     }
408     if((url->datafp = socket_fdopen(fd, "rb")) == NULL)
409     {
410 	url_errno = errno;
411 	closesocket(fd);
412 	url_ftp_close((URL)url);
413 	errno = url_errno;
414 	return NULL;
415     }
416 
417     if(url_ftp_proxy_host != NULL)
418     {
419 	/* proxy */
420 	sprintf(buff, "GET %s HTTP/1.0\r\n", name);
421 	socket_write(fd, buff, (long)strlen(buff));
422 #ifdef DEBUG
423 	printf("FTP<%s", buff);
424 #endif /* DEBUG */
425 
426 	if(url_user_agent)
427 	{
428 	    sprintf(buff, "User-Agent: %s\r\n", url_user_agent);
429 	    socket_write(fd, buff, (long)strlen(buff));
430 #ifdef DEBUG
431 	    printf("FTP<%s", buff);
432 #endif /* DEBUG */
433 	}
434 	socket_write(fd, "\r\n", 2);
435 	errno = 0;
436 	if(socket_fgets(buff, BUFSIZ, url->datafp) == NULL)
437 	{
438 	    if(errno == 0)
439 		errno = ENOENT;
440 	    url_errno = errno;
441 	    url_ftp_close((URL)url);
442 	    return NULL;
443 	}
444 #ifdef DEBUG
445 	printf("FTP>%s", buff);
446 #endif /* DEBUG */
447 
448 	p = buff;
449 	if(strncmp(p, "HTTP/1.0 ", 9) == 0 || strncmp(p, "HTTP/1.1 ", 9) == 0)
450 	    p += 9;
451 	if(strncmp(p, "200", 3) != 0) /* Not success */
452 	{
453 	    url_ftp_close((URL)url);
454 	    url_errno = errno = guess_errno(buff);
455 	    return NULL;
456 	}
457 
458 	/* Skip mime header */
459 	while(socket_fgets(buff, BUFSIZ, url->datafp) != NULL)
460 	{
461 	    if(buff[0] == '\n' || (buff[0] == '\r' && buff[1] == '\n'))
462 		break; /* end of heaer */
463 #ifdef DEBUG
464 	    printf("FTP>%s", buff);
465 #endif /* DEBUG */
466 	}
467     }
468     else
469     {
470 	/* not proxy */
471 	if(socket_fgets(buff, BUFSIZ, url->ctlifp) == NULL)
472 	{
473 	    url_ftp_close((URL)url);
474 	    url_errno = errno;
475 	    return NULL;
476 	}
477 
478 #ifdef DEBUG
479 	printf("FTP<%s", buff);
480 #endif /* DEBUG */
481 
482 	if(strncmp(buff, "150", 3) != 0)
483 	{
484 	    url_ftp_close((URL)url);
485 	    url_errno = errno = guess_errno(buff);
486 	    return NULL;
487 	}
488 	url->abor = 1;
489     }
490 
491 #ifdef __W32__
492     return url_buff_open((URL)url, 1);
493 #else
494     return (URL)url;
495 #endif /* __W32__ */
496 }
497 
url_ftp_read(URL url,void * buff,long n)498 static long url_ftp_read(URL url, void *buff, long n)
499 {
500     URL_ftp *urlp = (URL_ftp *)url;
501 
502     n = socket_fread(buff, n, urlp->datafp);
503     if(n <= 0)
504 	urlp->abor = 0;
505     return n;
506 }
507 
url_ftp_gets(URL url,char * buff,int n)508 static char *url_ftp_gets(URL url, char *buff, int n)
509 {
510     URL_ftp *urlp = (URL_ftp *)url;
511 
512     buff = socket_fgets(buff, n, urlp->datafp);
513     if(buff == NULL)
514 	urlp->abor = 0;
515     return buff;
516 }
517 
url_ftp_fgetc(URL url)518 static int url_ftp_fgetc(URL url)
519 {
520     URL_ftp *urlp = (URL_ftp *)url;
521     int n;
522     unsigned char c;
523 
524     n = socket_fread(&c, 1, urlp->datafp);
525     if(n <= 0)
526     {
527 	urlp->abor = 0;
528 	if(errno)
529 	    url_errno = errno;
530 	return EOF;
531     }
532     return (int)c;
533 }
534 
url_ftp_close(URL url)535 static void url_ftp_close(URL url)
536 {
537     URL_ftp *urlp = (URL_ftp *)url;
538     int save_errno = errno;
539 
540     if(urlp->datafp != NULL)
541 	socket_fclose(urlp->datafp);
542     else
543 	urlp->abor = 0;
544     if(urlp->ctlofp != NULL)
545     {
546 	if(urlp->abor)
547 	    socket_fwrite("ABOR\r\n", 6, urlp->ctlofp);
548 	socket_fwrite("QUIT\r\n", 6, urlp->ctlofp);
549 	socket_fflush(urlp->ctlofp);
550 	socket_fclose(urlp->ctlofp);
551     }
552     if(urlp->ctlifp != NULL)
553 	socket_fclose(urlp->ctlifp);
554     free(url);
555     errno = save_errno;
556 }
557