Fri, 30 Aug 2013 12:48:15 +0200
errors on too many arguments
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2013 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 <errno.h> #include <unistd.h> #include <time.h> #include <libxml/xmlerror.h> #include <sys/types.h> #include <ucx/string.h> #include <dirent.h> #include <libidav/utils.h> #include "config.h" #include "crypto.h" #include "main.h" static DavContext *ctx; void xmlerrorfnc(void * c, const char * msg, ... ) { // nothing } int main(int argc, char **argv) { xmlGenericErrorFunc fnc = xmlerrorfnc; initGenericErrorDefaultFunc(&fnc); load_config(); ctx = dav_context_new(); dav_add_namespace(ctx, "U", "http://www.uap-core.de/"); 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]); return -1; } int ret = -1; 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")) { printf("put\n"); 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, "date")) { ret = cmd_date(args); } else { print_usage(argv[0]); } dav_context_destroy(ctx); return ret; } void print_usage(char *cmd) { fprintf(stderr, "Usage: %s command [options] arguments...\n\n", cmd); fprintf(stderr, "Commands:\n"); fprintf(stderr, " list [-altR] [-u <date>] <url>\n"); fprintf( stderr, " get [-pR] [-k <key>] [-o <file>] [-u <date>] <url>\n"); fprintf(stderr, " put [-pR] [-k <key>] <url> <file>\n"); fprintf(stderr, " mkdir <url>\n"); fprintf(stderr, " remove <url>\n"); fprintf(stderr, " date [url]\n"); fprintf(stderr, "\n"); fprintf(stderr, "Options:\n"); fprintf(stderr, " -k <key> Key to use for encryption or decryption\n"); fprintf(stderr, " -p Don't encrypt or decrypt files\n"); fprintf(stderr, " -R " "Recursively do the operation for all children\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, "\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"); } void url_get_parts(char *url, char **root, char **path) { size_t ulen = strlen(url); *root = NULL; *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; } for(int i=s;i<ulen;i++) { char c = url[i]; if(c == '/') { sstr_t r = sstrn(url, i); sstr_t p = sstrsubs(sstr(url), i); if(p.length == 0) { p = sstrn("/", 1); } *root = sstrdup(r).ptr; *path = sstrdup(p).ptr; return; } } *root = strdup(url); *path = strdup("/"); } void print_resource_error(DavSession *sn, char *path) { char *res_url = util_concat_path(sn->base_url, path); switch(sn->error) { default: { fprintf(stderr, "Operation failed for resource %s.\n", res_url); break; } case DAV_NOT_FOUND: { fprintf(stderr, "Resource %s not found.\n", res_url); break; } case DAV_UNAUTHORIZED: { fprintf(stderr, "Authentication required.\n"); break; } case DAV_FORBIDDEN: { fprintf(stderr, "Access forbidden.\n"); break; } case DAV_METHOD_NOT_ALLOWED: { fprintf(stderr, "Method not allowed.\n"); } case DAV_CONFLICT: { fprintf( stderr, "Missing intermediate collections for resource %s.\n", res_url); } } free(res_url); } int cmd_list(CmdArgs *a) { if(a->argc != 1) { fprintf(stderr, "Too %s arguments\n", a->argc < 1 ? "few":"many"); return -1; } DavSession *sn = NULL; char *url = a->argv[0]; char *root = NULL; char *path = NULL; char *base = NULL; url_get_parts(url, &root, &path); Repository *repo = get_repository(root); if(repo) { base = util_concat_path(repo->url, path); sn = dav_session_new_auth(ctx, base, repo->user, repo->password); } else { base = util_concat_path(root, path); sn = dav_session_new(ctx, base); } char *update = cmd_getoption(a, "update"); time_t t = 0; if(update) { t = util_parse_lastmodified(update); } DavResource *ls; if(cmd_getoption(a, "recursive")) { printf("base: %s\n", base); if(update) { ls = dav_query( sn, "get U:crypto-key from /* where lastmodified > %t", t); } else { ls = dav_query(sn, "get U:crypto-key from /*"); } } else { if(update) { ls = dav_query( sn, "get U:crypto-key from / where lastmodified > %t", t); } else { ls = dav_query(sn, "get U:crypto-key from /"); } } if(!ls) { print_resource_error(sn, path); free(root); free(path); free(base); return -1; } // parameters int show_all = cmd_getoption(a, "all") ? 1 : 0; void (*print_func)(DavResource*, CmdArgs *); if(cmd_getoption(a, "list")) { print_func = ls_print_list_elm; } else { print_func = ls_print_elm; } DavResource *child = ls->children; while(child) { if(child->name[0] != '.' || show_all) { print_func(child, a); } child = child->next; } free(root); free(path); free(base); return 0; } 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) { snprintf(str, 16, ""); } 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; } void ls_print_list_elm(DavResource *res, CmdArgs *a) { int recursive = cmd_getoption(a, "recursive") ? 1 : 0; char flags[16]; memset(flags, '-', 15); flags[2] = '\0'; int type_width = 0; char *type = res->contenttype; if(res->iscollection) { flags[0] = 'd'; type = ""; } char *keyprop = dav_get_property_ns( res, "http://www.uap-core.de/", "crypto-key"); if(keyprop) { flags[1] = 'c'; } 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); char *name = recursive ? res->path+1 : res->name; printf( "%s %*s %10s %12s %s\n", flags, type_width, type, size, date, name); free(date); free(size); if(recursive) { DavResource *child = res->children; while(child) { ls_print_list_elm(child, a); child = child->next; } } } void ls_print_elm(DavResource *res, CmdArgs *a) { int recursive = cmd_getoption(a, "recursive") ? 1 : 0; char *name = recursive ? res->path+1 : res->name; printf("%s\n", name); if(recursive) { DavResource *child = res->children; while(child) { ls_print_elm(child, a); child = child->next; } } } 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"); return -1; } DavSession *sn = NULL; char *url = a->argv[0]; char *root = NULL; char *path = NULL; url_get_parts(url, &root, &path); Repository *repo = get_repository(root); if(repo) { sn = dav_session_new_auth(ctx, repo->url, repo->user, repo->password); } else { sn = dav_session_new(ctx, root); } char *update = cmd_getoption(a, "update"); time_t t = 0; if(update) { t = util_parse_lastmodified(update); } int recursive = cmd_getoption(a, "recursive") ? 1 : 0; DavResource *res; if(recursive) { if(update) { res = dav_query( sn, "get U:crypto-key from %s* where lastmodified > %t", path, t); } else { res = dav_query(sn, "get U:crypto-key from %s*", path); } } else { if(update) { res = dav_query( sn, "get U:crypto-key from %s where lastmodified > %t", path, t); } else { res = dav_query(sn, "get U:crypto-key from %s", path); } } if(!res) { print_resource_error(sn, path); return -1; } if(!recursive && res->iscollection) { char *res_url = util_concat_path(sn->base_url, path); fprintf(stderr, "Resource %s is a collection.\n", res_url); fprintf(stderr, "Use the -R option to download collections.\n"); free(res_url); return -1; } /* * determine the output file * use stdout if the output file is - */ char *outfile = cmd_getoption(a, "output"); if(!outfile) { if(res->iscollection) { outfile = ""; } else { outfile = res->name; } } else if(res->iscollection && !strcmp(outfile, "-")) { fprintf( stderr, "Cannot write output to stdout " "if the requested resource is a collection.\n"); return -1; } int ret = get_resource(repo, res, a, outfile); return ret; } int get_resource(Repository *repo, DavResource *res, CmdArgs *a, char *out) { size_t outlen = strlen(out); // print some status message in recursive mode if(cmd_getoption(a, "recursive")) { char *res_url = util_concat_path(res->session->base_url, res->path); printf("get: %s\n", res_url); free(res_url); } if(res->iscollection) { // create directory if(outlen != 0) { mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; int ret = util_mkdir(out, mode); if(ret != 0 && errno != EEXIST) { return 1; } } DavResource *child = res->children; while(child) { char *newout = outlen > 0 ? util_concat_path(out, child->name) : child->name; int ret = get_resource(repo, child, a, newout); if(outlen > 0) { free(newout); } if(ret) { return 1; } child = child->next; } return 0; } FILE *fout = !strcmp(out, "-") ? stdout : fopen(out, "w"); if(!fout) { fprintf(stderr, "cannot open output file\n"); return -1; } /* * if the -p (plain) option is specified we don't decrypt files * use a key specified with the -k (key) option, a key from the * key property or the repository default key */ void *out_stream = fout; dav_write_func write_func = (dav_write_func)fwrite; AESDecrypter *dec = NULL; char *plain = cmd_getoption(a, "plain"); char *keyname = cmd_getoption(a, "key"); if(!plain) { char *keyprop = dav_get_property_ns( res, "http://www.uap-core.de/", "crypto-key"); Key *key = NULL; char *kn = NULL; if(keyname) { kn = keyname; } else if(keyprop) { kn = keyprop; } else if(repo && repo->decrypt) { kn = repo->default_key; } if(kn) { key = get_key(kn); if(!key) { fprintf(stderr, "Key %s not found!\n", kn); // TODO: free if(cmd_getoption(a, "recursive")) { // skip the file in recursive mode char *res_url = util_concat_path( res->session->base_url, res->path); printf("Skipping resource: %s\n", res_url); free(res_url); return 0; } else { printf("Abort.\n"); // abort return 1; } } } if(key) { dec = aes_decrypter_new(key, fout, (dav_write_func)fwrite); out_stream = dec; write_func = (dav_write_func)aes_write; } } int ret = dav_get_content(res, out_stream, write_func); if(dec) { aes_decrypter_close(dec); } fclose(fout); if(ret && strcmp(out, "-")) { unlink(out); } return 0; } 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"); return -1; } DavSession *sn = NULL; char *url = a->argv[0]; char *file = a->argv[1]; char *root = NULL; char *path = NULL; url_get_parts(url, &root, &path); Repository *repo = get_repository(root); if(repo) { sn = dav_session_new_auth(ctx, repo->url, repo->user, repo->password); } else { sn = dav_session_new(ctx, root); } int ret; if(!strcmp(file, "-")) { FILE *in = stdin; ret = put_file(repo, a, sn, path, "stdin", in); } else { ret = put_entry(repo, a, sn, path, file); } return ret; } int put_entry(Repository *repo, CmdArgs *a, DavSession *sn, char *path, char *file) { int recursive = cmd_getoption(a, "recursive") ? 1 : 0; if(recursive) { printf("put: %s\n", file); } 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) { return 1; } DIR *dir = opendir(file); if(!dir) { // error } struct dirent *entry; while((entry = readdir(dir)) != NULL) { if(!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) { continue; } 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); free(entry_path); free(entry_file); if(r) { ret = 1; break; } } closedir(dir); } else if(S_ISREG(s.st_mode)) { /* * use stdin if the input file is - */ FILE *in = fopen(file, "r"); 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); //free(path); fclose(in); } return ret; } int put_file(Repository *repo, CmdArgs *a, DavSession *sn, char *path, char *name, FILE *in) { DavResource *res = dav_query(sn, "get - from %s", path); 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 { if(sn->errorstr) { fprintf(stderr, "Error: %s\n", sn->errorstr); } else { fprintf(stderr, "Error\n"); } return -1; } } else if(res->iscollection) { // TODO: free res char *newpath = util_concat_path(path, name); free(path); path = newpath; res = dav_resource_new(sn, path); } AESEncrypter *enc = NULL; char *keyname = cmd_getoption(a, "key"); char *kn = NULL; char *plain = cmd_getoption(a, "plain"); if(!plain && (keyname || repo)) { kn = keyname ? keyname : repo->default_key; if(kn) { Key *key = get_key(kn); if(!key) { fprintf(stderr, "Key %s not found!\nAbort.\n", kn); // TODO: free return -1; } if(keyname || repo->encrypt) { enc = aes_encrypter_new(key, in, (dav_read_func)fread); } } } if(enc) { dav_set_content(res, enc, (dav_read_func)aes_read); dav_set_property_ns(res, "http://www.uap-core.de/", "crypto-key", kn); } else { dav_set_content(res, in, (dav_read_func)fread); } 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); } fclose(in); return -1; } if(enc) { aes_encrypter_close(enc); } 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"); return -1; } DavSession *sn = NULL; char *url = a->argv[0]; char *root = NULL; char *path = NULL; url_get_parts(url, &root, &path); Repository *repo = get_repository(root); if(repo) { sn = dav_session_new_auth(ctx, repo->url, repo->user, repo->password); } else { sn = dav_session_new(ctx, root); } DavResource *res = dav_resource_new(sn, path); if(!res) { fprintf(stderr, "error\n"); return -1; } if(dav_delete(res)) { print_resource_error(sn, res->path); fprintf(stderr, "Cannot delete resource.\n"); return -1; } 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"); return -1; } DavSession *sn = NULL; char *url = a->argv[0]; char *root = NULL; char *path = NULL; url_get_parts(url, &root, &path); Repository *repo = get_repository(root); if(repo) { sn = dav_session_new_auth(ctx, repo->url, repo->user, repo->password); } else { sn = dav_session_new(ctx, root); } DavResource *res = dav_resource_new(sn, path); if(!res) { fprintf(stderr, "error\n"); return -1; } res->iscollection = 1; if(dav_create(res)) { print_resource_error(sn, res->path); fprintf(stderr, "Cannot create collection.\n"); return -1; } 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) { DavSession *sn = NULL; char *url = a->argv[0]; char *root = NULL; char *path = NULL; url_get_parts(url, &root, &path); Repository *repo = get_repository(root); if(repo) { sn = dav_session_new_auth(ctx, repo->url, repo->user, repo->password); } else { sn = dav_session_new(ctx, root); } 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; } return 0; } else { fprintf(stderr, "Too many arguments\n"); return -1; } } int cmd_sync(CmdArgs *a) { if(a->argc < 2) { fprintf(stderr, "Too few arguments\n"); return -1; } return 0; }