/* pfs-dav.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 "hackerlab/bugs/panic.h"
#include "hackerlab/mem/alloc-limits.h"
#include "hackerlab/mem/mem.h"
#include "hackerlab/char/str.h"
#include "hackerlab/fmt/cvt.h"
#include "hackerlab/fs/file-names.h"
#include "hackerlab/mem/talloc.h"
#include "hackerlab/vu/safe.h"
#include "libfsutils/tmp-files.h"
#include "libfsutils/file-contents.h"
#include "libarch/authinfo.h"
#include "libarch/archives.h"
#include "libarch/debug.h"
#include "libarch/pfs-dav.h"
#include "po/gettext.h"
#include <neon/ne_session.h>
#include <neon/ne_basic.h>
#include <neon/ne_request.h>
#include <neon/ne_auth.h>
#include <neon/ne_props.h>
#include <neon/ne_uri.h>
#include <neon/ne_redirect.h>
#include <neon/ne_socket.h>
#undef MIN
#define MIN(A,B) ((A) < (B) ? (A) : (B))
#define LISTING_FILE ".listing"
static const ne_propname ls_props[] =
{
{ "DAV:", "getcontentlength" },
{ "DAV:", "getlastmodified" },
{ "DAV:", "resourcetype" },
{ NULL }
};
struct authinfo
{
char *username;
char *password;
char *machine;
};
struct ls_data
{
char *files;
char *uri;
};
struct dav_is_dir_ls_data
{
char *uri;
int is_dir;
};
struct arch_pfs_dav_session
{
struct arch_pfs_session pfs;
char * cwd;
char * dav_scheme;
char * dav_hostname;
int dav_port;
struct authinfo * proxy_auth;
ne_session * sess;
ne_server_capabilities sess_opts;
};
/* __STDC__ prototypes for static functions */
static void fs_disconnect (struct arch_pfs_session **pfs);
static t_uchar * pfs_file_contents (struct arch_pfs_session * p, t_uchar * path, int soft_errors);
static rel_table pfs_directory_files (struct arch_pfs_session * p, t_uchar * path, int soft_errors);
static int quick_file_exists (struct arch_pfs_session * p, t_uchar * path);
static int pfs_file_exists (struct arch_pfs_session * p, t_uchar * path);
static int pfs_get_file (struct arch_pfs_session * p, int out_fd, t_uchar * path, int soft_errors);
static int pfs_put_file (struct arch_pfs_session * p, t_uchar * path, mode_t perms, int in_fd, int soft_errors);
static int pfs_mkdir (struct arch_pfs_session * p, t_uchar * path, mode_t mode, int soft_errors);
static int pfs_rename (struct arch_pfs_session * p, t_uchar ** errstr, t_uchar * from, t_uchar * to, int soft_errors);
static int pfs_is_dir (struct arch_pfs_session * p, t_uchar * path);
static int dav_is_dir (struct arch_pfs_dav_session * pfs, char * dir);
static int pfs_rmdir (struct arch_pfs_session * p, t_uchar * path, int soft_errors);
static int pfs_rm (struct arch_pfs_session * p, t_uchar * path, int soft_errors);
static void dav_is_dir_results (void * userdata, const char * uri, const ne_prop_result_set * set);
static int dav_client_cwd (struct arch_pfs_dav_session * pfs, t_uchar * path, int soft_errors);
static int dav_client_auth (void * userdata, const char * realm, int attempt,
char * username, char * password);
static t_uchar * abs_path (t_uchar * cwd, t_uchar * path);
static t_uchar * dirfold (t_uchar *dir);
static void results (void * userdata, const char * uri, const ne_prop_result_set * set);
struct arch_pfs_vtable dav_pfs_fns =
{
fs_disconnect,
pfs_file_exists,
pfs_is_dir,
pfs_file_contents,
pfs_get_file,
pfs_directory_files,
pfs_put_file,
pfs_mkdir,
pfs_rename,
pfs_rmdir,
pfs_rm,
};
int
arch_pfs_dav_supported_protocol (t_uchar * uri)
{
if (!str_cmp_prefix ("http:", uri) ||
!str_cmp_prefix ("https:", uri))
return 1;
else
return 0;
}
/* Display informations about the certificate, and accept it */
static int
ssl_accept(void *userdata, int failures, const ne_ssl_certificate *c)
{
/* (trivial) code taken from cadaver.c */
char *tmp, from[NE_SSL_VDATELEN], to[NE_SSL_VDATELEN];
const char *ident;
struct authinfo * auth = userdata;
ident = ne_ssl_cert_identity(c);
if (ident)
debug (dbg_dav, 1,
_("WARNING: Accepting untrusted server certificate for `%s':\n"),
ident);
else
debug (dbg_dav, 1,
_("WARNING: Accepting untrusted server certificate\n"));
if (failures & NE_SSL_IDMISMATCH)
{
debug (dbg_dav,1,
_("Certificate was issued to hostname `%s' instead of `%s'.\n"
"This connection may be hijacked!\n"),
ne_ssl_cert_identity(c),
auth->machine);
}
#define PRINT_AND_FREE(str, dn) \
tmp = ne_ssl_readable_dname(dn); safe_printfmt(2, str, tmp); free(tmp)
PRINT_AND_FREE("Issued to: %s\n", ne_ssl_cert_subject(c));
PRINT_AND_FREE("Issued by: %s\n", ne_ssl_cert_issuer(c));
ne_ssl_cert_validity(c, from, to);
safe_printfmt(2, "Certificate is valid from %s to %s\n", from, to);
return 0;
}
struct arch_pfs_session *
arch_pfs_dav_connect (t_uchar * uri, int soft_errors)
{
int ign;
struct arch_pfs_dav_session * answer = 0;
t_uchar * host;
t_uchar * user;
t_uchar * passwd;
t_uchar * hostname;
t_uchar * portstr;
t_uchar * root_path = 0;
t_uchar * proxy;
t_uchar * proxy_port;
struct authinfo * auth = 0;
int proxy_port_num;
/* ne_sock_init() is idempotent, so just init always */
if (ne_sock_init ())
{
if (soft_errors)
return NULL;
panic (_("arch_pfs_dav_connect: ne_sock_init() failed."));
}
answer = (struct arch_pfs_dav_session *)lim_malloc (0, sizeof (*answer));
mem_set0 ((t_uchar *)answer, sizeof (*answer));
answer->proxy_auth = talloc (NULL, struct authinfo);
answer->pfs.vtable = &dav_pfs_fns;
if (!str_cmp_prefix ("http://", uri))
{
answer->dav_scheme = str_save (0, "http");
host = str_save (0, uri + sizeof ("http://") - 1);
}
else if (!str_cmp_prefix ("https://", uri))
{
answer->dav_scheme = str_save (0, "https");
host = str_save (0, uri + sizeof ("https://") - 1);
}
else
{
if (soft_errors)
{
lim_free (0, answer);
return NULL;
}
panic (_("bogus uri to arch_pfs_dav_connect"));
}
root_path = str_chr_index (host, '/');
if (root_path)
{
t_uchar * pos;
pos = root_path;
root_path = str_save (0, root_path);
*pos = 0;
}
hostname = str_chr_index (host, '@');
auth = lim_malloc (0, sizeof (struct authinfo));
if (!hostname)
{
answer->dav_hostname = str_save (0, host);
auth->machine = str_save (0, host);
auth->username = str_save(0, "");
auth->password = str_save(0, "");
}
else
{
user = host;
auth = lim_malloc (0, sizeof (struct authinfo));
passwd = str_chr_index_n (user, hostname - user, ':');
if (passwd++)
{
auth->username = str_save_n (0, user, (passwd - 1) - user);
auth->password = str_save_n (0, passwd, hostname - passwd);
}
else
{
auth->username = str_save_n (0, user, hostname - user);
auth->password = str_save (0, "");
}
/* machine name will be useful to lookup in ~/.authinfo file */
auth->machine = str_save (0, hostname + 1);
answer->dav_hostname = str_save (0, hostname + 1);
}
portstr = str_chr_index (answer->dav_hostname, ':');
if (portstr)
*(portstr++) = 0;
answer->dav_port = ne_uri_defaultport (answer->dav_scheme);
if (portstr && (0 > cvt_decimal_to_uint (&ign, &answer->dav_port, portstr, str_length (portstr))))
{
if (soft_errors)
{
lim_free (0, answer);
/* FIXME free answers resources */
return NULL;
}
safe_printfmt (2, "illegal port number in uri -- %s\n", uri);
exit (2);
}
answer->sess = ne_session_create (answer->dav_scheme, answer->dav_hostname, answer->dav_port);
if (!str_cmp ("https", answer->dav_scheme))
{
ne_ssl_trust_default_ca (answer->sess);
/* accept SSL certificate anyway */
ne_ssl_set_verify(answer->sess, ssl_accept, auth);
}
ne_set_server_auth (answer->sess, dav_client_auth, auth);
ne_set_useragent (answer->sess, "arch-client/0.1");
/*
* Retrieve the proxy. Do not alter the environment variable.
* The proxy variable is formatted as
* scheme://host:port/
* We only support http as the scheme.
*/
proxy = getenv ("http_proxy");
if (proxy)
proxy = str_save (0, proxy);
else
{
proxy = getenv ("HTTP_PROXY");
if (proxy)
proxy = str_save (0, proxy);
}
if (proxy && !str_cmp_prefix ("http://", proxy))
{
t_uchar * proxy_path = proxy + 7;
proxy_port = str_chr_index (proxy_path, ':');
if (proxy_port)
{
unsigned proxy_port_len;
*proxy_port++ = 0;
proxy_port_len = str_length (proxy_port);
/* The port may be followed by a trailing slash (according to
rcollins, it _must_ be followed by a slash, strictly speaking,
but this restriction is widely ignored both by software and by
users, so it's not practical to enforce it, and there's no
real gain from doing so). */
if (proxy_port_len > 0 && proxy_port[proxy_port_len - 1] == '/')
proxy_port_len--;
if (cvt_decimal_to_int (&ign, &proxy_port_num, proxy_port, proxy_port_len))
{
if (soft_errors)
{
lim_free (0, answer);
return NULL;
}
safe_printfmt (2, "ill formated http proxy port number from $http_proxy\n");
exit (2);
}
ne_session_proxy (answer->sess, proxy_path, proxy_port_num);
}
answer->proxy_auth->machine =
talloc_strdup(answer->proxy_auth, proxy_path);
answer->proxy_auth->username =
talloc_strdup(answer->proxy_auth, "");
answer->proxy_auth->password =
talloc_strdup(answer->proxy_auth, "");
ne_set_proxy_auth(answer->sess, dav_client_auth, answer->proxy_auth);
}
if (root_path)
{
int result = dav_client_cwd (answer, root_path, soft_errors);
if (result)
{
if (soft_errors)
{
lim_free (0, answer);
return NULL;
}
panic (_("cannot change DAV cwd."));
}
}
lim_free (0, host);
lim_free (0, root_path);
lim_free (0, proxy);
return (struct arch_pfs_session *)answer;
}
void
fs_disconnect (struct arch_pfs_session **pfs)
{
struct arch_pfs_dav_session * local_pfs = (struct arch_pfs_dav_session *)*pfs;
struct authinfo * proxy_auth = local_pfs->proxy_auth;
talloc_free (proxy_auth);
lim_free (0, *pfs);
*pfs = NULL;
}
static t_uchar *
pfs_file_contents (struct arch_pfs_session * p, t_uchar * path, int soft_errors)
{
struct arch_pfs_dav_session * pfs = (struct arch_pfs_dav_session *)p;
t_uchar * tmp_path = 0;
int fd;
t_uchar * dav_path = 0;
int ne_err;
t_uchar * answer = 0;
tmp_path = tmp_file_name_in_tmp (",,pfs-dav-file-contents");
fd = safe_open (tmp_path, O_RDWR | O_CREAT | O_EXCL, 0000);
safe_unlink (tmp_path);
dav_path = abs_path (pfs->cwd, (char *)path);
ne_err = ne_get (pfs->sess, dav_path, fd);
if (ne_err)
{
if (!soft_errors)
{
safe_printfmt (2, "webdav error: %s\n", ne_get_error (pfs->sess));
exit (2);
}
}
else
{
safe_lseek (fd, (off_t)0, SEEK_SET);
answer = fd_contents (fd);
}
safe_close (fd);
lim_free (0, tmp_path);
lim_free (0, dav_path);
return answer;
}
static rel_table
pfs_directory_files (struct arch_pfs_session * p, t_uchar * path, int soft_errors)
{
struct arch_pfs_dav_session * pfs = (struct arch_pfs_dav_session *)p;
int ne_err;
t_uchar * dir = 0;
t_uchar * file_string = 0;
rel_table answer = 0;
if (pfs->sess_opts.dav_class1)
{
struct ls_data data;
dir = abs_path (pfs->cwd, path);
if (dir[str_length (dir) - 1] != '/')
dir = str_realloc_cat(0, dir, "/");
data.files = str_save (0, "");;
data.uri = dir;
if ((ne_err = ne_simple_propfind (pfs->sess, dir, NE_DEPTH_ONE, ls_props, results, &data)))
{
if (!soft_errors)
{
safe_printfmt (2, "webdav error (directory_files): %s (%s)\n", dir, ne_get_error (pfs->sess));
exit (2);
}
}
else
{
file_string = str_save (0, data.files);
}
lim_free (0, data.files);
}
else
{
dir = file_name_in_vicinity (0, path, LISTING_FILE);
file_string = pfs_file_contents (p, dir, 1);
}
answer = rel_ws_split (file_string);
lim_free (0, dir);
lim_free (0, file_string);
return answer;
}
static int
quick_file_exists (struct arch_pfs_session * p, t_uchar * path)
{
int answer = -1;
struct arch_pfs_dav_session * pfs = (struct arch_pfs_dav_session *) p;
t_uchar * dav_path = abs_path (pfs->cwd, (char *) path);
ne_request *req = ne_request_create (pfs -> sess, "HEAD", dav_path);
int ret = ne_request_dispatch (req);
if (ret == NE_OK)
switch (ne_get_status (req) -> code)
{
case 200:
answer = 1;
break;
case 404:
answer = 0;
break;
default:
break;
};
ne_request_destroy(req);
lim_free(0, dav_path);
return answer;
}
static int
pfs_file_exists (struct arch_pfs_session * p, t_uchar * path)
{
t_uchar * path_dir = 0;
t_uchar * path_tail = 0;
rel_table files = 0;
int answer = -1;
int x;
if (0) answer = quick_file_exists (p, path);
if (answer != -1)
return answer;
else
answer = 0;
/* There is probably a better way to do this in webdav. */
path_dir = file_name_directory_file (0, path);
if (!path_dir)
{
path_dir = str_save (0, ".");
}
path_tail = file_name_tail (0, path);
files = pfs_directory_files (p, path_dir, 1);
for (x = 0; x < rel_n_records (files); ++x)
{
if (!str_cmp (path_tail, files[x][0]))
{
answer = 1;
break;
}
}
lim_free (0, path_dir);
lim_free (0, path_tail);
rel_free_table (files);
return answer;
}
static int
pfs_get_file (struct arch_pfs_session * p, int out_fd, t_uchar * path, int soft_errors)
{
struct arch_pfs_dav_session * pfs = (struct arch_pfs_dav_session *)p;
t_uchar * dav_path = 0;
int ne_err;
int answer = 0;
dav_path = abs_path (pfs->cwd, (char *)path);
ne_err = ne_get (pfs->sess, dav_path, out_fd);
if (ne_err)
{
if (!soft_errors)
{
safe_printfmt (2, "webdav error: %s\n", ne_get_error (pfs->sess));
exit (2);
}
else
answer = -1;
}
lim_free (0, dav_path);
return answer;
}
static int
pfs_put_file (struct arch_pfs_session * p, t_uchar * path, mode_t perms, int in_fd, int soft_errors)
{
struct arch_pfs_dav_session * pfs = (struct arch_pfs_dav_session *)p;
int ne_err;
t_uchar * file;
int answer = 0;
file = abs_path (pfs->cwd, path);
ne_err = ne_put (pfs->sess, file, in_fd);
if (ne_err)
{
if (!soft_errors)
{
safe_printfmt (2, "webdav error(put_file): writing to %s (%s)\n", file, ne_get_error (pfs->sess));
exit (2);
}
else
{
answer = -1;
}
}
lim_free (0, file);
return answer;
}
static int
pfs_mkdir (struct arch_pfs_session * p, t_uchar * path, mode_t mode, int soft_errors)
{
struct arch_pfs_dav_session * pfs = (struct arch_pfs_dav_session *)p;
int ne_err;
t_uchar * dir = 0;
int answer = 0;
dir = abs_path (pfs->cwd, path);
if (dir[str_length (dir) - 1] != '/')
dir = str_realloc_cat (0, dir, "/");
if ((ne_err = ne_mkcol (pfs->sess, dir)))
{
if (!soft_errors)
{
safe_printfmt (2, "webdav error (pfs_mkdir): %s (%s)\n", path, ne_get_error (pfs->sess));
exit (2);
}
else
answer = -1;
}
lim_free (0, dir);
return answer;
}
static int
pfs_rename (struct arch_pfs_session * p, t_uchar ** errstr, t_uchar * from, t_uchar * to, int soft_errors)
{
struct arch_pfs_dav_session * pfs = (struct arch_pfs_dav_session *)p;
int ne_err;
int ret;
t_uchar * from2;
t_uchar * to2;
int result = 0;
from2 = abs_path (pfs->cwd, from);
to2 = abs_path (pfs->cwd, to);
ret = dav_is_dir (pfs, from2);
if (ret == -1)
{
if (errstr)
*errstr = str_save (0, "file does not exist for rename");
result = -1;
}
else
{
if (ret)
{
if (from2[str_length (from2) - 1] != '/')
from2 = str_realloc_cat (0, from2, "/");
if (to2[str_length (to2) - 1] != '/')
to2 = str_realloc_cat (0, to2, "/");
}
if (0 < dav_is_dir (pfs, to2))
{
char * b;
char * file;
b = file = str_save (0, from);
filename:
file = str_chr_rindex (file, '/');
if (!file)
file = b;
else if (!file[1])
{
file[0] = 0;
goto filename;
}
else
file++;
to2 = str_realloc_cat_many (0, to2, "/", file, str_end);
lim_free (0, b);
}
if ((ne_err = ne_move (pfs->sess, 0, from2, to2)))
{
if (errstr)
*errstr = str_save (0, ne_get_error (pfs->sess));
result = -1;
}
}
lim_free (0, from2);
lim_free (0, to2);
return result;
}
static int
pfs_is_dir (struct arch_pfs_session * p, t_uchar * path)
{
struct arch_pfs_dav_session * pfs = (struct arch_pfs_dav_session *)p;
t_uchar * abs = 0;
int answer;
abs = abs_path (pfs->cwd, path);
answer = dav_is_dir (pfs, abs);
lim_free (0, abs);
return answer;
}
static int
dav_is_dir (struct arch_pfs_dav_session * pfs, char * dir)
{
char * filename;
char * parent;
struct dav_is_dir_ls_data data;
int ne_err;
int answer;
parent = str_save (0, dir);
filename = str_chr_rindex (parent, '/');
filename[1] = 0;
data.uri = dir;
data.is_dir = 0;
if((ne_err = ne_simple_propfind (pfs->sess, parent, NE_DEPTH_ONE, ls_props, dav_is_dir_results, &data)))
{
answer = -1;
}
else
{
answer = data.is_dir;
}
lim_free (0, parent);
return answer;
}
static int
pfs_rmdir (struct arch_pfs_session * p, t_uchar * path, int soft_errors)
{
struct arch_pfs_dav_session * pfs = (struct arch_pfs_dav_session *)p;
int ne_err;
t_uchar * dir = 0;
int answer = 0;
dir = abs_path (pfs->cwd, path);
if (dir[str_length (dir) - 1] != '/')
dir = str_realloc_cat (0, dir, "/");
if ((ne_err = ne_delete (pfs->sess, dir)))
{
if (!soft_errors)
{
safe_printfmt (2, "webdav error (pfs_rmdir): %s (%s)\n", dir, ne_get_error (pfs->sess));
exit (2);
}
else
answer = -1;
}
lim_free (0, dir);
return answer;
}
static int
pfs_rm (struct arch_pfs_session * p, t_uchar * path, int soft_errors)
{
struct arch_pfs_dav_session * pfs = (struct arch_pfs_dav_session *)p;
int ne_err;
t_uchar * abs = 0;
int answer = 0;
abs = abs_path (pfs->cwd, path);
if ((ne_err = ne_delete (pfs->sess, abs)))
{
if (!soft_errors)
{
safe_printfmt (2, "webdav error (pfs_rm): %s (%s)\n", path, ne_get_error (pfs->sess));
exit (2);
}
else
answer = -1;
}
lim_free (0, abs);
return 0;
}
static void
dav_is_dir_results (void * userdata, const char * uri, const ne_prop_result_set * set)
{
struct dav_is_dir_ls_data * data = (struct dav_is_dir_ls_data *)userdata;
int len;
len = str_length (data->uri);
if (!str_cmp_n (uri, len, data->uri, len))
{
if (uri[str_length (uri) - 1] == '/')
data->is_dir = 1;
else
data->is_dir = 0;
}
}
static int
dav_client_cwd (struct arch_pfs_dav_session * pfs, t_uchar * path, int soft_errors)
{
int ne_err;
t_uchar * tmp;
t_uchar * dir;
int result = 0;
dir = abs_path (pfs->cwd, path);
if (dir[str_length (dir) - 1] != '/')
dir = str_realloc_cat (0, dir, "/");
if ((ne_err = ne_options (pfs->sess, dir, &pfs->sess_opts)))
{
/* if error assume that server doesn't implement OPTIONS */
pfs->sess_opts.dav_class1 = 0;
pfs->sess_opts.dav_class2 = 0;
pfs->sess_opts.dav_executable = 0;
}
if (pfs->sess_opts.dav_class1) /* if the server supports DAV */
{
if ((ne_err = ne_simple_propfind (pfs->sess, dir, NE_DEPTH_ONE, ls_props, NULL, NULL)))
{
if (soft_errors)
{
result = -1;
goto exit;
}
safe_printfmt (2, "webdav error: %s\n", ne_get_error (pfs->sess));
exit (2);
}
}
else /* no dav, so look for a listing file */
{
time_t mtime;
char * dirlisting = 0;
dirlisting = str_alloc_cat (0, dir, LISTING_FILE);
if ((ne_err = ne_getmodtime (pfs->sess, dirlisting, &mtime)))
{
if (soft_errors)
{
result = -1;
goto exit;
}
safe_printfmt (2, "unable to access URL: %s\nwebdav error: %s\n", dirlisting, ne_get_error (pfs->sess));
exit (2);
}
lim_free (0, dirlisting);
}
tmp = ne_path_unescape (dir);
if (pfs->cwd)
free (pfs->cwd);
pfs->cwd = tmp;
exit:
lim_free (0, dir);
return result;
}
static int
dav_client_auth (void * userdata, const char * realm, int attempt,
char * username, char * password)
{
struct authinfo *auth;
int len;
if (attempt > 1)
return attempt;
/* Since we got a void *, we need to cast it. This is an easy way
* to do so. */
auth = userdata;
len = str_length (auth->username);
if (len)
str_cpy_n (username, auth->username, MIN (len, NE_ABUFSIZ - 1));
else
{
/* User is not set. Try getting it from the authinfo file */
t_uchar * get_user = NULL;
get_user = arch_auth_username (auth->machine);
len = str_length(get_user);
str_cpy_n (username, get_user, MIN (len, NE_ABUFSIZ - 1));
lim_free (0, get_user);
}
len = str_length (auth->password);
if (len)
str_cpy_n (password, auth->password, MIN (len, NE_ABUFSIZ - 1));
else
{
/* pass not set. try getting it from the authinfo file */
t_uchar * get_pass = NULL;
get_pass = arch_auth_password (username, auth->machine);
len = str_length(get_pass);
str_cpy_n (password, get_pass, MIN (len, NE_ABUFSIZ - 1));
lim_free (0, get_pass);
}
return 0;
}
static t_uchar *
abs_path (t_uchar * cwd, t_uchar * path)
{
t_uchar * file;
t_uchar * tmp;
if (cwd == NULL)
cwd = "/";
if (path[0] != '/')
file = str_alloc_cat (0, cwd, path);
else
file = str_save (0, path);
dirfold (file);
tmp = ne_path_escape (file);
lim_free (0, file);
file = str_save (0, tmp);
free (tmp);
return file;
}
static t_uchar *
dirfold (t_uchar *dir)
{
t_uchar * buf;
t_uchar * this;
t_uchar * next;
int dir_i = 0;
this = next = buf = str_save (0, dir);
while ((this = str_separate (&next, "/")) != NULL)
{
if (str_length (this) == 0 || (str_length (this) == 1 && this[0] == '.'))
continue;
else if (str_length (this) == 2 && *this == '.' && this[1] == '.')
{
if (dir_i > 0)
dir_i = (int)(strrchr (dir, '/') - (char *)dir);
dir[dir_i] = 0;
}
else
{
dir[dir_i++] = '/';
strcpy (dir + dir_i, this);
dir_i += str_length (this);
}
}
lim_free (0, buf);
if (!str_length (dir))
str_cpy (dir, "/");
return dir;
}
static void
results (void * userdata, const char * uri, const ne_prop_result_set * set)
{
int n;
char * file, * tmp;
struct ls_data * data = userdata;
if (str_cmp (data->uri, uri))
{
if (1 == (n = str_length (uri)))
return;
if (uri[n - 1] == '/')
n--;
file = str_chr_rindex_n (uri, n, '/') + 1;
n = str_length (file);
if (file[n - 1] == '/')
n--;
file = str_save_n (0, file, n);
tmp = ne_path_unescape (file);
lim_free (0, file);
data->files = str_realloc_cat_many (0, data->files, tmp, "\r\n", str_end);
free (tmp);
}
}
/* tag: Tom Lord Thu Jun 5 15:23:06 2003 (pfs-dav.c)
*/
syntax highlighted by Code2HTML, v. 0.9.1