Add to git.
[pthrlib.git] / src / pthr_ftpc.c
1 /* FTP client library.
2  * - by Richard W.M. Jones <rich@annexia.org>
3  *
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.
8  *
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.
13  *
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.
17  *
18  * $Id: pthr_ftpc.c,v 1.7 2002/12/01 14:29:27 rich Exp $
19  */
20
21 #include "config.h"
22
23 #include <stdio.h>
24 #include <stdlib.h>
25
26 #ifdef HAVE_STRING_H
27 #include <string.h>
28 #endif
29
30 #ifdef HAVE_NETDB_H
31 #include <netdb.h>
32 #endif
33
34 #ifdef HAVE_NETINET_IN_H
35 #include <netinet/in.h>
36 #endif
37
38 #ifdef HAVE_FCNTL_H
39 #include <fcntl.h>
40 #endif
41
42 #ifdef HAVE_SYS_SOCKET_H
43 #include <sys/socket.h>
44 #endif
45
46 #ifdef HAVE_NETINET_IN_H
47 #include <netinet/in.h>
48 #endif
49
50 #ifdef HAVE_ARPA_INET_H
51 #include <arpa/inet.h>
52 #endif
53
54 #include <pool.h>
55 #include <vector.h>
56 #include <pstring.h>
57
58 #include "pthr_pseudothread.h"
59 #include "pthr_iolib.h"
60 #include "pthr_ftpc.h"
61
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)))
72
73 #define REPLY_BUFFER_SIZE 2048
74
75 struct ftpc
76 {
77   /* General information about the connection. */
78   pool pool;
79   io_handle io;
80
81   int port;
82   int passive_mode;
83   int verbose;
84
85   struct sockaddr_in addr;
86   int pasv_data_port;
87
88   /* Reply buffer - stores the last line read from the server. */
89   char *reply;
90
91   /* These are kept for information only. */
92   char *server;
93   const char *username;
94   char *server_greeting;
95 };
96
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);
101
102 ftpc
103 new_ftpc (pool pool, const char *server)
104 {
105   ftpc f;
106   char *t;
107   struct hostent *h;
108   int sock, code;
109
110   f = pcalloc (pool, 1, sizeof *f);
111   f->pool = pool;
112   f->server = pstrdup (pool, server);
113   f->reply = pmalloc (pool, REPLY_BUFFER_SIZE);
114
115 #if 0
116   /* Just during testing, enable verbose mode always. */
117   f->verbose = 1;
118 #endif
119
120   /* Name contains a port number? If so, extract it out first. */
121   t = strrchr (f->server, ':');
122   if (t)
123     {
124       *t = 0;
125       if (sscanf (t+1, "%d", &f->port) != 1)
126         {
127           fprintf (stderr, "bad port number: %s\n", t+1);
128           return 0;
129         }
130     }
131   else
132     {
133       struct servent *se;
134
135       /* Try to determine the default port for FTP. */
136       se = getservbyname ("ftp", "tcp");
137       if (se)
138         f->port = ntohs (se->s_port);
139       else
140         f->port = 21;           /* Default FTP control port. */
141     }
142
143   /* Resolve the name of the server, if necessary. */
144   h = gethostbyname (f->server);
145   if (!h)
146     {
147       herror (f->server);
148       return 0;
149     }
150
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);
154
155   /* Create a socket. */
156   sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
157   if (sock == -1)
158     {
159       perror ("socket");
160       return 0;
161     }
162
163   /* Set non-blocking. */
164   if (fcntl (sock, F_SETFL, O_NONBLOCK) == -1)
165     {
166       perror ("fcntl: O_NONBLOCK");
167       return 0;
168     }
169
170   /* Attempt to connect to the server. */
171   if (pth_connect (sock, (struct sockaddr *) &f->addr, sizeof f->addr)
172       == -1)
173     {
174       perror (f->server);
175       return 0;
176     }
177
178   /* Wrap up the socket in an IO handle. */
179   f->io = io_fdopen (sock);
180   if (!f->io) return 0;
181
182   /* Expect a response string back immediately from the server. */
183   code = eat_reply (f);
184
185   /* Save the server greeting. */
186   f->server_greeting = pstrdup (f->pool, f->reply+4);
187
188   if (IS_NOT_2xx (code))
189     {
190       fprintf (stderr, "bad response from server %d\n", code);
191       return 0;
192     }
193
194   return f;
195 }
196
197 int
198 ftpc_set_passive_mode (ftpc f, int flag)
199 {
200   return f->passive_mode = flag;
201 }
202
203 int
204 ftpc_set_verbose (ftpc f, int flag)
205 {
206   return f->verbose = flag;
207 }
208
209 void
210 ftpc_perror (ftpc f, const char *msg)
211 {
212   fprintf (stderr, "%s: %s\n", msg, f->reply);
213 }
214
215 int
216 ftpc_login (ftpc f, const char *username, const char *password)
217 {
218   int is_anonymous, code;
219
220   is_anonymous = !username || strcmp (username, "ftp") == 0 ||
221     strcmp (username, "anonymous") == 0;
222
223   if (is_anonymous)
224     {
225       if (!username)
226         username = "ftp";
227       if (!password)
228         {
229           char *logname = getenv ("LOGNAME");
230
231           if (!logname) logname = "nobody";
232           password = psprintf (f->pool, "%s@", logname);
233         }
234     }
235   else
236     {
237       if (!password) password = "";
238     }
239
240   f->username = username;
241
242   /* Send the USER command. */
243   code = do_command (f, "USER", username);
244   if (IS_NOT_3xx (code))
245     return -1;
246
247   /* Send the PASS command. */
248   code = do_command (f, "PASS", password);
249   if (IS_NOT_2xx (code))
250     return -1;
251
252   return 0;
253 }
254
255 int
256 ftpc_type (ftpc f, char type)
257 {
258   int code;
259   char t[2] = { type, 0 };
260
261   code = do_command (f, "TYPE", t);
262   return IS_2xx (code) ? 0 : -1;
263 }
264
265 int
266 ftpc_ascii (ftpc f)
267 {
268   return ftpc_type (f, 'a');
269 }
270
271 int
272 ftpc_binary (ftpc f)
273 {
274   return ftpc_type (f, 'i');
275 }
276
277 int
278 ftpc_cwd (ftpc f, const char *pathname)
279 {
280   int code = do_command (f, "CWD", pathname);
281   return IS_2xx (code) ? 0 : -1;
282 }
283
284 int
285 ftpc_cdup (ftpc f)
286 {
287   int code = do_command (f, "CDUP", 0);
288   return IS_2xx (code) ? 0 : -1;
289 }
290
291 char *
292 ftpc_pwd (ftpc f)
293 {
294   int code, len;
295   char *path;
296
297   code = do_command (f, "PWD", 0);
298   if (IS_NOT_2xx (code)) return 0;
299
300   path = pstrdup (f->pool, &f->reply[4]);
301
302   /* Strip quotes around the pathname, if there are any. */
303   len = strlen (path);
304   if (len >= 2 && path[0] == '"' && path[len-1] == '"')
305     {
306       path[len-1] = '\0';
307       path++;
308     }
309
310   return path;
311 }
312
313 int
314 ftpc_mkdir (ftpc f, const char *pathname)
315 {
316   int code = do_command (f, "MKD", pathname);
317   return IS_2xx (code) ? 0 : -1;
318 }
319
320 int
321 ftpc_rmdir (ftpc f, const char *pathname)
322 {
323   int code = do_command (f, "RMD", pathname);
324   return IS_2xx (code) ? 0 : -1;
325 }
326
327 int
328 ftpc_delete (ftpc f, const char *pathname)
329 {
330   int code = do_command (f, "DELE", pathname);
331   return IS_2xx (code) ? 0 : -1;
332 }
333
334 vector
335 ftpc_ls (ftpc f, pool pool, const char *pathname)
336 {
337   int code, data_sock;
338   io_handle io;
339   vector v;
340
341   /* Issue PORT or PASV command and get a socket. */
342   if ((data_sock = issue_port_or_pasv (f)) == -1)
343     return 0;
344
345   if (!pool) pool = f->pool;
346
347   v = new_vector (pool, const char *);
348
349   /* Issue the NLST command. */
350   code = do_command (f, "NLST -a", pathname);
351   if (IS_NOT_1xx (code)) { close (data_sock); return 0; }
352
353   /* Open data connection to server. */
354   io = open_data (f, data_sock);
355   if (!io) return 0;
356
357   /* Read the data, line at a time. */
358   while (io_fgets (f->reply, REPLY_BUFFER_SIZE, io, 0))
359     {
360       char *s = pstrdup (pool, f->reply);
361       vector_push_back (v, s);
362     }
363
364   /* Close the data connection. */
365   io_fclose (io);
366
367   /* Check return code. */
368   code = eat_reply (f);
369   return IS_2xx (code) ? v : 0;
370 }
371
372 vector
373 ftpc_dir (ftpc f, pool pool, const char *pathname)
374 {
375   int code, data_sock;
376   io_handle io;
377   vector v;
378
379   /* Issue PORT or PASV command and get a socket. */
380   if ((data_sock = issue_port_or_pasv (f)) == -1)
381     return 0;
382
383   if (!pool) pool = f->pool;
384
385   v = new_vector (pool, const char *);
386
387   /* Issue the LIST command. */
388   code = do_command (f, "LIST -a", pathname);
389   if (IS_NOT_1xx (code)) { close (data_sock); return 0; }
390
391   /* Open data connection to server. */
392   io = open_data (f, data_sock);
393   if (!io) return 0;
394
395   /* Read the data, line at a time. */
396   while (io_fgets (f->reply, REPLY_BUFFER_SIZE, io, 0))
397     {
398       char *s = pstrdup (pool, f->reply);
399       vector_push_back (v, s);
400     }
401
402   /* Close the data connection. */
403   io_fclose (io);
404
405   /* Check return code. */
406   code = eat_reply (f);
407   return IS_2xx (code) ? v : 0;
408 }
409
410 /* XXX Only works for binary transfers. */
411 int
412 ftpc_get (ftpc f, const char *remote_file, const char *local_file)
413 {
414   int code, data_sock, n;
415   FILE *fp;
416   io_handle io;
417   char buf[1024];
418
419   /* Issue PORT or PASV command and get a socket. */
420   if ((data_sock = issue_port_or_pasv (f)) == -1)
421     return -1;
422
423   /* Issue the RETR command. */
424   code = do_command (f, "RETR", remote_file);
425   if (IS_NOT_1xx (code)) { close (data_sock); return -1; }
426
427   /* Open the local file. */
428   fp = fopen (local_file, "w");
429   if (fp == 0) { perror (local_file); return -1; }
430
431   /* Open data connection to server. */
432   io = open_data (f, data_sock);
433   if (!io) return -1;
434
435   /* Read the data, block at a time. */
436   while ((n = io_fread (buf, 1, sizeof buf, io)) > 0)
437     {
438       if (fwrite (buf, 1, n, fp) != n)
439         {
440           perror (local_file);
441           fclose (fp);
442           io_fclose (io);
443           return -1;
444         }
445     }
446
447   /* Close everything. */
448   fclose (fp);
449   io_fclose (io);
450
451   /* Check return code. */
452   code = eat_reply (f);
453   return IS_2xx (code) ? 0 : -1;
454 }
455
456 /* XXX Only works for binary transfers. */
457 int
458 ftpc_put (ftpc f, const char *local_file, const char *remote_file)
459 {
460   int code, data_sock, n;
461   FILE *fp;
462   io_handle io;
463   char buf[1024];
464
465   /* Issue PORT or PASV command and get a socket. */
466   if ((data_sock = issue_port_or_pasv (f)) == -1)
467     return -1;
468
469   /* Issue the STOR command. */
470   code = do_command (f, "STOR", remote_file);
471   if (IS_NOT_1xx (code)) { close (data_sock); return -1; }
472
473   /* Open the local file. */
474   fp = fopen (local_file, "r");
475   if (fp == 0) { perror (local_file); return -1; }
476
477   /* Open data connection to server. */
478   io = open_data (f, data_sock);
479   if (!io) return -1;
480
481   /* Read the data, block at a time. */
482   while ((n = fread (buf, 1, sizeof buf, fp)) > 0)
483     {
484       if (io_fwrite (buf, 1, n, io) != n)
485         {
486           perror (remote_file);
487           fclose (fp);
488           io_fclose (io);
489           return -1;
490         }
491     }
492
493   /* Close everything. */
494   fclose (fp);
495   io_fclose (io);
496
497   /* Check return code. */
498   code = eat_reply (f);
499   return IS_2xx (code) ? 0 : -1;
500 }
501
502 int
503 ftpc_quote (ftpc f, const char *cmd)
504 {
505   int code;
506
507   code = do_command (f, cmd, 0);
508   return IS_2xx (code) ? 0 : -1;
509 }
510
511 int
512 ftpc_quit (ftpc f)
513 {
514   int code;
515
516   code = do_command (f, "QUIT", 0);
517   io_fclose (f->io);
518   return IS_2xx (code) ? 0 : -1;
519 }
520
521 /* Execute a command, get the reply and return the code. */
522 static int
523 do_command (ftpc f, const char *cmd, const char *arg)
524 {
525   if (f->verbose)
526     {
527       if (f->username)
528         fprintf (stderr, "%s@%s: ", f->username, f->server);
529       else
530         fprintf (stderr, "%s: ", f->server);
531
532       if (arg == 0)
533         fprintf (stderr, "%s\r\n", cmd);
534       else
535         fprintf (stderr, "%s %s\r\n", cmd, arg);
536     }
537
538   if (arg == 0)
539     io_fprintf (f->io, "%s\r\n", cmd);
540   else
541     io_fprintf (f->io, "%s %s\r\n", cmd, arg);
542
543   return eat_reply (f);
544 }
545
546 /* Eat the reply from the server, and throw it all away, except for
547  * the code.
548  */
549 static int
550 eat_reply (ftpc f)
551 {
552   int code;
553
554   while (io_fgets (f->reply, REPLY_BUFFER_SIZE, f->io, 0))
555     {
556       if (f->verbose)
557         {
558           if (f->username)
559             fprintf (stderr, "%s@%s: %s\n", f->username, f->server, f->reply);
560           else
561             fprintf (stderr, "%s: %s\n", f->server, f->reply);
562         }
563
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");
570
571       /* Is this the last line? */
572       if (f->reply[3] == ' ')
573         {
574           /* Yes: extract the code. */
575           code = 100 * (f->reply[0] - '0') +
576             10 * (f->reply[1] - '0') + (f->reply[2] - '0');
577
578           return code;
579         }
580     }
581
582   pth_die ("server closed the connection unexpectedly");
583 }
584
585 /* Issue either PORT or PASV command to the server and get a socket. */
586 static int
587 issue_port_or_pasv (ftpc f)
588 {
589   int code, a1, a2, a3, a4, p1, p2;
590   char *t;
591   struct sockaddr_in addr;
592   int addr_len, data_sock;
593
594   /* Create data socket. */
595   data_sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
596   if (data_sock == -1)
597     {
598       perror ("socket");
599       return -1;
600     }
601
602   /* Set non-blocking. */
603   if (fcntl (data_sock, F_SETFL, O_NONBLOCK) == -1) abort ();
604
605   if (f->passive_mode)
606     {
607       /* Issue PASV command to get a port number. */
608       code = do_command (f, "PASV", 0);
609       if (IS_NOT_2xx (code)) return -1;
610
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
614        * the port number.
615        */
616       t = strchr (&f->reply[4], '(');
617       if (!t ||
618           sscanf (t, "( %d , %d , %d , %d , %d , %d )",
619                   &a1, &a2, &a3, &a4, &p1, &p2) != 6)
620         {
621           close (data_sock);
622           fprintf (stderr, "cannot parse reply from PASV command\n");
623           return -1;
624         }
625
626       f->pasv_data_port = p1 * 256 + p2;
627     }
628   else
629     {
630       /* Bind the socket. */
631       addr.sin_family = AF_INET;
632       addr.sin_port = 0;
633       addr.sin_addr.s_addr = INADDR_ANY;
634       if (bind (data_sock, (struct sockaddr *) &addr, sizeof addr) == -1)
635         {
636           close (data_sock);
637           perror ("bind");
638           return -1;
639         }
640
641       /* Listen on socket. */
642       if (listen (data_sock, 1) == -1)
643         {
644           close (data_sock);
645           perror ("listen");
646           return -1;
647         }
648
649       addr_len = sizeof addr;
650       if (getsockname (data_sock, (struct sockaddr *) &addr, &addr_len)
651           == -1)
652         {
653           close (data_sock);
654           perror ("getsockname");
655           return -1;
656         }
657
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;
668     }
669
670   return data_sock;
671 }
672
673 /* Open a data connection to the server (which is expecting it). */
674 static io_handle
675 open_data (ftpc f, int data_sock)
676 {
677   io_handle io;
678   struct sockaddr_in addr;
679   int addr_len, sock;
680
681   if (f->passive_mode)
682     {
683       /* Connect to server. */
684       f->addr.sin_port = htons (f->pasv_data_port);
685
686       if (pth_connect (data_sock, (struct sockaddr *) &f->addr,
687                        sizeof f->addr) == -1)
688         {
689           close (data_sock);
690           perror (f->server);
691           return 0;
692         }
693
694       io = io_fdopen (data_sock);
695       if (!io) return 0;
696     }
697   else
698     {
699       addr_len = sizeof addr;
700
701       /* Accept connection from server. */
702       if ((sock = pth_accept (data_sock,
703                               (struct sockaddr *) &addr, &addr_len)) == -1)
704         {
705           close (data_sock);
706           perror ("accept");
707           return 0;
708         }
709
710       close (data_sock);
711
712       /* Verify this connection comes from the server. */
713       if (addr.sin_addr.s_addr != f->addr.sin_addr.s_addr)
714         {
715           fprintf (stderr, "connection accepted, but not from FTP server");
716           return 0;
717         }
718
719       io = io_fdopen (sock);
720       if (!io) return 0;
721     }
722
723   return io;
724 }