Logo Search packages:      
Sourcecode: davfs2 version File versions  Download package

webdav.c

/* 
** Neon and CODA  nterface
**
** This library and program is free software; you can redistribute it and/or
** modify it under the terms of the GNU Library General Public
** License as published by the Free Software Foundation; either
** version 2 of the License, or (at your option) any later version.
**   
** This library 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
** Library General Public License for more details.
**
** You should have received a copy of the GNU Library General Public
** License along with this library; if not, write to the Free
** Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
** MA 02111-1307, USA
*/

#include "config.h"

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <ne_auth.h>
#include <ne_basic.h>
#include <ne_dates.h>
#include <ne_locks.h>
#include <ne_props.h>
#include <ne_request.h>
#include <ne_socket.h>
#include <ne_uri.h>

#include "webdav.h"


extern struct stat dav_file_stat;
extern struct stat dav_dir_stat;
/*2005-05-29, werner, security fix
extern int dav_noexec;*/
extern char *dav_url;
/* Global variables */
static ne_session *session;

char *dav_username;
char *dav_password;
char *dav_p_user;
char *dav_p_password;

static ne_uri server = { 0 };        /* current URI of server. */
/* Global options */

static char *path;            /* current working collection */

static ne_lock_store *lock_store;

static int dav_errno; /* dav error number */

/* When > 0 this indicates to mount even without connection, must
   be reset when the filesystem is mounted. */
int mount_anyway = 0;


#define STATUS(x)  (atoi(ne_get_error(session))==(x))

#if 0
/* Props list for ls */ 
static const ne_propname ls_props[] = {
    {"DAV:", "displayname"},
    {"DAV:", "resourcetype"},
    {NULL}
};
#endif

/* Props list for stat */
static const ne_propname stat_props[] = {
    {"DAV:", "displayname"},
    {"DAV:", "resourcetype"},
    {"DAV:", "getcontentlength"},
    {"DAV:", "getlastmodified"},
    {"http://apache.org/dav/props/", "executable"},
    {NULL}
};

void dav_close_connection(void)
{
    /* NE BUG? */
    if(session)
    ne_session_destroy(session);
    
    NE_FREE(path);
    
    DBG1("Connection to `%s' closed.\n", server.host);
}


/* HTTP STATUS and errno Mapping */
static int dav_set_errno() {
    int http_status=500;
    const char *err_msg = ne_get_error(session);
    DBG1("NE_ERROR: %s", err_msg);
    
    /* parse the status line */
    if (err_msg)
    http_status = atoi(err_msg);
    
    switch(http_status) {
    case 204:
    case 200:
    case 201:
    case 207:
    dav_errno = 0;
    break;
    
    case 400:
    dav_errno = EINVAL;
    break;
    
    case 401:
    dav_errno = EPERM;
    break;

    case 404:
    dav_errno = ENOENT;
    break;
    
    case 423:
    dav_errno = EDEADLK;
    break;
    
    case 500:
    default:
    dav_errno = EIO;
    break;
    }
    
    return  dav_errno ? -1 : 0;
}

/* Get error */
int dav_get_errno() {
    return dav_errno;
}

/* Call-back-function called by neon to get credentials.*/
static int auth(void *ud, const char *realm, int attempt,
                char *username, char *password) {
    DBG2("Neon wants credentials for %s. %i. attempt.\n", realm, attempt);
    if (dav_username != NULL)
        strncpy(username, dav_username, NE_ABUFSIZ - 1);
    if (dav_password != NULL)
        strncpy(password, dav_password, NE_ABUFSIZ - 1);
    return attempt;
}

/* Call-back-function called by neon to get credentials.*/
static int proxy_auth(void *ud, const char *realm, int attempt,
                      char *username, char *password) {
    DBG2("Neon wants credentials for %s. %i. attempt.\n", realm, attempt);
    if (dav_p_user != NULL)
        strncpy(username, dav_p_user, NE_ABUFSIZ - 1);
    if (dav_p_password != NULL)
        strncpy(password, dav_p_password, NE_ABUFSIZ - 1);
    return attempt;
}

/* Allow only certificates verified by CAs that come with openssl,
    or ask user.
   TODO: Better handling of the case when no terminal is available. */
static int fail_verify(void *ud, int fs, const ne_ssl_certificate *cert) {
    char *issuer = ne_ssl_readable_dname(ne_ssl_cert_issuer(cert));
    char *subject = ne_ssl_readable_dname(ne_ssl_cert_subject(cert));
    char *digest = ne_calloc(NE_SSL_DIGESTLEN);
    if (ne_ssl_cert_digest(cert, digest) != 0)
        NE_FREE(digest);
    if (printf("Server cerifticate could not be verified.\n") <= 0) {
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR),
               "Certificate validation request failed");
        return -1;
    }
    printf("  presented for `%s':\n", ne_ssl_cert_identity(cert));
    printf("  Issuer:  %s\n", issuer);
    printf("  Subject: %s\n", subject);
    printf("  Fingerprint: %s\n", digest);
    NE_FREE(issuer);
    NE_FREE(subject);
    NE_FREE(digest);
    char *s = NULL;
    size_t n = 0;
    ssize_t len = 0;
    printf("If you can't verify the fingerprint the server may be faked\n");
    printf("or there may be a man-in-the-middle-attack!\n");
    printf("I am not a coward and accept the certificate anyway [y,N]? "),
    len = getline(&s, &n, stdin);
    if (len < 0)
        abort();
    if (*(s + len -1) == '\n')
        *(s + len -1) = '\0';
    if (strcmp(s, "y") == 0) {
        NE_FREE(s);
        return 0;
    } else {
        NE_FREE(s);
        return -1;
    }
}

static void set_close_head(ne_request *req, void *userdata, ne_buffer *hdr)
{
    ne_buffer_zappend(hdr, "Connection: close\r\n");
}

static int dav_init_session(dav_args *args) {
    /* Lock info */
    lock_store = ne_lockstore_create(); // TODO: free me
    ne_lockstore_register(lock_store, session);

    /* Set timeout */
    ne_set_read_timeout(session, DAV_READ_TIMEOUT);

    
#if NE_VERSION_MINOR == 24
    ne_set_expect100(session, 1);
#endif
    
    char *useragent = ne_concat(PACKAGE_TARNAME, "/", PACKAGE_VERSION, NULL);
    ne_set_useragent(session, useragent);
    NE_FREE(useragent);
    
    ne_hook_pre_send(session, set_close_head, NULL);
    
    ne_set_server_auth(session, auth, NULL);

    if (args->p_host != NULL) {
        DBG2("Set proxy: %s:%d\n", args->p_host, args->p_port);
        ne_session_proxy(session, args->p_host, args->p_port);
        ne_set_proxy_auth(session, proxy_auth, NULL);
    }
    
    
    if (strcasecmp(server.scheme, "https") == 0) {
        ne_ssl_set_verify(session, fail_verify, NULL);
        ne_ssl_trust_default_ca(session);
    }
    return 0;
}    


int dav_open_connection(dav_args *args) {
    int ret;
       
    ne_server_capabilities caps;

    server.path = NULL;        /* this is used as a temporary store. */
    
    /* Parse the URL */
    if (ne_uri_parse(dav_url, &server) || server.host == NULL) {
        fprintf(stderr, "Could not parse URL `%s'\n", dav_url);
        return -1;
    }
    if( strlen(server.path)>1 && *(server.path+strlen(server.path)-1)=='/')
            *(server.path+strlen(server.path)-1)=0;

    if (server.scheme == NULL)
        server.scheme = ne_strdup("http");

    if (!server.port)
        server.port = ne_uri_defaultport(server.scheme);

    if (strcasecmp(server.scheme, "https") == 0) {
#if NE_VERSION_MINOR == 24
        if (!ne_supports_ssl()) {
#else
        if (!ne_has_support(NE_FEATURE_SSL)) {
#endif
            fprintf(stderr, "SSL is not enabled.\n");
            return -1;
        }
    }

    session = ne_session_create(server.scheme, server.host, server.port);

    /* Initialize the session */
    dav_init_session(args);

    /* Ugly workaround for http-escaped urls and non-http-escaped
       urls with spaces. */
    char *tmp_path = ne_path_unescape(server.path);
    NE_FREE(server.path);
    server.path = tmp_path;

    if (strcmp(server.path, "/") == 0) {
        tmp_path = ne_strdup("/");
    } else {
        char *p = ne_concat(server.path, "/", NULL);
        tmp_path = ne_path_escape(p);
        NE_FREE(p);
    }
    ret = ne_options(session, tmp_path, &caps);
    NE_FREE(tmp_path);
    path = server.path;

    switch (ret) {
    case NE_OK:
        break;
    
    case NE_CONNECT:
        if (args->p_host) {
            fprintf(stderr, "Could not connect to `%s' on port %d:\n  %s\n",
                    args->p_host, args->p_port, ne_get_error(session));
        } 
        else {
            fprintf(stderr, "Could not connect to `%s' on port %d:\n  %s\n",
                    server.host, server.port, ne_get_error(session));
        }

        if (args->mountanyway) {
                mount_anyway = 2;
                DBG0("Mounting whithout connection to the server.");
                fprintf(stderr, "Mounting whithout connection to the server.\n");
                return 0;
        } else {
            char *s = NULL;
            size_t n = 0;
            ssize_t len = 0;
            printf("Do you want to mount the filesystem anyway?\n");
            printf("It will only be useable when the server becomes"
                   " reachable again.\n");
            printf("Mount anyway [y,N]? "),
            len = getline(&s, &n, stdin);
            if (len < 0)
                abort();
            if (*(s + len -1) == '\n')
                *(s + len -1) = '\0';
            if (strcmp(s, "y") == 0) {
                NE_FREE(s);
                mount_anyway = 2;
                DBG0("Mounting whithout connection to the server.");
                return 0;
            }
            NE_FREE(s);
        }
        break;
    
    case NE_LOOKUP:
        fprintf(stderr, ne_get_error(session));
        break;
    
    default:
        /* FIXME: This is NOT a "could not open connection" error */
        fprintf(stderr, "Could not contact server:\n%s\n", ne_get_error(session));
        break;
    }
   
    return dav_set_errno();
}


/* Free dav_prop_result */
void dav_prop_result_destroy (dav_prop_result *res) {
    
    dav_prop_result *tofree;
    
    while(res) {
        /* Free name */
        NE_FREE(res->fname);
    
        tofree = res;
        res = res->next;
        NE_FREE(tofree);
    }    
    
    return;
}


/* Merge current path and filename */
static char *resolve_path(const char *p, const char *filename, int isdir)
{
    char *ret, *escaped_uri;
    char *path, *fname;

    while(*filename=='/')
        filename++;

    path = ne_strdup(p);
    fname = ne_strdup(filename);

    dav_no_trail(path);
    dav_no_trail(fname);

    if(strlen(fname)) {
        if (isdir) 
            ret = ne_concat(path, "/", fname, "/", NULL);
        else 
            ret = ne_concat(path, "/", fname, NULL);
    }
    else { /* No fname */
        if(isdir) 
            ret = ne_concat(path, "/", NULL);
        else 
            ret = ne_strdup(path);
    }

    /* Just in case, fname, path is / and isnot dir */
    if (strlen(ret)==0) {
        NE_FREE(ret);
        ret = ne_strdup("/");
    }
    
    escaped_uri = ne_path_escape(ret);
    
    NE_FREE(path);
    NE_FREE(fname);
    NE_FREE(ret);
   
    return escaped_uri;
}

/*
** callback hander to save results
*/
static void results(void *userdata, const char *o_uri,
            const ne_prop_result_set * set)
{
    dav_prop_result_header *hdr = userdata;
    dav_prop_result *result;
    const ne_status *status = NULL;
    const char *data;
    int is_dir = 0;

    /* o_uri may be a compete url or only a path.
       We need the path. */
    const char *uri;
    char *url = ne_uri_unparse(&server);
    *(url + strlen(url) - strlen(server.path)) = '\0';
    if (strstr(o_uri, url) == o_uri) {
        uri = o_uri + strlen(url);
    } else {
        uri = o_uri;
    }
    NE_FREE(url);

    /* Allocation for one result */
    result = ne_calloc(sizeof(*result));

    /* displayname */
    data = ne_propset_value(set, &stat_props[0]);
    if (data) {
        result->fname = ne_path_unescape(data);
            if( strlen(result->fname)>0
                            && result->fname[strlen(result->fname)-1]=='/')
                result->fname[strlen(result->fname)-1]=0;
    } else {
        char *parent = ne_path_parent(uri);
        char *fname;
        /* It is / */
        if (parent==NULL) 
            result->fname = ne_strdup(uri);
        else {
            char *tofree;
            tofree = fname = ne_path_unescape(uri+strlen(parent));
            while(*fname=='/')
                fname++;
        
            if( strlen(fname)>0 && *(fname+strlen(fname)-1)=='/')
                *(fname+strlen(fname)-1)=0;

            result->fname = ne_strdup(fname); //TODO: free me
            NE_FREE(tofree);
        
            /* parent needs to unescape too */
            tofree = parent;
            parent = ne_path_unescape(parent);
            NE_FREE(tofree);
        }
        NE_FREE(parent);
    }
    
    data = ne_propset_value(set, &stat_props[1]);
    
    
    if (data && strstr(data, "collection")) {
        result->f_st = dav_dir_stat;
        is_dir = 1;
    }
    else {
        result->f_st = dav_file_stat;
    }

    /* FIXME : Do we need to return . and .. ?? */
    /* It's hang in the empty directory in RedHat 7.1, kernel 2.4.9 */
    /* It might be Coda's bug */
    /* Anyway Fill . */
    if(!hdr->is_stat && !strcmp(uri, hdr->uri)) {
        NE_FREE(result->fname);
        result->fname = ne_strdup(".");

        /* Add to the hdr */
        if (hdr->first == NULL) {
            hdr->first = hdr->last = result;
        }
        else {
            hdr->last->next = result;
            hdr->last = result;
        }
    
        /* FIXME : Do we need to PROFIND parent resource?? */
        /* Let's make .. */
        result = ne_calloc(sizeof(*result));
        memcpy(result, hdr->last, sizeof(*result));
        result->fname = ne_strdup("..");
    }
        
    /* Fill stat data only in stat case */
    if (1 || hdr->is_stat) {
        /* Content-length */
        data = ne_propset_value(set, &stat_props[2]);
        if (data)
            result->f_st.st_size = atoi(data);
        else  /* Should we do someting in case 404? */ 
            status = ne_propset_status(set, &stat_props[2]);
    
        /* date */
        data = ne_propset_value(set, &stat_props[3]);
        if ( data )
            result->f_st.st_atime = 
              result->f_st.st_mtime = 
              result->f_st.st_ctime = ne_httpdate_parse(data);
    
        /* executable */
        data = ne_propset_value(set, &stat_props[4]);
        /*2005-05-29, werner, security fix, no execute
        if (data && !dav_noexec && !is_dir) {
            if (result->f_st.st_mode & S_IRUSR)
                result->f_st.st_mode |= S_IXUSR;
            if (result->f_st.st_mode & S_IRGRP)
                result->f_st.st_mode |= S_IXGRP;
            if (result->f_st.st_mode & S_IROTH)
                result->f_st.st_mode |= S_IXOTH;
        }*/
    
        /*result->f_st.st_blksize = DAVFS_BLKSIZE;*/
        result->f_st.st_blocks = 
                (result->f_st.st_size / result->f_st.st_blksize) + 1;
        /*result->f_st.st_size / DAVFS_BLKSIZE+ 1;*/
    }
    
    /* Add to the hdr */
    if (hdr->first == NULL) {
        hdr->first = hdr->last = result;
    }
    else {
        hdr->last->next = result;
        hdr->last = result;
    }
}


dav_prop_result * dav_opendir(const char *name)
{
    int ret;
    dav_prop_result_header phdr = { 0 };
    ne_propfind_handler *pfh;
    
    phdr.uri  = resolve_path(path, name, 1);

    DBG1("  PROPFIND 1 %s", phdr.uri);
    pfh = ne_propfind_create(session, phdr.uri, 1);
    ret = ne_propfind_named(pfh, stat_props, results, &phdr);
    
    ne_propfind_destroy(pfh);
    
    NE_FREE(phdr.uri);

    /* Data must be freed using dav_closedir */
    return phdr.first;
}



void dav_closedir(dav_prop_result *res) 
{
    dav_prop_result_destroy(res);
}

int dav_mkdir(char *name, int mode)
{
    char *uri = resolve_path(path, name, 0);
    DBG1("  MAKECOLL %s", uri);
    ne_mkcol(session, uri);

    NE_FREE(uri);
    return dav_set_errno();
}

int dav_rmdir(char *name)
{
    char *uri = resolve_path(path, name, 1);
    
    DBG1("  DELETE %s", uri);
    ne_delete(session, uri);
    NE_FREE(uri);
    
    /* Check if it is locked */
    return dav_set_errno();
}

int dav_delete(char *name)
{
    char *uri = resolve_path(path, name, 0);
    
    DBG1("  DELETE %s", uri);
    ne_delete(session, uri);
    NE_FREE(uri);
    
    /* Check if it is locked */
    return dav_set_errno();
}


/* Move */
int dav_rename(char *src, char *des, int is_dir, int try_num)
{
    char *src_uri = resolve_path(path, src, is_dir);
    char *des_uri = resolve_path(path, des, is_dir);
    
    /* Move with over write */
    DBG2("  MOVE %s %s", src_uri, des_uri);
    ne_move(session, 1, src_uri, des_uri); 
    NE_FREE(src_uri);
    NE_FREE(des_uri);

    /* Slash with file */
    if (try_num == 0 && STATUS(400)) {
        return dav_rename(src, des, 0, 1);
    }
        
    return dav_set_errno();
}

/* Get file and save it to the local file */
char *dav_getlocalcopy(const char *name) {
    int fd;
    char *uri = resolve_path(path, name, 0);
    char *fname;

    if ((fname = dav_get_tempnam("get", &fd))==NULL || fd ==-1) {
        NE_FREE(uri);
        NE_FREE(fname);
        return NULL;
    }

    DBG1("  GET %s", uri);
    if (ne_get(session, uri, fd) != NE_OK) {
        dav_set_errno();
        close(fd);
        remove(fname);
        NE_FREE(fname);
    } else {
        close(fd);
    }
        
    
    NE_FREE(uri);

    /* Return empty file name on error */
    return fname;
}

/* Lock the resource for write  and save the lock to lock_store */
int dav_lock(const char *name) {
    char *uri = resolve_path(path, name, 0);
    struct ne_lock *lock = NULL;
    
    /* Check whether already locked */
    char *backup_path = server.path;
    server.path = uri;
    lock = ne_lockstore_findbyuri(lock_store, &server);
    server.path = backup_path;
    if (lock != NULL) {
        NE_FREE(uri);
        return 0;
    }
    
    /* Let's lock the resource */
    lock = ne_lock_create();
    lock->depth = NE_DEPTH_ZERO;
    ne_fill_server_uri(session, &lock->uri);
    lock->uri.path = ne_strdup(uri);
    lock->scope = ne_lockscope_exclusive;
    lock->type =  ne_locktype_write;
    lock->owner = ne_concat("<href>", "davfs2", "</href>", NULL);
    
    DBG1("  LOCK %s", uri);
    NE_FREE(uri);

    if (ne_lock(session, lock) != NE_OK) {
        /* otherwise, throw it away */
        ne_lock_destroy(lock);
        return dav_set_errno();
    }
    
    /* success: remember the lock. */
    ne_lockstore_add(lock_store, lock);
    return NE_OK;
}


int dav_unlock(const char *name) {
    int ret = NE_OK;
    char *uri = resolve_path(path, name, 0);
    struct ne_lock *lock;
    
    /* Lst's get the lock */
    char *backup_path = server.path;
    server.path = uri; /* Change path */
    lock = ne_lockstore_findbyuri(lock_store, &server);
    server.path = backup_path;
    NE_FREE(uri);

    if (!lock) {
        DBG0("  Should be locked, before open\n");
        return 0;
    }
    
    DBG1("  LOCK %s", lock->uri.path);
    if (ne_unlock(session, lock) != NE_OK) {
        ret = -1;
    }
    
    ne_lockstore_remove(lock_store, lock);
    ne_lock_destroy(lock);
    return ret;
}


int dav_put(const char *name, const char *fname) {
    int fd;
    int ret = NE_OK;
    char *uri = resolve_path(path, name, 0);

    fd = open(fname, O_RDONLY);
    if( fd < 0 ) {
        NE_FREE(uri);
        return EIO;
    }

    DBG1("  PUT %s", uri);
    if( ne_put(session, uri, fd) != NE_OK) { 
        dav_set_errno();
        ret = dav_get_errno();
    }
    close(fd);

    NE_FREE(uri);
    return ret;
}


/* Get localfile and save it to the server 
 * It should be locked
 */
int dav_put_unlock(const char *name, const char *fname) {
    int fd;
    int ret = NE_OK;
    char *uri = resolve_path(path, name, 0);
    struct ne_lock *lock;
    
    fd = open(fname, O_RDONLY);
    
    fd = open(fname, O_RDONLY);
    if( fd < 0 ) {
        NE_FREE(uri);
        return -1;
    }

    DBG1("  PUT %s", uri);
    if( ne_put(session, uri, fd)!=NE_OK) { 
        dav_set_errno();
        ret = -1;
    }
    close(fd);
    
    /* Lst's get the lock */
    char *backup_path = server.path;
    server.path = uri; /* Change path */
    lock = ne_lockstore_findbyuri(lock_store, &server);
    server.path = backup_path;
    NE_FREE(uri);

    if (!lock) {
        DBG0("  Should be locked, before open\n");
        return -1;
    }
    
    DBG1("  UNLOCK %s", lock->uri.path);
    if (ne_unlock(session, lock) != NE_OK) {
        dav_set_errno();
        ret = -1;
    }
    
    ne_lockstore_remove(lock_store, lock);
    ne_lock_destroy(lock);
    
    return ret;
}


/* let's get stat from cache*/
int dav_stat_cache(const char *name, struct stat *st, 
           dav_prop_result *result, 
           const char *base_dir) 
{
    dav_prop_result *one_res;
    char *parent;
    int len;
    
    if (base_dir == NULL) 
    return -1;
    
    /* Get parent */
    parent = ne_path_parent(name);
    
    /* Let's add '/' for parent */
    if (parent == NULL) {
        len = 1;
        parent = ne_strdup("/");
    } else {
        len = strlen(parent);
        dav_no_trail(parent);
    }
   
    DBG2("\n  CACHE DIR: %s<->%s\n", parent, 
         (base_dir?base_dir:"(null)"));
           
    /* Diffrent parent. Let's return */
    if (!base_dir || strcmp(parent, base_dir)) {
        NE_FREE(parent);
        return -1;
    }

    
    NE_FREE(parent);
    
    /* Let's walk and find the file is in the cache */
    for (one_res = result; one_res; one_res= one_res->next) {
        DBG2("\n  CACHE FNAME: %s<->%s\n", 
             one_res->fname, name+len);
        /* Is the same file name */
        if (!strcmp(one_res->fname, name+len)) {
            DBG0("  I got it from cache \n");
            *st = one_res->f_st;
            return NE_OK;
        }
    }
    
    return -1;
}

/* let's get stat */
int dav_stat(const char *name, struct stat *st, 
         int is_dir, int is_second)
{
    int ret = -1;
    dav_prop_result_header phdr = { 0 };
    ne_propfind_handler *pfh;
    int status;

    /* Get the new path */
    phdr.uri = resolve_path(path, name, is_dir);
    
    /* It is stat */
    phdr.is_stat = 1;

    DBG1("  PROPFIND 0 %s", phdr.uri);
    pfh = ne_propfind_create(session, phdr.uri, 0);
    ret = ne_propfind_named(pfh, stat_props, results, &phdr);

    if (phdr.first) {
        ret = 0;
        /* Copy stat data */
        *st = phdr.first->f_st;
    }

    /* Let's save the status code */
    status = ne_get_status(ne_propfind_get_request(pfh))->code;

    NE_FREE(phdr.first);
    NE_FREE(phdr.uri);
    ne_propfind_destroy(pfh);

    if (mount_anyway > 0) {
        --mount_anyway;
        if (ret != 0)
            *st = dav_dir_stat;
        return 0;
    }

    switch(status) {
    case 405:
        fprintf(stderr, "WebDAV Enabled??\n");
        return dav_set_errno();
    case 401:
        fprintf(stderr, "Needs password\n");
        return dav_set_errno();
    }
    
    
    /*## FIXME: Get the right status
     * We expecting 207. Otherwise try with(without) slash
     */
    /* No slash for dir */
    if (is_second == 0 && is_dir==0 && status!=207) {
        return dav_stat(name, st, 1, 1);
    }
    
    /* Slash for file */
    if (is_second == 0 && is_dir==1 && status!=207) {
        return dav_stat(name, st, 0, 1);
    }

    /* Set errno and return value */
    return dav_set_errno();

}

/* FIXME: How can we change mode? */
int dav_chmod(const char *name, unsigned short mode) {
    return 0;
}

/* start emacs stuff */
/* 
 * local variables: 
 * eval: (load-file "../tools/davfs-emacs.el") 
 * end: 
 */ 


Generated by  Doxygen 1.6.0   Back to index