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 (1)
  • Rob Swindell's avatar
    Store contents (list) of archive files in filebase (in the "msg tail") · 5374a113
    Rob Swindell authored
    This will allow fast/easy display of archive contents without actually reading the archive files.
    
    Introduces some new functions:
    - list_archive_contents()
    - smb_adddfile_withlist()
    
    A new SMB convenience variable ("tail", aliased as "content" for a file).
    A new file detail level ("file_detail_content", exposed in JS as FileBase.DETAIL.CONTENTS) which adds a "content" array property to file metadata objects for JS FileBase.get().
    
    Files already added to the new filebases won't have this archive content automatically - I'm looking into that now (likely a new or updated JS script to run).
    5374a113
......@@ -245,7 +245,7 @@ void addlist(char *inpath, uint dirnum, const char* uploader, uint dskip, uint s
reupload(&smb, &f);
}
else
result = smb_addfile(&smb, &f, SMB_SELFPACK, ext_desc, filepath);
result = smb_addfile(&smb, &f, SMB_SELFPACK, ext_desc, NULL, filepath);
smb_freefilemem(&f);
if(result != SMB_SUCCESS)
fprintf(stderr, "!Error %d (%s) adding file to %s", result, smb.last_error, smb.file);
......@@ -415,7 +415,7 @@ void addlist(char *inpath, uint dirnum, const char* uploader, uint dskip, uint s
reupload(&smb, &f);
}
else
result = smb_addfile(&smb, &f, SMB_SELFPACK, ext_desc, filepath);
result = smb_addfile(&smb, &f, SMB_SELFPACK, ext_desc, NULL, filepath);
smb_freefilemem(&f);
if(result != SMB_SUCCESS)
fprintf(stderr, "!ERROR %d (%s) writing to %s\n"
......@@ -795,7 +795,7 @@ int main(int argc, char **argv)
result = smb_updatemsg(&smb, &f);
}
else
result = smb_addfile(&smb, &f, SMB_SELFPACK, ext_desc, str);
result = smb_addfile(&smb, &f, SMB_SELFPACK, ext_desc, NULL, str);
if(mode&UL_STATS)
updatestats(l);
files++;
......
......@@ -29,6 +29,7 @@
#include "load_cfg.h" // smb_open_dir()
#include "scfglib.h"
#include "sauce.h"
#include "crc32.h"
/* libarchive: */
#include <archive.h>
......@@ -636,8 +637,11 @@ bool addfile(scfg_t* cfg, uint dirnum, file_t* f, const char* extdesc, client_t*
getfilepath(cfg, f, fpath);
file_client_hfields(f, client);
int result = smb_addfile(&smb, f, SMB_SELFPACK, extdesc, fpath);
str_list_t list = list_archive_contents(fpath, /* pattern: */NULL
,(cfg->dir[dirnum]->misc & DIR_NOHASH) == 0, /* error: */NULL, /* size: */0);
int result = smb_addfile_withlist(&smb, f, SMB_SELFPACK, extdesc, list, fpath);
smb_close(&smb);
strListFree(&list);
return result == SMB_SUCCESS;
}
......@@ -698,6 +702,108 @@ 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, 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;
}
iniSetString(&list, pathname, "type", type, /* style: */NULL);
iniSetBytes(&list, pathname, "size", 1, archive_entry_size(entry), /* style: */NULL);
iniSetDateTime(&list, pathname, "time", true, archive_entry_mtime(entry), NULL);
iniSetInteger(&list, pathname, "mode", archive_entry_mode(entry), NULL);
iniSetString(&list, pathname, "format", archive_format_name(ar), NULL);
iniSetString(&list, pathname, "compression", archive_filter_name(ar, 0), NULL);
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);
iniSetHexInt(&list, pathname, "crc32", crc32, NULL);
char hex[128];
iniSetString(&list, pathname, "md5", MD5_hex(hex, md5), NULL);
iniSetString(&list, pathname, "sha1", SHA1_hex(hex, sha1), NULL);
}
}
archive_read_free(ar);
return list;
}
str_list_t directory(const char* path)
{
int flags = GLOB_MARK;
......
......@@ -67,6 +67,7 @@ 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, 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,6 +164,51 @@ 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)
{
......@@ -181,67 +226,67 @@ set_file_properties(JSContext *cx, JSObject* obj, file_t* f, enum file_detail de
|| !JS_DefineProperty(cx, obj, "name", STRING_TO_JSVAL(js_str), NULL, NULL, flags))
return false;
if(((f->from != NULL && *f->from != '\0') || detail > file_detail_extdesc)
if(((f->from != NULL && *f->from != '\0') || detail > file_detail_content)
&& ((js_str = JS_NewStringCopyZ(cx, f->from)) == NULL
|| !JS_DefineProperty(cx, obj, "from", STRING_TO_JSVAL(js_str), NULL, NULL, flags)))
return false;
if(((f->from_ip != NULL && *f->from_ip != '\0') || detail > file_detail_extdesc)
if(((f->from_ip != NULL && *f->from_ip != '\0') || detail > file_detail_content)
&& ((js_str = JS_NewStringCopyZ(cx, f->from_ip)) == NULL
|| !JS_DefineProperty(cx, obj, "from_ip_addr", STRING_TO_JSVAL(js_str), NULL, NULL, flags)))
return false;
if(((f->from_host != NULL && *f->from_host != '\0') || detail > file_detail_extdesc)
if(((f->from_host != NULL && *f->from_host != '\0') || detail > file_detail_content)
&& ((js_str = JS_NewStringCopyZ(cx, f->from_host)) == NULL
|| !JS_DefineProperty(cx, obj, "from_host_name", STRING_TO_JSVAL(js_str), NULL, NULL, flags)))
return false;
if(((f->from_prot != NULL && *f->from_prot != '\0') || detail > file_detail_extdesc)
if(((f->from_prot != NULL && *f->from_prot != '\0') || detail > file_detail_content)
&& ((js_str = JS_NewStringCopyZ(cx, f->from_prot)) == NULL
|| !JS_DefineProperty(cx, obj, "from_protocol", STRING_TO_JSVAL(js_str), NULL, NULL, flags)))
return false;
if(((f->from_port != NULL && *f->from_port != '\0') || detail > file_detail_extdesc)
if(((f->from_port != NULL && *f->from_port != '\0') || detail > file_detail_content)
&& ((js_str = JS_NewStringCopyZ(cx, f->from_port)) == NULL
|| !JS_DefineProperty(cx, obj, "from_port", STRING_TO_JSVAL(js_str), NULL, NULL, flags)))
return false;
if(((f->author != NULL && *f->author != '\0') || detail > file_detail_extdesc)
if(((f->author != NULL && *f->author != '\0') || detail > file_detail_content)
&& ((js_str = JS_NewStringCopyZ(cx, f->author)) == NULL
|| !JS_DefineProperty(cx, obj, "author", STRING_TO_JSVAL(js_str), NULL, NULL, flags)))
return false;
if(((f->author_org != NULL && *f->author_org != '\0') || detail > file_detail_extdesc)
if(((f->author_org != NULL && *f->author_org != '\0') || detail > file_detail_content)
&& ((js_str = JS_NewStringCopyZ(cx, f->author_org)) == NULL
|| !JS_DefineProperty(cx, obj, "author_org", STRING_TO_JSVAL(js_str), NULL, NULL, flags)))
return false;
if(((f->to_list != NULL && *f->to_list != '\0') || detail > file_detail_extdesc)
if(((f->to_list != NULL && *f->to_list != '\0') || detail > file_detail_content)
&& ((js_str = JS_NewStringCopyZ(cx, f->to_list)) == NULL
|| !JS_DefineProperty(cx, obj, "to_list", STRING_TO_JSVAL(js_str), NULL, NULL, flags)))
return false;
val = BOOLEAN_TO_JSVAL(f->idx.attr & FILE_ANONYMOUS);
if((val == JSVAL_TRUE || detail > file_detail_extdesc)
if((val == JSVAL_TRUE || detail > file_detail_content)
&& !JS_DefineProperty(cx, obj, "anon", val, NULL, NULL, flags))
return false;
if(((f->tags != NULL && *f->tags != '\0') || detail > file_detail_extdesc)
if(((f->tags != NULL && *f->tags != '\0') || detail > file_detail_content)
&& ((js_str = JS_NewStringCopyZ(cx, f->tags)) == NULL
|| !JS_DefineProperty(cx, obj, "tags", STRING_TO_JSVAL(js_str), NULL, NULL, flags)))
return false;
if(((f->desc != NULL && *f->desc != '\0') || detail > file_detail_extdesc)
if(((f->desc != NULL && *f->desc != '\0') || detail > file_detail_content)
&& ((js_str = JS_NewStringCopyZ(cx, f->desc)) == NULL
|| !JS_DefineProperty(cx, obj, "desc", STRING_TO_JSVAL(js_str), NULL, NULL, flags)))
return false;
if(((f->extdesc != NULL && *f->extdesc != '\0') || detail > file_detail_extdesc)
if(((f->extdesc != NULL && *f->extdesc != '\0') || detail > file_detail_content)
&& ((js_str = JS_NewStringCopyZ(cx, f->extdesc)) == NULL
|| !JS_DefineProperty(cx, obj, "extdesc", STRING_TO_JSVAL(js_str), NULL, NULL, flags)))
return false;
if(f->cost > 0 || detail > file_detail_extdesc) {
if(f->cost > 0 || detail > file_detail_content) {
val = UINT_TO_JSVAL(f->cost);
if(!JS_DefineProperty(cx, obj, "cost", val, NULL, NULL, flags))
return false;
......@@ -253,17 +298,17 @@ set_file_properties(JSContext *cx, JSObject* obj, file_t* f, enum file_detail de
val = UINT_TO_JSVAL(f->hdr.when_written.time);
if(!JS_DefineProperty(cx, obj, "time", val, NULL, NULL, flags))
return false;
if(f->hdr.when_imported.time > 0 || detail > file_detail_extdesc) {
if(f->hdr.when_imported.time > 0 || detail > file_detail_content) {
val = UINT_TO_JSVAL(f->hdr.when_imported.time);
if(!JS_DefineProperty(cx, obj, "added", val, NULL, NULL, flags))
return false;
}
if(f->hdr.last_downloaded > 0 || detail > file_detail_extdesc) {
if(f->hdr.last_downloaded > 0 || detail > file_detail_content) {
val = UINT_TO_JSVAL(f->hdr.last_downloaded);
if(!JS_DefineProperty(cx, obj, "last_downloaded", val, NULL, NULL, flags))
return false;
}
if(f->hdr.times_downloaded > 0 || detail > file_detail_extdesc) {
if(f->hdr.times_downloaded > 0 || detail > file_detail_content) {
val = UINT_TO_JSVAL(f->hdr.times_downloaded);
if(!JS_DefineProperty(cx, obj, "times_downloaded", val, NULL, NULL, flags))
return false;
......@@ -291,6 +336,32 @@ 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);
}
return true;
}
......@@ -1164,7 +1235,10 @@ js_add_file(JSContext *cx, uintN argc, jsval *arglist)
char fpath[MAX_PATH + 1];
getfilepath(scfg, &file, fpath);
file_client_hfields(&file, client);
p->smb_result = smb_addfile(&p->smb, &file, SMB_SELFPACK, extdesc, fpath);
str_list_t list = list_archive_contents(fpath, /* pattern: */NULL
,(scfg->dir[file.dir]->misc & DIR_NOHASH) == 0, /* error: */NULL, /* size: */0);
p->smb_result = smb_addfile_withlist(&p->smb, &file, SMB_SELFPACK, extdesc, list, fpath);
strListFree(&list);
JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(p->smb_result == SMB_SUCCESS));
}
JS_RESUMEREQUEST(cx, rc);
......@@ -1246,8 +1320,13 @@ js_update_file(JSContext *cx, uintN argc, jsval *arglist)
if(strcmp(extdesc ? extdesc : "", file.extdesc ? file.extdesc : "") == 0)
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, newfname);
if((p->smb_result = smb_removefile(&p->smb, &file)) == SMB_SUCCESS) {
str_list_t list = list_archive_contents(newfname, /* pattern: */NULL
,file.dir < scfg->total_dirs && (scfg->dir[file.dir]->misc & DIR_NOHASH) == 0
,/* error: */NULL, /* size: */0);
p->smb_result = smb_addfile_withlist(&p->smb, &file, SMB_SELFPACK, extdesc, list, newfname);
strListFree(&list);
}
}
}
}
......@@ -1749,6 +1828,7 @@ 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 plus extended descriptions",
"Normal level of file detail plus extended descriptions and archived contents",
"Maximum file detail, include undefined/null property values",
NULL
};
......@@ -1789,10 +1869,12 @@ 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, "MAX", INT_TO_JSVAL(file_detail_extdesc + 1), NULL, NULL
JS_DefineProperty(cx, detail, "CONTENTS", 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);
#ifdef BUILD_JSDOCS
js_DescribeSyncObject(cx, detail, "Detail level numeric constants", 0);
js_DescribeSyncObject(cx, detail, "Detail level numeric constants (in increasing verbosity)", 0);
js_CreateArrayOfStrings(cx, detail, "_property_desc_list", filebase_detail_prop_desc, JSPROP_READONLY);
#endif
}
......
......@@ -660,7 +660,10 @@ bool upgrade_file_bases(bool hash)
if(*extdesc)
body = extdesc;
}
result = smb_addfile(&smb, &file, SMB_FASTALLOC, body, fpath);
str_list_t list = list_archive_contents(fpath, /* pattern: */NULL
,(scfg.dir[i]->misc & DIR_NOHASH) == 0, /* error: */NULL, /* size: */0);
result = smb_addfile_withlist(&smb, &file, SMB_FASTALLOC, body, list, fpath);
strListFree(&list);
}
if(result != SMB_SUCCESS) {
fprintf(stderr, "\n!Error %d (%s) adding file to %s\n", result, smb.last_error, smb.file);
......
......@@ -632,6 +632,10 @@ typedef struct { /* Message or File */
uchar* text; /* Message body text (optional) */
char* extdesc; /* File extended description */
};
union {
uchar* tail; /* Message body tail (optional) */
char* content; /* Archive content list */
};
char* tags; /* Message tags (space-delimited) */
char* editor; /* Message editor (if known) */
char* mime_version; /* MIME Version (if applicable) */
......
......@@ -295,7 +295,9 @@ int smb_getfile(smb_t* smb, smbfile_t* file, enum file_detail detail)
if((result = smb_getmsghdr(smb, file)) != SMB_SUCCESS)
return result;
if(detail >= file_detail_extdesc)
file->extdesc = smb_getmsgtxt(smb, file, GETMSGTXT_ALL);
file->extdesc = smb_getmsgtxt(smb, file, GETMSGTXT_BODY_ONLY);
if(detail >= file_detail_content)
file->content = smb_getmsgtxt(smb, file, GETMSGTXT_TAIL_ONLY);
}
file->dir = smb->dirnum;
......@@ -325,7 +327,7 @@ void smb_freefilemem(smbfile_t* file)
/****************************************************************************/
/****************************************************************************/
int smb_addfile(smb_t* smb, smbfile_t* file, int storage, const char* extdesc, const char* path)
int smb_addfile(smb_t* smb, smbfile_t* file, int storage, const char* extdesc, const char* content, const char* path)
{
if(file->name == NULL || *file->name == '\0') {
safe_snprintf(smb->last_error, sizeof(smb->last_error), "%s missing name", __FUNCTION__);
......@@ -343,7 +345,28 @@ int smb_addfile(smb_t* smb, smbfile_t* file, int storage, const char* extdesc, c
}
file->hdr.attr |= MSG_FILE;
file->hdr.type = SMB_MSG_TYPE_FILE;
return smb_addmsg(smb, file, storage, SMB_HASH_SOURCE_NONE, XLAT_NONE, /* body: */(const uchar*)extdesc, /* tail: */NULL);
return smb_addmsg(smb, file, storage, SMB_HASH_SOURCE_NONE, XLAT_NONE
,/* body: */(const uchar*)extdesc, /* tail: */(const uchar*)content);
}
/****************************************************************************/
/* Like smb_addfile(), except 'content' is a str_list_t: 'list' */
/****************************************************************************/
int smb_addfile_withlist(smb_t* smb, smbfile_t* file, int storage, const char* extdesc, str_list_t list, const char* path)
{
char* content = NULL;
int result;
if(list != NULL) {
size_t size = strListCount(list) * 1024;
content = calloc(1, size);
if(content == NULL)
return SMB_ERR_MEM;
strListCombine(list, content, size - 1, "\r\n");
}
result = smb_addfile(smb, file, storage, extdesc, content, path);
free(content);
return result;
}
/****************************************************************************/
......@@ -353,7 +376,7 @@ int smb_renewfile(smb_t* smb, smbfile_t* file, int storage, const char* path)
int result;
if((result = smb_removefile(smb, file)) != SMB_SUCCESS)
return result;
return smb_addfile(smb, file, storage, file->extdesc, path);
return smb_addfile(smb, file, storage, file->extdesc, file->content, path);
}
/****************************************************************************/
......
......@@ -1141,6 +1141,10 @@ void smb_freemsgmem(smbmsg_t* msg)
free(msg->text);
msg->text = NULL;
}
if(msg->tail != NULL) {
free(msg->tail);
msg->tail = NULL;
}
}
/****************************************************************************/
......
......@@ -283,8 +283,9 @@ SMBEXPORT int smb_open_fp(smb_t*, FILE**, int share);
SMBEXPORT void smb_close_fp(FILE**);
/* New FileBase API: */
enum file_detail { file_detail_index, file_detail_normal, file_detail_extdesc };
SMBEXPORT int smb_addfile(smb_t*, smbfile_t*, int storage, const char* extdesc, const char* path);
enum file_detail { file_detail_index, file_detail_normal, file_detail_extdesc, file_detail_content };
SMBEXPORT int smb_addfile(smb_t*, smbfile_t*, int storage, const char* extdesc, const char* content, const char* path);
SMBEXPORT int smb_addfile_withlist(smb_t*, smbfile_t*, int storage, const char* extdesc, str_list_t, const char* path);
SMBEXPORT int smb_renewfile(smb_t*, smbfile_t*, int storage, const char* path);
SMBEXPORT int smb_getfile(smb_t*, smbfile_t*, enum file_detail);
SMBEXPORT int smb_putfile(smb_t*, smbfile_t*);
......