static tr_quark append_new_quark (const void * str, size_t len) { tr_quark ret; struct tr_key_struct * tmp; tmp = tr_new (struct tr_key_struct, 1); tmp->str = tr_strndup (str, len); tmp->len = len; ret = TR_N_KEYS + tr_ptrArraySize (&my_runtime); tr_ptrArrayAppend (&my_runtime, tmp); return ret; }
static char* makeURL( tr_webseed * w, const tr_file * file ) { char * ret; struct evbuffer * out = tr_getBuffer( ); const char * url = w->url; const size_t url_len = strlen( url ); evbuffer_add( out, url, url_len ); /* if url ends with a '/', add the torrent name */ if( url[url_len - 1] == '/' ) { const char * str = file->name; /* this is like curl_escape() but doesn't munge the * '/' directory separators in the path */ while( str && *str ) { switch( *str ) { case ',': case '-': case '.': case '/': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': evbuffer_add( out, str, 1 ); break; default: evbuffer_add_printf( out, "%%%02X", *str ); break; } str++; } } ret = tr_strndup( EVBUFFER_DATA( out ), EVBUFFER_LENGTH( out ) ); tr_releaseBuffer( out ); return ret; }
static uint64_t parseDownloadDir( tr_torrent * tor, const uint8_t * buf, uint32_t len ) { uint64_t ret = 0; if( buf && *buf && len ) { tr_free( tor->downloadDir ); tor->downloadDir = tr_strndup( (char*)buf, len ); ret = TR_FR_DOWNLOAD_DIR; } return ret; }
static char* getOldTorrentFilename( const tr_handle * handle, const tr_info * inf ) { char * ret; struct evbuffer * buf = evbuffer_new( ); evbuffer_add_printf( buf, "%s%c%s", tr_getTorrentDir( handle ), TR_PATH_DELIMITER, inf->hashString ); if( handle->tag ) evbuffer_add_printf( buf, "-%s", handle->tag ); ret = tr_strndup( EVBUFFER_DATA( buf ), EVBUFFER_LENGTH( buf ) ); evbuffer_free( buf ); return ret; }
static char* getOldTorrentFilename( const tr_session * session, const tr_info * inf ) { char * ret; struct evbuffer * buf = tr_getBuffer( ); evbuffer_add_printf( buf, "%s%c%s", tr_getTorrentDir( session ), TR_PATH_DELIMITER, inf->hashString ); if( session->tag ) evbuffer_add_printf( buf, "-%s", session->tag ); ret = tr_strndup( EVBUFFER_DATA( buf ), EVBUFFER_LENGTH( buf ) ); tr_releaseBuffer( buf ); return ret; }
static int tr_parser_parse_octal_literal(const char **str) { const char *s; char *copy, *copy_end; size_t len = 0; unsigned int value; if(str == NULL || *str == NULL) return INVALID_CHAR; // Manually count the number of octal numerals. // That is needed because strtoul skips whitespace, which we must not do. for(s = *str; *s != '\0' && *s >= '0' && *s <= '7'; s++) len++; if(len == 0) return INVALID_CHAR; copy = tr_strndup(*str, len); if(copy == NULL) return INVALID_CHAR; while(len > 0) { copy[len] = '\0'; value = (unsigned int)strtoul(copy, ©_end, 8); // strtol failed if(copy == copy_end) { value = INVALID_CHAR; break; // literal too large, try with one fewer character } else if(value > UCHAR_MAX) { len--; // all good } else { *str += (copy_end - copy); break; } } free(copy); return value; }
static char* makeURL( tr_webseed * w, const tr_file * file ) { char * ret; struct evbuffer * out = evbuffer_new( ); const char * url = w->url; const size_t url_len = strlen( url ); evbuffer_add( out, url, url_len ); /* if url ends with a '/', add the torrent name */ if( url[url_len - 1] == '/' && file->name ) tr_http_escape( out, file->name, strlen(file->name), FALSE ); ret = tr_strndup( EVBUFFER_DATA( out ), EVBUFFER_LENGTH( out ) ); evbuffer_free( out ); return ret; }
static char* replaceSubstr( const char * str, const char * in, const char * out ) { char * walk; struct evbuffer * buf = evbuffer_new( ); const size_t inlen = strlen( in ); const size_t outlen = strlen( out ); while(( walk = strstr( str, in ))) { evbuffer_add( buf, str, walk-str ); evbuffer_add( buf, out, outlen ); str = walk + inlen; } walk = tr_strndup( EVBUFFER_DATA( buf ), EVBUFFER_LENGTH( buf ) ); evbuffer_free( buf ); return walk; }
static char_class_t tr_parser_try_parse_class(const char **str, tr_parser_error_t* error_out) { const char* class_name_end; char* class_name; ptrdiff_t class_name_len; char_class_t char_class; if(str == NULL || *str == NULL) return CC_INVALID; class_name_end = tr_parser_find_next_token(*str, ":]"); if(class_name_end == NULL) return CC_INVALID; class_name_len = (class_name_end - *str); if(class_name_len == 0) { tr_parser_error(error_out, *str, "missing character class name"); return CC_INVALID; } class_name = tr_strndup(*str, class_name_len); if(class_name == NULL) { tr_fatal_error("out of memory"); return CC_INVALID; } char_class = char_class_get(class_name); if(char_class == CC_INVALID) { tr_parser_error(error_out, *str, "invalid character class `%s`", class_name); free(class_name); return CC_INVALID; } free(class_name); *str = class_name_end + 2; return char_class; }
char * tr_sys_path_dirname (const char * path, tr_error ** error) { if (path == NULL || path[0] == '\0') return tr_strdup ("."); if (!is_valid_path (path)) { set_system_error (error, ERROR_PATH_NOT_FOUND); return NULL; } const bool is_unc = is_unc_path (path); if (is_unc && path[2] == '\0') return tr_strdup (path); const char * end = path + strlen (path); while (end > path && is_slash (*(end - 1))) --end; if (end == path) return tr_strdup ("/"); const char * name = end; while (name > path && *(name - 1) != ':' && !is_slash (*(name - 1))) --name; while (name > path && is_slash (*(name - 1))) --name; if (name == path) return tr_strdup (is_unc ? "\\\\" : "."); if (name > path && *(name - 1) == ':' && *name != '\0' && !is_slash (*name)) return tr_strdup_printf ("%c:.", path[0]); return tr_strndup (path, name - path); }
char* tr_sys_path_basename(char const* path, tr_error** error) { if (path == NULL || path[0] == '\0') { return tr_strdup("."); } if (!is_valid_path(path)) { set_system_error(error, ERROR_PATH_NOT_FOUND); return NULL; } char const* end = path + strlen(path); while (end > path && is_slash(*(end - 1))) { --end; } if (end == path) { return tr_strdup("/"); } char const* name = end; while (name > path && *(name - 1) != ':' && !is_slash(*(name - 1))) { --name; } if (name == end) { return tr_strdup("/"); } return tr_strndup(name, end - name); }
static bool path_contains_no_symlinks (const char * path) { const char * p = path; while (*p != '\0') { tr_sys_path_info info; char * pathPart; const char * slashPos = strchr (p, '/'); #ifdef _WIN32 const char * backslashPos = strchr (p, '\\'); if (slashPos == NULL || (backslashPos != NULL && backslashPos < slashPos)) slashPos = backslashPos; #endif if (slashPos == NULL) slashPos = p + strlen (p) - 1; pathPart = tr_strndup (path, slashPos - path + 1); if (!tr_sys_path_get_info (pathPart, TR_SYS_PATH_NO_FOLLOW, &info, NULL) || (info.type != TR_SYS_PATH_IS_FILE && info.type != TR_SYS_PATH_IS_DIRECTORY)) { tr_free (pathPart); return false; } tr_free (pathPart); p = slashPos + 1; } return true; }
static int getfile( char ** setme, const char * root, tr_benc * path ) { int err; if( !tr_bencIsList( path ) ) { err = TR_EINVALID; } else { struct evbuffer * buf = evbuffer_new( ); int n = tr_bencListSize( path ); int i; evbuffer_add( buf, root, strlen( root ) ); for( i = 0; i < n; ++i ) { const char * str; if( tr_bencGetStr( tr_bencListChild( path, i ), &str ) && strcmp( str, ".." ) ) { evbuffer_add( buf, TR_PATH_DELIMITER_STR, 1 ); evbuffer_add( buf, str, strlen( str ) ); } } *setme = tr_strndup( EVBUFFER_DATA( buf ), EVBUFFER_LENGTH( buf ) ); /* fprintf( stderr, "[%s]\n", *setme ); */ evbuffer_free( buf ); err = 0; } return err; }
static void on_scrape_done (tr_session * session, bool did_connect, bool did_timeout, long response_code, const void * msg, size_t msglen, void * vdata) { tr_scrape_response * response; struct scrape_data * data = vdata; response = &data->response; response->did_connect = did_connect; response->did_timeout = did_timeout; dbgmsg (data->log_name, "Got scrape response for \"%s\"", response->url); if (response_code != HTTP_OK) { const char * fmt = _("Tracker gave HTTP response code %1$ld (%2$s)"); const char * response_str = tr_webGetResponseStr (response_code); response->errmsg = tr_strdup_printf (fmt, response_code, response_str); } else { tr_variant top; int64_t intVal; tr_variant * files; tr_variant * flags; size_t len; const char * str; const bool variant_loaded = !tr_variantFromBenc (&top, msg, msglen); if (getenv ("TR_CURL_VERBOSE") != NULL) { if (!variant_loaded) fprintf (stderr, "%s", "Scrape response was not in benc format\n"); else { int i, len; char * str = tr_variantToStr (&top, TR_VARIANT_FMT_JSON, &len); fprintf (stderr, "%s", "Scrape response:\n< "); for (i=0; i<len; ++i) fputc (str[i], stderr); fputc ('\n', stderr); tr_free (str); } } if (variant_loaded) { if (tr_variantDictFindStr (&top, TR_KEY_failure_reason, &str, &len)) response->errmsg = tr_strndup (str, len); if (tr_variantDictFindDict (&top, TR_KEY_flags, &flags)) if (tr_variantDictFindInt (flags, TR_KEY_min_request_interval, &intVal)) response->min_request_interval = intVal; if (tr_variantDictFindDict (&top, TR_KEY_files, &files)) { int i = 0; for (;;) { int j; tr_quark key; tr_variant * val; /* get the next "file" */ if (!tr_variantDictChild (files, i++, &key, &val)) break; /* populate the corresponding row in our response array */ for (j=0; j<response->row_count; ++j) { struct tr_scrape_response_row * row = &response->rows[j]; if (!memcmp (tr_quark_get_string(key,NULL), row->info_hash, SHA_DIGEST_LENGTH)) { if (tr_variantDictFindInt (val, TR_KEY_complete, &intVal)) row->seeders = intVal; if (tr_variantDictFindInt (val, TR_KEY_incomplete, &intVal)) row->leechers = intVal; if (tr_variantDictFindInt (val, TR_KEY_downloaded, &intVal)) row->downloads = intVal; if (tr_variantDictFindInt (val, TR_KEY_downloaders, &intVal)) row->downloaders = intVal; break; } } } } tr_variantFree (&top); } } tr_runInEventThread (session, on_scrape_done_eventthread, data); }
static void on_announce_done (tr_session * session, bool did_connect, bool did_timeout, long response_code, const void * msg, size_t msglen, void * vdata) { tr_announce_response * response; struct announce_data * data = vdata; response = &data->response; response->did_connect = did_connect; response->did_timeout = did_timeout; dbgmsg (data->log_name, "Got announce response"); if (response_code != HTTP_OK) { const char * fmt = _("Tracker gave HTTP response code %1$ld (%2$s)"); const char * response_str = tr_webGetResponseStr (response_code); response->errmsg = tr_strdup_printf (fmt, response_code, response_str); } else { tr_variant benc; const bool variant_loaded = !tr_variantFromBenc (&benc, msg, msglen); if (getenv ("TR_CURL_VERBOSE") != NULL) { if (!variant_loaded) fprintf (stderr, "%s", "Announce response was not in benc format\n"); else { int i, len; char * str = tr_variantToStr (&benc, TR_VARIANT_FMT_JSON, &len); fprintf (stderr, "%s", "Announce response:\n< "); for (i=0; i<len; ++i) fputc (str[i], stderr); fputc ('\n', stderr); tr_free (str); } } if (variant_loaded && tr_variantIsDict (&benc)) { int64_t i; size_t len; tr_variant * tmp; const char * str; const uint8_t * raw; if (tr_variantDictFindStr (&benc, TR_KEY_failure_reason, &str, &len)) response->errmsg = tr_strndup (str, len); if (tr_variantDictFindStr (&benc, TR_KEY_warning_message, &str, &len)) response->warning = tr_strndup (str, len); if (tr_variantDictFindInt (&benc, TR_KEY_interval, &i)) response->interval = i; if (tr_variantDictFindInt (&benc, TR_KEY_min_interval, &i)) response->min_interval = i; if (tr_variantDictFindStr (&benc, TR_KEY_tracker_id, &str, &len)) response->tracker_id_str = tr_strndup (str, len); if (tr_variantDictFindInt (&benc, TR_KEY_complete, &i)) response->seeders = i; if (tr_variantDictFindInt (&benc, TR_KEY_incomplete, &i)) response->leechers = i; if (tr_variantDictFindInt (&benc, TR_KEY_downloaded, &i)) response->downloads = i; if (tr_variantDictFindRaw (&benc, TR_KEY_peers6, &raw, &len)) { dbgmsg (data->log_name, "got a peers6 length of %zu", len); response->pex6 = tr_peerMgrCompact6ToPex (raw, len, NULL, 0, &response->pex6_count); } if (tr_variantDictFindRaw (&benc, TR_KEY_peers, &raw, &len)) { dbgmsg (data->log_name, "got a compact peers length of %zu", len); response->pex = tr_peerMgrCompactToPex (raw, len, NULL, 0, &response->pex_count); } else if (tr_variantDictFindList (&benc, TR_KEY_peers, &tmp)) { response->pex = listToPex (tmp, &response->pex_count); dbgmsg (data->log_name, "got a peers list with %zu entries", response->pex_count); } } if (variant_loaded) tr_variantFree (&benc); } tr_runInEventThread (session, on_announce_done_eventthread, data); }
static int tr_parser_try_parse_repeat(const char **str, unsigned int *repeat_count, tr_parser_error_t* error_out) { const char *repeat_start, *repeat_marker, *repeat_end; char *repeat_count_str; int c; if(str == NULL || *str == NULL) return INVALID_CHAR; // check for a valid repeat structure repeat_marker = tr_parser_find_next_token(*str, "*"); if(repeat_marker == NULL) return INVALID_CHAR; repeat_end = tr_parser_find_next_token(repeat_marker + 1, "]"); if(repeat_end == NULL) return INVALID_CHAR; repeat_start = *str; c = tr_parser_parse_one_char(&repeat_start, error_out); if(c == INVALID_CHAR) { tr_parser_error(error_out, repeat_start, "repetition with no character"); return INVALID_CHAR; } // copy 0 or more characters that lie between the asterisk and the closing // bracket. repeat_count_str = tr_strndup(repeat_marker + 1, repeat_end - (repeat_marker + 1)); if(repeat_count_str == NULL) { tr_fatal_error("out of memory"); return INVALID_CHAR; } // no characters means an indefinite repetition, as does a repetition count // of zero. if (*repeat_count_str == '\0') { *repeat_count = 0; } else { char* repeat_count_str_end = NULL; *repeat_count = strtoul(repeat_count_str, &repeat_count_str_end, 0); // the repetition count should end right before the closing bracket if(repeat_count_str_end != repeat_end) { tr_parser_error(error_out, repeat_count_str, "invalid number of repetitions `%.*s`", (int)(repeat_end - repeat_count_str), repeat_count_str); *repeat_count = 0; return INVALID_CHAR; } } *str = repeat_end + 1; return c; }
static void addTask( void * vtask ) { struct tr_web_task * task = vtask; const tr_session * session = task->session; if( ( session == NULL ) || ( session->web == NULL ) ) return; if( !task->resolved_host ) { dbgmsg( "couldn't resolve host for \"%s\"... task failed", task->url ); task_finish( task, 0 ); } else { CURL * e = curl_easy_init( ); struct tr_web * web = session->web; const int timeout = getTimeoutFromURL( task->url ); const long verbose = getenv( "TR_CURL_VERBOSE" ) != NULL; const char * user_agent = TR_NAME "/" SHORT_VERSION_STRING; /* insert the resolved host into the URL s.t. curl's DNS won't block * even if -- like on most OSes -- it wasn't built with C-Ares :( * "http://www.craptrackular.org/announce?key=val&key2=..." becomes * "http://127.0.0.1/announce?key=val&key2=..." */ { char * host; struct evbuffer * buf = evbuffer_new( ); char * pch = strstr( task->url, task->host ); char * tail = pch + strlen( task->host ); evbuffer_add( buf, task->url, pch - task->url ); evbuffer_add_printf( buf, "%s", task->resolved_host ); evbuffer_add_printf( buf, "%s", tail ); task->resolved_url = tr_strndup( EVBUFFER_DATA( buf ), EVBUFFER_LENGTH( buf ) ); dbgmsg( "old url: \"%s\" -- new url: \"%s\"", task->url, task->resolved_url ); evbuffer_free( buf ); /* Manually add a Host: argument that refers to the true URL */ if( ( ( task->port <= 0 ) ) || ( ( task->port == 80 ) && !strncmp( task->url, "http://", 7 ) ) || ( ( task->port == 443 ) && !strncmp( task->url, "https://", 8 ) ) ) host = tr_strdup_printf( "Host: %s", task->host ); else host = tr_strdup_printf( "Host: %s:%d", task->host, task->port ); task->slist = curl_slist_append( NULL, host ); task->slist = curl_slist_append( task->slist, "Accept:" ); curl_easy_setopt( e, CURLOPT_HTTPHEADER, task->slist ); tr_free( host ); } dbgmsg( "adding task #%lu [%s]", task->tag, task->resolved_url ? task->resolved_url : task->url ); if( !task->range && session->isProxyEnabled ) { curl_easy_setopt( e, CURLOPT_PROXY, session->proxy ); curl_easy_setopt( e, CURLOPT_PROXYAUTH, CURLAUTH_ANY ); curl_easy_setopt( e, CURLOPT_PROXYPORT, session->proxyPort ); curl_easy_setopt( e, CURLOPT_PROXYTYPE, getCurlProxyType( session->proxyType ) ); } if( !task->range && session->isProxyAuthEnabled ) { char * str = tr_strdup_printf( "%s:%s", session->proxyUsername, session->proxyPassword ); curl_easy_setopt( e, CURLOPT_PROXYUSERPWD, str ); tr_free( str ); } task->easy = e; task->multi = web->multi; /* use our own timeout instead of CURLOPT_TIMEOUT because the latter * doesn't play nicely with curl_multi. See curl bug #2501457 */ task->timer_event_isSet = TRUE; evtimer_set( &task->timer_event, task_timeout_cb, task ); tr_timerAdd( &task->timer_event, timeout, 0 ); curl_easy_setopt( e, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 ); curl_easy_setopt( e, CURLOPT_SOCKOPTFUNCTION, sockoptfunction ); curl_easy_setopt( e, CURLOPT_SOCKOPTDATA, task ); curl_easy_setopt( e, CURLOPT_WRITEDATA, task ); curl_easy_setopt( e, CURLOPT_WRITEFUNCTION, writeFunc ); curl_easy_setopt( e, CURLOPT_DNS_CACHE_TIMEOUT, MIN_DNS_CACHE_TIME ); curl_easy_setopt( e, CURLOPT_FOLLOWLOCATION, 1L ); curl_easy_setopt( e, CURLOPT_AUTOREFERER, 1L ); curl_easy_setopt( e, CURLOPT_FORBID_REUSE, 1L ); curl_easy_setopt( e, CURLOPT_MAXREDIRS, -1L ); curl_easy_setopt( e, CURLOPT_PRIVATE, task ); curl_easy_setopt( e, CURLOPT_SSL_VERIFYHOST, 0L ); curl_easy_setopt( e, CURLOPT_SSL_VERIFYPEER, 0L ); curl_easy_setopt( e, CURLOPT_URL, task->resolved_url ? task->resolved_url : task->url ); curl_easy_setopt( e, CURLOPT_USERAGENT, user_agent ); curl_easy_setopt( e, CURLOPT_VERBOSE, verbose ); if( web->haveAddr ) curl_easy_setopt( e, CURLOPT_INTERFACE, tr_ntop_non_ts( &web->addr ) ); if( task->range ) curl_easy_setopt( e, CURLOPT_RANGE, task->range ); if( curl_multi_add_handle( web->multi, e ) == CURLM_OK ) ++web->taskCount; } }
static uint64_t loadFromFile(tr_torrent* tor, uint64_t fieldsToLoad) { TR_ASSERT(tr_isTorrent(tor)); size_t len; int64_t i; char const* str; char* filename; tr_variant top; bool boolVal; uint64_t fieldsLoaded = 0; bool const wasDirty = tor->isDirty; tr_error* error = NULL; filename = getResumeFilename(tor); if (!tr_variantFromFile(&top, TR_VARIANT_FMT_BENC, filename, &error)) { tr_logAddTorDbg(tor, "Couldn't read \"%s\": %s", filename, error->message); tr_error_free(error); tr_free(filename); return fieldsLoaded; } tr_logAddTorDbg(tor, "Read resume file \"%s\"", filename); if ((fieldsToLoad & TR_FR_CORRUPT) != 0 && tr_variantDictFindInt(&top, TR_KEY_corrupt, &i)) { tor->corruptPrev = i; fieldsLoaded |= TR_FR_CORRUPT; } if ((fieldsToLoad & (TR_FR_PROGRESS | TR_FR_DOWNLOAD_DIR)) != 0 && tr_variantDictFindStr(&top, TR_KEY_destination, &str, &len) && str != NULL && *str != '\0') { bool const is_current_dir = tor->currentDir == tor->downloadDir; tr_free(tor->downloadDir); tor->downloadDir = tr_strndup(str, len); if (is_current_dir) { tor->currentDir = tor->downloadDir; } fieldsLoaded |= TR_FR_DOWNLOAD_DIR; } if ((fieldsToLoad & (TR_FR_PROGRESS | TR_FR_INCOMPLETE_DIR)) != 0 && tr_variantDictFindStr(&top, TR_KEY_incomplete_dir, &str, &len) && str != NULL && *str != '\0') { bool const is_current_dir = tor->currentDir == tor->incompleteDir; tr_free(tor->incompleteDir); tor->incompleteDir = tr_strndup(str, len); if (is_current_dir) { tor->currentDir = tor->incompleteDir; } fieldsLoaded |= TR_FR_INCOMPLETE_DIR; } if ((fieldsToLoad & TR_FR_DOWNLOADED) != 0 && tr_variantDictFindInt(&top, TR_KEY_downloaded, &i)) { tor->downloadedPrev = i; fieldsLoaded |= TR_FR_DOWNLOADED; } if ((fieldsToLoad & TR_FR_UPLOADED) != 0 && tr_variantDictFindInt(&top, TR_KEY_uploaded, &i)) { tor->uploadedPrev = i; fieldsLoaded |= TR_FR_UPLOADED; } if ((fieldsToLoad & TR_FR_MAX_PEERS) != 0 && tr_variantDictFindInt(&top, TR_KEY_max_peers, &i)) { tor->maxConnectedPeers = i; fieldsLoaded |= TR_FR_MAX_PEERS; } if ((fieldsToLoad & TR_FR_RUN) != 0 && tr_variantDictFindBool(&top, TR_KEY_paused, &boolVal)) { tor->isRunning = !boolVal; fieldsLoaded |= TR_FR_RUN; } if ((fieldsToLoad & TR_FR_ADDED_DATE) != 0 && tr_variantDictFindInt(&top, TR_KEY_added_date, &i)) { tor->addedDate = i; fieldsLoaded |= TR_FR_ADDED_DATE; } if ((fieldsToLoad & TR_FR_DONE_DATE) != 0 && tr_variantDictFindInt(&top, TR_KEY_done_date, &i)) { tor->doneDate = i; fieldsLoaded |= TR_FR_DONE_DATE; } if ((fieldsToLoad & TR_FR_ACTIVITY_DATE) != 0 && tr_variantDictFindInt(&top, TR_KEY_activity_date, &i)) { tr_torrentSetActivityDate(tor, i); fieldsLoaded |= TR_FR_ACTIVITY_DATE; } if ((fieldsToLoad & TR_FR_TIME_SEEDING) != 0 && tr_variantDictFindInt(&top, TR_KEY_seeding_time_seconds, &i)) { tor->secondsSeeding = i; fieldsLoaded |= TR_FR_TIME_SEEDING; } if ((fieldsToLoad & TR_FR_TIME_DOWNLOADING) != 0 && tr_variantDictFindInt(&top, TR_KEY_downloading_time_seconds, &i)) { tor->secondsDownloading = i; fieldsLoaded |= TR_FR_TIME_DOWNLOADING; } if ((fieldsToLoad & TR_FR_BANDWIDTH_PRIORITY) != 0 && tr_variantDictFindInt(&top, TR_KEY_bandwidth_priority, &i) && tr_isPriority(i)) { tr_torrentSetPriority(tor, i); fieldsLoaded |= TR_FR_BANDWIDTH_PRIORITY; } if ((fieldsToLoad & TR_FR_PEERS) != 0) { fieldsLoaded |= loadPeers(&top, tor); } if ((fieldsToLoad & TR_FR_FILE_PRIORITIES) != 0) { fieldsLoaded |= loadFilePriorities(&top, tor); } if ((fieldsToLoad & TR_FR_PROGRESS) != 0) { fieldsLoaded |= loadProgress(&top, tor); } if ((fieldsToLoad & TR_FR_DND) != 0) { fieldsLoaded |= loadDND(&top, tor); } if ((fieldsToLoad & TR_FR_SPEEDLIMIT) != 0) { fieldsLoaded |= loadSpeedLimits(&top, tor); } if ((fieldsToLoad & TR_FR_RATIOLIMIT) != 0) { fieldsLoaded |= loadRatioLimits(&top, tor); } if ((fieldsToLoad & TR_FR_IDLELIMIT) != 0) { fieldsLoaded |= loadIdleLimits(&top, tor); } if ((fieldsToLoad & TR_FR_FILENAMES) != 0) { fieldsLoaded |= loadFilenames(&top, tor); } if ((fieldsToLoad & TR_FR_NAME) != 0) { fieldsLoaded |= loadName(&top, tor); } /* loading the resume file triggers of a lot of changes, * but none of them needs to trigger a re-saving of the * same resume information... */ tor->isDirty = wasDirty; tr_variantFree(&top); tr_free(filename); return fieldsLoaded; }
static void handle_upload( struct evhttp_request * req, struct tr_rpc_server * server ) { if( req->type != EVHTTP_REQ_POST ) { send_simple_response( req, 405, NULL ); } else { const char * content_type = evhttp_find_header( req->input_headers, "Content-Type" ); const char * query = strchr( req->uri, '?' ); const int paused = query && strstr( query + 1, "paused=true" ); const char * in = (const char *) EVBUFFER_DATA( req->input_buffer ); size_t inlen = EVBUFFER_LENGTH( req->input_buffer ); const char * boundary_key = "boundary="; const char * boundary_key_begin = strstr( content_type, boundary_key ); const char * boundary_val = boundary_key_begin ? boundary_key_begin + strlen( boundary_key ) : "arglebargle"; char * boundary = tr_strdup_printf( "--%s", boundary_val ); const size_t boundary_len = strlen( boundary ); const char * delim = tr_memmem( in, inlen, boundary, boundary_len ); while( delim ) { size_t part_len; const char * part = delim + boundary_len; inlen -= ( part - in ); in = part; delim = tr_memmem( in, inlen, boundary, boundary_len ); part_len = delim ? (size_t)( delim - part ) : inlen; if( part_len ) { char * text = tr_strndup( part, part_len ); if( strstr( text, "filename=\"" ) ) { const char * body = strstr( text, "\r\n\r\n" ); if( body ) { char * b64; size_t body_len; tr_benc top, *args; struct evbuffer * json = tr_getBuffer( ); body += 4; /* walk past the \r\n\r\n */ body_len = part_len - ( body - text ); if( body_len >= 2 && !memcmp( &body[body_len - 2], "\r\n", 2 ) ) body_len -= 2; tr_bencInitDict( &top, 2 ); args = tr_bencDictAddDict( &top, "arguments", 2 ); tr_bencDictAddStr( &top, "method", "torrent-add" ); b64 = tr_base64_encode( body, body_len, NULL ); tr_bencDictAddStr( args, "metainfo", b64 ); tr_bencDictAddInt( args, "paused", paused ); tr_bencSaveAsJSON( &top, json ); tr_rpc_request_exec_json( server->session, EVBUFFER_DATA( json ), EVBUFFER_LENGTH( json ), NULL, NULL ); tr_releaseBuffer( json ); tr_free( b64 ); tr_bencFree( &top ); } } tr_free( text ); } } tr_free( boundary ); /* use xml here because json responses to file uploads is trouble. * see http://www.malsup.com/jquery/form/#sample7 for details */ evhttp_add_header( req->output_headers, "Content-Type", "text/xml; charset=UTF-8" ); send_simple_response( req, HTTP_OK, NULL ); } }