1998-02-27 07:54:42 +03:00
|
|
|
/* Virtual File System: FTP file system.
|
|
|
|
Copyright (C) 1995 The Free Software Foundation
|
|
|
|
|
|
|
|
Written by: 1995 Ching Hui
|
|
|
|
1995 Jakub Jelinek
|
|
|
|
1995, 1996, 1997 Miguel de Icaza
|
|
|
|
1997 Norbert Warmuth
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
|
|
|
|
|
|
|
/* FTPfs TODO:
|
|
|
|
|
|
|
|
- make it more robust - all the connects etc. should handle EADDRINUSE and
|
|
|
|
ERETRY (have I spelled these names correctly?)
|
|
|
|
- make the user able to flush a connection - all the caches will get empty
|
|
|
|
etc., (tarfs as well), we should give there a user selectable timeout
|
|
|
|
and assign a key sequence.
|
|
|
|
- use hash table instead of linklist to cache ftpfs directory.
|
|
|
|
- complete rename operation.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <pwd.h>
|
|
|
|
#include <ctype.h> /* For isdigit */
|
|
|
|
#ifdef SCO_FLAVOR
|
|
|
|
# include <sys/timeb.h> /* alex: for struct timeb definition */
|
|
|
|
#endif /* SCO_FLAVOR */
|
|
|
|
#include <time.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#if defined(HAVE_UNISTD_H)
|
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
|
|
|
#if HAVE_SYS_SELECT_H
|
|
|
|
# include <sys/select.h>
|
|
|
|
#endif
|
|
|
|
#include "../src/fs.h"
|
|
|
|
#include "../src/mad.h"
|
|
|
|
#include "../src/setup.h"
|
|
|
|
#include "../src/tty.h" /* enable/disable interrupt key */
|
|
|
|
#include <netdb.h> /* struct hostent */
|
|
|
|
#include <sys/socket.h> /* AF_INET */
|
|
|
|
#include <netinet/in.h> /* struct in_addr */
|
|
|
|
#ifdef HAVE_SETSOCKOPT
|
|
|
|
# include <netinet/ip.h> /* IP options */
|
|
|
|
#endif
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <arpa/ftp.h>
|
|
|
|
#include <arpa/telnet.h>
|
|
|
|
#ifndef SCO_FLAVOR
|
|
|
|
# include <sys/time.h> /* alex: this redefines struct timeval */
|
|
|
|
#endif /* SCO_FLAVOR */
|
|
|
|
#include <sys/param.h>
|
|
|
|
|
|
|
|
#ifdef USE_TERMNET
|
|
|
|
#include <termnet.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "../src/mem.h"
|
|
|
|
#define WANT_PARSE_LS_LGA
|
|
|
|
#include "vfs.h"
|
|
|
|
#include "tcputil.h"
|
|
|
|
#include "../src/util.h"
|
|
|
|
#include "../src/dialog.h"
|
|
|
|
#include "container.h"
|
|
|
|
#include "ftpfs.h"
|
|
|
|
#ifndef MAXHOSTNAMELEN
|
|
|
|
# define MAXHOSTNAMELEN 64
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define UPLOAD_ZERO_LENGTH_FILE
|
|
|
|
|
|
|
|
void print_vfs_message(char *, ...);
|
|
|
|
|
|
|
|
static int ftpfserrno;
|
|
|
|
static int code;
|
|
|
|
|
|
|
|
/* Delay to retry a connection */
|
|
|
|
int ftpfs_retry_seconds = 30;
|
|
|
|
|
|
|
|
/* Method to use to connect to ftp sites */
|
|
|
|
int ftpfs_use_passive_connections = 1;
|
|
|
|
|
|
|
|
/* Use the ~/.netrc */
|
|
|
|
int use_netrc = 1;
|
|
|
|
|
|
|
|
extern char *home_dir;
|
|
|
|
|
|
|
|
/* Anonymous setup */
|
|
|
|
char *ftpfs_anonymous_passwd;
|
|
|
|
int ftpfs_directory_timeout;
|
|
|
|
|
|
|
|
/* Proxy host */
|
|
|
|
char *ftpfs_proxy_host = 0;
|
|
|
|
|
|
|
|
/* Reget flag */
|
|
|
|
int do_reget = 0;
|
|
|
|
|
|
|
|
/* wether we have to use proxy by default? */
|
|
|
|
int ftpfs_always_use_proxy;
|
|
|
|
|
|
|
|
/* source routing host */
|
|
|
|
extern int source_route;
|
|
|
|
|
|
|
|
/* If set, then log dialog between server and client */
|
|
|
|
static int ftpfs_debug_server_dialog = 0;
|
|
|
|
|
|
|
|
/* Where we store the transactions */
|
|
|
|
static FILE *ftpfs_logfile;
|
|
|
|
|
|
|
|
/* If true, the directory cache is forced to reload */
|
|
|
|
static int force_expiration = 0;
|
|
|
|
|
|
|
|
struct linklist *ftpfs_connections_list;
|
|
|
|
|
|
|
|
extern char *last_current_dir;
|
|
|
|
|
|
|
|
/* command wait_flag: */
|
|
|
|
#define NONE 0x00
|
|
|
|
#define WAIT_REPLY 0x01
|
|
|
|
#define WANT_STRING 0x02
|
|
|
|
char reply_str [80];
|
|
|
|
|
|
|
|
static char *ftpfs_get_current_directory(struct ftpfs_connection *bucket);
|
|
|
|
static int __ftpfs_chdir (struct ftpfs_connection *bucket ,char *remote_path);
|
|
|
|
|
|
|
|
/* Extract the hostname and username from the path */
|
|
|
|
/* path is in the form: [user@]hostname:port/remote-dir, e.g.:
|
|
|
|
* ftp://sunsite.unc.edu/pub/linux
|
|
|
|
* ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
|
|
|
|
* ftp://tsx-11.mit.edu:8192/
|
|
|
|
* ftp://joe@foo.edu:11321/private
|
|
|
|
* If the user is empty, e.g. ftp://@roxanne/private, then your login name
|
|
|
|
* is supplied.
|
|
|
|
* */
|
|
|
|
|
|
|
|
char *ftpfs_get_host_and_username (char *path, char **host, char **user, int *port, char **pass)
|
|
|
|
{
|
|
|
|
return get_host_and_username (path, host, user, port, 21, 1, pass);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
select_on_two (int fd1, int fd2)
|
|
|
|
{
|
|
|
|
fd_set set;
|
|
|
|
struct timeval timeout;
|
|
|
|
int v;
|
|
|
|
int maxfd = (fd1 > fd2 ? fd1 : fd2) + 1;
|
|
|
|
|
|
|
|
timeout.tv_sec = 1;
|
|
|
|
timeout.tv_usec = 0;
|
|
|
|
FD_ZERO(&set);
|
|
|
|
FD_SET(fd1, &set);
|
|
|
|
FD_SET(fd2, &set);
|
|
|
|
v = select (maxfd, &set, 0, 0, &timeout);
|
|
|
|
if (v <= 0)
|
|
|
|
return v;
|
|
|
|
if (FD_ISSET (fd1, &set))
|
|
|
|
return 1;
|
|
|
|
if (FD_ISSET (fd2, &set))
|
|
|
|
return 2;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
get_line (int sock, char *buf, int buf_len)
|
|
|
|
{
|
|
|
|
int i, status;
|
|
|
|
char c;
|
|
|
|
|
|
|
|
for (i = 0; i < buf_len; i++, buf++) {
|
|
|
|
if (read(sock, buf, sizeof(char)) <= 0)
|
|
|
|
return 0;
|
|
|
|
if (ftpfs_debug_server_dialog){
|
|
|
|
fwrite (buf, 1, 1, ftpfs_logfile);
|
|
|
|
fflush (ftpfs_logfile);
|
|
|
|
}
|
|
|
|
if (*buf == '\n') {
|
|
|
|
*buf = 0;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*buf = 0;
|
|
|
|
while ((status = read(sock, &c, sizeof(c))) > 0){
|
|
|
|
if (ftpfs_debug_server_dialog){
|
|
|
|
fwrite (&c, 1, 1, ftpfs_logfile);
|
|
|
|
fflush (ftpfs_logfile);
|
|
|
|
}
|
|
|
|
if (c == '\n')
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
|
|
|
|
int getreply (int sock, char *string_buf, int string_len)
|
|
|
|
{
|
|
|
|
char answer[1024];
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
if (!get_line(sock, answer, sizeof(answer))) {
|
|
|
|
if (string_buf)
|
|
|
|
*string_buf = 0;
|
|
|
|
code = 421;
|
|
|
|
return 4;
|
|
|
|
}
|
|
|
|
switch(sscanf(answer, "%d", &code)) {
|
|
|
|
case 0:
|
|
|
|
if (string_buf) {
|
|
|
|
strncpy(string_buf, answer, string_len - 1);
|
|
|
|
*(string_buf + string_len - 1) = 0;
|
|
|
|
}
|
|
|
|
code = 500;
|
|
|
|
return 5;
|
|
|
|
case 1:
|
|
|
|
if (answer[3] == '-') {
|
|
|
|
while (1) {
|
|
|
|
if (!get_line(sock, answer, sizeof(answer))) {
|
|
|
|
if (string_buf)
|
|
|
|
*string_buf = 0;
|
|
|
|
code = 421;
|
|
|
|
return 4;
|
|
|
|
}
|
|
|
|
if ((sscanf(answer, "%d", &i) > 0) &&
|
|
|
|
(code == i) && (answer[3] == ' '))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (string_buf) {
|
|
|
|
strncpy(string_buf, answer, string_len - 1);
|
|
|
|
*(string_buf + string_len - 1) = 0;
|
|
|
|
}
|
|
|
|
return code / 100;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ftpentry_destructor (void *data)
|
|
|
|
{
|
|
|
|
struct ftpentry *fe = data;
|
|
|
|
|
|
|
|
fe->count--;
|
|
|
|
if ((fe->tmp_reget == 1 && fe->local_filename)){
|
|
|
|
unlink (fe->local_filename);
|
|
|
|
fe->tmp_reget = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fe->count > 0)
|
|
|
|
return;
|
|
|
|
free(fe->name);
|
|
|
|
if (fe->linkname)
|
|
|
|
free(fe->linkname);
|
|
|
|
if (fe->local_filename) {
|
|
|
|
if (fe->local_is_temp) {
|
|
|
|
if (!fe->local_stat.st_mtime)
|
|
|
|
unlink(fe->local_filename);
|
|
|
|
else {
|
|
|
|
struct stat sb;
|
|
|
|
|
|
|
|
if (stat (fe->local_filename, &sb) >=0 &&
|
|
|
|
fe->local_stat.st_mtime == sb.st_mtime)
|
|
|
|
unlink (fe->local_filename); /* Delete only if it hasn't changed */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free(fe->local_filename);
|
|
|
|
fe->local_filename = 0;
|
|
|
|
}
|
|
|
|
if (fe->remote_filename)
|
|
|
|
free(fe->remote_filename);
|
|
|
|
if (fe->l_stat)
|
|
|
|
free(fe->l_stat);
|
|
|
|
free(fe);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ftpfs_dir_destructor(void *data)
|
|
|
|
{
|
|
|
|
struct ftpfs_dir *fd = data;
|
|
|
|
|
|
|
|
fd->count--;
|
|
|
|
if (fd->count > 0)
|
|
|
|
return;
|
|
|
|
free(fd->remote_path);
|
|
|
|
linklist_destroy(fd->file_list, ftpentry_destructor);
|
|
|
|
free(fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int command (struct ftpfs_connection *bucket, int wait_reply,
|
|
|
|
char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
char buf[2048]; /* FIXME: buffer exceed ?? */
|
|
|
|
int n, status;
|
|
|
|
int sock = qsock (bucket);
|
|
|
|
|
|
|
|
va_start (ap, fmt);
|
|
|
|
vsprintf (buf, fmt, ap);
|
|
|
|
va_end (ap);
|
|
|
|
n = strlen(buf);
|
|
|
|
buf[n++] = '\r';
|
|
|
|
buf[n++] = '\n';
|
|
|
|
buf[n] = 0;
|
|
|
|
|
|
|
|
if (ftpfs_debug_server_dialog){
|
|
|
|
if (strncmp (buf, "PASS ", 5) == 0) {
|
|
|
|
char *tmp = "PASS <Password not logged>\r\n";
|
|
|
|
fwrite (tmp, strlen (tmp), 1, ftpfs_logfile);
|
|
|
|
} else
|
|
|
|
fwrite (buf, strlen (buf), 1, ftpfs_logfile);
|
|
|
|
fflush (ftpfs_logfile);
|
|
|
|
}
|
|
|
|
got_sigpipe = 0;
|
|
|
|
enable_interrupt_key();
|
|
|
|
status = write(sock, buf, strlen(buf));
|
|
|
|
if (status < 0){
|
|
|
|
code = 421;
|
|
|
|
if (errno == EPIPE){
|
|
|
|
got_sigpipe = 1;
|
|
|
|
}
|
|
|
|
disable_interrupt_key();
|
|
|
|
return TRANSIENT;
|
|
|
|
}
|
|
|
|
disable_interrupt_key();
|
|
|
|
|
|
|
|
if (wait_reply)
|
|
|
|
return getreply (sock, (wait_reply & WANT_STRING) ? reply_str : NULL, sizeof (reply_str)-1);
|
|
|
|
return COMPLETE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ftpfs_connection_close (void *data)
|
|
|
|
{
|
|
|
|
struct ftpfs_connection *bucket = data;
|
|
|
|
|
|
|
|
if (qsock (bucket) != -1){
|
|
|
|
print_vfs_message ("ftpfs: Disconnecting from %s", qhost(bucket));
|
|
|
|
command(bucket, NONE, "QUIT");
|
|
|
|
close(qsock(bucket));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ftpfs_free_bucket (void *data)
|
|
|
|
{
|
|
|
|
struct ftpfs_connection *bucket = data;
|
|
|
|
|
|
|
|
free(qhost(bucket));
|
|
|
|
free(quser(bucket));
|
|
|
|
if (qcdir(bucket))
|
|
|
|
free(qcdir(bucket));
|
|
|
|
if (qhome(bucket))
|
|
|
|
free(qhome(bucket));
|
|
|
|
if (qupdir(bucket))
|
|
|
|
free(qupdir(bucket));
|
|
|
|
if (bucket->password)
|
|
|
|
wipe_password (bucket->password);
|
|
|
|
linklist_destroy(qdcache(bucket), ftpfs_dir_destructor);
|
|
|
|
free(bucket);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ftpfs_connection_destructor(void *data)
|
|
|
|
{
|
|
|
|
ftpfs_connection_close (data);
|
|
|
|
ftpfs_free_bucket (data);
|
|
|
|
}
|
|
|
|
|
1998-03-02 21:09:16 +03:00
|
|
|
/* some defines only used by changetype */
|
|
|
|
/* These two are valid values for the second parameter */
|
|
|
|
#define TYPE_ASCII 0
|
|
|
|
#define TYPE_BINARY 1
|
|
|
|
|
|
|
|
/* This one is only used to initialize bucket->isbinary, don't use it as
|
|
|
|
second parameter to changetype. */
|
|
|
|
#define TYPE_UNKNOWN -1
|
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
static int
|
|
|
|
changetype (struct ftpfs_connection *bucket, int binary)
|
|
|
|
{
|
1998-03-02 21:09:16 +03:00
|
|
|
if (binary != bucket->isbinary) {
|
1998-02-27 07:54:42 +03:00
|
|
|
if (command (bucket, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE) {
|
|
|
|
ftpfserrno = EIO;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
bucket->isbinary = binary;
|
|
|
|
}
|
|
|
|
return binary;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
flush_all_directory(struct ftpfs_connection *bucket)
|
|
|
|
{
|
|
|
|
linklist_delete_all(qdcache(bucket), ftpfs_dir_destructor);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This routine logs the user in */
|
|
|
|
static int
|
|
|
|
login_server (struct ftpfs_connection *bucket, char *netrcpass)
|
|
|
|
{
|
|
|
|
#if defined(HSC_PROXY)
|
|
|
|
char *proxypass, *proxyname;
|
|
|
|
#endif
|
|
|
|
char *pass;
|
|
|
|
char *op;
|
|
|
|
char *name; /* login user name */
|
|
|
|
int anon = 0;
|
1998-03-02 21:09:16 +03:00
|
|
|
|
|
|
|
bucket->isbinary = TYPE_UNKNOWN;
|
1998-02-27 07:54:42 +03:00
|
|
|
if (netrcpass)
|
|
|
|
op = strdup (netrcpass);
|
|
|
|
else {
|
|
|
|
if (!strcmp (quser(bucket), "anonymous") ||
|
|
|
|
!strcmp (quser(bucket), "ftp")) {
|
|
|
|
op = strdup(ftpfs_anonymous_passwd);
|
|
|
|
anon = 1;
|
|
|
|
} else {
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
if (!bucket->password){
|
|
|
|
p = copy_strings (" FTP: Password required for ", quser(bucket),
|
|
|
|
" ", NULL);
|
|
|
|
op = input_dialog (p, "Password:", "");
|
|
|
|
free (p);
|
|
|
|
if (op == NULL) {
|
|
|
|
ftpfserrno = EPERM;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
bucket->password = strdup (op);
|
|
|
|
} else
|
|
|
|
op = strdup (bucket->password);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!anon || ftpfs_debug_server_dialog)
|
|
|
|
pass = strdup (op);
|
|
|
|
else
|
|
|
|
pass = copy_strings ("-", op, 0);
|
|
|
|
wipe_password (op);
|
|
|
|
|
|
|
|
|
|
|
|
/* Proxy server accepts: username@host-we-want-to-connect*/
|
|
|
|
if (qproxy (bucket)){
|
|
|
|
#if defined(HSC_PROXY)
|
|
|
|
char *p, *host;
|
|
|
|
int port;
|
|
|
|
p = ftpfs_get_host_and_username(ftpfs_proxy_host, &host, &proxyname,
|
|
|
|
&port, &proxypass);
|
|
|
|
if (p)
|
|
|
|
free (p);
|
|
|
|
|
|
|
|
free(host);
|
|
|
|
if (proxypass)
|
|
|
|
wipe_password (proxypass);
|
|
|
|
p = copy_strings(" Proxy: Password required for ", proxyname, " ",
|
|
|
|
NULL);
|
|
|
|
proxypass = input_dialog (p, "Password:", "");
|
|
|
|
free(p);
|
|
|
|
if (proxypass == NULL) {
|
|
|
|
ftpfserrno = EPERM;
|
|
|
|
wipe_password (pass);
|
|
|
|
free (proxyname);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
name = strdup(quser (bucket));
|
|
|
|
#else
|
|
|
|
name = copy_strings (quser(bucket), "@",
|
|
|
|
qhost(bucket)[0] == '!' ? qhost(bucket)+1 : qhost(bucket), 0);
|
|
|
|
#endif
|
|
|
|
} else
|
|
|
|
name = strdup (quser (bucket));
|
|
|
|
|
|
|
|
if (getreply (qsock(bucket), NULL, 0) == COMPLETE) {
|
|
|
|
#if defined(HSC_PROXY)
|
|
|
|
if (qproxy(bucket)) {
|
|
|
|
print_vfs_message("ftpfs: sending proxy login name");
|
|
|
|
if (command (bucket, 1, "USER %s", proxyname) == CONTINUE) {
|
|
|
|
print_vfs_message("ftpfs: sending proxy user password");
|
|
|
|
if (command (bucket, 1, "PASS %s", proxypass) == COMPLETE)
|
|
|
|
{
|
|
|
|
print_vfs_message("ftpfs: proxy authentication succeeded");
|
|
|
|
if (command (bucket, 1, "SITE %s", qhost(bucket)+1) ==
|
|
|
|
COMPLETE) {
|
|
|
|
print_vfs_message("ftpfs: connected to %s", qhost(bucket)+1);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
bucket->failed_on_login = 1;
|
|
|
|
/* ftpfserrno = E; */
|
|
|
|
if (proxypass)
|
|
|
|
wipe_password (proxypass);
|
|
|
|
wipe_password (pass);
|
|
|
|
free (proxyname);
|
|
|
|
free (name);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
bucket->failed_on_login = 1;
|
|
|
|
/* ftpfserrno = E; */
|
|
|
|
if (proxypass)
|
|
|
|
wipe_password (proxypass);
|
|
|
|
wipe_password (pass);
|
|
|
|
free (proxyname);
|
|
|
|
free (name);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
bucket->failed_on_login = 1;
|
|
|
|
/* ftpfserrno = E; */
|
|
|
|
if (proxypass)
|
|
|
|
wipe_password (proxypass);
|
|
|
|
wipe_password (pass);
|
|
|
|
free (proxyname);
|
|
|
|
free (name);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (proxypass)
|
|
|
|
wipe_password (proxypass);
|
|
|
|
free (proxyname);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
print_vfs_message("ftpfs: sending login name");
|
|
|
|
code = command (bucket, WAIT_REPLY, "USER %s", name);
|
|
|
|
|
|
|
|
switch (code){
|
|
|
|
case CONTINUE:
|
|
|
|
print_vfs_message("ftpfs: sending user password");
|
|
|
|
if (command (bucket, WAIT_REPLY, "PASS %s", pass) != COMPLETE)
|
|
|
|
break;
|
|
|
|
|
|
|
|
case COMPLETE:
|
|
|
|
print_vfs_message("ftpfs: logged in");
|
|
|
|
wipe_password (pass);
|
|
|
|
free (name);
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
default:
|
|
|
|
bucket->failed_on_login = 1;
|
|
|
|
/* ftpfserrno = E; */
|
|
|
|
if (bucket->password)
|
|
|
|
wipe_password (bucket->password);
|
|
|
|
bucket->password = 0;
|
|
|
|
|
|
|
|
/* This matches the end of the code below, just to make it
|
|
|
|
* obvious to the optimizer
|
|
|
|
*/
|
|
|
|
wipe_password (pass);
|
|
|
|
free (name);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
print_vfs_message ("ftpfs: Login incorrect for user %s ", quser(bucket));
|
|
|
|
ftpfserrno = EPERM;
|
|
|
|
wipe_password (pass);
|
|
|
|
free (name);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_SETSOCKOPT
|
|
|
|
static void
|
|
|
|
setup_source_route (int socket, int dest)
|
|
|
|
{
|
|
|
|
char buffer [20];
|
|
|
|
char *ptr = buffer;
|
|
|
|
|
|
|
|
if (!source_route)
|
|
|
|
return;
|
|
|
|
bzero (buffer, sizeof (buffer));
|
|
|
|
*ptr++ = IPOPT_LSRR;
|
|
|
|
*ptr++ = 3 + 8;
|
|
|
|
*ptr++ = 4; /* pointer */
|
|
|
|
|
|
|
|
/* First hop */
|
|
|
|
bcopy ((char *) &source_route, ptr, sizeof (int));
|
|
|
|
ptr += 4;
|
|
|
|
|
|
|
|
/* Second hop (ie, final destination) */
|
|
|
|
bcopy ((char *) &dest, ptr, sizeof (int));
|
|
|
|
ptr += 4;
|
|
|
|
while ((ptr - buffer) & 3)
|
|
|
|
ptr++;
|
|
|
|
if (setsockopt (socket, IPPROTO_IP, IP_OPTIONS,
|
|
|
|
buffer, ptr - buffer) < 0)
|
|
|
|
message_2s (1, " Error ", " Could not set source routing (%s)", unix_error_string (errno));
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
#define setup_source_route(x,y)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static struct no_proxy_entry {
|
|
|
|
char *domain;
|
|
|
|
void *next;
|
|
|
|
} *no_proxy;
|
|
|
|
|
|
|
|
static void
|
|
|
|
load_no_proxy_list ()
|
|
|
|
{
|
|
|
|
/* FixMe: shouldn't be hardcoded!!! */
|
|
|
|
char s[258]; /* provide for 256 characters and nl */
|
|
|
|
struct no_proxy_entry *np, *current;
|
|
|
|
FILE *npf;
|
|
|
|
int c;
|
|
|
|
char *p;
|
|
|
|
static int loaded;
|
|
|
|
|
|
|
|
if (loaded)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (exist_file (LIBDIR "mc.no_proxy") &&
|
|
|
|
(npf = fopen (LIBDIR "mc.no_proxy", "r"))) {
|
|
|
|
while (fgets (s, 258, npf) || !(feof (npf) || ferror (npf))) {
|
|
|
|
if (!(p = strchr (s, '\n'))) { /* skip bogus entries */
|
|
|
|
while ((c = getc (npf)) != EOF && c != '\n')
|
|
|
|
;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p == s)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
*p = '\0';
|
|
|
|
p = xmalloc (strlen (s), "load_no_proxy_list:1");
|
|
|
|
np = xmalloc (sizeof (*np), "load_no_proxy_list:2");
|
|
|
|
strcpy (p, s);
|
|
|
|
np->domain = p;
|
|
|
|
np->next = 0;
|
|
|
|
if (no_proxy)
|
|
|
|
current->next = np;
|
|
|
|
else
|
|
|
|
no_proxy = np;
|
|
|
|
current = np;
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose (npf);
|
|
|
|
loaded = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ftpfs_check_proxy (char *host)
|
|
|
|
{
|
|
|
|
struct no_proxy_entry *npe;
|
|
|
|
|
|
|
|
if (!ftpfs_proxy_host || !*ftpfs_proxy_host || !host || !*host)
|
|
|
|
return 0; /* sanity check */
|
|
|
|
|
|
|
|
if (*host == '!')
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (!ftpfs_always_use_proxy)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!strchr (host, '.'))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
load_no_proxy_list ();
|
|
|
|
for (npe = no_proxy; npe; npe=npe->next) {
|
|
|
|
char *domain = npe->domain;
|
|
|
|
|
|
|
|
if (domain[0] == '.') {
|
|
|
|
int ld = strlen (domain);
|
|
|
|
int lh = strlen (host);
|
|
|
|
|
|
|
|
while (ld && lh && host[lh - 1] == domain[ld - 1]) {
|
|
|
|
ld--;
|
|
|
|
lh--;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ld)
|
|
|
|
return 0;
|
|
|
|
} else
|
|
|
|
if (!strcasecmp (host, domain))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ftpfs_get_proxy_host_and_port (char *proxy, char **host, int *port)
|
|
|
|
{
|
|
|
|
char *user, *pass, *dir;
|
|
|
|
|
|
|
|
#if defined(HSC_PROXY)
|
|
|
|
#define HSC_DEFAULT_PORT 9875
|
|
|
|
dir = get_host_and_username(proxy, host, &user, port, HSC_DEFAULT_PORT, 1,
|
|
|
|
&pass);
|
|
|
|
#else
|
|
|
|
dir = get_host_and_username(proxy, host, &user, port, 21, 1,
|
|
|
|
&pass);
|
|
|
|
#endif
|
|
|
|
free(user);
|
|
|
|
if (pass)
|
|
|
|
wipe_password (pass);
|
|
|
|
if (dir)
|
|
|
|
free(dir);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ftpfs_open_socket(struct ftpfs_connection *bucket)
|
|
|
|
{
|
|
|
|
struct sockaddr_in server_address;
|
|
|
|
struct hostent *hp;
|
|
|
|
int my_socket;
|
|
|
|
char *host;
|
|
|
|
int port;
|
|
|
|
int free_host = 0;
|
|
|
|
|
|
|
|
/* Use a proxy host? */
|
|
|
|
host = qhost(bucket);
|
|
|
|
|
|
|
|
if (!host || !*host){
|
|
|
|
print_vfs_message ("ftpfs: Invalid host name.");
|
|
|
|
ftpfserrno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Hosts to connect to that start with a ! should use proxy */
|
|
|
|
if (qproxy(bucket)) {
|
|
|
|
ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &port);
|
|
|
|
free_host = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get host address */
|
|
|
|
bzero ((char *) &server_address, sizeof (server_address));
|
|
|
|
server_address.sin_family = AF_INET;
|
|
|
|
server_address.sin_addr.s_addr = inet_addr (host);
|
|
|
|
if (server_address.sin_addr.s_addr != -1)
|
|
|
|
server_address.sin_family = AF_INET;
|
|
|
|
else {
|
|
|
|
hp = gethostbyname(host);
|
|
|
|
if (hp == NULL){
|
|
|
|
print_vfs_message("ftpfs: Invalid host address.");
|
|
|
|
ftpfserrno = EINVAL;
|
|
|
|
if (free_host)
|
|
|
|
free (host);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
server_address.sin_family = hp->h_addrtype;
|
|
|
|
bcopy ((char *) hp->h_addr, (char *) &server_address.sin_addr,
|
|
|
|
hp->h_length);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define THE_PORT qproxy(bucket) ? port : qport (bucket)
|
|
|
|
|
|
|
|
server_address.sin_port = htons (THE_PORT);
|
|
|
|
|
|
|
|
/* Connect */
|
|
|
|
if ((my_socket = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
|
|
|
|
ftpfserrno = errno;
|
|
|
|
if (free_host)
|
|
|
|
free (host);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
setup_source_route (my_socket, server_address.sin_addr.s_addr);
|
|
|
|
|
|
|
|
print_vfs_message("ftpfs: making connection to %s", host);
|
|
|
|
if (free_host)
|
|
|
|
free (host);
|
|
|
|
|
|
|
|
enable_interrupt_key(); /* clear the interrupt flag */
|
|
|
|
|
|
|
|
if (connect (my_socket, (struct sockaddr *) &server_address,
|
|
|
|
sizeof (server_address)) < 0){
|
|
|
|
ftpfserrno = errno;
|
|
|
|
if (errno == EINTR && got_interrupt())
|
|
|
|
print_vfs_message("ftpfs: connection interrupted by user");
|
|
|
|
else
|
|
|
|
print_vfs_message("ftpfs: connection to server failed: %s",
|
|
|
|
unix_error_string(errno));
|
|
|
|
disable_interrupt_key();
|
|
|
|
close (my_socket);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
disable_interrupt_key();
|
|
|
|
return my_socket;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct ftpfs_connection *
|
|
|
|
open_command_connection (char *host, char *user, int port, char *netrcpass)
|
|
|
|
{
|
|
|
|
struct ftpfs_connection *bucket;
|
|
|
|
int retry_seconds, count_down;
|
|
|
|
|
|
|
|
bucket = xmalloc(sizeof(struct ftpfs_connection),
|
|
|
|
"struct ftpfs_connection");
|
|
|
|
|
|
|
|
if (bucket == NULL) {
|
|
|
|
ftpfserrno = ENOMEM;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#ifdef HAVE_MAD
|
|
|
|
{
|
|
|
|
extern void *watch_free_pointer;
|
|
|
|
|
|
|
|
if (!watch_free_pointer)
|
|
|
|
watch_free_pointer = host;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
qhost(bucket) = strdup (host);
|
|
|
|
quser(bucket) = strdup (user);
|
|
|
|
qcdir(bucket) = NULL;
|
|
|
|
qport(bucket) = port;
|
|
|
|
qlock(bucket) = 0;
|
|
|
|
qhome(bucket) = NULL;
|
|
|
|
qproxy(bucket)= 0;
|
|
|
|
qupdir(bucket)= 0;
|
|
|
|
qdcache(bucket)=0;
|
|
|
|
bucket->__inode_counter = 0;
|
|
|
|
bucket->lock = 0;
|
|
|
|
bucket->use_proxy = ftpfs_check_proxy (host);
|
|
|
|
bucket->password = 0;
|
|
|
|
bucket->use_passive_connection = ftpfs_use_passive_connections | source_route;
|
|
|
|
bucket->use_source_route = source_route;
|
1998-03-02 21:09:16 +03:00
|
|
|
bucket->isbinary = TYPE_UNKNOWN;
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
/* We do not want to use the passive if we are using proxies */
|
|
|
|
if (bucket->use_proxy)
|
|
|
|
bucket->use_passive_connection = 0;
|
|
|
|
|
|
|
|
if ((qdcache(bucket) = linklist_init()) == NULL) {
|
|
|
|
ftpfserrno = ENOMEM;
|
|
|
|
free (qhost(bucket));
|
|
|
|
free (quser(bucket));
|
|
|
|
free (bucket);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
retry_seconds = 0;
|
|
|
|
do {
|
|
|
|
bucket->failed_on_login = 0;
|
|
|
|
|
|
|
|
qsock(bucket) = ftpfs_open_socket(bucket);
|
|
|
|
if (qsock(bucket) == -1) {
|
|
|
|
ftpfs_free_bucket (bucket);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (login_server(bucket, netrcpass)) {
|
|
|
|
/* Logged in, no need to retry the connection */
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
if (bucket->failed_on_login){
|
|
|
|
/* Close only the socket descriptor */
|
|
|
|
close (qsock (bucket));
|
|
|
|
} else {
|
|
|
|
ftpfs_connection_destructor (bucket);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (ftpfs_retry_seconds){
|
|
|
|
retry_seconds = ftpfs_retry_seconds;
|
|
|
|
enable_interrupt_key ();
|
|
|
|
for (count_down = retry_seconds; count_down; count_down--){
|
|
|
|
print_vfs_message ("Waiting to retry... %d (Control-C to cancel)\n", count_down);
|
|
|
|
sleep (1);
|
|
|
|
if (got_interrupt ()){
|
|
|
|
/* ftpfserrno = E; */
|
|
|
|
disable_interrupt_key ();
|
|
|
|
ftpfs_free_bucket (bucket);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
disable_interrupt_key ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while (retry_seconds);
|
|
|
|
|
|
|
|
qhome(bucket) = ftpfs_get_current_directory (bucket);
|
|
|
|
if (!qhome(bucket))
|
|
|
|
qhome(bucket) = strdup ("/");
|
|
|
|
if (last_current_dir) {
|
|
|
|
if (last_current_dir [strlen (last_current_dir) - 1] == '/')
|
|
|
|
qupdir(bucket) = strdup (last_current_dir);
|
|
|
|
else
|
|
|
|
qupdir(bucket) = copy_strings (last_current_dir, "/", NULL);
|
|
|
|
} else
|
|
|
|
qupdir(bucket) = strdup ("/");
|
|
|
|
return bucket;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
is_connection_closed(struct ftpfs_connection *bucket)
|
|
|
|
{
|
|
|
|
fd_set rset;
|
|
|
|
struct timeval t;
|
|
|
|
|
|
|
|
if (got_sigpipe){
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
t.tv_sec = 0;
|
|
|
|
t.tv_usec = 0;
|
|
|
|
FD_ZERO(&rset);
|
|
|
|
FD_SET(qsock(bucket), &rset);
|
|
|
|
while (1) {
|
|
|
|
if (select(qsock(bucket) + 1, &rset, NULL, NULL, &t) < 0)
|
|
|
|
if (errno != EINTR)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
#if 0
|
|
|
|
if (FD_ISSET(qsock(bucket), &rset)) {
|
|
|
|
n = read(qsock(bucket), &read_ahead, sizeof(read_ahead));
|
|
|
|
if (n <= 0)
|
|
|
|
return 1;
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This routine keeps track of open connections */
|
|
|
|
/* Returns a connected socket to host */
|
|
|
|
static struct ftpfs_connection *
|
|
|
|
ftpfs_open_link (char *host, char *user, int port, char *netrcpass)
|
|
|
|
{
|
|
|
|
int sock;
|
|
|
|
struct ftpfs_connection *bucket;
|
|
|
|
struct linklist *lptr;
|
|
|
|
|
|
|
|
for (lptr = ftpfs_connections_list->next;
|
|
|
|
lptr != ftpfs_connections_list; lptr = lptr->next) {
|
|
|
|
bucket = lptr->data;
|
|
|
|
if ((strcmp (host, qhost(bucket)) == 0) &&
|
|
|
|
(strcmp (user, quser(bucket)) == 0) &&
|
|
|
|
(port == qport(bucket))) {
|
|
|
|
|
|
|
|
/* check the connection is closed or not, just hack */
|
|
|
|
if (is_connection_closed(bucket)) {
|
|
|
|
flush_all_directory(bucket);
|
|
|
|
sock = ftpfs_open_socket(bucket);
|
|
|
|
if (sock != -1) {
|
|
|
|
close(qsock(bucket));
|
|
|
|
qsock(bucket) = sock;
|
|
|
|
if (login_server(bucket, netrcpass))
|
|
|
|
return bucket;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* connection refused */
|
|
|
|
lptr->prev->next = lptr->next;
|
|
|
|
lptr->next->prev = lptr->prev;
|
|
|
|
ftpfs_connection_destructor(bucket);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return bucket;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bucket = open_command_connection(host, user, port, netrcpass);
|
|
|
|
if (bucket == NULL)
|
|
|
|
return NULL;
|
|
|
|
if (!linklist_insert(ftpfs_connections_list, bucket)) {
|
|
|
|
ftpfserrno = ENOMEM;
|
|
|
|
ftpfs_connection_destructor(bucket);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return bucket;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The returned directory should always contain a trailing slash */
|
|
|
|
static char *ftpfs_get_current_directory(struct ftpfs_connection *bucket)
|
|
|
|
{
|
|
|
|
char buf[4096], *bufp, *bufq;
|
|
|
|
|
|
|
|
if (command(bucket, NONE, "PWD") == COMPLETE &&
|
|
|
|
getreply(qsock(bucket), buf, sizeof(buf)) == COMPLETE) {
|
|
|
|
bufp = NULL;
|
|
|
|
for (bufq = buf; *bufq; bufq++)
|
|
|
|
if (*bufq == '"') {
|
|
|
|
if (!bufp) {
|
|
|
|
bufp = bufq + 1;
|
|
|
|
} else {
|
|
|
|
*bufq = 0;
|
|
|
|
if (*bufp) {
|
|
|
|
if (*(bufq - 1) != '/') {
|
|
|
|
*bufq++ = '/';
|
|
|
|
*bufq = 0;
|
|
|
|
}
|
|
|
|
return strdup (bufp);
|
|
|
|
} else {
|
|
|
|
ftpfserrno = EIO;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ftpfserrno = EIO;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ftpfs_fill_names (void (*func)(char *))
|
|
|
|
{
|
|
|
|
struct linklist *lptr;
|
|
|
|
char *path_name;
|
|
|
|
struct ftpfs_connection *bucket;
|
|
|
|
|
|
|
|
if (!ftpfs_connections_list)
|
|
|
|
return;
|
|
|
|
lptr = ftpfs_connections_list;
|
|
|
|
do {
|
|
|
|
if ((bucket = lptr->data) != 0){
|
|
|
|
|
|
|
|
path_name = copy_strings ("ftp://", quser (bucket),
|
|
|
|
"@", qhost (bucket),
|
|
|
|
qcdir(bucket), 0);
|
|
|
|
(*func)(path_name);
|
|
|
|
free (path_name);
|
|
|
|
}
|
|
|
|
lptr = lptr->next;
|
|
|
|
} while (lptr != ftpfs_connections_list);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Setup Passive ftp connection, we use it for source routed connections */
|
|
|
|
static int
|
|
|
|
setup_passive (int my_socket, struct ftpfs_connection *bucket, struct sockaddr_in *sa)
|
|
|
|
{
|
|
|
|
int xa, xb, xc, xd, xe, xf;
|
|
|
|
char n [6];
|
|
|
|
char *c = reply_str;
|
|
|
|
|
|
|
|
if (command (bucket, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Parse remote parameters */
|
|
|
|
for (c = reply_str + 4; (*c) && (!isdigit (*c)); c++)
|
|
|
|
;
|
|
|
|
if (!*c)
|
|
|
|
return 0;
|
|
|
|
if (!isdigit (*c))
|
|
|
|
return 0;
|
|
|
|
if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
|
|
|
|
return 0;
|
|
|
|
n [0] = (unsigned char) xa;
|
|
|
|
n [1] = (unsigned char) xb;
|
|
|
|
n [2] = (unsigned char) xc;
|
|
|
|
n [3] = (unsigned char) xd;
|
|
|
|
n [4] = (unsigned char) xe;
|
|
|
|
n [5] = (unsigned char) xf;
|
|
|
|
|
|
|
|
bcopy ((void *)n, &(sa->sin_addr.s_addr), 4);
|
|
|
|
bcopy ((void *)&n[4], &(sa->sin_port), 2);
|
|
|
|
setup_source_route (my_socket, sa->sin_addr.s_addr);
|
|
|
|
if (connect (my_socket, (struct sockaddr *) sa, sizeof (struct sockaddr_in)) < 0)
|
|
|
|
return 0;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
initconn (struct ftpfs_connection *bucket)
|
|
|
|
{
|
|
|
|
struct sockaddr_in data_addr;
|
|
|
|
int data, len = sizeof(data_addr);
|
|
|
|
struct protoent *pe;
|
|
|
|
|
|
|
|
getsockname(qsock(bucket), (struct sockaddr *) &data_addr, &len);
|
|
|
|
data_addr.sin_port = 0;
|
|
|
|
|
|
|
|
pe = getprotobyname("tcp");
|
|
|
|
if (pe == NULL) {
|
|
|
|
ftpfserrno = EIO;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
data = socket (AF_INET, SOCK_STREAM, pe->p_proto);
|
|
|
|
if (data < 0) {
|
|
|
|
ftpfserrno = EIO;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef ORIGINAL_CONNECT_CODE
|
|
|
|
if (bucket->use_source_route){
|
|
|
|
int c;
|
|
|
|
|
|
|
|
if ((c = setup_passive (data, bucket, &data_addr)))
|
|
|
|
return data;
|
|
|
|
print_vfs_message("ftpfs: could not setup passive mode for source routing");
|
|
|
|
bucket->use_source_route = 0;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
if (bucket->use_passive_connection){
|
|
|
|
if ((bucket->use_passive_connection = setup_passive (data, bucket, &data_addr)))
|
|
|
|
return data;
|
|
|
|
|
|
|
|
bucket->use_source_route = 0;
|
|
|
|
bucket->use_passive_connection = 0;
|
|
|
|
print_vfs_message ("ftpfs: could not setup passive mode");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
/* If passive setup fails, fallback to active connections */
|
|
|
|
/* Active FTP connection */
|
|
|
|
if (bind (data, (struct sockaddr *)&data_addr, len) < 0)
|
|
|
|
goto error_return;
|
|
|
|
getsockname(data, (struct sockaddr *) &data_addr, &len);
|
|
|
|
if (listen (data, 1) < 0)
|
|
|
|
goto error_return;
|
|
|
|
{
|
|
|
|
unsigned char *a = (unsigned char *)&data_addr.sin_addr;
|
|
|
|
unsigned char *p = (unsigned char *)&data_addr.sin_port;
|
|
|
|
|
|
|
|
if (command (bucket, WAIT_REPLY, "PORT %d,%d,%d,%d,%d,%d", a[0], a[1],
|
|
|
|
a[2], a[3], p[0], p[1]) != COMPLETE)
|
|
|
|
goto error_return;
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
error_return:
|
|
|
|
close(data);
|
|
|
|
ftpfserrno = EIO;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
open_data_connection (struct ftpfs_connection *bucket, char *cmd, char *remote,
|
|
|
|
int isbinary)
|
|
|
|
{
|
|
|
|
struct sockaddr_in from;
|
|
|
|
int s, j, data, fromlen = sizeof(from);
|
|
|
|
|
|
|
|
if ((s = initconn (bucket)) == -1)
|
|
|
|
return -1;
|
|
|
|
if (changetype (bucket, isbinary) == -1)
|
|
|
|
return -1;
|
|
|
|
if (do_reget > 0){
|
|
|
|
j = command (bucket, WAIT_REPLY, "REST %d", do_reget);
|
|
|
|
do_reget = 0;
|
|
|
|
if (j != CONTINUE)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (remote)
|
|
|
|
j = command (bucket, WAIT_REPLY, "%s %s", cmd, remote);
|
|
|
|
else
|
|
|
|
j = command (bucket, WAIT_REPLY, "%s", cmd);
|
|
|
|
if (j != PRELIM) {
|
|
|
|
ftpfserrno = EPERM;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
enable_interrupt_key();
|
|
|
|
if (bucket->use_passive_connection)
|
|
|
|
data = s;
|
|
|
|
else {
|
|
|
|
data = accept (s, (struct sockaddr *)&from, &fromlen);
|
|
|
|
if (data < 0) {
|
|
|
|
ftpfserrno = errno;
|
|
|
|
close(s);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
close(s);
|
|
|
|
}
|
|
|
|
disable_interrupt_key();
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ftpfs_get_path:
|
|
|
|
* makes BUCKET point to the connection bucket descriptor for PATH
|
|
|
|
* returns a malloced string with the pathname relative to BUCKET.
|
|
|
|
*/
|
|
|
|
static char*
|
|
|
|
ftpfs_get_path (struct ftpfs_connection **bucket, char *path)
|
|
|
|
{
|
|
|
|
char *user, *host, *remote_path, *pass;
|
|
|
|
int port;
|
|
|
|
|
|
|
|
/* An absolute path name, try to determine connection socket */
|
|
|
|
if (strncmp (path, "ftp://", 6) == 0){
|
|
|
|
path += 6;
|
|
|
|
|
|
|
|
if (!(remote_path = ftpfs_get_host_and_username (path, &host, &user,
|
|
|
|
&port, &pass))) {
|
|
|
|
ftpfserrno = ENOENT;
|
|
|
|
free (host);
|
|
|
|
free (user);
|
|
|
|
if (pass)
|
|
|
|
wipe_password (pass);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if ((*bucket = ftpfs_open_link (host, user, port, pass)) == NULL) {
|
|
|
|
free (remote_path);
|
|
|
|
free (host);
|
|
|
|
free (user);
|
|
|
|
if (pass)
|
|
|
|
wipe_password (pass);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
free (host);
|
|
|
|
free (user);
|
|
|
|
if (pass)
|
|
|
|
wipe_password (pass);
|
|
|
|
return remote_path;
|
|
|
|
}
|
|
|
|
/* never get here !!! */
|
|
|
|
message_1s(1, " Error ", " Oops, you just hit a bug in the code ");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ftpfs_abort (struct ftpfs_connection *bucket, int dsock)
|
|
|
|
{
|
|
|
|
static unsigned char ipbuf[3] = { IAC, IP, IAC };
|
|
|
|
fd_set mask;
|
|
|
|
char buf[1024];
|
|
|
|
|
|
|
|
print_vfs_message("ftpfs: aborting transfer.");
|
|
|
|
if (send(qsock(bucket), ipbuf, sizeof(ipbuf), MSG_OOB) != sizeof(ipbuf)) {
|
|
|
|
print_vfs_message("ftpfs: abort error: %s", unix_error_string(errno));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (command(bucket, NONE, "%cABOR", DM) != COMPLETE){
|
|
|
|
print_vfs_message ("ftpfs: abort failed");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (dsock != -1) {
|
|
|
|
FD_ZERO(&mask);
|
|
|
|
FD_SET(dsock, &mask);
|
|
|
|
if (select(dsock + 1, &mask, NULL, NULL, NULL) > 0)
|
|
|
|
while (read(dsock, buf, sizeof(buf)) > 0);
|
|
|
|
}
|
|
|
|
if ((getreply(qsock(bucket), NULL, 0) == TRANSIENT) && (code == 426))
|
|
|
|
getreply(qsock(bucket), NULL, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
resolve_symlink(struct ftpfs_connection *bucket, struct ftpfs_dir *dir)
|
|
|
|
{
|
|
|
|
char buffer[2048] = "", *filename;
|
|
|
|
int sock;
|
|
|
|
FILE *fp;
|
|
|
|
struct stat s;
|
|
|
|
struct linklist *flist;
|
|
|
|
struct ftpentry *fe;
|
|
|
|
|
|
|
|
print_vfs_message("Resolving symlink...");
|
|
|
|
|
|
|
|
if (strchr (dir->remote_path, ' ')) {
|
|
|
|
if (__ftpfs_chdir(bucket, dir->remote_path) != COMPLETE) {
|
|
|
|
print_vfs_message("ftpfs: CWD failed.");
|
|
|
|
return;
|
|
|
|
}
|
1998-03-02 21:09:16 +03:00
|
|
|
sock = open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII);
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
sock = open_data_connection (bucket, "LIST -lLa",
|
1998-03-02 21:09:16 +03:00
|
|
|
dir->remote_path, TYPE_ASCII);
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
if (sock == -1) {
|
|
|
|
print_vfs_message("ftpfs: couldn't resolve symlink");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
fp = fdopen(sock, "r");
|
|
|
|
if (fp == NULL) {
|
|
|
|
close(sock);
|
|
|
|
print_vfs_message("ftpfs: couldn't resolve symlink");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
enable_interrupt_key();
|
|
|
|
flist = dir->file_list->next;
|
|
|
|
while (1) {
|
|
|
|
do {
|
|
|
|
if (flist == dir->file_list)
|
|
|
|
goto done;
|
|
|
|
fe = flist->data;
|
|
|
|
flist = flist->next;
|
|
|
|
} while (!S_ISLNK(fe->s.st_mode));
|
|
|
|
while (1) {
|
|
|
|
if (fgets (buffer, sizeof (buffer), fp) == NULL)
|
|
|
|
goto done;
|
|
|
|
if (parse_ls_lga (buffer, &s, &filename, NULL)) {
|
|
|
|
int r = strcmp(fe->name, filename);
|
|
|
|
free(filename);
|
|
|
|
if (r == 0) {
|
|
|
|
fe->l_stat = xmalloc(sizeof(struct stat),
|
|
|
|
"resolve_symlink: struct stat");
|
|
|
|
if (fe->l_stat == NULL)
|
|
|
|
goto done;
|
|
|
|
*fe->l_stat = s;
|
|
|
|
(*fe->l_stat).st_ino = bucket->__inode_counter++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (r < 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
done:
|
|
|
|
while (fgets(buffer, sizeof(buffer), fp) != NULL);
|
|
|
|
disable_interrupt_key();
|
|
|
|
fclose(fp);
|
|
|
|
getreply(qsock(bucket), NULL, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
get_line_interruptible (char *buffer, int size, int fd)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < size-1; i++) {
|
|
|
|
n = read (fd, buffer+i, 1);
|
|
|
|
if (n == -1 && errno == EINTR){
|
|
|
|
buffer [i] = 0;
|
|
|
|
return EINTR;
|
|
|
|
}
|
|
|
|
if (n == 0){
|
|
|
|
buffer [i] = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (buffer [i] == '\n'){
|
|
|
|
buffer [i] = 0;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
buffer [size-1] = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return true if path is the same directoy as the one we are on now */
|
|
|
|
static int
|
|
|
|
ftpfs_same_dir (char *path, struct ftpfs_connection *bucket)
|
|
|
|
{
|
|
|
|
if (!qcdir (bucket))
|
|
|
|
return 0;
|
|
|
|
if (strcmp (path, qcdir (bucket)) == 0)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct ftpfs_dir *
|
|
|
|
retrieve_dir(struct ftpfs_connection *bucket, char *remote_path)
|
|
|
|
{
|
|
|
|
#ifdef OLD_READ
|
|
|
|
FILE *fp;
|
|
|
|
#endif
|
|
|
|
int sock, has_symlinks;
|
|
|
|
struct linklist *file_list, *p;
|
|
|
|
struct ftpentry *fe;
|
|
|
|
char buffer[8192];
|
|
|
|
struct ftpfs_dir *dcache;
|
|
|
|
int got_intr = 0;
|
|
|
|
int has_spaces = (strchr (remote_path, ' ') != NULL);
|
|
|
|
|
|
|
|
for (p = qdcache(bucket)->next;p != qdcache(bucket);
|
|
|
|
p = p->next) {
|
|
|
|
dcache = p->data;
|
|
|
|
if (strcmp(dcache->remote_path, remote_path) == 0) {
|
|
|
|
struct timeval tim;
|
|
|
|
|
|
|
|
gettimeofday(&tim, NULL);
|
|
|
|
if ((tim.tv_sec < dcache->timestamp.tv_sec) && !force_expiration)
|
|
|
|
return dcache;
|
|
|
|
else {
|
|
|
|
force_expiration = 0;
|
|
|
|
p->next->prev = p->prev;
|
|
|
|
p->prev->next = p->next;
|
|
|
|
ftpfs_dir_destructor(dcache);
|
|
|
|
free (p);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
has_symlinks = 0;
|
|
|
|
print_vfs_message("ftpfs: Reading FTP directory...");
|
|
|
|
if (has_spaces)
|
|
|
|
if (__ftpfs_chdir(bucket, remote_path) != COMPLETE) {
|
|
|
|
ftpfserrno = ENOENT;
|
|
|
|
print_vfs_message("ftpfs: CWD failed.");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
file_list = linklist_init();
|
|
|
|
if (file_list == NULL) {
|
|
|
|
ftpfserrno = ENOMEM;
|
|
|
|
print_vfs_message("ftpfs: couldn't get a file listing");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
dcache = xmalloc(sizeof(struct ftpfs_dir),
|
|
|
|
"struct ftpfs_dir");
|
|
|
|
if (dcache == NULL) {
|
|
|
|
ftpfserrno = ENOMEM;
|
|
|
|
linklist_destroy(file_list, NULL);
|
|
|
|
print_vfs_message("ftpfs: FAIL");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
gettimeofday(&dcache->timestamp, NULL);
|
|
|
|
dcache->timestamp.tv_sec += ftpfs_directory_timeout;
|
|
|
|
dcache->file_list = file_list;
|
|
|
|
dcache->remote_path = strdup(remote_path);
|
|
|
|
dcache->count = 1;
|
|
|
|
|
|
|
|
if (has_spaces)
|
1998-03-02 21:09:16 +03:00
|
|
|
sock = open_data_connection (bucket, "LIST -la", ".", TYPE_ASCII);
|
1998-02-27 07:54:42 +03:00
|
|
|
else {
|
|
|
|
char *path = copy_strings (remote_path, PATH_SEP_STR, ".", (char *) 0);
|
|
|
|
|
1998-03-02 21:09:16 +03:00
|
|
|
sock = open_data_connection (bucket, "LIST -la", path, TYPE_ASCII);
|
1998-02-27 07:54:42 +03:00
|
|
|
free (path);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sock == -1)
|
|
|
|
goto error_3;
|
|
|
|
|
|
|
|
#ifdef OLD_READ
|
|
|
|
#define close_this_sock(x,y) fclose (x)
|
|
|
|
fp = fdopen(sock, "r");
|
|
|
|
if (fp == NULL) {
|
|
|
|
ftpfserrno = errno;
|
|
|
|
close(sock);
|
|
|
|
goto error_2;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
#define close_this_sock(x,y) close (y)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Clear the interrupt flag */
|
|
|
|
enable_interrupt_key ();
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
#ifdef OLD_READ
|
|
|
|
while (fgets (buffer, sizeof (buffer), fp) != NULL) {
|
|
|
|
if (got_intr = got_interrupt ())
|
|
|
|
break;
|
|
|
|
#else
|
|
|
|
while ((got_intr = get_line_interruptible (buffer, sizeof (buffer), sock)) != EINTR){
|
|
|
|
#endif
|
|
|
|
int eof = got_intr == 0;
|
|
|
|
|
|
|
|
if (ftpfs_debug_server_dialog){
|
|
|
|
fputs (buffer, ftpfs_logfile);
|
|
|
|
fflush (ftpfs_logfile);
|
|
|
|
}
|
|
|
|
if (buffer [0] == 0 && eof)
|
|
|
|
break;
|
|
|
|
fe = xmalloc(sizeof(struct ftpentry), "struct ftpentry");
|
|
|
|
fe->freshly_created = 0;
|
|
|
|
fe->tmp_reget = 0;
|
|
|
|
if (fe == NULL) {
|
|
|
|
ftpfserrno = ENOMEM;
|
|
|
|
goto error_1;
|
|
|
|
}
|
|
|
|
if (parse_ls_lga (buffer, &fe->s, &fe->name, &fe->linkname)) {
|
|
|
|
fe->count = 1;
|
|
|
|
fe->local_filename = fe->remote_filename = NULL;
|
|
|
|
fe->l_stat = NULL;
|
|
|
|
fe->bucket = bucket;
|
|
|
|
(fe->s).st_ino = bucket->__inode_counter++;
|
|
|
|
if (S_ISLNK(fe->s.st_mode))
|
|
|
|
has_symlinks = 1;
|
|
|
|
|
|
|
|
if (!linklist_insert(file_list, fe)) {
|
|
|
|
free(fe);
|
|
|
|
ftpfserrno = ENOMEM;
|
|
|
|
goto error_1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
free(fe);
|
|
|
|
if (eof)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (got_intr){
|
|
|
|
disable_interrupt_key();
|
|
|
|
print_vfs_message("ftpfs: reading FTP directory interrupt by user");
|
|
|
|
#ifdef OLD_READ
|
|
|
|
ftpfs_abort(bucket, fileno(fp));
|
|
|
|
#else
|
|
|
|
ftpfs_abort(bucket, sock);
|
|
|
|
#endif
|
|
|
|
close_this_sock(fp, sock);
|
|
|
|
ftpfserrno = EINTR;
|
|
|
|
goto error_3;
|
|
|
|
}
|
|
|
|
close_this_sock(fp, sock);
|
|
|
|
disable_interrupt_key();
|
|
|
|
if (getreply (qsock (bucket), NULL, 0) != COMPLETE) {
|
|
|
|
ftpfserrno = EIO;
|
|
|
|
goto error_3;
|
|
|
|
}
|
|
|
|
if (file_list->next == file_list) {
|
|
|
|
ftpfserrno = EACCES;
|
|
|
|
goto error_3;
|
|
|
|
}
|
|
|
|
if (!linklist_insert(qdcache(bucket), dcache)) {
|
|
|
|
ftpfserrno = ENOMEM;
|
|
|
|
goto error_3;
|
|
|
|
}
|
|
|
|
if (has_symlinks)
|
|
|
|
resolve_symlink(bucket, dcache);
|
|
|
|
print_vfs_message("ftpfs: got listing");
|
|
|
|
return dcache;
|
|
|
|
error_1:
|
|
|
|
disable_interrupt_key();
|
|
|
|
close_this_sock(fp, sock);
|
|
|
|
#ifdef OLD_READ
|
|
|
|
error_2:
|
|
|
|
#endif
|
|
|
|
getreply(qsock(bucket), NULL, 0);
|
|
|
|
error_3:
|
|
|
|
free(dcache->remote_path);
|
|
|
|
free(dcache);
|
|
|
|
linklist_destroy(file_list, ftpentry_destructor);
|
|
|
|
print_vfs_message("ftpfs: failed");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
store_file(struct ftpentry *fe)
|
|
|
|
{
|
|
|
|
int local_handle, sock, n, total;
|
|
|
|
#ifdef HAVE_STRUCT_LINGER
|
|
|
|
struct linger li;
|
|
|
|
#else
|
|
|
|
int flag_one = 1;
|
|
|
|
#endif
|
|
|
|
char buffer[8192];
|
|
|
|
struct stat s;
|
|
|
|
|
|
|
|
local_handle = open(fe->local_filename, O_RDONLY);
|
|
|
|
unlink (fe->local_filename);
|
|
|
|
if (local_handle == -1) {
|
|
|
|
ftpfserrno = EIO;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
fstat(local_handle, &s);
|
1998-03-02 21:09:16 +03:00
|
|
|
sock = open_data_connection(fe->bucket, "STOR", fe->remote_filename, TYPE_BINARY);
|
1998-02-27 07:54:42 +03:00
|
|
|
if (sock < 0) {
|
|
|
|
close(local_handle);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#ifdef HAVE_STRUCT_LINGER
|
|
|
|
li.l_onoff = 1;
|
|
|
|
li.l_linger = 120;
|
|
|
|
setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof(li));
|
|
|
|
#else
|
|
|
|
setsockopt(sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
|
|
|
|
#endif
|
|
|
|
total = 0;
|
|
|
|
|
|
|
|
enable_interrupt_key();
|
|
|
|
while (1) {
|
|
|
|
while ((n = read(local_handle, buffer, sizeof(buffer))) < 0) {
|
|
|
|
if (errno == EINTR) {
|
|
|
|
if (got_interrupt()) {
|
|
|
|
ftpfserrno = EINTR;
|
|
|
|
goto error_return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
ftpfserrno = errno;
|
|
|
|
goto error_return;
|
|
|
|
}
|
|
|
|
if (n == 0)
|
|
|
|
break;
|
|
|
|
while (write(sock, buffer, n) < 0) {
|
|
|
|
if (errno == EINTR) {
|
|
|
|
if (got_interrupt()) {
|
|
|
|
ftpfserrno = EINTR;
|
|
|
|
goto error_return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
ftpfserrno = errno;
|
|
|
|
goto error_return;
|
|
|
|
}
|
|
|
|
total += n;
|
|
|
|
print_vfs_message("ftpfs: storing file %d (%d)",
|
|
|
|
total, s.st_size);
|
|
|
|
}
|
|
|
|
disable_interrupt_key();
|
|
|
|
close(sock);
|
|
|
|
close(local_handle);
|
|
|
|
if (getreply (qsock (fe->bucket), NULL, 0) != COMPLETE) {
|
|
|
|
ftpfserrno = EIO;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
error_return:
|
|
|
|
disable_interrupt_key();
|
|
|
|
close(sock);
|
|
|
|
close(local_handle);
|
|
|
|
getreply(qsock(fe->bucket), NULL, 0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* These variables are for the _ctl routine */
|
|
|
|
static char *localname = NULL;
|
|
|
|
static struct ftpentry *remoteent;
|
|
|
|
static int remotetotal = 0;
|
|
|
|
static int transfer_started = 0;
|
|
|
|
static char *remotebuffer;
|
|
|
|
static int isremotecopy = 0;
|
|
|
|
static int remotelocal_handle, remotesock, remoten, remotestat_size;
|
|
|
|
|
|
|
|
int retrieve_file_start(struct ftpentry *fe)
|
|
|
|
{
|
|
|
|
if (fe->local_filename == NULL) {
|
|
|
|
ftpfserrno = ENOMEM;
|
|
|
|
return 0;
|
|
|
|
}
|
1998-03-02 21:09:16 +03:00
|
|
|
remotesock = open_data_connection(fe->bucket, "RETR", fe->remote_filename, TYPE_BINARY);
|
1998-02-27 07:54:42 +03:00
|
|
|
if (remotesock == -1) {
|
|
|
|
ftpfserrno = EACCES;
|
|
|
|
free (fe->local_filename);
|
|
|
|
fe->local_filename = NULL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
remotetotal = 0;
|
|
|
|
remoteent = fe;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int retrieve_file_start2(struct ftpentry *fe)
|
|
|
|
{
|
|
|
|
remotelocal_handle = open(fe->local_filename, O_RDWR | O_CREAT | O_TRUNC, 0600);
|
|
|
|
if (remotelocal_handle == -1) {
|
|
|
|
ftpfserrno = EIO;
|
|
|
|
free(fe->local_filename);
|
|
|
|
fe->local_filename = NULL;
|
|
|
|
fe->local_is_temp = 1;
|
|
|
|
close(remotesock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
remotestat_size = fe->s.st_size;
|
|
|
|
remotebuffer = xmalloc (8192, "");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ftpfs_flushdir ()
|
|
|
|
{
|
|
|
|
force_expiration = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ftpfs_ctl (void *data, int ctlop, int arg)
|
|
|
|
{
|
|
|
|
int n = 0;
|
|
|
|
|
|
|
|
switch (ctlop) {
|
|
|
|
case MCCTL_ISREMOTECOPY:
|
|
|
|
return isremotecopy;
|
|
|
|
|
|
|
|
case MCCTL_REMOTECOPYCHUNK:
|
|
|
|
if (!transfer_started)
|
|
|
|
if (!retrieve_file_start2 (remoteent)){
|
|
|
|
return MCERR_TARGETOPEN;
|
|
|
|
} else
|
|
|
|
transfer_started = 1;
|
|
|
|
|
|
|
|
enable_interrupt_key ();
|
|
|
|
if (!remoten) {
|
|
|
|
int v = select_on_two (remotesock, 0);
|
|
|
|
|
|
|
|
if (((v < 0) && (errno == EINTR)) || v == 0){
|
|
|
|
disable_interrupt_key ();
|
|
|
|
return MCERR_DATA_ON_STDIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((n = read(remotesock, remotebuffer, 8192)) < 0){
|
|
|
|
disable_interrupt_key ();
|
|
|
|
if (errno == EINTR)
|
|
|
|
return MCERR_DATA_ON_STDIN;
|
|
|
|
else
|
|
|
|
return MCERR_READ;
|
|
|
|
}
|
|
|
|
if (n == 0) {
|
|
|
|
if (getreply (qsock (remoteent->bucket), NULL, 0) != COMPLETE) {
|
|
|
|
ftpfserrno = EIO;
|
|
|
|
}
|
|
|
|
close(remotelocal_handle);
|
|
|
|
close(remotesock);
|
|
|
|
if (localname){
|
|
|
|
free (localname);
|
|
|
|
localname = NULL;
|
|
|
|
}
|
|
|
|
disable_interrupt_key ();
|
|
|
|
transfer_started = 0;
|
|
|
|
return MCERR_FINISH;
|
|
|
|
}
|
|
|
|
disable_interrupt_key ();
|
|
|
|
remotetotal += n;
|
|
|
|
remoten = n;
|
|
|
|
} else
|
|
|
|
n = remoten;
|
|
|
|
if (write(remotelocal_handle, remotebuffer, remoten) < 0)
|
|
|
|
return MCERR_WRITE;
|
|
|
|
remoten = 0;
|
|
|
|
return n;
|
|
|
|
|
|
|
|
/* We get this message if the transfer was aborted */
|
|
|
|
case MCCTL_FINISHREMOTE:
|
|
|
|
if (localname) {
|
|
|
|
free (localname);
|
|
|
|
localname = NULL;
|
|
|
|
}
|
|
|
|
if (!arg) { /* OK */
|
|
|
|
if (stat (remoteent->local_filename, &remoteent->local_stat) < 0)
|
|
|
|
remoteent->local_stat.st_mtime = 0;
|
|
|
|
} else
|
|
|
|
remoteent->local_stat.st_mtime = 0;
|
|
|
|
transfer_started = 0;
|
|
|
|
ftpfs_abort (remoteent->bucket, remotesock);
|
|
|
|
ftpfserrno = EINTR;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ftpfs_setctl (char *path, int ctlop, char *arg)
|
|
|
|
{
|
|
|
|
switch (ctlop) {
|
|
|
|
case MCCTL_SETREMOTECOPY: if (localname) free (localname);
|
|
|
|
localname = strdup (vfs_canon (arg));
|
|
|
|
return 1;
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int retrieve_file(struct ftpentry *fe)
|
|
|
|
{
|
|
|
|
int total, tmp_reget = do_reget;
|
|
|
|
char buffer[8192];
|
|
|
|
int local_handle, sock, n;
|
|
|
|
|
|
|
|
if (fe->local_filename)
|
|
|
|
return 1;
|
|
|
|
fe->local_stat.st_mtime = 0;
|
|
|
|
fe->local_filename = strdup(tmpnam(NULL));
|
|
|
|
fe->local_is_temp = 1;
|
|
|
|
if (fe->local_filename == NULL) {
|
|
|
|
ftpfserrno = ENOMEM;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
local_handle = open(fe->local_filename, O_RDWR | O_CREAT | O_TRUNC, 0600);
|
|
|
|
if (local_handle == -1) {
|
|
|
|
ftpfserrno = EIO;
|
|
|
|
free(fe->local_filename);
|
|
|
|
fe->local_filename = NULL;
|
|
|
|
return 0;
|
|
|
|
}
|
1998-03-02 21:09:16 +03:00
|
|
|
sock = open_data_connection(fe->bucket, "RETR", fe->remote_filename, TYPE_BINARY);
|
1998-02-27 07:54:42 +03:00
|
|
|
if (sock == -1) {
|
|
|
|
ftpfserrno = EACCES;
|
|
|
|
goto error_3;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear the interrupt status */
|
|
|
|
enable_interrupt_key ();
|
|
|
|
total = 0;
|
|
|
|
if (tmp_reget > 0)
|
|
|
|
total = tmp_reget;
|
|
|
|
else
|
|
|
|
total = 0;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
int stat_size = fe->s.st_size;
|
|
|
|
while ((n = read(sock, buffer, sizeof(buffer))) < 0) {
|
|
|
|
if (errno == EINTR) {
|
|
|
|
if (got_interrupt ()) {
|
|
|
|
disable_interrupt_key();
|
|
|
|
ftpfs_abort(fe->bucket, sock);
|
|
|
|
ftpfserrno = EINTR;
|
|
|
|
goto error_2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
ftpfserrno = errno;
|
|
|
|
disable_interrupt_key();
|
|
|
|
goto error_1;
|
|
|
|
}
|
|
|
|
if (n == 0)
|
|
|
|
break;
|
|
|
|
total += n;
|
|
|
|
if (stat_size == 0)
|
|
|
|
print_vfs_message ("ftpfs: Getting file: %ld bytes transfered",
|
|
|
|
total);
|
|
|
|
else
|
|
|
|
print_vfs_message ("ftpfs: Getting file: %3d%% (%ld bytes transfered)",
|
|
|
|
total*100/stat_size, total);
|
|
|
|
while (write(local_handle, buffer, n) < 0) {
|
|
|
|
if (errno == EINTR) {
|
|
|
|
if (got_interrupt()) {
|
|
|
|
ftpfs_abort(fe->bucket, sock);
|
|
|
|
ftpfserrno = EINTR;
|
|
|
|
goto error_2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
ftpfserrno = errno;
|
|
|
|
goto error_1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
close(local_handle);
|
|
|
|
close(sock);
|
|
|
|
if (getreply (qsock (fe->bucket), NULL, 0) != COMPLETE) {
|
|
|
|
ftpfserrno = EIO;
|
|
|
|
goto error_2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stat (fe->local_filename, &fe->local_stat) < 0)
|
|
|
|
fe->local_stat.st_mtime = 0;
|
|
|
|
|
|
|
|
if (tmp_reget > 0)
|
|
|
|
fe->tmp_reget = 1;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
error_1:
|
|
|
|
getreply(qsock(fe->bucket), NULL, 0);
|
|
|
|
error_2:
|
|
|
|
close(sock);
|
|
|
|
error_3:
|
|
|
|
disable_interrupt_key ();
|
|
|
|
close(local_handle);
|
|
|
|
unlink(fe->local_filename);
|
|
|
|
free(fe->local_filename);
|
|
|
|
fe->local_filename = NULL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct ftpentry *
|
|
|
|
_get_file_entry(struct ftpfs_connection *bucket, char *file_name,
|
|
|
|
int op, int flags)
|
|
|
|
{
|
|
|
|
char *p, q;
|
|
|
|
struct ftpentry *ent;
|
|
|
|
struct linklist *file_list, *lptr;
|
|
|
|
struct ftpfs_dir *dcache;
|
|
|
|
struct stat sb;
|
|
|
|
|
|
|
|
p = strrchr(file_name, '/');
|
|
|
|
q = *p;
|
|
|
|
*p = '\0';
|
|
|
|
dcache = retrieve_dir(bucket, *file_name ? file_name : "/");
|
|
|
|
if (dcache == NULL)
|
|
|
|
return NULL;
|
|
|
|
file_list = dcache->file_list;
|
|
|
|
*p++ = q;
|
|
|
|
if (!*p)
|
|
|
|
p = ".";
|
|
|
|
for (lptr = file_list->next; lptr != file_list; lptr = lptr->next) {
|
|
|
|
ent = lptr->data;
|
|
|
|
if (strcmp(p, ent->name) == 0) {
|
|
|
|
if (S_ISLNK(ent->s.st_mode) && (op & FTPFS_RESOLVE_SYMLINK)) {
|
|
|
|
if (ent->l_stat == NULL) {
|
|
|
|
ftpfserrno = ENOENT;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (S_ISLNK(ent->l_stat->st_mode)) {
|
|
|
|
ftpfserrno = ELOOP;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ent && (op & FTPFS_OPEN)) {
|
|
|
|
mode_t fmode;
|
|
|
|
|
|
|
|
fmode = S_ISLNK(ent->s.st_mode)
|
|
|
|
? ent->l_stat->st_mode
|
|
|
|
: ent->s.st_mode;
|
|
|
|
if (S_ISDIR(fmode)) {
|
|
|
|
ftpfserrno = EISDIR;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (!S_ISREG(fmode)) {
|
|
|
|
ftpfserrno = EPERM;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if ((flags & O_EXCL) && (flags & O_CREAT)) {
|
|
|
|
ftpfserrno = EEXIST;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (ent->remote_filename == NULL) {
|
|
|
|
ent->remote_filename = strdup(file_name);
|
|
|
|
if (ent->remote_filename == NULL) {
|
|
|
|
ftpfserrno = ENOMEM;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ent->local_filename == NULL ||
|
|
|
|
!ent->local_stat.st_mtime ||
|
|
|
|
stat (ent->local_filename, &sb) < 0 ||
|
|
|
|
sb.st_mtime != ent->local_stat.st_mtime) {
|
|
|
|
int handle;
|
|
|
|
|
|
|
|
if (ent->local_filename){
|
|
|
|
free (ent->local_filename);
|
|
|
|
ent->local_filename = 0;
|
|
|
|
}
|
|
|
|
if (flags & O_TRUNC) {
|
|
|
|
ent->local_filename = strdup(tmpnam(NULL));
|
|
|
|
if (ent->local_filename == NULL) {
|
|
|
|
ftpfserrno = ENOMEM;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
handle = open(ent->local_filename, O_CREAT | O_TRUNC |
|
|
|
|
O_RDWR, 0600);
|
|
|
|
if (handle < 0) {
|
|
|
|
ftpfserrno = EIO;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
close(handle);
|
|
|
|
if (stat (ent->local_filename, &ent->local_stat) < 0)
|
|
|
|
ent->local_stat.st_mtime = 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (localname != NULL) {
|
|
|
|
isremotecopy = 1;
|
|
|
|
ent->local_is_temp = 0;
|
|
|
|
ent->local_stat.st_mtime = 0;
|
|
|
|
ent->local_filename = strdup (localname);
|
|
|
|
if (!retrieve_file_start (ent)) {
|
|
|
|
isremotecopy = 0;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return ent;
|
|
|
|
}
|
|
|
|
if (!retrieve_file(ent))
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (flags & O_TRUNC) {
|
|
|
|
truncate(ent->local_filename, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((op & FTPFS_OPEN) && (flags & O_CREAT)) {
|
|
|
|
int handle;
|
|
|
|
|
|
|
|
ent = xmalloc(sizeof(struct ftpentry), "struct ftpentry");
|
|
|
|
ent->freshly_created = 0;
|
|
|
|
ent->tmp_reget = 0;
|
|
|
|
if (ent == NULL) {
|
|
|
|
ftpfserrno = ENOMEM;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
ent->count = 1;
|
|
|
|
ent->linkname = NULL;
|
|
|
|
ent->l_stat = NULL;
|
|
|
|
ent->bucket = bucket;
|
|
|
|
ent->name = strdup(p);
|
|
|
|
ent->remote_filename = strdup(file_name);
|
|
|
|
ent->local_filename = strdup(tmpnam(NULL));
|
|
|
|
if (!ent->name && !ent->remote_filename && !ent->local_filename) {
|
|
|
|
ftpentry_destructor(ent);
|
|
|
|
ftpfserrno = ENOMEM;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
handle = creat(ent->local_filename, 0700);
|
|
|
|
if (handle == -1) {
|
|
|
|
ftpfserrno = EIO;
|
|
|
|
ftpentry_destructor(ent);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
fstat(handle, &ent->s);
|
|
|
|
close(handle);
|
|
|
|
#if 0
|
|
|
|
/* This is very wrong - like this a zero length file will be always created
|
|
|
|
and usually preclude uploading anything more desirable */
|
|
|
|
#if defined(UPLOAD_ZERO_LENGTH_FILE)
|
|
|
|
if (!store_file(ent)) {
|
|
|
|
ftpentry_destructor(ent);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
if (!linklist_insert(file_list, ent)) {
|
|
|
|
ftpfserrno = ENOMEM;
|
|
|
|
ftpentry_destructor(ent);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
ent->freshly_created = 1;
|
|
|
|
return ent;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ftpfserrno = ENOENT;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct ftpentry *
|
|
|
|
get_file_entry(char *path, int op, int flags)
|
|
|
|
{
|
|
|
|
struct ftpfs_connection *bucket;
|
|
|
|
struct ftpentry *fe;
|
|
|
|
char *remote_path;
|
|
|
|
|
|
|
|
if (!(remote_path = ftpfs_get_path (&bucket, path)))
|
|
|
|
return NULL;
|
|
|
|
isremotecopy = 0;
|
|
|
|
fe = _get_file_entry(bucket, remote_path, op,
|
|
|
|
flags);
|
|
|
|
free(remote_path);
|
|
|
|
#if 0
|
|
|
|
if (op & FTPFS_FREE_RESOURCE)
|
|
|
|
vfs_add_noncurrent_stamps (&ftpfs_vfs_ops, (vfsid) bucket, NULL);
|
|
|
|
#endif
|
|
|
|
return fe;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define OPT_FLUSH 1
|
|
|
|
#define OPT_IGNORE_ERROR 2
|
|
|
|
|
|
|
|
static int ftpfs_normal_flush = 1;
|
|
|
|
|
|
|
|
void ftpfs_hint_reread(int reread)
|
|
|
|
{
|
|
|
|
if (reread)
|
|
|
|
ftpfs_normal_flush++;
|
|
|
|
else
|
|
|
|
ftpfs_normal_flush--;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
send_ftp_command(char *filename, char *cmd, int flags)
|
|
|
|
{
|
|
|
|
char *remote_path;
|
|
|
|
struct ftpfs_connection *bucket;
|
|
|
|
int r;
|
|
|
|
int flush_directory_cache = (flags & OPT_FLUSH) && (ftpfs_normal_flush > 0);
|
|
|
|
|
|
|
|
if (!(remote_path = ftpfs_get_path(&bucket, filename)))
|
|
|
|
return -1;
|
|
|
|
r = command (bucket, WAIT_REPLY, cmd, remote_path);
|
|
|
|
free(remote_path);
|
|
|
|
vfs_add_noncurrent_stamps (&ftpfs_vfs_ops, (vfsid) bucket, NULL);
|
|
|
|
if (flags & OPT_IGNORE_ERROR)
|
|
|
|
r = COMPLETE;
|
|
|
|
if (r != COMPLETE) {
|
|
|
|
ftpfserrno = EPERM;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (flush_directory_cache)
|
|
|
|
flush_all_directory(bucket);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This routine is called as the last step in load_setup */
|
|
|
|
void
|
|
|
|
ftpfs_init_passwd(void)
|
|
|
|
{
|
|
|
|
struct passwd *passwd_info;
|
|
|
|
char *p, hostname[MAXHOSTNAMELEN];
|
|
|
|
struct hostent *hp;
|
|
|
|
|
|
|
|
ftpfs_anonymous_passwd = load_anon_passwd ();
|
|
|
|
if (ftpfs_anonymous_passwd)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ((passwd_info = getpwuid (geteuid ())) == NULL)
|
|
|
|
p = "guest";
|
|
|
|
else
|
|
|
|
p = passwd_info->pw_name;
|
|
|
|
gethostname(hostname, sizeof(hostname));
|
|
|
|
hp = gethostbyname(hostname);
|
|
|
|
if (hp != NULL)
|
|
|
|
ftpfs_anonymous_passwd = copy_strings (p, "@", hp->h_name, NULL);
|
|
|
|
else
|
|
|
|
ftpfs_anonymous_passwd = copy_strings (p, "@", hostname, NULL);
|
|
|
|
endpwent ();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ftpfs_init ()
|
|
|
|
{
|
|
|
|
ftpfs_connections_list = linklist_init();
|
|
|
|
ftpfs_directory_timeout = FTPFS_DIRECTORY_TIMEOUT;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ftpfs_done(void)
|
|
|
|
{
|
|
|
|
linklist_destroy(ftpfs_connections_list, ftpfs_connection_destructor);
|
|
|
|
if (ftpfs_debug_server_dialog)
|
|
|
|
fclose (ftpfs_logfile);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The callbacks */
|
|
|
|
|
|
|
|
struct ftpfs_filp {
|
|
|
|
unsigned int has_changed:1;
|
|
|
|
struct ftpentry *fe;
|
|
|
|
int local_handle;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void *ftpfs_open (char *file, int flags, int mode)
|
|
|
|
{
|
|
|
|
struct ftpfs_filp *fp;
|
|
|
|
struct ftpentry *fe;
|
|
|
|
|
|
|
|
fp = xmalloc(sizeof(struct ftpfs_filp), "struct ftpfs_filp");
|
|
|
|
if (fp == NULL) {
|
|
|
|
ftpfserrno = ENOMEM;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
fe = get_file_entry(file, FTPFS_OPEN | FTPFS_RESOLVE_SYMLINK, flags);
|
|
|
|
if (fe == NULL) {
|
|
|
|
free(fp);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (!isremotecopy) {
|
|
|
|
fp->local_handle = open(fe->local_filename, flags, mode);
|
|
|
|
if (fp->local_handle < 0) {
|
|
|
|
ftpfserrno = errno;
|
|
|
|
free(fp);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
fp->local_handle = -1;
|
|
|
|
#ifdef UPLOAD_ZERO_LENGTH_FILE
|
|
|
|
fp->has_changed = fe->freshly_created;
|
|
|
|
#else
|
|
|
|
fp->has_changed = 0;
|
|
|
|
#endif
|
|
|
|
fp->fe = fe;
|
|
|
|
qlock(fe->bucket)++;
|
|
|
|
fe->count++;
|
|
|
|
return fp;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ftpfs_read (void *data, char *buffer, int count)
|
|
|
|
{
|
|
|
|
struct ftpfs_filp *fp;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
fp = data;
|
|
|
|
n = read(fp->local_handle, buffer, count);
|
|
|
|
if (n < 0)
|
|
|
|
ftpfserrno = errno;
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ftpfs_write (void *data, char *buf, int nbyte)
|
|
|
|
{
|
|
|
|
struct ftpfs_filp *fp;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
fp = data;
|
|
|
|
n = write(fp->local_handle, buf, nbyte);
|
|
|
|
if (n < 0)
|
|
|
|
ftpfserrno = errno;
|
|
|
|
fp->has_changed = 1;
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ftpfs_close (void *data)
|
|
|
|
{
|
|
|
|
struct ftpfs_filp *fp = data;
|
|
|
|
int result = 0;
|
|
|
|
|
|
|
|
if (fp->has_changed) {
|
|
|
|
if (!store_file(fp->fe))
|
|
|
|
result = -1;
|
|
|
|
if (ftpfs_normal_flush)
|
|
|
|
flush_all_directory(fp->fe->bucket);
|
|
|
|
}
|
|
|
|
if (fp->local_handle >= 0)
|
|
|
|
close(fp->local_handle);
|
|
|
|
qlock(fp->fe->bucket)--;
|
|
|
|
ftpentry_destructor(fp->fe);
|
|
|
|
free(fp);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ftpfs_errno (void)
|
|
|
|
{
|
|
|
|
return ftpfserrno;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Explanation:
|
|
|
|
* On some operating systems (Slowaris 2 for example)
|
|
|
|
* the d_name member is just a char long (Nice trick that break everything,
|
|
|
|
* so we need to set up some space for the filename.
|
|
|
|
*/
|
|
|
|
struct ftpfs_dirent {
|
|
|
|
struct dirent dent;
|
|
|
|
#ifdef NEED_EXTRA_DIRENT_BUFFER
|
|
|
|
char extra_buffer [MC_MAXPATHLEN];
|
|
|
|
#endif
|
|
|
|
struct linklist *pos;
|
|
|
|
struct ftpfs_dir *dcache;
|
|
|
|
};
|
|
|
|
|
|
|
|
char *ftpfs_gethome (char *servername)
|
|
|
|
{
|
|
|
|
struct ftpfs_connection *bucket;
|
|
|
|
char *remote_path;
|
|
|
|
|
|
|
|
if (!(remote_path = ftpfs_get_path (&bucket, servername)))
|
|
|
|
return NULL;
|
|
|
|
free (remote_path);
|
|
|
|
return qhome(bucket);
|
|
|
|
}
|
|
|
|
|
|
|
|
char *ftpfs_getupdir (char *servername)
|
|
|
|
{
|
|
|
|
struct ftpfs_connection *bucket;
|
|
|
|
char *remote_path;
|
|
|
|
|
|
|
|
if (!(remote_path = ftpfs_get_path (&bucket, servername)))
|
|
|
|
return NULL;
|
|
|
|
free (remote_path);
|
|
|
|
return qupdir(bucket);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *ftpfs_opendir (char *dirname)
|
|
|
|
{
|
|
|
|
struct ftpfs_connection *bucket;
|
|
|
|
char *remote_path;
|
|
|
|
struct ftpfs_dirent *dirp;
|
|
|
|
|
|
|
|
if (!(remote_path = ftpfs_get_path (&bucket, dirname)))
|
|
|
|
return NULL;
|
|
|
|
dirp = xmalloc(sizeof(struct ftpfs_dirent), "struct ftpfs_dirent");
|
|
|
|
if (dirp == NULL) {
|
|
|
|
ftpfserrno = ENOMEM;
|
|
|
|
goto error_return;
|
|
|
|
}
|
|
|
|
dirp->dcache = retrieve_dir(bucket, remote_path);
|
|
|
|
if (dirp->dcache == NULL)
|
|
|
|
goto error_return;
|
|
|
|
dirp->pos = dirp->dcache->file_list->next;
|
|
|
|
free(remote_path);
|
|
|
|
dirp->dcache->count++;
|
|
|
|
return (void *)dirp;
|
|
|
|
error_return:
|
|
|
|
vfs_add_noncurrent_stamps (&ftpfs_vfs_ops, (vfsid) bucket, NULL);
|
|
|
|
free(remote_path);
|
|
|
|
free(dirp);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *ftpfs_readdir (void *data)
|
|
|
|
{
|
|
|
|
struct ftpentry *fe;
|
|
|
|
struct ftpfs_dirent *dirp = data;
|
|
|
|
|
|
|
|
if (dirp->pos == dirp->dcache->file_list)
|
|
|
|
return NULL;
|
|
|
|
fe = dirp->pos->data;
|
|
|
|
strcpy (&(dirp->dent.d_name [0]), fe->name);
|
|
|
|
#ifndef DIRENT_LENGTH_COMPUTED
|
|
|
|
dirp->d_namlen = strlen (dirp->d_name);
|
|
|
|
#endif
|
|
|
|
dirp->pos = dirp->pos->next;
|
|
|
|
return (void *) &dirp->dent;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ftpfs_closedir (void *info)
|
|
|
|
{
|
|
|
|
struct ftpfs_dirent *dirp = info;
|
|
|
|
ftpfs_dir_destructor(dirp->dcache);
|
|
|
|
free(dirp);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ftpfs_lstat (char *path, struct stat *buf)
|
|
|
|
{
|
|
|
|
struct ftpentry *fe;
|
|
|
|
|
|
|
|
fe = get_file_entry(path, FTPFS_FREE_RESOURCE, 0);
|
|
|
|
if (fe) {
|
|
|
|
*buf = fe->s;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ftpfs_stat (char *path, struct stat *buf)
|
|
|
|
{
|
|
|
|
struct ftpentry *fe;
|
|
|
|
|
|
|
|
fe = get_file_entry(path, FTPFS_RESOLVE_SYMLINK | FTPFS_FREE_RESOURCE, 0);
|
|
|
|
if (fe) {
|
|
|
|
if (!S_ISLNK(fe->s.st_mode))
|
|
|
|
*buf = fe->s;
|
|
|
|
else
|
|
|
|
*buf = *fe->l_stat;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ftpfs_fstat (void *data, struct stat *buf)
|
|
|
|
{
|
|
|
|
struct ftpfs_filp *fp = data;
|
|
|
|
|
|
|
|
if (!S_ISLNK(fp->fe->s.st_mode))
|
|
|
|
*buf = fp->fe->s;
|
|
|
|
else
|
|
|
|
*buf = *fp->fe->l_stat;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ftpfs_chmod (char *path, int mode)
|
|
|
|
{
|
|
|
|
char buf[40];
|
|
|
|
|
|
|
|
sprintf(buf, "SITE CHMOD %4.4o %%s", mode & 07777);
|
|
|
|
return send_ftp_command(path, buf, OPT_IGNORE_ERROR | OPT_FLUSH);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ftpfs_chown (char *path, int owner, int group)
|
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
ftpfserrno = EPERM;
|
|
|
|
return -1;
|
|
|
|
#else
|
|
|
|
/* Everyone knows it is not possible to chown remotely, so why bother them.
|
|
|
|
If someone's root, then copy/move will always try to chown it... */
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ftpfs_readlink (char *path, char *buf, int size)
|
|
|
|
{
|
|
|
|
struct ftpentry *fe;
|
|
|
|
|
|
|
|
fe = get_file_entry(path, FTPFS_FREE_RESOURCE, 0);
|
|
|
|
if (!fe)
|
|
|
|
return -1;
|
|
|
|
if (!S_ISLNK(fe->s.st_mode)) {
|
|
|
|
ftpfserrno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (fe->linkname == NULL) {
|
|
|
|
ftpfserrno = EACCES;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (strlen(fe->linkname) >= size) {
|
|
|
|
ftpfserrno = ERANGE;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
strncpy(buf, fe->linkname, size);
|
|
|
|
return strlen(fe->linkname);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ftpfs_unlink (char *path)
|
|
|
|
{
|
|
|
|
return send_ftp_command(path, "DELE %s", 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ftpfs_symlink (char *n1, char *n2)
|
|
|
|
{
|
|
|
|
ftpfserrno = EPERM;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ftpfs_rename (char *path1, char *path2)
|
|
|
|
{
|
|
|
|
ftpfserrno = EPERM;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
__ftpfs_chdir (struct ftpfs_connection *bucket ,char *remote_path)
|
|
|
|
{
|
|
|
|
#ifdef ARE_WE_SURE_WE_DONT_NEED_THIS_P
|
|
|
|
int r;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (ftpfs_same_dir (remote_path, bucket))
|
|
|
|
return COMPLETE;
|
|
|
|
|
|
|
|
#ifdef ARE_WE_SURE_WE_DONT_NEED_THIS_P
|
|
|
|
r = command (bucket, WAIT_REPLY, "CWD %s", remote_path);
|
|
|
|
if (r != COMPLETE) {
|
|
|
|
ftpfserrno = EIO;
|
|
|
|
} else {
|
|
|
|
if (qcdir(bucket))
|
|
|
|
free(qcdir(bucket));
|
|
|
|
qcdir(bucket) = strdup (remote_path);
|
|
|
|
}
|
|
|
|
return r;
|
|
|
|
#else
|
|
|
|
|
|
|
|
if (qcdir(bucket))
|
|
|
|
free(qcdir(bucket));
|
|
|
|
qcdir(bucket) = strdup (remote_path);
|
|
|
|
|
|
|
|
return COMPLETE;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ftpfs_chdir (char *path)
|
|
|
|
{
|
|
|
|
char *remote_path;
|
|
|
|
struct ftpfs_connection *bucket;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
retry:
|
|
|
|
if (!(remote_path = ftpfs_get_path(&bucket, path)))
|
|
|
|
return -1;
|
|
|
|
got_sigpipe = 0;
|
|
|
|
r = __ftpfs_chdir (bucket, remote_path);
|
|
|
|
free (remote_path);
|
|
|
|
if (got_sigpipe)
|
|
|
|
goto retry;
|
|
|
|
|
|
|
|
vfs_add_noncurrent_stamps (&ftpfs_vfs_ops, (vfsid) bucket, NULL);
|
|
|
|
return r == COMPLETE ? 0 : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ftpfs_lseek (void *data, off_t offset, int whence)
|
|
|
|
{
|
|
|
|
struct ftpfs_filp *fp = data;
|
|
|
|
|
|
|
|
return lseek(fp->local_handle, offset, whence);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ftpfs_mknod (char *path, int mode, int dev)
|
|
|
|
{
|
|
|
|
ftpfserrno = EPERM;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ftpfs_mkdir (char *path, mode_t mode)
|
|
|
|
{
|
|
|
|
return send_ftp_command(path, "MKD %s", 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ftpfs_rmdir (char *path)
|
|
|
|
{
|
|
|
|
return send_ftp_command(path, "RMD %s", 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ftpfs_link (char *p1, char *p2)
|
|
|
|
{
|
|
|
|
ftpfserrno = EPERM;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static vfsid ftpfs_getid (char *p, struct vfs_stamping **parent)
|
|
|
|
{
|
|
|
|
struct ftpfs_connection *bucket;
|
|
|
|
char *remote_path;
|
|
|
|
|
|
|
|
*parent = NULL; /* We are not enclosed in any other fs */
|
|
|
|
|
|
|
|
if (!(remote_path = ftpfs_get_path (&bucket, p)))
|
|
|
|
return (vfsid) -1;
|
|
|
|
else {
|
|
|
|
free(remote_path);
|
|
|
|
return (vfsid) bucket;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ftpfs_nothingisopen (vfsid id)
|
|
|
|
{
|
|
|
|
return qlock((struct ftpfs_connection *)id) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ftpfs_free (vfsid id)
|
|
|
|
{
|
|
|
|
struct ftpfs_connection *bucket = (struct ftpfs_connection *) id;
|
|
|
|
|
|
|
|
ftpfs_connection_destructor(bucket);
|
|
|
|
linklist_delete(ftpfs_connections_list, bucket);
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *ftpfs_getlocalcopy (char *path)
|
|
|
|
{
|
|
|
|
struct ftpfs_filp *fp = (struct ftpfs_filp *) ftpfs_open (path, O_RDONLY, 0);
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
if (fp == NULL)
|
|
|
|
return NULL;
|
|
|
|
if (fp->fe->local_filename == NULL) {
|
|
|
|
ftpfs_close ((void *) fp);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
p = strdup (fp->fe->local_filename);
|
|
|
|
qlock(fp->fe->bucket)++;
|
|
|
|
fp->fe->count++;
|
|
|
|
ftpfs_close ((void *) fp);
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ftpfs_ungetlocalcopy (char *path, char *local, int has_changed)
|
|
|
|
{
|
|
|
|
struct ftpfs_filp *fp = (struct ftpfs_filp *) ftpfs_open (path, O_WRONLY, 0);
|
|
|
|
|
|
|
|
if (fp == NULL)
|
|
|
|
return;
|
|
|
|
if (!strcmp (fp->fe->local_filename, local)) {
|
|
|
|
fp->has_changed = has_changed;
|
|
|
|
qlock(fp->fe->bucket)--;
|
|
|
|
ftpentry_destructor(fp->fe);
|
|
|
|
ftpfs_close ((void *) fp);
|
|
|
|
} else {
|
|
|
|
/* Should not happen */
|
|
|
|
ftpfs_close ((void *) fp);
|
|
|
|
mc_def_ungetlocalcopy (path, local, has_changed);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ftpfs_set_debug (char *file)
|
|
|
|
{
|
|
|
|
if ((ftpfs_logfile = fopen (file, "w+")) != NULL)
|
|
|
|
ftpfs_debug_server_dialog = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ftpfs_forget (char *file)
|
|
|
|
{
|
|
|
|
struct linklist *l;
|
|
|
|
char *host, *user, *pass, *rp;
|
|
|
|
int port;
|
|
|
|
|
|
|
|
if (strncmp (file, "ftp://", 6) != 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
file += 6;
|
|
|
|
if (!(rp = ftpfs_get_host_and_username (file, &host, &user, &port, &pass))) {
|
|
|
|
free (host);
|
|
|
|
free (user);
|
|
|
|
if (pass)
|
|
|
|
wipe_password (pass);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* we do not care about the path actually */
|
|
|
|
free (rp);
|
|
|
|
|
|
|
|
for (l = ftpfs_connections_list->next; l != ftpfs_connections_list; l = l->next){
|
|
|
|
struct ftpfs_connection *bucket = l->data;
|
|
|
|
|
|
|
|
if ((strcmp (host, qhost (bucket)) == 0) &&
|
|
|
|
(strcmp (user, quser (bucket)) == 0) &&
|
|
|
|
(port == qport (bucket))){
|
|
|
|
|
|
|
|
/* close socket: the child owns it now */
|
|
|
|
close (bucket->sock);
|
|
|
|
bucket->sock = -1;
|
|
|
|
|
|
|
|
/* reopen the connection */
|
|
|
|
bucket->sock = ftpfs_open_socket (bucket);
|
|
|
|
if (bucket->sock != -1)
|
|
|
|
login_server (bucket, pass);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free (host);
|
|
|
|
free (user);
|
|
|
|
if (pass)
|
|
|
|
wipe_password (pass);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_MMAP
|
|
|
|
caddr_t ftpfs_mmap (caddr_t addr, size_t len, int prot, int flags, void *data, off_t offset)
|
|
|
|
{
|
|
|
|
return (caddr_t)-1; /* We do not mmap to far away */
|
|
|
|
}
|
|
|
|
|
|
|
|
int ftpfs_munmap (caddr_t addr, size_t len, void *data)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
vfs ftpfs_vfs_ops = {
|
|
|
|
ftpfs_open,
|
|
|
|
ftpfs_close,
|
|
|
|
ftpfs_read,
|
|
|
|
ftpfs_write,
|
|
|
|
|
|
|
|
ftpfs_opendir,
|
|
|
|
ftpfs_readdir,
|
|
|
|
ftpfs_closedir,
|
|
|
|
|
|
|
|
ftpfs_stat,
|
|
|
|
ftpfs_lstat,
|
|
|
|
ftpfs_fstat,
|
|
|
|
|
|
|
|
ftpfs_chmod,
|
|
|
|
ftpfs_chown,
|
|
|
|
NULL,
|
|
|
|
|
|
|
|
ftpfs_readlink,
|
|
|
|
ftpfs_symlink,
|
|
|
|
ftpfs_link,
|
|
|
|
ftpfs_unlink,
|
|
|
|
|
|
|
|
ftpfs_rename,
|
|
|
|
ftpfs_chdir,
|
|
|
|
ftpfs_errno,
|
|
|
|
ftpfs_lseek,
|
|
|
|
ftpfs_mknod,
|
|
|
|
|
|
|
|
ftpfs_getid,
|
|
|
|
ftpfs_nothingisopen,
|
|
|
|
ftpfs_free,
|
|
|
|
|
|
|
|
ftpfs_getlocalcopy,
|
|
|
|
ftpfs_ungetlocalcopy,
|
|
|
|
|
|
|
|
ftpfs_mkdir,
|
|
|
|
ftpfs_rmdir,
|
|
|
|
ftpfs_ctl,
|
|
|
|
ftpfs_setctl,
|
|
|
|
ftpfs_forget
|
|
|
|
#ifdef HAVE_MMAP
|
|
|
|
, ftpfs_mmap,
|
|
|
|
ftpfs_munmap
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
#ifdef USE_NETRC
|
|
|
|
static char buffer[100];
|
|
|
|
static char *netrc, *netrcp;
|
|
|
|
|
|
|
|
static int netrc_next (void)
|
|
|
|
{
|
|
|
|
char *p;
|
|
|
|
int i;
|
|
|
|
static char *keywords [] = { "default", "machine",
|
|
|
|
"login", "password", "passwd",
|
|
|
|
"account", "macdef" };
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
netrcp = skip_separators (netrcp);
|
|
|
|
if (*netrcp != '\n')
|
|
|
|
break;
|
|
|
|
netrcp++;
|
|
|
|
}
|
|
|
|
if (!*netrcp)
|
|
|
|
return 0;
|
|
|
|
p = buffer;
|
|
|
|
if (*netrcp == '"') {
|
|
|
|
for (;*netrcp != '"' && *netrcp; netrcp++) {
|
|
|
|
if (*netrcp == '\\')
|
|
|
|
netrcp++;
|
|
|
|
*p++ = *netrcp;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (;*netrcp != '\n' && *netrcp != '\t' && *netrcp != ' ' &&
|
|
|
|
*netrcp != ',' && *netrcp; netrcp++) {
|
|
|
|
if (*netrcp == '\\')
|
|
|
|
netrcp++;
|
|
|
|
*p++ = *netrcp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*p = 0;
|
|
|
|
if (!*buffer)
|
|
|
|
return 0;
|
|
|
|
for (i = 0; i < sizeof (keywords) / sizeof (keywords [0]); i++)
|
|
|
|
if (!strcmp (keywords [i], buffer))
|
|
|
|
break;
|
|
|
|
return i + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int lookup_netrc (char *host, char **login, char **pass)
|
|
|
|
{
|
|
|
|
char *netrcname, *tmp;
|
|
|
|
char hostname[MAXHOSTNAMELEN], *domain;
|
|
|
|
int keyword;
|
|
|
|
struct stat mystat;
|
|
|
|
static int be_angry = 1;
|
|
|
|
static struct rupcache {
|
|
|
|
struct rupcache *next;
|
|
|
|
char *host;
|
|
|
|
char *login;
|
|
|
|
char *pass;
|
|
|
|
} *rup_cache = NULL, *rupp;
|
|
|
|
|
|
|
|
for (rupp = rup_cache; rupp != NULL; rupp = rupp->next)
|
|
|
|
if (!strcmp (host, rupp->host)) {
|
|
|
|
if (rupp->login != NULL)
|
|
|
|
*login = strdup (rupp->login);
|
|
|
|
if (rupp->pass != NULL)
|
|
|
|
*pass = strdup (rupp->pass);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
netrcname = xmalloc (strlen (home_dir) + strlen ("/.netrc") + 1, "netrc");
|
|
|
|
strcpy (netrcname, home_dir);
|
|
|
|
strcat (netrcname, "/.netrc");
|
|
|
|
netrcp = netrc = load_file (netrcname);
|
|
|
|
if (netrc == NULL) {
|
|
|
|
free (netrcname);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (gethostname (hostname, sizeof (hostname)) < 0)
|
|
|
|
*hostname = 0;
|
|
|
|
if (!(domain = strchr (hostname, '.')))
|
|
|
|
domain = "";
|
|
|
|
|
|
|
|
while ((keyword = netrc_next ())) {
|
|
|
|
if (keyword == 2) {
|
|
|
|
if (netrc_next () != 8)
|
|
|
|
continue;
|
|
|
|
if (strcasecmp (host, buffer) &&
|
|
|
|
((tmp = strchr (host, '.')) == NULL ||
|
|
|
|
strcasecmp (tmp, domain) ||
|
|
|
|
strncasecmp (host, buffer, tmp - host) ||
|
|
|
|
buffer [tmp - host]))
|
|
|
|
continue;
|
|
|
|
} else if (keyword != 1)
|
|
|
|
continue;
|
|
|
|
while ((keyword = netrc_next ()) > 2) {
|
|
|
|
switch (keyword) {
|
|
|
|
case 3:
|
|
|
|
if (netrc_next ())
|
|
|
|
if (*login == NULL)
|
|
|
|
*login = strdup (buffer);
|
|
|
|
else if (strcmp (*login, buffer))
|
|
|
|
keyword = 20;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
case 5:
|
|
|
|
if (strcmp (*login, "anonymous") && strcmp (*login, "ftp") &&
|
|
|
|
stat (netrcname, &mystat) >= 0 &&
|
|
|
|
(mystat.st_mode & 077)) {
|
|
|
|
if (be_angry) {
|
|
|
|
message_1s (1, "Error", "~/.netrc file has not correct mode.\n"
|
|
|
|
"Remove password or correct mode.");
|
|
|
|
be_angry = 0;
|
|
|
|
}
|
|
|
|
free (netrc);
|
|
|
|
free (netrcname);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (netrc_next () && *pass == NULL)
|
|
|
|
*pass = strdup (buffer);
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
if (stat (netrcname, &mystat) >= 0 &&
|
|
|
|
(mystat.st_mode & 077)) {
|
|
|
|
if (be_angry) {
|
|
|
|
message_1s (1, "Error", "~/.netrc file has not correct mode.\n"
|
|
|
|
"Remove password or correct mode.");
|
|
|
|
be_angry = 0;
|
|
|
|
}
|
|
|
|
free (netrc);
|
|
|
|
free (netrcname);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
netrc_next ();
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
for (;;) {
|
|
|
|
while (*netrcp != '\n' && *netrcp);
|
|
|
|
if (*netrcp != '\n')
|
|
|
|
break;
|
|
|
|
netrcp++;
|
|
|
|
if (*netrcp == '\n' || !*netrcp)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (keyword == 20)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (keyword == 20)
|
|
|
|
continue;
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
rupp = (struct rupcache *) xmalloc (sizeof (struct rupcache), "");
|
|
|
|
rupp->host = strdup (host);
|
|
|
|
rupp->login = rupp->pass = 0;
|
|
|
|
|
|
|
|
if (*login != NULL)
|
|
|
|
rupp->login = strdup (*login);
|
|
|
|
if (*pass != NULL)
|
|
|
|
rupp->pass = strdup (*pass);
|
|
|
|
rupp->next = rup_cache;
|
|
|
|
rup_cache = rupp;
|
|
|
|
|
|
|
|
free (netrc);
|
|
|
|
free (netrcname);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef HAVE_STRNCASECMP
|
|
|
|
int strncasecmp (char *s, char *d, int l)
|
|
|
|
{
|
|
|
|
int result;
|
|
|
|
|
|
|
|
while (l--){
|
|
|
|
if (result = (0x20 | *s) - (0x20 | *d))
|
|
|
|
break;
|
|
|
|
if (!*s)
|
|
|
|
return 0;
|
|
|
|
s++;
|
|
|
|
d++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif /* USE_NETRC */
|