Mon, 29 Jan 2024 10:41:00 +0100
add config related code from dav / load config and fill repo list
--- a/application/application.c Sun Jan 28 20:47:40 2024 +0100 +++ b/application/application.c Mon Jan 29 10:41:00 2024 +0100 @@ -31,15 +31,26 @@ #include <string.h> #include <stdbool.h> +#include <libidav/webdav.h> + #include "window.h" +#include "config.h" +static DavContext* davctx; void application_init(void) { + davctx = dav_context_new(); + application_create_menu(); } void application_startup(UiEvent* event, void* data) { + if (load_config(davctx)) { + // TODO: error + exit(-1); + } + UiObject* win = window_create(); DavApp* app = application_create_app_document(); @@ -88,14 +99,27 @@ } + DavApp* application_create_app_document(void) { DavApp* doc = ui_document_new(sizeof(DavApp)); UiContext* ctx = ui_document_context(doc); doc->repos = ui_list_new(ctx, "repolist"); - ui_list_append(doc->repos, "test"); - - + // create repo list + application_update_repolist(doc); return doc; } + +void application_update_repolist(DavApp* app) { + DavConfig* config = get_config(); + DavCfgRepository* repo = config->repositories; + + // TODO: free list content ptr + ui_list_clear(app->repos); + + for (DavCfgRepository* repo = config->repositories; repo; repo = repo->next) { + // TODO: copy repo name + ui_list_append(app->repos, repo->name.value.ptr); + } +}
--- a/application/application.h Sun Jan 28 20:47:40 2024 +0100 +++ b/application/application.h Mon Jan 29 10:41:00 2024 +0100 @@ -26,6 +26,9 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#ifndef IDAV_APPLICATION_H +#define IDAV_APPLICATION_H + #include <ui/ui.h> #include <stdio.h> @@ -34,6 +37,10 @@ #include <libidav/webdav.h> #include <libidav/config.h> +#ifdef __cplusplus +extern "C" { +#endif + typedef struct DavApp { DavConfig *dav_config; @@ -56,3 +63,11 @@ DavApp* application_create_app_document(void); + +void application_update_repolist(DavApp *app); + +#ifdef __cplusplus +} +#endif + +#endif /* IDAV_APPLICATION_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/application/config.c Mon Jan 29 10:41:00 2024 +0100 @@ -0,0 +1,328 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 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 <sys/types.h> +#include <cx/hash_map.h> +#include <cx/utils.h> +#include <errno.h> +#include <libxml/tree.h> + +#include "pwd.h" +#include "config.h" +#include "system.h" + +#include <libidav/utils.h> +#include <libidav/config.h> + +#define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b) +#define xstrEQ(a,b) !xmlStrcasecmp(BAD_CAST a, BAD_CAST b) + +#define print_error(lineno, ...) \ + do {\ + fprintf(stderr, "Error (config.xml line %u): ", lineno); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "Abort.\n"); \ + } while(0); +#define print_warning(lineno, ...) \ + do {\ + fprintf(stderr, "Warning (config.xml line %u): ", lineno); \ + fprintf(stderr, __VA_ARGS__); \ + } while(0); + +#ifdef _WIN32 +#define ENV_HOME getenv("USERPROFILE") +#else +#define ENV_HOME getenv("HOME") +#endif /* _WIN32 */ + +static CxMap* repos; +static CxMap* keys; + +static DavConfig* davconfig; +static PwdStore* pstore; + +static char* secretstore_unlock_cmd; +static char* secretstore_lock_cmd; + +int check_config_dir(void) { + char* file = util_concat_path(ENV_HOME, ".dav"); + int ret = 0; + if (util_mkdir(file, S_IRWXU)) { + if (errno != EEXIST) { + ret = 1; + } + } + free(file); + return ret; +} + +static DavContext* context; + +void create_default_config(char* file) { + xmlDoc* doc = xmlNewDoc(BAD_CAST "1.0"); + xmlNode* root = xmlNewNode(NULL, BAD_CAST "configuration"); + xmlDocSetRootElement(doc, root); + xmlSaveFormatFileEnc(file, doc, "UTF-8", 1); + xmlFreeDoc(doc); +} + +char* config_file_path(char* name) { + char* davd = util_concat_path(ENV_HOME, ".dav"); + if (!davd) { + return NULL; + } + char* path = util_concat_path(davd, name); + free(davd); + return path; +} + +cxmutstr config_load_file(const char* path) { + FILE* file = sys_fopen(path, "r"); + if (!file) { + return (cxmutstr) { NULL, 0 }; + } + + CxBuffer buf; + cxBufferInit(&buf, NULL, 1024, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND); + cx_stream_copy(file, &buf, (cx_read_func)fread, (cx_write_func)cxBufferWrite); + fclose(file); + + return cx_mutstrn(buf.space, buf.size); +} + +int load_config(DavContext* ctx) { + context = ctx; + // TODO: free the config somewhere + repos = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); + keys = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); + + char* pwfile = util_concat_path(ENV_HOME, ".dav/secrets.crypt"); + pstore = pwdstore_open(pwfile); + free(pwfile); + + char* file = util_concat_path(ENV_HOME, ".dav/config.xml"); + + struct stat s; + if (stat(file, &s)) { + switch (errno) { + case ENOENT: { + return 0; + } + default: { + perror("Cannot load config.xml"); + } + } + return 1; + } + + cxmutstr config_content = config_load_file(file); + int config_error; + davconfig = dav_config_load(config_content, &config_error); + free(config_content.ptr); + free(file); + + if (!davconfig) { + fprintf(stderr, "Cannot load config.xml\n"); + return 1; + } + + return dav_config_register_keys(davconfig, ctx, load_key_file); +} + +DavConfig* get_config(void) { + return davconfig; +} + +int store_config(void) { + if (check_config_dir()) { + return 1; + } + + CxBuffer* buf = dav_config2buf(davconfig); + if (!buf) { + return 1; + } + + char* file = util_concat_path(ENV_HOME, ".dav/config.xml"); + FILE* cout = sys_fopen(file, "w"); + if (!cout) { + cxBufferFree(buf); + return 1; + } + + // should only fail if we run out of disk space or something like that + // in that case, the config file is only destroyed + // could only be prevented, if we write to a temp file first and than + // rename it + fwrite(buf->space, buf->size, 1, cout); + + cxBufferFree(buf); + fclose(cout); + + return 0; +} + +void free_config(void) { + if (davconfig) { + dav_config_free(davconfig); + } +} + +cxmutstr load_key_file(const char* filename) { + cxmutstr k; + k.ptr = NULL; + k.length = 0; + + FILE* file = NULL; + if (filename[0] == '/') { + file = sys_fopen(filename, "r"); + } + else { + char* path = util_concat_path(ENV_HOME, ".dav/"); + char* p2 = util_concat_path(path, filename); + file = sys_fopen(p2, "r"); + free(path); + free(p2); + } + + if (!file) { + fprintf(stderr, "Error: cannot load keyfile %s\n", filename); + return k; + } + + char* data = malloc(256); + size_t r = fread(data, 1, 256, file); + k.ptr = data; + k.length = r; + + fclose(file); + return k; +} + +static char* get_attr_content(xmlNode* node) { + // TODO: remove code duplication (util_xml_get_text) + while (node) { + if (node->type == XML_TEXT_NODE) { + return (char*)node->content; + } + node = node->next; + } + return NULL; +} + +int load_namespace(const xmlNode* node) { + const char* prefix = NULL; + const char* uri = NULL; + + xmlAttr* attr = node->properties; + while (attr) { + if (attr->type == XML_ATTRIBUTE_NODE) { + char* value = get_attr_content(attr->children); + if (!value) { + print_error( + node->line, + "missing value for attribute %s\n", (char*)attr->name); + return 1; + } + if (xstreq(attr->name, "prefix")) { + prefix = value; + } + else if (xstreq(attr->name, "uri")) { + uri = value; + } + else { + print_error( + node->line, + "unexpected attribute %s\n", (char*)attr->name); + return 1; + } + } + attr = attr->next; + } + + if (!prefix) { + print_error(node->line, "missing prefix attribute\n"); + return 1; + } + if (!uri) { + print_error(node->line, "missing uri attribute\n"); + return 1; + } + + if (dav_get_namespace(context, prefix)) { + print_error(node->line, "namespace prefix '%s' already used\n", prefix); + return 1; + } + + return dav_add_namespace(context, prefix, uri); +} + +int load_secretstore(const xmlNode* node) { + // currently only one secretstore is supported + + if (!pstore) { + return 0; + } + + node = node->children; + int error = 0; + while (node) { + if (node->type == XML_ELEMENT_NODE) { + char* value = util_xml_get_text(node); + if (value) { + if (xstreq(node->name, "unlock-command")) { + pstore->unlock_cmd = strdup(value); + } + else if (xstreq(node->name, "lock-command")) { + pstore->lock_cmd = strdup(value); + } + } + } + node = node->next; + } + + return error; +} + +PwdStore* get_pwdstore(void) { + return pstore; +} + +int pwdstore_save(PwdStore* pwdstore) { + if (check_config_dir()) { + return 1; + } + + char* pwfile = util_concat_path(ENV_HOME, ".dav/secrets.crypt"); + int ret = pwdstore_store(pwdstore, pwfile); + free(pwfile); + return ret; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/application/config.h Mon Jan 29 10:41:00 2024 +0100 @@ -0,0 +1,72 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 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. + */ + +#ifndef CONFIG_H +#define CONFIG_H + +#include <cx/string.h> +#include <stdbool.h> +#include <libidav/webdav.h> +#include "pwd.h" + +#include <libidav/config.h> + +#ifdef __cplusplus +extern "C" { +#endif + + +#define HTTP_PROXY 1 +#define HTTPS_PROXY 2 + + +int check_config_dir(void); + +char* config_file_path(char* name); + +cxmutstr config_load_file(const char* path); + +int load_config(DavContext* ctx); +DavConfig* get_config(void); +int store_config(void); +void free_config(void); + +cxmutstr load_key_file(const char* filename); + +PwdStore* get_pwdstore(void); +int pwdstore_save(PwdStore* pwdstore); + + +int request_auth(DavSession* sn, void* userdata); + +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_H */ +
--- a/application/main.c Sun Jan 28 20:47:40 2024 +0100 +++ b/application/main.c Mon Jan 29 10:41:00 2024 +0100 @@ -36,8 +36,10 @@ #include <ui/ui.h> #include "application.h" +#include "system.h" int idav_main(int argc, char **argv) { + sys_init(); ui_init("idav", argc, argv); application_init(); @@ -45,6 +47,7 @@ ui_main(); + sys_uninit(); return 0; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/application/pwd.c Mon Jan 29 10:41:00 2024 +0100 @@ -0,0 +1,472 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2018 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 "pwd.h" + +#include <cx/buffer.h> +#include <cx/utils.h> +#include <cx/hash_map.h> + +#ifdef _WIN32 +#include <winsock.h> +#pragma comment(lib, "Ws2_32.lib") +#else +#include <netinet/in.h> +#endif + +PwdStore* pwdstore_open(const char *file) { + FILE *in = fopen(file, "r"); + if(!in) { + return NULL; + } + + CxBuffer *buf = cxBufferCreate(NULL, 2048, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); + cx_stream_copy(in, buf, (cx_read_func)fread, (cx_write_func)cxBufferWrite); + fclose(in); + + if(buf->size < PWDS_HEADER_SIZE || buf->space[0] != PWDS_MAGIC_CHAR) { + cxBufferFree(buf); + return NULL; + } + + PwdStore *p = malloc(sizeof(PwdStore)); + p->ids = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); + p->locations = cxLinkedListCreateSimple(CX_STORE_POINTERS); + p->noloc = cxLinkedListCreateSimple(CX_STORE_POINTERS); + p->index = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); + p->content = buf; + p->key = NULL; + p->unlock_cmd = NULL; + p->lock_cmd = NULL; + p->encoffset = PWDS_HEADER_SIZE; + p->isdecrypted = 0; + + if(pwdstore_getindex(p)) { + pwdstore_free(p); + return NULL; + } + + return p; +} + +PwdStore* pwdstore_new(void) { + PwdStore *p = calloc(1, sizeof(PwdStore)); + p->ids = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); + p->locations = cxLinkedListCreateSimple(CX_STORE_POINTERS); + p->noloc = cxLinkedListCreateSimple(CX_STORE_POINTERS); + p->index = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); + p->content = cxBufferCreate(NULL, PWDS_HEADER_SIZE, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); + PWDS_MAGIC(p) = PWDS_MAGIC_CHAR; + PWDS_VERSION(p) = 1; + PWDS_ENC(p) = DAV_KEY_AES256; + PWDS_PWFUNC(p) = DAV_PWFUNC_PBKDF2_SHA256; + dav_rand_bytes((unsigned char*)p->content->space+4, 16); + p->isdecrypted = 1; + p->encoffset = PWDS_HEADER_SIZE; + return p; +} + +static int readval(CxBuffer *in, char **val, int allowzero) { + // value = length string + // length = uint32 + // string = bytes + + *val = NULL; + + // get length + uint32_t length = 0; + if(cxBufferRead(&length, 1, sizeof(uint32_t), in) != sizeof(uint32_t)) { + return 0; + } + length = ntohl(length); // convert from BE to host byte order + if(length == 0) { + if(allowzero) { + return 1; + } else { + return 0; + } + } + if(length > PWDSTORE_MAX_LEN) { + return 0; + } + + // get value + char *value = malloc(length + 1); + value[length] = 0; + if(cxBufferRead(value, 1, length, in) != length) { + free(value); + return 0; + } + + *val = value; + return 1; +} + +static int read_indexentry(PwdStore *p, CxBuffer *in) { + // read type of index element + int type = cxBufferGet(in); + if(type == EOF || type != 0) { + // only type 0 supported yet + return 0; + } + + char *id = NULL; + CxList *locations = cxLinkedListCreateSimple(CX_STORE_POINTERS); + locations->simple_destructor = free; + + // get id (required) + int ret = 0; + if(readval(in, &id, FALSE)) { + ret = 1; + // get locations + char *location = NULL; + while((ret = readval(in, &location, TRUE)) == 1) { + if(!location) { + break; + } + cxListAdd(locations, location); + } + } + + if(ret) { + pwdstore_put_index(p, id, locations); + } else { + if(id) free(id); + cxListDestroy(locations); + } + + return ret; +} + +static int read_pwdentry(PwdStore *p, CxBuffer *in) { + int type = cxBufferGet(in); + if(type == EOF || type != 0) { + // only type 0 supported yet + return 0; + } + + char *id = NULL; + char *location = NULL; + char *user = NULL; + char *password = NULL; + + int ret = 0; + if(readval(in, &id, FALSE)) { + if(readval(in, &user, FALSE)) { + if(readval(in, &password, FALSE)) { + pwdstore_put(p, id, user, password); + ret = 1; + } + } + } + + if(id) free(id); + if(location) free(location); + if(user) free(user); + if(password) free(password); + + return ret; +} + +static int remove_list_entries(PwdStore *s, const char *id) { + int ret = 0; + + CxList *loc_entry = NULL; + CxList *noloc_entry = NULL; + + CxMutIterator i = cxListMutIterator(s->locations); + cx_foreach(PwdIndexEntry*, ie, i) { + if(!strcmp(ie->id, id)) { + cxIteratorFlagRemoval(i); + // TODO: break loop + } + } + i = cxListMutIterator(s->noloc); + cx_foreach(PwdIndexEntry*, ie, i) { + if(!strcmp(ie->id, id)) { + cxIteratorFlagRemoval(i); + // TODO: break loop + } + } + + return ret; +} + +void pwdstore_remove_entry(PwdStore *s, const char *id) { + while(remove_list_entries(s, id)) {} + + CxHashKey key = cx_hash_key_str(id); + PwdIndexEntry *i = cxMapRemoveAndGet(s->index, key); + PwdEntry *e = cxMapRemoveAndGet(s->ids, key); + + if(i) { + cxListDestroy(i->locations); + free(i->id); + free(i); + } + if(e) { + free(e->id); + free(e->user); + free(e->password); + free(e); + } +} + +int pwdstore_getindex(PwdStore *s) { + uint32_t netindexlen; + + // set the position to the last 4 bytes of the header + // for reading index length + s->content->pos = PWDS_HEADER_SIZE - sizeof(uint32_t); + + // read indexlen and convert to host byte order + if(cxBufferRead(&netindexlen, 1, sizeof(uint32_t), s->content) != sizeof(uint32_t)) { + return 1; + } + uint32_t indexlen = ntohl(netindexlen); + + // integer overflow check + if(UINT32_MAX - PWDS_HEADER_SIZE < indexlen) { + return 1; + } + if(s->content->size < PWDS_HEADER_SIZE + indexlen) { + return 1; + } + // encrypted content starts after the index content + s->encoffset = PWDS_HEADER_SIZE + indexlen; + + // the index starts after the header + CxBuffer *index = cxBufferCreate(s->content->space+PWDS_HEADER_SIZE, indexlen, cxDefaultAllocator, 0); + index->size = indexlen; + + // read index + while(read_indexentry(s, index)) {} + + // free index buffer structure (not the content) + cxBufferFree(index); + + return 0; +} + +int pwdstore_decrypt(PwdStore *p) { + if(!p->key) { + return 1; + } + if(p->isdecrypted) { + return 0; + } + + // decrypt contet + size_t encsz = p->content->size - p->encoffset; + CxBuffer *enc = cxBufferCreate(p->content->space + p->encoffset, encsz, cxDefaultAllocator, 0); + enc->size = encsz; + enc->size = p->content->size - p->encoffset; + CxBuffer *content = aes_decrypt_buffer(enc, p->key); + cxBufferFree(enc); + if(!content) { + return 1; + } + + while(read_pwdentry(p, content)) {} + + cxBufferFree(content); + + return 0; +} + +int pwdstore_setpassword(PwdStore *p, const char *password) { + DavKey *key = dav_pw2key( + password, + (unsigned char*)(p->content->space + 4), + 16, + PWDS_PWFUNC(p), + PWDS_ENC(p)); + if(!key) { + return 1; + } + + p->key = key; + return 0; +} + +void pwdstore_encsettings(PwdStore *p, uint8_t enc, uint8_t pwfunc) { + PWDS_ENC(p) = enc; + PWDS_PWFUNC(p) = pwfunc; +} + +void pwdstore_free_entry(PwdEntry *e) { + if(e->id) free(e->id); + if(e->user) free(e->user); + if(e->password) free(e->password); + free(e); +} + +void pwdstore_free(PwdStore* p) { + p->ids->simple_destructor = (cx_destructor_func)pwdstore_free_entry; + cxMapDestroy(p->ids); + + cxListDestroy(p->locations); + + if(p->content) { + cxBufferFree(p->content); + } + + free(p); +} + +int pwdstore_has_id(PwdStore *s, const char *id) { + return cxMapGet(s->index, cx_hash_key_str(id)) ? 1 : 0; +} + +PwdEntry* pwdstore_get(PwdStore *p, const char *id) { + PwdEntry *e = cxMapGet(p->ids, cx_hash_key_str(id)); + if(e && e->user && e->password) { + return e; + } else { + return NULL; + } +} + +void pwdstore_put(PwdStore *p, const char *id, const char *username, const char *password) { + PwdEntry *entry = malloc(sizeof(PwdEntry)); + entry->id = strdup(id); + entry->user = strdup(username); + entry->password = strdup(password); + cxMapPut(p->ids, cx_hash_key_str(id), entry); +} + +void pwdstore_put_index(PwdStore *p, char *id, CxList *locations) { + PwdIndexEntry *e = cxMapGet(p->index, cx_hash_key_str(id)); + if(e) { + return; + } + PwdIndexEntry *newentry = malloc(sizeof(PwdIndexEntry)); + newentry->id = id; + if(locations) { + newentry->locations = locations; + cxListAdd(p->locations, newentry); + } else { + newentry->locations = NULL; + cxListAdd(p->noloc, newentry); + } + cxMapPut(p->index, cx_hash_key_str(id), newentry); +} + +void write_index_entry(CxBuffer *out, PwdIndexEntry *e) { + uint32_t idlen = strlen(e->id); + uint32_t netidlen = htonl(idlen); + + cxBufferPut(out, 0); // type + + cxBufferWrite(&netidlen, 1, sizeof(uint32_t), out); + cxBufferWrite(e->id, 1, idlen, out); + + CxIterator i = cxListIterator(e->locations); + cx_foreach(char *, location, i) { + uint32_t locationlen = strlen(location); + uint32_t netlocationlen = htonl(locationlen); + + cxBufferWrite(&netlocationlen, 1, sizeof(uint32_t), out); + cxBufferWrite(location, 1, locationlen, out); + } + + uint32_t terminate = 0; + cxBufferWrite(&terminate, 1, sizeof(uint32_t), out); +} + +int pwdstore_store(PwdStore *p, const char *file) { + if(!p->key) { + return 1; + } + + CxBuffer *index = cxBufferCreate(NULL, 2048, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); + CxBuffer *content = cxBufferCreate(NULL, 2048, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); + + // create index + CxIterator i = cxListIterator(p->noloc); + cx_foreach(PwdIndexEntry*, e, i) { + write_index_entry(index, e); + } + i = cxListIterator(p->locations); + cx_foreach(PwdIndexEntry*, e, i) { + write_index_entry(index, e); + } + + i = cxMapIteratorValues(p->ids); + cx_foreach(PwdEntry*, value, i) { + if(!value->id || !value->user || !value->password) { + continue; + } + + uint32_t idlen = strlen(value->id); + uint32_t ulen = strlen(value->user); + uint32_t plen = strlen(value->password); + uint32_t netidlen = htonl(idlen); + uint32_t netulen = htonl(ulen); + uint32_t netplen = htonl(plen); + + // content buffer + cxBufferPut(content, 0); // type + + cxBufferWrite(&netidlen, 1, sizeof(uint32_t), content); + cxBufferWrite(value->id, 1, idlen, content); + cxBufferWrite(&netulen, 1, sizeof(uint32_t), content); + cxBufferWrite(value->user, 1, ulen, content); + cxBufferWrite(&netplen, 1, sizeof(uint32_t), content); + cxBufferWrite(value->password, 1, plen, content); + } + + content->pos = 0; + CxBuffer *enc = aes_encrypt_buffer(content, p->key); + + p->content->pos = PWDS_HEADER_SIZE - sizeof(uint32_t); + p->content->size = PWDS_HEADER_SIZE; + + // add index after header + uint32_t netindexlen = htonl((uint32_t)index->size); + cxBufferWrite(&netindexlen, 1, sizeof(uint32_t), p->content); + cxBufferWrite(index->space, 1, index->size, p->content); + + // add encrypted buffer + cxBufferWrite(enc->space, 1, enc->size, p->content); + + cxBufferFree(enc); + + FILE *out = fopen(file, "w"); + if(!out) { + return 1; + } + fwrite(p->content->space, 1, p->content->size, out); + fclose(out); + + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/application/pwd.h Mon Jan 29 10:41:00 2024 +0100 @@ -0,0 +1,197 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2018 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. + */ + +#ifndef PWD_H +#define PWD_H + +#include <stdlib.h> +#include <inttypes.h> + +#include <cx/map.h> +#include <cx/buffer.h> +#include <cx/linked_list.h> +#include <libidav/crypto.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define PWDSTORE_MAX_LEN 4096 + +/* + * File Format: + * + * file = header, index, enc_content + * header = magic, version, enc, pwfunc, salt, indexlen + * magic = 1 byte + * version = 1 byte + * enc = 1 byte + * pwfunc = 1 byte + * salt = 16 bytes + * indexlen = uint32 + * index = { itype length id locations zero } + * enc_content = iv bytes + * iv = 16 bytes + * content = { entry } + * entry = itype length id length username length password + * length = uint32 + * zero = 4 zero bytes + * itype = 1 byte + * id = string + * locations = { length string } + * username = string + * password = string + * + * The content is AES encrypted with a key derived from a password + * and the salt. The first 16 bytes are the aes iv. + * + * All integers are big endian + */ + +#define PWDS_HEADER_SIZE 24 + +typedef struct PwdStore PwdStore; +typedef struct PwdEntry PwdEntry; +typedef struct PwdIndexEntry PwdIndexEntry; + +struct PwdStore { + /* + * map of all credentials + * key is the username + * value is PwdEntry* + */ + CxMap *ids; + + /* + * list of all credentials with location + * value is PwdIndexEntry* + */ + CxList *locations; + + /* + * list of all credentials without location + * value is PwdIndexEntry* + */ + CxList *noloc; + + /* + * index map that contains all elements from the lists + * 'locations' and 'noloc' + */ + CxMap *index; + + /* + * a buffer containing the complete file content + */ + CxBuffer *content; + + /* + * key used for encryption/decryption + */ + DavKey *key; + + /* + * optional shell command, that is used for getting the master password + */ + char *unlock_cmd; + + /* + * optional shell command, that is exected when the secretstore is closed + */ + char *lock_cmd; + + /* + * start offset of the encrypted buffer + */ + uint32_t encoffset; + + /* + * indicates if the PwdStore is decrypted with pwdstore_decrypt + */ + uint8_t isdecrypted; +}; + +#define PWDS_MAGIC(p) (p)->content->space[0] +#define PWDS_VERSION(p) (p)->content->space[1] +#define PWDS_ENC(p) (p)->content->space[2] +#define PWDS_PWFUNC(p) (p)->content->space[3] + +#define PWDS_MAGIC_CHAR 'P' + +struct PwdEntry { + char *id; + char *user; + char *password; +}; + +struct PwdIndexEntry { + char *id; + CxList *locations; +}; + +/* + * opens the password store + * the content is still encrypted and must be decrypted using pwdstore_decrypt + */ +PwdStore* pwdstore_open(const char *file); + +PwdStore* pwdstore_new(void); + +/* + * decrypts the password store with a password + */ +int pwdstore_decrypt(PwdStore *p); + +int pwdstore_setpassword(PwdStore *p, const char *password); + +void pwdstore_encsettings(PwdStore *p, uint8_t enc, uint8_t pwfunc); + +void pwdstore_free_entry(PwdEntry *e); +void pwdstore_free(PwdStore* p); + +int pwdstore_has_id(PwdStore *s, const char *id); +int pwdstore_has_location(PwdStore *s, const char *location); + +PwdEntry* pwdstore_get(PwdStore *p, const char *id); + +void pwdstore_put(PwdStore *p, const char *id, const char *username, const char *password); +void pwdstore_put_index(PwdStore *p, char *id, CxList *locations); + +void pwdstore_remove_entry(PwdStore *s, const char *id); + +int pwdstore_store(PwdStore *p, const char *file); + +/* private */ +int pwdstore_getindex(PwdStore *s); + +#ifdef __cplusplus +} +#endif + +#endif /* PWD_H */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/application/system.c Mon Jan 29 10:41:00 2024 +0100 @@ -0,0 +1,531 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 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 <libidav/utils.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <errno.h> + +#include <cx/string.h> + +#ifndef _WIN32 +#include <unistd.h> +#endif + +#include "system.h" + +void sys_freedirent(SysDirEnt *ent) { + free(ent->name); + free(ent); +} + +#ifndef _WIN32 +/* ---------- POSIX implementation ---------- */ + +void sys_init(void) { + +} +void sys_uninit(void) { + +} + +SYS_DIR sys_opendir(const char *path) { + DIR *dir = opendir(path); + if(!dir) { + return NULL; + } + SysDir *d = malloc(sizeof(SysDir)); + d->dir = dir; + d->ent = NULL; + return d; +} + +SysDirEnt* sys_readdir(SYS_DIR dir) { + if(dir->ent) { + free(dir->ent->name); + free(dir->ent); + dir->ent = NULL; + } + struct dirent *ent = readdir(dir->dir); + if(ent) { + SysDirEnt *e = malloc(sizeof(SysDirEnt)); + e->name = strdup(ent->d_name); + dir->ent = e; + return e; + } + return NULL; +} + +void sys_closedir(SYS_DIR dir) { + closedir(dir->dir); + if(dir->ent) { + free(dir->ent->name); + free(dir->ent); + } + free(dir); +} + +FILE* sys_fopen(const char *path, const char *mode) { + return fopen(path, mode); +} + +int sys_stat(const char *path, SYS_STAT *s) { + return stat(path, s); +} + +int sys_lstat(const char *path, SYS_STAT *s) { + return lstat(path, s); +} + +int sys_islink(const char *path) { + struct stat s; + if(!lstat(path, &s)) { + return S_ISLNK(s.st_mode); + } + return 0; +} + +int sys_rename(const char *oldpath, const char *newpath) { + return rename(oldpath, newpath); +} + +int sys_unlink(const char *path) { + return unlink(path); +} + +int sys_mkdir(const char *path) { + return mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); +} + +char* sys_readlink(const char *path, SYS_STAT *s) { + char *ret = NULL; + + off_t l_sz = s->st_size + 16; + size_t lnksize = l_sz > 256 ? l_sz : 256; + char *lnkbuf = malloc(lnksize); + + ssize_t len = 0; + for(int i=0;i<4;i++) { + // we try to read the link at most 4 times + // only repeat if the buffer is too small + len = readlink(path, lnkbuf, lnksize); + if(len < lnksize) { + ret = lnkbuf; // success + lnkbuf[len] = 0; // terminate buffer + break; + } + lnksize *= 2; // retry with bigger buffer + lnkbuf = realloc(lnkbuf, lnksize); + } + + if(!ret) { + free(lnkbuf); + } + return ret; +} + +int sys_symlink(const char *target, const char *linkpath) { + int err = symlink(target, linkpath); + if(err && errno == EEXIST) { + if(unlink(linkpath)) { + return 1; + } + return sys_symlink(target, linkpath); + } + return err; +} + +int sys_truncate(const char* path, off_t length) { + return truncate(path, length); +} + +#else +/* ---------- Windows implementation ---------- */ + +#include <windows.h> +#include <winnls.h> +#include <shobjidl.h> +#include <objbase.h> +#include <objidl.h> + +#include <direct.h> +#include <wchar.h> + +void sys_init(void) { + HRESULT res = CoInitialize(NULL); + if(res != S_OK) { + fprintf(stderr, "Error: CoInitialize failed\n"); + } +} + +void sys_uninit(void) { + CoUninitialize(); +} + +static wchar_t* path2winpath(const char *path, int dir, int *newlen) { + size_t len = strlen(path); + size_t lenadd = dir ? 2 : 0; + + + wchar_t *wpath = calloc(len+lenadd+1, sizeof(wchar_t)); + int wlen = MultiByteToWideChar( + CP_UTF8, + 0, + path, + len, + wpath, + len+1 + ); + if(newlen) { + *newlen = wlen; + } + for(int i=0;i<wlen;i++) { + if(wpath[i] == L'/') { + wpath[i] = L'\\'; + } + } + + if(dir) { + if(wpath[wlen-1] != L'\\') { + wpath[wlen++] = L'\\'; + } + wpath[wlen++] = L'*'; + } + wpath[wlen] = 0; + + return wpath; +} + +static char* winpath2multibyte(const wchar_t *wpath, size_t wlen) { + size_t maxlen = wlen * 4; + char *ret = malloc(maxlen + 1); + int ret_len = WideCharToMultiByte( + CP_UTF8, + 0, + wpath, + wlen, + ret, + maxlen, + NULL, + NULL); + ret[ret_len] = 0; + return ret; +} + + + +SYS_DIR sys_opendir(const char *path) { + struct WinDir *dir = malloc(sizeof(struct WinDir)); + wchar_t *dirpath = path2winpath(path, TRUE, NULL); + if(!dirpath) { + fprintf(stderr, "Cannot convert path \"%s\" to UTF16\n", path); + free(dir); + return NULL; + } + dir->first = 1; + dir->handle = FindFirstFileW(dirpath, &dir->finddata); + free(dirpath); + if(dir->handle == INVALID_HANDLE_VALUE) { + free(dir); + return NULL; + } + dir->ent = NULL; + return dir; +} + +SysDirEnt* sys_readdir(SYS_DIR dir) { + if(dir->ent) { + free(dir->ent->name); + free(dir->ent); + dir->ent = NULL; + } + if(dir->first) { + dir->first = 0; + } else { + if(FindNextFileW(dir->handle, &dir->finddata) == 0) { + return NULL; + } + } + + size_t namelen = wcslen(dir->finddata.cFileName); + + char *name = malloc((namelen+1)*4); + int nlen = WideCharToMultiByte( + CP_UTF8, + 0, + dir->finddata.cFileName, + -1, + name, + 256, + NULL, + NULL); + if(nlen > 0) { + name[nlen] = 0; + SysDirEnt *ent = malloc(sizeof(SysDirEnt)); + ent->name = name; + dir->ent = ent; + return ent; + } else { + return NULL; + } +} + +void sys_closedir(SYS_DIR dir) { + if(dir->ent) { + free(dir->ent->name); + free(dir->ent); + } + FindClose(dir->handle); + free(dir); +} + +FILE* sys_fopen(const char *path, const char *mode) { + wchar_t *fpath = path2winpath(path, FALSE, NULL); + wchar_t *fmode = path2winpath(mode, FALSE, NULL); + + FILE *file = (fpath && fmode) ? _wfopen(fpath, fmode) : NULL; + free(fpath); + free(fmode); + return file; +} + +int sys_stat(const char *path, SYS_STAT *s) { + wchar_t *fpath = path2winpath(path, FALSE, NULL); + if(!fpath) { + fprintf(stderr, "Cannot convert path \"%s\" to UTF16\n", path); + return -1; + } + int ret = _wstat64(fpath, s); + free(fpath); + return ret; +} + +int sys_lstat(const char *path, SYS_STAT *s) { + return sys_stat(path, s); // unsupported on windows +} + +int sys_islink(const char *path) { + // don't use symlinks on windows, because it is not really useful + // however, we interpret .lnk files as symlinks + int ret = 0; + + cxstring path_s = cx_str(path); + if(cx_strsuffix(path_s, CX_STR(".lnk"))) { + // looks like a .lnk file + // check content + IShellLink *sl; + HRESULT hres; + hres = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, (LPVOID*)&sl); + if(SUCCEEDED(hres)) { + IPersistFile *file; + hres = sl->lpVtbl->QueryInterface(sl, &IID_IPersistFile, (void**)&file); + if(!SUCCEEDED(hres)) { + sl->lpVtbl->Release(sl); + return ret; + } + + int newlen = 0; + wchar_t *wpath = path2winpath(path, 0, &newlen); + + hres = file->lpVtbl->Load(file, wpath, STGM_READ); + if(SUCCEEDED(hres)) { + ret = 1; + file->lpVtbl->Release(file); + } + free(wpath); + + sl->lpVtbl->Release(sl); + } + } + return ret; +} + +int sys_rename(const char *oldpath, const char *newpath) { + wchar_t *o = path2winpath(oldpath, FALSE, NULL); + wchar_t *n = path2winpath(newpath, FALSE, NULL); + if(!o || !n) { + return -1; + } + + struct __stat64 s; + if(!_wstat64(n, &s)) { + if(_wunlink(n)) { + fprintf(stderr, "sys_rename: cannot delete existing file: %ls\n", n); + } + } + + int ret = _wrename(o, n); + free(o); + free(n); + return ret; +} + +int sys_unlink(const char *path) { + wchar_t *wpath = path2winpath(path, FALSE, NULL); + if(!wpath) { + fprintf(stderr, "sys_unlink: cannot convert path\n"); + return -1; + } + int ret = _wunlink(wpath); + free(wpath); + return ret; +} + +int sys_mkdir(const char *path) { + wchar_t *wpath = path2winpath(path, FALSE, NULL); + if(!wpath) { + fprintf(stderr, "sys_mkdir: cannot convert path\n"); + return -1; + } + int ret = _wmkdir(wpath); + free(wpath); + return ret; +} + +char* sys_readlink(const char *path, SYS_STAT *s) { + char *ret_link = NULL; + + // create COM object for using the ShellLink interface + IShellLinkW *sl; + HRESULT hres = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, (LPVOID*)&sl); + if(!SUCCEEDED(hres)) { + return NULL; + } + + IPersistFile *file; + hres = sl->lpVtbl->QueryInterface(sl, &IID_IPersistFile, (void**)&file); + if(!SUCCEEDED(hres)) { + sl->lpVtbl->Release(sl); + return NULL; + } + + // load .lnk file + int newlen = 0; + wchar_t *wpath = path2winpath(path, 0, &newlen); + hres = file->lpVtbl->Load(file, wpath, STGM_READ); + if(SUCCEEDED(hres)) { + WCHAR link_path[MAX_PATH]; + memset(link_path, 0, MAX_PATH); + + hres = sl->lpVtbl->Resolve(sl, 0, SLR_NO_UI); + if(SUCCEEDED(hres)) { + hres = sl->lpVtbl->GetPath(sl, link_path, MAX_PATH, NULL, SLGP_SHORTPATH); + if(SUCCEEDED(hres)) { + ret_link = winpath2multibyte(link_path, wcslen(link_path)); + } + } + } + // cleanup + free(wpath); + file->lpVtbl->Release(file); + sl->lpVtbl->Release(sl); + + return ret_link; +} + +int sys_symlink(const char *target, const char *linkpath) { + // convert relative target to absolut path + char *link_parent = util_parent_path(linkpath); + char *target_unnormalized = util_concat_path(link_parent, target); + char *target_normalized = util_path_normalize(target_unnormalized); + + free(link_parent); + free(target_unnormalized); + + // convert to wchar_t* + int wtargetlen = 0; + wchar_t *wtarget = path2winpath(target_normalized, FALSE, &wtargetlen); + free(target_normalized); + if(!wtarget) { + return 1; + } + + int wlinkpathlen = 0; + wchar_t *wlinkpath = path2winpath(linkpath, FALSE, &wlinkpathlen); + if(!wlinkpath) { + free(wtarget); + return 1; + } + + int ret = 1; + + // create COM object for using the ShellLink interface + IShellLinkW *sl; + HRESULT hres = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, (LPVOID*)&sl); + if(SUCCEEDED(hres)) { + IPersistFile *file; + hres = sl->lpVtbl->QueryInterface(sl, &IID_IPersistFile, (void**)&file); + if(SUCCEEDED(hres)) { + // try to load the shortcut + file->lpVtbl->Load(file, wlinkpath, STGM_READ); // ignore error + + // set path + hres = sl->lpVtbl->SetPath(sl, wtarget); + if(SUCCEEDED(hres)) { + hres = file->lpVtbl->Save(file, wlinkpath, TRUE); + if(SUCCEEDED(hres)) { + // successfully created/modified shortcut + ret = 0; // ok + } + } + + file->lpVtbl->Release(file); + } + + sl->lpVtbl->Release(sl); + } + + free(wtarget); + free(wlinkpath); + + return ret; +} + +int sys_truncate(const char* path, off_t length) { + wchar_t* wpath = path2winpath(path, FALSE, NULL); + if (!wpath) { + fprintf(stderr, "sys_truncate: cannot convert path\n"); + return -1; + } + + FILE* file = _wfopen(wpath, L"wb"); + int ret = 1; + if (file) { + ret = _chsize(fileno(file), length); + fclose(file); + } + + free(wpath); + return ret; +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/application/system.h Mon Jan 29 10:41:00 2024 +0100 @@ -0,0 +1,115 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 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. + */ + +#ifndef DAV_SYSTEM_H +#define DAV_SYSTEM_H + +#include <stdio.h> +#include <time.h> +#include <sys/types.h> +#include <sys/stat.h> + + +#ifdef _WIN32 +#include <Windows.h> +#define mode_t unsigned int +#else +#include <dirent.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SysDirEnt { + char *name; +} SysDirEnt; + +#ifdef _WIN32 +struct WinDir { + int first; + HANDLE handle; + WIN32_FIND_DATAW finddata; + SysDirEnt *ent; +}; +#define SYS_DIR struct WinDir* +#define SYS_STAT struct __stat64 + +typedef int uid_t; +typedef int gid_t; + +#define SYS_ISLINK(path, mode) sys_islink(path) +#define SYS_LINK_EXT ".lnk" + +#else + +typedef struct SysDir { + DIR *dir; + SysDirEnt *ent; +} SysDir; + +#define SYS_DIR SysDir* +#define SYS_STAT struct stat + +#define SYS_ISLINK(p, s) S_ISLNK(s.st_mode) + +#endif + +typedef int(*stat_func)(const char*, SYS_STAT *); + +void sys_init(void); +void sys_uninit(void); + +void sys_freedirent(SysDirEnt *ent); +SYS_DIR sys_opendir(const char *path); +SysDirEnt* sys_readdir(SYS_DIR dir); +void sys_closedir(SYS_DIR dir); + +FILE* sys_fopen(const char *path, const char *mode); + +int sys_stat(const char *path, SYS_STAT *s); +int sys_lstat(const char *path, SYS_STAT *s); + +int sys_islink(const char *path); + +int sys_rename(const char *oldpath, const char *newpath); +int sys_unlink(const char *path); +int sys_mkdir(const char *path); + +char* sys_readlink(const char *path, SYS_STAT *s); + +int sys_symlink(const char *target, const char *linkpath); + +int sys_truncate(const char* path, off_t length); + +#ifdef __cplusplus +} +#endif + +#endif /* DAV_SYSTEM_H */ +
--- a/application/window.h Sun Jan 28 20:47:40 2024 +0100 +++ b/application/window.h Mon Jan 29 10:41:00 2024 +0100 @@ -26,6 +26,22 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#ifndef IDAV_WINDOW_H +#define IDAV_WINDOW_H + #include <ui/ui.h> +#ifdef __cplusplus +extern "C" { +#endif + UiObject* window_create(void); + + + +#ifdef __cplusplus +} +#endif + +#endif /* IDAV_WINDOW_H */ +
--- a/libidav/config.c Sun Jan 28 20:47:40 2024 +0100 +++ b/libidav/config.c Mon Jan 29 10:41:00 2024 +0100 @@ -392,6 +392,7 @@ DavCfgRepository* dav_repository_new(DavConfig *config) { DavCfgRepository *repo = cxMalloc(config->mp->allocator, sizeof(DavCfgRepository)); + memset(repo, 0, sizeof(DavCfgRepository)); repo->decrypt_name.value = false; repo->decrypt_content.value = true; repo->decrypt_properties.value = false;
--- a/make/vs/idav/idav.vcxproj Sun Jan 28 20:47:40 2024 +0100 +++ b/make/vs/idav/idav.vcxproj Mon Jan 29 10:41:00 2024 +0100 @@ -81,6 +81,9 @@ <OutDir>$(SolutionDir)..\..\build\vs\$(Platform)\$(Configuration)\</OutDir> <IntDir>..\..\..\build\vs\idav\$(Platform)\$(Configuration)\</IntDir> </PropertyGroup> + <PropertyGroup Label="Vcpkg"> + <VcpkgEnableManifest>true</VcpkgEnableManifest> + </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> <WarningLevel>Level3</WarningLevel> @@ -112,7 +115,7 @@ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ClCompile> <WarningLevel>Level3</WarningLevel> - <SDLCheck>true</SDLCheck> + <SDLCheck>false</SDLCheck> <PreprocessorDefinitions>_DEBUG;_CONSOLE;UI_WINUI;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ConformanceMode>true</ConformanceMode> <AdditionalIncludeDirectories>..\..\..\ucx;..\vcpkg_installed\x64-windows\x64-windows\include;..\..\..\ui\;..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> @@ -146,6 +149,9 @@ <ProjectReference Include="..\..\..\ui\winui\winui.vcxproj"> <Project>{59f97886-bf49-4b3f-9ef6-fa7a84f3ab56}</Project> </ProjectReference> + <ProjectReference Include="..\libidav\libidav.vcxproj"> + <Project>{c29c0378-6548-48e8-9426-31922515212a}</Project> + </ProjectReference> <ProjectReference Include="..\ucx\ucx.vcxproj"> <Project>{27da0164-3475-43e2-a1a4-a5d07d305749}</Project> </ProjectReference> @@ -155,11 +161,17 @@ </ItemGroup> <ItemGroup> <ClCompile Include="..\..\..\application\application.c" /> + <ClCompile Include="..\..\..\application\config.c" /> <ClCompile Include="..\..\..\application\main.c" /> + <ClCompile Include="..\..\..\application\pwd.c" /> + <ClCompile Include="..\..\..\application\system.c" /> <ClCompile Include="..\..\..\application\window.c" /> </ItemGroup> <ItemGroup> <ClInclude Include="..\..\..\application\application.h" /> + <ClInclude Include="..\..\..\application\config.h" /> + <ClInclude Include="..\..\..\application\pwd.h" /> + <ClInclude Include="..\..\..\application\system.h" /> <ClInclude Include="..\..\..\application\window.h" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
--- a/make/vs/idav/idav.vcxproj.filters Sun Jan 28 20:47:40 2024 +0100 +++ b/make/vs/idav/idav.vcxproj.filters Mon Jan 29 10:41:00 2024 +0100 @@ -28,6 +28,15 @@ <ClCompile Include="..\..\..\application\window.c"> <Filter>src</Filter> </ClCompile> + <ClCompile Include="..\..\..\application\config.c"> + <Filter>src</Filter> + </ClCompile> + <ClCompile Include="..\..\..\application\system.c"> + <Filter>src</Filter> + </ClCompile> + <ClCompile Include="..\..\..\application\pwd.c"> + <Filter>src</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="..\..\..\application\application.h"> @@ -36,5 +45,14 @@ <ClInclude Include="..\..\..\application\window.h"> <Filter>src</Filter> </ClInclude> + <ClInclude Include="..\..\..\application\config.h"> + <Filter>src</Filter> + </ClInclude> + <ClInclude Include="..\..\..\application\system.h"> + <Filter>src</Filter> + </ClInclude> + <ClInclude Include="..\..\..\application\pwd.h"> + <Filter>src</Filter> + </ClInclude> </ItemGroup> </Project> \ No newline at end of file