Sun, 03 Dec 2017 10:17:15 +0100
replaces RAND_pseudo_bytes with RAND_bytes
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2016 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdbool.h> #include <errno.h> #include <unistd.h> #include <time.h> #include <sys/types.h> #include <ucx/string.h> #include <ucx/utils.h> #include <dirent.h> #include <libidav/utils.h> #include <libidav/crypto.h> #include <libidav/session.h> #include "config.h" #include "error.h" #include "assistant.h" #include "main.h" static DavContext *ctx; static int printxmlerror = 1; static void xmlerrorfnc(void * c, const char * msg, ... ) { if(printxmlerror) { va_list ap; va_start(ap, msg); vfprintf(stderr, msg, ap); va_end(ap); } } //define DO_THE_TEST #include <libidav/davqlparser.h> #include <libidav/davqlexec.h> void test() { } int main(int argc, char **argv) { if(argc < 2) { fprintf(stderr, "Missing command\n"); print_usage(argv[0]); return -1; } char *cmd = argv[1]; CmdArgs *args = cmd_parse_args(argc - 2, argv + 2); if(!args) { print_usage(argv[0]); cmd_args_free(args); return -1; } xmlGenericErrorFunc fnc = xmlerrorfnc; initGenericErrorDefaultFunc(&fnc); ctx = dav_context_new(); dav_add_namespace(ctx, "apache", "http://apache.org/dav/props/"); int cfgret = load_config(ctx); int ret = EXIT_FAILURE; printxmlerror = 0; #ifdef DO_THE_TEST test(); return 0; #endif if(!strcmp(cmd, "check") || !strcmp(cmd, "check-config")) { if(!cfgret) { fprintf(stdout, "Configuration OK.\n"); ret = EXIT_SUCCESS; } else { /* no output, the warnings are written by load_config */ ret = EXIT_FAILURE; } } else if(!cfgret) { if(!strcasecmp(cmd, "list") || !strcasecmp(cmd, "ls")) { ret = cmd_list(args); } else if(!strcasecmp(cmd, "get")) { ret = cmd_get(args); } else if(!strcasecmp(cmd, "put")) { ret = cmd_put(args); } else if( !strcasecmp(cmd, "remove") || !strcasecmp(cmd, "rm") || !strcasecmp(cmd, "delete")) { ret = cmd_remove(args); } else if(!strcasecmp(cmd, "mkdir") || !strcasecmp(cmd, "mkcol")) { ret = cmd_mkdir(args); } else if(!strcasecmp(cmd, "copy") || !strcasecmp(cmd, "cp")) { ret = cmd_move(args, true); } else if(!strcasecmp(cmd, "move") || !strcasecmp(cmd, "mv")) { ret = cmd_move(args, false); } else if(!strcasecmp(cmd, "date")) { ret = cmd_date(args); } else if(!strcasecmp(cmd, "set-property")) { ret = cmd_set_property(args); } else if(!strcasecmp(cmd, "get-property")) { ret = cmd_get_property(args); } else if(!strcasecmp(cmd, "remove-property")) { ret = cmd_remove_property(args); } else if(!strcasecmp(cmd, "lock")) { ret = cmd_lock(args); } else if(!strcasecmp(cmd, "unlock")) { ret = cmd_unlock(args); } else if(!strcasecmp(cmd, "info")) { ret = cmd_info(args); } else if(!strcasecmp(cmd, "add-repository") || !strcasecmp(cmd, "add-repo")) { ret = cmd_add_repository(args); } else if(!strcasecmp(cmd, "list-repositories") || !strcasecmp(cmd, "list-repos")) { ret = list_repositories(); } else if(!strcasecmp(cmd, "version") || !strcasecmp(cmd, "-version") || !strcasecmp(cmd, "--version")) { fprintf(stderr, "dav %s\n", DAV_VERSION); } else { print_usage(argv[0]); } } dav_context_destroy(ctx); cmd_args_free(args); free_config(); xmlCleanupParser(); curl_global_cleanup(); return ret; } static char *cmdusageinfo[] = { "list [-altdepcR] [-u <date>] <url>", "get [-pcRKA] [-o <file>] [-u <date>] <url>", "put [-pcRA] [-k <key>] [-L <lock>] <url> <file>", "mkdir [-pc] [-k <key>] [-L <lock>] <url>", "remove [-pc] [-L <lock>] <url>", "copy [-pcO] [-L <lock>] <url> <url>", "move [-pcO] [-L <lock>] <url> <url>", "get-property [-pc] [-n <uri>] <url> <property>", "set-property [-pc] [-L <lock>] [-n <uri>] <url> <property> [value]", "remove-property [-pc] [-n <uri>] <url> <property>", "lock [-pc] [-T timeout] <url>", "unlock [-pc] [-L <lock>] <url>", "info [-pc] <url>", "date [url]", NULL }; char* find_usage_str(char *cmd) { sstr_t c = sstr(cmd); for(int i=0;;i++) { char *str = cmdusageinfo[i]; if(!str) { break; } sstr_t u = sstr(str); if(sstrprefix(u, c)) { return str; } } return NULL; } void print_usage(char *cmd) { for(int i=0;;i++) { char *str = cmdusageinfo[i]; if(!str) { break; } fprintf(stderr, " %s\n", str); } fprintf(stderr, "\n"); fprintf(stderr, "Options:\n"); fprintf(stderr, " -k <key> Key to use for encryption\n"); fprintf(stderr, " -p Don't encrypt or decrypt files\n"); fprintf(stderr, " -c Enable full encryption\n"); fprintf(stderr, " -R " "Recursively do the operation for all children\n"); fprintf(stderr, " -K Keep already present files\n"); fprintf(stderr, " -o <file> Write output to file\n"); fprintf( stderr, " -u <date> " "Get resources which are modified since the specified date\n"); fprintf(stderr, " -a show all files\n"); fprintf(stderr, " -l print resources in long list format\n"); fprintf(stderr, " -t print content type\n"); fprintf(stderr, " -d order by last modified date\n"); fprintf(stderr, " -e show extended flags\n"); fprintf(stderr, " -O override resources\n"); fprintf(stderr, " -A tar import/export\n"); fprintf(stderr, " -L <lock> specificy lock token\n"); fprintf(stderr, " -T <sec> timeout in seconds\n"); fprintf(stderr, " -n <uri> specify namespace uri\n"); fprintf(stderr, " -N disable authentication prompt (all commands)\n"); fprintf(stderr, " -i disable cert verification (all commands)\n"); fprintf(stderr, " -v verbose output (all commands)\n"); fprintf(stderr, "\n"); fprintf(stderr, "Config commands:\n"); fprintf(stderr, " add-repository\n"); fprintf(stderr, " list-repositories\n"); fprintf(stderr, " check-config\n"); fprintf(stderr, "\n"); fprintf(stderr, "Instead of an url you can pass a repository name " "with an optional path:\n"); fprintf(stderr, " <repository>/path/\n"); fprintf(stderr, "\n"); } int request_auth(Repository *repo, DavSession *sn, CmdArgs *a) { if(cmd_getoption(a, "noinput")) { return 0; } static int login = 0; if(login) { return 0; } char *user = NULL; char ubuf[256]; if(repo->user) { user = repo->user; } else { fprintf(stderr, "User: "); fflush(stderr); user = fgets(ubuf, 256, stdin); } if(!user) { return 0; } char *password = util_password_input("Password: "); if(!password || strlen(password) == 0) { return 0; } size_t ulen = strlen(user); if(user[ulen-1] == '\n') { user[ulen-1] = '\0'; } dav_session_set_auth(sn, user, password); free(password); login = 1; return 1; } static Repository* url2repo(char *url, char **path) { size_t ulen = strlen(url); *path = NULL; int s; if(ulen > 7 && !strncasecmp(url, "http://", 7)) { s = 7; } else if(ulen > 8 && !strncasecmp(url, "https://", 8)) { s = 8; } else { s = 1; } sstr_t r = sstr(url); sstr_t p = sstr("/"); for(int i=s;i<ulen;i++) { char c = url[i]; if(c == '/') { r = sstrn(url, i); p = sstrsubs(sstr(url), i); if(p.length == 0) { p = sstrn("/", 1); } break; } } Repository *repo = get_repository(r); if(repo) { *path = sstrdup(p).ptr; } else { repo = calloc(1, sizeof(Repository)); repo->name = strdup(""); repo->decrypt_content = true; repo->verification = true; repo->authmethods = CURLAUTH_BASIC; if(url[ulen-1] == '/') { repo->url = strdup(url); *path = strdup("/"); } else if (strchr(url, '/')) { repo->url = util_parent_path(url); // TODO: check/fix *path = strdup(util_resource_name(url)-1); } else { repo->url = strdup(url); *path = strdup("/"); } } return repo; } static int set_session_config(DavSession *sn, CmdArgs *a) { char *plain = cmd_getoption(a, "plain"); char *crypt = cmd_getoption(a, "crypt"); if(plain && crypt) { fprintf(stderr, "Error: -p and -c option set\n"); return 1; } if (plain) { sn->flags &= ~DAV_SESSION_FULL_ENCRYPTION; } else if(crypt) { sn->flags |= DAV_SESSION_FULL_ENCRYPTION; } if (cmd_getoption(a, "verbose")) { curl_easy_setopt(sn->handle, CURLOPT_VERBOSE, 1L); curl_easy_setopt(sn->handle, CURLOPT_STDERR, stderr); } return 0; } static void set_session_lock(DavSession *sn, CmdArgs *a) { char *locktoken = cmd_getoption(a, "lock"); if(locktoken) { DavLock *lock = dav_create_lock(sn, locktoken, NULL); dav_add_collection_lock(sn, "/", lock); } } static DavSession* connect_to_repo(Repository *repo, CmdArgs *a) { DavSession *sn = dav_session_new_auth(ctx, repo->url, repo->user, repo->password); sn->flags = get_repository_flags(repo); sn->key = dav_context_get_key(ctx, repo->default_key); curl_easy_setopt(sn->handle, CURLOPT_HTTPAUTH, repo->authmethods); curl_easy_setopt(sn->handle, CURLOPT_SSLVERSION, repo->ssl_version); if(repo->cert) { curl_easy_setopt(sn->handle, CURLOPT_CAINFO, repo->cert); } if(!repo->verification || cmd_getoption(a, "insecure")) { curl_easy_setopt(sn->handle, CURLOPT_SSL_VERIFYPEER, 0); curl_easy_setopt(sn->handle, CURLOPT_SSL_VERIFYHOST, 0); } return sn; } #define LIST_QUERY_ORDER_BY_NAME "select `idav:crypto-name`,`idav:crypto-key`,D:lockdiscovery,apache:executable from %s with depth = %d where lastmodified > %t order by iscollection desc, name" #define LIST_QUERY_ORDER_BY_DATE "select `idav:crypto-name`,`idav:crypto-key`,D:lockdiscovery,apache:executable from %s with depth = %d where lastmodified > %t order by iscollection desc, lastmodified desc" int cmd_list(CmdArgs *a) { if(a->argc != 1) { fprintf(stderr, "Too %s arguments\n", a->argc < 1 ? "few":"many"); fprintf(stderr, "Usage: dav %s\n", find_usage_str("list")); return -1; } char *url = a->argv[0]; char *path = NULL; Repository *repo = url2repo(url, &path); DavSession *sn = connect_to_repo(repo, a); if(set_session_config(sn, a)) { return -1; } char *update = cmd_getoption(a, "update"); char *date = cmd_getoption(a, "date"); time_t t = -1; if(update) { t = util_parse_lastmodified(update); } int depth = cmd_getoption(a, "recursive") ? -1 : 1; int ret = -1; DavResource *ls; while(ret != 0) { ls = dav_query( sn, date ? LIST_QUERY_ORDER_BY_DATE : LIST_QUERY_ORDER_BY_NAME, path, depth, t); if(!ls) { if(sn->error == DAV_UNAUTHORIZED) { if(request_auth(repo, sn, a)) { continue; } } print_resource_error(sn, path); break; } // parameters void (*print_func)(DavResource*, char *, CmdArgs *); if(cmd_getoption(a, "list") || cmd_getoption(a, "extended")) { print_func = ls_print_list_elm; } else { print_func = ls_print_elm; } DavResource *child = ls->children; while(child) { print_func(child, path, a); child = child->next; } // leave loop ret = 0; } free(path); //free(base); dav_session_destroy(sn); return ret; } static char* ls_date_str(time_t tm) { struct tm t; struct tm n; time_t now = time(NULL); #ifdef _WIN32 memcpy(&t, localtime(&tm), sizeof(struct tm)); memcpy(&n, localtime(&now), sizeof(struct tm)); #else localtime_r(&tm, &t); localtime_r(&now, &n); #endif /* _WIN32 */ char *str = malloc(16); if(t.tm_year == n.tm_year) { strftime(str, 16, "%b %d %H:%M", &t); } else { strftime(str, 16, "%b %d %Y", &t); } return str; } static char* ls_size_str(DavResource *res) { char *str = malloc(16); uint64_t size = res->contentlength; if(res->iscollection) { str[0] = '\0'; // currently no information for collections } else if(size < 0x400) { snprintf(str, 16, "%" PRIu64 " bytes", size); } else if(size < 0x100000) { float s = (float)size/0x400; int diff = (s*100 - (int)s*100); if(diff > 90) { diff = 0; s += 0.10f; } if(size < 0x2800 && diff != 0) { // size < 10 KiB snprintf(str, 16, "%.1f KiB", s); } else { snprintf(str, 16, "%.0f KiB", s); } } else if(size < 0x40000000) { float s = (float)size/0x100000; int diff = (s*100 - (int)s*100); if(diff > 90) { diff = 0; s += 0.10f; } if(size < 0xa00000 && diff != 0) { // size < 10 MiB snprintf(str, 16, "%.1f MiB", s); } else { size /= 0x100000; snprintf(str, 16, "%.0f MiB", s); } } else if(size < 0x1000000000ULL) { float s = (float)size/0x40000000; int diff = (s*100 - (int)s*100); if(diff > 90) { diff = 0; s += 0.10f; } if(size < 0x280000000 && diff != 0) { // size < 10 GiB snprintf(str, 16, "%.1f GiB", s); } else { size /= 0x40000000; snprintf(str, 16, "%.0f GiB", s); } } else { size /= 1024; float s = (float)size/0x40000000; int diff = (s*100 - (int)s*100); if(diff > 90) { diff = 0; s += 0.10f; } if(size < 0x280000000 && diff != 0) { // size < 10 TiB snprintf(str, 16, "%.1f TiB", s); } else { size /= 0x40000000; snprintf(str, 16, "%.0f TiB", s); } } return str; } static char* ls_name(char *parent, char *path, int *len) { if(parent) { path += strlen(parent); } if(path[0] == '/') { path++; } int pathlen = strlen(path); if(path[pathlen-1] == '/') { pathlen--; } *len = pathlen; return path; } void ls_print_list_elm(DavResource *res, char *parent, CmdArgs *a) { int recursive = cmd_getoption(a, "recursive") ? 1 : 0; int show_all = cmd_getoption(a, "all") ? 1 : 0; if(res->name[0] == '.' && !show_all) { return; } char flags[16]; memset(flags, '-', 15); int type_width = 0; char *type = res->contenttype; if(res->iscollection) { flags[0] = 'd'; type = ""; } char *keyprop = dav_get_string_property_ns( res, DAV_NS, "crypto-key"); if(keyprop) { flags[1] = 'c'; } if(cmd_getoption(a, "extended")) { flags[6] = '\0'; if(dav_get_string_property(res, "D:lockdiscovery")) { flags[2] = 'l'; } char *executable = dav_get_string_property_ns( res, "http://apache.org/dav/props/", "executable"); if(executable && util_getboolean(executable)) { flags[3] = 'x'; } } else { flags[2] = '\0'; } if(cmd_getoption(a, "type")) { type_width = 20; } if(type == NULL || type_width == 0) { type = ""; } char *date = ls_date_str(res->lastmodified); char *size = ls_size_str(res); int namelen = strlen(res->name); char *name = recursive ? ls_name(parent, res->path, &namelen) : res->name; //char *name = recursive ? res->path+1 : res->name; printf( "%s %*s %10s %12s %.*s\n", flags, type_width, type, size, date, namelen, name); free(date); free(size); if(recursive) { DavResource *child = res->children; while(child) { //ls_print_list_elm(child, a); if(child->name[0] != '.' || show_all) { ls_print_list_elm(child, parent, a); } child = child->next; } } } void ls_print_elm(DavResource *res, char *parent, CmdArgs *a) { int recursive = cmd_getoption(a, "recursive") ? 1 : 0; int show_all = cmd_getoption(a, "all") ? 1 : 0; if(res->name[0] == '.' && !show_all) { return; } int namelen = strlen(res->name); char *name = recursive ? ls_name(parent, res->path, &namelen) : res->name; printf("%.*s\n", namelen, name); if(recursive) { DavResource *child = res->children; while(child) { ls_print_elm(child, parent, a); child = child->next; } } } static void free_getres(void *r) { GetResource *getres = r; free(getres->path); free(getres); } int cmd_get(CmdArgs *a) { if(a->argc != 1) { // TODO: change this, when get supports retrieval of multiple files fprintf(stderr, "Too %s arguments\n", a->argc < 1 ? "few":"many"); fprintf(stderr, "Usage: dav %s\n", find_usage_str("get")); return -1; } char *url = a->argv[0]; char *path = NULL; Repository *repo = url2repo(url, &path); DavSession *sn = connect_to_repo(repo, a); if(set_session_config(sn, a)) { return -1; } char *update = cmd_getoption(a, "update"); time_t t = -1; if(update) { t = util_parse_lastmodified(update); if (t == 0) { fprintf(stderr, "Invalid date format. Possible formats are:\n" " RFC-1123 - example: Thu, 29 Nov 2012 21:35:35 GMT\n" " RFC-3339 - example: 2012-11-29T21:35:35Z\n"); return -1; } } int recursive = cmd_getoption(a, "recursive") ? 1 : 0; DavResource *res; int depth = recursive ? -1 : 1; for(int i=0;i<2;i++) { res = dav_query( sn, "select - from %s with depth = %d where iscollection or lastmodified > %t", path, depth, t); if(!res && sn->error == DAV_UNAUTHORIZED) { if(request_auth(repo, sn, a)) { continue; } } break; } if(!res) { print_resource_error(sn, path); return -1; } if(!recursive && res->iscollection) { fprintf(stderr, "Resource %s is a collection.\n", res->path); fprintf(stderr, "Use the -R option to download collections.\n"); return -1; } /* * determine the output file * use stdout if the output file is - */ char *outfile = cmd_getoption(a, "output"); char *basepath = outfile; char *tar = cmd_getoption(a, "tar"); if(!outfile) { if(res->iscollection) { basepath = ""; } else { basepath = res->name; } if(tar) { outfile = "-"; } } else if(tar) { basepath = ""; } else if(res->iscollection && !strcmp(outfile, "-")) { fprintf( stderr, "Cannot write output to stdout " "if the requested resource is a collection.\n"); return -1; } // get list of resources UcxList *reslist = NULL; uint64_t totalsize = 0; uint64_t rescount = 0; GetResource *getres = malloc(sizeof(GetResource)); getres->res = res; getres->path = strdup(basepath); char *structure = cmd_getoption(a, "structure"); // iterate over resource tree UcxList *stack = ucx_list_prepend(NULL, getres); while(stack) { GetResource *g = stack->data; stack = ucx_list_remove(stack, stack); if(g->res->iscollection) { DavResource *child = g->res->children; while(child) { // add resource to stack size_t pathlen = strlen(g->path); GetResource *newres = malloc(sizeof(GetResource)); newres->res = child; newres->path = pathlen > 0 ? util_concat_path(g->path, child->name) : strdup(child->name); stack = ucx_list_prepend(stack, newres); child = child->next; } } else { if(structure) { // download only directory structure // this is a hidden feature and will be replaced in the future continue; // skip non-collection resource } totalsize += g->res->contentlength; rescount++; } if(strlen(g->path) == 0) { free_getres(g); } else { reslist = ucx_list_append(reslist, g); } } // download resources int ret; getfunc get; TarOutputStream *tout = NULL; if(tar) { get = (getfunc)resource2tar; FILE *tarfile = strcmp(outfile, "-") ? fopen(outfile, "wb") : stdout; if(!tarfile) { perror("Cannot open tar output file"); return -1; } tout = tar_open(tarfile); } else { get = get_resource; } UCX_FOREACH(elm, reslist) { GetResource *getres = elm->data; ret = get(repo, getres, a, tout); if(ret) { break; } } if(tar) { // close tar stream if(tar_close(tout)) { fprintf(stderr, "tar stream broken\n"); ret = -1; } } ucx_list_free_content(reslist, free_getres); ucx_list_free(reslist); free(path); return ret; } int get_resource(Repository *repo, GetResource *getres, CmdArgs *a, void *unused) { DavResource *res = getres->res; char *out = res->path; if(res->iscollection) { printf("get: %s\n", res->path); mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; int ret = util_mkdir(out, mode); if(ret != 0 && errno != EEXIST) { fprintf(stderr, "Cannot create directory '%s': ", out); perror(""); return 1; } return 0; } int isstdout = !strcmp(out, "-"); if(cmd_getoption(a, "keep") && !isstdout) { struct stat s; if(stat(out, &s)) { if(errno != ENOENT) { perror("stat"); } } else { if(cmd_getoption(a, "recursive")) { printf("skip: %s\n", res->path); } return 0; } } // print some status message in recursive mode if(cmd_getoption(a, "recursive")) { printf("get: %s\n", res->path); } FILE *fout = isstdout ? stdout : fopen(out, "wb"); if(!fout) { fprintf(stderr, "cannot open output file\n"); return -1; } int ret = dav_get_content(res, fout, (dav_write_func)fwrite); fclose(fout); if(ret && strcmp(out, "-")) { print_resource_error(res->session, res->path); //if(strcmp(out, "-")) { // unlink(out); //} } return 0; } #define DEFAULT_DIR_MODE T_IRUSR | T_IWUSR | T_IXUSR | T_IRGRP | T_IXGRP | T_IROTH | T_IXOTH #define DEFAULT_FILE_MODE T_IRUSR | T_IWUSR | T_IRGRP | T_IROTH int resource2tar(Repository *repo, GetResource *res, CmdArgs *a, TarOutputStream *tar) { DavResource *d = res->res; if(d->iscollection) { fprintf(stderr, "add d: %s\n", res->path); return tar_add_dir(tar, res->path, DEFAULT_DIR_MODE, d->lastmodified); } fprintf(stderr, "add f: %s\n", res->path); // add tar file header if(tar_begin_file(tar, res->path, DEFAULT_FILE_MODE, d->contentlength, d->lastmodified)) { fprintf(stderr, "TAR Error: %s\n", tar_error2str(tar->error)); return -1; } if(dav_get_content(d, tar, (dav_write_func)tar_fwrite)) { print_resource_error(d->session, d->path); return -1; } // download content return tar_end_file(tar); } int cmd_put(CmdArgs *a) { if(a->argc != 2) { // TODO: change, when put supports multiple files (however it should do) fprintf(stderr, "Too %s arguments\n", a->argc < 2 ? "few":"many"); fprintf(stderr, "Usage: dav %s\n", find_usage_str("put")); return -1; } char *url = a->argv[0]; char *file = a->argv[1]; char *path = NULL; Repository *repo = url2repo(url, &path); DavSession *sn = connect_to_repo(repo, a); if(set_session_config(sn, a)) { return -1; } set_session_lock(sn, a); // override the session key if the -k option is specified char *keyname = cmd_getoption(a, "key"); if(keyname) { DavKey *key = dav_context_get_key(ctx, keyname); if(key) { sn->key = key; } else { fprintf(stderr, "Key %s not found!\nAbort.\n", keyname); // TODO: free return -1; } } char *tar = cmd_getoption(a, "tar"); int ret; if(!tar) { if(!strcmp(file, "-")) { FILE *in = stdin; ret = put_file(repo, a, sn, path, "stdin", in, 0); } else { ret = put_entry(repo, a, sn, path, file, TRUE); } } else { ret = put_tar(repo, a, sn, file, path); } free(path); return ret; } int put_entry(Repository *repo, CmdArgs *a, DavSession *sn, char *path, char *file, DavBool root) { int recursive = cmd_getoption(a, "recursive") ? 1 : 0; struct stat s; if(stat(file, &s)) { perror("stat"); fprintf(stderr, "cannot stat file %s\n", file); return -1; } int ret = 0; if(S_ISDIR(s.st_mode)) { if(!recursive) { fprintf( stderr, "%s is a directory.\nUse the -R option to upload directories.\n", file); return 1; } if(!root) { printf("mkcol: %s\n", file); } DIR *dir = opendir(file); if(!dir) { // error } struct dirent *entry; int nument = 0; while((entry = readdir(dir)) != NULL) { if(!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) { continue; } nument++; char *entry_file = util_concat_path(file, entry->d_name); char *entry_path = util_concat_path(path, entry->d_name); int r = put_entry(repo, a, sn, entry_path, entry_file, FALSE); free(entry_path); free(entry_file); if(r) { ret = 1; break; } } closedir(dir); if(nument == 0) { // create empty directory DavResource *res = dav_resource_new(sn, path); res->iscollection = TRUE; if(!dav_exists(res)) { if(dav_create(res)) { fprintf(stderr, "Cannot create collection %s\n", path); print_resource_error(sn, res->path); ret = 1; } } dav_resource_free(res); } } else if(S_ISREG(s.st_mode)) { if(recursive) { printf("put: %s\n", file); } /* * use stdin if the input file is - */ FILE *in = fopen(file, "rb"); if(!in) { fprintf(stderr, "cannot open input file\n"); return -1; } char *filename = util_resource_name(file); //path = util_concat_path(path, filename); ret = put_file(repo, a, sn, path, filename, in, s.st_size); //free(path); fclose(in); } return ret; } int put_tar(Repository *repo, CmdArgs *a, DavSession *sn, char *tarfile, char *path) { int isstdin = !strcmp(tarfile, "-"); FILE *in = isstdin ? stdin : fopen(tarfile, "rb"); if(!in) { perror("Cannot open tar file"); return -1; } DavResource *col = NULL; for(int i=0;i<2;i++) { col = dav_query(sn, "select - from %s", path); if(!col && sn->error == DAV_UNAUTHORIZED) { if(request_auth(repo, sn, a)) { continue; } } break; } if(!col) { if(sn->error == DAV_NOT_FOUND) { col = dav_resource_new(sn, path); col->iscollection = TRUE; if(dav_create(col)) { print_resource_error(sn, path); return -1; } } else { print_resource_error(sn, path); return -1; } } else if(!col->iscollection) { fprintf(stderr, "%s is not a collection\n", col->href); return -1; } int ret = 0; TarInputStream *tar = tar_inputstream_open(in); TarEntry *e = NULL; while((e = tar_read_entry(tar)) != NULL) { char *newpath = util_concat_path(path, e->path); if(e->type == TAR_TYPE_FILE) { fprintf(stderr, "put: %s\n", e->path); DavResource *res = dav_resource_new(sn, newpath); dav_set_content(res, tar, (dav_read_func)tar_fread); dav_set_content_length(res, (size_t)e->size); if(dav_store(res)) { print_resource_error(sn, res->path); fprintf(stderr, "Cannot upload file.\n"); if(sn->errorstr) { fprintf(stderr, "%s\n", sn->errorstr); } return -1; } } else if(e->type == TAR_TYPE_DIRECTORY) { printf("mkcol: %s\n", e->path); DavResource *res = dav_resource_new(sn, newpath); res->iscollection = TRUE; if(!dav_exists(res)) { if(dav_create(res)) { fprintf(stderr, "Cannot create collection %s\n", newpath); print_resource_error(sn, res->path); ret = 1; free(newpath); break; } } } else { fprintf(stderr, "skip: %s\n", e->path); } free(newpath); } if(tar->error != TAR_OK) { ret = -1; } if(!isstdin) { fclose(in); } return ret; } int put_file(Repository *repo, CmdArgs *a, DavSession *sn, char *path, char *name, FILE *in, off_t len) { DavResource *res = NULL; for(int i=0;i<2;i++) { res = dav_query(sn, "select - from %s", path); if(!res && sn->error == DAV_UNAUTHORIZED) { if(request_auth(repo, sn, a)) { continue; } } break; } if(!res) { if(sn->error == DAV_NOT_FOUND) { res = dav_resource_new(sn, path); if(dav_create(res)) { fprintf(stderr, "Cannot create resource.\n"); return -1; } } else { print_resource_error(sn, path); return -1; } } else if(res->iscollection) { // TODO: free res char *newpath = util_concat_path(path, name); path = newpath; res = dav_resource_new(sn, path); free(newpath); int ret = put_file(repo, a, sn, res->path, NULL, in, len); // TODO: free res return ret; } dav_set_content(res, in, (dav_read_func)fread); if(len > 0 && len < 0x7d000000) { dav_set_content_length(res, (size_t)len); } if(dav_store(res)) { print_resource_error(sn, res->path); fprintf(stderr, "Cannot upload file.\n"); if(sn->errorstr) { fprintf(stderr, "%s\n", sn->errorstr); } return -1; } return 0; } int cmd_remove(CmdArgs *a) { if(a->argc != 1) { // TODO: change, when removal of multiple files is supported fprintf(stderr, "Too %s arguments\n", a->argc < 1 ? "few":"many"); fprintf(stderr, "Usage: dav %s\n", find_usage_str("remove")); return -1; } char *url = a->argv[0]; char *path = NULL; Repository *repo = url2repo(url, &path); DavSession *sn = connect_to_repo(repo, a); if(set_session_config(sn, a)) { return -1; } set_session_lock(sn, a); DavResource *res = dav_resource_new(sn, path); if(!res) { fprintf(stderr, "error\n"); return -1; } int err = 0; for(int i=0;i<2;i++) { err = dav_delete(res); if(err && sn->error == DAV_UNAUTHORIZED && request_auth(repo, sn, a)) { continue; } else { break; } } if(err) { print_resource_error(sn, res->path); fprintf(stderr, "Cannot delete resource.\n"); return -1; } free(path); return 0; } int cmd_mkdir(CmdArgs *a) { if(a->argc != 1) { // TODO: change, when creation of multiple dirs is supported fprintf(stderr, "Too %s arguments\n", a->argc < 1 ? "few":"many"); fprintf(stderr, "Usage: dav %s\n", find_usage_str("mkdir")); return -1; } char *url = a->argv[0]; char *path = NULL; Repository *repo = url2repo(url, &path); DavSession *sn = connect_to_repo(repo, a); if(set_session_config(sn, a)) { return -1; } set_session_lock(sn, a); // override the session key if the -k option is specified char *keyname = cmd_getoption(a, "key"); if(keyname) { DavKey *key = dav_context_get_key(ctx, keyname); if(key) { sn->key = key; } else { fprintf(stderr, "Key %s not found!\nAbort.\n", keyname); // TODO: free return -1; } } DavResource *res = dav_resource_new(sn, path); if(!res) { fprintf(stderr, "error\n"); return -1; } res->iscollection = 1; int err = 0; for(int i=0;i<2;i++) { err = dav_create(res); if(err && sn->error == DAV_UNAUTHORIZED && request_auth(repo, sn, a)) { continue; } else { break; } } if(err) { print_resource_error(sn, res->path); fprintf(stderr, "Cannot create collection.\n"); return -1; } free(path); return 0; } int cmd_move(CmdArgs *a, int cp) { if(a->argc != 2) { // TODO: change, when creation of multiple dirs is supported fprintf(stderr, "Too %s arguments\n", a->argc < 2 ? "few":"many"); fprintf(stderr, "Usage: dav %s\n", find_usage_str(cp ? "copy" : "move")); return -1; } char *url1 = a->argv[0]; char *path1 = NULL; Repository *repo1 = url2repo(url1, &path1); char *url2 = a->argv[1]; char *path2 = NULL; Repository *repo2 = url2repo(url2, &path2); DavSession *sn = connect_to_repo(repo1, a); if(set_session_config(sn, a)) { return -1; } set_session_lock(sn, a); DavBool override = cmd_getoption(a, "override") ? true : false; if(repo1 == repo2) { DavResource *res = dav_resource_new(sn, path1); int err = cp ? dav_copy_o(res, path2, override) : dav_move_o(res, path2, override); if(err) { print_resource_error(sn, res->path); fprintf(stderr, "Cannot %s resource.\n", cp ? "copy" : "move"); return -1; } } else { char *server1 = util_url_base(repo1->url); char *server2 = util_url_base(repo2->url); if(!strcmp(server1, server2)) { DavSession *sn2 = connect_to_repo(repo2, a); if(set_session_config(sn2, a)) { return -1; } DavResource *dest = dav_resource_new(sn2, path2); char *desthref = dav_resource_get_href(dest); char *desturl = util_get_url(sn2, desthref); DavResource *res = dav_resource_new(sn, path1); int err = cp ? dav_copyto(res, desturl, override) : dav_moveto(res, desturl, override); free(desturl); dav_session_destroy(sn2); if(err) { print_resource_error(sn, res->path); fprintf(stderr, "Cannot %s resource.\n", cp ? "copy" : "move"); return -1; } } else { fprintf(stderr, "Copy or Move not supported for different hosts.\n"); return -1; } } dav_session_destroy(sn); return 0; } static size_t get_date_header_cb(void *header, int s, int n, void *data) { char **date_str = (char**)data; //printf("header: %.*s\n", s*n, header); sstr_t h = sstrn(header, s*n); if(sstrprefix(h, S("Date:"))) { sstr_t v = sstrsubs(h, 5); v = sstrdup(sstrtrim(v)); *date_str = v.ptr; } return s*n; } int cmd_date(CmdArgs *a) { if(a->argc < 1) { time_t now = time(NULL); struct tm *date = gmtime(&now); char str[32]; putenv("LC_TIME=C"); size_t len = strftime(str, 32, "%a, %d %b %Y %H:%M:%S GMT\n", date); fwrite(str, 1, len, stdout); } else if (a->argc == 1) { char *url = a->argv[0]; char *path = NULL; DavSession *sn = connect_to_repo(url2repo(url, &path), a); DavResource *res = dav_resource_new(sn, path); char *date = NULL; curl_easy_setopt(sn->handle, CURLOPT_HEADERFUNCTION, get_date_header_cb); curl_easy_setopt(sn->handle, CURLOPT_WRITEHEADER, &date); if(dav_exists(res) && date) { printf("%s\n", date); } else { return -1; } free(path); return 0; } else { fprintf(stderr, "Too many arguments\n"); fprintf(stderr, "Usage: dav %s\n", find_usage_str("date")); return -1; } return 0; } int cmd_get_property(CmdArgs *a) { if(a->argc < 2) { fprintf(stderr, "Too few arguments\n"); fprintf(stderr, "Usage: dav %s\n", find_usage_str("get-property")); return -1; } char *url = a->argv[0]; char *path = NULL; Repository *repo = url2repo(url, &path); DavSession *sn = connect_to_repo(repo, a); if(set_session_config(sn, a)) { return -1; } char *namespace = cmd_getoption(a, "namespace"); char *property = a->argv[1]; DavPropName propname; if(namespace) { propname.ns = namespace; propname.name = property; } else { dav_get_property_namespace_str(ctx, property, &propname.ns, &propname.name); if(!propname.ns || !propname.name) { fprintf(stderr, "Error: unknown namespace prefix\n"); return -1; } } DavResource *res = dav_resource_new(sn, path); for(int i=0;i<2;i++) { if(dav_load_prop(res, &propname, 1)) { if(i == 0 && sn->error == DAV_UNAUTHORIZED && request_auth(repo, sn, a)) { continue; } print_resource_error(sn, res->path); return -1; } break; } // TODO: show xml //DavXmlNode *x = dav_get_property_ns(res, propname.ns, propname.name); char *value = dav_get_string_property_ns(res, propname.ns, propname.name); if(!value) { fprintf(stderr, "Error: no property value.\n"); return -1; } int ret = 0; if(value) { printf("%s\n", value); } else { // TODO: correct error message fprintf(stderr, "Error: property not found.\n"); ret = -1; } free(path); return ret; } int cmd_set_property(CmdArgs *a) { if(a->argc < 2) { fprintf(stderr, "Too few arguments\n"); fprintf(stderr, "Usage: dav %s\n", find_usage_str("set-property")); return -1; } char *url = a->argv[0]; char *path = NULL; Repository *repo = url2repo(url, &path); DavSession *sn = connect_to_repo(repo, a); if(set_session_config(sn, a)) { return -1; } set_session_lock(sn, a); DavResource *res = dav_resource_new(sn, path); for(int i=0;i<2;i++) { if(!dav_exists(res)) { if(i == 0 && sn->error == DAV_UNAUTHORIZED && request_auth(repo, sn, a)) { continue; } print_resource_error(sn, res->path); return -1; } } char *namespace = cmd_getoption(a, "namespace"); char *property = a->argv[1]; char *value = a->argc > 2 ? a->argv[2] : stdin2str(); if(namespace) { dav_set_property_ns(res, namespace, property, value); } else { dav_set_property(res, property, value); } int ret = 0; for(int i=0;i<2;i++) { if(dav_store(res)) { if(i == 0 && sn->error == DAV_UNAUTHORIZED && request_auth(repo, sn, a)) { continue; } print_resource_error(sn, res->path); fprintf(stderr, "Cannot set property.\n"); ret = -1; } break; } free(path); return ret; } int cmd_remove_property(CmdArgs *a) { if(a->argc < 2) { fprintf(stderr, "Too few arguments\n"); fprintf(stderr, "Usage: dav %s\n", find_usage_str("remove-property")); return -1; } char *url = a->argv[0]; char *path = NULL; Repository *repo = url2repo(url, &path); DavSession *sn = connect_to_repo(repo, a); if(set_session_config(sn, a)) { return -1; } char *namespace = cmd_getoption(a, "namespace"); char *property = a->argv[1]; DavPropName propname; if(namespace) { propname.ns = namespace; propname.name = property; } else { dav_get_property_namespace_str(ctx, property, &propname.ns, &propname.name); } int ret = 0; DavResource *res = dav_resource_new(sn, path); dav_remove_property_ns(res, propname.ns, propname.name); for(int i=0;i<2;i++) { if(dav_store(res)) { if(i == 0 && sn->error == DAV_UNAUTHORIZED && request_auth(repo, sn, a)) { continue; } print_resource_error(sn, res->path); fprintf(stderr, "Cannot set property.\n"); ret = -1; } break; } free(path); return ret; } int cmd_lock(CmdArgs *a) { if(a->argc != 1) { fprintf(stderr, "Too %s arguments\n", a->argc > 1 ? "many" : "few"); fprintf(stderr, "Usage: dav %s\n", find_usage_str("lock")); return -1; } char *url = a->argv[0]; char *path = NULL; Repository *repo = url2repo(url, &path); DavSession *sn = connect_to_repo(repo, a); ucx_mempool_reg_destr(sn->mp, path, free); if(set_session_config(sn, a)) { return -1; } time_t timeout = 0; char *timeoutstr = cmd_getoption(a, "timeout"); if(timeoutstr) { if(!sstrcasecmp(sstr(timeoutstr), S("infinite"))) { timeout = -1; } else { timeout = (time_t)atoi(timeoutstr); // TODO: use strtol } } DavResource *res = dav_resource_new(sn, path); for(int i=0;i<2;i++) { if(!dav_lock_t(res, timeout)) { break; } if(i == 0 && sn->error == DAV_UNAUTHORIZED && request_auth(repo, sn, a)) { continue; } print_resource_error(sn, res->path); return -1; } DavLock *lock = dav_get_lock(sn, res->path); if(!lock) { // this should really not happen // do some damage control dav_unlock(res); fprintf(stderr, "Error: Cannot find lock token for %s\n", res->path); return -1; } printf("%s\n", lock->token); dav_session_destroy(sn); return 0; } static char* read_line() { UcxBuffer *buf = ucx_buffer_new(NULL, 128, UCX_BUFFER_AUTOEXTEND); int c; while((c = getchar()) != EOF) { if(c == '\n') { break; } ucx_buffer_putc(buf, c); } char *str = NULL; sstr_t line = sstrtrim(sstrn(buf->space, buf->size)); if(line.length != 0) { str = sstrdup(line).ptr; } ucx_buffer_free(buf); return str; } int cmd_unlock(CmdArgs *a) { if(a->argc != 1) { fprintf(stderr, "Too %s arguments\n", a->argc > 1 ? "many" : "few"); fprintf(stderr, "Usage: dav %s\n", find_usage_str("unlock")); return -1; } char *url = a->argv[0]; char *path = NULL; Repository *repo = url2repo(url, &path); DavSession *sn = connect_to_repo(url2repo(url, &path), a); ucx_mempool_reg_destr(sn->mp, path, free); if(set_session_config(sn, a)) { return -1; } char *locktoken = cmd_getoption(a, "lock"); if(locktoken) { DavLock *lock = dav_create_lock(sn, locktoken, NULL); dav_add_collection_lock(sn, "/", lock); } else { locktoken = read_line(); if(!locktoken) { fprintf(stderr, "No lock token specified.\nAbort.\n"); return -1; } DavLock *lock = dav_create_lock(sn, locktoken, NULL); dav_add_collection_lock(sn, "/", lock); free(locktoken); } int ret = 0; DavResource *res = dav_resource_new(sn, path); for(int i=0;i<2;i++) { if(!dav_unlock(res)) { break; } if(i == 0 && sn->error == DAV_UNAUTHORIZED && request_auth(repo, sn, a)) { continue; } print_resource_error(sn, res->path); ret = -1; break; } dav_session_destroy(sn); return ret; } static int count_children(DavResource *res) { DavResource *child = res->children; int count = 0; while(child) { count++; child = child->next; } return count; } int cmd_info(CmdArgs *a) { if(a->argc < 1) { fprintf(stderr, "Too few arguments\n"); fprintf(stderr, "Usage: dav %s\n", find_usage_str("info")); return -1; } char *url = a->argv[0]; char *path = NULL; Repository *repo = url2repo(url, &path); DavSession *sn = connect_to_repo(repo, a); if(set_session_config(sn, a)) { return -1; } DavResource *res = dav_resource_new(sn, path); for(int i=0;i<2;i++) { if(!dav_load(res)) { printf("name: %s\n", res->name); printf("path: %s\n", res->path); char *server = util_url_base(sn->base_url); char *url = util_concat_path(server, res->href); printf("url: %s\n", url); free(url); free(server); if(res->iscollection) { printf("type: collection\n"); printf("size: %d\n", count_children(res)); } else { printf("type: resource\n"); char *len = ls_size_str(res); printf("size: %s\n", len); free(len); } size_t count = 0; DavPropName *properties = dav_get_property_names(res, &count); char *last_ns = NULL; for(int i=0;i<count;i++) { DavPropName p = properties[i]; if(!last_ns || strcmp(last_ns, p.ns)) { printf("\nnamespace: %s\n", p.ns); last_ns = p.ns; } sstr_t value = sstr(dav_get_string_property_ns(res, p.ns, p.name)); value = sstrtrim(value); printf(" %s: %.*s\n", p.name, (int)value.length, value.ptr); } dav_session_free(sn, properties); return 0; } else { if(i == 0 && sn->error == DAV_UNAUTHORIZED && request_auth(repo, sn, a)) { continue; } print_resource_error(sn, res->path); break; } } return -1; } char* stdin2str() { UcxBuffer *buf = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND); size_t size = ucx_stream_copy(stdin, buf, fread, ucx_buffer_write); if(size == 0) { ucx_buffer_free(buf); return NULL; } else { ucx_buffer_putc(buf, '\0'); char *str = buf->space; free(buf); return str; } } /* ---------- config commands ---------- */ int cmd_add_repository(CmdArgs *args) { printf("Each repository must have an unique name.\n"); char *name = assistant_getcfg("name"); if(!name) { fprintf(stderr, "Abort\n"); return -1; } if(get_repository(sstr(name))) { fprintf(stderr, "Repository %s already exists.\nAbort\n", name); return -1; } printf("\nSpecify the repository base url.\n"); char *url = assistant_getcfg("url"); if(!url) { fprintf(stderr, "Abort\n"); return -1; } printf("\nUser for HTTP authentication.\n"); char *user = assistant_getoptcfg("user"); char *password = NULL; if(user) { password = assistant_gethiddenoptcfg("password"); } printf("\n"); Repository repo; memset(&repo, 0, sizeof(Repository)); repo.name = name; repo.url = url; repo.user = user; repo.password = password; int ret = 0; if(add_repository(&repo)) { fprintf(stderr, "Cannot write config.xml\n"); ret = -1; } else { printf("\nAdded repository: %s (%s)\n", name, url); } free(name); free(url); if(user) { free(user); } if(password) { free(password); } return ret; }