wi_p7_message_t * wi_p7_socket_read_message(wi_p7_socket_t *p7_socket, wi_time_interval_t timeout) { wi_p7_message_t *p7_message; wi_string_t *prefix = NULL; char length_buffer[_WI_P7_SOCKET_LENGTH_SIZE]; uint32_t length; if(p7_socket->serialization == WI_P7_UNKNOWN || p7_socket->serialization == WI_P7_BINARY) { if(wi_socket_read_buffer(p7_socket->socket, timeout, length_buffer, sizeof(length_buffer)) <= 0) return NULL; length = wi_read_swap_big_to_host_int32(length_buffer, 0); if(p7_socket->serialization == WI_P7_UNKNOWN) { if(length == _WI_P7_SOCKET_XML_MAGIC) { p7_socket->serialization = WI_P7_XML; prefix = WI_STR("<?xm"); } else if(length <= _WI_P7_SOCKET_BINARY_MAGIC_SIZE) { p7_socket->serialization = WI_P7_BINARY; } } } if(p7_socket->serialization == WI_P7_BINARY) p7_message = _wi_p7_socket_read_binary_message(p7_socket, timeout, length); else if(p7_socket->serialization == WI_P7_XML) p7_message = _wi_p7_socket_read_xml_message(p7_socket, timeout, prefix); else { wi_error_set_libwired_p7_error(WI_ERROR_P7_HANDSHAKEFAILED, WI_STR("Invalid data from remote host")); return NULL; } if(!p7_message) return NULL; if(p7_message->binary_size == 0) { wi_error_set_libwired_p7_error(WI_ERROR_P7_INVALIDMESSAGE, WI_STR("Invalid data from remote host")); return NULL; } wi_p7_message_deserialize(p7_message); wi_log_info(WI_STR("Received %@"), p7_message); wi_log_info(WI_STR("Received %llu raw bytes, %llu processed bytes, compressed to %.2f%%"), p7_socket->read_raw_bytes, p7_socket->read_processed_bytes, ((double) p7_socket->read_raw_bytes / (double) p7_socket->read_processed_bytes) * 100.0); return p7_message; }
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 void wd_cmd_pass(wi_array_t *arguments) { wd_client_t *client = wd_client(); wi_string_t *password; wd_chat_t *chat; if(client->state != WD_CLIENT_STATE_GAVE_USER) return; client->account = wi_retain(wd_accounts_read_user_and_group(client->login)); if(!client->account) { wd_reply(510, WI_STR("Login Failed")); wi_log_info(WI_STR("Login from %@/%@/%@ failed: %@"), client->nick, client->login, client->ip, WI_STR("No such account")); return; } password = WI_ARRAY(arguments, 0); if(!wi_is_equal(client->account->password, password)) { wd_reply(510, WI_STR("Login Failed")); wi_log_info(WI_STR("Login from %@/%@/%@ failed: %@"), client->nick, client->login, client->ip, WI_STR("Wrong password")); return; } wi_log_info(WI_STR("Login from %@/%@/%@ succeeded"), client->nick, client->login, client->ip); wi_lock_lock(client->flag_lock); client->admin = (client->account->kick_users || client->account->ban_users); client->state = WD_CLIENT_STATE_LOGGED_IN; wi_lock_unlock(client->flag_lock); wi_lock_lock(wd_status_lock); wd_current_users++; wd_total_users++; wd_write_status(true); wi_lock_unlock(wd_status_lock); wd_reply(201, WI_STR("%u"), client->uid); chat = wd_chat_with_cid(WD_PUBLIC_CID); wd_chat_add_client(chat, client); }
static void wd_control_accept_thread(wi_runtime_instance_t *argument) { wi_pool_t *pool; wi_socket_t *socket = argument; wi_string_t *ip; wd_user_t *user; pool = wi_pool_init(wi_pool_alloc()); ip = wi_address_string(wi_socket_address(socket)); if(!wi_socket_accept_tls(socket, wd_control_socket_tls, 30.0)) { wi_log_err(WI_STR("Could not accept a TLS connection for %@: %m"), ip); goto end; } if(!wi_socket_set_timeout(socket, 30.0)) wi_log_warn(WI_STR("Could not set timeout for %@: %m"), ip); wi_socket_set_direction(socket, WI_SOCKET_READ); wi_log_info(WI_STR("Connect from %@"), ip); user = wd_user_with_socket(socket); wd_users_add_user(user); wd_users_set_user_for_thread(user); wd_command_loop_for_user(user); end: wi_socket_close(socket); wi_release(pool); }
wi_boolean_t wi_settings_read_file(wi_settings_t *settings) { wi_file_t *file; wi_string_t *string; file = wi_file_for_reading(wi_settings_config_path); if(!file) { wi_log_err(WI_STR("Could not open %@: %s"), wi_settings_config_path, strerror(errno)); return false; } wi_log_info(WI_STR("Reading %@"), wi_settings_config_path); _wi_settings_clear(settings); settings->file = wi_settings_config_path; settings->line = 0; while((string = wi_file_read_line(file))) { settings->line++; if(wi_string_length(string) > 0 && !wi_string_has_prefix(string, WI_STR("#"))) _wi_settings_parse_setting(settings, string); } settings->file = NULL; return true; }
void wi_tests_start(void) { _wi_tests_start_date = wi_date_init(wi_date_alloc()); wi_tests_passed = wi_tests_failed = 0; wi_log_info(WI_STR("Tests started at %@"), wi_date_string_with_format(_wi_tests_start_date, WI_STR("%Y-%m-%d %H:%M:%S"))); }
static void wd_cmd_hello(wi_array_t *arguments) { wd_client_t *client = wd_client(); wi_string_t *string; if(client->state != WD_CLIENT_STATE_CONNECTED) return; if(wd_ip_is_banned(client->ip)) { wd_reply(511, WI_STR("Banned")); wi_log_info(WI_STR("Connection from %@ denied, host is banned"), client->ip); client->state = WD_CLIENT_STATE_DISCONNECTED; return; } string = wi_date_iso8601_string(wd_start_date); wd_reply(200, WI_STR("%#@%c%#@%c%#@%c%#@%c%#@%c%u%c%llu"), wd_server_version_string, WD_FIELD_SEPARATOR, wd_protocol_version_string, WD_FIELD_SEPARATOR, wd_settings.name, WD_FIELD_SEPARATOR, wd_settings.description, WD_FIELD_SEPARATOR, string, WD_FIELD_SEPARATOR, wd_files_count, WD_FIELD_SEPARATOR, wd_files_size); client->state = WD_CLIENT_STATE_SAID_HELLO; }
wi_boolean_t wi_settings_read_file(wi_settings_t *settings, wi_boolean_t chroot) { wi_file_t *file; wi_string_t *path, *string; wi_boolean_t result = true; path = wi_full_path(wi_settings_config_path); file = wi_file_for_reading(path); if(!file) { wi_log_err(WI_STR("Could not open %@: %s"), path, strerror(errno)); return false; } wi_log_info(WI_STR("Reading %@"), path); _wi_settings_clear(settings); settings->file = path; settings->line = 0; settings->chroot = chroot; while((string = wi_file_read_line(file))) { settings->line++; if(wi_string_length(string) > 0 && !wi_string_has_prefix(string, WI_STR("#"))) { if(!_wi_settings_parse_setting(settings, string)) result = false; } } settings->file = NULL; return result; }
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); } }
void wi_tests_stop_and_report(void) { wi_uinteger_t tests; tests = wi_tests_passed + wi_tests_failed; wi_log_info(WI_STR("Tests stopped at %@"), wi_date_string_with_format(_wi_tests_start_date, WI_STR("%Y-%m-%d %H:%M:%S"))); wi_log_info(WI_STR("%lu %@ passed (%.1f%%), %lu failed (%.1f%%) in %.3f seconds"), wi_tests_passed, wi_tests_passed == 1 ? WI_STR("test") : WI_STR("tests"), tests > 0 ? ((double) wi_tests_passed / (double) tests) * 100.0 : 0.0, wi_tests_failed, tests > 0 ? ((double) wi_tests_failed / (double) tests) * 100.0 : 0.0, wi_date_time_interval_since_now(_wi_tests_start_date)); wi_release(_wi_tests_start_date); }
void wd_banlist_initialize(void) { wd_banlist_create_tables(); if(wi_fs_path_exists(WI_STR("banlist"), NULL)) { if(wd_banlist_convert_banlist(WI_STR("banlist"))) { wi_log_info(WI_STR("Migrated banlist to database")); wi_fs_delete_path(WI_STR("banlist")); } } }
void wr_client_reconnect(void) { wr_connected = false; wr_reconnecting = true; wi_log_info(WI_STR("Reconnecting in 10 seconds...")); sleep(10); wr_client_connect(wb_hostname, wb_port, wb_login, wb_password); wr_reconnecting = false; }
void wd_trackers_register(wi_boolean_t update) { if(wi_array_count(wd_trackers) > 0) { wi_release(wd_trackers_guest_account); wd_trackers_guest_account = wi_retain(wd_accounts_read_user_and_group(WI_STR("guest"))); wi_log_info(WI_STR("Registering with trackers...")); if(!wi_thread_create_thread(wd_trackers_register_thread, wi_number_with_bool(update))) wi_log_err(WI_STR("Could not create a register thread: %m")); } }
wi_boolean_t wi_p7_socket_write_message(wi_p7_socket_t *p7_socket, wi_time_interval_t timeout, wi_p7_message_t *p7_message) { wi_boolean_t result; wi_p7_message_serialize(p7_message); wi_log_info(WI_STR("Sending %@"), p7_message); if(p7_socket->serialization == WI_P7_BINARY) result = _wi_p7_socket_write_binary_message(p7_socket, timeout, p7_message); else result = _wi_p7_socket_write_xml_message(p7_socket, timeout, p7_message); if(!result) return false; wi_log_info(WI_STR("Sent %llu raw bytes, %llu processed bytes, compressed to %.2f%%"), p7_socket->sent_raw_bytes, p7_socket->sent_processed_bytes, ((double) p7_socket->sent_raw_bytes / (double) p7_socket->sent_processed_bytes) * 100.0); return true; }
static void wd_control_listen_thread(wi_runtime_instance_t *argument) { wi_pool_t *pool; wi_socket_t *socket; wi_address_t *address; wi_string_t *ip; wd_user_t *user; pool = wi_pool_init(wi_pool_alloc()); while(wd_running) { wi_pool_drain(pool); /* accept new user */ socket = wi_socket_accept_multiple(wd_control_sockets, wd_control_socket_context, 30.0, &address); if(!address) { wi_log_err(WI_STR("Could not accept a connection: %m")); continue; } ip = wi_address_string(address); if(!socket) { wi_log_err(WI_STR("Could not accept a connection for %@: %m"), ip); continue; } wi_socket_set_direction(socket, WI_SOCKET_READ); wi_log_info(WI_STR("Connect from %@"), ip); /* spawn a user thread */ user = wd_user_with_socket(socket); if(!wi_thread_create_thread(wd_control_thread, user)) wi_log_err(WI_STR("Could not create a thread for %@: %m"), ip); } wi_release(pool); }
void wr_client_disconnect(void) { if(wr_connected) { wi_log_info(WI_STR("Connection to %@ closed"), wi_address_string(wi_socket_address(wr_socket))); wr_connected = false; } wr_runloop_remove_socket(wr_socket); wi_socket_close(wr_socket); wi_release(wr_p7_socket); wi_release(wr_socket); wi_release(wr_server); wi_release(wr_password); wr_chats_clear(); wr_users_clear(); }
int main(int argc, const char **argv) { wi_pool_t *pool; wi_string_t *string; int ch, facility; wi_boolean_t no_chroot, test_config; /* init libwired */ wi_initialize(); wi_load(argc, argv); pool = wi_pool_init(wi_pool_alloc()); wi_log_startup = true; wi_log_syslog = true; wi_log_syslog_facility = LOG_DAEMON; /* init core systems */ wt_init_version(); wt_status_lock = wi_lock_init(wi_lock_alloc()); wt_start_date = wi_date_init(wi_date_alloc()); /* set defaults */ wi_root_path = wi_string_init_with_cstring(wi_string_alloc(), WT_ROOT); wi_settings_config_path = wi_string_init_with_cstring(wi_string_alloc(), WT_CONFIG_PATH); no_chroot = false; test_config = false; /* parse command line switches */ while((ch = getopt(argc, (char * const *) argv, "46Dd:f:hi:L:ls:tuVv")) != -1) { switch(ch) { case '4': wt_address_family = WI_ADDRESS_IPV4; break; case '6': wt_address_family = WI_ADDRESS_IPV6; break; case 'D': wt_daemonize = false; wi_log_stderr = true; break; case 'd': wi_release(wi_root_path); wi_root_path = wi_string_init_with_cstring(wi_string_alloc(), optarg); break; case 'f': wi_release(wi_settings_config_path); wi_settings_config_path = wi_string_init_with_cstring(wi_string_alloc(), optarg); break; case 'i': wi_log_limit = wi_string_uint32(wi_string_with_cstring(optarg)); break; case 'L': wi_log_syslog = false; wi_log_file = true; wi_release(wi_log_path); wi_log_path = wi_string_init_with_cstring(wi_string_alloc(), optarg); break; case 'l': wi_log_level++; break; case 's': string = wi_string_with_cstring(optarg); facility = wi_log_syslog_facility_with_name(string); if(facility < 0) { wi_log_err(WI_STR("Could not find syslog facility \"%@\": %m"), string); } wi_log_syslog_facility = facility; break; case 't': test_config = true; break; case 'u': no_chroot = true; break; case 'V': case 'v': wt_version(); break; case '?': case 'h': default: wt_usage(); break; } } /* open log */ wi_log_open(); /* init subsystems */ wt_init_ssl(); wt_init_clients(); wt_init_servers(); /* read the config file */ wt_settings_chroot = !no_chroot; wt_init_settings(); if(!wt_read_config()) exit(1); /* change root directory */ if(!no_chroot) { if(!wi_change_root()) wi_log_err(WI_STR("Could not change root to %@: %m"), wi_root_path); } /* apply config */ wt_apply_config(); if(test_config) { printf("Config OK\n"); exit(0); } /* dump command line */ if(wi_log_level >= WI_LOG_DEBUG) { wi_log_debug(WI_STR("Started as %@ %@"), wi_process_path(wi_process()), wi_array_components_joined_by_string(wi_process_arguments(wi_process()), WI_STR(" "))); } /* init tracker */ wi_log_info(WI_STR("Starting Wired Tracker version %@"), wt_version_string); wt_init_tracker(); /* detach (don't chdir, don't close i/o channels) */ if(wt_daemonize) { if(!wi_daemon()) wi_log_err(WI_STR("Could not become a daemon: %m")); } /* switch user/group */ wi_switch_user(wt_settings.user, wt_settings.group); /* create tracker threads after privilege drop */ wt_init_signals(); wt_block_signals(); wt_schedule_servers(); wt_fork_tracker(); wt_write_pid(); wt_write_status(true); wi_log_startup = false; wi_release(pool); pool = wi_pool_init(wi_pool_alloc()); /* enter the signal handling thread in the main thread */ wt_signal_thread(NULL); /* dropped out */ wt_cleanup(); wi_log_close(); wi_release(pool); return 0; }
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_control_thread(wi_runtime_instance_t *argument) { wi_pool_t *pool; wd_client_t *client = argument; wi_string_t *string; unsigned int i = 0; int state; pool = wi_pool_init(wi_pool_alloc()); wd_clients_add_client(client); wd_client_set(client); while(client->state <= WD_CLIENT_STATE_LOGGED_IN) { if(!pool) pool = wi_pool_init(wi_pool_alloc()); if(client->buffer_offset == 0) { do { state = wi_socket_wait(client->socket, 0.1); } while(state == 0 && client->state <= WD_CLIENT_STATE_LOGGED_IN); if(client->state > WD_CLIENT_STATE_LOGGED_IN) { /* invalid state */ break; } if(state < 0) { 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; } } } wd_client_lock_socket(client); string = wi_socket_read_to_string(client->socket, 0.0, WI_STR(WD_MESSAGE_SEPARATOR_STR)); wd_client_unlock_socket(client); if(!string || wi_string_length(string) == 0) { if(!string) wi_log_info(WI_STR("Could not read from %@: %m"), client->ip); break; } wd_parse_command(string); if(++i % 10 == 0) { wi_release(pool); pool = NULL; } } /* announce parting if client disconnected by itself */ if(client->state == WD_CLIENT_STATE_LOGGED_IN) { client->state = WD_CLIENT_STATE_DISCONNECTED; wd_broadcast_lock(); wd_client_broadcast_leave(client, WD_PUBLIC_CID); wd_broadcast_unlock(); } /* update status for clients logged in and above */ if(client->state >= WD_CLIENT_STATE_LOGGED_IN) { wi_lock_lock(wd_status_lock); wd_current_users--; wd_write_status(true); wi_lock_unlock(wd_status_lock); } wi_log_info(WI_STR("Disconnect from %@"), client->ip); wi_socket_close(client->socket); wd_clients_remove_client(client); wi_release(pool); }
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); }
void wr_client_connect(wi_string_t *hostname, wi_uinteger_t port, wi_string_t *login, wi_string_t *password) { wi_enumerator_t *enumerator; wi_array_t *addresses; wi_address_t *address; wi_p7_socket_t *p7_socket; wi_p7_message_t *message; wi_socket_t *socket; wi_string_t *ip; wr_server_t *server; wi_log_info(WI_STR("Connecting to %@..."), hostname); if(port == 0) port = WR_PORT; if(!login) login = WI_STR("guest"); if(!password) password = WI_STR(""); addresses = wi_host_addresses(wi_host_with_string(hostname)); if(!addresses) { wi_log_info(WI_STR("Could not resolve \"%@\": %m"), hostname); return; } enumerator = wi_array_data_enumerator(addresses); while((address = wi_enumerator_next_data(enumerator))) { ip = wi_address_string(address); wi_log_info(WI_STR("Trying %@ at port %u..."), ip, port); wi_address_set_port(address, port); socket = wi_autorelease(wi_socket_init_with_address(wi_socket_alloc(), address, WI_SOCKET_TCP)); if(!socket) { wi_log_info(WI_STR("Could not open a socket to %@: %m"), ip); continue; } wi_socket_set_interactive(socket, true); if(!wi_socket_connect(socket, 10.0)) { wi_log_info(WI_STR("Could not connect to %@: %m"), ip); continue; } p7_socket = wi_autorelease(wi_p7_socket_init_with_socket(wi_p7_socket_alloc(), socket, wr_p7_spec)); if(!wi_p7_socket_connect(p7_socket, 10.0, WI_P7_COMPRESSION_DEFLATE | WI_P7_ENCRYPTION_RSA_AES256_SHA1 | WI_P7_CHECKSUM_SHA1, WI_P7_BINARY, login, wi_string_sha1(password))) { wi_log_info(WI_STR("Could not connect to %@: %m"), ip); continue; } wi_log_info(WI_STR("Connected using %@/%u bits, logging in..."), wi_cipher_name(wi_p7_socket_cipher(p7_socket)), wi_cipher_bits(wi_p7_socket_cipher(p7_socket))); server = wr_client_login(p7_socket, login, password); if(!server) break; wi_log_info(WI_STR("Logged in, welcome to %@"), wr_server_name(server)); message = wi_p7_message_with_name(WI_STR("wired.chat.join_chat"), wr_p7_spec); wi_p7_message_set_uint32_for_name(message, wr_chat_id(wr_public_chat), WI_STR("wired.chat.id")); wr_client_write_message(p7_socket, message); wr_connected = true; wr_socket = wi_retain(socket); wr_p7_socket = wi_retain(p7_socket); wr_server = wi_retain(server); wr_password = wi_retain(password); wi_socket_set_direction(wr_socket, WI_SOCKET_READ); wr_runloop_add_socket(wr_socket, &wr_runloop_server_callback); // suscribe bot watchers wb_bot_subscribe_watchers(wb_bot); break; } }
int main(int argc, const char **argv) { wi_mutable_array_t *arguments; wi_pool_t *pool; wi_string_t *string, *root_path; int ch, facility; wi_boolean_t test_config, daemonize, change_directory, switch_user; /* init libwired */ wi_initialize(); wi_load(argc, argv); pool = wi_pool_init(wi_pool_alloc()); wi_log_syslog = true; wi_log_syslog_facility = LOG_DAEMON; /* init core systems */ wt_version_init(); wt_status_lock = wi_lock_init(wi_lock_alloc()); wt_start_date = wi_date_init(wi_date_alloc()); /* set defaults */ root_path = WI_STR(WT_ROOT); wi_settings_config_path = wi_string_init_with_cstring(wi_string_alloc(), WT_CONFIG_PATH); test_config = false; daemonize = true; change_directory = true; switch_user = true; /* init reexec argument list */ arguments = wi_array_init(wi_mutable_array_alloc()); /* parse command line switches */ while((ch = getopt(argc, (char * const *) argv, "46Dd:f:hi:L:ls:tuVvXx")) != -1) { switch(ch) { case '4': wt_address_family = WI_ADDRESS_IPV4; break; case '6': wt_address_family = WI_ADDRESS_IPV6; break; case 'D': daemonize = false; wi_log_stderr = true; break; case 'd': root_path = wi_string_with_cstring(optarg); break; case 'f': wi_release(wi_settings_config_path); wi_settings_config_path = wi_string_init_with_cstring(wi_string_alloc(), optarg); break; case 'i': wi_log_limit = wi_string_uint32(wi_string_with_cstring(optarg)); break; case 'L': wi_log_syslog = false; wi_log_file = true; wi_release(wi_log_path); wi_log_path = wi_string_init_with_cstring(wi_string_alloc(), optarg); break; case 'l': wi_log_level++; break; case 's': string = wi_string_with_cstring(optarg); facility = wi_log_syslog_facility_with_name(string); if(facility < 0) wi_log_fatal(WI_STR("Could not find syslog facility \"%@\": %m"), string); wi_log_syslog_facility = facility; break; case 't': test_config = true; break; case 'u': break; case 'V': case 'v': wt_version(); break; case 'X': daemonize = false; break; case 'x': daemonize = false; change_directory = false; switch_user = false; break; case '?': case 'h': default: wt_usage(); break; } wi_mutable_array_add_data(arguments, wi_string_with_format(WI_STR("-%c"), ch)); if(optarg) wi_mutable_array_add_data(arguments, wi_string_with_cstring(optarg)); } /* detach */ if(daemonize) { wi_mutable_array_add_data(arguments, WI_STR("-X")); switch(wi_fork()) { case -1: wi_log_fatal(WI_STR("Could not fork: %m")); break; case 0: if(!wi_execv(wi_string_with_cstring(argv[0]), arguments)) wi_log_fatal(WI_STR("Could not execute %s: %m"), argv[0]); break; default: _exit(0); break; } } wi_release(arguments); /* change directory */ if(change_directory) { if(!wi_fs_change_directory(root_path)) wi_log_error(WI_STR("Could not change directory to %@: %m"), root_path); } /* open log */ wi_log_open(); /* init subsystems */ wt_ssl_init(); wt_clients_init(); wt_servers_init(); /* read the config file */ wt_settings_init(); if(!wt_settings_read_config()) exit(1); /* apply settings */ wt_settings_apply_settings(); if(test_config) { printf("Config OK\n"); exit(0); } /* dump command line */ wi_log_info(WI_STR("Started as %@ %@"), wi_process_path(wi_process()), wi_array_components_joined_by_string(wi_process_arguments(wi_process()), WI_STR(" "))); /* init tracker */ wi_log_info(WI_STR("Starting Wired Tracker version %@"), wt_version_string); wt_tracker_init(); /* switch user/group */ if(switch_user) wi_switch_user(wt_settings.user, wt_settings.group); /* create tracker threads after privilege drop */ wt_signals_init(); wt_block_signals(); wt_servers_schedule(); wt_tracker_create_threads(); wt_write_pid(); wt_write_status(true); /* clean up pool after startup */ wi_pool_drain(pool); /* enter the signal handling thread in the main thread */ wt_signal_thread(NULL); /* dropped out */ wt_cleanup(); wi_log_close(); wi_release(pool); return 0; }
void wd_server_init(void) { wi_enumerator_t *enumerator; wi_array_t *array, *addresses; wi_address_t *address; wi_socket_t *control_socket, *transfer_socket; wi_string_t *ip, *string; wi_address_family_t family; wd_control_sockets = wi_array_init(wi_mutable_array_alloc()); wd_transfer_sockets = wi_array_init(wi_mutable_array_alloc()); addresses = wi_array_init(wi_mutable_array_alloc()); if(wi_array_count(wd_settings.address) > 0) { /* listen on configured addresses */ wi_array_rdlock(wd_settings.address); enumerator = wi_array_data_enumerator(wd_settings.address); while((string = wi_enumerator_next_data(enumerator))) { array = wi_host_addresses(wi_host_with_string(string)); if(array) wi_mutable_array_add_data_from_array(addresses, array); else wi_log_err(WI_STR("Could not resolve \"%@\": %m"), string); } wi_array_unlock(wd_settings.address); } else { /* add wildcard addresses */ wi_mutable_array_add_data(addresses, wi_address_wildcard_for_family(WI_ADDRESS_IPV6)); wi_mutable_array_add_data(addresses, wi_address_wildcard_for_family(WI_ADDRESS_IPV4)); } enumerator = wi_array_data_enumerator(addresses); while((address = wi_enumerator_next_data(enumerator))) { ip = wi_address_string(address); family = wi_address_family(address); /* force address family? */ if(wd_address_family != WI_ADDRESS_NULL && family != wd_address_family) continue; /* create sockets */ wi_address_set_port(address, wd_settings.port); control_socket = wi_autorelease(wi_socket_init_with_address(wi_socket_alloc(), address, WI_SOCKET_TCP)); wi_address_set_port(address, wd_settings.port + 1); transfer_socket = wi_autorelease(wi_socket_init_with_address(wi_socket_alloc(), address, WI_SOCKET_TCP)); if(!control_socket || !transfer_socket) { wi_log_warn(WI_STR("Could not create socket for %@: %m"), ip); continue; } /* listen on sockets */ if(!wi_socket_listen(control_socket)) { wi_log_warn(WI_STR("Could not listen on %@ port %u: %m"), ip, wi_address_port(wi_socket_address(control_socket))); continue; } if(!wi_socket_listen(transfer_socket)) { wi_log_warn(WI_STR("Could not listen on %@ port %u: %m"), ip, wi_address_port(wi_socket_address(transfer_socket))); continue; } wi_socket_set_interactive(control_socket, true); wi_socket_set_interactive(transfer_socket, false); /* add to list of sockets */ wi_mutable_array_add_data(wd_control_sockets, control_socket); wi_mutable_array_add_data(wd_transfer_sockets, transfer_socket); wi_log_info(WI_STR("Listening on %@ ports %d-%d"), ip, wd_settings.port, wd_settings.port + 1); } if(wi_array_count(wd_control_sockets) == 0 || wi_array_count(wd_transfer_sockets) == 0) wi_log_fatal(WI_STR("No addresses available for listening")); wi_release(addresses); #ifdef HAVE_DNS_SD_H if(wd_settings.zeroconf) wd_server_register_dnssd(); #endif }
static void wd_server_register_dnssd_reply(DNSServiceRef client, DNSServiceFlags flags, DNSServiceErrorType error, const char *name, const char *regtype, const char *domain, void *context) { wi_log_info(WI_STR("DNS service discovery reply for %s.%s%s: %d"), name, regtype, domain, error); }
void wd_server_apply_settings(void) { wi_data_t *data; wi_string_t *hostname; /* reload banner */ if(wd_settings.banner) { if(wd_settings.banner_changed) { data = wi_data_init_with_contents_of_file(wi_data_alloc(), wd_settings.banner); if(data) { wi_release(wd_banner); wd_banner = wi_retain(wi_data_base64(data)); } else { wi_log_err(WI_STR("Could not open %@: %m"), wd_settings.banner); } wi_release(data); } } else { wi_release(wd_banner); wd_banner = NULL; } /* reload server name/description */ if(wd_settings.name_changed || wd_settings.description_changed) wd_server_send_server_info(true); /* set SSL cipher list */ if(wd_settings.controlcipher) { if(!wi_socket_tls_set_ciphers(wd_control_socket_tls, wd_settings.controlcipher)) { wi_log_err(WI_STR("Could not set TLS cipher list \"%@\": %m"), wd_settings.controlcipher); } } if(wd_settings.transfercipher) { if(!wi_socket_tls_set_ciphers(wd_transfer_socket_tls, wd_settings.transfercipher)) { wi_log_err(WI_STR("Could not set TLS cipher list \"%@\": %m"), wd_settings.transfercipher); } } /* load SSL certificate */ if(!wd_certificate && !wd_private_key) { if(wd_settings.certificate) { wd_private_key = wi_rsa_init_with_pem_file(wi_rsa_alloc(), wd_settings.certificate); if(!wd_private_key) wi_log_warn(WI_STR("Could not find RSA key in %@, creating one..."), wd_settings.certificate); wd_certificate = wi_x509_init_with_pem_file(wi_x509_alloc(), wd_settings.certificate); if(!wd_certificate) wi_log_warn(WI_STR("Could not find certificate in %@, creating one..."), wd_settings.certificate); } if(!wd_private_key) { wd_private_key = wi_rsa_init_with_bits(wi_rsa_alloc(), 1024); if(wd_private_key) wi_log_info(WI_STR("Created 1024-bit RSA key")); else wi_log_err(WI_STR("Could not create RSA key: %m")); } if(!wd_certificate) { hostname = wi_process_hostname(wi_process()); wd_certificate = wi_x509_init_with_common_name(wi_x509_alloc(), wd_private_key, hostname); if(wd_certificate) wi_log_info(WI_STR("Created self-signed certificate for %@"), hostname); else wi_log_err(WI_STR("Could not create self-signed certificate: %m")); } if(!wi_socket_tls_set_private_key(wd_control_socket_tls, wd_private_key) || !wi_socket_tls_set_private_key(wd_transfer_socket_tls, wd_private_key)) { wi_log_err(WI_STR("Could not set TLS private key: %m")); } if(!wi_socket_tls_set_certificate(wd_control_socket_tls, wd_certificate) || !wi_socket_tls_set_certificate(wd_transfer_socket_tls, wd_certificate)) { wi_log_err(WI_STR("Could not set TLS certificate: %m")); } } }
wi_boolean_t wi_config_read_file(wi_config_t *config) { wi_enumerator_t *enumerator; wi_runtime_instance_t *instance; wi_file_t *file; wi_mutable_array_t *array; wi_mutable_dictionary_t *previous_values; wi_string_t *string, *name, *value; wi_config_type_t type; file = wi_file_for_reading(config->path); if(!file) { wi_log_err(WI_STR("Could not open %@: %m"), config->path); return false; } wi_log_info(WI_STR("Reading %@"), config->path); config->line = 0; wi_lock_lock(config->lock); previous_values = config->values; config->values = wi_dictionary_init(wi_mutable_dictionary_alloc()); if(config->defaults) { enumerator = wi_dictionary_key_enumerator(config->defaults); while((name = wi_enumerator_next_data(enumerator))) { // instance = wi_mutable_copy(wi_dictionary_data_for_key(config->defaults, name)); instance = wi_dictionary_data_for_key(config->defaults, name); if(wi_runtime_id(instance) == wi_array_runtime_id()) instance = wi_autorelease(wi_mutable_copy(instance)); wi_mutable_dictionary_set_data_for_key(config->values, instance, name); // wi_release(instance); } } while((string = wi_file_read_line(file))) { config->line++; if(wi_string_length(string) > 0 && !wi_string_has_prefix(string, WI_STR("#"))) { if(_wi_config_parse_string(config, string, &name, &value)) { instance = _wi_config_instance_for_setting(config, name, value, &type); if(instance) { wi_log_debug(WI_STR(" %@ = %@"), name, value); if(type == WI_CONFIG_STRINGLIST) { array = wi_dictionary_data_for_key(config->values, name); if(!array) { array = wi_mutable_array(); wi_mutable_dictionary_set_data_for_key(config->values, array, name); } wi_mutable_array_add_data(array, instance); } else { wi_mutable_dictionary_set_data_for_key(config->values, instance, name); } } else { _wi_config_log_error(config, name); } } else { wi_error_set_libwired_error(WI_ERROR_SETTINGS_SYNTAXERROR); _wi_config_log_error(config, string); } } } enumerator = wi_dictionary_key_enumerator(config->values); while((name = wi_enumerator_next_data(enumerator))) { instance = wi_dictionary_data_for_key(config->values, name); if(!previous_values || !wi_is_equal(instance, wi_dictionary_data_for_key(previous_values, name))) wi_mutable_set_add_data(config->changes, name); } wi_release(previous_values); wi_lock_unlock(config->lock); return true; }
static void wd_tracker_register(wd_tracker_t *tracker) { wi_enumerator_t *enumerator; wi_address_t *address; wi_array_t *arguments; wi_string_t *ip, *string; uint32_t message; wi_boolean_t fatal = false; if(!wi_lock_trylock(tracker->register_lock)) return; enumerator = wi_array_data_enumerator(tracker->addresses); while((address = wi_enumerator_next_data(enumerator))) { tracker->active = false; tracker->address = NULL; ip = wi_address_string(address); wi_log_info(WI_STR("Trying %@ for tracker %@..."), ip, tracker->host); tracker->socket = wi_autorelease(wi_socket_init_with_address(wi_socket_alloc(), address, WI_SOCKET_TCP)); if(!tracker->socket) { wi_log_err(WI_STR("Could not create socket for tracker %@: %m"), tracker->host); continue; } if(!wi_socket_connect(tracker->socket, 30.0)) { wi_log_err(WI_STR("Could not connect to tracker %@: %m"), tracker->host); continue; } if(!wi_socket_connect_tls(tracker->socket, tracker->tls, 30.0)) { wi_log_err(WI_STR("Could not connect to tracker %@: %m"), tracker->host); continue; } if(!wd_tracker_write(tracker, WI_STR("HELLO"))) continue; if(!wd_tracker_read(tracker, &message, &arguments)) continue; if(message != 200) { string = wi_array_components_joined_by_string(arguments, WI_STR(" ")); wi_log_err(WI_STR("Could not register with tracker %@: Unexpected reply \"%u %@\""), tracker->host, message, string); fatal = true; continue; } if(!wd_tracker_write(tracker, WI_STR("CLIENT %#@"), wd_server_version_string)) continue; if(!wd_tracker_write(tracker, WI_STR("REGISTER %#@%c%#@%c%#@%c%u%c%#@"), tracker->category, WD_FIELD_SEPARATOR, wd_settings.url, WD_FIELD_SEPARATOR, wd_settings.name, WD_FIELD_SEPARATOR, wd_settings.bandwidth, WD_FIELD_SEPARATOR, wd_settings.description)) continue; if(!wd_tracker_read(tracker, &message, &arguments)) continue; if(message != 700 || wi_array_count(arguments) < 1) { string = wi_array_components_joined_by_string(arguments, WI_STR(" ")); wi_log_err(WI_STR("Could not register with tracker %@: Unexpected reply \"%u %@\""), tracker->host, message, string); break; } wi_release(tracker->key); tracker->key = wi_retain(WI_ARRAY(arguments, 0)); tracker->public_key = wi_retain(wi_socket_ssl_public_key(tracker->socket)); if(!tracker->public_key) { wi_log_err(WI_STR("Could not get public key from the tracker %@: %m"), tracker->host); break; } wi_log_info(WI_STR("Registered with the tracker %@"), tracker->host); tracker->active = true; tracker->address = address; break; } wi_lock_unlock(tracker->register_lock); }
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); }