* POTFILES.in: add gnome/gcustom-layout.c file
[midnight-commander.git] / vfs / ftpfs.c
blob8b066b6e3fa002ea1dd5b82fef7af6bf14ba0736
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;
175 static char buf[255]; /* No one ever needs more ;-).
176 Actually I consider this static a bug
177 -- Norbert */
179 if (!bucket->remote_is_amiga || strlen (remote_path) >= sizeof (buf) - 1)
180 return remote_path;
181 else {
182 if (logfile) {
183 fprintf (logfile, "MC -- translate_path: %s\n", remote_path);
184 fflush (logfile);
187 if (*remote_path == '/' && remote_path[1] == '\0')
188 return "."; /* Don't change "/" into "", e.g. "CWD " would be
189 invalid. */
191 /* strip leading slash */
192 if (*remote_path == '/')
193 strcpy (buf, remote_path + 1);
194 else
195 strcpy (buf, remote_path);
197 /* replace first occurance of ":/" with ":" */
198 if ((p = strchr (buf, ':')) && *(p + 1) == '/')
199 strcpy (p + 1, p + 2);
201 /* strip trailing "/." */
202 if ((p = strrchr (buf, '/')) && *(p + 1) == '.' && *(p + 2) == '\0')
203 *p = '\0';
204 return buf;
208 /* Extract the hostname and username from the path */
211 * path is in the form: [user@]hostname:port/remote-dir, e.g.:
212 * ftp://sunsite.unc.edu/pub/linux
213 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
214 * ftp://tsx-11.mit.edu:8192/
215 * ftp://joe@foo.edu:11321/private
216 * If the user is empty, e.g. ftp://@roxanne/private, then your login name
217 * is supplied.
221 static char *
222 my_get_host_and_username (char *path, char **host, char **user, int *port, char **pass)
224 return vfs_split_url (path, host, user, port, pass, 21, URL_DEFAULTANON);
227 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
228 static int
229 get_reply (int sock, char *string_buf, int string_len)
231 char answer[1024];
232 int i;
234 for (;;) {
235 if (!get_line (sock, answer, sizeof (answer), '\n')){
236 if (string_buf)
237 *string_buf = 0;
238 code = 421;
239 return 4;
241 switch (sscanf(answer, "%d", &code)){
242 case 0:
243 if (string_buf) {
244 strncpy (string_buf, answer, string_len - 1);
245 *(string_buf + string_len - 1) = 0;
247 code = 500;
248 return 5;
249 case 1:
250 if (answer[3] == '-') {
251 while (1) {
252 if (!get_line (sock, answer, sizeof(answer), '\n')){
253 if (string_buf)
254 *string_buf = 0;
255 code = 421;
256 return 4;
258 if ((sscanf (answer, "%d", &i) > 0) &&
259 (code == i) && (answer[3] == ' '))
260 break;
263 if (string_buf){
264 strncpy (string_buf, answer, string_len - 1);
265 *(string_buf + string_len - 1) = 0;
267 return code / 100;
272 static int
273 command (struct connection *bucket, int wait_reply, char *fmt, ...)
275 va_list ap;
276 char *str, *fmt_str;
277 int status;
278 int sock = qsock (bucket);
280 va_start (ap, fmt);
282 fmt_str = g_strdup_vprintf (fmt, ap);
283 va_end (ap);
285 str = g_strconcat (fmt_str, "\r\n", NULL);
286 g_free (fmt_str);
288 if (logfile){
289 if (strncmp (str, "PASS ", 5) == 0){
290 char *tmp = "PASS <Password not logged>\r\n";
292 fwrite (tmp, strlen (tmp), 1, logfile);
293 } else
294 fwrite (str, strlen (str), 1, logfile);
296 fflush (logfile);
299 got_sigpipe = 0;
300 enable_interrupt_key ();
301 status = write (sock, str, strlen (str));
302 g_free (str);
304 if (status < 0){
305 code = 421;
307 if (errno == EPIPE){
308 got_sigpipe = 1;
310 disable_interrupt_key ();
311 return TRANSIENT;
313 disable_interrupt_key ();
315 if (wait_reply)
316 return get_reply (sock, (wait_reply & WANT_STRING) ? reply_str : NULL, sizeof (reply_str)-1);
317 return COMPLETE;
320 static void
321 connection_close (void *data)
323 struct connection *bucket = data;
325 if (qsock (bucket) != -1){
326 print_vfs_message ("ftpfs: Disconnecting from %s", qhost (bucket));
327 command(bucket, NONE, "QUIT");
328 close(qsock(bucket));
332 /* some defines only used by changetype */
333 /* These two are valid values for the second parameter */
334 #define TYPE_ASCII 0
335 #define TYPE_BINARY 1
337 /* This one is only used to initialize bucket->isbinary, don't use it as
338 second parameter to changetype. */
339 #define TYPE_UNKNOWN -1
341 static int
342 changetype (struct connection *bucket, int binary)
344 if (binary != bucket->isbinary) {
345 if (command (bucket, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE)
346 ERRNOR (EIO, -1);
347 bucket->isbinary = binary;
349 return binary;
352 /* This routine logs the user in */
353 static int
354 login_server (struct connection *bucket, char *netrcpass)
356 #if defined(HSC_PROXY)
357 char *proxypass, *proxyname;
358 #endif
359 char *pass;
360 char *op;
361 char *name; /* login user name */
362 int anon = 0;
363 char reply_string[255];
365 bucket->isbinary = TYPE_UNKNOWN;
366 if (netrcpass)
367 op = g_strdup (netrcpass);
368 else {
369 if (!strcmp (quser (bucket), "anonymous") ||
370 !strcmp (quser (bucket), "ftp")) {
371 op = g_strdup (ftpfs_anonymous_passwd);
372 anon = 1;
373 } else {
374 char *p;
376 if (!bucket->password){
377 p = g_strconcat (" FTP: Password required for ", quser (bucket),
378 " ", NULL);
379 op = vfs_get_password (p);
380 g_free (p);
381 if (op == NULL)
382 ERRNOR (EPERM, 0);
383 bucket->password = g_strdup (op);
384 } else
385 op = g_strdup (bucket->password);
389 if (!anon || logfile)
390 pass = g_strdup (op);
391 else
392 pass = g_strconcat ("-", op, NULL);
393 wipe_password (op);
396 /* Proxy server accepts: username@host-we-want-to-connect*/
397 if (qproxy (bucket)){
398 #if defined(HSC_PROXY)
399 char *p, *host;
400 int port;
401 p = my_get_host_and_username (ftpfs_proxy_host, &host, &proxyname,
402 &port, &proxypass);
403 if (p)
404 g_free (p);
406 g_free (host);
407 if (proxypass)
408 wipe_password (proxypass);
409 p = g_strconcat (" Proxy: Password required for ", proxyname, " ",
410 NULL);
411 proxypass = vfs_get_password (p);
412 g_free (p);
413 if (proxypass == NULL) {
414 wipe_password (pass);
415 g_free (proxyname);
416 ERRNOR (EPERM, 0);
418 name = g_strdup (quser (bucket));
419 #else
420 name = g_strconcat (quser (bucket), "@",
421 qhost (bucket)[0] == '!' ? qhost (bucket)+1 : qhost (bucket), NULL);
422 #endif
423 } else
424 name = g_strdup (quser (bucket));
426 if (get_reply (qsock(bucket), reply_string, sizeof (reply_string) - 1) == COMPLETE) {
427 g_strup (reply_string);
428 bucket->remote_is_amiga = strstr (reply_string, "AMIGA") != 0;
429 if (logfile) {
430 fprintf (logfile, "MC -- remote_is_amiga = %d\n", bucket->remote_is_amiga);
431 fflush (logfile);
433 #if defined(HSC_PROXY)
434 if (qproxy (bucket)){
435 print_vfs_message ("ftpfs: sending proxy login name");
436 if (command (bucket, 1, "USER %s", proxyname) != CONTINUE)
437 goto proxyfail;
439 print_vfs_message ("ftpfs: sending proxy user password");
440 if (command (bucket, 1, "PASS %s", proxypass) != COMPLETE)
441 goto proxyfail;
443 print_vfs_message ("ftpfs: proxy authentication succeeded");
444 if (command (bucket, 1, "SITE %s", qhost (bucket)+1) != COMPLETE)
445 goto proxyfail;
447 print_vfs_message ("ftpfs: connected to %s", qhost (bucket)+1);
448 if (0) {
449 proxyfail:
450 bucket->failed_on_login = 1;
451 /* my_errno = E; */
452 if (proxypass)
453 wipe_password (proxypass);
454 wipe_password (pass);
455 g_free (proxyname);
456 g_free (name);
457 ERRNOR (EPERM, 0);
459 if (proxypass)
460 wipe_password (proxypass);
461 g_free (proxyname);
463 #endif
464 print_vfs_message ("ftpfs: sending login name");
465 code = command (bucket, WAIT_REPLY, "USER %s", name);
467 switch (code){
468 case CONTINUE:
469 print_vfs_message ("ftpfs: sending user password");
470 if (command (bucket, WAIT_REPLY, "PASS %s", pass) != COMPLETE)
471 break;
473 case COMPLETE:
474 print_vfs_message ("ftpfs: logged in");
475 wipe_password (pass);
476 g_free (name);
477 return 1;
479 default:
480 bucket->failed_on_login = 1;
481 /* my_errno = E; */
482 if (bucket->password)
483 wipe_password (bucket->password);
484 bucket->password = 0;
486 goto login_fail;
489 print_vfs_message ("ftpfs: Login incorrect for user %s ", quser (bucket));
490 login_fail:
491 wipe_password (pass);
492 g_free (name);
493 ERRNOR (EPERM, 0);
496 #ifdef HAVE_SETSOCKOPT
497 static void
498 setup_source_route (int socket, int dest)
500 char buffer [20];
501 char *ptr = buffer;
503 if (!source_route)
504 return;
505 bzero (buffer, sizeof (buffer));
506 *ptr++ = IPOPT_LSRR;
507 *ptr++ = 3 + 8;
508 *ptr++ = 4; /* pointer */
510 /* First hop */
511 bcopy ((char *) &source_route, ptr, sizeof (int));
512 ptr += 4;
514 /* Second hop (ie, final destination) */
515 bcopy ((char *) &dest, ptr, sizeof (int));
516 ptr += 4;
517 while ((ptr - buffer) & 3)
518 ptr++;
519 if (setsockopt (socket, IPPROTO_IP, IP_OPTIONS,
520 buffer, ptr - buffer) < 0)
521 message_2s (1, MSG_ERROR, _(" Could not set source routing (%s)"), unix_error_string (errno));
523 #else
524 #define setup_source_route(x,y)
525 #endif
527 static struct no_proxy_entry {
528 char *domain;
529 void *next;
530 } *no_proxy;
532 static void
533 load_no_proxy_list ()
535 /* FixMe: shouldn't be hardcoded!!! */
536 char s[BUF_LARGE]; /* provide for BUF_LARGE characters */
537 struct no_proxy_entry *np, *current = 0;
538 FILE *npf;
539 int c;
540 char *p, *mc_file;
541 static int loaded;
543 if (loaded)
544 return;
546 mc_file = concat_dir_and_file (mc_home, "mc.no_proxy");
547 if (exist_file (mc_file) &&
548 (npf = fopen (mc_file, "r"))) {
549 while (fgets (s, sizeof(s), npf) || !(feof (npf) || ferror (npf))) {
550 if (!(p = strchr (s, '\n'))) { /* skip bogus entries */
551 while ((c = fgetc (npf)) != EOF && c != '\n')
553 continue;
556 if (p == s)
557 continue;
559 *p = '\0';
561 np = g_new (struct no_proxy_entry, 1);
562 np->domain = g_strdup (s);
563 np->next = NULL;
564 if (no_proxy)
565 current->next = np;
566 else
567 no_proxy = np;
568 current = np;
571 fclose (npf);
572 loaded = 1;
574 g_free (mc_file);
577 static int
578 ftpfs_check_proxy (char *host)
580 struct no_proxy_entry *npe;
582 if (!ftpfs_proxy_host || !*ftpfs_proxy_host || !host || !*host)
583 return 0; /* sanity check */
585 if (*host == '!')
586 return 1;
588 if (!ftpfs_always_use_proxy)
589 return 0;
591 if (!strchr (host, '.'))
592 return 0;
594 load_no_proxy_list ();
595 for (npe = no_proxy; npe; npe=npe->next) {
596 char *domain = npe->domain;
598 if (domain[0] == '.') {
599 int ld = strlen (domain);
600 int lh = strlen (host);
602 while (ld && lh && host[lh - 1] == domain[ld - 1]) {
603 ld--;
604 lh--;
607 if (!ld)
608 return 0;
609 } else
610 if (!g_strcasecmp (host, domain))
611 return 0;
614 return 1;
617 static void
618 ftpfs_get_proxy_host_and_port (char *proxy, char **host, int *port)
620 char *user, *pass, *dir;
622 #if defined(HSC_PROXY)
623 #define PORT 9875
624 #else
625 #define PORT 21
626 #endif
627 dir = vfs_split_url (proxy, host, &user, port, &pass, PORT, URL_DEFAULTANON);
629 g_free (user);
630 if (pass)
631 wipe_password (pass);
632 if (dir)
633 g_free (dir);
636 static int
637 ftpfs_open_socket (struct connection *bucket)
639 struct sockaddr_in server_address;
640 struct hostent *hp;
641 int my_socket;
642 char *host;
643 int port;
644 int free_host = 0;
646 /* Use a proxy host? */
647 host = qhost (bucket);
649 if (!host || !*host){
650 print_vfs_message ("ftpfs: Invalid host name.");
651 my_errno = EINVAL;
652 return -1;
655 /* Hosts to connect to that start with a ! should use proxy */
656 if (qproxy (bucket)){
657 ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &port);
658 free_host = 1;
661 /* Get host address */
662 bzero ((char *) &server_address, sizeof (server_address));
663 server_address.sin_family = AF_INET;
664 server_address.sin_addr.s_addr = inet_addr (host);
665 if (server_address.sin_addr.s_addr != -1)
666 server_address.sin_family = AF_INET;
667 else {
668 hp = gethostbyname (host);
669 if (hp == NULL){
670 print_vfs_message ("ftpfs: Invalid host address.");
671 my_errno = EINVAL;
672 if (free_host)
673 g_free (host);
674 return -1;
676 server_address.sin_family = hp->h_addrtype;
678 /* We copy only 4 bytes, we can not trust hp->h_length, as it comes from the DNS */
679 bcopy ((char *) hp->h_addr, (char *) &server_address.sin_addr, 4);
682 #define THE_PORT qproxy(bucket) ? port : qport (bucket)
684 server_address.sin_port = htons (THE_PORT);
686 /* Connect */
687 if ((my_socket = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
688 my_errno = errno;
689 if (free_host)
690 g_free (host);
691 return -1;
693 setup_source_route (my_socket, server_address.sin_addr.s_addr);
695 print_vfs_message ("ftpfs: making connection to %s", host);
696 if (free_host)
697 g_free (host);
699 enable_interrupt_key (); /* clear the interrupt flag */
701 if (connect (my_socket, (struct sockaddr *) &server_address,
702 sizeof (server_address)) < 0){
703 my_errno = errno;
704 if (errno == EINTR && got_interrupt ())
705 print_vfs_message ("ftpfs: connection interrupted by user");
706 else
707 print_vfs_message ("ftpfs: connection to server failed: %s",
708 unix_error_string(errno));
709 disable_interrupt_key();
710 close (my_socket);
711 return -1;
713 disable_interrupt_key();
714 return my_socket;
717 static struct connection *
718 open_command_connection (char *host, char *user, int port, char *netrcpass)
720 struct connection *bucket;
721 int retry_seconds, count_down;
723 bucket = g_new (struct connection, 1);
725 if (bucket == NULL)
726 ERRNOR (ENOMEM, NULL);
727 #ifdef HAVE_MAD
729 extern void *watch_free_pointer;
731 if (!watch_free_pointer)
732 watch_free_pointer = host;
734 #endif
735 qhost (bucket) = g_strdup (host);
736 quser (bucket) = g_strdup (user);
737 qcdir (bucket) = NULL;
738 qport (bucket) = port;
739 qlock (bucket) = 0;
740 qhome (bucket) = NULL;
741 qproxy (bucket)= 0;
742 qupdir (bucket)= 0;
743 qdcache (bucket)=0;
744 bucket->__inode_counter = 0;
745 bucket->lock = 0;
746 bucket->use_proxy = ftpfs_check_proxy (host);
747 bucket->password = 0;
748 bucket->use_passive_connection = ftpfs_use_passive_connections | source_route;
749 bucket->use_source_route = source_route;
750 bucket->strict_rfc959_list_cmd = !ftpfs_use_unix_list_options;
751 bucket->isbinary = TYPE_UNKNOWN;
752 bucket->remote_is_amiga = 0;
754 /* We do not want to use the passive if we are using proxies */
755 if (bucket->use_proxy)
756 bucket->use_passive_connection = 0;
758 if ((qdcache (bucket) = linklist_init ()) == NULL) {
759 my_errno = ENOMEM;
760 g_free (qhost (bucket));
761 g_free (quser (bucket));
762 g_free (bucket);
763 return NULL;
766 retry_seconds = 0;
767 do {
768 bucket->failed_on_login = 0;
770 qsock (bucket) = ftpfs_open_socket (bucket);
771 if (qsock (bucket) == -1) {
772 free_bucket (bucket);
773 return NULL;
776 if (login_server (bucket, netrcpass)) {
777 /* Logged in, no need to retry the connection */
778 break;
779 } else {
780 if (bucket->failed_on_login){
781 /* Close only the socket descriptor */
782 close (qsock (bucket));
783 } else {
784 connection_destructor (bucket);
785 return NULL;
787 if (ftpfs_retry_seconds){
788 retry_seconds = ftpfs_retry_seconds;
789 enable_interrupt_key ();
790 for (count_down = retry_seconds; count_down; count_down--){
791 print_vfs_message ("Waiting to retry... %d (Control-C to cancel)", count_down);
792 sleep (1);
793 if (got_interrupt ()){
794 /* my_errno = E; */
795 disable_interrupt_key ();
796 free_bucket (bucket);
797 return NULL;
800 disable_interrupt_key ();
803 } while (retry_seconds);
805 qhome (bucket) = ftpfs_get_current_directory (bucket);
806 if (!qhome (bucket))
807 qhome (bucket) = g_strdup (PATH_SEP_STR);
808 qupdir (bucket) = g_strdup (PATH_SEP_STR); /* FIXME: I changed behavior to ignore last_current_dir */
809 return bucket;
812 static int
813 is_connection_closed (struct connection *bucket)
815 fd_set rset;
816 struct timeval t;
818 if (got_sigpipe){
819 return 1;
821 t.tv_sec = 0;
822 t.tv_usec = 0;
823 FD_ZERO (&rset);
824 FD_SET (qsock (bucket), &rset);
825 while (1) {
826 if (select (qsock (bucket) + 1, &rset, NULL, NULL, &t) < 0)
827 if (errno != EINTR)
828 return 1;
829 return 0;
830 #if 0
831 if (FD_ISSET (qsock(bucket), &rset)) {
832 n = read (qsock(bucket), &read_ahead, sizeof (read_ahead));
833 if (n <= 0)
834 return 1;
835 } else
836 #endif
840 /* This routine keeps track of open connections */
841 /* Returns a connected socket to host */
842 static struct connection *
843 open_link (char *host, char *user, int port, char *netrcpass)
845 int sock;
846 struct connection *bucket;
847 struct linklist *lptr;
849 for (lptr = connections_list->next;
850 lptr != connections_list; lptr = lptr->next) {
851 bucket = lptr->data;
852 if ((strcmp (host, qhost (bucket)) == 0) &&
853 (strcmp (user, quser (bucket)) == 0) &&
854 (port == qport (bucket))) {
856 /* check the connection is closed or not, just hack */
857 if (is_connection_closed (bucket)) {
858 flush_all_directory (bucket);
859 sock = ftpfs_open_socket (bucket);
860 if (sock != -1) {
861 close (qsock (bucket));
862 qsock (bucket) = sock;
863 if (login_server (bucket, netrcpass))
864 return bucket;
867 /* connection refused */
868 lptr->prev->next = lptr->next;
869 lptr->next->prev = lptr->prev;
870 connection_destructor (bucket);
871 return NULL;
873 return bucket;
876 bucket = open_command_connection (host, user, port, netrcpass);
877 if (bucket == NULL)
878 return NULL;
879 if (!linklist_insert (connections_list, bucket)) {
880 my_errno = ENOMEM;
881 connection_destructor (bucket);
882 return NULL;
884 return bucket;
887 /* The returned directory should always contain a trailing slash */
888 static char *
889 ftpfs_get_current_directory (struct connection *bucket)
891 char buf[4096], *bufp, *bufq;
893 if (command (bucket, NONE, "PWD") == COMPLETE &&
894 get_reply(qsock(bucket), buf, sizeof(buf)) == COMPLETE) {
895 bufp = NULL;
896 for (bufq = buf; *bufq; bufq++)
897 if (*bufq == '"') {
898 if (!bufp) {
899 bufp = bufq + 1;
900 } else {
901 *bufq = 0;
902 if (*bufp) {
903 if (*(bufq - 1) != '/') {
904 *bufq++ = '/';
905 *bufq = 0;
907 if (*bufp == '/')
908 return g_strdup (bufp);
909 else {
910 /* If the remote server is an Amiga a leading slash
911 might be missing. MC needs it because it is used
912 as seperator between hostname and path internally. */
913 return g_strconcat( "/", bufp, 0);
915 } else {
916 my_errno = EIO;
917 return NULL;
922 my_errno = EIO;
923 return NULL;
927 /* Setup Passive ftp connection, we use it for source routed connections */
928 static int
929 setup_passive (int my_socket, struct connection *bucket, struct sockaddr_in *sa)
931 int xa, xb, xc, xd, xe, xf;
932 char n [6];
933 char *c = reply_str;
935 if (command (bucket, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
936 return 0;
938 /* Parse remote parameters */
939 for (c = reply_str + 4; (*c) && (!isdigit (*c)); c++)
941 if (!*c)
942 return 0;
943 if (!isdigit (*c))
944 return 0;
945 if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
946 return 0;
947 n [0] = (unsigned char) xa;
948 n [1] = (unsigned char) xb;
949 n [2] = (unsigned char) xc;
950 n [3] = (unsigned char) xd;
951 n [4] = (unsigned char) xe;
952 n [5] = (unsigned char) xf;
954 bcopy ((void *)n, &(sa->sin_addr.s_addr), 4);
955 bcopy ((void *)&n[4], &(sa->sin_port), 2);
956 setup_source_route (my_socket, sa->sin_addr.s_addr);
957 if (connect (my_socket, (struct sockaddr *) sa, sizeof (struct sockaddr_in)) < 0)
958 return 0;
959 return 1;
962 static int
963 initconn (struct connection *bucket)
965 struct sockaddr_in data_addr;
966 int data, len = sizeof(data_addr);
967 struct protoent *pe;
969 getsockname(qsock(bucket), (struct sockaddr *) &data_addr, &len);
970 data_addr.sin_port = 0;
972 pe = getprotobyname("tcp");
973 if (pe == NULL)
974 ERRNOR (EIO, -1);
975 data = socket (AF_INET, SOCK_STREAM, pe->p_proto);
976 if (data < 0)
977 ERRNOR (EIO, -1);
979 #ifdef ORIGINAL_CONNECT_CODE
980 if (bucket->use_source_route){
981 int c;
983 if ((c = setup_passive (data, bucket, &data_addr)))
984 return data;
985 print_vfs_message("ftpfs: could not setup passive mode for source routing");
986 bucket->use_source_route = 0;
988 #else
989 if (bucket->use_passive_connection){
990 if ((bucket->use_passive_connection = setup_passive (data, bucket, &data_addr)))
991 return data;
993 bucket->use_source_route = 0;
994 bucket->use_passive_connection = 0;
995 print_vfs_message ("ftpfs: could not setup passive mode");
997 #endif
998 /* If passive setup fails, fallback to active connections */
999 /* Active FTP connection */
1000 if (bind (data, (struct sockaddr *)&data_addr, len) < 0)
1001 goto error_return;
1002 getsockname(data, (struct sockaddr *) &data_addr, &len);
1003 if (listen (data, 1) < 0)
1004 goto error_return;
1006 unsigned char *a = (unsigned char *)&data_addr.sin_addr;
1007 unsigned char *p = (unsigned char *)&data_addr.sin_port;
1009 if (command (bucket, WAIT_REPLY, "PORT %d,%d,%d,%d,%d,%d", a[0], a[1],
1010 a[2], a[3], p[0], p[1]) != COMPLETE)
1011 goto error_return;
1013 return data;
1014 error_return:
1015 close(data);
1016 my_errno = EIO;
1017 return -1;
1020 static int
1021 open_data_connection (struct connection *bucket, char *cmd, char *remote,
1022 int isbinary, int reget)
1024 struct sockaddr_in from;
1025 int s, j, data, fromlen = sizeof(from);
1027 if ((s = initconn (bucket)) == -1)
1028 return -1;
1029 if (changetype (bucket, isbinary) == -1)
1030 return -1;
1031 if (reget > 0){
1032 j = command (bucket, WAIT_REPLY, "REST %d", reget);
1033 if (j != CONTINUE)
1034 return -1;
1036 if (remote)
1037 j = command (bucket, WAIT_REPLY, "%s %s", cmd,
1038 translate_path (bucket, remote));
1039 else
1040 j = command (bucket, WAIT_REPLY, "%s", cmd);
1041 if (j != PRELIM)
1042 ERRNOR (EPERM, -1);
1043 enable_interrupt_key();
1044 if (bucket->use_passive_connection)
1045 data = s;
1046 else {
1047 data = accept (s, (struct sockaddr *)&from, &fromlen);
1048 if (data < 0) {
1049 my_errno = errno;
1050 close(s);
1051 return -1;
1053 close(s);
1055 disable_interrupt_key();
1056 return data;
1059 static void
1060 my_abort (struct connection *bucket, int dsock)
1062 static unsigned char ipbuf[3] = { IAC, IP, IAC };
1063 fd_set mask;
1064 char buf[1024];
1066 print_vfs_message("ftpfs: aborting transfer.");
1067 if (send(qsock(bucket), ipbuf, sizeof(ipbuf), MSG_OOB) != sizeof(ipbuf)) {
1068 print_vfs_message("ftpfs: abort error: %s", unix_error_string(errno));
1069 return;
1072 if (command(bucket, NONE, "%cABOR", DM) != COMPLETE){
1073 print_vfs_message ("ftpfs: abort failed");
1074 return;
1076 if (dsock != -1) {
1077 FD_ZERO(&mask);
1078 FD_SET(dsock, &mask);
1079 if (select(dsock + 1, &mask, NULL, NULL, NULL) > 0)
1080 while (read(dsock, buf, sizeof(buf)) > 0);
1082 if ((get_reply(qsock(bucket), NULL, 0) == TRANSIENT) && (code == 426))
1083 get_reply(qsock(bucket), NULL, 0);
1086 static void
1087 resolve_symlink_without_ls_options(struct connection *bucket, struct dir *dir)
1089 struct linklist *flist;
1090 struct direntry *fe, *fel;
1091 char tmp[MC_MAXPATHLEN];
1093 dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1094 for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next) {
1095 /* flist->data->l_stat is alread initialized with 0 */
1096 fel = flist->data;
1097 if (S_ISLNK(fel->s.st_mode)) {
1098 if (fel->linkname[0] == '/') {
1099 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1100 continue;
1101 strcpy (tmp, fel->linkname);
1102 } else {
1103 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1104 continue;
1105 strcpy (tmp, dir->remote_path);
1106 if (tmp[1] != '\0')
1107 strcat (tmp, "/");
1108 strcat (tmp + 1, fel->linkname);
1110 for ( ;; ) {
1111 canonicalize_pathname (tmp);
1112 fe = _get_file_entry(bucket, tmp, 0, 0);
1113 if (fe) {
1114 if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0) {
1115 /* Symlink points to link which isn't resolved, yet. */
1116 if (fe->linkname[0] == '/') {
1117 if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1118 break;
1119 strcpy (tmp, fe->linkname);
1120 } else {
1121 /* at this point tmp looks always like this
1122 /directory/filename, i.e. no need to check
1123 strrchr's return value */
1124 *(strrchr (tmp, '/') + 1) = '\0'; /* dirname */
1125 if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1126 break;
1127 strcat (tmp, fe->linkname);
1129 continue;
1130 } else {
1131 fel->l_stat = g_new (struct stat, 1);
1132 if ( S_ISLNK (fe->s.st_mode))
1133 *fel->l_stat = *fe->l_stat;
1134 else
1135 *fel->l_stat = fe->s;
1136 (*fel->l_stat).st_ino = bucket->__inode_counter++;
1139 break;
1143 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1146 static void
1147 resolve_symlink_with_ls_options(struct connection *bucket, struct dir *dir)
1149 char buffer[2048] = "", *filename;
1150 int sock;
1151 FILE *fp;
1152 struct stat s;
1153 struct linklist *flist;
1154 struct direntry *fe;
1156 dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1157 if (strchr (dir->remote_path, ' ')) {
1158 if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE) {
1159 print_vfs_message("ftpfs: CWD failed.");
1160 return;
1162 sock = open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1164 else
1165 sock = open_data_connection (bucket, "LIST -lLa",
1166 dir->remote_path, TYPE_ASCII, 0);
1168 if (sock == -1) {
1169 print_vfs_message("ftpfs: couldn't resolve symlink");
1170 return;
1173 fp = fdopen(sock, "r");
1174 if (fp == NULL) {
1175 close(sock);
1176 print_vfs_message("ftpfs: couldn't resolve symlink");
1177 return;
1179 enable_interrupt_key();
1180 flist = dir->file_list->next;
1181 while (1) {
1182 do {
1183 if (flist == dir->file_list)
1184 goto done;
1185 fe = flist->data;
1186 flist = flist->next;
1187 } while (!S_ISLNK(fe->s.st_mode));
1188 while (1) {
1189 if (fgets (buffer, sizeof (buffer), fp) == NULL)
1190 goto done;
1191 if (logfile){
1192 fputs (buffer, logfile);
1193 fflush (logfile);
1195 if (vfs_parse_ls_lga (buffer, &s, &filename, NULL)) {
1196 int r = strcmp(fe->name, filename);
1197 g_free(filename);
1198 if (r == 0) {
1199 fe->l_stat = g_new (struct stat, 1);
1200 if (fe->l_stat == NULL)
1201 goto done;
1202 *fe->l_stat = s;
1203 (*fe->l_stat).st_ino = bucket->__inode_counter++;
1204 break;
1206 if (r < 0)
1207 break;
1211 done:
1212 while (fgets(buffer, sizeof(buffer), fp) != NULL);
1213 disable_interrupt_key();
1214 fclose(fp);
1215 get_reply(qsock(bucket), NULL, 0);
1218 static void
1219 resolve_symlink(struct connection *bucket, struct dir *dir)
1221 print_vfs_message("Resolving symlink...");
1223 if (bucket->strict_rfc959_list_cmd)
1224 resolve_symlink_without_ls_options(bucket, dir);
1225 else
1226 resolve_symlink_with_ls_options(bucket, dir);
1230 #define X "ftp"
1231 #define X_myname "/#ftp:"
1232 #define vfs_X_ops vfs_ftpfs_ops
1233 #define X_fill_names ftpfs_fill_names
1234 #define X_hint_reread ftpfs_hint_reread
1235 #define X_flushdir ftpfs_flushdir
1236 #define X_done ftpfs_done
1237 #include "shared_ftp_fish.c"
1239 static char*
1240 get_path (struct connection **bucket, char *path)
1242 return s_get_path (bucket, path, "/#ftp:");
1245 /* Inserts an entry for "." (and "..") into the linked list. Ignore any
1246 errors because "." isn't important (as fas as you don't try to save a
1247 file in the root dir of the ftp server).
1248 Actually the dot is needed when stating the root directory, e.g.
1249 mc_stat ("/ftp#localhost", &buf). Down the call tree _get_file_entry
1250 gets called with filename = "/" which will be transformed into "."
1251 before searching for a fileentry. Whithout "." in the linked list
1252 this search fails. -- Norbert. */
1253 static void
1254 insert_dots (struct linklist *file_list, struct connection *bucket)
1256 struct direntry *fe;
1257 int i;
1258 char buffer[][58] = {
1259 "drwxrwxrwx 1 0 0 1024 Jan 1 1970 .",
1260 "drwxrwxrwx 1 0 0 1024 Jan 1 1970 .."
1263 for (i = 0; i < 2; i++ ) {
1264 fe = malloc(sizeof(struct direntry));
1265 if (fe == NULL)
1266 return;
1267 if (vfs_parse_ls_lga (buffer[i], &fe->s, &fe->name, &fe->linkname)) {
1268 fe->freshly_created = 0;
1269 fe->count = 1;
1270 fe->local_filename = fe->remote_filename = NULL;
1271 fe->l_stat = NULL;
1272 fe->bucket = bucket;
1273 (fe->s).st_ino = bucket->__inode_counter++;
1275 if (!linklist_insert(file_list, fe))
1276 free(fe);
1277 } else
1278 free (fe);
1282 static struct dir *
1283 retrieve_dir(struct connection *bucket, char *remote_path, int resolve_symlinks)
1285 #ifdef OLD_READ
1286 FILE *fp;
1287 #endif
1288 int sock, has_symlinks;
1289 struct linklist *file_list, *p;
1290 struct direntry *fe;
1291 char buffer[8192];
1292 struct dir *dcache;
1293 int got_intr = 0;
1294 int dot_found = 0;
1295 int dot_dot_found = 0;
1296 int has_spaces = (strchr (remote_path, ' ') != NULL);
1298 canonicalize_pathname (remote_path);
1299 for (p = qdcache(bucket)->next;p != qdcache(bucket);
1300 p = p->next) {
1301 dcache = p->data;
1302 if (strcmp(dcache->remote_path, remote_path) == 0) {
1303 struct timeval tim;
1305 gettimeofday(&tim, NULL);
1306 if (((tim.tv_sec < dcache->timestamp.tv_sec) && !force_expiration)
1307 || dcache->symlink_status == FTPFS_RESOLVING_SYMLINKS) {
1308 if (resolve_symlinks && dcache->symlink_status == FTPFS_UNRESOLVED_SYMLINKS)
1309 resolve_symlink(bucket, dcache);
1310 return dcache;
1311 } else {
1312 force_expiration = 0;
1313 p->next->prev = p->prev;
1314 p->prev->next = p->next;
1315 dir_destructor(dcache);
1316 g_free (p);
1317 break;
1322 has_symlinks = 0;
1323 if (bucket->strict_rfc959_list_cmd)
1324 print_vfs_message("ftpfs: Reading FTP directory %s... (don't use UNIX ls options)", remote_path);
1325 else
1326 print_vfs_message("ftpfs: Reading FTP directory %s...", remote_path);
1327 if (has_spaces || bucket->strict_rfc959_list_cmd || ftpfs_first_cd_then_ls) {
1328 char *p;
1330 p = translate_path (bucket, remote_path);
1331 if (ftpfs_chdir_internal (bucket, p) != COMPLETE) {
1332 my_errno = ENOENT;
1333 print_vfs_message("ftpfs: CWD failed.");
1334 return NULL;
1338 file_list = linklist_init();
1339 if (file_list == NULL)
1340 ERRNOR (ENOMEM, NULL);
1341 dcache = g_new (struct dir, 1);
1342 if (dcache == NULL) {
1343 my_errno = ENOMEM;
1344 linklist_destroy(file_list, NULL);
1345 print_vfs_message("ftpfs: FAIL");
1346 return NULL;
1348 gettimeofday(&dcache->timestamp, NULL);
1349 dcache->timestamp.tv_sec += ftpfs_directory_timeout;
1350 dcache->file_list = file_list;
1351 dcache->remote_path = g_strdup(remote_path);
1352 dcache->count = 1;
1353 dcache->symlink_status = FTPFS_NO_SYMLINKS;
1355 if (bucket->strict_rfc959_list_cmd == 1)
1356 sock = open_data_connection (bucket, "LIST", 0, TYPE_ASCII, 0);
1357 else if (has_spaces || ftpfs_first_cd_then_ls)
1358 sock = open_data_connection (bucket, "LIST -la", ".", TYPE_ASCII, 0);
1359 else {
1360 /* Trailing "/." is necessary if remote_path is a symlink
1361 but don't generate "//." */
1362 char *path = g_strconcat (remote_path,
1363 remote_path[1] == '\0' ? "" : PATH_SEP_STR,
1364 ".", (char *) 0);
1366 sock = open_data_connection (bucket, "LIST -la", path, TYPE_ASCII, 0);
1367 g_free (path);
1370 if (sock == -1)
1371 goto fallback;
1373 #ifdef OLD_READ
1374 #define close_this_sock(x,y) fclose (x)
1375 fp = fdopen(sock, "r");
1376 if (fp == NULL) {
1377 my_errno = errno;
1378 close(sock);
1379 goto error_2;
1381 #else
1382 #define close_this_sock(x,y) close (y)
1383 #endif
1385 /* Clear the interrupt flag */
1386 enable_interrupt_key ();
1388 errno = 0;
1389 #ifdef OLD_READ
1390 while (fgets (buffer, sizeof (buffer), fp) != NULL) {
1391 if (got_intr = got_interrupt ())
1392 break;
1393 #else
1394 while ((got_intr = get_line_interruptible (buffer, sizeof (buffer), sock)) != EINTR){
1395 #endif
1396 int eof = got_intr == 0;
1398 if (logfile){
1399 fputs (buffer, logfile);
1400 fputs ("\n", logfile);
1401 fflush (logfile);
1403 if (buffer [0] == 0 && eof)
1404 break;
1405 fe = g_new (struct direntry, 1);
1406 fe->freshly_created = 0;
1407 fe->local_filename = NULL;
1408 if (fe == NULL) {
1409 my_errno = ENOMEM;
1410 goto error_1;
1412 if (vfs_parse_ls_lga (buffer, &fe->s, &fe->name, &fe->linkname)) {
1413 if (strcmp (fe->name, ".") == 0)
1414 dot_found = 1;
1415 fe->count = 1;
1416 fe->local_filename = fe->remote_filename = NULL;
1417 fe->l_stat = NULL;
1418 fe->bucket = bucket;
1419 (fe->s).st_ino = bucket->__inode_counter++;
1420 if (S_ISLNK(fe->s.st_mode))
1421 has_symlinks = 1;
1423 if (!linklist_insert(file_list, fe)) {
1424 g_free(fe);
1425 my_errno = ENOMEM;
1426 goto error_1;
1429 else
1430 g_free(fe);
1431 if (eof)
1432 break;
1434 if (got_intr){
1435 disable_interrupt_key();
1436 print_vfs_message("ftpfs: reading FTP directory interrupt by user");
1437 #ifdef OLD_READ
1438 my_abort(bucket, fileno(fp));
1439 #else
1440 my_abort(bucket, sock);
1441 #endif
1442 close_this_sock(fp, sock);
1443 my_errno = EINTR;
1444 goto error_3;
1446 close_this_sock(fp, sock);
1447 disable_interrupt_key();
1448 if ((get_reply (qsock (bucket), NULL, 0) != COMPLETE) ||
1449 (file_list->next == file_list))
1450 goto fallback;
1452 if (!dot_found)
1453 insert_dots (file_list, bucket);
1455 if (!linklist_insert(qdcache(bucket), dcache)) {
1456 my_errno = ENOMEM;
1457 goto error_3;
1459 if (has_symlinks) {
1460 if (resolve_symlinks)
1461 resolve_symlink(bucket, dcache);
1462 else
1463 dcache->symlink_status = FTPFS_UNRESOLVED_SYMLINKS;
1465 print_vfs_message("ftpfs: got listing");
1466 return dcache;
1467 error_1:
1468 disable_interrupt_key();
1469 close_this_sock(fp, sock);
1470 #ifdef OLD_READ
1471 error_2:
1472 #endif
1473 get_reply(qsock(bucket), NULL, 0);
1474 error_3:
1475 g_free(dcache->remote_path);
1476 g_free(dcache);
1477 linklist_destroy(file_list, direntry_destructor);
1478 print_vfs_message("ftpfs: failed");
1479 return NULL;
1481 fallback:
1482 if (bucket->__inode_counter == 0 && (!bucket->strict_rfc959_list_cmd)) {
1483 /* It's our first attempt to get a directory listing from this
1484 server (UNIX style LIST command) */
1485 bucket->strict_rfc959_list_cmd = 1;
1486 g_free(dcache->remote_path);
1487 g_free(dcache);
1488 linklist_destroy(file_list, direntry_destructor);
1489 return retrieve_dir (bucket, remote_path, resolve_symlinks);
1491 my_errno = EACCES;
1492 g_free(dcache->remote_path);
1493 g_free(dcache);
1494 linklist_destroy(file_list, direntry_destructor);
1495 print_vfs_message("ftpfs: failed; nowhere to fallback to");
1496 return NULL;
1499 static int
1500 store_file(struct direntry *fe)
1502 int local_handle, sock, n, total;
1503 #ifdef HAVE_STRUCT_LINGER
1504 struct linger li;
1505 #else
1506 int flag_one = 1;
1507 #endif
1508 char buffer[8192];
1509 struct stat s;
1511 local_handle = open(fe->local_filename, O_RDONLY);
1512 unlink (fe->local_filename);
1513 if (local_handle == -1)
1514 ERRNOR (EIO, 0);
1515 fstat(local_handle, &s);
1516 sock = open_data_connection(fe->bucket, "STOR", fe->remote_filename, TYPE_BINARY, 0);
1517 if (sock < 0) {
1518 close(local_handle);
1519 return 0;
1521 #ifdef HAVE_STRUCT_LINGER
1522 li.l_onoff = 1;
1523 li.l_linger = 120;
1524 setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof(li));
1525 #else
1526 setsockopt(sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1527 #endif
1528 total = 0;
1530 enable_interrupt_key();
1531 while (1) {
1532 while ((n = read(local_handle, buffer, sizeof(buffer))) < 0) {
1533 if (errno == EINTR) {
1534 if (got_interrupt()) {
1535 my_errno = EINTR;
1536 goto error_return;
1538 else
1539 continue;
1541 my_errno = errno;
1542 goto error_return;
1544 if (n == 0)
1545 break;
1546 while (write(sock, buffer, n) < 0) {
1547 if (errno == EINTR) {
1548 if (got_interrupt()) {
1549 my_errno = EINTR;
1550 goto error_return;
1552 else
1553 continue;
1555 my_errno = errno;
1556 goto error_return;
1558 total += n;
1559 print_vfs_message("ftpfs: storing file %d (%d)",
1560 total, s.st_size);
1562 disable_interrupt_key();
1563 close(sock);
1564 close(local_handle);
1565 if (get_reply (qsock (fe->bucket), NULL, 0) != COMPLETE)
1566 ERRNOR (EIO, 0);
1567 return 1;
1568 error_return:
1569 disable_interrupt_key();
1570 close(sock);
1571 close(local_handle);
1572 get_reply(qsock(fe->bucket), NULL, 0);
1573 return 0;
1576 static int
1577 linear_start(struct direntry *fe, int offset)
1579 fe->local_stat.st_mtime = 0;
1580 fe->data_sock = open_data_connection(fe->bucket, "RETR", fe->remote_filename, TYPE_BINARY, offset);
1581 if (fe->data_sock == -1)
1582 ERRNOR (EACCES, 0);
1583 fe->linear_state = LS_LINEAR_OPEN;
1584 return 1;
1587 static void
1588 linear_abort (struct direntry *fe)
1590 my_abort(fe->bucket, fe->data_sock);
1591 fe->data_sock = -1;
1594 static int
1595 linear_read (struct direntry *fe, void *buf, int len)
1597 int n;
1598 while ((n = read (fe->data_sock, buf, len))<0) {
1599 if ((errno == EINTR) && !got_interrupt())
1600 continue;
1601 break;
1604 if (n<0)
1605 linear_abort(fe);
1607 if (!n) {
1608 if ((get_reply (qsock (fe->bucket), NULL, 0) != COMPLETE)) {
1609 my_errno = EIO;
1610 n=-1;
1612 close (fe->data_sock);
1613 fe->data_sock = -1;
1615 ERRNOR (errno, n);
1618 static void
1619 linear_close (struct direntry *fe)
1621 if (fe->data_sock != -1)
1622 linear_abort(fe);
1625 int ftpfs_ctl (void *data, int ctlop, int arg)
1627 struct filp *fp = data;
1628 switch (ctlop) {
1629 case MCCTL_IS_NOTREADY:
1631 int v;
1633 if (!fp->fe->linear_state)
1634 vfs_die ("You may not do this");
1635 if (fp->fe->linear_state == LS_LINEAR_CLOSED)
1636 return 0;
1638 v = select_on_two (fp->fe->data_sock, 0);
1639 if (((v < 0) && (errno == EINTR)) || v == 0)
1640 return 1;
1641 return 0;
1643 default:
1644 return 0;
1648 static int
1649 send_ftp_command(char *filename, char *cmd, int flags)
1651 char *remote_path, *p;
1652 struct connection *bucket;
1653 int r;
1654 int flush_directory_cache = (flags & OPT_FLUSH) && (normal_flush > 0);
1656 if (!(remote_path = get_path(&bucket, filename)))
1657 return -1;
1658 p = translate_path (bucket, remote_path);
1659 r = command (bucket, WAIT_REPLY, cmd, p);
1660 g_free(remote_path);
1661 vfs_add_noncurrent_stamps (&vfs_ftpfs_ops, (vfsid) bucket, NULL);
1662 if (flags & OPT_IGNORE_ERROR)
1663 r = COMPLETE;
1664 if (r != COMPLETE)
1665 ERRNOR (EPERM, -1);
1666 if (flush_directory_cache)
1667 flush_all_directory(bucket);
1668 return 0;
1671 /* This routine is called as the last step in load_setup */
1672 void
1673 ftpfs_init_passwd(void)
1675 struct passwd *passwd_info;
1676 char *p, hostname[MAXHOSTNAMELEN];
1677 struct hostent *hp;
1679 ftpfs_anonymous_passwd = load_anon_passwd ();
1680 if (ftpfs_anonymous_passwd)
1681 return;
1683 if ((passwd_info = getpwuid (geteuid ())) == NULL)
1684 p = "unknown";
1685 else
1686 p = passwd_info->pw_name;
1687 gethostname(hostname, sizeof(hostname));
1688 hp = gethostbyname(hostname);
1689 if (hp != NULL)
1690 ftpfs_anonymous_passwd = g_strconcat (p, "@", hp->h_name, NULL);
1691 else
1692 ftpfs_anonymous_passwd = g_strconcat (p, "@", hostname, NULL);
1693 endpwent ();
1697 ftpfs_init (vfs *me)
1699 connections_list = linklist_init();
1700 ftpfs_directory_timeout = FTPFS_DIRECTORY_TIMEOUT;
1701 return 1;
1704 static int ftpfs_chmod (vfs *me, char *path, int mode)
1706 char buf[BUF_SMALL];
1708 g_snprintf(buf, sizeof(buf), "SITE CHMOD %4.4o %%s", mode & 07777);
1709 return send_ftp_command(path, buf, OPT_IGNORE_ERROR | OPT_FLUSH);
1712 static int ftpfs_chown (vfs *me, char *path, int owner, int group)
1714 #if 0
1715 my_errno = EPERM;
1716 return -1;
1717 #else
1718 /* Everyone knows it is not possible to chown remotely, so why bother them.
1719 If someone's root, then copy/move will always try to chown it... */
1720 return 0;
1721 #endif
1724 static int ftpfs_unlink (vfs *me, char *path)
1726 return send_ftp_command(path, "DELE %s", OPT_FLUSH);
1729 /* Return true if path is the same directoy as the one we are on now */
1730 static int
1731 is_same_dir (char *path, struct connection *bucket)
1733 if (!qcdir (bucket))
1734 return 0;
1735 if (strcmp (path, qcdir (bucket)) == 0)
1736 return 1;
1737 return 0;
1740 static int
1741 ftpfs_chdir_internal (struct connection *bucket ,char *remote_path)
1743 int r;
1744 char *p;
1746 if (!bucket->cwd_defered && is_same_dir (remote_path, bucket))
1747 return COMPLETE;
1749 p = translate_path (bucket, remote_path);
1750 r = command (bucket, WAIT_REPLY, "CWD %s", p);
1752 if (r != COMPLETE) {
1753 my_errno = EIO;
1754 } else {
1755 if (qcdir(bucket))
1756 g_free(qcdir(bucket));
1757 qcdir(bucket) = g_strdup (remote_path);
1758 bucket->cwd_defered = 0;
1760 return r;
1763 static int ftpfs_rename (vfs *me, char *path1, char *path2)
1765 send_ftp_command(path1, "RNFR %s", OPT_FLUSH);
1766 return send_ftp_command(path2, "RNTO %s", OPT_FLUSH);
1769 static int ftpfs_mkdir (vfs *me, char *path, mode_t mode)
1771 return send_ftp_command(path, "MKD %s", OPT_FLUSH);
1774 static int ftpfs_rmdir (vfs *me, char *path)
1776 return send_ftp_command(path, "RMD %s", OPT_FLUSH);
1779 void ftpfs_set_debug (char *file)
1781 logfile = fopen (file, "w+");
1784 static void my_forget (char *file)
1786 struct linklist *l;
1787 char *host, *user, *pass, *rp;
1788 int port;
1790 #ifndef BROKEN_PATHS
1791 if (strncmp (file, "/#ftp:", 6))
1792 return; /* Normal: consider cd /bla/#ftp */
1793 #else
1794 if (!(file = strstr (file, "/#ftp:")))
1795 return;
1796 #endif
1798 file += 6;
1799 if (!(rp = my_get_host_and_username (file, &host, &user, &port, &pass))) {
1800 g_free (host);
1801 g_free (user);
1802 if (pass)
1803 wipe_password (pass);
1804 return;
1807 /* we do not care about the path actually */
1808 g_free (rp);
1810 for (l = connections_list->next; l != connections_list; l = l->next){
1811 struct connection *bucket = l->data;
1813 if ((strcmp (host, qhost (bucket)) == 0) &&
1814 (strcmp (user, quser (bucket)) == 0) &&
1815 (port == qport (bucket))){
1817 /* close socket: the child owns it now */
1818 close (bucket->sock);
1819 bucket->sock = -1;
1821 /* reopen the connection */
1822 bucket->sock = ftpfs_open_socket (bucket);
1823 if (bucket->sock != -1)
1824 login_server (bucket, pass);
1825 break;
1828 g_free (host);
1829 g_free (user);
1830 if (pass)
1831 wipe_password (pass);
1834 vfs vfs_ftpfs_ops = {
1835 NULL, /* This is place of next pointer */
1836 "File Tranfer Protocol (ftp)",
1837 F_NET, /* flags */
1838 "ftp:", /* prefix */
1839 NULL, /* data */
1840 0, /* errno */
1841 ftpfs_init,
1842 ftpfs_done,
1843 ftpfs_fill_names,
1844 NULL,
1846 s_open,
1847 s_close,
1848 s_read,
1849 s_write,
1851 s_opendir,
1852 s_readdir,
1853 s_closedir,
1854 s_telldir,
1855 s_seekdir,
1857 s_stat,
1858 s_lstat,
1859 s_fstat,
1861 ftpfs_chmod,
1862 ftpfs_chown, /* not really implemented but returns success */
1863 NULL,
1865 s_readlink,
1866 NULL,
1867 NULL,
1868 ftpfs_unlink,
1870 ftpfs_rename,
1871 s_chdir,
1872 s_errno,
1873 s_lseek,
1874 NULL,
1876 s_getid,
1877 s_nothingisopen,
1878 s_free,
1880 s_getlocalcopy,
1881 s_ungetlocalcopy,
1883 ftpfs_mkdir,
1884 ftpfs_rmdir,
1885 ftpfs_ctl,
1886 s_setctl
1888 MMAPNULL
1891 #ifdef USE_NETRC
1892 static char buffer[BUF_MEDIUM];
1893 static char *netrc, *netrcp;
1895 static int netrc_next (void)
1897 char *p;
1898 int i;
1899 static char *keywords [] = { "default", "machine",
1900 "login", "password", "passwd",
1901 "account", "macdef" };
1903 while (1) {
1904 netrcp = skip_separators (netrcp);
1905 if (*netrcp != '\n')
1906 break;
1907 netrcp++;
1909 if (!*netrcp)
1910 return 0;
1911 p = buffer;
1912 if (*netrcp == '"') {
1913 for (;*netrcp != '"' && *netrcp; netrcp++) {
1914 if (*netrcp == '\\')
1915 netrcp++;
1916 *p++ = *netrcp;
1918 } else {
1919 for (;*netrcp != '\n' && *netrcp != '\t' && *netrcp != ' ' &&
1920 *netrcp != ',' && *netrcp; netrcp++) {
1921 if (*netrcp == '\\')
1922 netrcp++;
1923 *p++ = *netrcp;
1926 *p = 0;
1927 if (!*buffer)
1928 return 0;
1929 for (i = 0; i < sizeof (keywords) / sizeof (keywords [0]); i++)
1930 if (!strcmp (keywords [i], buffer))
1931 break;
1932 return i + 1;
1935 int lookup_netrc (char *host, char **login, char **pass)
1937 char *netrcname, *tmp;
1938 char hostname[MAXHOSTNAMELEN], *domain;
1939 int keyword;
1940 struct stat mystat;
1941 static int be_angry = 1;
1942 static struct rupcache {
1943 struct rupcache *next;
1944 char *host;
1945 char *login;
1946 char *pass;
1947 } *rup_cache = NULL, *rupp;
1949 for (rupp = rup_cache; rupp != NULL; rupp = rupp->next)
1950 if (!strcmp (host, rupp->host)) {
1951 if (rupp->login != NULL)
1952 *login = g_strdup (rupp->login);
1953 if (rupp->pass != NULL)
1954 *pass = g_strdup (rupp->pass);
1955 return 0;
1957 netrcname = concat_dir_and_file (home_dir, ".netrc");
1958 netrcp = netrc = load_file (netrcname);
1959 if (netrc == NULL) {
1960 g_free (netrcname);
1961 return 0;
1963 if (gethostname (hostname, sizeof (hostname)) < 0)
1964 *hostname = 0;
1965 if (!(domain = strchr (hostname, '.')))
1966 domain = "";
1968 while ((keyword = netrc_next ())) {
1969 if (keyword == 2) {
1970 if (netrc_next () != 8)
1971 continue;
1972 if (g_strcasecmp (host, buffer) &&
1973 ((tmp = strchr (host, '.')) == NULL ||
1974 g_strcasecmp (tmp, domain) ||
1975 g_strncasecmp (host, buffer, tmp - host) ||
1976 buffer [tmp - host]))
1977 continue;
1978 } else if (keyword != 1)
1979 continue;
1980 while ((keyword = netrc_next ()) > 2) {
1981 switch (keyword) {
1982 case 3:
1983 if (netrc_next ())
1984 if (*login == NULL)
1985 *login = g_strdup (buffer);
1986 else if (strcmp (*login, buffer))
1987 keyword = 20;
1988 break;
1989 case 4:
1990 case 5:
1991 if (strcmp (*login, "anonymous") && strcmp (*login, "ftp") &&
1992 stat (netrcname, &mystat) >= 0 &&
1993 (mystat.st_mode & 077)) {
1994 if (be_angry) {
1995 message_1s (1, MSG_ERROR, _("~/.netrc file has not correct mode.\n"
1996 "Remove password or correct mode."));
1997 be_angry = 0;
1999 g_free (netrc);
2000 g_free (netrcname);
2001 return -1;
2003 if (netrc_next () && *pass == NULL)
2004 *pass = g_strdup (buffer);
2005 break;
2006 case 6:
2007 if (stat (netrcname, &mystat) >= 0 &&
2008 (mystat.st_mode & 077)) {
2009 if (be_angry) {
2010 message_1s (1, MSG_ERROR, _("~/.netrc file has not correct mode.\n"
2011 "Remove password or correct mode."));
2012 be_angry = 0;
2014 g_free (netrc);
2015 g_free (netrcname);
2016 return -1;
2018 netrc_next ();
2019 break;
2020 case 7:
2021 for (;;) {
2022 while (*netrcp != '\n' && *netrcp);
2023 if (*netrcp != '\n')
2024 break;
2025 netrcp++;
2026 if (*netrcp == '\n' || !*netrcp)
2027 break;
2029 break;
2031 if (keyword == 20)
2032 break;
2034 if (keyword == 20)
2035 continue;
2036 else
2037 break;
2039 rupp = g_new (struct rupcache, 1);
2040 rupp->host = g_strdup (host);
2041 rupp->login = rupp->pass = 0;
2043 if (*login != NULL)
2044 rupp->login = g_strdup (*login);
2045 if (*pass != NULL)
2046 rupp->pass = g_strdup (*pass);
2047 rupp->next = rup_cache;
2048 rup_cache = rupp;
2050 g_free (netrc);
2051 g_free (netrcname);
2052 return 0;
2055 #endif /* USE_NETRC */