"Fossies" - the Fresh Open Source Software Archive

Member "sitecopy-0.16.6/src/davdriver.c" (16 Jul 2008, 20764 Bytes) of archive /linux/www/sitecopy-0.16.6.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format (assuming markdown format). Alternatively you can here view or download the uninterpreted source code file. A member file download can also be achieved by clicking within a package contents listing on the according byte size field.

/* sitecopy WebDAV protocol driver module Copyright © 2000-2008, Joe Orton joe@manyfish.co.uk

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.

*/

include “config.h”

include <sys/types.h>

include <sys/stat.h> / For S_IXUSR /

ifdef HAVE_STRING_H

include <string.h>

endif

ifdef HAVE_STDLIB_H

include <stdlib.h>

endif

ifdef HAVE_FCNTL_H

include <fcntl.h>

endif

ifdef HAVE_UNISTD_H

include <unistd.h>

endif

include <errno.h>

include “common.h” / for strerror /

include <ne_request.h>

include <ne_basic.h>

include <ne_basic.h>

include <ne_props.h>

include <ne_alloc.h>

include <ne_uri.h>

include <ne_auth.h>

include <ne_dates.h>

include <ne_socket.h>

include “protocol.h”

include “frontend.h”

include “i18n.h”

include “common.h”

struct fetch_context { struct proto_file files; struct proto_file tail; const char root; };

define ENABLE_PROGRESS do { ne_set_progress(sess, site_sock_progress_cb, NULL); } while (0)

if NE_VERSION_MAJOR == 0 && NE_VERSION_MINOR > 26

define DISABLE_PROGRESS do { ne_set_notifier(sess, NULL, NULL); } while (0)

else

define DISABLE_PROGRESS do { ne_set_progress(sess, NULL, NULL); } while (0)

endif

/ TODO: * not really sure whether we should be using an enum here… what * should the client do with resourcetypes it doesn’t understand? * ignore them, or presume they have the same semantics as “normal” * resources. I really don’t know. / struct private { int iscollection; };

define ELM_resourcetype (NE_PROPS_STATE_TOP + 1)

define ELM_collection (NE_PROPS_STATE_TOP + 2)

/ The element definitions for the complex prop handler. / static const struct ne_xml_idmap fetch_elms[] = { { “DAV:”, “resourcetype”, ELM_resourcetype }, { “DAV:”, “collection”, ELM_collection }, };

static const ne_propname props[] = { { “DAV:”, “getcontentlength” }, { “DAV:”, “getlastmodified” }, { “http://apache.org/dav/props/”, “executable” }, { “DAV:”, “resourcetype” }, { NULL } };

/ Set session error string to ‘msg: strerror(errnum)’. / static void syserr(ne_session sess, const char msg, int errnum) { char err[256]; ne_set_error(sess, “%s: %s”, msg, ne_strerror(errnum, err, sizeof err)); }

static int get_server_port(struct site *site) { return site->http_secure ? 443 : 80; }

static int get_proxy_port(struct site *site) { return 8080; }

static int auth_common(void userdata, fe_login_context ctx, const char realm, int attempt, char username, char password) { struct site_host *host = userdata; if (host->username && host->password) { strcpy(username, host->username); strcpy(password, host->password); return attempt; } else { return fe_login(ctx, realm, host->hostname, username, password); } }

static int server_auth_cb(void userdata, const char realm, int attempt, char username, char password) { return auth_common(userdata, fe_login_server, realm, attempt, username, password); }

static int proxy_auth_cb(void userdata, const char realm, int attempt, char username, char password) { return auth_common(userdata, fe_login_proxy, realm, attempt, username, password); }

if NE_VERSION_MINOR < 27

static void notify_cb(void userdata, ne_conn_status status, const char info) {

define MAP(a) case ne_conn##a: fe_connection(fe##a, info); break

switch (status) {
MAP(namelookup);
MAP(connecting);
MAP(connected);
default:
break;
}

undef MAP

}

else

static void notify_status(void userdata, ne_session_status status, const ne_session_status_info info) { switch (status) { case ne_status_lookup: fe_connection(fe_namelookup, info->lu.hostname); break; case ne_status_connecting: fe_connection(fe_connecting, NULL); break; case ne_status_connected: fe_connection(fe_connected, NULL); break; default: break; } }

endif

static int h2s(ne_session *sess, int errcode) { switch (errcode) { case NE_OK: return SITE_OK; case NE_AUTH: return SITE_AUTH; case NE_PROXYAUTH: return SITE_PROXYAUTH; case NE_FAILED: return SITE_FAILED; case NE_CONNECT: return SITE_CONNECT; case NE_LOOKUP: return SITE_LOOKUP; case NE_TIMEOUT: ne_set_error(sess, _(“The connection timed out.”)); return SITE_ERRORS; case NE_ERROR: default: return SITE_ERRORS; } }

/ Callback invoked when SSL server cert verification fails. / static int verify_certificate(void userdata, int failures, const ne_ssl_certificate cert) { struct site *site = userdata;

/* If the server cert has not changed since the user accepted it,
 * trust the cert, unless it has expired, in which case the user
 * should get a warning. */
if (site->server_cert 
    && ne_ssl_cert_cmp(cert, site->server_cert) == 0
    && (failures & NE_SSL_EXPIRED) == 0) {
    return 0;
}

if (fe_accept_cert(cert, failures)) {
    /* Not accepted by user => fail verification. */
    return -1;
}

if (ne_ssl_cert_write(cert, site->certfile)) {
    fe_warning(_("Could not write SSL certificate"),
               NULL, site->certfile);
}

return 0;

}

static int init(void session, struct site site) { ne_session sess; ne_server_capabilities caps = {0}; int ret; char *root;

sess = ne_session_create(site->http_secure?"https":"http",
             site->server.hostname, site->server.port);

*session = sess;

if (site->http_secure && !ne_has_support(NE_FEATURE_SSL)) {
ne_set_error(sess, _("SSL support has not be compiled in."));
return SITE_FAILED;
}

if (site->http_secure) {
    if (access(site->certfile, R_OK) == 0) {
        site->server_cert = ne_ssl_cert_read(site->certfile);
        if (site->server_cert == NULL) {
            ne_set_error(sess, _("Could not load certificate `%s'."),
                         site->certfile);
            return SITE_FAILED;
        }
    }
    ne_ssl_set_verify(sess, verify_certificate, site);
}

if NE_VERSION_MINOR < 27

ne_set_status(sess, notify_cb, NULL);

else

ne_set_notifier(sess, notify_status, NULL);

endif

if (site->http_limit) {

if NE_VERSION_MINOR > 25

    ne_set_session_flag(sess, NE_SESSFLAG_PERSIST, 0);

else

    ne_set_persist(sess, 0);

endif

}

/* Note, this won't differentiate between xsitecopy and
 * sitecopy... maybe we should put a comment in as well. */
ne_set_useragent(sess, PACKAGE_NAME "/" PACKAGE_VERSION);

if (site->proxy.hostname) {
ne_set_proxy_auth(sess, proxy_auth_cb, &site->proxy);
ne_session_proxy(sess, site->proxy.hostname, site->proxy.port);
}

ne_set_server_auth(sess, server_auth_cb, &site->server);

if (site->http_secure && site->client_cert) {
    ne_ssl_client_cert *cc;

    cc = ne_ssl_clicert_read(site->client_cert);
    if (!cc) {
        ne_set_error(sess, _("Could not read SSL client certificate '%s'."),
                     site->client_cert);
        return SITE_FAILED;
    }

    if (ne_ssl_clicert_encrypted(cc)) {
        char password[FE_LBUFSIZ];

        if (fe_decrypt_clicert(cc, password)) {
            return SITE_FAILED;
        }

        if (ne_ssl_clicert_decrypt(cc, password) != 0) {
            ne_set_error(sess, _("Could not decrypt SSL client "
                                 "certificate '%s'."), site->client_cert);
            return SITE_FAILED;
        }
    }

    ne_ssl_set_clicert(sess, cc);

    ne_ssl_clicert_free(cc);
}

if (site->http_tolerant) {
/* Skip the OPTIONS, since we ignore failure anyway. */
return SITE_OK;
}

root = ne_path_escape(site->remote_root);
ret = ne_options(sess, root, &caps);
ne_free(root);
if (ret == NE_OK) {
if (!caps.dav_class1) {
    ne_set_error(sess, 
            _("The server does not appear to be a WebDAV server."));
    return SITE_FAILED;
} else if (site->perms != sitep_ignore && !caps.dav_executable) {
    /* Need to set permissions, but the server can't do that */
    ne_set_error(sess, 
            _("The server does not support the executable live property."));
    return SITE_FAILED;
}
} else {
    ret = h2s(sess, ret);
    if (ret == SITE_ERRORS)
        ret = SITE_FAILED;
    return ret;
}


return SITE_OK;

}

static void finish(void session) { ne_session sess = session; ne_session_destroy(sess); }

static int file_move(void session, const char from, const char to) { ne_session sess = session; char efrom, eto; int ret;

efrom = ne_path_escape(from);
eto = ne_path_escape(to);

/* Always overwrite destination. */
ret = ne_move(sess, 1, efrom, eto);

free(efrom);
free(eto);

return h2s(sess, ret);

}

static int file_upload(void session, const char local, const char remote, int ascii) { int ret, fd = open(local, O_RDONLY | OPEN_BINARY_FLAGS); ne_session sess = session; char *eremote;

if (fd < 0) {
syserr(sess, _("Could not open file"), errno);
return SITE_ERRORS;
}

eremote = ne_path_escape(remote);
ENABLE_PROGRESS;
ret = ne_put(sess, eremote, fd);
DISABLE_PROGRESS;
free(eremote);

(void) close(fd);

return h2s(sess, ret);

}

/ conditional PUT using If-Unmodified-Since. / static int put_if_unmodified(ne_session sess, const char uri, int fd, time_t since) { ne_request req = ne_request_create(sess, “PUT”, uri); char date = ne_rfc1123_date(since); int ret;

/* Add in the conditional header */
ne_add_request_header(req, "If-Unmodified-Since", date);
ne_free(date);

if NE_VERSION_MINOR == 24

ne_set_request_body_fd(req, fd);

else

{
    struct stat st;

    if (fstat(fd, &st) < 0) {
        int errnum = errno;
        ne_set_error(sess, _("Could not stat file: %s"), strerror(errnum));
        return NE_ERROR;
    }

    ne_set_request_body_fd(req, fd, 0, st.st_size);
}

endif

ret = ne_request_dispatch(req);

if (ret == NE_OK) {
if (ne_get_status(req)->code == 412) {
    ret = NE_FAILED;
} else if (ne_get_status(req)->klass != 2) {
    ret = NE_ERROR;
}
}

ne_request_destroy(req);

return ret;

}

static int file_upload_cond(void session, const char local, const char remote, int ascii, time_t t) { ne_session sess = session; int ret, fd = open(local, O_RDONLY | OPEN_BINARY_FLAGS); char *eremote;

if (fd < 0) {
syserr(sess, _("Could not open file"), errno);
return SITE_ERRORS;
}

eremote = ne_path_escape(remote);
ENABLE_PROGRESS;
ret = h2s(sess, put_if_unmodified(sess, eremote, fd, t));
DISABLE_PROGRESS;
free(eremote);

(void) close(fd);

return ret;

}

static int file_get_modtime(void session, const char remote, time_t modtime) { ne_session sess = session; int ret; char *eremote;

eremote = ne_path_escape(remote);
ret = ne_getmodtime(sess,eremote,modtime);
free(eremote);

return h2s(sess, ret);

}

static int file_download(void session, const char local, const char remote, int ascii) { ne_session sess = session; int ret, fd = open(local, O_TRUNC | O_CREAT | O_WRONLY | OPEN_BINARY_FLAGS, 0644); char *eremote;

if (fd < 0) {
syserr(sess, _("Could not open file"), errno);
return SITE_ERRORS;
}

eremote = ne_path_escape(remote);
ENABLE_PROGRESS;
ret = h2s(sess, ne_get(sess, eremote, fd));
DISABLE_PROGRESS;
free(eremote);

if (close(fd)) {
ret = SITE_ERRORS;
}

return ret;

}

static int file_read(void session, const char remote, ne_block_reader reader, void userdata) { ne_session sess = session; int ret; char eremote; ssize_t bytes; ne_request req;

eremote = ne_path_escape(remote);    
req = ne_request_create(sess, "GET", eremote);
ENABLE_PROGRESS;
do {
char buf[BUFSIZ];
ret = ne_begin_request(req);
if (ret != NE_OK) break;
while ((bytes = ne_read_response_block(req, buf, sizeof buf)) > 0)
    reader(userdata, buf, bytes);
if (bytes < 0)
    ret = NE_ERROR;
else
    ret = ne_end_request(req);
} while (ret == NE_RETRY);
DISABLE_PROGRESS;
free(eremote);

return h2s(sess, ret);

}

static int file_delete(void session, const char remote) { ne_session sess = session; char eremote = ne_path_escape(remote); int ret = ne_delete(sess, eremote);

free(eremote);
return h2s(sess, ret);

}

static int file_chmod(void session, const char remote, mode_t mode) { ne_session sess = session; static const ne_propname execprop = { “http://apache.org/dav/props/”, “executable” }; / Use a single operation; set the executable property to… / ne_proppatch_operation ops[] = { { &execprop, ne_propset, NULL }, { NULL } }; char eremote = ne_path_escape(remote); int ret;

/* True or false, depending... */
if (mode & S_IXUSR) {
ops[0].value = "T";
} else {
ops[0].value = "F";
}

ret = ne_proppatch(sess, eremote, ops);
free(eremote);

return h2s(sess, ret);

}

/ Returns escaped path string for a directory. / static char coll_escape(const char dirname) { char *ret = ne_path_escape(dirname); if (!ne_path_has_trailing_slash(ret)) { ret = ne_realloc(ret, strlen(ret) + 2); strcat(ret, “/”); } return ret; }

static int dir_create(void session, const char dirname) { ne_session sess = session; char edirname = coll_escape(dirname); int ret = ne_mkcol(sess, edirname); free(edirname); return h2s(sess, ret); }

/ TODO: check whether it is empty first / static int dir_remove(void session, const char dirname) { ne_session sess = session; char edirname = coll_escape(dirname); int ret = ne_delete(sess, edirname); free(edirname); return h2s(sess, ret); }

/ Insert the file in the list in the appropriate position (keeping it * sorted). / static void insert_file(struct fetch_context ctx, struct proto_file file) { if (ctx->tail) { ctx->tail->next = file; } else { (*ctx->files) = file; } ctx->tail = file; file->next = NULL; }

if NE_VERSION_MINOR > 25

static void pfind_results(void userdata, const ne_uri uri, const ne_prop_result_set *set)

else

static void pfind_results(void userdata, const char href, const ne_prop_result_set *set)

endif

{ struct fetch_context ctx = userdata; struct private private = ne_propset_private(set); const char clength = NULL, modtime = NULL, isexec = NULL; struct proto_file file; int iscoll; char *uhref;

iscoll = private->iscollection;

if NE_VERSION_MINOR < 26

/* For >= 0.26, this is handled by the destroy_private
 * callback. */
ne_free(private);

endif

if NE_VERSION_MINOR < 26

/* Strip down to the abspath segment */
if (strncmp(href, "http://", 7) == 0)
href = strchr(href+7, '/');

if (strncmp(href, "https://", 8) == 0)
href = strchr(href+8, '/');

if (href == NULL) {
NE_DEBUG(NE_DBG_HTTP, "invalid!\n");
return;
}

uhref = ne_path_unescape(href);

else

uhref = ne_path_unescape(uri->path);

endif

NE_DEBUG(NE_DBG_HTTP, "URI: [%s]: ", uhref);

if (!ne_path_childof(ctx->root, uhref)) {
/* URI not a child of the root collection...  ignore this
 * resource */
NE_DEBUG(NE_DBG_HTTP, "not child of root collection!\n");
return;
}

NE_DEBUG(NE_DBG_HTTP, "okay.\n");

if (!iscoll) {
const ne_status *status = NULL;

clength = ne_propset_value(set, &props[0]);    
modtime = ne_propset_value(set, &props[1]);
isexec = ne_propset_value(set, &props[2]);

if (clength == NULL)
    status = ne_propset_status(set, &props[0]);
if (modtime == NULL)
    status = ne_propset_status(set, &props[1]);

if (clength == NULL || modtime == NULL) {
    fe_warning(_("Could not access resource"), uhref, 
           status?status->reason_phrase:NULL);
    return;
}

}

file = ne_calloc(sizeof(struct proto_file));
file->filename = ne_strdup(uhref+strlen(ctx->root));

if (iscoll) {
file->type = proto_dir;

/* Strip the trailing slash if it has one. */
if (ne_path_has_trailing_slash(file->filename)) {
    file->filename[strlen(file->filename) - 1] = '\0';
}

} else {
file->type = proto_file;
file->size = atoi(clength);
file->modtime = modtime?ne_httpdate_parse(modtime):0;
if (isexec && strcasecmp(isexec, "T") == 0) {
    file->mode = 0755;
} else {
    file->mode = 0644;
}
}

/* Insert the file into the files list. */
insert_file(ctx, file);

}

static int start_element(void userdata, int parent, const char nspace, const char name, const char **atts) { ne_propfind_handler handler = userdata; struct private *priv = ne_propfind_current_private(handler); int state;

state = ne_xml_mapid(fetch_elms, NE_XML_MAPLEN(fetch_elms), nspace, name);

if (parent == NE_207_STATE_PROP && state == ELM_resourcetype)
    return ELM_resourcetype;

if (parent == ELM_resourcetype && state == ELM_collection)
    priv->iscollection = 1;

return NE_XML_DECLINE;

}

/ Creates the private structure. /

if NE_VERSION_MINOR > 25

static void create_private(void userdata, const ne_uri *uri)

else

static void create_private(void userdata, const char *uri)

endif

{ return ne_calloc(sizeof(struct private)); }

if NE_VERSION_MINOR > 25

static void destroy_private(void userdata, void private) { struct private *priv = private;

ne_free(priv);

}

endif

/ TODO: optimize: only ask for lastmod + executable when we really * need them: it does waste bandwidth and time to ask for executable * when we don’t want it, since it forces a 404 propstat for each * non-collection resource if it is not defined. / static int fetch_list(void session, const char dirname, int need_modtimes, struct proto_file files) { ne_session sess = session; int ret; struct fetch_context ctx; ne_propfind_handler ph; char *edirname = ne_path_escape(dirname);

ctx.root = dirname;
ctx.files = files;
ctx.tail = NULL;
ph = ne_propfind_create(sess, edirname, NE_DEPTH_ONE);

/* The complex props. */
ne_propfind_set_private(ph, create_private,

if NE_VERSION_MINOR > 25

                        destroy_private, 

endif

                        NULL);

/* Register the handler for the complex props. */
ne_xml_push_handler(ne_propfind_get_parser(ph), start_element, NULL, NULL, ph);

ret = ne_propfind_named(ph, props, pfind_results, &ctx);

free(edirname);

return h2s(sess,ret);

}

static int unimp_link2(void session, const char l, const char target) { ne_session sess = session; ne_set_error(sess, “Operation not supported”); return SITE_UNSUPPORTED; }

static int unimp_link1(void session, const char l) { ne_session *sess = session; ne_set_error(sess, “Operation not supported”); return SITE_UNSUPPORTED; }

static const char error(void session) { ne_session *sess = session; return ne_get_error(sess); }

/ The WebDAV protocol driver / const struct proto_driver dav_driver = { init, finish, file_move, file_upload, file_upload_cond, file_get_modtime, file_download, file_read, file_delete, file_chmod, dir_create, dir_remove, unimp_link2, / create link / unimp_link2, / change link target / unimp_link1, / delete link / fetch_list, error, get_server_port, get_proxy_port, “WebDAV” };