1999-08-30 Miguel de Icaza <miguel@gnu.org>
[midnight-commander.git] / vfs / ftpfs.c
blobe7d77b866e915c05882c9c042ae2b14dddc47388
1 /* Virtual File System: FTP file system.
2 Copyright (C) 1995 The Free Software Foundation
4 Written by: 1995 Ching Hui
5 1995 Jakub Jelinek
6 1995, 1996, 1997 Miguel de Icaza
7 1997 Norbert Warmuth
8 1998 Pavel Machek
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Library General Public License
12 as published by the Free Software Foundation; either version 2 of
13 the License, or (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU Library General Public License for more details.
20 You should have received a copy of the GNU Library General Public
21 License along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
24 /* FTPfs TODO:
26 - make it more robust - all the connects etc. should handle EADDRINUSE and
27 ERETRY (have I spelled these names correctly?)
28 - make the user able to flush a connection - all the caches will get empty
29 etc., (tarfs as well), we should give there a user selectable timeout
30 and assign a key sequence.
31 - use hash table instead of linklist to cache ftpfs directory.
34 /* Namespace pollution: horrible */
36 #include <config.h>
37 #include <stdio.h>
38 #include <errno.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <stdlib.h>
42 #include <sys/types.h>
43 #include <stdarg.h>
44 #include <fcntl.h>
45 #include <pwd.h>
46 #include <ctype.h> /* For isdigit */
47 #ifdef SCO_FLAVOR
48 # include <sys/timeb.h> /* alex: for struct timeb definition */
49 #endif /* SCO_FLAVOR */
50 #include <time.h>
51 #include <sys/types.h>
52 #if defined(HAVE_UNISTD_H)
53 #include <unistd.h>
54 #endif
55 #if HAVE_SYS_SELECT_H
56 # include <sys/select.h>
57 #endif
59 #include "../src/setup.h"
61 #include <netdb.h> /* struct hostent */
62 #include <sys/socket.h> /* AF_INET */
63 #include <netinet/in.h> /* struct in_addr */
64 #ifdef HAVE_SETSOCKOPT
65 # include <netinet/ip.h> /* IP options */
66 #endif
67 #include <arpa/inet.h>
68 #include <arpa/ftp.h>
69 #include <arpa/telnet.h>
70 #ifndef SCO_FLAVOR
71 # include <sys/time.h> /* alex: this redefines struct timeval */
72 #endif /* SCO_FLAVOR */
73 #include <sys/param.h>
75 #ifdef USE_TERMNET
76 #include <termnet.h>
77 #endif
79 #include "utilvfs.h"
81 #include "vfs.h"
82 #include "tcputil.h"
83 #include "../src/dialog.h"
84 #include "container.h"
85 #include "ftpfs.h"
86 #ifndef MAXHOSTNAMELEN
87 # define MAXHOSTNAMELEN 64
88 #endif
90 #define ERRNOR(x,y) do { my_errno = x; return y; } while(0)
91 #define UPLOAD_ZERO_LENGTH_FILE
93 static int my_errno;
94 static int code;
96 /* Delay to retry a connection */
97 int ftpfs_retry_seconds = 30;
99 /* Method to use to connect to ftp sites */
100 int ftpfs_use_passive_connections = 1;
102 /* Method used to get directory listings:
103 * 1: try 'LIST -la <path>', if it fails
104 * fall back to CWD <path>; LIST
105 * 0: always use CWD <path>; LIST
107 int ftpfs_use_unix_list_options = 1;
109 /* First "CWD <path>", then "LIST -la ." */
110 int ftpfs_first_cd_then_ls;
112 /* Use the ~/.netrc */
113 int use_netrc = 1;
115 extern char *home_dir;
117 /* Anonymous setup */
118 char *ftpfs_anonymous_passwd = 0;
119 int ftpfs_directory_timeout;
121 /* Proxy host */
122 char *ftpfs_proxy_host = 0;
124 /* wether we have to use proxy by default? */
125 int ftpfs_always_use_proxy;
127 /* source routing host */
128 extern int source_route;
130 /* Where we store the transactions */
131 static FILE *logfile = NULL;
133 /* If true, the directory cache is forced to reload */
134 static int force_expiration = 0;
136 static struct linklist *connections_list;
138 /* command wait_flag: */
139 #define NONE 0x00
140 #define WAIT_REPLY 0x01
141 #define WANT_STRING 0x02
142 static char reply_str [80];
144 static struct direntry *_get_file_entry (struct connection *bucket,
145 char *file_name, int op, int flags);
147 static char *ftpfs_get_current_directory (struct connection *bucket);
148 static int ftpfs_chdir_internal (struct connection *bucket,
149 char *remote_path);
150 static void free_bucket (void *data);
151 static void connection_destructor (void *data);
152 static void flush_all_directory (struct connection *bucket);
153 static int get_line (int sock, char *buf, int buf_len,
154 char term);
155 static char *get_path (struct connection **bucket,
156 char *path);
158 /* char *translate_path (struct ftpfs_connection *bucket, char *remote_path)
159 Translate a Unix path, i.e. MC's internal path representation (e.g.
160 /somedir/somefile) to a path valid for the remote server. Every path
161 transfered to the remote server has to be mangled by this function
162 right prior to sending it.
163 Currently only Amiga ftp servers are handled in a special manner.
165 When the remote server is an amiga:
166 a) strip leading slash if necesarry
167 b) replace first occurance of ":/" with ":"
168 c) strip trailing "/."
171 static char *
172 translate_path (struct connection *bucket, char *remote_path)
174 char *p;
176 if (!bucket->remote_is_amiga)
177 return g_strdup (remote_path);
178 else {
179 char *ret;
181 if (logfile) {
182 fprintf (logfile, "MC -- translate_path: %s\n", remote_path);
183 fflush (logfile);
187 * Don't change "/" into "", e.g. "CWD " would be
188 * invalid.
190 if (*remote_path == '/' && remote_path[1] == '\0')
191 return g_strdup (".");
193 /* strip leading slash */
194 if (*remote_path == '/')
195 ret = g_strdup (remote_path + 1);
196 else
197 ret = g_strdup (remote_path);
199 /* replace first occurance of ":/" with ":" */
200 if ((p = strchr (ret, ':')) && *(p + 1) == '/')
201 strcpy (p + 1, p + 2);
203 /* strip trailing "/." */
204 if ((p = strrchr (ret, '/')) && *(p + 1) == '.' && *(p + 2) == '\0')
205 *p = '\0';
206 return ret;
210 /* Extract the hostname and username from the path */
213 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
214 * ftp://sunsite.unc.edu/pub/linux
215 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
216 * ftp://tsx-11.mit.edu:8192/
217 * ftp://joe@foo.edu:11321/private
218 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
219 * is supplied.
223 static char *
224 my_get_host_and_username (char *path, char **host, char **user, int *port, char **pass)
226 return vfs_split_url (path, host, user, port, pass, 21, URL_DEFAULTANON);
229 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
230 static int
231 get_reply (int sock, char *string_buf, int string_len)
233 char answer[1024];
234 int i;
236 for (;;) {
237 if (!get_line (sock, answer, sizeof (answer), '\n')){
238 if (string_buf)
239 *string_buf = 0;
240 code = 421;
241 return 4;
243 switch (sscanf(answer, "%d", &code)){
244 case 0:
245 if (string_buf) {
246 strncpy (string_buf, answer, string_len - 1);
247 *(string_buf + string_len - 1) = 0;
249 code = 500;
250 return 5;
251 case 1:
252 if (answer[3] == '-') {
253 while (1) {
254 if (!get_line (sock, answer, sizeof(answer), '\n')){
255 if (string_buf)
256 *string_buf = 0;
257 code = 421;
258 return 4;
260 if ((sscanf (answer, "%d", &i) > 0) &&
261 (code == i) && (answer[3] == ' '))
262 break;
265 if (string_buf){
266 strncpy (string_buf, answer, string_len - 1);
267 *(string_buf + string_len - 1) = 0;
269 return code / 100;
274 static int
275 command (struct connection *bucket, int wait_reply, char *fmt, ...)
277 va_list ap;
278 char *str, *fmt_str;
279 int status;
280 int sock = qsock (bucket);
282 va_start (ap, fmt);
284 fmt_str = g_strdup_vprintf (fmt, ap);
285 va_end (ap);
287 str = g_strconcat (fmt_str, "\r\n", NULL);
288 g_free (fmt_str);
290 if (logfile){
291 if (strncmp (str, "PASS ", 5) == 0){
292 char *tmp = "PASS <Password not logged>\r\n";
294 fwrite (tmp, strlen (tmp), 1, logfile);
295 } else
296 fwrite (str, strlen (str), 1, logfile);
298 fflush (logfile);
301 got_sigpipe = 0;
302 enable_interrupt_key ();
303 status = write (sock, str, strlen (str));
304 g_free (str);
306 if (status < 0){
307 code = 421;
309 if (errno == EPIPE){
310 got_sigpipe = 1;
312 disable_interrupt_key ();
313 return TRANSIENT;
315 disable_interrupt_key ();
317 if (wait_reply)
318 return get_reply (sock, (wait_reply & WANT_STRING) ? reply_str : NULL, sizeof (reply_str)-1);
319 return COMPLETE;
322 static void
323 connection_close (void *data)
325 struct connection *bucket = data;
327 if (qsock (bucket) != -1){
328 print_vfs_message (_("ftpfs: Disconnecting from %s"), qhost (bucket));
329 command(bucket, NONE, "QUIT");
330 close(qsock(bucket));
334 /* some defines only used by changetype */
335 /* These two are valid values for the second parameter */
336 #define TYPE_ASCII 0
337 #define TYPE_BINARY 1
339 /* This one is only used to initialize bucket->isbinary, don't use it as
340 second parameter to changetype. */
341 #define TYPE_UNKNOWN -1
343 static int
344 changetype (struct connection *bucket, int binary)
346 if (binary != bucket->isbinary) {
347 if (command (bucket, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE)
348 ERRNOR (EIO, -1);
349 bucket->isbinary = binary;
351 return binary;
354 /* This routine logs the user in */
355 static int
356 login_server (struct connection *bucket, char *netrcpass)
358 #if defined(HSC_PROXY)
359 char *proxypass, *proxyname;
360 #endif
361 char *pass;
362 char *op;
363 char *name; /* login user name */
364 int anon = 0;
365 char reply_string[255];
367 bucket->isbinary = TYPE_UNKNOWN;
368 if (netrcpass)
369 op = g_strdup (netrcpass);
370 else {
371 if (!strcmp (quser (bucket), "anonymous") ||
372 !strcmp (quser (bucket), "ftp")) {
373 op = g_strdup (ftpfs_anonymous_passwd);
374 anon = 1;
375 } else {
376 char *p;
378 if (!bucket->password){
379 p = g_strconcat (_(" FTP: Password required for "), quser (bucket),
380 " ", NULL);
381 op = vfs_get_password (p);
382 g_free (p);
383 if (op == NULL)
384 ERRNOR (EPERM, 0);
385 bucket->password = g_strdup (op);
386 } else
387 op = g_strdup (bucket->password);
391 if (!anon || logfile)
392 pass = g_strdup (op);
393 else
394 pass = g_strconcat ("-", op, NULL);
395 wipe_password (op);
398 /* Proxy server accepts: username@host-we-want-to-connect*/
399 if (qproxy (bucket)){
400 #if defined(HSC_PROXY)
401 char *p, *host;
402 int port;
403 p = my_get_host_and_username (ftpfs_proxy_host, &host, &proxyname,
404 &port, &proxypass);
405 if (p)
406 g_free (p);
408 g_free (host);
409 if (proxypass)
410 wipe_password (proxypass);
411 p = g_strconcat (_(" Proxy: Password required for "), proxyname, " ",
412 NULL);
413 proxypass = vfs_get_password (p);
414 g_free (p);
415 if (proxypass == NULL) {
416 wipe_password (pass);
417 g_free (proxyname);
418 ERRNOR (EPERM, 0);
420 name = g_strdup (quser (bucket));
421 #else
422 name = g_strconcat (quser (bucket), "@",
423 qhost (bucket)[0] == '!' ? qhost (bucket)+1 : qhost (bucket), NULL);
424 #endif
425 } else
426 name = g_strdup (quser (bucket));
428 if (get_reply (qsock(bucket), reply_string, sizeof (reply_string) - 1) == COMPLETE) {
429 g_strup (reply_string);
430 bucket->remote_is_amiga = strstr (reply_string, "AMIGA") != 0;
431 if (logfile) {
432 fprintf (logfile, "MC -- remote_is_amiga = %d\n", bucket->remote_is_amiga);
433 fflush (logfile);
435 #if defined(HSC_PROXY)
436 if (qproxy (bucket)){
437 print_vfs_message (_("ftpfs: sending proxy login name"));
438 if (command (bucket, 1, "USER %s", proxyname) != CONTINUE)
439 goto proxyfail;
441 print_vfs_message (_("ftpfs: sending proxy user password"));
442 if (command (bucket, 1, "PASS %s", proxypass) != COMPLETE)
443 goto proxyfail;
445 print_vfs_message (_("ftpfs: proxy authentication succeeded"));
446 if (command (bucket, 1, "SITE %s", qhost (bucket)+1) != COMPLETE)
447 goto proxyfail;
449 print_vfs_message (_("ftpfs: connected to %s"), qhost (bucket)+1);
450 if (0) {
451 proxyfail:
452 bucket->failed_on_login = 1;
453 /* my_errno = E; */
454 if (proxypass)
455 wipe_password (proxypass);
456 wipe_password (pass);
457 g_free (proxyname);
458 g_free (name);
459 ERRNOR (EPERM, 0);
461 if (proxypass)
462 wipe_password (proxypass);
463 g_free (proxyname);
465 #endif
466 print_vfs_message (_("ftpfs: sending login name"));
467 code = command (bucket, WAIT_REPLY, "USER %s", name);
469 switch (code){
470 case CONTINUE:
471 print_vfs_message (_("ftpfs: sending user password"));
472 if (command (bucket, WAIT_REPLY, "PASS %s", pass) != COMPLETE)
473 break;
475 case COMPLETE:
476 print_vfs_message (_("ftpfs: logged in"));
477 wipe_password (pass);
478 g_free (name);
479 return 1;
481 default:
482 bucket->failed_on_login = 1;
483 /* my_errno = E; */
484 if (bucket->password)
485 wipe_password (bucket->password);
486 bucket->password = 0;
488 goto login_fail;
491 print_vfs_message (_("ftpfs: Login incorrect for user %s "), quser (bucket));
492 login_fail:
493 wipe_password (pass);
494 g_free (name);
495 ERRNOR (EPERM, 0);
498 #ifdef HAVE_SETSOCKOPT
499 static void
500 setup_source_route (int socket, int dest)
502 char buffer [20];
503 char *ptr = buffer;
505 if (!source_route)
506 return;
507 bzero (buffer, sizeof (buffer));
508 *ptr++ = IPOPT_LSRR;
509 *ptr++ = 3 + 8;
510 *ptr++ = 4; /* pointer */
512 /* First hop */
513 bcopy ((char *) &source_route, ptr, sizeof (int));
514 ptr += 4;
516 /* Second hop (ie, final destination) */
517 bcopy ((char *) &dest, ptr, sizeof (int));
518 ptr += 4;
519 while ((ptr - buffer) & 3)
520 ptr++;
521 if (setsockopt (socket, IPPROTO_IP, IP_OPTIONS,
522 buffer, ptr - buffer) < 0)
523 message_2s (1, MSG_ERROR, _(" Could not set source routing (%s)"), unix_error_string (errno));
525 #else
526 #define setup_source_route(x,y)
527 #endif
529 static struct no_proxy_entry {
530 char *domain;
531 void *next;
532 } *no_proxy;
534 static void
535 load_no_proxy_list ()
537 /* FixMe: shouldn't be hardcoded!!! */
538 char s[BUF_LARGE]; /* provide for BUF_LARGE characters */
539 struct no_proxy_entry *np, *current = 0;
540 FILE *npf;
541 int c;
542 char *p, *mc_file;
543 static int loaded;
545 if (loaded)
546 return;
548 mc_file = concat_dir_and_file (mc_home, "mc.no_proxy");
549 if (exist_file (mc_file) &&
550 (npf = fopen (mc_file, "r"))) {
551 while (fgets (s, sizeof(s), npf) || !(feof (npf) || ferror (npf))) {
552 if (!(p = strchr (s, '\n'))) { /* skip bogus entries */
553 while ((c = fgetc (npf)) != EOF && c != '\n')
555 continue;
558 if (p == s)
559 continue;
561 *p = '\0';
563 np = g_new (struct no_proxy_entry, 1);
564 np->domain = g_strdup (s);
565 np->next = NULL;
566 if (no_proxy)
567 current->next = np;
568 else
569 no_proxy = np;
570 current = np;
573 fclose (npf);
574 loaded = 1;
576 g_free (mc_file);
579 static int
580 ftpfs_check_proxy (char *host)
582 struct no_proxy_entry *npe;
584 if (!ftpfs_proxy_host || !*ftpfs_proxy_host || !host || !*host)
585 return 0; /* sanity check */
587 if (*host == '!')
588 return 1;
590 if (!ftpfs_always_use_proxy)
591 return 0;
593 if (!strchr (host, '.'))
594 return 0;
596 load_no_proxy_list ();
597 for (npe = no_proxy; npe; npe=npe->next) {
598 char *domain = npe->domain;
600 if (domain[0] == '.') {
601 int ld = strlen (domain);
602 int lh = strlen (host);
604 while (ld && lh && host[lh - 1] == domain[ld - 1]) {
605 ld--;
606 lh--;
609 if (!ld)
610 return 0;
611 } else
612 if (!g_strcasecmp (host, domain))
613 return 0;
616 return 1;
619 static void
620 ftpfs_get_proxy_host_and_port (char *proxy, char **host, int *port)
622 char *user, *pass, *dir;
624 #if defined(HSC_PROXY)
625 #define PORT 9875
626 #else
627 #define PORT 21
628 #endif
629 dir = vfs_split_url (proxy, host, &user, port, &pass, PORT, URL_DEFAULTANON);
631 g_free (user);
632 if (pass)
633 wipe_password (pass);
634 if (dir)
635 g_free (dir);
638 static int
639 ftpfs_open_socket (struct connection *bucket)
641 struct sockaddr_in server_address;
642 struct hostent *hp;
643 int my_socket;
644 char *host;
645 int port;
646 int free_host = 0;
648 /* Use a proxy host? */
649 host = qhost (bucket);
651 if (!host || !*host){
652 print_vfs_message (_("ftpfs: Invalid host name."));
653 my_errno = EINVAL;
654 return -1;
657 /* Hosts to connect to that start with a ! should use proxy */
658 if (qproxy (bucket)){
659 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &port);
660 free_host = 1;
663 /* Get host address */
664 bzero ((char *) &server_address, sizeof (server_address));
665 server_address.sin_family = AF_INET;
666 server_address.sin_addr.s_addr = inet_addr (host);
667 if (server_address.sin_addr.s_addr != -1)
668 server_address.sin_family = AF_INET;
669 else {
670 hp = gethostbyname (host);
671 if (hp == NULL){
672 print_vfs_message (_("ftpfs: Invalid host address."));
673 my_errno = EINVAL;
674 if (free_host)
675 g_free (host);
676 return -1;
678 server_address.sin_family = hp->h_addrtype;
680 /* We copy only 4 bytes, we can not trust hp->h_length, as it comes from the DNS */
681 bcopy ((char *) hp->h_addr, (char *) &server_address.sin_addr, 4);
684 #define THE_PORT qproxy(bucket) ? port : qport (bucket)
686 server_address.sin_port = htons (THE_PORT);
688 /* Connect */
689 if ((my_socket = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
690 my_errno = errno;
691 if (free_host)
692 g_free (host);
693 return -1;
695 setup_source_route (my_socket, server_address.sin_addr.s_addr);
697 print_vfs_message (_("ftpfs: making connection to %s"), host);
698 if (free_host)
699 g_free (host);
701 enable_interrupt_key (); /* clear the interrupt flag */
703 if (connect (my_socket, (struct sockaddr *) &server_address,
704 sizeof (server_address)) < 0){
705 my_errno = errno;
706 if (errno == EINTR && got_interrupt ())
707 print_vfs_message (_("ftpfs: connection interrupted by user"));
708 else
709 print_vfs_message (_("ftpfs: connection to server failed: %s"),
710 unix_error_string(errno));
711 disable_interrupt_key();
712 close (my_socket);
713 return -1;
715 disable_interrupt_key();
716 return my_socket;
719 static struct connection *
720 open_command_connection (char *host, char *user, int port, char *netrcpass)
722 struct connection *bucket;
723 int retry_seconds, count_down;
725 bucket = g_new (struct connection, 1);
727 if (bucket == NULL)
728 ERRNOR (ENOMEM, NULL);
729 #ifdef HAVE_MAD
731 extern void *watch_free_pointer;
733 if (!watch_free_pointer)
734 watch_free_pointer = host;
736 #endif
737 qhost (bucket) = g_strdup (host);
738 quser (bucket) = g_strdup (user);
739 qcdir (bucket) = NULL;
740 qport (bucket) = port;
741 qlock (bucket) = 0;
742 qhome (bucket) = NULL;
743 qproxy (bucket)= 0;
744 qupdir (bucket)= 0;
745 qdcache (bucket)=0;
746 bucket->__inode_counter = 0;
747 bucket->lock = 0;
748 bucket->use_proxy = ftpfs_check_proxy (host);
749 bucket->password = 0;
750 bucket->use_passive_connection = ftpfs_use_passive_connections | source_route;
751 bucket->use_source_route = source_route;
752 bucket->strict_rfc959_list_cmd = !ftpfs_use_unix_list_options;
753 bucket->isbinary = TYPE_UNKNOWN;
754 bucket->remote_is_amiga = 0;
756 /* We do not want to use the passive if we are using proxies */
757 if (bucket->use_proxy)
758 bucket->use_passive_connection = 0;
760 if ((qdcache (bucket) = linklist_init ()) == NULL) {
761 my_errno = ENOMEM;
762 g_free (qhost (bucket));
763 g_free (quser (bucket));
764 g_free (bucket);
765 return NULL;
768 retry_seconds = 0;
769 do {
770 bucket->failed_on_login = 0;
772 qsock (bucket) = ftpfs_open_socket (bucket);
773 if (qsock (bucket) == -1) {
774 free_bucket (bucket);
775 return NULL;
778 if (login_server (bucket, netrcpass)) {
779 /* Logged in, no need to retry the connection */
780 break;
781 } else {
782 if (bucket->failed_on_login){
783 /* Close only the socket descriptor */
784 close (qsock (bucket));
785 } else {
786 connection_destructor (bucket);
787 return NULL;
789 if (ftpfs_retry_seconds){
790 retry_seconds = ftpfs_retry_seconds;
791 enable_interrupt_key ();
792 for (count_down = retry_seconds; count_down; count_down--){
793 print_vfs_message (_("Waiting to retry... %d (Control-C to cancel)"), count_down);
794 sleep (1);
795 if (got_interrupt ()){
796 /* my_errno = E; */
797 disable_interrupt_key ();
798 free_bucket (bucket);
799 return NULL;
802 disable_interrupt_key ();
805 } while (retry_seconds);
807 qhome (bucket) = ftpfs_get_current_directory (bucket);
808 if (!qhome (bucket))
809 qhome (bucket) = g_strdup (PATH_SEP_STR);
810 qupdir (bucket) = g_strdup (PATH_SEP_STR); /* FIXME: I changed behavior to ignore last_current_dir */
811 return bucket;
814 static int
815 is_connection_closed (struct connection *bucket)
817 fd_set rset;
818 struct timeval t;
820 if (got_sigpipe){
821 return 1;
823 t.tv_sec = 0;
824 t.tv_usec = 0;
825 FD_ZERO (&rset);
826 FD_SET (qsock (bucket), &rset);
827 while (1) {
828 if (select (qsock (bucket) + 1, &rset, NULL, NULL, &t) < 0)
829 if (errno != EINTR)
830 return 1;
831 return 0;
832 #if 0
833 if (FD_ISSET (qsock(bucket), &rset)) {
834 n = read (qsock(bucket), &read_ahead, sizeof (read_ahead));
835 if (n <= 0)
836 return 1;
837 } else
838 #endif
842 /* This routine keeps track of open connections */
843 /* Returns a connected socket to host */
844 static struct connection *
845 open_link (char *host, char *user, int port, char *netrcpass)
847 int sock;
848 struct connection *bucket;
849 struct linklist *lptr;
851 for (lptr = connections_list->next;
852 lptr != connections_list; lptr = lptr->next) {
853 bucket = lptr->data;
854 if ((strcmp (host, qhost (bucket)) == 0) &&
855 (strcmp (user, quser (bucket)) == 0) &&
856 (port == qport (bucket))) {
858 /* check the connection is closed or not, just hack */
859 if (is_connection_closed (bucket)) {
860 flush_all_directory (bucket);
861 sock = ftpfs_open_socket (bucket);
862 if (sock != -1) {
863 close (qsock (bucket));
864 qsock (bucket) = sock;
865 if (login_server (bucket, netrcpass))
866 return bucket;
869 /* connection refused */
870 lptr->prev->next = lptr->next;
871 lptr->next->prev = lptr->prev;
872 connection_destructor (bucket);
873 return NULL;
875 return bucket;
878 bucket = open_command_connection (host, user, port, netrcpass);
879 if (bucket == NULL)
880 return NULL;
881 if (!linklist_insert (connections_list, bucket)) {
882 my_errno = ENOMEM;
883 connection_destructor (bucket);
884 return NULL;
886 return bucket;
889 /* The returned directory should always contain a trailing slash */
890 static char *
891 ftpfs_get_current_directory (struct connection *bucket)
893 char buf[4096], *bufp, *bufq;
895 if (command (bucket, NONE, "PWD") == COMPLETE &&
896 get_reply(qsock(bucket), buf, sizeof(buf)) == COMPLETE) {
897 bufp = NULL;
898 for (bufq = buf; *bufq; bufq++)
899 if (*bufq == '"') {
900 if (!bufp) {
901 bufp = bufq + 1;
902 } else {
903 *bufq = 0;
904 if (*bufp) {
905 if (*(bufq - 1) != '/') {
906 *bufq++ = '/';
907 *bufq = 0;
909 if (*bufp == '/')
910 return g_strdup (bufp);
911 else {
912 /* If the remote server is an Amiga a leading slash
913 might be missing. MC needs it because it is used
914 as seperator between hostname and path internally. */
915 return g_strconcat( "/", bufp, 0);
917 } else {
918 my_errno = EIO;
919 return NULL;
924 my_errno = EIO;
925 return NULL;
929 /* Setup Passive ftp connection, we use it for source routed connections */
930 static int
931 setup_passive (int my_socket, struct connection *bucket, struct sockaddr_in *sa)
933 int xa, xb, xc, xd, xe, xf;
934 char n [6];
935 char *c = reply_str;
937 if (command (bucket, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
938 return 0;
940 /* Parse remote parameters */
941 for (c = reply_str + 4; (*c) && (!isdigit (*c)); c++)
943 if (!*c)
944 return 0;
945 if (!isdigit (*c))
946 return 0;
947 if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
948 return 0;
949 n [0] = (unsigned char) xa;
950 n [1] = (unsigned char) xb;
951 n [2] = (unsigned char) xc;
952 n [3] = (unsigned char) xd;
953 n [4] = (unsigned char) xe;
954 n [5] = (unsigned char) xf;
956 bcopy ((void *)n, &(sa->sin_addr.s_addr), 4);
957 bcopy ((void *)&n[4], &(sa->sin_port), 2);
958 setup_source_route (my_socket, sa->sin_addr.s_addr);
959 if (connect (my_socket, (struct sockaddr *) sa, sizeof (struct sockaddr_in)) < 0)
960 return 0;
961 return 1;
964 static int
965 initconn (struct connection *bucket)
967 struct sockaddr_in data_addr;
968 int data, len = sizeof(data_addr);
969 struct protoent *pe;
971 getsockname(qsock(bucket), (struct sockaddr *) &data_addr, &len);
972 data_addr.sin_port = 0;
974 pe = getprotobyname("tcp");
975 if (pe == NULL)
976 ERRNOR (EIO, -1);
977 data = socket (AF_INET, SOCK_STREAM, pe->p_proto);
978 if (data < 0)
979 ERRNOR (EIO, -1);
981 #ifdef ORIGINAL_CONNECT_CODE
982 if (bucket->use_source_route){
983 int c;
985 if ((c = setup_passive (data, bucket, &data_addr)))
986 return data;
987 print_vfs_message(_("ftpfs: could not setup passive mode for source routing"));
988 bucket->use_source_route = 0;
990 #else
991 if (bucket->use_passive_connection){
992 if ((bucket->use_passive_connection = setup_passive (data, bucket, &data_addr)))
993 return data;
995 bucket->use_source_route = 0;
996 bucket->use_passive_connection = 0;
997 print_vfs_message (_("ftpfs: could not setup passive mode"));
999 #endif
1000 /* If passive setup fails, fallback to active connections */
1001 /* Active FTP connection */
1002 if (bind (data, (struct sockaddr *)&data_addr, len) < 0)
1003 goto error_return;
1004 getsockname(data, (struct sockaddr *) &data_addr, &len);
1005 if (listen (data, 1) < 0)
1006 goto error_return;
1008 unsigned char *a = (unsigned char *)&data_addr.sin_addr;
1009 unsigned char *p = (unsigned char *)&data_addr.sin_port;
1011 if (command (bucket, WAIT_REPLY, "PORT %d,%d,%d,%d,%d,%d", a[0], a[1],
1012 a[2], a[3], p[0], p[1]) != COMPLETE)
1013 goto error_return;
1015 return data;
1016 error_return:
1017 close(data);
1018 my_errno = EIO;
1019 return -1;
1022 static int
1023 open_data_connection (struct connection *bucket, char *cmd, char *remote,
1024 int isbinary, int reget)
1026 struct sockaddr_in from;
1027 int s, j, data, fromlen = sizeof(from);
1029 if ((s = initconn (bucket)) == -1)
1030 return -1;
1031 if (changetype (bucket, isbinary) == -1)
1032 return -1;
1033 if (reget > 0){
1034 j = command (bucket, WAIT_REPLY, "REST %d", reget);
1035 if (j != CONTINUE)
1036 return -1;
1038 if (remote){
1039 char *path = translate_path (bucket, remote);
1041 j = command (bucket, WAIT_REPLY, "%s %s", cmd, path);
1042 g_free (path);
1043 } else
1044 j = command (bucket, WAIT_REPLY, "%s", cmd);
1045 if (j != PRELIM)
1046 ERRNOR (EPERM, -1);
1047 enable_interrupt_key();
1048 if (bucket->use_passive_connection)
1049 data = s;
1050 else {
1051 data = accept (s, (struct sockaddr *)&from, &fromlen);
1052 if (data < 0) {
1053 my_errno = errno;
1054 close(s);
1055 return -1;
1057 close(s);
1059 disable_interrupt_key();
1060 return data;
1063 static void
1064 my_abort (struct connection *bucket, int dsock)
1066 static unsigned char ipbuf[3] = { IAC, IP, IAC };
1067 fd_set mask;
1068 char buf[1024];
1070 print_vfs_message(_("ftpfs: aborting transfer."));
1071 if (send(qsock(bucket), ipbuf, sizeof(ipbuf), MSG_OOB) != sizeof(ipbuf)) {
1072 print_vfs_message(_("ftpfs: abort error: %s"), unix_error_string(errno));
1073 return;
1076 if (command(bucket, NONE, "%cABOR", DM) != COMPLETE){
1077 print_vfs_message (_("ftpfs: abort failed"));
1078 return;
1080 if (dsock != -1) {
1081 FD_ZERO(&mask);
1082 FD_SET(dsock, &mask);
1083 if (select(dsock + 1, &mask, NULL, NULL, NULL) > 0)
1084 while (read(dsock, buf, sizeof(buf)) > 0);
1086 if ((get_reply(qsock(bucket), NULL, 0) == TRANSIENT) && (code == 426))
1087 get_reply(qsock(bucket), NULL, 0);
1090 static void
1091 resolve_symlink_without_ls_options(struct connection *bucket, struct dir *dir)
1093 struct linklist *flist;
1094 struct direntry *fe, *fel;
1095 char tmp[MC_MAXPATHLEN];
1096 int depth;
1098 dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1099 for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next) {
1100 /* flist->data->l_stat is alread initialized with 0 */
1101 fel = flist->data;
1102 if (S_ISLNK(fel->s.st_mode) && fel->linkname) {
1103 if (fel->linkname[0] == '/') {
1104 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1105 continue;
1106 strcpy (tmp, fel->linkname);
1107 } else {
1108 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1109 continue;
1110 strcpy (tmp, dir->remote_path);
1111 if (tmp[1] != '\0')
1112 strcat (tmp, "/");
1113 strcat (tmp + 1, fel->linkname);
1115 for ( depth = 0; depth < 100; depth++) { /* depth protects against recursive symbolic links */
1116 canonicalize_pathname (tmp);
1117 fe = _get_file_entry(bucket, tmp, 0, 0);
1118 if (fe) {
1119 if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0) {
1120 /* Symlink points to link which isn't resolved, yet. */
1121 if (fe->linkname[0] == '/') {
1122 if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1123 break;
1124 strcpy (tmp, fe->linkname);
1125 } else {
1126 /* at this point tmp looks always like this
1127 /directory/filename, i.e. no need to check
1128 strrchr's return value */
1129 *(strrchr (tmp, '/') + 1) = '\0'; /* dirname */
1130 if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1131 break;
1132 strcat (tmp, fe->linkname);
1134 continue;
1135 } else {
1136 fel->l_stat = g_new (struct stat, 1);
1137 if ( S_ISLNK (fe->s.st_mode))
1138 *fel->l_stat = *fe->l_stat;
1139 else
1140 *fel->l_stat = fe->s;
1141 (*fel->l_stat).st_ino = bucket->__inode_counter++;
1144 break;
1148 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1151 static void
1152 resolve_symlink_with_ls_options(struct connection *bucket, struct dir *dir)
1154 char buffer[2048] = "", *filename;
1155 int sock;
1156 FILE *fp;
1157 struct stat s;
1158 struct linklist *flist;
1159 struct direntry *fe;
1160 int switch_method = 0;
1162 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1163 if (strchr (dir->remote_path, ' ')) {
1164 if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE) {
1165 print_vfs_message(_("ftpfs: CWD failed."));
1166 return;
1168 sock = open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1170 else
1171 sock = open_data_connection (bucket, "LIST -lLa",
1172 dir->remote_path, TYPE_ASCII, 0);
1174 if (sock == -1) {
1175 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1176 return;
1179 fp = fdopen(sock, "r");
1180 if (fp == NULL) {
1181 close(sock);
1182 print_vfs_message(_("ftpfs: couldn't resolve symlink"));
1183 return;
1185 enable_interrupt_key();
1186 flist = dir->file_list->next;
1187 while (1) {
1188 do {
1189 if (flist == dir->file_list)
1190 goto done;
1191 fe = flist->data;
1192 flist = flist->next;
1193 } while (!S_ISLNK(fe->s.st_mode));
1194 while (1) {
1195 if (fgets (buffer, sizeof (buffer), fp) == NULL)
1196 goto done;
1197 if (logfile){
1198 fputs (buffer, logfile);
1199 fflush (logfile);
1201 if (vfs_parse_ls_lga (buffer, &s, &filename, NULL)) {
1202 int r = strcmp(fe->name, filename);
1203 g_free(filename);
1204 if (r == 0) {
1205 if (S_ISLNK (s.st_mode)) {
1206 /* This server doesn't understand LIST -lLa */
1207 switch_method = 1;
1208 goto done;
1210 fe->l_stat = g_new (struct stat, 1);
1211 if (fe->l_stat == NULL)
1212 goto done;
1213 *fe->l_stat = s;
1214 (*fe->l_stat).st_ino = bucket->__inode_counter++;
1215 break;
1217 if (r < 0)
1218 break;
1222 done:
1223 while (fgets(buffer, sizeof(buffer), fp) != NULL);
1224 disable_interrupt_key();
1225 fclose(fp);
1226 get_reply(qsock(bucket), NULL, 0);
1227 if (switch_method) {
1228 bucket->strict_rfc959_list_cmd = 1;
1229 resolve_symlink_without_ls_options(bucket, dir);
1233 static void
1234 resolve_symlink(struct connection *bucket, struct dir *dir)
1236 print_vfs_message(_("Resolving symlink..."));
1238 if (bucket->strict_rfc959_list_cmd)
1239 resolve_symlink_without_ls_options(bucket, dir);
1240 else
1241 resolve_symlink_with_ls_options(bucket, dir);
1245 #define X "ftp"
1246 #define X_myname "/#ftp:"
1247 #define vfs_X_ops vfs_ftpfs_ops
1248 #define X_fill_names ftpfs_fill_names
1249 #define X_hint_reread ftpfs_hint_reread
1250 #define X_flushdir ftpfs_flushdir
1251 #define X_done ftpfs_done
1252 #include "shared_ftp_fish.c"
1254 static char*
1255 get_path (struct connection **bucket, char *path)
1257 return s_get_path (bucket, path, "/#ftp:");
1260 /* Inserts an entry for "." (and "..") into the linked list. Ignore any
1261 errors because "." isn't important (as fas as you don't try to save a
1262 file in the root dir of the ftp server).
1263 Actually the dot is needed when stating the root directory, e.g.
1264 mc_stat ("/ftp#localhost", &buf). Down the call tree _get_file_entry
1265 gets called with filename = "/" which will be transformed into "."
1266 before searching for a fileentry. Whithout "." in the linked list
1267 this search fails. -- Norbert. */
1268 static void
1269 insert_dots (struct linklist *file_list, struct connection *bucket)
1271 struct direntry *fe;
1272 int i;
1273 char buffer[][58] = {
1274 "drwxrwxrwx 1 0 0 1024 Jan 1 1970 .",
1275 "drwxrwxrwx 1 0 0 1024 Jan 1 1970 .."
1278 for (i = 0; i < 2; i++ ) {
1279 fe = g_new (struct direntry, 1);
1280 if (fe == NULL)
1281 return;
1282 if (vfs_parse_ls_lga (buffer[i], &fe->s, &fe->name, &fe->linkname)) {
1283 fe->freshly_created = 0;
1284 fe->count = 1;
1285 fe->local_filename = fe->remote_filename = NULL;
1286 fe->l_stat = NULL;
1287 fe->bucket = bucket;
1288 (fe->s).st_ino = bucket->__inode_counter++;
1290 if (!linklist_insert(file_list, fe))
1291 g_free(fe);
1292 } else
1293 g_free (fe);
1297 static struct dir *
1298 retrieve_dir(struct connection *bucket, char *remote_path, int resolve_symlinks)
1300 #ifdef OLD_READ
1301 FILE *fp;
1302 #endif
1303 int sock, has_symlinks;
1304 struct linklist *file_list, *p;
1305 struct direntry *fe;
1306 char buffer[8192];
1307 struct dir *dcache;
1308 int got_intr = 0;
1309 int dot_found = 0;
1310 int has_spaces = (strchr (remote_path, ' ') != NULL);
1312 canonicalize_pathname (remote_path);
1313 for (p = qdcache(bucket)->next;p != qdcache(bucket);
1314 p = p->next) {
1315 dcache = p->data;
1316 if (strcmp(dcache->remote_path, remote_path) == 0) {
1317 struct timeval tim;
1319 gettimeofday(&tim, NULL);
1320 if (((tim.tv_sec < dcache->timestamp.tv_sec) && !force_expiration)
1321 || dcache->symlink_status == FTPFS_RESOLVING_SYMLINKS) {
1322 if (resolve_symlinks && dcache->symlink_status == FTPFS_UNRESOLVED_SYMLINKS)
1323 resolve_symlink(bucket, dcache);
1324 return dcache;
1325 } else {
1326 force_expiration = 0;
1327 p->next->prev = p->prev;
1328 p->prev->next = p->next;
1329 dir_destructor(dcache);
1330 g_free (p);
1331 break;
1336 has_symlinks = 0;
1337 if (bucket->strict_rfc959_list_cmd)
1338 print_vfs_message(_("ftpfs: Reading FTP directory %s... (don't use UNIX ls options)"), remote_path);
1339 else
1340 print_vfs_message(_("ftpfs: Reading FTP directory %s..."), remote_path);
1341 if (has_spaces || bucket->strict_rfc959_list_cmd || ftpfs_first_cd_then_ls) {
1342 if (ftpfs_chdir_internal (bucket, remote_path) != COMPLETE) {
1343 my_errno = ENOENT;
1344 print_vfs_message(_("ftpfs: CWD failed."));
1345 return NULL;
1349 file_list = linklist_init();
1350 if (file_list == NULL)
1351 ERRNOR (ENOMEM, NULL);
1352 dcache = g_new (struct dir, 1);
1353 if (dcache == NULL) {
1354 my_errno = ENOMEM;
1355 linklist_destroy(file_list, NULL);
1356 print_vfs_message(_("ftpfs: FAIL"));
1357 return NULL;
1359 gettimeofday(&dcache->timestamp, NULL);
1360 dcache->timestamp.tv_sec += ftpfs_directory_timeout;
1361 dcache->file_list = file_list;
1362 dcache->remote_path = g_strdup(remote_path);
1363 dcache->count = 1;
1364 dcache->symlink_status = FTPFS_NO_SYMLINKS;
1366 if (bucket->strict_rfc959_list_cmd == 1)
1367 sock = open_data_connection (bucket, "LIST", 0, TYPE_ASCII, 0);
1368 else if (has_spaces || ftpfs_first_cd_then_ls)
1369 sock = open_data_connection (bucket, "LIST -la", ".", TYPE_ASCII, 0);
1370 else {
1371 /* Trailing "/." is necessary if remote_path is a symlink
1372 but don't generate "//." */
1373 char *path = g_strconcat (remote_path,
1374 remote_path[1] == '\0' ? "" : PATH_SEP_STR,
1375 ".", (char *) 0);
1377 sock = open_data_connection (bucket, "LIST -la", path, TYPE_ASCII, 0);
1378 g_free (path);
1381 if (sock == -1)
1382 goto fallback;
1384 #ifdef OLD_READ
1385 #define close_this_sock(x,y) fclose (x)
1386 fp = fdopen(sock, "r");
1387 if (fp == NULL) {
1388 my_errno = errno;
1389 close(sock);
1390 goto error_2;
1392 #else
1393 #define close_this_sock(x,y) close (y)
1394 #endif
1396 /* Clear the interrupt flag */
1397 enable_interrupt_key ();
1399 errno = 0;
1400 #ifdef OLD_READ
1401 while (fgets (buffer, sizeof (buffer), fp) != NULL) {
1402 if (got_intr = got_interrupt ())
1403 break;
1404 #else
1405 while ((got_intr = get_line_interruptible (buffer, sizeof (buffer), sock)) != EINTR){
1406 #endif
1407 int eof = got_intr == 0;
1409 if (logfile){
1410 fputs (buffer, logfile);
1411 fputs ("\n", logfile);
1412 fflush (logfile);
1414 if (buffer [0] == 0 && eof)
1415 break;
1416 fe = g_new (struct direntry, 1);
1417 fe->freshly_created = 0;
1418 fe->local_filename = NULL;
1419 if (fe == NULL) {
1420 my_errno = ENOMEM;
1421 goto error_1;
1423 if (vfs_parse_ls_lga (buffer, &fe->s, &fe->name, &fe->linkname)) {
1424 if (strcmp (fe->name, ".") == 0)
1425 dot_found = 1;
1426 fe->count = 1;
1427 fe->local_filename = fe->remote_filename = NULL;
1428 fe->l_stat = NULL;
1429 fe->bucket = bucket;
1430 (fe->s).st_ino = bucket->__inode_counter++;
1431 if (S_ISLNK(fe->s.st_mode))
1432 has_symlinks = 1;
1434 if (!linklist_insert(file_list, fe)) {
1435 g_free(fe);
1436 my_errno = ENOMEM;
1437 goto error_1;
1440 else
1441 g_free(fe);
1442 if (eof)
1443 break;
1445 if (got_intr){
1446 disable_interrupt_key();
1447 print_vfs_message(_("ftpfs: reading FTP directory interrupt by user"));
1448 #ifdef OLD_READ
1449 my_abort(bucket, fileno(fp));
1450 #else
1451 my_abort(bucket, sock);
1452 #endif
1453 close_this_sock(fp, sock);
1454 my_errno = EINTR;
1455 goto error_3;
1457 close_this_sock(fp, sock);
1458 disable_interrupt_key();
1459 if ((get_reply (qsock (bucket), NULL, 0) != COMPLETE) ||
1460 (file_list->next == file_list))
1461 goto fallback;
1464 if (!dot_found)
1465 insert_dots (file_list, bucket);
1467 if (!linklist_insert(qdcache(bucket), dcache)) {
1468 my_errno = ENOMEM;
1469 goto error_3;
1471 if (has_symlinks) {
1472 if (resolve_symlinks)
1473 resolve_symlink(bucket, dcache);
1474 else
1475 dcache->symlink_status = FTPFS_UNRESOLVED_SYMLINKS;
1477 print_vfs_message(_("ftpfs: got listing"));
1478 return dcache;
1479 error_1:
1480 disable_interrupt_key();
1481 close_this_sock(fp, sock);
1482 #ifdef OLD_READ
1483 error_2:
1484 #endif
1485 get_reply(qsock(bucket), NULL, 0);
1486 error_3:
1487 g_free(dcache->remote_path);
1488 g_free(dcache);
1489 linklist_destroy(file_list, direntry_destructor);
1490 print_vfs_message(_("ftpfs: failed"));
1491 return NULL;
1493 fallback:
1494 if (bucket->__inode_counter == 0 && (!bucket->strict_rfc959_list_cmd)) {
1495 /* It's our first attempt to get a directory listing from this
1496 server (UNIX style LIST command) */
1497 bucket->strict_rfc959_list_cmd = 1;
1498 g_free(dcache->remote_path);
1499 g_free(dcache);
1500 linklist_destroy(file_list, direntry_destructor);
1501 return retrieve_dir (bucket, remote_path, resolve_symlinks);
1504 /* Ok, maybe the directory exists but the remote server doesn't
1505 list "." and "..".
1506 Check whether the directory exists:
1508 if (has_spaces || bucket->strict_rfc959_list_cmd ||
1509 ftpfs_first_cd_then_ls) /* CWD has been already performed, i.e.
1510 we know that remote_path exists */
1511 goto ok;
1512 else {
1513 if (bucket->remote_is_amiga) {
1514 /* The Amiga ftp server needs extra processing because it
1515 always gets relative pathes instead of absolute pathes
1516 like anyone else */
1517 char *p = ftpfs_get_current_directory (bucket);
1519 if (ftpfs_chdir_internal (bucket, remote_path) == COMPLETE) {
1520 ftpfs_chdir_internal (bucket, p);
1521 g_free (p);
1522 goto ok;
1524 } else {
1525 if (ftpfs_chdir_internal (bucket, remote_path) == COMPLETE)
1526 goto ok;
1530 my_errno = EACCES;
1531 g_free(dcache->remote_path);
1532 g_free(dcache);
1533 linklist_destroy(file_list, direntry_destructor);
1534 print_vfs_message(_("ftpfs: failed; nowhere to fallback to"));
1535 return NULL;
1538 static int
1539 store_file(struct direntry *fe)
1541 int local_handle, sock, n, total;
1542 #ifdef HAVE_STRUCT_LINGER
1543 struct linger li;
1544 #else
1545 int flag_one = 1;
1546 #endif
1547 char buffer[8192];
1548 struct stat s;
1550 local_handle = open(fe->local_filename, O_RDONLY);
1551 unlink (fe->local_filename);
1552 if (local_handle == -1)
1553 ERRNOR (EIO, 0);
1554 fstat(local_handle, &s);
1555 sock = open_data_connection(fe->bucket, "STOR", fe->remote_filename, TYPE_BINARY, 0);
1556 if (sock < 0) {
1557 close(local_handle);
1558 return 0;
1560 #ifdef HAVE_STRUCT_LINGER
1561 li.l_onoff = 1;
1562 li.l_linger = 120;
1563 setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof(li));
1564 #else
1565 setsockopt(sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1566 #endif
1567 total = 0;
1569 enable_interrupt_key();
1570 while (1) {
1571 while ((n = read(local_handle, buffer, sizeof(buffer))) < 0) {
1572 if (errno == EINTR) {
1573 if (got_interrupt()) {
1574 my_errno = EINTR;
1575 goto error_return;
1577 else
1578 continue;
1580 my_errno = errno;
1581 goto error_return;
1583 if (n == 0)
1584 break;
1585 while (write(sock, buffer, n) < 0) {
1586 if (errno == EINTR) {
1587 if (got_interrupt()) {
1588 my_errno = EINTR;
1589 goto error_return;
1591 else
1592 continue;
1594 my_errno = errno;
1595 goto error_return;
1597 total += n;
1598 print_vfs_message(_("ftpfs: storing file %d (%d)"),
1599 total, s.st_size);
1601 disable_interrupt_key();
1602 close(sock);
1603 close(local_handle);
1604 if (get_reply (qsock (fe->bucket), NULL, 0) != COMPLETE)
1605 ERRNOR (EIO, 0);
1606 return 1;
1607 error_return:
1608 disable_interrupt_key();
1609 close(sock);
1610 close(local_handle);
1611 get_reply(qsock(fe->bucket), NULL, 0);
1612 return 0;
1615 static int
1616 linear_start(struct direntry *fe, int offset)
1618 fe->local_stat.st_mtime = 0;
1619 fe->data_sock = open_data_connection(fe->bucket, "RETR", fe->remote_filename, TYPE_BINARY, offset);
1620 if (fe->data_sock == -1)
1621 ERRNOR (EACCES, 0);
1622 fe->linear_state = LS_LINEAR_OPEN;
1623 return 1;
1626 static void
1627 linear_abort (struct direntry *fe)
1629 my_abort(fe->bucket, fe->data_sock);
1630 fe->data_sock = -1;
1633 static int
1634 linear_read (struct direntry *fe, void *buf, int len)
1636 int n;
1637 while ((n = read (fe->data_sock, buf, len))<0) {
1638 if ((errno == EINTR) && !got_interrupt())
1639 continue;
1640 break;
1643 if (n<0)
1644 linear_abort(fe);
1646 if (!n) {
1647 if ((get_reply (qsock (fe->bucket), NULL, 0) != COMPLETE)) {
1648 my_errno = EIO;
1649 n=-1;
1651 close (fe->data_sock);
1652 fe->data_sock = -1;
1654 ERRNOR (errno, n);
1657 static void
1658 linear_close (struct direntry *fe)
1660 if (fe->data_sock != -1)
1661 linear_abort(fe);
1664 int ftpfs_ctl (void *data, int ctlop, int arg)
1666 struct filp *fp = data;
1667 switch (ctlop) {
1668 case MCCTL_IS_NOTREADY:
1670 int v;
1672 if (!fp->fe->linear_state)
1673 vfs_die ("You may not do this");
1674 if (fp->fe->linear_state == LS_LINEAR_CLOSED)
1675 return 0;
1677 v = select_on_two (fp->fe->data_sock, 0);
1678 if (((v < 0) && (errno == EINTR)) || v == 0)
1679 return 1;
1680 return 0;
1682 default:
1683 return 0;
1687 static int
1688 send_ftp_command(char *filename, char *cmd, int flags)
1690 char *remote_path, *p;
1691 struct connection *bucket;
1692 int r;
1693 int flush_directory_cache = (flags & OPT_FLUSH) && (normal_flush > 0);
1695 if (!(remote_path = get_path(&bucket, filename)))
1696 return -1;
1697 p = translate_path (bucket, remote_path);
1698 r = command (bucket, WAIT_REPLY, cmd, p);
1699 g_free (p);
1700 g_free(remote_path);
1701 vfs_add_noncurrent_stamps (&vfs_ftpfs_ops, (vfsid) bucket, NULL);
1702 if (flags & OPT_IGNORE_ERROR)
1703 r = COMPLETE;
1704 if (r != COMPLETE)
1705 ERRNOR (EPERM, -1);
1706 if (flush_directory_cache)
1707 flush_all_directory(bucket);
1708 return 0;
1711 /* This routine is called as the last step in load_setup */
1712 void
1713 ftpfs_init_passwd(void)
1715 struct passwd *passwd_info;
1716 char *p, hostname[MAXHOSTNAMELEN];
1717 struct hostent *hp;
1719 ftpfs_anonymous_passwd = load_anon_passwd ();
1720 if (ftpfs_anonymous_passwd)
1721 return;
1723 if ((passwd_info = getpwuid (geteuid ())) == NULL)
1724 p = "unknown";
1725 else
1726 p = passwd_info->pw_name;
1727 gethostname(hostname, sizeof(hostname));
1728 hp = gethostbyname(hostname);
1729 if (hp != NULL)
1730 ftpfs_anonymous_passwd = g_strconcat (p, "@", hp->h_name, NULL);
1731 else
1732 ftpfs_anonymous_passwd = g_strconcat (p, "@", hostname, NULL);
1733 endpwent ();
1737 ftpfs_init (vfs *me)
1739 connections_list = linklist_init();
1740 ftpfs_directory_timeout = FTPFS_DIRECTORY_TIMEOUT;
1741 return 1;
1744 static int ftpfs_chmod (vfs *me, char *path, int mode)
1746 char buf[BUF_SMALL];
1748 g_snprintf(buf, sizeof(buf), "SITE CHMOD %4.4o %%s", mode & 07777);
1749 return send_ftp_command(path, buf, OPT_IGNORE_ERROR | OPT_FLUSH);
1752 static int ftpfs_chown (vfs *me, char *path, int owner, int group)
1754 #if 0
1755 my_errno = EPERM;
1756 return -1;
1757 #else
1758 /* Everyone knows it is not possible to chown remotely, so why bother them.
1759 If someone's root, then copy/move will always try to chown it... */
1760 return 0;
1761 #endif
1764 static int ftpfs_unlink (vfs *me, char *path)
1766 return send_ftp_command(path, "DELE %s", OPT_FLUSH);
1769 /* Return true if path is the same directoy as the one we are on now */
1770 static int
1771 is_same_dir (char *path, struct connection *bucket)
1773 if (!qcdir (bucket))
1774 return 0;
1775 if (strcmp (path, qcdir (bucket)) == 0)
1776 return 1;
1777 return 0;
1780 static int
1781 ftpfs_chdir_internal (struct connection *bucket ,char *remote_path)
1783 int r;
1784 char *p;
1786 if (!bucket->cwd_defered && is_same_dir (remote_path, bucket))
1787 return COMPLETE;
1789 p = translate_path (bucket, remote_path);
1790 r = command (bucket, WAIT_REPLY, "CWD %s", p);
1791 g_free (p);
1793 if (r != COMPLETE) {
1794 my_errno = EIO;
1795 } else {
1796 if (qcdir(bucket))
1797 g_free(qcdir(bucket));
1798 qcdir(bucket) = g_strdup (remote_path);
1799 bucket->cwd_defered = 0;
1801 return r;
1804 static int ftpfs_rename (vfs *me, char *path1, char *path2)
1806 send_ftp_command(path1, "RNFR %s", OPT_FLUSH);
1807 return send_ftp_command(path2, "RNTO %s", OPT_FLUSH);
1810 static int ftpfs_mkdir (vfs *me, char *path, mode_t mode)
1812 return send_ftp_command(path, "MKD %s", OPT_FLUSH);
1815 static int ftpfs_rmdir (vfs *me, char *path)
1817 return send_ftp_command(path, "RMD %s", OPT_FLUSH);
1820 void ftpfs_set_debug (const char *file)
1822 logfile = fopen (file, "w+");
1825 static void my_forget (char *file)
1827 struct linklist *l;
1828 char *host, *user, *pass, *rp;
1829 int port;
1831 #ifndef BROKEN_PATHS
1832 if (strncmp (file, "/#ftp:", 6))
1833 return; /* Normal: consider cd /bla/#ftp */
1834 #else
1835 if (!(file = strstr (file, "/#ftp:")))
1836 return;
1837 #endif
1839 file += 6;
1840 if (!(rp = my_get_host_and_username (file, &host, &user, &port, &pass))) {
1841 g_free (host);
1842 g_free (user);
1843 if (pass)
1844 wipe_password (pass);
1845 return;
1848 /* we do not care about the path actually */
1849 g_free (rp);
1851 for (l = connections_list->next; l != connections_list; l = l->next){
1852 struct connection *bucket = l->data;
1854 if ((strcmp (host, qhost (bucket)) == 0) &&
1855 (strcmp (user, quser (bucket)) == 0) &&
1856 (port == qport (bucket))){
1858 /* close socket: the child owns it now */
1859 close (bucket->sock);
1860 bucket->sock = -1;
1862 /* reopen the connection */
1863 bucket->sock = ftpfs_open_socket (bucket);
1864 if (bucket->sock != -1)
1865 login_server (bucket, pass);
1866 break;
1869 g_free (host);
1870 g_free (user);
1871 if (pass)
1872 wipe_password (pass);
1875 vfs vfs_ftpfs_ops = {
1876 NULL, /* This is place of next pointer */
1877 "File Tranfer Protocol (ftp)",
1878 F_NET, /* flags */
1879 "ftp:", /* prefix */
1880 NULL, /* data */
1881 0, /* errno */
1882 ftpfs_init,
1883 ftpfs_done,
1884 ftpfs_fill_names,
1885 NULL,
1887 s_open,
1888 s_close,
1889 s_read,
1890 s_write,
1892 s_opendir,
1893 s_readdir,
1894 s_closedir,
1895 s_telldir,
1896 s_seekdir,
1898 s_stat,
1899 s_lstat,
1900 s_fstat,
1902 ftpfs_chmod,
1903 ftpfs_chown, /* not really implemented but returns success */
1904 NULL,
1906 s_readlink,
1907 NULL,
1908 NULL,
1909 ftpfs_unlink,
1911 ftpfs_rename,
1912 s_chdir,
1913 s_errno,
1914 s_lseek,
1915 NULL,
1917 s_getid,
1918 s_nothingisopen,
1919 s_free,
1921 s_getlocalcopy,
1922 s_ungetlocalcopy,
1924 ftpfs_mkdir,
1925 ftpfs_rmdir,
1926 ftpfs_ctl,
1927 s_setctl
1929 MMAPNULL
1932 #ifdef USE_NETRC
1933 static char buffer[BUF_MEDIUM];
1934 static char *netrc, *netrcp;
1936 static int netrc_next (void)
1938 char *p;
1939 int i;
1940 static char *keywords [] = { "default", "machine",
1941 "login", "password", "passwd",
1942 "account", "macdef" };
1944 while (1) {
1945 netrcp = skip_separators (netrcp);
1946 if (*netrcp != '\n')
1947 break;
1948 netrcp++;
1950 if (!*netrcp)
1951 return 0;
1952 p = buffer;
1953 if (*netrcp == '"') {
1954 for (;*netrcp != '"' && *netrcp; netrcp++) {
1955 if (*netrcp == '\\')
1956 netrcp++;
1957 *p++ = *netrcp;
1959 } else {
1960 for (;*netrcp != '\n' && *netrcp != '\t' && *netrcp != ' ' &&
1961 *netrcp != ',' && *netrcp; netrcp++) {
1962 if (*netrcp == '\\')
1963 netrcp++;
1964 *p++ = *netrcp;
1967 *p = 0;
1968 if (!*buffer)
1969 return 0;
1970 for (i = 0; i < sizeof (keywords) / sizeof (keywords [0]); i++)
1971 if (!strcmp (keywords [i], buffer))
1972 break;
1973 return i + 1;
1976 int lookup_netrc (char *host, char **login, char **pass)
1978 char *netrcname, *tmp;
1979 char hostname[MAXHOSTNAMELEN], *domain;
1980 int keyword;
1981 struct stat mystat;
1982 static int be_angry = 1;
1983 static struct rupcache {
1984 struct rupcache *next;
1985 char *host;
1986 char *login;
1987 char *pass;
1988 } *rup_cache = NULL, *rupp;
1990 for (rupp = rup_cache; rupp != NULL; rupp = rupp->next)
1991 if (!strcmp (host, rupp->host)) {
1992 if (rupp->login != NULL)
1993 *login = g_strdup (rupp->login);
1994 if (rupp->pass != NULL)
1995 *pass = g_strdup (rupp->pass);
1996 return 0;
1998 netrcname = concat_dir_and_file (home_dir, ".netrc");
1999 netrcp = netrc = load_file (netrcname);
2000 if (netrc == NULL) {
2001 g_free (netrcname);
2002 return 0;
2004 if (gethostname (hostname, sizeof (hostname)) < 0)
2005 *hostname = 0;
2006 if (!(domain = strchr (hostname, '.')))
2007 domain = "";
2009 while ((keyword = netrc_next ())) {
2010 if (keyword == 2) {
2011 if (netrc_next () != 8)
2012 continue;
2013 if (g_strcasecmp (host, buffer) &&
2014 ((tmp = strchr (host, '.')) == NULL ||
2015 g_strcasecmp (tmp, domain) ||
2016 g_strncasecmp (host, buffer, tmp - host) ||
2017 buffer [tmp - host]))
2018 continue;
2019 } else if (keyword != 1)
2020 continue;
2021 while ((keyword = netrc_next ()) > 2) {
2022 switch (keyword) {
2023 case 3:
2024 if (netrc_next ())
2025 if (*login == NULL)
2026 *login = g_strdup (buffer);
2027 else if (strcmp (*login, buffer))
2028 keyword = 20;
2029 break;
2030 case 4:
2031 case 5:
2032 if (strcmp (*login, "anonymous") && strcmp (*login, "ftp") &&
2033 stat (netrcname, &mystat) >= 0 &&
2034 (mystat.st_mode & 077)) {
2035 if (be_angry) {
2036 message_1s (1, MSG_ERROR, _("~/.netrc file has not correct mode.\n"
2037 "Remove password or correct mode."));
2038 be_angry = 0;
2040 g_free (netrc);
2041 g_free (netrcname);
2042 return -1;
2044 if (netrc_next () && *pass == NULL)
2045 *pass = g_strdup (buffer);
2046 break;
2047 case 6:
2048 if (stat (netrcname, &mystat) >= 0 &&
2049 (mystat.st_mode & 077)) {
2050 if (be_angry) {
2051 message_1s (1, MSG_ERROR, _("~/.netrc file has not correct mode.\n"
2052 "Remove password or correct mode."));
2053 be_angry = 0;
2055 g_free (netrc);
2056 g_free (netrcname);
2057 return -1;
2059 netrc_next ();
2060 break;
2061 case 7:
2062 for (;;) {
2063 while (*netrcp != '\n' && *netrcp);
2064 if (*netrcp != '\n')
2065 break;
2066 netrcp++;
2067 if (*netrcp == '\n' || !*netrcp)
2068 break;
2070 break;
2072 if (keyword == 20)
2073 break;
2075 if (keyword == 20)
2076 continue;
2077 else
2078 break;
2080 rupp = g_new (struct rupcache, 1);
2081 rupp->host = g_strdup (host);
2082 rupp->login = rupp->pass = 0;
2084 if (*login != NULL)
2085 rupp->login = g_strdup (*login);
2086 if (*pass != NULL)
2087 rupp->pass = g_strdup (*pass);
2088 rupp->next = rup_cache;
2089 rup_cache = rupp;
2091 g_free (netrc);
2092 g_free (netrcname);
2093 return 0;
2096 #endif /* USE_NETRC */