#include "finfo.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <limits.h>
#include <cx/string.h>
#include <cx/list.h>
#include <cx/array_list.h>
#include <libidav/crypto.h>
#include <libidav/utils.h>
#ifndef _WIN32
#include <unistd.h>
#endif
#include "libxattr.h"
uint32_t parse_finfo_settings(
const char *str,
char **error) {
cxstring s = cx_str(str);
if(!cx_strcmp(s,
CX_STR(
"*")) || !cx_strcmp(s,
CX_STR(
"a")) || !cx_strcmp(s,
CX_STR(
"all"))) {
return FINFO_MTIME|
FINFO_OWNER|
FINFO_MODE|
FINFO_XATTR;
}
CxStrtokCtx fs = cx_strtok(s,
CX_STR(
","),
INT_MAX);
cxstring f;
uint32_t finfo =
0;
char *err =
NULL;
while(cx_strtok_next(&fs, &f)) {
if(!cx_strcasecmp(f,
CX_STR(
"mtime"))) {
finfo |=
FINFO_MTIME;
}
else if(!cx_strcasecmp(f,
CX_STR(
"owner"))) {
finfo |=
FINFO_OWNER;
}
else if(!cx_strcasecmp(f,
CX_STR(
"mode"))) {
finfo |=
FINFO_MODE;
}
else if(!cx_strcasecmp(f,
CX_STR(
"xattr"))) {
finfo |=
FINFO_XATTR;
}
else if(error && !err) {
err = cx_strdup(f).ptr;
continue;
}
}
return err ?
0 : finfo;
}
int resource_set_finfo(
const char *path, DavResource *res,
uint32_t finfo) {
if(!path || finfo ==
0) {
return 0;
}
struct stat s;
if(stat(path, &s)) {
fprintf(stderr,
"failed to stat: %s\n", path);
return 1;
}
return resource_set_finfo_s(&s, res, finfo);
}
int resource_set_finfo_s(
struct stat *s, DavResource *res,
uint32_t finfo) {
if(finfo ==
0) {
return 0;
}
DavXmlNode *content =
NULL;
DavXmlNode *last =
NULL;
if((finfo &
FINFO_MTIME) ==
FINFO_MTIME) {
char str[
32];
struct tm *date = gmtime(&s->st_mtime);
strftime(str,
32,
"%a, %d %b %Y %H:%M:%S GMT", date);
DavXmlNode *mtime = dav_text_element(res->session,
DAV_PROPS_NS,
"mtime", str);
content = mtime;
last = mtime;
}
#ifndef _WIN32
if((finfo &
FINFO_OWNER) ==
FINFO_OWNER) {
}
if((finfo &
FINFO_MODE) ==
FINFO_MODE) {
mode_t mode = s->st_mode &
07777;
char str[
32];
snprintf(str,
32,
"%o", (
int)mode);
DavXmlNode *xmode = dav_text_element(res->session,
DAV_PROPS_NS,
"mode", str);
if(last) {
last->next = xmode;
}
else {
content = xmode;
}
last = xmode;
}
#endif
dav_set_property_ns(res,
DAV_PROPS_NS,
"finfo", content);;
return 0;
}
static void* array_realloc(
void *array,
size_t capacity,
size_t elem_size,
struct cx_array_reallocator_s *alloc)
{
return realloc(array, capacity * elem_size);
}
XAttributes* xml_get_attributes(DavXmlNode *xml) {
XAttributes *attributes = calloc(
1,
sizeof(XAttributes));
size_t x_names_size =
0;
size_t x_names_alloc =
8;
size_t x_values_size =
0;
size_t x_values_alloc =
8;
attributes->names = calloc(x_names_alloc,
sizeof(
char*));
attributes->values = calloc(x_values_alloc,
sizeof(cxmutstr));
struct cx_array_reallocator_s re = { .realloc = array_realloc };
size_t count =
0;
char *hash =
NULL;
DavXmlNode *node = xml;
for(;node;node=node->next) {
if(node->type ==
DAV_XML_ELEMENT) {
if(!strcmp(node->name,
"hash")) {
hash = dav_xml_getstring(node->children);
}
else if(!strcmp(node->name,
"xattr")) {
char *xattr_name = dav_xml_get_attr(node,
"name");
if(xattr_name) {
char *xname = strdup(xattr_name);
cx_array_copy(
(
void**)&attributes->names,
&x_names_size,
&x_names_alloc,
count,
&xname,
sizeof(
void*),
1,
&re);
char *text = dav_xml_getstring(node->children);
if(!text) {
text =
"";
}
int len =
0;
char *val = util_base64decode_len(text, &len);
cxmutstr value;
value.ptr = val;
value.length = len;
cx_array_copy(
(
void**)&attributes->values,
&x_values_size,
&x_values_alloc,
count,
&value,
sizeof(cxmutstr),
1,
&re);
count++;
}
}
}
}
if(count ==
0) {
free(attributes->names);
free(attributes->values);
free(attributes);
return NULL;
}
attributes->hash = hash ? strdup(hash) :
NULL;
attributes->nattr = count;
return attributes;
}
XAttributes* file_get_attributes(
const char *path,
xattr_filter_func filter,
void *filterdata)
{
ssize_t nelm =
0;
char **attributes = xattr_list(path, &nelm);
if(nelm <=
0) {
return NULL;
}
XAttributes *xattr = malloc(
sizeof(XAttributes));
xattr->nattr =
0;
xattr->names = calloc(nelm,
sizeof(
char*));
xattr->values = calloc(nelm,
sizeof(cxmutstr));
DAV_SHA_CTX *sha256 = dav_hash_init();
size_t nattr =
0;
for(
int i=
0;i<nelm;i++) {
if(filter) {
if(!filter(attributes[i], filterdata)) {
continue;
}
}
ssize_t valuelen =
0;
char *value = xattr_get(path, attributes[i], &valuelen);
if(valuelen >=
0) {
dav_hash_update(sha256, attributes[i], strlen(attributes[i]));
dav_hash_update(sha256, value, valuelen);
xattr->names[nattr] = attributes[i];
cxmutstr v;
v.ptr = value;
v.length = valuelen;
xattr->values[nattr] = v;
nattr++;
}
else {
free(attributes[i]);
}
}
xattr->nattr = nattr;
unsigned char hash[
DAV_SHA256_DIGEST_LENGTH];
dav_hash_final(sha256, hash);
xattr->hash = util_hexstr(hash,
DAV_SHA256_DIGEST_LENGTH);
free(attributes);
if(nattr >
0) {
return xattr;
}
else {
xattributes_free(xattr);
return NULL;
}
}
int resource_set_xattr(DavResource *res, XAttributes *xattr) {
if(!xattr || xattr->nattr ==
0) {
return 0;
}
DavXmlNode *content = dav_xml_createnode_with_text(
DAV_PROPS_NS,
"hash", xattr->hash);
DavXmlNode *last = content;
for(
int i=
0;i<xattr->nattr;i++) {
DavXmlNode *attr = dav_xml_createnode(
DAV_PROPS_NS,
"xattr");
dav_xml_add_attr(attr,
"name", xattr->names[i]);
last->next = attr;
last = attr;
cxmutstr value = xattr->values[i];
if(value.length >
0) {
char *encval = util_base64encode(value.ptr, value.length);
attr->children = dav_xml_createtextnode(encval);
free(encval);
}
}
dav_set_property_ns(res,
DAV_PROPS_NS,
"xattributes", content);
return 0;
}
void xattributes_free(XAttributes *xattr) {
free(xattr->hash);
for(
int i=
0;i<xattr->nattr;i++) {
free(xattr->names[i]);
free(xattr->values[i].ptr);
}
free(xattr);
}
char* get_xattr_hash(DavXmlNode *finfo) {
DavXmlNode *node = finfo;
while(node) {
if(node->type ==
DAV_XML_ELEMENT && !strcmp(node->name,
"hash")) {
return dav_xml_getstring(node->children);
}
node = node->next;
}
return NULL;
}
void finfo_get_values(DavXmlNode *xml, FileInfo *outval) {
memset(outval,
0,
sizeof(FileInfo));
DavXmlNode *node = xml;
while(node) {
if(node->type ==
DAV_XML_ELEMENT) {
if(!strcmp(node->name,
"mtime")) {
char *mtime = dav_xml_getstring(node->children);
if(mtime) {
outval->last_modified = util_parse_lastmodified(mtime);
outval->date_set =
TRUE;
}
}
else if(!strcmp(node->name,
"mode")) {
char *mode_str = dav_xml_getstring(node->children);
if(mode_str) {
char *end;
errno =
0;
long int mode = strtol(mode_str, &end,
8);
if(errno ==
0) {
mode &=
07777;
outval->mode = mode;
outval->mode_set =
TRUE;
}
}
}
}
node = node->next;
}
}