static int test_list (void) { size_t len; int64_t i; const char * str; tr_variant top; tr_rpc_parse_list_str (&top, "12", -1); check (tr_variantIsInt (&top)); check (tr_variantGetInt (&top, &i)); check_int_eq (12, i); tr_variantFree (&top); tr_rpc_parse_list_str (&top, "12", 1); check (tr_variantIsInt (&top)); check (tr_variantGetInt (&top, &i)); check_int_eq (1, i); tr_variantFree (&top); tr_rpc_parse_list_str (&top, "6,7", -1); check (tr_variantIsList (&top)); check (tr_variantListSize (&top) == 2); check (tr_variantGetInt (tr_variantListChild (&top, 0), &i)); check_int_eq (6, i); check (tr_variantGetInt (tr_variantListChild (&top, 1), &i)); check_int_eq (7, i); tr_variantFree (&top); tr_rpc_parse_list_str (&top, "asdf", -1); check (tr_variantIsString (&top)); check (tr_variantGetStr (&top, &str, &len)); check_int_eq (4, len); check_streq ("asdf", str); tr_variantFree (&top); tr_rpc_parse_list_str (&top, "1,3-5", -1); check (tr_variantIsList (&top)); check (tr_variantListSize (&top) == 4); check (tr_variantGetInt (tr_variantListChild (&top, 0), &i)); check_int_eq (1, i); check (tr_variantGetInt (tr_variantListChild (&top, 1), &i)); check_int_eq (3, i); check (tr_variantGetInt (tr_variantListChild (&top, 2), &i)); check_int_eq (4, i); check (tr_variantGetInt (tr_variantListChild (&top, 3), &i)); check_int_eq (5, i); tr_variantFree (&top); return 0; }
static tr_variant* get_node (tr_ptrArray * stack, tr_quark * key, tr_variant * top, int * err) { tr_variant * node = NULL; if (tr_ptrArrayEmpty (stack)) { node = top; } else { tr_variant * parent = tr_ptrArrayBack (stack); if (tr_variantIsList (parent)) { node = tr_variantListAdd (parent); } else if (*key && tr_variantIsDict (parent)) { node = tr_variantDictAdd (parent, *key); *key = 0; } else { *err = EILSEQ; } } return node; }
static tr_variant* get_node (struct jsonsl_st * jsn) { tr_variant * parent; tr_variant * node = NULL; struct json_wrapper_data * data = jsn->data; parent = tr_ptrArrayEmpty (&data->stack) ? NULL : tr_ptrArrayBack (&data->stack); if (!parent) { node = data->top; } else if (tr_variantIsList (parent)) { node = tr_variantListAdd (parent); } else if (tr_variantIsDict (parent) && (data->key!=NULL)) { node = tr_variantDictAdd (parent, tr_quark_new (data->key, data->keylen)); data->key = NULL; data->keylen = 0; } return node; }
static const char* parseFiles (tr_info * inf, tr_variant * files, const tr_variant * length) { int64_t len; inf->totalSize = 0; if (tr_variantIsList (files)) /* multi-file mode */ { tr_file_index_t i; struct evbuffer * buf = evbuffer_new (); inf->isMultifile = 1; inf->fileCount = tr_variantListSize (files); inf->files = tr_new0 (tr_file, inf->fileCount); for (i=0; i<inf->fileCount; i++) { tr_variant * file; tr_variant * path; file = tr_variantListChild (files, i); if (!tr_variantIsDict (file)) return "files"; if (!tr_variantDictFindList (file, TR_KEY_path_utf_8, &path)) if (!tr_variantDictFindList (file, TR_KEY_path, &path)) return "path"; if (!getfile (&inf->files[i].name, inf->name, path, buf)) return "path"; if (!tr_variantDictFindInt (file, TR_KEY_length, &len)) return "length"; inf->files[i].length = len; inf->totalSize += len; } evbuffer_free (buf); } else if (tr_variantGetInt (length, &len)) /* single-file mode */ { if (path_is_suspicious (inf->name)) return "path"; inf->isMultifile = 0; inf->fileCount = 1; inf->files = tr_new0 (tr_file, 1); inf->files[0].name = tr_strdup (inf->name); inf->files[0].length = len; inf->totalSize += len; } else { return "length"; } return NULL; }
static bool getfile (char ** setme, const char * root, tr_variant * path, struct evbuffer * buf) { bool success = false; size_t root_len = 0; *setme = NULL; /* root's already been checked by caller */ assert (!path_component_is_suspicious (root)); if (tr_variantIsList (path)) { int i; const int n = tr_variantListSize (path); success = true; evbuffer_drain (buf, evbuffer_get_length (buf)); root_len = strlen (root); evbuffer_add (buf, root, root_len); for (i=0; i<n; i++) { size_t len; const char * str; if (!tr_variantGetStr (tr_variantListChild (path, i), &str, &len) || path_component_is_suspicious (str)) { success = false; break; } if (!*str) continue; evbuffer_add (buf, TR_PATH_DELIMITER_STR, 1); evbuffer_add (buf, str, len); } } if (success && (evbuffer_get_length (buf) <= root_len)) { success = false; } if (success) { *setme = tr_utf8clean ((char*)evbuffer_pullup (buf, -1), evbuffer_get_length (buf)); /*fprintf (stderr, "[%s]\n", *setme);*/ } return success; }
static int test1(void) { char const* in = "{\n" " \"headers\": {\n" " \"type\": \"request\",\n" " \"tag\": 666\n" " },\n" " \"body\": {\n" " \"name\": \"torrent-info\",\n" " \"arguments\": {\n" " \"ids\": [ 7, 10 ]\n" " }\n" " }\n" "}\n"; tr_variant top; tr_variant* headers; tr_variant* body; tr_variant* args; tr_variant* ids; char const* str; int64_t i; int const err = tr_variantFromJson(&top, in, strlen(in)); check_int(err, ==, 0); check(tr_variantIsDict(&top)); check_ptr((headers = tr_variantDictFind(&top, tr_quark_new("headers", 7))), !=, NULL); check(tr_variantIsDict(headers)); check(tr_variantDictFindStr(headers, tr_quark_new("type", 4), &str, NULL)); check_str(str, ==, "request"); check(tr_variantDictFindInt(headers, TR_KEY_tag, &i)); check_int(i, ==, 666); check_ptr((body = tr_variantDictFind(&top, tr_quark_new("body", 4))), !=, NULL); check(tr_variantDictFindStr(body, TR_KEY_name, &str, NULL)); check_str(str, ==, "torrent-info"); check_ptr((args = tr_variantDictFind(body, tr_quark_new("arguments", 9))), !=, NULL); check(tr_variantIsDict(args)); check_ptr((ids = tr_variantDictFind(args, TR_KEY_ids)), !=, NULL); check(tr_variantIsList(ids)); check_uint(tr_variantListSize(ids), ==, 2); check(tr_variantGetInt(tr_variantListChild(ids, 0), &i)); check_int(i, ==, 7); check(tr_variantGetInt(tr_variantListChild(ids, 1), &i)); check_int(i, ==, 10); tr_variantFree(&top); return 0; }
static int test1 (void) { const char * in = "{\n" " \"headers\": {\n" " \"type\": \"request\",\n" " \"tag\": 666\n" " },\n" " \"body\": {\n" " \"name\": \"torrent-info\",\n" " \"arguments\": {\n" " \"ids\": [ 7, 10 ]\n" " }\n" " }\n" "}\n"; tr_variant top, *headers, *body, *args, *ids; const char * str; int64_t i; const int err = tr_variantFromJson (&top, in, strlen(in)); check (!err); check (tr_variantIsDict (&top)); check ((headers = tr_variantDictFind (&top, tr_quark_new("headers",7)))); check (tr_variantIsDict (headers)); check (tr_variantDictFindStr (headers, tr_quark_new("type",4), &str, NULL)); check_streq ("request", str); check (tr_variantDictFindInt (headers, TR_KEY_tag, &i)); check_int_eq (666, i); check ((body = tr_variantDictFind (&top, tr_quark_new("body",4)))); check (tr_variantDictFindStr (body, TR_KEY_name, &str, NULL)); check_streq ("torrent-info", str); check ((args = tr_variantDictFind (body, tr_quark_new("arguments",9)))); check (tr_variantIsDict (args)); check ((ids = tr_variantDictFind (args, TR_KEY_ids))); check (tr_variantIsList (ids)); check_int_eq (2, tr_variantListSize (ids)); check (tr_variantGetInt (tr_variantListChild (ids, 0), &i)); check_int_eq (7, i); check (tr_variantGetInt (tr_variantListChild (ids, 1), &i)); check_int_eq (10, i); tr_variantFree (&top); return 0; }
static bool getfile (char ** setme, const char * root, tr_variant * path, struct evbuffer * buf) { bool success = false; if (tr_variantIsList (path)) { int i; const int n = tr_variantListSize (path); evbuffer_drain (buf, evbuffer_get_length (buf)); evbuffer_add (buf, root, strlen (root)); for (i=0; i<n; i++) { size_t len; const char * str; if (tr_variantGetStr (tr_variantListChild (path, i), &str, &len)) { evbuffer_add (buf, TR_PATH_DELIMITER_STR, 1); evbuffer_add (buf, str, len); } } *setme = tr_utf8clean ((char*)evbuffer_pullup (buf, -1), evbuffer_get_length (buf)); /* fprintf (stderr, "[%s]\n", *setme); */ success = true; } if ((*setme != NULL) && path_is_suspicious (*setme)) { tr_free (*setme); *setme = NULL; success = false; } return success; }
static char const* parseFiles(tr_info* inf, tr_variant* files, tr_variant const* length) { int64_t len; inf->totalSize = 0; if (tr_variantIsList(files)) /* multi-file mode */ { struct evbuffer* buf; char const* result; if (path_component_is_suspicious(inf->name)) { return "path"; } buf = evbuffer_new(); result = NULL; inf->isFolder = true; inf->fileCount = tr_variantListSize(files); inf->files = tr_new0(tr_file, inf->fileCount); for (tr_file_index_t i = 0; i < inf->fileCount; i++) { tr_variant* file; tr_variant* path; file = tr_variantListChild(files, i); if (!tr_variantIsDict(file)) { result = "files"; break; } if (!tr_variantDictFindList(file, TR_KEY_path_utf_8, &path)) { if (!tr_variantDictFindList(file, TR_KEY_path, &path)) { result = "path"; break; } } if (!getfile(&inf->files[i].name, inf->name, path, buf)) { result = "path"; break; } if (!tr_variantDictFindInt(file, TR_KEY_length, &len)) { result = "length"; break; } inf->files[i].length = len; inf->totalSize += len; } evbuffer_free(buf); return result; } else if (tr_variantGetInt(length, &len)) /* single-file mode */ { if (path_component_is_suspicious(inf->name)) { return "path"; } inf->isFolder = false; inf->fileCount = 1; inf->files = tr_new0(tr_file, 1); inf->files[0].name = tr_strdup(inf->name); inf->files[0].length = len; inf->totalSize += len; } else { return "length"; } return NULL; }
static uint64_t loadProgress(tr_variant* dict, tr_torrent* tor) { uint64_t ret = 0; tr_variant* prog; tr_info const* inf = tr_torrentInfo(tor); for (size_t i = 0; i < inf->pieceCount; ++i) { inf->pieces[i].timeChecked = 0; } if (tr_variantDictFindDict(dict, TR_KEY_progress, &prog)) { char const* err; char const* str; uint8_t const* raw; size_t rawlen; tr_variant* l; tr_variant* b; struct tr_bitfield blocks = TR_BITFIELD_INIT; if (tr_variantDictFindList(prog, TR_KEY_time_checked, &l)) { /* per-piece timestamps were added in 2.20. If some of a file's pieces have been checked more recently than the file's mtime, and some lest recently, then that file will have a list containing timestamps for each piece. However, the most common use case is that the file doesn't change after it's downloaded. To reduce overhead in the .resume file, only a single timestamp is saved for the file if *all* or *none* of the pieces were tested more recently than the file's mtime. */ for (tr_file_index_t fi = 0; fi < inf->fileCount; ++fi) { tr_variant* b = tr_variantListChild(l, fi); tr_file const* f = &inf->files[fi]; if (tr_variantIsInt(b)) { int64_t t; tr_variantGetInt(b, &t); for (tr_piece_index_t i = f->firstPiece; i <= f->lastPiece; ++i) { inf->pieces[i].timeChecked = (time_t)t; } } else if (tr_variantIsList(b)) { int64_t offset = 0; int const pieces = f->lastPiece + 1 - f->firstPiece; tr_variantGetInt(tr_variantListChild(b, 0), &offset); for (int i = 0; i < pieces; ++i) { int64_t t = 0; tr_variantGetInt(tr_variantListChild(b, i + 1), &t); inf->pieces[f->firstPiece + i].timeChecked = (time_t)(t != 0 ? t + offset : 0); } } } } else if (tr_variantDictFindList(prog, TR_KEY_mtimes, &l)) { /* Before 2.20, we stored the files' mtimes in the .resume file. When loading the .resume file, a torrent's file would be flagged as untested if its stored mtime didn't match its real mtime. */ for (tr_file_index_t fi = 0; fi < inf->fileCount; ++fi) { int64_t t; if (tr_variantGetInt(tr_variantListChild(l, fi), &t)) { tr_file const* f = &inf->files[fi]; time_t const mtime = tr_torrentGetFileMTime(tor, fi); time_t const timeChecked = mtime == t ? mtime : 0; for (tr_piece_index_t i = f->firstPiece; i <= f->lastPiece; ++i) { inf->pieces[i].timeChecked = timeChecked; } } } } err = NULL; tr_bitfieldConstruct(&blocks, tor->blockCount); if ((b = tr_variantDictFind(prog, TR_KEY_blocks)) != NULL) { size_t buflen; uint8_t const* buf; if (!tr_variantGetRaw(b, &buf, &buflen)) { err = "Invalid value for \"blocks\""; } else if (buflen == 3 && memcmp(buf, "all", 3) == 0) { tr_bitfieldSetHasAll(&blocks); } else if (buflen == 4 && memcmp(buf, "none", 4) == 0) { tr_bitfieldSetHasNone(&blocks); } else { tr_bitfieldSetRaw(&blocks, buf, buflen, true); } } else if (tr_variantDictFindStr(prog, TR_KEY_have, &str, NULL)) { if (strcmp(str, "all") == 0) { tr_bitfieldSetHasAll(&blocks); } else { err = "Invalid value for HAVE"; } } else if (tr_variantDictFindRaw(prog, TR_KEY_bitfield, &raw, &rawlen)) { tr_bitfieldSetRaw(&blocks, raw, rawlen, true); } else { err = "Couldn't find 'pieces' or 'have' or 'bitfield'"; } if (err != NULL) { tr_logAddTorDbg(tor, "Torrent needs to be verified - %s", err); } else { tr_cpBlockInit(&tor->completion, &blocks); } tr_bitfieldDestruct(&blocks); ret = TR_FR_PROGRESS; } return ret; }