Synchronet now requires the libarchive development package (e.g. libarchive-dev on Debian-based Linux distros, libarchive.org for more info) to build successfully.

Commits (3)
......@@ -700,111 +700,6 @@ int archive_type(const char* archive, char* str, size_t size)
return result;
}
str_list_t list_archive_contents(const char* filename, const char* pattern, bool hash, bool sort, char* error, size_t maxerrlen)
{
int result;
struct archive *ar;
struct archive_entry *entry;
if((ar = archive_read_new()) == NULL) {
safe_snprintf(error, maxerrlen, "archive_read_new() returned NULL");
return NULL;
}
archive_read_support_filter_all(ar);
archive_read_support_format_all(ar);
if((result = archive_read_open_filename(ar, filename, 10240)) != ARCHIVE_OK) {
safe_snprintf(error, maxerrlen, "archive_read_open_filename() returned %d: %s"
,result, archive_error_string(ar));
archive_read_free(ar);
return NULL;
}
str_list_t list = strListInit();
if(list == NULL) {
safe_snprintf(error, maxerrlen, "strListInit() returned NULL");
archive_read_free(ar);
return NULL;
}
while(1) {
result = archive_read_next_header(ar, &entry);
if(result != ARCHIVE_OK) {
if(result != ARCHIVE_EOF) {
safe_snprintf(error, maxerrlen, "archive_read_next_header() returned %d: %s"
,result, archive_error_string(ar));
archive_read_free(ar);
strListFree(&list);
return NULL;
}
break;
}
const char* pathname = archive_entry_pathname(entry);
if(pathname == NULL)
continue;
if(pattern != NULL && *pattern && !wildmatch(pathname, pattern, /* path: */false, /* case-sensitive: */false))
continue;
const char* type;
switch(archive_entry_filetype(entry)) {
case AE_IFREG:
type = "file";
break;
case AE_IFLNK:
type = "link";
break;
case AE_IFDIR:
type = "directory";
break;
default:
continue;
}
strListAppendFormat(&list, "[%s]", pathname);
strListAppendFormat(&list, "type=%s", type);
strListAppendFormat(&list, "size=%"PRId64, archive_entry_size(entry));
strListAppendFormat(&list, "time=%"PRId64,archive_entry_mtime(entry));
strListAppendFormat(&list, "mode=%d", archive_entry_mode(entry));
strListAppendFormat(&list, "format=%s", archive_format_name(ar));
strListAppendFormat(&list, "compression=%s", archive_filter_name(ar, 0));
if(hash && archive_entry_filetype(entry) == AE_IFREG) {
MD5 md5_ctx;
SHA1_CTX sha1_ctx;
uint8_t md5[MD5_DIGEST_SIZE];
uint8_t sha1[SHA1_DIGEST_SIZE];
uint32_t crc32 = 0;
MD5_open(&md5_ctx);
SHA1Init(&sha1_ctx);
const void *buff;
size_t size;
la_int64_t offset;
for(;;) {
result = archive_read_data_block(ar, &buff, &size, &offset);
if(result != ARCHIVE_OK)
break;
crc32 = crc32i(~crc32, buff, size);
MD5_digest(&md5_ctx, buff, size);
SHA1Update(&sha1_ctx, buff, size);
}
MD5_close(&md5_ctx, md5);
SHA1Final(&sha1_ctx, sha1);
strListAppendFormat(&list, "crc32=0x%lx", crc32);
char hex[128];
strListAppendFormat(&list, "md5=%s", MD5_hex(hex, md5));
strListAppendFormat(&list, "sha1=%s", SHA1_hex(hex, sha1));
}
}
archive_read_free(ar);
if(sort)
iniSortSections(&list, /* sort_keys: */FALSE);
return list;
}
str_list_t directory(const char* path)
{
int flags = GLOB_MARK;
......
......@@ -67,7 +67,6 @@ DLLEXPORT int file_sauce_hfields(file_t*, struct sauce_charinfo*);
DLLEXPORT str_list_t directory(const char* path);
DLLEXPORT long create_archive(const char* archive, const char* format
,bool with_path, str_list_t file_list, char* error, size_t maxerrlen);
DLLEXPORT str_list_t list_archive_contents(const char* archive, const char* pattern, bool hash, bool sort, char* error, size_t maxerrlen);
DLLEXPORT char* cmdstr(scfg_t*, user_t*, const char* instr, const char* fpath, const char* fspec, char* cmd, size_t);
DLLEXPORT long extract_files_from_archive(const char* archive, const char* outdir, const char* allowed_filename_chars
,bool with_path, long max_files, str_list_t file_list, char* error, size_t);
......
......@@ -164,51 +164,6 @@ js_dump_file(JSContext *cx, uintN argc, jsval *arglist)
return JS_TRUE;
}
static bool
set_content_properties(JSContext *cx, JSObject* obj, const char* fname, str_list_t ini)
{
const char* val;
jsval js_val;
JSString* js_str;
const char* key;
const uintN flags = JSPROP_ENUMERATE | JSPROP_READONLY;
if(fname == NULL
|| (js_str = JS_NewStringCopyZ(cx, fname)) == NULL
|| !JS_DefineProperty(cx, obj, "name", STRING_TO_JSVAL(js_str), NULL, NULL, flags))
return false;
const char* key_list[] = { "type", "time", "format", "compression", "md5", "sha1", NULL };
for(size_t i = 0; key_list[i] != NULL; i++) {
key = key_list[i];
if((val = iniGetString(ini, NULL, key, NULL, NULL)) != NULL
&& ((js_str = JS_NewStringCopyZ(cx, val)) == NULL
|| !JS_DefineProperty(cx, obj, key, STRING_TO_JSVAL(js_str), NULL, NULL, flags)))
return false;
}
key = "size";
js_val = DOUBLE_TO_JSVAL((jsdouble)iniGetBytes(ini, NULL, key, 1, -1));
if(!JS_DefineProperty(cx, obj, key, js_val, NULL, NULL, flags))
return false;
key = "mode";
if(iniKeyExists(ini, NULL, key)) {
js_val = INT_TO_JSVAL(iniGetInteger(ini, NULL, key, 0));
if(!JS_DefineProperty(cx, obj, key, js_val, NULL, NULL, flags))
return false;
}
key = "crc32";
if(iniKeyExists(ini, NULL, key)) {
js_val = UINT_TO_JSVAL(iniGetLongInt(ini, NULL, key, 0));
if(!JS_DefineProperty(cx, obj, key, js_val, NULL, NULL, flags))
return false;
}
return true;
}
static bool
set_file_properties(JSContext *cx, JSObject* obj, file_t* f, enum file_detail detail)
{
......@@ -336,31 +291,10 @@ set_file_properties(JSContext *cx, JSObject* obj, file_t* f, enum file_detail de
return false;
}
if(detail >= file_detail_content) {
JSObject* array;
if((array = JS_NewArrayObject(cx, 0, NULL)) == NULL) {
JS_ReportError(cx, "array allocation failure, line %d", __LINE__);
return false;
}
str_list_t ini = strListSplit(NULL, f->content, "\r\n");
str_list_t file_list = iniGetSectionList(ini, NULL);
if(file_list != NULL) {
for(size_t i = 0; file_list[i] != NULL; i++) {
JSObject* fobj;
if((fobj = JS_NewObject(cx, NULL, NULL, array)) == NULL) {
JS_ReportError(cx, "object allocation failure, line %d", __LINE__);
return false;
}
str_list_t section = iniGetSection(ini, file_list[i]);
set_content_properties(cx, fobj, file_list[i], section);
JS_DefineElement(cx, array, i, OBJECT_TO_JSVAL(fobj), NULL, NULL, JSPROP_ENUMERATE);
iniFreeStringList(section);
}
strListFree(&file_list);
}
strListFree(&ini);
JS_DefineProperty(cx, obj, "content", OBJECT_TO_JSVAL(array), NULL, NULL, JSPROP_ENUMERATE);
}
if(((f->content != NULL && *f->content != '\0') || detail >= file_detail_content)
&& ((js_str = JS_NewStringCopyZ(cx, f->content)) == NULL
|| !JS_DefineProperty(cx, obj, "metadata", STRING_TO_JSVAL(js_str), NULL, NULL, flags)))
return false;
return true;
}
......@@ -434,7 +368,7 @@ parse_file_index_properties(JSContext *cx, JSObject* obj, fileidxrec_t* idx)
}
static int
parse_file_properties(JSContext *cx, JSObject* obj, file_t* file, char** extdesc)
parse_file_properties(JSContext *cx, JSObject* obj, file_t* file, char** extdesc, char** metadata)
{
char* cp = NULL;
size_t cp_sz = 0;
......@@ -602,6 +536,18 @@ parse_file_properties(JSContext *cx, JSObject* obj, file_t* file, char** extdesc
}
truncsp(*extdesc);
}
prop_name = "metadata";
if(metadata != NULL && JS_GetProperty(cx, obj, prop_name, &val) && !JSVAL_NULL_OR_VOID(val)) {
FREE_AND_NULL(*metadata);
JSVALUE_TO_MSTRING(cx, val, *metadata, NULL);
HANDLE_PENDING(cx, *metadata);
if(*metadata == NULL) {
free(cp);
JS_ReportError(cx, "Invalid '%s' string in file object", prop_name);
return SMB_ERR_MEM;
}
truncsp(*metadata);
}
prop_name = "tags";
if(JS_GetProperty(cx, obj, prop_name, &val) && !JSVAL_NULL_OR_VOID(val)) {
JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
......@@ -1183,6 +1129,7 @@ js_add_file(JSContext *cx, uintN argc, jsval *arglist)
jsval* argv = JS_ARGV(cx, arglist);
private_t* p;
char* extdesc = NULL;
char* metadata = NULL;
file_t file;
client_t* client = NULL;
bool use_diz_always = false;
......@@ -1207,7 +1154,7 @@ js_add_file(JSContext *cx, uintN argc, jsval *arglist)
uintN argn = 0;
if(argn < argc && JSVAL_IS_OBJECT(argv[argn])) {
p->smb_result = parse_file_properties(cx, JSVAL_TO_OBJECT(argv[argn]), &file, &extdesc);
p->smb_result = parse_file_properties(cx, JSVAL_TO_OBJECT(argv[argn]), &file, &extdesc, &metadata);
if(p->smb_result != SMB_SUCCESS)
return JS_TRUE;
argn++;
......@@ -1236,7 +1183,7 @@ js_add_file(JSContext *cx, uintN argc, jsval *arglist)
getfilepath(scfg, &file, fpath);
if(file.from_ip == NULL)
file_client_hfields(&file, client);
p->smb_result = smb_addfile(&p->smb, &file, SMB_SELFPACK, extdesc, /* contents: */NULL, fpath);
p->smb_result = smb_addfile(&p->smb, &file, SMB_SELFPACK, extdesc, metadata, fpath);
JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(p->smb_result == SMB_SUCCESS));
}
JS_RESUMEREQUEST(cx, rc);
......@@ -1297,10 +1244,11 @@ js_update_file(JSContext *cx, uintN argc, jsval *arglist)
JSBool result = JS_TRUE;
char* extdesc = NULL;
char* metadata = NULL;
rc=JS_SUSPENDREQUEST(cx);
if(filename != NULL && fileobj != NULL
&& (p->smb_result = smb_loadfile(&p->smb, filename, &file, file_detail_extdesc)) == SMB_SUCCESS) {
p->smb_result = parse_file_properties(cx, fileobj, &file, &extdesc);
p->smb_result = parse_file_properties(cx, fileobj, &file, &extdesc, &metadata);
if((extdesc == NULL || use_diz_always == true)
&& file.dir < scfg->total_dirs
&& (scfg->dir[file.dir]->misc & DIR_DIZ)) {
......@@ -1324,7 +1272,7 @@ js_update_file(JSContext *cx, uintN argc, jsval *arglist)
p->smb_result = smb_putfile(&p->smb, &file);
else {
if((p->smb_result = smb_removefile(&p->smb, &file)) == SMB_SUCCESS) {
p->smb_result = smb_addfile(&p->smb, &file, SMB_SELFPACK, extdesc, /* contents: */NULL, newfname);
p->smb_result = smb_addfile(&p->smb, &file, SMB_SELFPACK, extdesc, metadata, newfname);
}
}
}
......@@ -1670,7 +1618,7 @@ static jsSyncMethodSpec js_filebase_functions[] = {
"<tr><td align=top><tt>crc32</tt><td>32-bit CRC of file contents"
"<tr><td align=top><tt>md5</tt><td>128-bit MD5 digest of file contents (hexadecimal)"
"<tr><td align=top><tt>sha1</tt><td>160-bit SHA-1 digest of file contents (hexadecimal)"
"<tr><td align=top><tt>content</tt><td>Array of archived file details (<tt>name, size, time, crc32, md5<tt>, etc.)"
"<tr><td align=top><tt>metadata</tt><td>File metadata in JSON format"
"</table>"
)
,31900
......@@ -1737,7 +1685,7 @@ static jsSyncMethodSpec js_filebase_functions[] = {
},
{"dump", js_dump_file, 1, JSTYPE_ARRAY
,JSDOCSTR("filename")
,JSDOCSTR("dump file metadata to an array of strings for diagnostic uses")
,JSDOCSTR("dump file header fields to an array of strings for diagnostic uses")
,31900
},
{"format_name", js_format_file_name,1, JSTYPE_STRING
......@@ -1853,9 +1801,9 @@ js_filebase_constructor(JSContext *cx, uintN argc, jsval *arglist)
#ifdef BUILD_JSDOCS
static char* filebase_detail_prop_desc[] = {
"Include indexed-filenames only",
"Normal level of file detail (e.g. full filenames, minimal meta data)",
"Normal level of file detail (e.g. full filenames, minimal metadata)",
"Normal level of file detail plus extended descriptions",
"Normal level of file detail plus extended descriptions and archived contents",
"Normal level of file detail plus extended descriptions and JSON-metadata",
"Maximum file detail, include undefined/null property values",
NULL
};
......@@ -1896,7 +1844,7 @@ JSObject* js_CreateFileBaseClass(JSContext* cx, JSObject* parent, scfg_t* cfg)
, JSPROP_PERMANENT|JSPROP_ENUMERATE|JSPROP_READONLY);
JS_DefineProperty(cx, detail, "EXTENDED", INT_TO_JSVAL(file_detail_extdesc), NULL, NULL
, JSPROP_PERMANENT|JSPROP_ENUMERATE|JSPROP_READONLY);
JS_DefineProperty(cx, detail, "CONTENTS", INT_TO_JSVAL(file_detail_content), NULL, NULL
JS_DefineProperty(cx, detail, "METADATA", INT_TO_JSVAL(file_detail_content), NULL, NULL
, JSPROP_PERMANENT|JSPROP_ENUMERATE|JSPROP_READONLY);
JS_DefineProperty(cx, detail, "MAX", INT_TO_JSVAL(file_detail_content + 1), NULL, NULL
, JSPROP_PERMANENT|JSPROP_ENUMERATE|JSPROP_READONLY);
......