#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 "config.h"
#include "system.h"
#include <libidav/utils.h>
#include <libidav/config.h>
#include <libidav/pwdstore.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
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;
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: {
davconfig = dav_config_new(
NULL);
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;
}
if(dav_config_register_namespaces(davconfig, ctx)) {
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;
}
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;
}
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;
}
typedef struct CredLocation {
char *id;
char *location;
} CredLocation;
static int cmp_url_cred_entry(CredLocation *e1, CredLocation *e2,
void *n) {
return strcmp(e2->location, e1->location);
}
static void free_cred_location(CredLocation *c) {
free(c->location);
free(c);
}
int get_stored_credentials(
char *credid,
char **user,
char **password) {
if(!credid) {
return 0;
}
PwdStore *secrets = get_pwdstore();
if(!secrets) {
fprintf(stderr,
"Error: no secrets store available\n");
return 0;
}
if(pwdstore_has_id(secrets, credid)) {
if(!secrets->isdecrypted) {
if(pwdstore_decrypt_secrets(secrets)) {
return 0;
}
}
PwdEntry *s_cred = pwdstore_get(secrets, credid);
if(s_cred) {
*user = s_cred->user;
*password = s_cred->password;
return 1;
}
}
else {
fprintf(stderr,
"Error: credentials id ''%s'' not found\n", credid);
}
return 0;
}
int get_location_credentials(DavCfgRepository *repo,
const char *path,
char **user,
char **password) {
PwdStore *secrets = get_pwdstore();
if(!secrets) {
return 0;
}
CxList *locations = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)cmp_url_cred_entry,
CX_STORE_POINTERS);
cxDefineDestructor(locations, free_cred_location);
CxIterator i = cxListIterator(secrets->locations);
cx_foreach(PwdIndexEntry*, e, i) {
CxIterator entry_iter = cxListIterator(e->locations);
cx_foreach(
char *, loc, entry_iter) {
cxmutstr rpath;
DavCfgRepository *r = dav_config_url2repo_s(davconfig, cx_str(loc), &rpath);
CredLocation *urlentry = calloc(
1,
sizeof(CredLocation));
urlentry->id = e->id;
urlentry->location = util_concat_path_s(cx_strcast(r->url.value), cx_strcast(rpath)).ptr;
cxListAdd(locations, urlentry);
free(rpath.ptr);
}
}
cxListSort(locations);
cxmutstr req_url_proto = util_concat_path_s(cx_strcast(repo->url.value), cx_str(path));
cxstring req_url = cx_strcast(req_url_proto);
if(cx_strprefix(req_url,
CX_STR(
"http://"))) {
req_url = cx_strsubs(req_url,
7);
}
else if(cx_strprefix(req_url,
CX_STR(
"https://"))) {
req_url = cx_strsubs(req_url,
8);
}
char *id =
NULL;
int ret =
0;
i = cxListIterator(locations);
cx_foreach(CredLocation*, cred, i) {
cxstring cred_url = cx_str(cred->location);
if(cx_strprefix(cred_url,
CX_STR(
"http://"))) {
cred_url = cx_strsubs(cred_url,
7);
}
else if(cx_strprefix(cred_url,
CX_STR(
"https://"))) {
cred_url = cx_strsubs(cred_url,
8);
}
if(cx_strprefix(req_url, cred_url)) {
id = cred->id;
break;
}
}
if(id && (secrets->isdecrypted || !pwdstore_decrypt_secrets(secrets))) {
PwdEntry *cred = pwdstore_get(secrets, id);
if(cred) {
*user = cred->user;
*password = cred->password;
ret =
1;
}
}
free(req_url_proto.ptr);
cxListDestroy(locations);
return ret;
}