static void _wi_timer_thread(wi_runtime_instance_t *argument) { wi_pool_t *pool; wi_timer_t *timer, *fire_timer; wi_time_interval_t interval, diff; wi_boolean_t locked; pool = wi_pool_init(wi_pool_alloc()); while(true) { fire_timer = NULL; locked = true; interval = wi_time_interval(); timer = _wi_timer_first_timer(); if(!timer) { locked = wi_condition_lock_lock_when_condition(_wi_timer_lock, 1, 0.0); timer = _wi_timer_first_timer(); interval = wi_time_interval(); if(timer && timer->fire - interval <= _WI_TIMER_MINIMUM_INTERVAL) fire_timer = timer; } else { diff = timer->fire - interval; if(diff <= _WI_TIMER_MINIMUM_INTERVAL) { fire_timer = timer; locked = false; } else { locked = wi_condition_lock_lock_when_condition(_wi_timer_lock, 1, diff); if(!locked) fire_timer = _wi_timer_first_timer(); } } if(locked) wi_condition_lock_unlock_with_condition(_wi_timer_lock, 0); if(fire_timer) { _wi_timer_invalidate(fire_timer); wi_timer_fire(fire_timer); if(timer->repeats) _wi_timer_schedule(fire_timer); } wi_pool_drain(pool); } wi_release(pool); }
static void wd_update_transfers(wi_timer_t *timer) { wi_list_node_t *node, *next_node; wd_transfer_t *transfer; wi_time_interval_t interval; wi_boolean_t update = false; if(wi_list_count(wd_transfers) > 0) { interval = wi_time_interval(); wi_list_wrlock(wd_transfers); for(node = wi_list_first_node(wd_transfers); node; node = next_node) { next_node = wi_list_node_next_node(node); transfer = wi_list_node_data(node); if(transfer->state == WD_TRANSFER_WAITING) { if(transfer->queue_time + WD_TRANSFERS_WAITING_INTERVAL < interval) { wi_list_remove_node(wd_transfers, node); update = true; } } } wi_list_unlock(wd_transfers); if(update) wd_transfers_update_queue(); } }
wi_boolean_t wi_condition_lock_lock_when_condition(wi_condition_lock_t *lock, int condition, wi_time_interval_t time) { struct timespec ts; int err; if((err = pthread_mutex_lock(&lock->mutex)) != 0) WI_ASSERT(false, "pthread_mutex_lock: %s", strerror(err)); if(lock->condition != condition) { if(time > 0.0) { ts = wi_dtots(wi_time_interval() + time); do { err = pthread_cond_timedwait(&lock->cond, &lock->mutex, &ts); if(err != 0 && err != ETIMEDOUT) WI_ASSERT(false, "pthread_cond_timedwait: %s", strerror(err)); } while(lock->condition != condition && err != ETIMEDOUT); if(err == ETIMEDOUT) { if((err = pthread_mutex_unlock(&lock->mutex)) != 0) WI_ASSERT(false, "pthread_mutex_unlock: %s", strerror(err)); return false; } } else { do { if((err = pthread_cond_wait(&lock->cond, &lock->mutex)) != 0) WI_ASSERT(false, "pthread_cond_wait: %s", strerror(err)); } while(lock->condition != condition); } } return true; }
static void wd_users_update_idle(wi_timer_t *timer) { wi_enumerator_t *enumerator; wd_user_t *user; wi_time_interval_t interval; wi_dictionary_rdlock(wd_users); if(wi_dictionary_count(wd_users) > 0) { interval = wi_time_interval(); enumerator = wi_dictionary_data_enumerator(wd_users); while((user = wi_enumerator_next_data(enumerator))) { wi_recursive_lock_lock(user->user_lock); if(user->state == WD_USER_LOGGED_IN && !user->idle && user->idle_time + wd_settings.idletime < interval) { user->idle = true; wd_user_broadcast_status(user); } wi_recursive_lock_unlock(user->user_lock); } } wi_dictionary_unlock(wd_users); }
void wr_draw_transfers(wi_boolean_t force) { static wi_time_interval_t update; wi_enumerator_t *enumerator; wi_mutable_string_t *string; wi_string_t *status; wr_transfer_t *transfer; wi_time_interval_t interval; wi_uinteger_t i = 0; interval = wi_time_interval(); if(!force && interval - update < 1.0) return; update = interval; wi_terminal_set_scroll(wr_terminal, wi_make_range(1 + wi_array_count(wr_transfers), wi_terminal_size(wr_terminal).height - 4)); enumerator = wi_array_data_enumerator(wr_transfers); while((transfer = wi_enumerator_next_data(enumerator))) { wi_terminal_move(wr_terminal, wi_make_point(0, i + 1)); wi_terminal_clear_line(wr_terminal); if(transfer->state == WR_TRANSFER_RUNNING && interval - transfer->start_time > 0.0) { transfer->speed = ((double) transfer->total_transferred - transfer->total_offset) / (interval - transfer->start_time); status = wi_string_with_format(WI_STR("%@/%@, %@/s"), wr_files_string_for_size(transfer->total_transferred), wr_files_string_for_size(transfer->total_size), wr_files_string_for_size(transfer->speed)); } else if(transfer->state == WR_TRANSFER_QUEUED) { status = wi_string_with_format(WI_STR("queued at %u"), transfer->queue); } else { status = wi_string_with_cstring("waiting"); } string = wi_mutable_string_with_format(WI_STR("%u %3.0f%% %@"), transfer->tid, transfer->total_size > 0 ? 100 * ((double) transfer->total_transferred / (double) transfer->total_size) : 0, transfer->name); wi_terminal_adjust_string_to_fit_width(wr_terminal, string); wi_mutable_string_delete_characters_from_index(string, wi_string_length(string) - wi_string_length(status)); wi_mutable_string_append_string(string, status); wi_terminal_printf(wr_terminal, WI_STR("%@"), string); i++; } wr_terminal_reset_location(); }
void wi_tests_run_test(const char *name, wi_run_test_func_t *function) { wi_pool_t *pool; wi_assert_handler_func_t *handler; wi_time_interval_t interval; if(_wi_tests_name) { if(wi_string_index_of_string(wi_string_with_utf8_string(name), _wi_tests_name, 0) == WI_NOT_FOUND) return; } if(wi_string_has_suffix(wi_string_with_utf8_string(name), WI_STR("initialize"))) { (*function)(); } else { _wi_tests_current_test = _wi_test_init_with_function(_wi_test_alloc(), wi_string_with_utf8_string(name), function); handler = wi_assert_handler; wi_assert_handler = _wi_tests_assert_handler; interval = wi_time_interval(); pool = wi_pool_init(wi_pool_alloc()); if(setjmp(_wi_tests_jmp_buf) == 0) (*_wi_tests_current_test->function)(); wi_release(pool); _wi_tests_current_test->interval = wi_time_interval() - interval; wi_assert_handler = handler; if(_wi_tests_current_test->passed) { wi_log_info(WI_STR("Test \"%@\" passed (%.3f seconds)"), _wi_tests_current_test->name, _wi_tests_current_test->interval); wi_tests_passed++; } else { wi_log_info(WI_STR("Test \"%@\" failed (%.3f seconds)"), _wi_tests_current_test->name, _wi_tests_current_test->interval); wi_tests_failed++; } wi_release(_wi_tests_current_test); } }
static wi_string_t * wd_tempban_description(wi_runtime_instance_t *instance) { wd_tempban_t *tempban = instance; return wi_string_with_format(WI_STR("<%@ %p>{ip = %@, time_remaining = %.0f}"), wi_runtime_class_name(tempban), tempban, tempban->ip, wi_time_interval() - tempban->interval); }
static void _wi_timer_schedule(wi_timer_t *timer) { timer->fire = wi_time_interval() + timer->interval; wi_array_wrlock(_wi_timers); wi_mutable_array_add_data_sorted(_wi_timers, timer, _wi_timer_compare); wi_array_unlock(_wi_timers); timer->scheduled = true; }
static wd_transfer_t * wd_transfer_init(wd_transfer_t *transfer) { transfer->queue = -1; transfer->queue_lock = wi_condition_lock_init_with_condition(wi_condition_lock_alloc(), 0); transfer->queue_time = wi_time_interval(); transfer->state = WD_TRANSFER_QUEUED; transfer->finished_lock = wi_condition_lock_init_with_condition(wi_condition_lock_alloc(), 0); return transfer; }
static void wr_msg_202(wi_array_t *arguments) { wi_time_interval_t interval; if(wr_ping_time > 0.0) { interval = wi_time_interval() - wr_ping_time; wr_wprintf_prefix(wr_console_window, WI_STR("Ping reply after %.2fms"), interval * 1000.0); wr_ping_time = 0.0; } }
void wt_control_thread(wi_runtime_instance_t *argument) { wi_pool_t *pool; wt_client_t *client = argument; wi_string_t *string; wi_socket_state_t state; wi_uinteger_t i = 0; pool = wi_pool_init(wi_pool_alloc()); wt_client_set(client); while(client->state <= WT_CLIENT_STATE_SAID_HELLO) { do { state = wi_socket_wait(client->socket, 0.1); } while(state == WI_SOCKET_TIMEOUT && client->state <= WT_CLIENT_STATE_SAID_HELLO); if(client->state > WT_CLIENT_STATE_SAID_HELLO) { /* invalid state */ break; } if(state == WI_SOCKET_ERROR) { if(wi_error_code() == EINTR) { /* got a signal */ continue; } else { /* error in TCP communication */ wi_log_err(WI_STR("Could not read from %@: %m"), client->ip); break; } } string = wi_socket_read_to_string(client->socket, 0.0, WI_STR(WT_MESSAGE_SEPARATOR_STR)); if(!string || wi_string_length(string) == 0) { if(!string) wi_log_info(WI_STR("Could not read from %@: %m"), client->ip); break; } wt_parse_command(string); if(++i % 10 == 0) wi_pool_drain(pool); } wi_log_info(WI_STR("Disconnect from %@ after %.2fs"), client->ip, wi_time_interval() - client->connect_time); wi_release(pool); }
static wd_transfer_t * wd_transfer_init_with_user(wd_transfer_t *transfer, wd_user_t *user) { transfer->state = WD_TRANSFER_QUEUED; transfer->queue_time = wi_time_interval(); transfer->user = wi_retain(user); transfer->hash = wi_retain(wi_data_sha1(wi_data_with_random_bytes(1024))); transfer->socket_lock = wi_lock_init(wi_lock_alloc()); transfer->state_lock = wi_condition_lock_init_with_condition(wi_condition_lock_alloc(), transfer->state); transfer->fd = -1; return transfer; }
void wr_draw_transfers(wi_boolean_t force) { static wi_time_interval_t update; wi_list_node_t *node; wi_string_t *string, *status; wr_transfer_t *transfer; wi_time_interval_t interval; unsigned int i = 0; interval = wi_time_interval(); if(!force && interval - update < 1.0) return; update = interval; wi_terminal_set_scroll(wr_terminal, wi_make_range(1 + wi_list_count(wr_transfers), wi_terminal_size(wr_terminal).height - 3)); WI_LIST_FOREACH(wr_transfers, node, transfer) { wi_terminal_move(wr_terminal, wi_make_point(0, i + 1)); wi_terminal_clear_line(wr_terminal); if(transfer->state == WR_TRANSFER_RUNNING && interval - transfer->start_time > 0.0) { transfer->speed = ((double) transfer->transferred - transfer->offset) / (interval - transfer->start_time); status = wi_string_with_format(WI_STR("%@/%@, %@/s"), wr_files_string_for_size(transfer->transferred), wr_files_string_for_size(transfer->size), wr_files_string_for_size(transfer->speed)); } else if(transfer->state == WR_TRANSFER_QUEUED) { status = wi_string_with_format(WI_STR("queued at %u"), transfer->queue); } else { status = wi_string_with_cstring("waiting"); } string = wi_string_with_format(WI_STR("%u %3.0f%% %@"), transfer->tid, transfer->size > 0 ? 100 * ((double) transfer->transferred / (double) transfer->size) : 0, transfer->name); wi_terminal_adjust_string_to_fit_width(wr_terminal, string); wi_string_delete_characters_from_index(string, wi_string_length(string) - wi_string_length(status)); wi_string_append_string(string, status); wi_terminal_printf(wr_terminal, WI_STR("%@"), string); i++; }
wi_boolean_t wd_banlist_add_ban(wi_string_t *ip, wi_date_t *expiration_date, wd_user_t *user, wi_p7_message_t *message) { wi_dictionary_t *results; wi_string_t *string; // check for negative expiration date if(expiration_date && wi_date_time_interval(expiration_date) - wi_time_interval() < 1.0) { wi_log_error(WI_STR("Could not add ban for \"%@\" expiring at %@: Negative expiration date"), ip, wi_date_string_with_format(expiration_date, WI_STR("%Y-%m-%d %H:%M:%S"))); wd_user_reply_internal_error(user, WI_STR("Ban has negative expiration date"), message); return false; } // check if an entry already exists for this IP address in the database results = wi_sqlite3_execute_statement(wd_database, WI_STR("SELECT ip FROM banlist WHERE ip = ?"), ip, NULL); // prevents for database statement error if(!results) { wi_log_error(WI_STR("Could not execute database statement: %m")); wd_user_reply_internal_error(user, wi_error_string(), message); return false; } // if an entry already exists, replay ban_exists error message and stop if(wi_dictionary_count(results) > 0) { wd_user_reply_error(user, WI_STR("wired.error.ban_exists"), message); return false; } // check if the ban is limited in time or not string = expiration_date ? wi_date_sqlite3_string(expiration_date) : NULL; // finally add the new ban entry to the database if(!wi_sqlite3_execute_statement(wd_database, WI_STR("INSERT INTO banlist " "(ip, expiration_date) " "VALUES " "(?, ?)"), ip, string, NULL)) { wi_log_error(WI_STR("Could not execute database statement: %m")); wd_user_reply_internal_error(user, wi_error_string(), message); return false; } return true; }
static void wr_msg_400(wi_array_t *arguments) { wi_address_t *address; wr_transfer_t *transfer; transfer = wr_transfers_transfer_with_path(WI_ARRAY(arguments, 0)); if(!transfer) return; address = wi_copy(wr_address); wi_address_set_port(address, wi_address_port(address) + 1); transfer->state = WR_TRANSFER_RUNNING; transfer->offset = wi_string_uint64(WI_ARRAY(arguments, 1)); transfer->transferred = transfer->offset; transfer->key = wi_retain(WI_ARRAY(arguments, 2)); transfer->start_time = wi_time_interval(); transfer->socket = wi_socket_init_with_address(wi_socket_alloc(), address, WI_SOCKET_TCP); wi_socket_set_interactive(transfer->socket, false); if(!wi_socket_connect(transfer->socket, wr_socket_context, 15.0)) { wr_printf_prefix(WI_STR("Could not connect to %@: %m"), wi_address_string(address)); wr_transfer_stop(transfer); goto end; } wi_file_seek(transfer->file, transfer->offset); if(transfer->type == WR_TRANSFER_DOWNLOAD) { wi_socket_set_direction(transfer->socket, WI_SOCKET_READ); wr_runloop_add_socket(transfer->socket, wr_runloop_download_callback); } else { wi_socket_set_direction(transfer->socket, WI_SOCKET_WRITE); wr_runloop_add_socket(transfer->socket, wr_runloop_upload_callback); } wr_send_command_on_socket(transfer->socket, WI_STR("TRANSFER %#@"), transfer->key); wr_printf_prefix(WI_STR("Starting transfer of \"%@\""), transfer->name); wr_draw_transfers(true); end: wi_release(address); }
static wd_user_t * wd_user_init_with_socket(wd_user_t *user, wi_socket_t *socket) { wi_address_t *address; user->uid = wd_user_next_uid(); user->socket = wi_retain(socket); user->state = WD_USER_CONNECTED; user->login_time = wi_time_interval(); user->idle_time = user->login_time; address = wi_socket_address(socket); user->ip = wi_retain(wi_address_string(address)); user->host = wi_retain(wi_address_hostname(address)); user->user_lock = wi_recursive_lock_init(wi_recursive_lock_alloc()); user->socket_lock = wi_lock_init(wi_lock_alloc()); user->transfers_queue = wi_array_init(wi_array_alloc()); return user; }
static void wt_update_servers(wi_timer_t *timer) { wi_enumerator_t *enumerator; wt_server_t *server; void *key; wi_time_interval_t interval, update; wi_boolean_t changed = false; wi_dictionary_wrlock(wt_servers); if(wi_dictionary_count(wt_servers) > 0) { interval = wi_time_interval(); enumerator = wi_array_data_enumerator(wi_dictionary_all_keys(wt_servers)); while((key = wi_enumerator_next_data(enumerator))) { server = wi_dictionary_data_for_key(wt_servers, key); update = server->update_time > 0.0 ? server->update_time : server->register_time; if(interval - update > wt_settings.minupdatetime) { wi_log_warn(WI_STR("Deleting \"%@\" with URL %@: Last update %.0f seconds ago considered too slow"), server->name, server->url, interval - update); wt_servers_remove_stats_for_server(server); wi_dictionary_remove_data_for_key(wt_servers, key); changed = true; } } } wi_dictionary_unlock(wt_servers); if(changed) { wi_lock_lock(wt_status_lock); wt_write_status(true); wi_lock_unlock(wt_status_lock); wt_servers_write_file(); } }
static void wd_parse_command(wi_string_t *buffer) { wd_client_t *client = wd_client(); wi_array_t *arguments; wi_string_t *command; unsigned int index; wi_parse_wired_command(buffer, &command, &arguments); index = wd_command_index(command); if(index == WI_NOT_FOUND) { wd_reply(501, WI_STR("Command Not Recognized")); return; } if(client->state < wd_commands[index].state) return; if(wi_array_count(arguments) < wd_commands[index].args) { wd_reply(503, WI_STR("Syntax Error")); return; } if(wd_commands[index].activate) { client->idle_time = wi_time_interval(); if(client->idle) { client->idle = false; wd_broadcast_lock(); wd_client_broadcast_status(client); wd_broadcast_unlock(); } } ((*wd_commands[index].action) (arguments)); }
static void wt_update_servers(wi_timer_t *timer) { wi_list_node_t *node, *next_node; wt_server_t *server; wi_time_interval_t interval, update; unsigned int count = 0; if(wi_list_count(wt_servers) > 0) { interval = wi_time_interval(); wi_list_rdlock(wt_servers); for(node = wi_list_first_node(wt_servers); node; node = next_node) { next_node = wi_list_node_next_node(node); server = wi_list_node_data(node); update = server->update_time > 0.0 ? server->update_time : server->register_time; if(interval - update > wt_settings.minupdatetime) { wi_log_warn(WI_STR("Deleting \"%@\" with URL %@: Last update %.0f seconds ago considered too slow"), server->name, server->url, interval - update); wt_server_stats_remove(server); wi_list_remove_node(wt_servers, node); count++; } } wi_list_unlock(wt_servers); if(count > 0) { wi_lock_lock(wt_status_lock); wt_write_status(true); wi_lock_unlock(wt_status_lock); wt_write_servers(); } } }
wi_time_interval_t wi_date_time_interval_since_now(wi_date_t *date) { return wi_time_interval() - date->interval; }
wi_date_t * wi_date_init(wi_date_t *date) { return wi_date_init_with_time_interval(date, wi_time_interval()); }
void wt_read_servers(void) { FILE *fp; wt_server_packed_t server_packed; wt_server_t *server; char magic[5]; wi_time_interval_t interval, update; unsigned int version, count = 0; wi_lock_lock(wt_servers_lock); fp = fopen(wi_string_cstring(wt_settings.servers), "r"); if(!fp) { if(errno != ENOENT) { wi_log_err(WI_STR("Could not open %@: %s"), wt_settings.servers, strerror(errno)); } goto end; } if(fread(&magic, 4, 1, fp) != 1 || strncmp(magic, WT_SERVER_MAGIC, 4) != 0) { wi_log_warn(WI_STR("Could not read %@: %s"), wt_settings.servers, "Not a server file"); goto end; } if(fread(&version, 4, 1, fp) != 1 || version != WT_SERVER_VERSION) { wi_log_warn(WI_STR("Could not read %@: %s"), wt_settings.servers, "Wrong version"); goto end; } wi_log_info(WI_STR("Reading %@"), wt_settings.servers); interval = wi_time_interval(); wi_list_wrlock(wt_servers); while((fread(&server_packed, sizeof(wt_server_packed_t), 1, fp)) > 0) { update = server_packed.update_time > 0 ? server_packed.update_time : server_packed.register_time; if(interval - update < wt_settings.minupdatetime) { server = wt_server_init_with_packed(wt_server_alloc(), server_packed); wi_lock_lock(wt_status_lock); wt_current_servers++; wt_current_users += server->users; wt_current_files += server->files; wt_current_size += server->size; wi_lock_unlock(wt_status_lock); wi_list_append_data(wt_servers, server); wi_release(server); count++; } } wi_list_unlock(wt_servers); if(count > 0) { wi_lock_lock(wt_status_lock); wt_write_status(true); wi_lock_unlock(wt_status_lock); wi_log_info(WI_STR("Loaded %u %s from %@"), count, count == 1 ? "server" : "servers", wt_settings.servers); } end: wi_lock_unlock(wt_servers_lock); }
void wd_transfers_update_queue(void) { wi_list_node_t *client_node, *transfer_node; wd_transfer_t *transfer; wd_client_t *client; unsigned int position_downloads, position_uploads; unsigned int client_downloads, client_uploads; unsigned int all_downloads, all_uploads; unsigned int position; wi_list_rdlock(wd_clients); wi_list_rdlock(wd_transfers); /* count total number of active transfers */ all_downloads = all_uploads = 0; WI_LIST_FOREACH(wd_transfers, transfer_node, transfer) { if(transfer->state == WD_TRANSFER_WAITING || transfer->state == WD_TRANSFER_RUNNING) { if(transfer->type == WD_TRANSFER_DOWNLOAD) all_downloads++; else all_uploads++; } } WI_LIST_FOREACH(wd_clients, client_node, client) { position_downloads = position_uploads = 0; client_downloads = client_uploads = 0; /* count number of active transfers for this client */ WI_LIST_FOREACH(wd_transfers, transfer_node, transfer) { if(transfer->client == client) { if(transfer->state == WD_TRANSFER_WAITING || transfer->state == WD_TRANSFER_RUNNING) { if(transfer->type == WD_TRANSFER_DOWNLOAD) client_downloads++; else client_uploads++; } } } /* traverse the transfer queue */ WI_LIST_FOREACH(wd_transfers, transfer_node, transfer) { if(transfer->type == WD_TRANSFER_DOWNLOAD) position_downloads++; else position_uploads++; if(transfer->state == WD_TRANSFER_QUEUED && transfer->client == client) { /* calculate number in queue */ if(transfer->type == WD_TRANSFER_DOWNLOAD) { if(all_downloads >= wd_settings.totaldownloads) position = position_downloads - all_downloads; else if(client_downloads >= wd_settings.clientdownloads) position = position_downloads - client_downloads; else position = 0; } else { if(all_uploads >= wd_settings.totaluploads) position = position_uploads - all_uploads; else if(client_uploads >= wd_settings.clientuploads) position = position_uploads - client_uploads; else position = 0; } if(position > 0) { if(transfer->queue != position) { /* queue position changed */ transfer->queue = position; wd_client_lock_socket(client); wd_sreply(client->socket, 401, WI_STR("%#@%c%u"), transfer->path, WD_FIELD_SEPARATOR, transfer->queue); wd_client_unlock_socket(client); } } else { /* queue position reached 0 */ transfer->queue = 0; transfer->state = WD_TRANSFER_WAITING; transfer->queue_time = wi_time_interval(); wd_client_lock_socket(client); wd_sreply(client->socket, 400, WI_STR("%#@%c%llu%c%#@"), transfer->path, WD_FIELD_SEPARATOR, transfer->offset, WD_FIELD_SEPARATOR, transfer->hash); wd_client_unlock_socket(client); if(transfer->type == WD_TRANSFER_DOWNLOAD) { all_downloads++; client_downloads++; } else { all_uploads++; client_uploads++; } } } } }
static void wd_transfers_update_queue(void) { wi_mutable_set_t *users; wi_mutable_array_t *sorted_users, *transfers_queue, *failed_transfers; wd_transfer_t *transfer; wd_user_t *user; wi_uinteger_t position; wi_uinteger_t i, count; wi_uinteger_t total_downloads, total_uploads, user_downloads, user_uploads, active_downloads, active_uploads; wi_boolean_t queue; wi_array_rdlock(wd_transfers); total_downloads = wd_settings.totaldownloads; user_downloads = wd_settings.clientdownloads; total_uploads = wd_settings.totaluploads; user_uploads = wd_settings.clientuploads; active_downloads = 0; active_uploads = 0; failed_transfers = wi_array_init(wi_mutable_array_alloc()); users = wi_set_init(wi_mutable_set_alloc()); count = wi_array_count(wd_transfers); for(i = 0; i < count; i++) { transfer = WI_ARRAY(wd_transfers, i); if(wd_transfer_state(transfer) == WD_TRANSFER_QUEUED) { wi_mutable_array_add_data(wd_user_transfers_queue(transfer->user), transfer); wi_mutable_set_add_data(users, transfer->user); } wd_user_clear_downloads(transfer->user); wd_user_clear_uploads(transfer->user); } for(i = 0; i < count; i++) { transfer = WI_ARRAY(wd_transfers, i); if(wd_transfer_state(transfer) == WD_TRANSFER_RUNNING) { if(transfer->type == WD_TRANSFER_DOWNLOAD) { active_downloads++; wd_user_increase_downloads(transfer->user); } else { active_uploads++; wd_user_increase_uploads(transfer->user); } } } count = wi_set_count(users); if(count > 0) { sorted_users = wi_autorelease(wi_mutable_copy(wi_set_all_data(users))); wi_mutable_array_sort(sorted_users, wd_transfers_compare_user); position = 1; while(count > 0) { for(i = 0; i < count; i++) { user = WI_ARRAY(sorted_users, i); transfers_queue = wd_user_transfers_queue(user); transfer = WI_ARRAY(transfers_queue, 0); if(transfer->type == WD_TRANSFER_DOWNLOAD) { queue = ((total_downloads > 0 && active_downloads >= total_downloads) || (user_downloads > 0 && wd_user_downloads(transfer->user) >= user_downloads)); } else { queue = ((total_uploads > 0 && active_uploads >= total_uploads) || (user_uploads > 0 && wd_user_uploads(transfer->user) >= user_uploads)); } if(queue) { if(transfer->queue != position) { transfer->queue = position; wd_user_lock_socket(transfer->user); wd_sreply(wd_user_socket(transfer->user), 401, WI_STR("%#@%c%u"), transfer->path, WD_FIELD_SEPARATOR, transfer->queue); wd_user_unlock_socket(transfer->user); } position++; } else { transfer->queue = 0; transfer->waiting_time = wi_time_interval(); wd_transfer_set_state(transfer, WD_TRANSFER_WAITING); if(wd_transfer_open(transfer)) { wd_transfer_create_timer(transfer); wd_user_lock_socket(transfer->user); wd_sreply(wd_user_socket(transfer->user), 400, WI_STR("%#@%c%llu%c%#@"), transfer->path, WD_FIELD_SEPARATOR, transfer->offset, WD_FIELD_SEPARATOR, transfer->hash); wd_user_unlock_socket(transfer->user); } else { wd_user_lock_socket(transfer->user); wd_sreply(wd_user_socket(transfer->user), 500, WI_STR("Command Failed")); wd_user_unlock_socket(transfer->user); wi_mutable_array_add_data(failed_transfers, transfer); } } wi_mutable_array_remove_data_at_index(transfers_queue, 0); if(wi_array_count(transfers_queue) == 0) { wi_mutable_array_remove_data_at_index(sorted_users, i); i--; count--; } } } } wi_mutable_array_remove_data_in_array(wd_transfers, failed_transfers); wi_array_unlock(wd_transfers); wi_release(users); wi_release(failed_transfers); }
static wi_boolean_t wd_transfer_download(wd_transfer_t *transfer) { wi_pool_t *pool; wi_socket_t *socket; wi_p7_socket_t *p7_socket; wd_account_t *account; char buffer[WD_TRANSFER_BUFFER_SIZE]; wi_socket_state_t state; wi_time_interval_t timeout, interval, speedinterval, statusinterval, accountinterval; wi_file_offset_t sendbytes, speedbytes, statsbytes; wi_uinteger_t i, transfers; ssize_t readbytes; int sd; wi_boolean_t data, result; wd_user_state_t user_state; interval = wi_time_interval(); speedinterval = interval; statusinterval = interval; accountinterval = interval; speedbytes = 0; statsbytes = 0; i = 0; socket = wd_user_socket(transfer->user); sd = wi_socket_descriptor(socket); p7_socket = wd_user_p7_socket(transfer->user); account = wd_user_account(transfer->user); data = true; result = true; wd_transfers_note_statistics(WD_TRANSFER_DOWNLOAD, WD_TRANSFER_STATISTICS_ADD, 0); wi_dictionary_rdlock(wd_transfers_user_downloads); transfers = (wi_integer_t) wi_dictionary_data_for_key(wd_transfers_user_downloads, transfer->key); wi_dictionary_unlock(wd_transfers_user_downloads); pool = wi_pool_init(wi_pool_alloc()); wd_user_lock_socket(transfer->user); while(wd_user_state(transfer->user) == WD_USER_LOGGED_IN) { if(data && transfer->remainingdatasize == 0) data = false; if(!data && transfer->remainingrsrcsize == 0) break; readbytes = read(data ? transfer->datafd : transfer->rsrcfd, buffer, sizeof(buffer)); if(readbytes <= 0) { if(readbytes < 0) { wi_log_error(WI_STR("Could not read download from \"%@\": %m"), data ? transfer->realdatapath : transfer->realrsrcpath, strerror(errno)); } result = false; break; } timeout = wi_time_interval(); do { user_state = wd_user_state(transfer->user); state = wi_socket_wait_descriptor(sd, 0.1, false, true); if(state == WI_SOCKET_TIMEOUT) { if(wi_time_interval() - timeout >= 30.0) break; } } while(state == WI_SOCKET_TIMEOUT && user_state == WD_USER_LOGGED_IN); if(state == WI_SOCKET_ERROR || wi_time_interval() - timeout >= 30.0) { wi_log_error(WI_STR("Could not wait for download to %@: %@"), wd_user_identifier(transfer->user), (state == WI_SOCKET_ERROR) ? wi_error_string() : WI_STR("Timed out")); result = false; break; } if(user_state != WD_USER_LOGGED_IN) { result = false; break; } if(data) { sendbytes = (transfer->remainingdatasize < (wi_file_offset_t) readbytes) ? transfer->remainingdatasize : (wi_file_offset_t) readbytes; } else { sendbytes = (transfer->remainingrsrcsize < (wi_file_offset_t) readbytes) ? transfer->remainingrsrcsize : (wi_file_offset_t) readbytes; } if(!wi_p7_socket_write_oobdata(p7_socket, 30.0, buffer, sendbytes)) { wi_log_error(WI_STR("Could not write download to %@: %m"), wd_user_identifier(transfer->user)); result = false; break; } if(data) transfer->remainingdatasize -= sendbytes; else transfer->remainingrsrcsize -= sendbytes; interval = wi_time_interval(); transfer->transferred += sendbytes; transfer->actualtransferred += sendbytes; speedbytes += sendbytes; statsbytes += sendbytes; transfer->speed = speedbytes / (interval - speedinterval); wd_transfer_limit_speed(transfer, wd_transfers_total_download_speed, wd_account_transfer_download_speed_limit(account), wd_current_downloads, transfers, speedbytes, interval, speedinterval); if(interval - speedinterval > 30.0) { speedbytes = 0; speedinterval = interval; } if(interval - statusinterval > wd_current_downloads) { wd_transfers_note_statistics(WD_TRANSFER_DOWNLOAD, WD_TRANSFER_STATISTICS_DATA, statsbytes); statsbytes = 0; statusinterval = interval; } if(interval - accountinterval > 15.0) { account = wd_user_account(transfer->user); accountinterval = interval; wi_dictionary_rdlock(wd_transfers_user_downloads); transfers = (wi_integer_t) wi_dictionary_data_for_key(wd_transfers_user_downloads, transfer->key); wi_dictionary_unlock(wd_transfers_user_downloads); } if(++i % 1000 == 0) wi_pool_drain(pool); } wd_user_unlock_socket(transfer->user); wi_release(pool); wd_transfers_note_statistics(WD_TRANSFER_DOWNLOAD, WD_TRANSFER_STATISTICS_REMOVE, statsbytes); return result; }
static void wt_cmd_register(wi_array_t *arguments) { wt_client_t *client = wt_client(); wi_enumerator_t *enumerator; wi_array_t *array; wi_address_t *address, *hostaddress; wi_url_t *url; wi_string_t *hostname; wt_server_t *server; wi_boolean_t failed = false, passed; uint32_t bandwidth; url = wi_autorelease(wi_url_init_with_string(wi_url_alloc(), WI_ARRAY(arguments, 1))); hostname = wi_url_host(url); address = wi_socket_address(client->socket); if(!wi_url_is_valid(url)) { /* invalid URL */ if(wt_settings.strictlookup) { wt_reply(503, WI_STR("Syntax Error")); wi_log_warn(WI_STR("Register from %@ as \"%@\" URL %@ aborted: %s"), client->ip, WI_ARRAY(arguments, 2), WI_ARRAY(arguments, 1), "Invalid URL"); return; } failed = true; goto failed; } if(wi_ip_version(hostname) > 0) { /* hostname is numeric, compare with source address */ if(!wi_is_equal(hostname, client->ip)) { /* IP mismatch */ if(wt_settings.strictlookup) { wt_reply(530, WI_STR("Address Mismatch")); wi_log_warn(WI_STR("Register from %@ as \"%@\" URL %@ denied: %s"), client->ip, WI_ARRAY(arguments, 2), WI_ARRAY(arguments, 1), "IP mismatch"); return; } failed = true; goto failed; } } else { /* hostname is symbolic */ if(wt_settings.lookup) { /* look up and compare with source address */ passed = false; array = wi_host_addresses(wi_host_with_string(hostname)); if(array) { enumerator = wi_array_data_enumerator(array); while((hostaddress = wi_enumerator_next_data(enumerator))) { if(wi_is_equal(hostaddress, address)) { passed = true; break; } } } if(!passed) { /* lookup failed */ if(wt_settings.strictlookup) { wt_reply(531, WI_STR("Address Mismatch")); wi_log_warn(WI_STR("Register from %@ as \"%@\" URL %@ denied: %s"), client->ip, WI_ARRAY(arguments, 2), WI_ARRAY(arguments, 1), "Lookup failed"); return; } failed = true; goto failed; } } if(wt_settings.reverselookup) { /* reverse look up and compare to hostname */ if(!wi_is_equal(wi_address_hostname(address), hostname)) { /* reverse lookup failed */ if(wt_settings.strictlookup) { wt_reply(531, WI_STR("Address Mismatch")); wi_log_warn(WI_STR("Register from %@ as \"%@\" URL % denied: %@"), client->ip, WI_ARRAY(arguments, 2), WI_ARRAY(arguments, 1), "Reverse lookup failed"); return; } failed = true; goto failed; } } } failed: /* get bandwidth */ bandwidth = wi_string_uint32(WI_ARRAY(arguments, 3)); /* bandwidth too low? */ if(wt_settings.minbandwidth > 0 && bandwidth < wt_settings.minbandwidth) { wt_reply(516, WI_STR("Permission Denied")); wi_log_warn(WI_STR("Register from %@ as \"%@\" URL %@ denied: Bandwidth %.0f Kbps considered too low"), client->ip, WI_ARRAY(arguments, 2), WI_ARRAY(arguments, 1), bandwidth / 128.0); return; } /* bandwidth too high? */ if(wt_settings.maxbandwidth > 0 && bandwidth > wt_settings.maxbandwidth) { wt_reply(516, WI_STR("Permission Denied")); wi_log_warn(WI_STR("Register from %@ as \"%@\" URL %@ denied: Bandwidth %.0f Kbps considered too high"), client->ip, WI_ARRAY(arguments, 2), WI_ARRAY(arguments, 1), bandwidth / 128.0); return; } /* is there an existing server from this host? */ server = wt_servers_server_with_ip(client->ip); if(server) { if(server->port == wi_url_port(url)) { /* remove existing server in preparation for re-registration */ wt_servers_remove_stats_for_server(server); wt_servers_remove_server(server); } else { /* multiple servers from the same IP allowed? */ if(!wt_settings.allowmultiple) { wt_reply(530, WI_STR("Address Registered")); wi_log_warn(WI_STR("Register from %@ as \"%@\" URL %@ denied: %s"), client->ip, WI_ARRAY(arguments, 2), WI_ARRAY(arguments, 1), "A server from the same address is already registered"); return; } } } /* rewrite URL if host verification failed */ if(failed) { wi_url_set_scheme(url, WI_STR("wired")); wi_url_set_host(url, client->ip); if(wi_string_length(WI_ARRAY(arguments, 1)) == 0) wi_log_info(WI_STR("Rewriting URL to %@"), wi_url_string(url)); else wi_log_info(WI_STR("Rewriting URL from %@ to %@"), WI_ARRAY(arguments, 1), wi_url_string(url)); } /* create new server */ server = wt_server_init(wt_server_alloc()); server->key = wi_retain(wi_string_sha1(wi_autorelease(wi_string_init_random_string_with_length(wi_string_alloc(), 1024)))); server->port = wi_url_port(url); server->bandwidth = bandwidth; server->register_time = wi_time_interval(); server->update_time = 0.0; server->ip = wi_retain(client->ip); server->category = wt_servers_category_is_valid(WI_ARRAY(arguments, 0)) ? wi_retain(WI_ARRAY(arguments, 0)) : wi_string_init(wi_string_alloc()); server->name = wi_retain(WI_ARRAY(arguments, 2)); server->description = wi_retain(WI_ARRAY(arguments, 4)); server->url = wi_copy(wi_url_string(url)); wt_servers_add_server(server); wt_servers_add_stats_for_server(server); /* reply 700 */ wt_reply(700, WI_STR("%@"), server->key); wi_log_info(WI_STR("Registered \"%@\" with URL %@"), server->name, server->url); wt_servers_write_file(); wi_release(server); }
static void wd_transfer_upload(wd_transfer_t *transfer) { wi_pool_t *pool; wi_string_t *path; wd_account_t *account; char buffer[WD_TRANSFER_BUFFER_SIZE]; wi_time_interval_t timeout, interval, speedinterval, statusinterval, accountinterval; wi_socket_state_t state; ssize_t result, speedbytes, statsbytes; int sd, bytes; pool = wi_pool_init(wi_pool_alloc()); /* start upload */ wi_log_info(WI_STR("Receiving \"%@\" from %@"), transfer->path, wd_user_identifier(transfer->user)); wi_socket_set_direction(transfer->socket, WI_SOCKET_READ); // if(!wi_socket_set_blocking(transfer->socket, false)) // wi_log_warn(WI_STR("Could not set non-blocking for %@: %m"), wd_user_ip(transfer->user)); sd = wi_socket_descriptor(transfer->socket); speedinterval = statusinterval = accountinterval = wi_time_interval(); speedbytes = statsbytes = 0; account = wd_user_account(transfer->user); /* update status */ wi_lock_lock(wd_status_lock); wd_current_uploads++; wd_total_uploads++; wd_write_status(true); wi_lock_unlock(wd_status_lock); while(wd_transfer_state(transfer) == WD_TRANSFER_RUNNING && transfer->transferred < transfer->size) { /* wait to read */ timeout = 0.0; do { state = wi_socket_wait_descriptor(sd, 0.1, true, false); if(state == WI_SOCKET_TIMEOUT) { timeout += 0.1; if(timeout >= 30.0) break; } } while(state == WI_SOCKET_TIMEOUT && wd_transfer_state(transfer) == WD_TRANSFER_RUNNING); if(state == WI_SOCKET_ERROR) { wi_log_err(WI_STR("Could not wait for upload from %@: %m"), wd_user_ip(transfer->user)); break; } if(timeout >= 30.0) { wi_log_err(WI_STR("Timed out waiting to read upload from %@"), wd_user_ip(transfer->user)); break; } /* read data */ bytes = wi_socket_read_buffer(transfer->socket, 30.0, buffer, sizeof(buffer)); if(bytes <= 0) { if(bytes < 0) { wi_log_err(WI_STR("Could not read upload from %@: %m"), wd_user_ip(transfer->user)); } break; } if((wi_file_offset_t) bytes > transfer->size - transfer->transferred) bytes = transfer->size - transfer->transferred; /* write data */ result = write(transfer->fd, buffer, bytes); if(result <= 0) { if(result < 0) { wi_log_err(WI_STR("Could not write upload to %@: %s"), transfer->realpath, strerror(errno)); } break; } /* update counters */ interval = wi_time_interval(); transfer->transferred += bytes; speedbytes += bytes; statsbytes += bytes; /* update speed */ transfer->speed = speedbytes / (interval - speedinterval); wd_transfer_limit_upload_speed(transfer, account, speedbytes, interval, speedinterval); if(interval - speedinterval > 30.0) { speedbytes = 0; speedinterval = interval; } /* update status */ if(interval - statusinterval > wd_current_uploads) { wi_lock_lock(wd_status_lock); wd_uploads_traffic += statsbytes; wd_write_status(false); wi_lock_unlock(wd_status_lock); statsbytes = 0; statusinterval = interval; } /* update account */ if(interval - accountinterval > 15.0) { account = wd_user_account(transfer->user); accountinterval = interval; } wi_pool_drain(pool); } wi_log_info(WI_STR("Received %@/%@ (%llu/%llu bytes) of \"%@\" from %@"), wd_files_string_for_bytes(transfer->transferred - transfer->offset), wd_files_string_for_bytes(transfer->size - transfer->offset), transfer->transferred - transfer->offset, transfer->size - transfer->offset, transfer->path, wd_user_identifier(transfer->user)); /* update status */ wd_transfer_set_state(transfer, WD_TRANSFER_STOPPED); wi_lock_lock(wd_status_lock); wd_current_uploads--; wd_uploads_traffic += statsbytes; wd_write_status(true); wi_lock_unlock(wd_status_lock); if(transfer->transferred == transfer->size) { path = wi_string_by_deleting_path_extension(transfer->realpath); if(wi_fs_rename_path(transfer->realpath, path)) { path = wi_string_by_appending_path_extension(transfer->path, WI_STR(WD_TRANSFERS_PARTIAL_EXTENSION)); wd_files_move_comment(path, transfer->path); } else { wi_log_warn(WI_STR("Could not move %@ to %@: %m"), transfer->realpath, path); } } wi_release(pool); }
static wd_tempban_t * wd_tempban_init_with_ip(wd_tempban_t *tempban, wi_string_t *ip) { tempban->ip = wi_retain(ip); tempban->interval = wi_time_interval(); return tempban; }