2 * - by Richard W.M. Jones <rich@annexia.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library 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 GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the Free
16 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 * $Id: pthr_ftpc.c,v 1.7 2002/12/01 14:29:27 rich Exp $
34 #ifdef HAVE_NETINET_IN_H
35 #include <netinet/in.h>
42 #ifdef HAVE_SYS_SOCKET_H
43 #include <sys/socket.h>
46 #ifdef HAVE_NETINET_IN_H
47 #include <netinet/in.h>
50 #ifdef HAVE_ARPA_INET_H
51 #include <arpa/inet.h>
58 #include "pthr_pseudothread.h"
59 #include "pthr_iolib.h"
60 #include "pthr_ftpc.h"
62 #define IS_1xx(c) ((c) >= 100 && (c) <= 199)
63 #define IS_2xx(c) ((c) >= 200 && (c) <= 299)
64 #define IS_3xx(c) ((c) >= 300 && (c) <= 399)
65 #define IS_4xx(c) ((c) >= 400 && (c) <= 499)
66 #define IS_5xx(c) ((c) >= 500 && (c) <= 599)
67 #define IS_NOT_1xx(c) (!(IS_1xx(c)))
68 #define IS_NOT_2xx(c) (!(IS_2xx(c)))
69 #define IS_NOT_3xx(c) (!(IS_3xx(c)))
70 #define IS_NOT_4xx(c) (!(IS_4xx(c)))
71 #define IS_NOT_5xx(c) (!(IS_5xx(c)))
73 #define REPLY_BUFFER_SIZE 2048
77 /* General information about the connection. */
85 struct sockaddr_in addr;
88 /* Reply buffer - stores the last line read from the server. */
91 /* These are kept for information only. */
94 char *server_greeting;
97 static int eat_reply (ftpc);
98 static int do_command (ftpc, const char *cmd, const char *arg);
99 static io_handle open_data (ftpc, int data_sock);
100 static int issue_port_or_pasv (ftpc f);
103 new_ftpc (pool pool, const char *server)
110 f = pcalloc (pool, 1, sizeof *f);
112 f->server = pstrdup (pool, server);
113 f->reply = pmalloc (pool, REPLY_BUFFER_SIZE);
116 /* Just during testing, enable verbose mode always. */
120 /* Name contains a port number? If so, extract it out first. */
121 t = strrchr (f->server, ':');
125 if (sscanf (t+1, "%d", &f->port) != 1)
127 fprintf (stderr, "bad port number: %s\n", t+1);
135 /* Try to determine the default port for FTP. */
136 se = getservbyname ("ftp", "tcp");
138 f->port = ntohs (se->s_port);
140 f->port = 21; /* Default FTP control port. */
143 /* Resolve the name of the server, if necessary. */
144 h = gethostbyname (f->server);
151 f->addr.sin_family = AF_INET;
152 memcpy (&f->addr.sin_addr, h->h_addr, sizeof f->addr.sin_addr);
153 f->addr.sin_port = htons (f->port);
155 /* Create a socket. */
156 sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
163 /* Set non-blocking. */
164 if (fcntl (sock, F_SETFL, O_NONBLOCK) == -1)
166 perror ("fcntl: O_NONBLOCK");
170 /* Attempt to connect to the server. */
171 if (pth_connect (sock, (struct sockaddr *) &f->addr, sizeof f->addr)
178 /* Wrap up the socket in an IO handle. */
179 f->io = io_fdopen (sock);
180 if (!f->io) return 0;
182 /* Expect a response string back immediately from the server. */
183 code = eat_reply (f);
185 /* Save the server greeting. */
186 f->server_greeting = pstrdup (f->pool, f->reply+4);
188 if (IS_NOT_2xx (code))
190 fprintf (stderr, "bad response from server %d\n", code);
198 ftpc_set_passive_mode (ftpc f, int flag)
200 return f->passive_mode = flag;
204 ftpc_set_verbose (ftpc f, int flag)
206 return f->verbose = flag;
210 ftpc_perror (ftpc f, const char *msg)
212 fprintf (stderr, "%s: %s\n", msg, f->reply);
216 ftpc_login (ftpc f, const char *username, const char *password)
218 int is_anonymous, code;
220 is_anonymous = !username || strcmp (username, "ftp") == 0 ||
221 strcmp (username, "anonymous") == 0;
229 char *logname = getenv ("LOGNAME");
231 if (!logname) logname = "nobody";
232 password = psprintf (f->pool, "%s@", logname);
237 if (!password) password = "";
240 f->username = username;
242 /* Send the USER command. */
243 code = do_command (f, "USER", username);
244 if (IS_NOT_3xx (code))
247 /* Send the PASS command. */
248 code = do_command (f, "PASS", password);
249 if (IS_NOT_2xx (code))
256 ftpc_type (ftpc f, char type)
259 char t[2] = { type, 0 };
261 code = do_command (f, "TYPE", t);
262 return IS_2xx (code) ? 0 : -1;
268 return ftpc_type (f, 'a');
274 return ftpc_type (f, 'i');
278 ftpc_cwd (ftpc f, const char *pathname)
280 int code = do_command (f, "CWD", pathname);
281 return IS_2xx (code) ? 0 : -1;
287 int code = do_command (f, "CDUP", 0);
288 return IS_2xx (code) ? 0 : -1;
297 code = do_command (f, "PWD", 0);
298 if (IS_NOT_2xx (code)) return 0;
300 path = pstrdup (f->pool, &f->reply[4]);
302 /* Strip quotes around the pathname, if there are any. */
304 if (len >= 2 && path[0] == '"' && path[len-1] == '"')
314 ftpc_mkdir (ftpc f, const char *pathname)
316 int code = do_command (f, "MKD", pathname);
317 return IS_2xx (code) ? 0 : -1;
321 ftpc_rmdir (ftpc f, const char *pathname)
323 int code = do_command (f, "RMD", pathname);
324 return IS_2xx (code) ? 0 : -1;
328 ftpc_delete (ftpc f, const char *pathname)
330 int code = do_command (f, "DELE", pathname);
331 return IS_2xx (code) ? 0 : -1;
335 ftpc_ls (ftpc f, pool pool, const char *pathname)
341 /* Issue PORT or PASV command and get a socket. */
342 if ((data_sock = issue_port_or_pasv (f)) == -1)
345 if (!pool) pool = f->pool;
347 v = new_vector (pool, const char *);
349 /* Issue the NLST command. */
350 code = do_command (f, "NLST -a", pathname);
351 if (IS_NOT_1xx (code)) { close (data_sock); return 0; }
353 /* Open data connection to server. */
354 io = open_data (f, data_sock);
357 /* Read the data, line at a time. */
358 while (io_fgets (f->reply, REPLY_BUFFER_SIZE, io, 0))
360 char *s = pstrdup (pool, f->reply);
361 vector_push_back (v, s);
364 /* Close the data connection. */
367 /* Check return code. */
368 code = eat_reply (f);
369 return IS_2xx (code) ? v : 0;
373 ftpc_dir (ftpc f, pool pool, const char *pathname)
379 /* Issue PORT or PASV command and get a socket. */
380 if ((data_sock = issue_port_or_pasv (f)) == -1)
383 if (!pool) pool = f->pool;
385 v = new_vector (pool, const char *);
387 /* Issue the LIST command. */
388 code = do_command (f, "LIST -a", pathname);
389 if (IS_NOT_1xx (code)) { close (data_sock); return 0; }
391 /* Open data connection to server. */
392 io = open_data (f, data_sock);
395 /* Read the data, line at a time. */
396 while (io_fgets (f->reply, REPLY_BUFFER_SIZE, io, 0))
398 char *s = pstrdup (pool, f->reply);
399 vector_push_back (v, s);
402 /* Close the data connection. */
405 /* Check return code. */
406 code = eat_reply (f);
407 return IS_2xx (code) ? v : 0;
410 /* XXX Only works for binary transfers. */
412 ftpc_get (ftpc f, const char *remote_file, const char *local_file)
414 int code, data_sock, n;
419 /* Issue PORT or PASV command and get a socket. */
420 if ((data_sock = issue_port_or_pasv (f)) == -1)
423 /* Issue the RETR command. */
424 code = do_command (f, "RETR", remote_file);
425 if (IS_NOT_1xx (code)) { close (data_sock); return -1; }
427 /* Open the local file. */
428 fp = fopen (local_file, "w");
429 if (fp == 0) { perror (local_file); return -1; }
431 /* Open data connection to server. */
432 io = open_data (f, data_sock);
435 /* Read the data, block at a time. */
436 while ((n = io_fread (buf, 1, sizeof buf, io)) > 0)
438 if (fwrite (buf, 1, n, fp) != n)
447 /* Close everything. */
451 /* Check return code. */
452 code = eat_reply (f);
453 return IS_2xx (code) ? 0 : -1;
456 /* XXX Only works for binary transfers. */
458 ftpc_put (ftpc f, const char *local_file, const char *remote_file)
460 int code, data_sock, n;
465 /* Issue PORT or PASV command and get a socket. */
466 if ((data_sock = issue_port_or_pasv (f)) == -1)
469 /* Issue the STOR command. */
470 code = do_command (f, "STOR", remote_file);
471 if (IS_NOT_1xx (code)) { close (data_sock); return -1; }
473 /* Open the local file. */
474 fp = fopen (local_file, "r");
475 if (fp == 0) { perror (local_file); return -1; }
477 /* Open data connection to server. */
478 io = open_data (f, data_sock);
481 /* Read the data, block at a time. */
482 while ((n = fread (buf, 1, sizeof buf, fp)) > 0)
484 if (io_fwrite (buf, 1, n, io) != n)
486 perror (remote_file);
493 /* Close everything. */
497 /* Check return code. */
498 code = eat_reply (f);
499 return IS_2xx (code) ? 0 : -1;
503 ftpc_quote (ftpc f, const char *cmd)
507 code = do_command (f, cmd, 0);
508 return IS_2xx (code) ? 0 : -1;
516 code = do_command (f, "QUIT", 0);
518 return IS_2xx (code) ? 0 : -1;
521 /* Execute a command, get the reply and return the code. */
523 do_command (ftpc f, const char *cmd, const char *arg)
528 fprintf (stderr, "%s@%s: ", f->username, f->server);
530 fprintf (stderr, "%s: ", f->server);
533 fprintf (stderr, "%s\r\n", cmd);
535 fprintf (stderr, "%s %s\r\n", cmd, arg);
539 io_fprintf (f->io, "%s\r\n", cmd);
541 io_fprintf (f->io, "%s %s\r\n", cmd, arg);
543 return eat_reply (f);
546 /* Eat the reply from the server, and throw it all away, except for
554 while (io_fgets (f->reply, REPLY_BUFFER_SIZE, f->io, 0))
559 fprintf (stderr, "%s@%s: %s\n", f->username, f->server, f->reply);
561 fprintf (stderr, "%s: %s\n", f->server, f->reply);
564 if (strlen (f->reply) < 4 ||
565 f->reply[0] < '1' || f->reply[0] > '5' ||
566 f->reply[1] < '0' || f->reply[1] > '9' ||
567 f->reply[2] < '0' || f->reply[2] > '9' ||
568 (f->reply[3] != ' ' && f->reply[3] != '-'))
569 pth_die ("badly formatted reply from server");
571 /* Is this the last line? */
572 if (f->reply[3] == ' ')
574 /* Yes: extract the code. */
575 code = 100 * (f->reply[0] - '0') +
576 10 * (f->reply[1] - '0') + (f->reply[2] - '0');
582 pth_die ("server closed the connection unexpectedly");
585 /* Issue either PORT or PASV command to the server and get a socket. */
587 issue_port_or_pasv (ftpc f)
589 int code, a1, a2, a3, a4, p1, p2;
591 struct sockaddr_in addr;
592 int addr_len, data_sock;
594 /* Create data socket. */
595 data_sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
602 /* Set non-blocking. */
603 if (fcntl (data_sock, F_SETFL, O_NONBLOCK) == -1) abort ();
607 /* Issue PASV command to get a port number. */
608 code = do_command (f, "PASV", 0);
609 if (IS_NOT_2xx (code)) return -1;
611 /* The reply should say something like:
612 * 227 Entering Passive Mode (A,A,A,A,P,P)
613 * We ignore the address fields and concentrate on just
616 t = strchr (&f->reply[4], '(');
618 sscanf (t, "( %d , %d , %d , %d , %d , %d )",
619 &a1, &a2, &a3, &a4, &p1, &p2) != 6)
622 fprintf (stderr, "cannot parse reply from PASV command\n");
626 f->pasv_data_port = p1 * 256 + p2;
630 /* Bind the socket. */
631 addr.sin_family = AF_INET;
633 addr.sin_addr.s_addr = INADDR_ANY;
634 if (bind (data_sock, (struct sockaddr *) &addr, sizeof addr) == -1)
641 /* Listen on socket. */
642 if (listen (data_sock, 1) == -1)
649 addr_len = sizeof addr;
650 if (getsockname (data_sock, (struct sockaddr *) &addr, &addr_len)
654 perror ("getsockname");
658 /* Issue a PORT command to tell the server our port number. */
659 a1 = (ntohl (f->addr.sin_addr.s_addr) >> 24) & 0xff;
660 a2 = (ntohl (f->addr.sin_addr.s_addr) >> 16) & 0xff;
661 a3 = (ntohl (f->addr.sin_addr.s_addr) >> 8) & 0xff;
662 a4 = ntohl (f->addr.sin_addr.s_addr) & 0xff;
663 p1 = ntohs (addr.sin_port) / 256;
664 p2 = ntohs (addr.sin_port) % 256;
665 t = psprintf (f->pool, "%d,%d,%d,%d,%d,%d", a1, a2, a3, a4, p1, p2);
666 code = do_command (f, "PORT", t);
667 if (IS_NOT_2xx (code)) return -1;
673 /* Open a data connection to the server (which is expecting it). */
675 open_data (ftpc f, int data_sock)
678 struct sockaddr_in addr;
683 /* Connect to server. */
684 f->addr.sin_port = htons (f->pasv_data_port);
686 if (pth_connect (data_sock, (struct sockaddr *) &f->addr,
687 sizeof f->addr) == -1)
694 io = io_fdopen (data_sock);
699 addr_len = sizeof addr;
701 /* Accept connection from server. */
702 if ((sock = pth_accept (data_sock,
703 (struct sockaddr *) &addr, &addr_len)) == -1)
712 /* Verify this connection comes from the server. */
713 if (addr.sin_addr.s_addr != f->addr.sin_addr.s_addr)
715 fprintf (stderr, "connection accepted, but not from FTP server");
719 io = io_fdopen (sock);