/* pfs-ftp.c:
*
****************************************************************
* Copyright (C) 2002, 2003 Scott Parish
* Copyright (C) 2003 Tom Lord
*
* See the file "COPYING" for further information about
* the copyright and warranty status of this work.
*/
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include "config-options.h"
#include "hackerlab/bugs/panic.h"
#include "hackerlab/os/errno-to-string.h"
#include "hackerlab/mem/alloc-limits.h"
#include "hackerlab/mem/mem.h"
#include "hackerlab/arrays/ar.h"
#include "hackerlab/char/char-class.h"
#include "hackerlab/char/str.h"
#include "hackerlab/fmt/cvt.h"
#include "hackerlab/fs/file-names.h"
#include "hackerlab/vu-network/url-socket.h"
#include "hackerlab/vu/safe.h"
#include "libfsutils/string-files.h"
#include "libfsutils/copy-file.h"
#include "libfsutils/tmp-files.h"
#include "libfsutils/file-contents.h"
#include "libawk/trim.h"
#include "libarch/archives.h"
#include "libarch/pfs-ftp.h"
struct arch_pfs_ftp_session
{
struct arch_pfs_session pfs;
char * cwd;
int in_fd;
int out_fd;
int is_wu;
t_uchar * remote_pwd;
};
/* __STDC__ prototypes for static functions */
static void fs_disconnect (struct arch_pfs_session **pfs);
static int pfs_ftp_file_exists (struct arch_pfs_session * p, t_uchar * path);
static int pfs_ftp_is_dir (struct arch_pfs_session * p, t_uchar * path);
static t_uchar * pfs_ftp_file_contents (struct arch_pfs_session * p, t_uchar * path, int soft_errors);
static int pfs_ftp_get_file (struct arch_pfs_session * p, int file_out_fd, t_uchar * path, int soft_errors);
static rel_table pfs_ftp_directory_files (struct arch_pfs_session * p, t_uchar * path, int soft_errors);
static int pfs_ftp_put_file (struct arch_pfs_session * p, t_uchar * path, mode_t perms, int file_in_fd, int soft_errors);
static int pfs_ftp_mkdir (struct arch_pfs_session * p, t_uchar * path, mode_t perms, int soft_errors);
static int pfs_ftp_rename (struct arch_pfs_session * p, t_uchar ** errstr, t_uchar * from, t_uchar * to, int soft_errors);
static int pfs_ftp_rmdir (struct arch_pfs_session * p, t_uchar * path, int soft_errors);
static int pfs_ftp_rm (struct arch_pfs_session * p, t_uchar * path, int soft_errors);
static int ftp_client_greet (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * len,
int in_fd,
int out_fd);
static int ftp_client_login (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd,
t_uchar * name,
t_uchar * passwd);
static int ftp_client_read_reply (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int fd);
static int ftp_client_read_simple_cmd_reply (alloc_limits limit,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd);
static int ftp_client_printfmt (int * errn, int fd, char * fmt, ...);
static int ftp_client_pasv (alloc_limits limits,
int * errn,
int * code,
t_ulong * host,
t_uint16 * port,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd);
static int ftp_client_type_image (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd);
static int ftp_client_begin_retr (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd,
t_uchar * path);
static int ftp_client_end_retr (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd);
static int ftp_client_begin_stor (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd,
t_uchar * path);
static int ftp_client_end_stor (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd);
static int ftp_client_begin_nlst (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd,
int is_wu_ftp,
t_uchar * path,
t_ulong host_addr,
t_uint16 port);
static int ftp_client_end_nlst (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd);
static int ftp_client_mkd (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd,
t_uchar * path);
static int ftp_client_rename (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd,
t_uchar * from,
t_uchar * to);
static int ftp_client_dele (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd,
t_uchar * path);
static int ftp_client_rmd (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd,
t_uchar * path);
static int ftp_client_cwd (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd,
t_uchar * path);
static int ftp_client_pwd (alloc_limits limits,
int * errn,
int * code,
t_uchar ** path,
long * path_len,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd);
struct arch_pfs_vtable ftp_pfs_fns =
{
fs_disconnect,
pfs_ftp_file_exists,
pfs_ftp_is_dir,
pfs_ftp_file_contents,
pfs_ftp_get_file,
pfs_ftp_directory_files,
pfs_ftp_put_file,
pfs_ftp_mkdir,
pfs_ftp_rename,
pfs_ftp_rmdir,
pfs_ftp_rm,
};
int
arch_pfs_ftp_supported_protocol (t_uchar * uri)
{
if (!str_cmp_prefix ("ftp://", uri) || !str_cmp_prefix ("wu-ftp://", uri))
return 1;
else
return 0;
}
struct arch_pfs_session *
arch_pfs_ftp_connect (t_uchar * uri, int soft_errors)
{
int errn;
int code = 0;
struct arch_pfs_ftp_session * answer = 0;
t_uchar * host = 0;
t_uchar * user = 0;
t_uchar * passwd = 0;
t_uchar * hostname = 0;
int status = -1;
t_uchar * text = 0;
long text_len = 0;
answer = (struct arch_pfs_ftp_session *)lim_malloc (0, sizeof (*answer));
mem_set0 ((t_uchar *)answer, sizeof (*answer));
answer->pfs.vtable = &ftp_pfs_fns;
{
t_uchar * prefix;
t_uchar * start;
t_uchar * stop;
if (!str_cmp_prefix ("ftp://", uri))
prefix = "ftp://";
else
{
invariant (!str_cmp_prefix ("wu-ftp://", uri));
prefix = "wu-ftp://";
answer->is_wu = 1;
}
start = uri + str_length (prefix);
stop = str_chr_index (start, '/');
host = str_save_n (0, start, stop - start);
answer->cwd = str_save (0, stop);
}
hostname = str_chr_index (host, '@');
if (!hostname)
{
hostname = str_save (0, host);
user = str_save (0, "anonymous");
passwd = str_save (0, "arch(tla version: " cfg__std__package ")");
}
else
{
user = host;
passwd = str_chr_index_n (user, hostname - user, ':');
if (passwd)
{
++passwd;
user = str_save_n (0, user, (passwd - 1) - user);
passwd = str_save_n (0, passwd, hostname - passwd);
hostname = str_save (0, hostname + 1);
}
else
{
user = str_save_n (0, user, hostname - user);
passwd = str_save (0, "ftp-utils");
hostname = str_save (0, hostname + 1);
}
}
/* hostname, user, passwd allocated strings.
*/
answer->in_fd = url_inet_client (&errn, hostname, 21);
if (answer->in_fd < 0)
{
status = -1;
text = str_save (0, "Unable to connect to server");
text_len = str_length (text);
}
else
{
int e;
answer->out_fd = vu_dup (&errn, answer->in_fd);
if (answer->out_fd < 0)
{
vu_close (&e, answer->in_fd);
text = str_save (0, "Unable to duplicate the socket descriptor");
text_len = str_length (text);
status = -1;
}
else
{
if ( (0 > vfdbuf_buffer_fd (&errn, answer->in_fd, 0, O_RDONLY, 0))
|| (0 > vfdbuf_buffer_fd (&errn, answer->out_fd, 0, O_WRONLY, 0))
|| (0 > ftp_client_greet (0, &errn, &code, &text, &text_len, answer->in_fd, answer->out_fd))
|| (0 > ftp_client_login (0, &errn, &code, &text, &text_len, answer->in_fd, answer->out_fd, user, passwd)))
{
vu_close (&e, answer->in_fd);
vu_close (&e, answer->out_fd);
status = -1;
}
else
{
long dir_len = 0;
if (0 > ftp_client_pwd (0, &errn, &code, &answer->remote_pwd, &dir_len, &text, &text_len, answer->in_fd, answer->out_fd))
{
vu_close (&e, answer->in_fd);
vu_close (&e, answer->out_fd);
status = -1;
}
else
{
answer->remote_pwd = lim_realloc (0, answer->remote_pwd, dir_len + 1);
answer->remote_pwd[dir_len] = 0;
status = 0;
}
}
}
}
lim_free (0, host);
lim_free (0, user);
lim_free (0, passwd);
if (status)
{
if (!soft_errors)
{
safe_printfmt (2, "ftp_client_connect: ftp error connecting to %s\n", hostname);
safe_printfmt (2, " %d: %.*s\n", code, (int)text_len, text);
exit (2);
}
/* FIXME: disconnect or otherwise free
* infd, outfd etc etc
*/
lim_free (0, answer);
answer = NULL;
}
lim_free (0, hostname);
return (struct arch_pfs_session *)answer;
}
void
fs_disconnect (struct arch_pfs_session **pfs)
{
/* FIXME: close stuff yadayadayada */
lim_free (0, *pfs);
*pfs = NULL;
}
static int
pfs_ftp_file_exists (struct arch_pfs_session * p, t_uchar * path)
{
struct arch_pfs_ftp_session * arch = (struct arch_pfs_ftp_session *)p;
t_uchar * abs_path = 0;
t_uchar * abs_path_dir = 0;
t_uchar * path_tail = 0;
rel_table nlst = 0;
int x;
int answer;
abs_path = file_name_in_vicinity (0, arch->cwd, path);
abs_path_dir = file_name_directory_file (0, abs_path);
path_tail = file_name_tail (0, abs_path);
nlst = pfs_ftp_directory_files (p, abs_path_dir, 1);
answer = 0;
for (x = 0; x < rel_n_records (nlst); ++x)
{
if (!str_cmp (path_tail, nlst[x][0]))
{
answer = 1;
break;
}
}
if (!answer)
{
/* On some servers the NLST command does not return dot files. A simple
* workaround is to NLST directly the file we expect to find */
nlst = pfs_ftp_directory_files (p, abs_path, 1);
for (x = 0; x < rel_n_records (nlst); ++x)
{
if (!str_cmp (path_tail, nlst[x][0]))
{
answer = 1;
break;
}
}
}
lim_free (0, abs_path);
lim_free (0, abs_path_dir);
lim_free (0, path_tail);
rel_free_table (nlst);
return answer;
}
static int
pfs_ftp_is_dir (struct arch_pfs_session * p, t_uchar * path)
{
struct arch_pfs_ftp_session * arch = (struct arch_pfs_ftp_session *)p;
int errn;
int code;
t_uchar * text;
long text_len;
t_uchar * abs_path = 0;
int chdir_stat;
int return_chdir_stat;
abs_path = file_name_in_vicinity (0, arch->cwd, path);
chdir_stat = ftp_client_cwd (0, &errn, &code, &text, &text_len, arch->in_fd, arch->out_fd, abs_path);
lim_free (0, text);
return_chdir_stat = ftp_client_cwd (0, &errn, &code, &text, &text_len, arch->in_fd, arch->out_fd, arch->remote_pwd);
lim_free (0, text);
if (return_chdir_stat < 0)
{
safe_printfmt (2, "ftp_client_is_dir: unrecoverable chdir error for path %s\n", arch->remote_pwd);
exit (2);
}
lim_free (0, abs_path);
return (chdir_stat >= 0);
}
static t_uchar *
pfs_ftp_file_contents (struct arch_pfs_session * p, t_uchar * path, int soft_errors)
{
int string_fd;
t_uchar * answer = 0;
int status;
string_fd = make_output_to_string_fd ();
status = pfs_ftp_get_file (p, string_fd, path, soft_errors);
answer = string_fd_close (string_fd);
if (status < 0)
{
lim_free (0, answer);
answer = 0;
}
return answer;
}
static int
pfs_ftp_get_file (struct arch_pfs_session * p, int file_out_fd, t_uchar * path, int soft_errors)
{
struct arch_pfs_ftp_session * arch = (struct arch_pfs_ftp_session *)p;
int errn;
int code;
t_uchar * text = 0;
long text_len;
int status = -1;
t_uchar * get_path = 0;
get_path = file_name_in_vicinity (0, arch->cwd, path);
if (0 > ftp_client_type_image (0, &errn, &code, &text, &text_len, arch->in_fd, arch->out_fd))
{
put_error:
if (!soft_errors)
{
safe_printfmt (2, "ftp_client_get: error getting file %s\n", get_path);
safe_printfmt (2, " %d: %.*s\n", code, (int)text_len, text);
exit (2);
}
status = -1;
}
else
{
t_ulong host_addr;
t_uint16 port;
lim_free (0, text);
text = 0;
if (0 > ftp_client_pasv (0, &errn, &code, &host_addr, &port, &text, &text_len, arch->in_fd, arch->out_fd))
{
goto put_error;
}
else
{
int file_in_fd;
lim_free (0, text);
text = 0;
file_in_fd = url_inet_client_addr (&errn, host_addr, (int)port);
if (file_in_fd < 0)
{
text = str_save (0, errno_to_string (errn));
text_len = str_length (text);
goto put_error;
}
else
{
if (0 > ftp_client_begin_retr (0, &errn, &code, &text, &text_len, arch->in_fd, arch->out_fd, get_path))
{
goto put_error;
}
else
{
copy_fd (file_in_fd, file_out_fd);
safe_close (file_in_fd);
if (0 > ftp_client_end_retr (0, &errn, &code, &text, &text_len, arch->in_fd, arch->out_fd))
{
goto put_error;
}
else
{
lim_free (0, text);
text = 0;
status = 0;
}
}
}
}
}
lim_free (0, text);
lim_free (0, get_path);
return status;
}
static rel_table
pfs_ftp_directory_files (struct arch_pfs_session * p, t_uchar * path, int soft_errors)
{
struct arch_pfs_ftp_session * arch = (struct arch_pfs_ftp_session *)p;
int errn;
int code;
t_uchar * text = 0;
long text_len;
rel_table answer = 0;
t_uchar * nlst_path = 0;
t_uchar * nlst_path_lastc = 0;
nlst_path = file_name_in_vicinity (0, arch->cwd, path);
/* filter out trailing "/." since publicfile handles dots specially
*/
if (*nlst_path && *(nlst_path+1))
{
nlst_path_lastc = nlst_path + strlen(nlst_path) - 1;
if ((*nlst_path_lastc == '.') && (*(nlst_path_lastc-1) == '/'))
*(nlst_path_lastc - 1) = 0;
}
if (0 > ftp_client_type_image (0, &errn, &code, &text, &text_len, arch->in_fd, arch->out_fd))
{
put_error:
if (!soft_errors)
{
safe_printfmt (2, "ftp_client_directory_files: error listing file %s\n", nlst_path);
safe_printfmt (2, " %d: %.*s\n", code, (int)text_len, text);
exit (2);
}
}
else
{
t_ulong host_addr;
t_uint16 port;
lim_free (0, text);
text = 0;
if (0 > ftp_client_pasv (0, &errn, &code, &host_addr, &port, &text, &text_len, arch->in_fd, arch->out_fd))
{
goto put_error;
}
else
{
int file_in_fd;
lim_free (0, text);
text = 0;
file_in_fd = ftp_client_begin_nlst (0, &errn, &code, &text, &text_len, arch->in_fd, arch->out_fd, arch->is_wu, nlst_path, host_addr, port);
if (0 > file_in_fd)
{
/* Workaround for buggy ftp servers that respond a 450 or 550 code for empty directories.
* 450 is reported by proftpd:
* "450 No files found"
* The pfs_ftp_is_dir ensures the directory exists as it chdir to the given path.
* Btw, we could not have used pfs_ftp_file_exists otherwise we would have created
* an infinte function call loop because pfs_ftp_file_exists relies on
* pfs_ftp_directory_files */
if ((code != 450 && code != 550) || !pfs_ftp_is_dir (p, path))
goto put_error;
}
else
{
t_uchar * raw_list = 0;
int file_out_fd;
file_out_fd = make_output_to_string_fd ();
copy_fd (file_in_fd, file_out_fd);
safe_close (file_in_fd);
raw_list = string_fd_close (file_out_fd);
if (0 > ftp_client_end_nlst (0, &errn, &code, &text, &text_len, arch->in_fd, arch->out_fd))
{
lim_free (0, raw_list);
goto put_error;
}
else
{
int x;
lim_free (0, text);
text = 0;
raw_list = trim_surrounding_ws (raw_list);
answer = rel_ws_split (raw_list);
lim_free (0, raw_list);
for (x = 0; x < rel_n_records (answer); ++x)
{
t_uchar * t = file_name_tail (0, answer[x][0]);
rel_replace_record (answer, x, rel_make_record
(t, NULL));
lim_free (0, t);
}
}
}
}
}
lim_free (0, text);
lim_free (0, nlst_path);
return answer;
}
static int
pfs_ftp_put_file (struct arch_pfs_session * p, t_uchar * path, mode_t perms, int file_in_fd, int soft_errors)
{
struct arch_pfs_ftp_session * arch = (struct arch_pfs_ftp_session *)p;
int errn;
int code;
t_uchar * text = 0;
long text_len;
int status = -1;
t_uchar * put_path = 0;
put_path = file_name_in_vicinity (0, arch->cwd, path);
if (0 > ftp_client_type_image (0, &errn, &code, &text, &text_len, arch->in_fd, arch->out_fd))
{
put_error:
if (!soft_errors)
{
safe_printfmt (2, "ftp_client_put: error putting file %s\n", put_path);
safe_printfmt (2, " %d: %.*s\n", code, (int)text_len, text);
exit (2);
}
status = -1;
}
else
{
t_ulong host_addr;
t_uint16 port;
lim_free (0, text);
text = 0;
if (0 > ftp_client_pasv (0, &errn, &code, &host_addr, &port, &text, &text_len, arch->in_fd, arch->out_fd))
{
goto put_error;
}
else
{
int file_out_fd;
lim_free (0, text);
text = 0;
file_out_fd = url_inet_client_addr (&errn, host_addr, (int)port);
if (file_out_fd < 0)
{
text = str_save (0, errno_to_string (errn));
text_len = str_length (text);
goto put_error;
}
else
{
if (0 > ftp_client_begin_stor (0, &errn, &code, &text, &text_len, arch->in_fd, arch->out_fd, put_path))
{
goto put_error;
}
else
{
copy_fd (file_in_fd, file_out_fd);
safe_close (file_out_fd);
if (0 > ftp_client_end_stor (0, &errn, &code, &text, &text_len, arch->in_fd, arch->out_fd))
{
goto put_error;
}
else
{
lim_free (0, text);
text = 0;
status = 0;
}
}
}
}
}
lim_free (0, text);
lim_free (0, put_path);
return status;
}
static int
pfs_ftp_mkdir (struct arch_pfs_session * p, t_uchar * path, mode_t perms, int soft_errors)
{
struct arch_pfs_ftp_session * arch = (struct arch_pfs_ftp_session *)p;
int errn;
int code;
t_uchar * text = 0;
long text_len;
int status;
t_uchar * mkdir_path = 0;
mkdir_path = file_name_in_vicinity (0, arch->cwd, path);
status = ftp_client_mkd (0, &errn, &code, &text, &text_len, arch->in_fd, arch->out_fd, mkdir_path);
if (status && !soft_errors)
{
safe_printfmt (2, "ftp_client_mkdir: error making directory %s\n", mkdir_path);
safe_printfmt (2, " %d: %.*s\n", code, (int)text_len, text);
exit (2);
}
lim_free (0, text);
lim_free (0, mkdir_path);
return status;
}
static int
pfs_ftp_rename (struct arch_pfs_session * p, t_uchar ** errstr, t_uchar * from, t_uchar * to, int soft_errors)
{
struct arch_pfs_ftp_session * arch = (struct arch_pfs_ftp_session *)p;
int errn;
int code = 0;
t_uchar * text = 0;
long text_len = 0;
int status;
t_uchar * from_path = 0;
t_uchar * to_path = 0;
from_path = file_name_in_vicinity (0, arch->cwd, from);
to_path = file_name_in_vicinity (0, arch->cwd, to);
status = ftp_client_rename (0, &errn, &code, &text, &text_len, arch->in_fd, arch->out_fd, from_path, to_path);
if (status && !soft_errors)
{
safe_printfmt (2, "ftp_client_rename: error renaming %s to %s\n", from_path, to_path);
safe_printfmt (2, " %d: %.*s\n", code, (int)text_len, text);
exit (2);
}
lim_free (0, text);
lim_free (0, from_path);
lim_free (0, to_path);
return status;
}
static int
pfs_ftp_rmdir (struct arch_pfs_session * p, t_uchar * path, int soft_errors)
{
struct arch_pfs_ftp_session * arch = (struct arch_pfs_ftp_session *)p;
int errn;
int code;
t_uchar * text = 0;
long text_len;
int status;
t_uchar * dele_path = 0;
dele_path = file_name_in_vicinity (0, arch->cwd, path);
status = ftp_client_rmd (0, &errn, &code, &text, &text_len, arch->in_fd, arch->out_fd, dele_path);
if (status && !soft_errors)
{
safe_printfmt (2, "ftp_client_rmdir: error deleting %s\n", dele_path);
safe_printfmt (2, " %d: %.*s\n", code, (int)text_len, text);
exit (2);
}
lim_free (0, text);
lim_free (0, dele_path);
return status;
}
static int
pfs_ftp_rm (struct arch_pfs_session * p, t_uchar * path, int soft_errors)
{
struct arch_pfs_ftp_session * arch = (struct arch_pfs_ftp_session *)p;
int errn;
int code;
t_uchar * text = 0;
long text_len;
int status;
t_uchar * dele_path = 0;
dele_path = file_name_in_vicinity (0, arch->cwd, path);
status = ftp_client_dele (0, &errn, &code, &text, &text_len, arch->in_fd, arch->out_fd, dele_path);
if (status && !soft_errors)
{
safe_printfmt (2, "ftp_client_rm: error deleting %s\n", dele_path);
safe_printfmt (2, " %d: %.*s\n", code, (int)text_len, text);
exit (2);
}
lim_free (0, text);
lim_free (0, dele_path);
return status;
}
static int
ftp_client_greet (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * len,
int in_fd,
int out_fd)
{
int delayed;
delayed = 0;
retry:
if (0 > ftp_client_read_reply (limits, errn, code, text, len, in_fd))
return -1;
if (*code == 120)
{
if (delayed)
{
*errn = EINVAL;
return -1;
}
else
{
delayed = 1;
goto retry;
}
}
if (*code == 220)
return 0;
if (*code == 421)
{
*errn = 0;
return -1;
}
*errn = EINVAL;
return -1;
}
/****************************************************************
*
* ftp_login
* USER <SP> <username> <CRLF>
* PASS <SP> <password> <CRLF>
*
* not implemented
* ACCT <SP> <account-information> <CRLF>
*
*/
static int
ftp_client_login (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd,
t_uchar * name,
t_uchar * passwd)
{
int user_repl_code;
t_uchar * user_repl_text;
long user_repl_len;
int pass_repl_code;
t_uchar * pass_repl_text;
long pass_repl_len;
if (0 > ftp_client_printfmt (errn, out_fd, "USER %s\r\n", name))
return -1;
if (0 > ftp_client_read_reply (limits, errn, &user_repl_code, &user_repl_text, &user_repl_len, in_fd))
return -1;
*code = user_repl_code;
if (!text)
lim_free (limits, user_repl_text);
else
{
*text = user_repl_text;
*text_len = user_repl_len;
}
{
int first_digit;
first_digit = user_repl_code / 100;
switch (first_digit)
{
default:
case 1:
*errn = EINVAL;
return -1;
case 2:
return 0;
case 3:
break;
case 4:
case 5:
*errn = 0;
return -1;
}
}
if (0 > ftp_client_printfmt (errn, out_fd, "PASS %s\r\n", passwd))
return -1;
if (0 > ftp_client_read_reply (limits, errn, &pass_repl_code, &pass_repl_text, &pass_repl_len, in_fd))
return -1;
*code = pass_repl_code;
if (!text)
lim_free (limits, pass_repl_text);
else
{
*text = lim_realloc (limits, *text, user_repl_len + pass_repl_len + 1);
mem_move (*text + user_repl_len, pass_repl_text, pass_repl_len);
(*text)[user_repl_len + pass_repl_len] = 0;
*text_len += pass_repl_len;
lim_free (limits, pass_repl_text);
}
{
int first_digit;
first_digit = pass_repl_code / 100;
switch (first_digit)
{
default:
case 1:
*errn = EINVAL;
return -1;
case 2:
return 0;
case 3:
*errn = ENOSYS;
return -1;
case 4:
case 5:
*errn = 0;
return -1;
}
}
}
/****************************************************************
* From rfc 959
*
* An FTP reply consists of a three digit number (transmitted as
* three alphanumeric characters) followed by some text. The number
* is intended for use by automata to determine what state to enter
* next; the text is intended for the human user. It is intended
* that the three digits contain enough encoded information that the
* user-process (the User-PI) will not need to examine the text and
* may either discard it or pass it on to the user, as appropriate.
* In particular, the text may be server-dependent, so there are
* likely to be varying texts for each reply code.
*
* A reply is defined to contain the 3-digit code, followed by Space
* <SP>, followed by one line of text (where some maximum line length
* has been specified), and terminated by the Telnet end-of-line
* code. There will be cases however, where the text is longer than
* a single line. In these cases the complete text must be bracketed
* so the User-process knows when it may stop reading the reply (i.e.
* stop processing input on the control connection) and go do other
* things. This requires a special format on the first line to
* indicate that more than one line is coming, and another on the
* last line to designate it as the last. At least one of these must
* contain the appropriate reply code to indicate the state of the
* transaction. To satisfy all factions, it was decided that both
* the first and last line codes should be the same.
*
* Thus the format for multi-line replies is that the first line will
* begin with the exact required reply code, followed immediately by a
* Hyphen, "-" (also known as Minus), followed by text. The last line
* will begin with the same code, followed immediately by Space <SP>,
* optionally some text, and the Telnet end-of-line code.
*
* For example:
* 123-First line
* Second line
* 234 A line beginning with numbers
* 123 The last line
*
* The user-process then simply needs to search for the second
* occurrence of the same reply code, followed by <SP> (Space), at the
* beginning of a line, and ignore all intermediary lines. If an
* intermediary line begins with a 3-digit number, the Server must pad
* the front to avoid confusion.
*
* This scheme allows standard system routines to be used for reply
* information (such as for the STAT reply), with "artificial" first
* and last lines tacked on. In rare cases where these routines are
* able to generate three digits and a Space at the beginning of any
* line, the beginning of each text line should be offset by some
* neutral text, like Space.
*
* This scheme assumes that multi-line replies may not be nested.
*/
static void
handle_reply_data (t_uchar **text, alloc_limits const limits,
int const len, long * text_len,
t_uchar const * const line)
{
if (text)
{
*text = lim_realloc (limits, *text, len + *text_len + 1);
mem_move (*text + *text_len, line, len);
(*text)[*text_len + len] = 0;
*text_len = len + *text_len;
}
}
static int
ftp_client_read_reply (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int fd)
{
t_uchar * line;
long len;
t_uchar c0;
t_uchar c1;
t_uchar c2;
if (vfdbuf_next_line (errn, &line, &len, fd))
return -1;
if ( (len < 4)
|| !char_is_digit(line[0])
|| !char_is_digit(line[1])
|| !char_is_digit(line[2])
|| ((line[3] != ' ') && (line[3] != '-')))
{
*errn = EINVAL;
return -1;
}
*code = ( (line[0] - '0') * 100
+ (line[1] - '0') * 10
+ (line[2] - '0'));
c0 = line[0];
c1 = line[1];
c2 = line[2];
if (text)
{
*text = str_save_n (limits, line, len);
*text_len = len;
}
if (line[3] != '-')
{
return 0;
}
while (1)
{
if (vfdbuf_next_line (errn, &line, &len, fd))
{
if (text)
lim_free (limits, *text);
return -1;
}
if ((len >= 4)
&& (char_is_digit(line[0]) && c0)
&& (char_is_digit(line[1]) && c1)
&& (char_is_digit(line[2]) && c2)
&& (line[3] == ' '))
{
/* Got the last line of the reply.
*/
handle_reply_data (text, limits, len, text_len, line);
return 0;
}
else if (len == 0)
{
/* bad ftp server hasn't finished the reply, but is looking like EOF
* observed with ftp.ece.nwu.edu
* RBC 20050316
*/
handle_reply_data (text, limits, len, text_len, line);
return 0;
}
else
{
handle_reply_data (text, limits, len, text_len, line);
}
}
/* not reached */
panic ("not reached in ftp_read_reply");
return -1;
}
/****************************************************************
*
* We first present the diagram that represents the largest group of FTP
* commands:
*
*
* 1,3 +---+
* ----------->| E |
* | +---+
* |
* +---+ cmd +---+ 2 +---+
* | B |---------->| W |---------->| S |
* +---+ +---+ +---+
* |
* | 4,5 +---+
* ----------->| F |
* +---+
*
*
* This diagram models the commands:
*
* ABOR, ALLO, DELE, CWD, CDUP, SMNT, HELP, MODE, NOOP, PASV,
* QUIT, SITE, PORT, SYST, STAT, RMD, MKD, PWD, STRU, and TYPE.
*
*/
static int
ftp_client_read_simple_cmd_reply (alloc_limits limit,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd)
{
if (0 > ftp_client_read_reply (limit, errn, code, text, text_len, in_fd))
return -1;
{
int first_digit;
first_digit = *code / 100;
switch (first_digit)
{
default:
case 1:
case 3:
*errn = EINVAL;
return -1;
case 2:
return 0;
case 4:
case 5:
*errn = 0;
return -1;
}
}
}
static int
ftp_client_printfmt (int * errn, int fd, char * fmt, ...)
{
int answer;
va_list ap;
#if 0
printfmt (errn, 2, "sending: ");
va_start (ap, fmt);
printfmt_va_list (errn, 2, fmt, ap);
va_end (ap);
#endif
va_start (ap, fmt);
answer = printfmt_va_list (errn, fd, fmt, ap);
va_end (ap);
if ( (answer >= 0)
&& vfdbuf_is_buffered (fd)
&& (0 > vfdbuf_flush (errn, fd)))
return -1;
return answer;
}
/****************************************************************
* ftp_port
*
* PORT <SP> <host-port> <CRLF>
*
*/
#if 0
static int
ftp_client_port (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd,
t_ulong host,
t_uint16 port)
{
if (0 > ftp_client_printfmt (errn, out_fd, "PORT %d,%d,%d,%d,%d,%d\r\n",
(int)((host >> 24) & 0xff),
(int)((host >> 16) & 0xff),
(int)((host >> 8) & 0xff),
(int)(host & 0xff),
(port >> 8) & 0xff,
port & 0xff))
return -1;
return ftp_client_read_simple_cmd_reply (limits, errn, code, text, text_len, in_fd);
}
#endif
/****************************************************************
* ftp_client_pasv_parse_equal
*
* =ipdec1,ipdec2,ipdec3,ipdec4,portdec1,portdec2
*
*/
static int
ftp_client_pasv_parse_equal (char *text, t_ulong *h, t_uint16 *p)
{
int er;
t_uchar * start_byte;
t_uchar * end_byte;
t_uint h1;
t_uint h2;
t_uint h3;
t_uint h4;
t_uint p1;
t_uint p2;
start_byte = str_chr_rindex (text, '=');
if (!start_byte)
return 1;
++start_byte;
end_byte = str_chr_index (start_byte, ',');
if (!end_byte)
return 1;
cvt_decimal_to_uint (&er, &h1, start_byte, end_byte - start_byte);
start_byte = end_byte + 1;
end_byte = str_chr_index (start_byte, ',');
if (!end_byte)
return 1;
cvt_decimal_to_uint (&er, &h2, start_byte, end_byte - start_byte);
start_byte = end_byte + 1;
end_byte = str_chr_index (start_byte, ',');
if (!end_byte)
return 1;
cvt_decimal_to_uint (&er, &h3, start_byte, end_byte - start_byte);
start_byte = end_byte + 1;
end_byte = str_chr_index (start_byte, ',');
if (!end_byte)
return 1;
cvt_decimal_to_uint (&er, &h4, start_byte, end_byte - start_byte);
start_byte = end_byte + 1;
end_byte = str_chr_index (start_byte, ',');
if (!end_byte)
return 1;
cvt_decimal_to_uint (&er, &p1, start_byte, end_byte - start_byte);
start_byte = end_byte + 1;
end_byte = start_byte;
while ((*end_byte >= '0') && (*end_byte <= '9'))
end_byte++;
if (!end_byte)
return 1;
cvt_decimal_to_uint (&er, &p2, start_byte, end_byte - start_byte);
*h = (h1 << 24) | (h2 << 16) | (h3 << 8) | h4;
*p = (p1 << 8) | p2;
return 0;
}
/****************************************************************
* ftp_client_pasv_parse_parenthesis
*
* (ipdec1,ipdec2,ipdec3,ipdec4,portdec1,portdec2)
*
*/
static int
ftp_client_pasv_parse_parenthesis (char *text, t_ulong *h, t_uint16 *p)
{
int er;
t_uchar * start_byte;
t_uchar * end_byte;
t_uint h1;
t_uint h2;
t_uint h3;
t_uint h4;
t_uint p1;
t_uint p2;
start_byte = str_chr_rindex (text, '(');
if (!start_byte)
return -1;
++start_byte;
end_byte = str_chr_index (start_byte, ',');
if (!end_byte)
return -1;
cvt_decimal_to_uint (&er, &h1, start_byte, end_byte - start_byte);
start_byte = end_byte + 1;
end_byte = str_chr_index (start_byte, ',');
if (!end_byte)
return -1;
cvt_decimal_to_uint (&er, &h2, start_byte, end_byte - start_byte);
start_byte = end_byte + 1;
end_byte = str_chr_index (start_byte, ',');
if (!end_byte)
return -1;
cvt_decimal_to_uint (&er, &h3, start_byte, end_byte - start_byte);
start_byte = end_byte + 1;
end_byte = str_chr_index (start_byte, ',');
if (!end_byte)
return -1;
cvt_decimal_to_uint (&er, &h4, start_byte, end_byte - start_byte);
start_byte = end_byte + 1;
end_byte = str_chr_index (start_byte, ',');
if (!end_byte)
return -1;
cvt_decimal_to_uint (&er, &p1, start_byte, end_byte - start_byte);
start_byte = end_byte + 1;
end_byte = str_chr_index (start_byte, ')');
if (!end_byte)
return -1;
cvt_decimal_to_uint (&er, &p2, start_byte, end_byte - start_byte);
*h = (h1 << 24) | (h2 << 16) | (h3 << 8) | h4;
*p = (p1 << 8) | p2;
return 0;
}
/****************************************************************
* ftp_pasv
*
* PASV <CRLF>
*/
static int
ftp_client_pasv (alloc_limits limits,
int * errn,
int * code,
t_ulong * host,
t_uint16 * port,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd)
{
t_uchar * t;
long tl;
int status;
if (0 > ftp_client_printfmt (errn, out_fd, "PASV\r\n"))
return -1;
t = 0;
tl = 0;
if (!text)
{
text = &t;
text_len = &tl;
}
if (0 > ftp_client_read_simple_cmd_reply (limits, errn, code, text, text_len, in_fd))
return -1;
status = 0;
if (*code != 227)
{
switch (*code / 100)
{
default:
case 1:
case 2:
case 3:
bogus_reply:
*errn = EINVAL;
status = -1;
break;
case 4:
case 5:
*errn = 0;
status = -1;
break;
}
}
else
{
t_ulong h = 0;
t_uint16 p = 0;
if (str_chr_rindex (*text, '('))
{
if (0 > ftp_client_pasv_parse_parenthesis(*text, &h, &p))
goto bogus_reply;
}
else if (str_chr_rindex (*text, '='))
{
if (0 > ftp_client_pasv_parse_equal(*text, &h, &p))
goto bogus_reply;
}
else
goto bogus_reply;
*host = h;
*port = p;
#if 0
if (host)
*host = htonl (h);
if (port)
*port = htons (p);
#endif
}
if (t)
lim_free (limits, t);
return status;
}
/****************************************************************
* ftp_type_image
* ftp_type_ascii
*
* TYPE <SP> <type-code> <CRLF>
*
*/
static int
ftp_client_type_image (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd)
{
if (0 > ftp_client_printfmt (errn, out_fd, "TYPE I\r\n"))
return -1;
return ftp_client_read_simple_cmd_reply (limits, errn, code, text, text_len, in_fd);
}
#if 0
static int
ftp_client_type_ascii (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd)
{
if (0 > ftp_client_printfmt (errn, out_fd, "TYPE A\r\n"))
return -1;
return ftp_client_read_simple_cmd_reply (limits, errn, code, text, text_len, in_fd);
}
#endif
/****************************************************************
* not implemented
*
* STRU <SP> <structure-code> <CRLF>
*
*/
/****************************************************************
* ftp_stream_mode
*
* MODE <SP> <mode-code> <CRLF>
*
*/
#if 0
static int
ftp_client_stream_mode (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd)
{
if (0 > ftp_client_printfmt (errn, out_fd, "MODE S\r\n"))
return -1;
return ftp_client_read_simple_cmd_reply (limits, errn, code, text, text_len, in_fd);
}
#endif
/****************************************************************
* ftp_client_begin_retr
* ftp_client_end_retr
*
* RETR <SP> <pathname> <CRLF>
*
*/
static int
ftp_client_begin_retr (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd,
t_uchar * path)
{
int c;
if (0 > ftp_client_printfmt (errn, out_fd, "RETR %s\r\n", path))
return -1;
if (0 > ftp_client_read_reply (limits, errn, &c, text, text_len, in_fd))
return -1;
if (code)
*code = c;
switch (c / 100)
{
case 1:
/* Not doing anything special for markers yet.
*
* 125 Data connection already open; transfer starting.
* 150 File status okay; about to open data connection.
* 110 Restart marker reply.
* In this case, the text is exact and not left to the
* particular implementation; it must read:
* MARK yyyy = mmmm
* Where yyyy is User-process data stream marker, and mmmm
* server's equivalent marker (note the spaces between markers
* and "=").
*/
return 0;
default:
case 2:
case 4:
case 5:
/*
* 226 Closing data connection.
* Requested file action successful (for example, file
* transfer or file abort).
* 250 Requested file action okay, completed.
*
* 421 Service not available, closing control connection.
* This may be a reply to any command if the service knows it
* must shut down.
* 425 Can't open data connection.
* 426 Connection closed; transfer aborted.
* 451 Requested action aborted: local error in processing.
* 450 Requested file action not taken.
* File unavailable (e.g., file busy).
*
* 550 Requested action not taken.
* File unavailable (e.g., file not found, no access).
* 500 Syntax error, command unrecognized.
* This may include errors such as command line too long.
* 501 Syntax error in parameters or arguments.
* 530 Not logged in.
*/
*errn = 0;
return -1;
case 3:
*errn = EINVAL;
return -1;
}
}
static int
ftp_client_end_retr (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd)
{
return ftp_client_read_simple_cmd_reply (limits, errn, code, text, text_len, in_fd);
}
/****************************************************************
* ftp_client_begin_stor
* ftp_client_end_stor
*
* STOR <SP> <pathname> <CRLF>
*
*/
static int
ftp_client_begin_stor (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd,
t_uchar * path)
{
int c;
if (0 > ftp_client_printfmt (errn, out_fd, "STOR %s\r\n", path))
return -1;
if (0 > ftp_client_read_reply (limits, errn, &c, text, text_len, in_fd))
return -1;
if (code)
*code = c;
switch (c / 100)
{
case 1:
/* Not doing anything special for markers yet.
*/
return 0;
default:
case 2:
case 4:
case 5:
*errn = 0;
return -1;
case 3:
*errn = EINVAL;
return -1;
}
}
static int
ftp_client_end_stor (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd)
{
return ftp_client_read_simple_cmd_reply (limits, errn, code, text, text_len, in_fd);
}
/****************************************************************
* ftp_client_begin_nlst
* ftp_client_end_nlst
*
* NLST [<SP> <pathname>] <CRLF>
*
*
* with wu-ftp:
*
* NLST <SP> -a [<SP> <pathname>] <CRLF>
*
*/
static int
ftp_client_begin_nlst (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd,
int is_wu_ftp,
t_uchar * path,
t_ulong host_addr,
t_uint16 port)
{
int c;
int data_fd = 0;
if (host_addr)
{
data_fd = url_inet_client_addr (errn, host_addr, (int)port);
if (data_fd < 0)
{
safe_printfmt (2, "ftp_client_begin_nlst: %s to port %d\n", errno_to_string (*errn), (int) port);
}
}
if (path && path[0])
{
if (0 > ftp_client_printfmt (errn, out_fd, "NLST%s %s\r\n", (is_wu_ftp ? " -a" : ""), path))
goto leave;
}
else
{
if (0 > ftp_client_printfmt (errn, out_fd, "NLST%s\r\n", (is_wu_ftp ? " -a" : "")))
goto leave;
}
if (0 > ftp_client_read_reply (limits, errn, &c, text, text_len, in_fd))
{
leave:
if (host_addr && data_fd >= 0)
safe_close (data_fd);
return -1;
}
if (code)
*code = c;
else
{
}
switch (c / 100)
{
case 1:
return data_fd;
default:
case 2:
case 4:
case 5:
*errn = 0;
if (host_addr && (data_fd >= 0))
safe_close (data_fd);
return -1;
case 3:
*errn = EINVAL;
if (host_addr && (data_fd >= 0))
safe_close (data_fd);
return -1;
}
}
static int
ftp_client_end_nlst (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd)
{
return ftp_client_read_simple_cmd_reply (limits, errn, code, text, text_len, in_fd);
}
/****************************************************************
* ftp_mkd
* MKD <SP> <pathname> <CRLF>
*
*/
static int
ftp_client_mkd (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd,
t_uchar * path)
{
if (0 > ftp_client_printfmt (errn, out_fd, "MKD %s\r\n", path))
return -1;
return ftp_client_read_simple_cmd_reply (limits, errn, code, text, text_len, in_fd);
}
/****************************************************************
* ftp_rename
*
* RNFR <SP> <pathname> <CRLF>
* RNTO <SP> <pathname> <CRLF>
*
*/
static int
ftp_client_rename (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd,
t_uchar * from,
t_uchar * to)
{
int rnfr_code;
t_uchar * rnfr_text;
long rnfr_len;
int rnto_code;
t_uchar * rnto_text;
long rnto_len;
if (0 > ftp_client_printfmt (errn, out_fd, "RNFR %s\r\n", from))
return -1;
if (0 > ftp_client_read_reply (limits, errn, &rnfr_code, &rnfr_text, &rnfr_len, in_fd))
return -1;
*code = rnfr_code;
if (!text)
lim_free (limits, rnfr_text);
else
{
*text = rnfr_text;
*text_len = rnfr_len;
}
{
int first_digit;
first_digit = rnfr_code / 100;
switch (first_digit)
{
default:
case 1:
case 2:
*errn = EINVAL;
return -1;
case 3:
break;
case 4:
case 5:
*errn = 0;
return -1;
}
}
if (0 > ftp_client_printfmt (errn, out_fd, "RNTO %s\r\n", to))
return -1;
if (0 > ftp_client_read_reply (limits, errn, &rnto_code, &rnto_text, &rnto_len, in_fd))
return -1;
*code = rnto_code;
if (!text)
lim_free (limits, rnto_text);
else
{
*text = lim_realloc (limits, *text, rnfr_len + rnto_len + 1);
mem_move (*text + rnfr_len, rnto_text, rnto_len);
(*text)[rnfr_len + rnto_len] = 0;
*text_len += rnto_len;
lim_free (limits, rnto_text);
}
{
int first_digit;
first_digit = rnto_code / 100;
switch (first_digit)
{
default:
case 1:
case 3:
*errn = EINVAL;
return -1;
case 2:
return 0;
case 4:
case 5:
*errn = 0;
return -1;
}
}
}
static int
ftp_client_dele (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd,
t_uchar * path)
{
if (0 > ftp_client_printfmt (errn, out_fd, "DELE %s\r\n", path))
return -1;
return ftp_client_read_simple_cmd_reply (limits, errn, code, text, text_len, in_fd);
}
/****************************************************************
* ftp_rmd
*
* RMD <SP> <pathname> <CRLF>
*
*/
static int
ftp_client_rmd (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd,
t_uchar * path)
{
if (0 > ftp_client_printfmt (errn, out_fd, "RMD %s\r\n", path))
return -1;
return ftp_client_read_simple_cmd_reply (limits, errn, code, text, text_len, in_fd);
}
/****************************************************************
* ftp_cwd
* CWD <SP> <pathname> <CRLF>
*/
static int
ftp_client_cwd (alloc_limits limits,
int * errn,
int * code,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd,
t_uchar * path)
{
if (0 > ftp_client_printfmt (errn, out_fd, "CWD %s\r\n", path))
return -1;
return ftp_client_read_simple_cmd_reply (limits, errn, code, text, text_len, in_fd);
}
/****************************************************************
* ftp_client_pwd
*
* PWD <CRLF>
*
*/
static int
ftp_client_pwd (alloc_limits limits,
int * errn,
int * code,
t_uchar ** path,
long * path_len,
t_uchar ** text,
long * text_len,
int in_fd,
int out_fd)
{
t_uchar * t;
long tl;
int status;
if (0 > ftp_client_printfmt (errn, out_fd, "PWD\r\n"))
return -1;
t = 0;
tl = 0;
if (!text)
{
text = &t;
text_len = &tl;
}
if (0 > ftp_client_read_simple_cmd_reply (limits, errn, code, text, text_len, in_fd))
return -1;
status = 0;
if (*code != 257)
{
switch (*code / 100)
{
default:
case 1:
case 2:
case 3:
bogus_reply:
*errn = EINVAL;
status = -1;
break;
case 4:
case 5:
*errn = 0;
status = -1;
break;
}
}
else
{
t_uchar * path_start;
t_uchar * path_end;
long doubled_quotes;
path_start = str_chr_index (*text, '"');
if (!path_start)
goto bogus_reply;
path_start += 1;
path_end = path_start;
doubled_quotes = 0;
while (1)
{
if (path_end == (*text + *text_len))
goto bogus_reply;
else if (*path_end == '"')
{
if (((path_end + 1) < (*text + *text_len)) && (*(path_end + 1) == '"'))
{
++doubled_quotes;
path_end += 2;
}
else
break;
}
else
++path_end;
}
if (path)
{
t_uchar * p;
*path = lim_malloc (limits, 1 + (path_end - path_start) - doubled_quotes);
p = *path;
while (path_start < path_end)
{
*p = *path_start;
++p;
if (*path_start == '"')
++path_start;
++path_start;
}
*p = 0;
*path_len = p - *path;
}
status = 0;
}
if (t)
lim_free (limits, t);
return status;
}
/* tag: Tom Lord Mon Jun 16 21:58:44 2003 (pfs-ftp.c)
*/
syntax highlighted by Code2HTML, v. 0.9.1