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

mount_davfs.c

/* 
** Mount the davfs file system.
 */

/*
 *  This file is part of davfs2.
 *
 *  davfs2 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.
 *
 *  davfs2 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 davfs2; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */


#include "config.h"

#include <argz.h>
#include <ctype.h>
#include <errno.h>
#include <error.h>
#include <fstab.h>
#include <getopt.h>
#include <grp.h>
#include <limits.h>
#include <mntent.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <termios.h>
#include <unistd.h>

#include <sys/mount.h>
#include <sys/stat.h>

#include <ne_string.h>
#include <ne_uri.h>

#include "dav_debug.h"
#include "defaults.h"
#include "kernel_interface.h"
#include "cache.h"
#include "webdav.h"
#include "mount_davfs.h"


/* Private global variables */
/*==========================*/

/* The URL of the WebDAV server as taken from commandline.  */
static char *url;

/* The mointpoint as taken from the command line. */
static char *mpoint;

/* The type of the kernel file system used. */
static char *kernel_fs;

/* The file that holds information about mounted filesystems
   (/proc/mounts or /etc/mtab) */
static char *mounts;

/* The PID file */
static char *pidfile;

/* This flags signals the message loop of the kernel interface whether it
   shall do what it says or just stop. Will be reset by termination_handler. */
static volatile int keep_on_running = 1;


/* Private function prototypes */
/*=============================*/

/* Parsing, checking and mounting. */

static void check_dirs(dav_args *args);

static char *check_double_mounts(void);

static void check_fstab(const dav_args *args);

static void check_mountpoint(dav_args *args);

static void check_permissions(dav_args *args);

static int do_mount(unsigned long int mopts, void *mdata);

static int is_mounted(void);

static dav_args *parse_commandline(int argc, char *argv[]);

static void parse_config(dav_args *args);

static void parse_secrets(dav_args *args);

static int save_pid(void);

static void termination_handler(int signo);

static void write_mtab_entry(const dav_args *args);

/* Helper functions. */

static int arg_to_int(const char *arg, int base, const char *opt);

static void ask_auth(char **user, char **password, const char *kind,
                     const char *host);

static char *decode_octal(const char *s);

static void delete_args(dav_args *args);

static void eval_modes(dav_args *args);

static void get_options(dav_args *args, char *option);

static dav_args *new_args(void);

static int parse_line(char *line, int parmc, char *parmv[]);

static void read_config(dav_args *args, const char * filename, int system);

static void read_secrets(dav_args *args, const char *filename);

static void split_proxy(char **host, int *port, const char *arg);

static void usage(void);


/* Public functions */
/*==================*/

int main(int argc, char *argv[]) {

    DBG0("Parsing command line.");
    dav_args *args = parse_commandline(argc, argv);

    DBG0("Checking for setuid.");
    if (geteuid() != 0)
        error(EXIT_FAILURE, 0, "Program is not setuid root.");
    if (seteuid(getuid()) != 0)
        error(EXIT_FAILURE, 0, "Can't change effective user id.");

    if (getuid() != 0) {
        DBG0("Checking fstab.");
        check_fstab(args);
    }

    DBG0("Checking mount point.");
    check_mountpoint(args);

    DBG0("Parsing configuration files.");
    parse_config(args);

    DBG0("Parsing secrets files.");
    parse_secrets(args);

    DBG0("Checking permissions.");
    check_permissions(args);

    DBG0("Checking directories.");
    check_dirs(args);

    DBG0("Checking for double mounts.");
    pidfile = check_double_mounts();

    DBG2("Changing persona to uid=%i, gid=%i", args->uid, args->dav_group);
    seteuid(0);
    if (setgid(args->dav_group) != 0)
        error(EXIT_FAILURE, 0, "Can't change group id.");
    if (seteuid(args->uid) != 0)
        error(EXIT_FAILURE, 0, "Can't change effective user id.");

    DBG0("Initialize WebDAV-session.");    
    dav_init_webdav(args->scheme, args->host, args->port, args->read_timeout,
                    args->locks, args->lock_timeout, args->username,
                    args->password, args->useproxy, args->p_host, args->p_port,
                    args->p_user, args->p_passwd, args->lock_owner,
                    args->displayname);

    DBG0("Initialize Cache.");
    dav_init_cache(args->uid, args->gid, args->file_mode, args->dir_mode,
                   args->file_umask, args->dir_umask, args->table_size,
                   args->expire, args->get_wait, args->retry, args->max_retry,
                   args->lock_refresh, args->cache_size, args->cache_dir,
                   args->backup_dir, args->host, args->path, mpoint);

    DBG0("Initialize kernel interface.");
    int dev = 0;
    dav_run_msgloop_fn run_msgloop = NULL;
    void *mdata = NULL;
    if (args->kernel_fs != NULL)
        kernel_fs = ne_strdup(args->kernel_fs);
    size_t buf_size = args->buf_size * 1024;
    int mounted = dav_init_kernel_interface(&dev, &run_msgloop, &mdata,
                                            &kernel_fs, &buf_size, url, mpoint,
                                            args->mopts, args->uid, args->gid,
                                            args->dir_mode);
    time_t idle_time = args->idle_time;

    DBG0("Fork into daemon mode; parent will mount.");
    pid_t childpid = fork();
    if (childpid > 0) {

        DBG2("Parent: Parent pid: %i, child pid: %i", getpid(), childpid);
        if (!mounted) {
            DBG0("Parent: Mounting filesystem.");
            if (do_mount(args->mopts, mdata) != 0) {
                kill(childpid, SIGTERM);
                exit(EXIT_FAILURE);
            }
        }

        DBG0("Parent: Writing mtab entry.");
        write_mtab_entry(args);

        DBG0("Parent: leaving now.");
        exit(EXIT_SUCCESS);

    } else if (childpid < 0) {
        error(EXIT_FAILURE, 0, "Could not start child process for daemon.");
    }

    DBG0("Set signal handler.");
    struct sigaction action;
    action.sa_handler = termination_handler;
    sigemptyset(&action.sa_mask);
    sigaddset(&action.sa_mask, SIGTERM);
    sigaddset(&action.sa_mask, SIGHUP);
    action.sa_flags = 0;
    sigaction(SIGTERM, &action, NULL);
    sigaction(SIGHUP, &action, NULL);

    DBG0("Releasing root privileges.");
    int ret = 0;
    seteuid(0);
    ret = setuid(args->uid);
    if (ret != 0) {
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR),
               "Could not release root priveleges.");
        kill(getppid(), SIGHUP);
    }
    delete_args(args);
    setsid();

    if (ret == 0) {
        DBG0("Writing pid file.");
        ret = save_pid();
    }
    if (ret != 0) {
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR),
               "Could not write pid file\n  %s.", pidfile);
        kill(getppid(), SIGHUP);
    }

    if (ret == 0) {
        DBG0("Starting message loop.");
        run_msgloop(dev, buf_size, idle_time, is_mounted, &keep_on_running);
    }

    DBG0("Closing.");
    dav_close_cache();
    dav_close_webdav();
    if (is_mounted()) {
        char *prog = ne_concat("/bin/umount ", mpoint, NULL);
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "  Unmounting %s.", mpoint);
        if (system(prog) != 0)
            syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "  Unmounting failed.");
    }
    remove(pidfile);
    return 0;
}


/* Private functions */
/*===================*/

/* Checks for the existence of necessary and usefull directories and files.
   If they don't exist it tries to create them. It also tries to set ownership
   and permissions appropriately.
   - The file DAV_MOUNTS which holds informations about mounted file systems.
     If it does not exist, _PATH_MOUNTED is used. The private global variable
     mounts is set to the name of the file to use.
   The next directories must exist (or must be created successfully) and have
   proper permissions. If not an error message is printed and the program is
   terminated:
   - The directory DAV_SYS_RUN to store the pid file.
   - The top level directory for the cache, as given in args->cache_dir.
   For an ordinary user the following directories and files should exist, but
   it is no error, if they don't and can't be created:
   - directory DAV_USER_DIR for per user configaration and cache files.
   - files DAV_USER_CONF and DAV_USER_SECRETS */
static void check_dirs(dav_args *args) {

    struct stat st;

    if (access(DAV_MOUNTS, R_OK) == 0) {
        mounts = DAV_MOUNTS;
    } else {
        mounts = _PATH_MOUNTED;
    }
    DBG1("Mounts in %s", mounts);

    seteuid(0);
    if (access(DAV_SYS_RUN, F_OK) != 0) {
        if (mkdir(DAV_SYS_RUN, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH | S_ISVTX)
                != 0)
            error(EXIT_FAILURE, 0, "Can't create %s.", DAV_SYS_RUN);
    }
    if (stat(DAV_SYS_RUN, &st) != 0)
        error(EXIT_FAILURE, 0, "Can't access %s.", DAV_SYS_RUN);
    if ((st.st_mode & (S_IRWXG | S_ISVTX)) != (S_IRWXG | S_ISVTX)) {
        if (chmod(DAV_SYS_RUN, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH | S_ISVTX)
                != 0)
            error(EXIT_FAILURE, 0, "Can't change mode of %s.", DAV_SYS_RUN);
    }
    if (st.st_gid != args->dav_group) {
        if (chown(DAV_SYS_RUN, 0, args->dav_group) != 0)
            error(EXIT_FAILURE, 0, "Can't change group of %s.", DAV_SYS_RUN);
    }
    seteuid(getuid());

    if (getuid() != 0) {

        char *path = ne_strdup(DAV_USER_DIR);
        struct passwd *pw = getpwuid(getuid());
        if (*path == '~' && pw != NULL && pw->pw_dir != NULL) {
            char *tmp_path = ne_concat(pw->pw_dir, path + 1, NULL);
            free(path);
            path = tmp_path;
        }
        if (access(path, F_OK) != 0)
            mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);

        if (access(path, F_OK) == 0) {
            char *cache = ne_concat(path, "/", DAV_USER_CACHE, NULL);
            if (access(cache, F_OK) != 0)
                mkdir(cache, S_IRWXU);
            free(cache);

            char *file_name = ne_concat(path, "/", DAV_USER_CONF, NULL);
            if (access(file_name, F_OK) != 0) {
                char *template = ne_concat(DOC_DIR, "/", DAV_USER_CONF,
                                           ".template", NULL);
                char *command = ne_concat("cp ", template, " ", file_name,
                                          NULL);
                system(command);
                free(command);
                free(template);
            }
            free(file_name);

            file_name = ne_concat(path, "/", DAV_USER_SECRETS, NULL);
            if (access(file_name, F_OK) != 0) {
                char *template = ne_concat(DOC_DIR, "/", DAV_USER_SECRETS,
                                           ".template", NULL);
                char *command = ne_concat("cp ", template, " ", file_name,
                                          NULL);
                if (system(command) == 0)
                    chmod(file_name, S_IRUSR | S_IWUSR);
                free(command);
                free(template);
            }
            free(file_name);

        }
        free(path);
    }

    if (strcmp(args->cache_dir, args->sys_cache) == 0) {

        seteuid(0);
        if (access(args->sys_cache, F_OK) != 0) {
            if (mkdir(args->sys_cache, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)
                    != 0)
                error(EXIT_FAILURE, 0, "Can't create %s.", args->sys_cache);
        }
        if (stat(args->sys_cache, &st) != 0)
            error(EXIT_FAILURE, 0, "Can't access %s.", args->sys_cache);
        if ((st.st_mode & S_IRWXG) != S_IRWXG) {
            if (chmod(args->sys_cache, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)
                    != 0)
                error(EXIT_FAILURE, 0, "Can't change mode of %s.",
                      args->sys_cache);
        }
        if (st.st_gid != args->dav_group) {
            if (chown(args->sys_cache, 0, args->dav_group) != 0)
                error(EXIT_FAILURE, 0, "Can't change group of %s.",
                      args->sys_cache);
        }
        seteuid(getuid());

    } else {

        struct passwd *pw = getpwuid(getuid());
        if (pw == NULL || pw->pw_name == NULL)
            error(EXIT_FAILURE, 0, "Could not read user data base.");
        if (access(args->cache_dir, F_OK) != 0) {
            if (mkdir(args->cache_dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)
                    != 0)
                error(EXIT_FAILURE, 0, "Can't create %s.", args->cache_dir);
        }
        if (stat(args->cache_dir, &st) != 0)
            error(EXIT_FAILURE, 0, "Can't access %s.", args->cache_dir);
        if ((st.st_uid != args->uid || (st.st_mode & S_IRWXU) != S_IRWXU)
                &&  (st.st_mode & S_IRWXO) != S_IRWXO) {
            if ((st.st_mode & S_IRWXG) != S_IRWXG)
                error(EXIT_FAILURE, 0, "Can't access %s.",
                      args->cache_dir);
            struct group *grp = getgrgid(st.st_gid);
            if (grp == NULL)
                error(EXIT_FAILURE, 0, "Could not read group data base.");
            char **members = grp->gr_mem;
            while (*members != NULL && strcmp(*members, pw->pw_name) != 0)
                members++;
            if (*members == NULL)
                error(EXIT_FAILURE, 0, "Can't access %s.", args->cache_dir);
        }

    }
}


/* Checks whether url is already mounted on mpoint, creates the name of the
   pid file and checks whether it already exists.
   If one of these tests is positive, it prints an error message and
   terminates the program. Otherwise it returns the name of the pid file.
   return value : the name of the pid file. */
static char *check_double_mounts(void) {

    FILE *mtab = setmntent(mounts, "r");
    if (mtab == NULL)
        error(EXIT_FAILURE, 0, "Could not open %s,\n"
              "  but mount information from this file is needed.", mounts);
    struct mntent *mt = getmntent(mtab);
    while (mt != NULL) {
        if (strcmp(mpoint, mt->mnt_dir) == 0
                    && strcmp(url, mt->mnt_fsname) == 0)
        error(EXIT_FAILURE, 0, "%s is already\n"
              "  mounted on %s.", url, mpoint);
        mt = getmntent(mtab);
    }
    endmntent(mtab);
    char *m = mpoint;
    while (*m == '/')
        m++;
    char *mp = ne_strdup(m);
    m = strchr(mp, '/');
    while (m != NULL) {
        *m = '-';
        m = strchr(mp, '/');
    }
    char *pidf = NULL;
    if (asprintf(&pidf, "%s/%s.pid", DAV_SYS_RUN, mp) < 0)
        abort();
    DBG1("PID file: %s", pidf);

    FILE *file = fopen(pidf, "r");
    if (file != NULL)
        error(EXIT_FAILURE, 0, "Found PID file %s.\n"
              "  Either %s is used\n  by another process, or\n"
              "  another mount process ended irregular.", pidf, mpoint);

    return pidf;
}


/* Checks fstab whether there is an entry for the mountpoint specified in args
   and compares the values in args with the values in fstab.
   If there is no such entry, or this entry does not allow user-mount, or the
   values differ, an error message is printed and the program terminates.
   Option useproxy, askauth and locks are not checked. The mounting user is
   free to choose them. They should be configured in davfs2.conf anyway. */
static void check_fstab(const dav_args *args) {

    dav_args *n_args = new_args();
    n_args->mopts = DAV_USER_MOPTS;

    setfsent();
    struct fstab *ft = getfsfile(mpoint);
    if (ft == NULL)
        error(EXIT_FAILURE, 0, "Could not read fstab.");

    if (strcmp(url, ft->fs_spec) != 0) {
        char *fstab_url = decode_octal(ft->fs_spec);
        if (strcmp(url, fstab_url) != 0)
            error(EXIT_FAILURE, 0, "URL different in %s", _PATH_FSTAB);
        free(fstab_url);
    }

    if (ft->fs_mntops != NULL)
        get_options(n_args, ft->fs_mntops);

    if (strcmp(DAV_FS_TYPE, ft->fs_vfstype) != 0)
        error(EXIT_FAILURE, 0, "File system type different in %s", _PATH_FSTAB);
    if (strcmp(args->conf, n_args->conf) != 0)
        error(EXIT_FAILURE, 0, "Config file different in %s", _PATH_FSTAB);
    if (n_args->user != 1)
        error(EXIT_FAILURE, 0, "No user option in %s", _PATH_FSTAB);
    if (args->mopts != n_args->mopts)
        error(EXIT_FAILURE, 0, "mount options different in %s", _PATH_FSTAB);
    if (args->uid != n_args->uid)
        error(EXIT_FAILURE, 0, "UID different in %s", _PATH_FSTAB);
    if (args->gid != n_args->gid)
        error(EXIT_FAILURE, 0, "GID different in %s", _PATH_FSTAB);
    if (args->dir_mode != n_args->dir_mode)
        error(EXIT_FAILURE, 0, "Dir mode different in %s", _PATH_FSTAB);
    if (args->file_mode != n_args->file_mode)
        error(EXIT_FAILURE, 0, "File mode different in %s", _PATH_FSTAB);

    endfsent();
    delete_args(n_args);
}


/* Checks if a valid mountpoint is specified.
   For non root users this must meet the additional conditions:
   - if the mount point is given as relative path, it must lie within
     the mounting users home directory (so a relative path in fstab
     - which might be useful in some cases - will not allow to to
     gain access to directories not intended).
   - if given as an absulute path it must not lie within another users
     home directory (even the administrator can't give the right to mount
     into another users home directory).
   If this conditions are not met or an error occurs, an error message is
   printed and exit(EXIT_FAILURE) is called. */
static void check_mountpoint(dav_args *args) {

    struct passwd *pw;

    if (*mpoint != '/') {
        char *mp = canonicalize_file_name(mpoint);
        if (mp == NULL)
            error(EXIT_FAILURE, 0, "Can't evaluate path of mount point %s.",
                  mpoint);
        if (getuid() != 0) {
            pw = getpwuid(getuid());
            if (pw == NULL || pw->pw_dir == NULL)
                error(EXIT_FAILURE, 0,
                      "Can't get home directory for uid %i.", getuid());
            if (strstr(mp, pw->pw_dir) != mp)
                error(EXIT_FAILURE, 0, "A relative mount point must lie within"
                                       " your home directory.");
        }
        free(mpoint);
        mpoint = mp;
    }

    if (strcmp(mpoint, "/") == 0 || strlen(mpoint) < 2)
        error(EXIT_FAILURE, 0, "Invalid mount point \"%s\".", mpoint);

    if (access(mpoint, F_OK) != 0)
        error(EXIT_FAILURE, 0, "Mount point %s does not exist.", mpoint);

    if (getuid() != 0) {
        pw = getpwuid(getuid());
        if (pw == NULL || pw->pw_dir == NULL)
            error(EXIT_FAILURE, 0, "Could not read user data base.");
        if(strstr(mpoint, pw->pw_dir) != mpoint) {
            setpwent();
            pw = getpwent();
            while (pw != NULL) {
                if (pw->pw_dir != NULL && pw->pw_uid != args->uid
                        && strlen(pw->pw_dir) > 0
                        && strstr(mpoint, pw->pw_dir) == mpoint)
                    error(EXIT_FAILURE, 0,
                          "You can't mount into another users home directory.\n"
                          "  \"%s\" is the home directory of user %s.",
                          pw->pw_dir, pw->pw_name);
                pw = getpwent();
            }
            endpwent();
        }
    }

    DBG1("  Mountpoint: %s", mpoint);
}


/* The mounting user must be either root or meet the following conditions:
   - The  uid must not differ from the option uid, if this option is used.
   - The user must belong to the group specified in option gid (if used).
   - The user must be member of group args->dav_group.
   If this conditions are not met or an error occurs, an error message is
   printed and exit(EXIT_FAILURE) is called.
   If the mounting user is root and option uid is given, but not option
   gid, then the gid is set to the default group of user uid. */
static void check_permissions(dav_args *args) {

    if (getuid() == 0)
        return;

    DBG0("  Checking uid.");
    if (args->uid != getuid())
        error(EXIT_FAILURE, 0,
              "You can't set file owner different from your uid.");

    DBG0("  Checking gid.");
    if (getgid() != args->gid) {
        struct passwd *pw = getpwuid(getuid());
        if (pw == NULL || pw->pw_name == NULL)
            error(EXIT_FAILURE, 0, "Could not read user data base.");
        struct group *grp = getgrgid(args->gid);
        if (grp == NULL)
            error(EXIT_FAILURE, 0, "Could not read group data base.");
        char **members = grp->gr_mem;
        while (*members != NULL && strcmp(*members, pw->pw_name) != 0)
            members++;
        if (*members == NULL)
            error(EXIT_FAILURE, 0,
                  "You must be memeber of the group of the file system.");
    }

    DBG0("  Checking membership in dav_group.");
    struct passwd *pw;
    pw = getpwuid(getuid());
    if (pw == NULL || pw->pw_name == NULL)
        error(EXIT_FAILURE, 0, "Could not read user data base.");
    if (pw->pw_gid != args->dav_group) {
        struct group *grp = getgrgid(args->dav_group);
        if (grp == NULL)
            error(EXIT_FAILURE, 0, "Could not read group data base.");
        char **members = grp->gr_mem;
        while (*members != NULL && strcmp(*members, pw->pw_name) != 0)
            members++;
        if (*members == NULL)
            error(EXIT_FAILURE, 0, "User %s must be member of group %s.",
                  pw->pw_name, grp->gr_name);
    }
}


/* Calls the mount()-function to mount the file system.
   Uses private global variables url and mpoint as device and mount point,
   kernel_fs as file system type, mopts as mount options and mdata
   as mount data.
   return value : 0 on success, -1 if mount() fails. */
static int do_mount(unsigned long int mopts, void *mdata) {

    uid_t orig = geteuid();
    seteuid(0);
    int ret = mount(url, mpoint, kernel_fs,  mopts, mdata);
    seteuid(orig);

    if (ret != 0) {
        error(0, errno, "Could not mount %s on %s.", url, mpoint);
        if (errno == ENODEV)
            error(0, 0, "%s unknown to kernel.", kernel_fs);
        if (errno == EBUSY)
            error(0, 0, "Mount point is busy.");
        return -1;
    }
    return 0;
}


/* Checks wether the file system is mounted.
   It uses information from the private global variables mounts (mtab-file),
   url (must be device in the mtab entry) and mpoint (mount point).
   return value : 0 - no matching entry in the mtab-file (not mounted)
                  1 - matching entry in the mtab-file (mounted) */
static int is_mounted(void) {

    int found = 0;
    FILE *mtab = setmntent(mounts, "r");
    if (mtab != NULL) {
        struct mntent *mt = getmntent(mtab);
        while ((mt != NULL) && (found == 0)) {
            if ((strcmp(kernel_fs, mt->mnt_type) == 0)
                        && (strcmp(mpoint, mt->mnt_dir) == 0)
                        && (strcmp(url, mt->mnt_fsname) == 0))
                found = 1;
            mt = getmntent(mtab);
        }
    }
    endmntent(mtab);
    return found;
}

/* Parses commandline arguments and options and stores them in args and the
   private global variables url and mpoint.
   For arguments and options please see the usage()-funktion.
   As soon as 'version' or 'help' is found, an appropriate message is printed
   and exit(EXIT_SUCCESS) is called.
   If it does not find exactly two non-option-arguments (url and mointpoint)
   it prints an error message and calls exit(EXIT_FAILURE).
   argc    : the number of arguments.
   argv[]  : array of argument strings.
   return value : args, containig the parsed options and arguments. The args
                  structure and all strings are newly allocated. The calling
                  function is responsible to free them. */
static dav_args *parse_commandline(int argc, char *argv[]) {

    DBG_CMDLINE(argv);

    dav_args *args = new_args();

    char *short_options = "Vho:";
    static const struct option options[] = {
        {"version", no_argument, NULL, 'V'},
        {"help", no_argument, NULL, 'h'},
        {"option", required_argument, NULL, 'o'},
        {0, 0, 0, 0}
    };

    int o;
    o = getopt_long(argc, argv, short_options, options, NULL);
    while (o != -1) {
        switch (o) {
        case 'V':
            printf("%s: %s  <%s>\n\n", PROGRAM_NAME, PACKAGE_STRING, DAV_HOME); 
            exit(EXIT_SUCCESS);
        case 'h':
            usage();
            exit(EXIT_SUCCESS);
        case 'o':
            get_options(args, optarg);
            break;
        case '?':
            break;
        default:
            error(EXIT_FAILURE, 0, "Unknown error parsing arguments.");
        }
        o = getopt_long(argc, argv, short_options, options, NULL);
    }

    int i = optind;
    switch (argc - i) {
    case 0:
    case 1:
        printf("Missing argument.\n");
        usage();
        exit(EXIT_FAILURE);
    case 2:
        if (*argv[i] == '\"' || *argv[i] == '\'') {
            url = ne_strndup(argv[i] + 1, strlen(argv[i]) -2);
        } else {
            url = ne_strdup(argv[i]);
        }
        i++;
        mpoint = ne_strdup(argv[i]);
        break;
    default:
        printf("Too many arguments.\n");
        usage();
        exit(EXIT_FAILURE);
    }

    if (mpoint == NULL)
        error(EXIT_FAILURE, 0, "No mountpoint specified.");
    while (strlen(mpoint) > 1 && *(mpoint + strlen(mpoint) -1) == '/')
        *(mpoint + strlen(mpoint) -1) = '\0';

    if (url == NULL)
        error(EXIT_FAILURE, 0, "No WebDAV-server specified.");
    ne_uri *uri = (ne_uri *) ne_malloc(sizeof(ne_uri));
    if (ne_uri_parse(url, uri) != 0 || uri->host == NULL
            || uri->path == NULL)
        error(EXIT_FAILURE, 0, "Invalid URL.");

    if (uri->scheme != NULL) {
        args->scheme = uri->scheme;
        uri->scheme = NULL;
    } else {
        args->scheme = ne_strdup("http");
    }

    args->host = uri->host;
    uri->host = NULL;
    if (uri->port != 0) {
        args->port = uri->port;
    } else {
        args->port = ne_uri_defaultport(args->scheme);
    }

    if (strlen(uri->path) < 1
            || *(uri->path + strlen(uri->path) -1) != '/') {
        args->path = ne_concat(uri->path, "/", NULL);
    } else {
        args->path = uri->path;
        uri->path = NULL;
    }

    ne_uri_free(uri);
    free(uri);
    DBG1("  URL: %s", url);
    DBG1("  Mount point: %s", mpoint);

    return args;       
}


/* Reads and parses the configuration files and stores the values in args.
   The system wide configuration file is parsed first. If args->conf is
   given it will be parsed too and overwrites the values from the system
   wide configuration file. */
static void parse_config(dav_args *args) {

    read_config(args, SYS_CONF, 1);

    struct passwd *pw = getpwuid(getuid());
    if (pw == NULL || pw->pw_dir == NULL)
        error(EXIT_FAILURE, 0, "Could not find user home directory.");

    if (args->conf != NULL) {
        if (*args->conf == '~') {
            int p = 1;
            if (*(args->conf + p) == '/')
                p++;
            char *f = ne_concat(pw->pw_dir, "/", args->conf + p, NULL);
            free(args->conf);
            args->conf = f;
        }
        read_config(args, args->conf, 0);
    }

    args->mopts |= DAV_MOPTS;

    if (getuid() == 0 && args->uid != 0 && args->gid == 0) {
        struct passwd *pw = getpwuid(args->uid);
        if (pw == NULL)
            error(EXIT_FAILURE, 0, "Could not read user data base.");
        args->gid = pw->pw_gid;
    }

    eval_modes(args);

    if (args->secrets != NULL && *args->secrets == '~') {
        int p = 1;
        if (*(args->secrets + p) == '/')
            p++;
        char *f = ne_concat(pw->pw_dir, "/", args->secrets + p, NULL);
        free(args->secrets);
        args->secrets = f;
    }

    if (args->p_host == NULL)
        args->useproxy = 0;

    if (args->cache_dir == NULL) {
        args->cache_dir = ne_strdup(args->sys_cache);
    } else if (*args->cache_dir == '~') {
        int p = 1;
        if (*(args->cache_dir + p) == '/')
            p++;
        char *f = ne_concat(pw->pw_dir, "/", args->cache_dir + p, NULL);
        free(args->cache_dir);
        args->cache_dir = f;
    }

    DBG_ARGS;
}


/* Reads the secrets file and asks the user interactivly for credentials if
   necessary. The user secrets file is parsed after the system wide secrets
   file, so it will have precedence. */
static void parse_secrets(dav_args *args) {

    seteuid(0);
    read_secrets(args, SYS_SECRETS);
    seteuid(getuid());

    if (args->secrets != NULL) {
        read_secrets(args, args->secrets);
    }

    if (args->askauth && args->useproxy
                      && (args->p_user == NULL || args->p_passwd == NULL))
        ask_auth(&args->p_user, &args->p_passwd, "proxy", args->p_host);
    if (args->askauth && (args->username == NULL || args->password == NULL))
        ask_auth(&args->username, &args->password, "server", url);

    DBG_SECRETS;
}


/* Saves the pid of the mount.davfs daemon in the pid-file. The name of the
   pid-file is taken from the private global variable pidfile. If an error
   occurs during opening of the pid-file, the function returns with -1. */
static int save_pid(void) {

    FILE *file = fopen(pidfile, "w");
    if (file == NULL)
        return -1;
    if (fprintf(file, "%i\n", getpid()) <= 0)
        return -1;
    fclose(file);
    chmod(pidfile, S_IRWXU | S_IRGRP | S_IROTH);
    return 0;
}


/* Signal handler for the daemon process.
   Stops the daemon process by calling the umount program. Additionally it
   sets global variable *keep_on_running to 0, so the daemon will stop even
   if umount fails. */
static void termination_handler(int signo) {

    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "PID: %i, got singal %i:",
                       getpid(), signo);
    if (is_mounted()) {
        char *prog = ne_concat("/bin/umount ", mpoint, NULL);
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "  Unmounting %s.", mpoint);
        if (system(prog) != 0)
            syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "  Unmounting failed.");
        free(prog);
    }

    keep_on_running = 0;
}


/* Adds an entry to _PATH_MOUNTED for the mounted file system.
   If this fails a warning will be printed, but this will not stop mounting. */
static void write_mtab_entry(const dav_args *args) {

    struct mntent mntent;
    mntent.mnt_fsname = url;
    mntent.mnt_dir = mpoint;
    mntent.mnt_type = kernel_fs;
    struct passwd *pw = getpwuid(getuid());
    if (pw == NULL) {
        error(0, 0, "Warning: Could not write entry in mtab.\n"
                    "         Could not read user data base.\n"
                    "         Mounting anyway.");
        return;
    }
    mntent.mnt_opts = ne_concat("user=", pw->pw_name, NULL);
    mntent. mnt_freq = 0;
    mntent. mnt_passno = 0;

    uid_t orig = geteuid();
    seteuid(0);
    int ret;
    FILE *mtab = setmntent(_PATH_MOUNTED, "a");
    if (mtab != NULL) {
        ret = addmntent(mtab, &mntent);
        endmntent(mtab);
    } else {
        error(0, 0, "Warning: Could not write entry in mtab."
                    "         But file system is mounted anyway.");
    }
    seteuid(orig);

    free(mntent.mnt_opts);
}


/* Helper functions. */

/* Searches arg from the beginning for digits, valid in base, and converts
   them to an integer. If arg does not start with valid digits an error
   message is printed and exit(EXIT_FAILURE) is called.
   Otherwise the integer is returned.
   arg    : string to be converted
   base   : radix of the number; value between 2 and 36
   opt    : name of the option, arg belongs to. Used in the error message.
   return value: the value of the integer number in arg */
static int arg_to_int(const char *arg, int base, const char *opt) {

    char *tail = NULL;
    int n = strtol(arg, &tail, base);
    if (n < 0 || tail == NULL) {
        char *b = NULL;
        if (base == 10) {
            b = " decimal";
        } else if (base == 8) {
            b = "n octal";
        } else {
            b = "";
        }
        error(EXIT_FAILURE, 0, "Argument must be a %s number -%s.", b, opt);
    }

    return n;
}


/* Get credentials for proxy or WebDAV-server from user interactivly.
   If the username is given in user, only the password will be asked for.
   user     : pointer to a string to return the username.
   password : pointer to a string to return the password.
   kind     : should be "server" or "proxy" to inform the user.
   host     : the url of the server or the FQDN of the proxy the
              credentials are needed for (to inform the user). */
static void ask_auth(char **user, char **password, const char *kind,
                     const char *host) {

    char *s = NULL;
    size_t n = 0;
    ssize_t len = 0;

    if (*user == NULL) {
        printf("\nPlease enter the username to authenticate with %s\n"
               "  %s\nor hit enter for none.\n", kind, host);
        printf("Username: ");
        len = getline(&s, &n, stdin);
        if (len < 1)
            abort();
        *user = ne_strndup(s, len - 1);
    }

    printf("\nPlease enter the password to authenticate %s with %s\n"
          "  %s\nor hit enter for none.\n", *user, kind, host);
    printf("Password: ");
    struct termios termios;
    tcgetattr(STDOUT_FILENO, &termios);
    termios.c_lflag &= ~(ECHO | ICANON);
    tcsetattr(STDOUT_FILENO, TCSANOW, &termios);
    len = getline(&s, &n, stdin);
    termios.c_lflag |= (ECHO | ICANON);
    tcsetattr(STDOUT_FILENO, TCSANOW, &termios);
    if (len < 1)
        abort();
    printf("\n");
    *password = ne_strndup(s, len - 1);

    free(s);
}


/* Searches string s for octal encoded characters (like \040 for space).
   It returns a new string where these have been replaced by the
   respective characters. */
static char *decode_octal(const char *s) {

    const char *old = s;
    char *decoded = calloc(strlen(s) +1, 1);
    if (decoded == NULL)
        abort();
    while (*old != '\0') {
        char *pos = strstr(old, "\\0");
        if (pos != NULL) {
            char *tail;
            int c = strtol(pos + 1, &tail, 8);
            if ((tail - pos) == 4 && c != 0 && c >= CHAR_MIN && c <= CHAR_MAX) {
                strncat(decoded, old, pos - old);
                *(decoded + strlen(decoded)) = c;
                old = tail;
            } else {
                pos += 2;
                strncat(decoded, old, pos - old);
                old = pos;
            }
        } else {
            strcat(decoded, old);
            old += strlen(old);
        }
    }
    
    return decoded;
}

/* Frees all strings held by args and finally frees args. */
static void delete_args(dav_args *args) {

    if (args->conf != NULL)
        free(args->conf);
    if (args->kernel_fs != NULL)
        free(args->kernel_fs);
    if (args->scheme != NULL)
        free(args->scheme);
    if (args->host != NULL)
        free(args->host);
    if (args->path != NULL)
        free(args->path);
    if (args->secrets != NULL)
        free(args->secrets);
    if (args->username != NULL)
        free(args->username);
    if (args->password != NULL)
        free(args->password);
    if (args->p_host != NULL)
        free(args->p_host);
    if (args->p_user != NULL)
        free(args->p_user);
    if (args->p_passwd != NULL)
        free(args->p_passwd);
    if (args->sys_cache != NULL)
        free(args->sys_cache);
    if (args->cache_dir != NULL)
        free(args->cache_dir);
    if (args->backup_dir != NULL)
        free(args->backup_dir);
    if (args->lock_owner != NULL)
        free(args->lock_owner);
    free(args);
}


/* Evaluates the umask and thedefault modes for directories and files from
   args->mopts, umask(), args->dir_mode and args->file_mode and stores them
   in args. */
static void eval_modes(dav_args *args) {

    if (args->mopts & MS_NOSUID)
        args->dir_umask = S_ISUID | S_ISGID;
    if (args->mopts & MS_RDONLY)
        args->dir_umask |= S_IWUSR | S_IWGRP | S_IWOTH;

    args->file_umask = args->dir_umask;
    if (args->mopts & MS_NOEXEC)
        args->file_umask |= S_IXUSR | S_IXGRP | S_IXOTH;

    mode_t default_mode = umask(0);
    umask(default_mode);
    default_mode = ~default_mode;
    default_mode &= S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;

    if (args->dir_mode == 0) {
        args->dir_mode = default_mode;
        args->dir_mode |= (args->dir_mode & S_IRUSR) ? S_IXUSR : 0;
        args->dir_mode |= (args->dir_mode & S_IRGRP) ? S_IXGRP : 0;
        args->dir_mode |= (args->dir_mode & S_IROTH) ? S_IXOTH : 0;
    }
    args->dir_mode &= ~args->dir_umask;
    args->dir_mode |= S_IFDIR;

    if (args->file_mode == 0)
        args->file_mode = default_mode;
    args->file_mode &= ~args->file_umask;
    args->file_mode |= S_IFREG;
}


/* Parses the string option and stores the values in the appropriate fields of
   args. If an unknown option is found exit(EXIT_FAILURE) is called.
   All strings returned in args are newly allocated, and the calling function
   is responsible to free them.
   option : a comma separated list of options (like the options in fstab and
            in the -o option of the mount-programm).
            For known options see the declaration at the beginning of the
            the function definition. */
static void get_options(dav_args *args, char *option) {

    enum {
        CONF = 0,
        UID,
        GID,
        FILE_MODE,
        DIR_MODE,
        USEPROXY,
        NOUSEPROXY,
        ASKAUTH,
        NOASKAUTH,
        LOCKS,
        NOLOCKS,
        USER,
        NOUSER,
        RW,
        RO,
        SUID,
        NOSUID,
        EXEC,
        NOEXEC,
        DEV,
        NODEV,
        ASYNC,
        AUTO,
        NOAUTO,
        DEFAULTS,
        END
    };
    char *suboptions[] = {
        [CONF] = "conf",
        [UID] = "uid",
        [GID] = "gid",
        [FILE_MODE] = "file_mode",
        [DIR_MODE] = "dir_mode",
        [USEPROXY] = "useproxy",
        [NOUSEPROXY] = "nouseproxy",
        [ASKAUTH] = "askauth",
        [NOASKAUTH] = "noaskauth",
        [LOCKS] = "locks",
        [NOLOCKS] = "nolocks",
        [USER] = "user",
        [NOUSER] = "nouser",
        [RW] = "rw",
        [RO] = "ro",
        [SUID] = "suid",
        [NOSUID] = "nosuid",
        [EXEC] = "exec",
        [NOEXEC] = "noexec",
        [DEV] = "dev",
        [NODEV] = "nodev",
        [ASYNC] = "async",
        [AUTO] = "auto",
        [NOAUTO] = "noauto",
        [DEFAULTS] ="defaults",
        [END] = NULL
    };

    int so;
    char *argument = NULL;
    struct passwd *pwd;
    struct group *grp;

    while (*option != 0) {
        so = getsubopt(&option, suboptions, &argument);
        if ((argument == NULL) && (so < USEPROXY))
            error(EXIT_FAILURE, 0,
                 "Option requires argument -%s.", suboptions[so]);
        switch (so) {
        case CONF:
            if (args->conf != NULL)
                free(args->conf);
            args->conf = ne_strdup(argument);
        case UID:
            pwd = getpwnam(argument);
            if (pwd == NULL) {
                args->uid = arg_to_int(argument, 10, suboptions[so]);
            } else {
                args->uid = pwd->pw_uid;
            }
            break;
        case GID:
            grp = getgrnam(argument);
            if (grp == NULL) {
                args->gid = arg_to_int(argument, 10, suboptions[so]);
            } else {
                args->gid = grp->gr_gid;
            }
            break;
        case FILE_MODE:
            args->file_mode = arg_to_int(argument, 8, suboptions[so]);
            break;
        case DIR_MODE:
            args->dir_mode = arg_to_int(argument, 8, suboptions[so]);
            break;
        case USEPROXY:
            args->useproxy = 1;
            break;
        case NOUSEPROXY:
            args->useproxy = 0;
            break;
        case ASKAUTH:
            args->askauth = 1;
            break;
        case NOASKAUTH:
            args->askauth = 0;
            break;
        case LOCKS:
            args->locks = 1;
            break;
        case NOLOCKS:
            args->locks = 0;
            break;
        case USER:
            args->user = 1;
            break;
        case NOUSER:
            args->user = 0;
            break;
        case RW:
            args->mopts &= ~MS_RDONLY;
            break;
        case RO:
            args->mopts |= MS_RDONLY;
            break;
        case SUID:
            args->mopts &= ~MS_NOSUID;
            break;
        case NOSUID:
            args->mopts |= MS_NOSUID;
            break;
        case EXEC:
            args->mopts &= ~MS_NOEXEC;
            break;
        case NOEXEC:
            args->mopts |= MS_NOEXEC;
            break;
        case DEV:
            args->mopts &= ~MS_NODEV;
            break;
        case NODEV:
            args->mopts |= MS_NODEV;
            break;
        case ASYNC:
        case AUTO:
        case NOAUTO:
        case DEFAULTS:
            break;
        default:
            if (so == -1) {
                printf("Unknown suboption %s.\n", argument);
                usage();
                exit(EXIT_FAILURE);
            }
        }
    }
}


/* Allocates a new dav_args-structure and initializes it.
   All members are set to reasonable defaults. */
static dav_args *new_args(void) {

    dav_args *args = ne_malloc(sizeof(*args));

    struct group *grp = getgrnam(DAV_GROUP);
    if (grp == NULL)
        error(EXIT_FAILURE, 0, "Can't get id of group %s.", DAV_GROUP);
    args->dav_group = grp->gr_gid;
    if (getuid() != 0) {
        args->conf = ne_concat(DAV_USER_DIR, "/", DAV_USER_CONF, NULL);
    } else {
        args->conf = NULL;
    }

    args->user = 0;
    args->mopts = DAV_MOPTS;
    args->kernel_fs = NULL;
    args->buf_size = 0;

    args->uid = getuid();
    args->gid = getgid();
    args->dir_umask = 0;
    args->file_umask = 0;
    args->dir_mode = 0;
    args->file_mode = 0;

    args->scheme = NULL;
    args->host = NULL;
    args->port = 0;
    args->path = NULL;

    if (getuid() != 0) {
        args->secrets = ne_concat(DAV_USER_DIR, "/", DAV_USER_SECRETS, NULL);
    } else {
        args->secrets = NULL;
    }
    args->username = NULL;
    args->password = NULL;

    char *proxy = getenv("http_proxy");
    if (proxy != NULL) {
        split_proxy(&args->p_host, &args->p_port, proxy);
    } else {
        args->p_host = NULL;
        args->p_port = DAV_DEFAULT_PROXY_PORT;
    }
    args->p_user = NULL;
    args->p_passwd = NULL;
    args->useproxy = DAV_USE_PROXY;

    args->askauth = DAV_ASKAUTH;
    args->displayname = DAV_DISPLAYNAME;
    args->locks = DAV_LOCKS;
    args->read_timeout = DAV_READ_TIMEOUT;

    args->sys_cache = ne_strdup(DAV_SYS_CACHE);
    if (getuid() != 0) {
        args->cache_dir = ne_concat(DAV_USER_DIR, "/", DAV_USER_CACHE, NULL);
    } else {
        args->cache_dir = NULL;
    }
    args->backup_dir = ne_strdup(DAV_BACKUP_DIR);
    args->cache_size = DAV_CACHE_SIZE;
    args->table_size = DAV_TABLE_SIZE;
    args->idle_time = DAV_IDLE_TIME;
    args->expire = DAV_EXPIRE;
    args->get_wait = DAV_GET_WAIT;
    args->retry = DAV_RETRY;
    args->max_retry = DAV_MAX_RETRY;
    args->lock_timeout = DAV_LOCK_TIMEOUT;
    args->lock_refresh = DAV_LOCK_REFRESH;
    args->lock_owner = NULL;
    return args;
}


/* Parses line for max. parmc white-space separated parameter tokens and
   returns them in parmv[].
   The '#' character marks the beginning of a comment and the rest of the line
   is ignored.
   Parameters containing one of the characters ' ' (space), '\t' (tab), '\',
   '"' or '#' must be enclosed in double quotes '"' *or* this character has to
   be escaped by preceeding a '\'-character.
   Inside double quotes the '"'-character must be escaped. '\' may be escaped;
   it must be escaped if there is more than on '\'-character in succession.
   Whitespace characters other than ' ' and tab must only occur at the end of
   the line.
   line    : the line to be parsed
   parmc   : the max. number of parameters. It is an error, if more than parmc
             parameters are found
   parmv[] : the parameters found are returned in this array. They are newly
             allocated strings and must be freed by the calling function.
   reurn value : the numer of parameters or -1 if an error occurs. */
static int parse_line(char *line, int parmc, char *parmv[]) {

    enum {
        SPACE,
        SPACE_EXP,
        PARM,
        PARM_ESC,
        PARM_QUO,
        PARM_QUO_ESC,
        ERROR,
        END
    };

    int state = SPACE;
    int parm_no = 0;
    char buf[254];
    char *pos = buf;
    char *end = buf + 253;
    char *p = line;

    while ((state != END) && (state != ERROR)) {
        switch (state) {
        case SPACE:
            if (*p == '\0' || *p == '#') {
                state = END;
            } else if (*p == '\"') {
                state = (parm_no < parmc) ? PARM_QUO : ERROR;
            } else if (*p == '\\') {
                state = (parm_no < parmc) ? PARM_ESC : ERROR;
            } else if (isspace(*p)) {
                ;
            } else if (isgraph(*p)) {
                *pos++ = *p;
                state = (parm_no < parmc) ? PARM : ERROR;
            } else {
                state = ERROR;
            }
            break;
        case SPACE_EXP:
            if (isspace(*p)) {
                state = SPACE;
            } else if (*p == '\0') {
                state = END;
            } else {
                state = ERROR;
            }
            break;
        case PARM:
            if (*p == '#' || *p == '\"') {
                state = ERROR;
            } else if (*p == '\\') {
                state = PARM_ESC;
            } else if (isspace(*p) || *p == '\0') {
                parmv[parm_no++] = ne_strndup(buf, pos - buf);
                pos = buf;
                state = SPACE;
            } else if (isgraph(*p)) {
                *pos++ = *p;
                state = (pos < end) ? PARM : ERROR;
            } else {
                state = ERROR;
            }
            break;
        case PARM_ESC:
            if (*p == '\"' || *p == '\\' || *p == '#' || *p == ' '
                    || *p == '\t') {
                *pos++ = *p;
                state = (pos < end) ? PARM : ERROR;
            } else {
                state = ERROR;
            }
            break;
        case PARM_QUO:
            if (*p == '\\') {
                state = PARM_QUO_ESC;
            } else if (*p == '\"') {
                parmv[parm_no++] = ne_strndup(buf, pos - buf);
                pos = buf;
                state = SPACE_EXP;
            } else if (isgraph(*p) || *p == ' ' || *p == '\t') {
                *pos++ = *p;
                state = (pos < end) ? PARM_QUO : ERROR;
            } else {
                state = ERROR;
            }
            break;
        case PARM_QUO_ESC:
            if (*p == '\"' || *p == '\\') {
                *pos++ = *p;
                state = (pos < end) ? PARM_QUO : ERROR;
            } else if (isgraph(*p) || *p == ' ' || *p == '\t') {
                *pos++ = '\\';
                state = (pos < end) ? PARM_QUO : ERROR;
                if (state == PARM_QUO) {
                    *pos++ = *p;
                    state = (pos < end) ? PARM_QUO : ERROR;
                }
            } else {
                state = ERROR;
            }
            break;
        }
        p++;
    }

    int i;
    if (state == END) {
        for (i = parm_no; i < parmc; i++)
            parmv[i] = NULL;
        return parm_no;
    } else {
        for (i = 0; i < parm_no; i++) {
            free(parmv[i]);
            parmv[i] = NULL;
        }
        return -1;
    }
}


/* Reads the configuration file filename and stores the values in args.
   filename : name of the configuration file.
   system   : boolean value. 1 means it is the system wide configuration
              file. Some parameters are allowed only in the system wide
              configuration file, some only in the user configuration file. */
static void read_config(dav_args *args, const char * filename, int system) {

    FILE *file = fopen(filename, "r");
    if (file == NULL) {
        DBG1("  %s could not be opened.", filename);
        return;
    }
    DBG0("  Reading config from");
    DBG1("    %s.", filename);

    size_t n = 0;
    char *line = NULL;
    int length = getline(&line, &n, file);
    int lineno = 1;
    int applies = 1;

    while (length > 0) {

        int parmc = 2;
        char *parmv[parmc];
        int count;
        count = parse_line(line, parmc, parmv);

        if (count == 1) {

            if (*parmv[0] != '[' || *(parmv[0] + strlen(parmv[0]) - 1) != ']')
                error_at_line(EXIT_FAILURE, 0, filename, lineno,
                              "Malformed line.");
            *(parmv[0] + strlen(parmv[0]) - 1) = '\0';
            applies = (strcmp(parmv[0] + 1, mpoint) == 0);

        } else if (applies && count == 2) {

            if (system && strcmp(parmv[0], "dav_group") == 0) {
                struct group *grp = getgrnam(parmv[1]);
                if (grp == NULL) {
                    args->dav_group = arg_to_int(parmv[1], 10, parmv[0]);
                } else {
                    args->dav_group = grp->gr_gid;
                }
            } else if (strcmp(parmv[0], "kernel_fs") == 0) {
                if (args->kernel_fs != NULL)
                  free(args->kernel_fs);
                args->kernel_fs = ne_strdup(parmv[1]); 
            } else if (strcmp(parmv[0], "buf_size") == 0) {
                args->buf_size = arg_to_int(parmv[1], 10, parmv[0]);
            } else if (!system && strcmp(parmv[0], "secrets") == 0) {
                if (args->secrets != NULL)
                    free(args->secrets);
                args->secrets = ne_strdup(parmv[1]); 
            } else if (strcmp(parmv[0], "proxy") == 0) {
                if (args->p_host != NULL)
                    free(args->p_host);
                split_proxy(&args->p_host, &args->p_port, parmv[1]); 
            } else if (strcmp(parmv[0], "use_proxy") == 0) {
                args->useproxy = arg_to_int(parmv[1], 10, parmv[0]);
            } else if (strcmp(parmv[0], "ask_auth") == 0) {
                args->askauth = arg_to_int(parmv[1], 10, parmv[0]);
            } else if (strcmp(parmv[0], "use_displayname") == 0) {
                args->displayname = arg_to_int(parmv[1], 10, parmv[0]);
            } else if (strcmp(parmv[0], "use_locks") == 0) {
                args->locks = arg_to_int(parmv[1], 10, parmv[0]);
            } else if (strcmp(parmv[0], "read_timeout") == 0) {
                args->read_timeout = arg_to_int(parmv[1], 10, parmv[0]);
            } else if (system && strcmp(parmv[0], "cache_dir") == 0) {
                if (args->sys_cache != NULL)
                  free(args->sys_cache);
                args->sys_cache = ne_strdup(parmv[1]); 
            } else if (!system && strcmp(parmv[0], "cache_dir") == 0) {
                if (args->cache_dir != NULL)
                    free(args->cache_dir);
                args->cache_dir = ne_strdup(parmv[1]); 
            } else if (strcmp(parmv[0], "backup_dir") == 0) {
                if (args->backup_dir != NULL)
                    free(args->backup_dir);
                args->backup_dir = ne_strdup(parmv[1]); 
            } else if (strcmp(parmv[0], "cache_size") == 0) {
                args->cache_size = arg_to_int(parmv[1], 10, parmv[0]); 
            } else if (strcmp(parmv[0], "table_size") == 0) {
                args->table_size = arg_to_int(parmv[1], 10, parmv[0]); 
            } else if (strcmp(parmv[0], "idle_time") == 0) {
                args->idle_time = arg_to_int(parmv[1], 10, parmv[0]); 
            } else if (strcmp(parmv[0], "expire") == 0) {
                args->expire = arg_to_int(parmv[1], 10, parmv[0]); 
            } else if (strcmp(parmv[0], "get_wait") == 0) {
                args->get_wait = arg_to_int(parmv[1], 10, parmv[0]); 
            } else if (strcmp(parmv[0], "retry") == 0) {
                args->retry = arg_to_int(parmv[1], 10, parmv[0]); 
            } else if (strcmp(parmv[0], "max_retry") == 0) {
                args->max_retry = arg_to_int(parmv[1], 10, parmv[0]);
            } else if (strcmp(parmv[0], "lock_timeout") == 0) {
                args->lock_timeout = arg_to_int(parmv[1], 10, parmv[0]);
            } else if (strcmp(parmv[0], "lock_refresh") == 0) {
                args->lock_refresh = arg_to_int(parmv[1], 10, parmv[0]);
            } else if (strcmp(parmv[0], "lock_owner") == 0) {
                if (args->lock_owner != NULL)
                    free(args->lock_owner);
                args->lock_owner = ne_strdup(parmv[1]);
            } else {
                error_at_line(EXIT_FAILURE, 0, filename, lineno,
                              "Unknown configuration parameter.");
            }

        } else if (count < 0 || count > 2) {

            error_at_line(EXIT_FAILURE, 0, filename, lineno,
                          "Malformed line.");
        }

        for (parmc = 0; parmc < count; parmc++)
            free(parmv[parmc]);
        length = getline(&line, &n, file);
        lineno++;
    }

    if (line != NULL)
        free(line);
    fclose(file);
}


/* Searches the file filename for credentials for server url and for the proxy
   args->p_host and stores them in args. */
static void read_secrets(dav_args *args, const char *filename) {

    struct stat st;
    if (stat(filename, &st) < 0) {
        DBG1("  %s could not be opened.", filename);
        return;
    }
    if (st.st_uid != geteuid())
        error(EXIT_FAILURE, 0, "%s has wrong owner.", filename);
    if ((st.st_mode &
          (S_IXUSR | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX)) != 0)
        error(EXIT_FAILURE, 0, "%s has wrong permissions.", filename);

    FILE *file = fopen(filename, "r");
    if (file == NULL) {
        DBG1("  %s could not be opened.", filename);
        return;
    }
    DBG0("  Reading secrets from");
    DBG1("    %s.", filename);

    size_t n = 0;
    char *line = NULL;
    int length = getline(&line, &n, file);
    int lineno = 1;

    while (length > 0) {
        int parmc = 3;
        char *parmv[parmc];
        int count;
        count = parse_line(line, parmc, parmv);
        if (count != 0 && count != 3 && count != 2)
            error_at_line(EXIT_FAILURE, 0, filename, lineno,
                          "Malformed line");

        if (count == 3) {
            if (args->p_host != NULL && strcmp(parmv[0], args->p_host) == 0) {
                if (args->p_user != NULL)
                  free(args->p_user);
                if (args->p_passwd != NULL)
                    free(args->p_passwd);
                args->p_user = ne_strdup(parmv[1]);
                args->p_passwd = ne_strdup(parmv[2]);
            } else if (url != NULL && strcmp(parmv[0], url) == 0) {
                if (args->username != NULL)
                    free(args->username);
                if (args->password != NULL)
                  free(args->password);
                args->username = ne_strdup(parmv[1]);
                args->password = ne_strdup(parmv[2]);
            }
        } else if (count == 2) {
            if (args->p_host != NULL && strcmp(parmv[0], args->p_host) == 0) {
                if (args->p_user != NULL)
                  free(args->p_user);
                if (args->p_passwd != NULL)
                    free(args->p_passwd);
                args->p_user = ne_strdup(parmv[1]);
            } else if (url != NULL && strcmp(parmv[0], url) == 0) {
                if (args->username != NULL)
                    free(args->username);
                if (args->password != NULL)
                  free(args->password);
                args->username = ne_strdup(parmv[1]);
            }
        }

        for (parmc = 0; parmc < count; parmc++)
            free(parmv[parmc]);
        length = getline(&line, &n, file);
        lineno++;
    }

    if (line != NULL)
        free(line);
    fclose(file);
}


/* Splits arg into hostname and port if there is a colon in arg. If there is no
   colon, arg is taken als hostname, and port is set to DAV_DEFAULT_PROXY_PORT.
   The string host will be newly allacoated. The calling function is
   responsible to free it.
   host  : A pointer to a string to return the hostname.
   port  : A pointer to an integer to return the port number.
   arg   : the string to be split. */
static void split_proxy(char **host, int *port, const char *arg) {

    char *ps = strchr(arg, ':');
    if (ps == NULL) {
        *host = ne_strdup(arg);
        *port = DAV_DEFAULT_PROXY_PORT;
    } else {
        *host = ne_strndup(arg, strlen(arg) - strlen(ps));
        ps++;
        *port = arg_to_int(ps, 10, "proxy:port");
    }
}


/* Prints version und help text. */
static void usage(void) {

    printf("%s: %s  <%s>\n", PROGRAM_NAME, PACKAGE_STRING, DAV_HOME); 
    printf("Usage: %s -V|--version   : print version string\n", PROGRAM_NAME);
    printf("       %s -h|--help      : print this message\n", PROGRAM_NAME);
    printf("  To mount a WebDAV-resource don't call %s directly,\n", PROGRAM_NAME);
    printf("  but use 'mount' instead.\n");
    printf("       mount <mountpoint>  : or\n");
    printf("       mount <server-url>  : mount the WebDAV-resource as specified\n");
    printf("                             in /etc/fstab. Filesystem type must be 'davfs'.\n");
    printf("                             For non-root users the option 'user' must be\n");
    printf("                             set in fstab.\n");
    printf("       mount -t davfs <server-url> <mountpoint> -o [options]\n");
    printf("                           : mount the WebDAV-resource <server-url>\n");
    printf("                             on mountpoint <mountpoint>. Only root\n");
    printf("                             is allowed to do this. Options is a\n");
    printf("                             comma separated list of options.\n");
    printf("  Recognised options:\n");
    printf("       conf=        : absolute path of user configuration file\n");
    printf("       uid=         : owner of the filesystem (username or numeric id)\n");
    printf("       gid=         : group of the filesystem (group name or numeric id)\n");
    printf("       file_mode=   : default file mode (ocatal)\n");
    printf("       dir_mode=    : default directory mode (ocatal)\n");
    printf("       ro / rw      : mount read-only / read-write\n");
    printf("       [no]exec     : (don't) allow execution of binaries\n");
    printf("       [no]suid     : (don't) allow suid and sgid bits to take effect\n");
    printf("       [no]user     : (don't) allow normal users to mount\n");
    printf("       [no]useproxy : (don't) use proxy for connection\n");
    printf("       [no]askauth  : (don't) ask interactivly for creditentials if not found\n");
    printf("                      in the secrets files.\n");
    printf("       [no]locks    : (don't) lock resources on the server\n\n");
}

Generated by  Doxygen 1.6.0   Back to index