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