diff --git a/src/sbbs3/js_archive.c b/src/sbbs3/js_archive.c index fe4d14ff4a70beb3729db474643830285f166cc3..67f22edd9c5deb586f8bf93235ff7e62135b207b 100644 --- a/src/sbbs3/js_archive.c +++ b/src/sbbs3/js_archive.c @@ -29,11 +29,6 @@ #include <stdbool.h> -typedef struct -{ - char name[MAX_PATH + 1]; -} archive_private_t; - JSClass js_archive_class; static JSBool @@ -48,9 +43,9 @@ js_create(JSContext *cx, uintN argc, jsval *arglist) char error[256] = ""; jsval val; jsrefcount rc; - archive_private_t* p; + const char* filename; - if((p = (archive_private_t*)js_GetClassPrivate(cx, obj, &js_archive_class)) == NULL) + if((filename = js_GetClassPrivate(cx, obj, &js_archive_class)) == NULL) return JS_FALSE; if(!js_argc(cx, argc, 1)) @@ -89,12 +84,12 @@ js_create(JSContext *cx, uintN argc, jsval *arglist) } char* fext; - if(*format == '\0' && (fext = getfext(p->name)) != NULL) + if(*format == '\0' && (fext = getfext(filename)) != NULL) SAFECOPY(format, fext + 1); if(*format == '\0') SAFECOPY(format, "zip"); rc = JS_SUSPENDREQUEST(cx); - long file_count = create_archive(p->name, format, with_path, file_list, error, sizeof(error)); + long file_count = create_archive(filename, format, with_path, file_list, error, sizeof(error)); strListFree(&file_list); JS_RESUMEREQUEST(cx, rc); if(file_count < 0) { @@ -117,10 +112,10 @@ js_extract(JSContext *cx, uintN argc, jsval *arglist) int32 max_files = 0; char error[256] = ""; jsrefcount rc; - archive_private_t* p; + const char* filename; JS_SET_RVAL(cx, arglist, JSVAL_VOID); - if((p = (archive_private_t*)js_GetClassPrivate(cx, obj, &js_archive_class)) == NULL) + if((filename = js_GetClassPrivate(cx, obj, &js_archive_class)) == NULL) return JS_FALSE; if(!js_argc(cx, argc, 1)) @@ -158,7 +153,7 @@ js_extract(JSContext *cx, uintN argc, jsval *arglist) } rc = JS_SUSPENDREQUEST(cx); - long extracted = extract_files_from_archive(p->name, outdir, allowed_filename_chars + long extracted = extract_files_from_archive(filename, outdir, allowed_filename_chars ,with_path, (ulong)max_files, file_list, error, sizeof(error)); strListFree(&file_list); free(outdir); @@ -180,13 +175,13 @@ js_archive_type(JSContext *cx, JSObject *obj, jsid id, jsval *vp) { char type[256] = ""; jsrefcount rc; - archive_private_t* p; + const char* filename; - if((p = (archive_private_t*)js_GetClassPrivate(cx, obj, &js_archive_class)) == NULL) + if((filename = js_GetClassPrivate(cx, obj, &js_archive_class)) == NULL) return JS_FALSE; rc = JS_SUSPENDREQUEST(cx); - int result = archive_type(p->name, type, sizeof(type)); + int result = archive_type(filename, type, sizeof(type)); JS_RESUMEREQUEST(cx, rc); if(result >= 0) { JSString* js_str = JS_NewStringCopyZ(cx, type); @@ -196,29 +191,37 @@ js_archive_type(JSContext *cx, JSObject *obj, jsid id, jsval *vp) return JS_TRUE; } -/* getter */ static JSBool -js_archive_directory(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +js_directory(JSContext *cx, uintN argc, jsval *arglist) { + jsval *argv = JS_ARGV(cx, arglist); + JSObject *obj = JS_THIS_OBJECT(cx, arglist); jsval val; jsrefcount rc; JSObject* array; JSString* js_str; struct archive *ar; struct archive_entry *entry; + bool hash = false; int result; - archive_private_t* p; + const char* filename; - if((p = (archive_private_t*)js_GetClassPrivate(cx, obj, &js_archive_class)) == NULL) + if((filename = js_GetClassPrivate(cx, obj, &js_archive_class)) == NULL) return JS_FALSE; + uintN argn = 0; + if(argc > argn && JSVAL_IS_BOOLEAN(argv[argn])) { + hash = JSVAL_TO_BOOLEAN(argv[argn]); + argn++; + } + if((ar = archive_read_new()) == NULL) { JS_ReportError(cx, "archive_read_new() returned NULL"); return JS_FALSE; } archive_read_support_filter_all(ar); archive_read_support_format_all(ar); - if((result = archive_read_open_filename(ar, p->name, 10240)) != ARCHIVE_OK) { + if((result = archive_read_open_filename(ar, filename, 10240)) != ARCHIVE_OK) { JS_ReportError(cx, "archive_read_open_filename() returned %d: %s" ,result, archive_error_string(ar)); archive_read_free(ar); @@ -356,28 +359,99 @@ js_archive_directory(JSContext *cx, JSObject *obj, jsid id, jsval *vp) JS_SetProperty(cx, obj, "fflags", &val); } + 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]; + uint16_t crc16 = 0; + 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); + crc16 = icrc16(crc16, buff, size); + MD5_digest(&md5_ctx, buff, size); + SHA1Update(&sha1_ctx, buff, size); + } + MD5_close(&md5_ctx, md5); + SHA1Final(&sha1_ctx, sha1); + val = UINT_TO_JSVAL(crc16); + if(!JS_SetProperty(cx, obj, "crc16", &val)) + break; + val = UINT_TO_JSVAL(crc32); + if(!JS_SetProperty(cx, obj, "crc32", &val)) + break; + char hex[128]; + if((js_str = JS_NewStringCopyZ(cx, MD5_hex(hex, md5))) == NULL) + break; + val = STRING_TO_JSVAL(js_str); + if(!JS_SetProperty(cx, obj, "md5", &val)) + break; + if((js_str = JS_NewStringCopyZ(cx, SHA1_hex(hex, sha1))) == NULL) + break; + val = STRING_TO_JSVAL(js_str); + if(!JS_SetProperty(cx, obj, "sha1", &val)) + break; + } + val = OBJECT_TO_JSVAL(obj); if(!JS_SetElement(cx, array, len++, &val)) break; } archive_read_free(ar); JS_RESUMEREQUEST(cx, rc); - *vp = OBJECT_TO_JSVAL(array); + JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(array)); return retval; } static jsSyncMethodSpec js_archive_functions[] = { { "create", js_create, 1, JSTYPE_NUMBER - ,JSDOCSTR("[string format] [,boolean with_path = <tt>false</tt>] [,array file_list]") + ,JSDOCSTR("[string format] [,boolean with_path = false] [,array file_list]") ,JSDOCSTR("create an archive of the specified format, returns the number of files archived, will throw exception upon error") ,31900 }, { "extract", js_extract, 1, JSTYPE_NUMBER - ,JSDOCSTR("output_directory [,boolean with_path = <tt>false</tt>] [,number max_files = 0] [,string file/pattern [...]]") + ,JSDOCSTR("output_directory [,boolean with_path = false] [,number max_files = 0] [,string file/pattern [...]]") ,JSDOCSTR("extract files from an archive to specified output directory, returns the number of files extracted, will throw exception upon error") ,31900 }, + { "directory", js_directory, 1, JSTYPE_ARRAY + ,JSDOCSTR("[,boolean hash = false]") + ,JSDOCSTR("return directory listing of archive as an array of objects<br>" + "archived object properties:<br>" + "<ul>" + "<li>string type - 'file', 'link', or 'directory'" + "<li>string name - file path/name" + "<li>string path - source path" + "<li>string symlink" + "<li>string hardlink" + "<li>number size - in bytes" + "<li>number time - in time_t format" + "<li>number mode" + "<li>string user" + "<li>string group" + "<li>string format" + "<li>string compression" + "<li>string fflags" + "<li>number crc16 - 16-bit CRC, when hash is true and type is file" + "<li>number crc32 - 32-bit CRC, when hash is true and type is file" + "<li>string md5 - hexadecimal MD-5 sum, when hash is true and type is file" + "<li>string sha1 - hexadecimal SHA-1 sum, when hash is true and type is file" + "</ul>" + "when <tt>hash</tt> is <tt>true</tt>, calculates and returns hash/digest values of files in stored archive") + ,31900 + }, {0} }; @@ -387,7 +461,7 @@ js_archive_constructor(JSContext *cx, uintN argc, jsval *arglist) JSObject *obj = JS_THIS_OBJECT(cx, arglist); jsval *argv = JS_ARGV(cx, arglist); JSString* str; - archive_private_t* p; + char* filename; obj = JS_NewObject(cx, &js_archive_class, NULL, NULL); JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(obj)); @@ -396,14 +470,9 @@ js_archive_constructor(JSContext *cx, uintN argc, jsval *arglist) return JS_FALSE; } - if((p=(archive_private_t*)calloc(1,sizeof(*p)))==NULL) { - JS_ReportError(cx, "calloc failed"); - return JS_FALSE; - } - - JSSTRING_TO_STRBUF(cx, str, p->name, sizeof(p->name), NULL); + JSSTRING_TO_MSTRING(cx, str, filename, NULL); - if(!JS_SetPrivate(cx, obj, p)) { + if(!JS_SetPrivate(cx, obj, filename)) { JS_ReportError(cx, "JS_SetPrivate failed"); return JS_FALSE; } @@ -413,41 +482,11 @@ js_archive_constructor(JSContext *cx, uintN argc, jsval *arglist) return JS_FALSE; } - if(!JS_DefineProperty(cx, obj, "directory", JSVAL_VOID, js_archive_directory, NULL, 0)) { - JS_ReportError(cx, "JS_DefineProperty failed"); - return JS_FALSE; - } - #ifdef BUILD_JSDOCS - js_DescribeSyncObject(cx,obj,"Class used for opening, creating, reading, or writing files on the local file system<p>" - "Special features include:</h2><ol type=disc>" - "<li>Exclusive-access files (default) or shared files<ol type=circle>" - "<li>optional record-locking" - "<li>buffered or non-buffered I/O" - "</ol>" - "<li>Support for binary files<ol type=circle>" - "<li>native or network byte order (endian)" - "<li>automatic Unix-to-Unix (<i>UUE</i>), yEncode (<i>yEnc</i>) or Base64 encoding/decoding" - "</ol>" - "<li>Support for ASCII text files<ol type=circle>" - "<li>supports line-based I/O<ol type=square>" - "<li>entire file may be read or written as an array of strings" - "<li>individual lines may be read or written one line at a time" - "</ol>" - "<li>supports fixed-length records<ol type=square>" - "<li>optional end-of-text (<i>etx</i>) character for automatic record padding/termination" - "<li>Synchronet <tt>.dat</tt> files use an <i>etx</i> value of 3 (Ctrl-C)" - "</ol>" - "<li>supports <tt>.ini</tt> formated configuration files<ol type=square>" - "<li>concept and support of <i>root</i> ini sections added in v3.12" - "</ol>" - "<li>optional ROT13 encoding/translation" - "</ol>" - "<li>Dynamically-calculated industry standard checksums (e.g. CRC-16, CRC-32, MD5)" - "</ol>" - ,310 - ); - js_DescribeSyncConstructor(cx,obj,"To create a new File object: <tt>var f = new File(<i>filename</i>)</tt>"); + js_DescribeSyncObject(cx,obj,"Class used for opening, creating, reading, or writing archive files on the local file system<p>" + ,31900 + ); + js_DescribeSyncConstructor(cx,obj,"To create a new Archive object: <tt>var a = new Archive(<i>filename</i>)</tt>"); js_CreateArrayOfStrings(cx, obj, "_property_desc_list", file_prop_desc, JSPROP_READONLY); #endif @@ -456,9 +495,9 @@ js_archive_constructor(JSContext *cx, uintN argc, jsval *arglist) static void js_finalize_archive(JSContext *cx, JSObject *obj) { - archive_private_t* p; + void* p; - if((p = (archive_private_t*)JS_GetPrivate(cx, obj)) == NULL) + if((p = JS_GetPrivate(cx, obj)) == NULL) return; free(p); JS_SetPrivate(cx, obj, NULL);