void server::handle_request_campaign(const server::request& req) { LOG_CS << "sending campaign '" << req.cfg["name"] << "' to " << req.addr << " using gzip\n"; config& campaign = get_campaign(req.cfg["name"]); if(!campaign) { send_error("Add-on '" + req.cfg["name"].str() + "' not found.", req.sock); } else { const int size = filesystem::file_size(campaign["filename"]); if(size < 0) { std::cerr << " size: <unknown> KiB\n"; ERR_CS << "File size unknown, aborting send.\n"; send_error("Add-on '" + req.cfg["name"].str() + "' could not be read by the server.", req.sock); return; } std::cerr << " size: " << size/1024 << "KiB\n"; async_send_file(req.sock, campaign["filename"], std::bind(&server::handle_new_client, this, _1), null_handler); // Clients doing upgrades or some other specific thing shouldn't bump // the downloads count. Default to true for compatibility with old // clients that won't tell us what they are trying to do. if(req.cfg["increase_downloads"].to_bool(true)) { const int downloads = campaign["downloads"].to_int() + 1; campaign["downloads"] = downloads; } } }
void server::handle_delete(const server::request& req) { const config& erase = req.cfg; if(read_only_) { LOG_CS << "in read-only mode, request to delete '" << erase["name"] << "' from " << req.addr << " denied\n"; send_error("Cannot delete add-on: The server is currently in read-only mode.", req.sock); return; } LOG_CS << "deleting campaign '" << erase["name"] << "' requested from " << req.addr << "\n"; config& campaign = get_campaign(erase["name"]); if(!campaign) { send_error("The add-on does not exist.", req.sock); return; } if(!authenticate(campaign, erase["passphrase"]) && (campaigns()["master_password"].empty() || campaigns()["master_password"] != erase["passphrase"])) { send_error("The passphrase is incorrect.", req.sock); return; } // Erase the campaign. filesystem::write_file(campaign["filename"], std::string()); if(remove(campaign["filename"].str().c_str()) != 0) { ERR_CS << "failed to delete archive for campaign '" << erase["name"] << "' (" << campaign["filename"] << "): " << strerror(errno) << '\n'; } config::child_itors itors = campaigns().child_range("campaign"); for(size_t index = 0; !itors.empty(); ++index, itors.pop_front()) { if(&campaign == &itors.front()) { campaigns().remove_child("campaign", index); break; } } write_config(); send_message("Add-on deleted.", req.sock); fire("hook_post_erase", erase["name"]); }
void server::handle_change_passphrase(const server::request& req) { const config& cpass = req.cfg; if(read_only_) { LOG_CS << "in read-only mode, request to change passphrase denied\n"; send_error("Cannot change passphrase: The server is currently in read-only mode.", req.sock); return; } config& campaign = get_campaign(cpass["name"]); if(!campaign) { send_error("No add-on with that name exists.", req.sock); } else if(!authenticate(campaign, cpass["passphrase"])) { send_error("Your old passphrase was incorrect.", req.sock); } else if(cpass["new_passphrase"].empty()) { send_error("No new passphrase was supplied.", req.sock); } else { set_passphrase(campaign, cpass["new_passphrase"]); write_config(); send_message("Passphrase changed.", req.sock); } }
void server::run() { network::connection sock = 0; time_t last_ts = monotonic_clock(); for(;;) { if(need_reload) { load_config(); // TODO: handle port number config changes need_reload = 0; last_ts = 0; LOG_CS << "Reloaded configuration\n"; } try { bool force_flush = false; std::string admin_cmd; if(input_ && input_->read_line(admin_cmd)) { control_line ctl = admin_cmd; if(ctl == "shut_down") { LOG_CS << "Shut down requested by admin, shutting down...\n"; break; } else if(ctl == "readonly") { if(ctl.args_count()) { cfg_["read_only"] = read_only_ = utils::string_bool(ctl[1], true); } LOG_CS << "Read only mode: " << (read_only_ ? "enabled" : "disabled") << '\n'; } else if(ctl == "flush") { force_flush = true; LOG_CS << "Flushing config to disk...\n"; } else if(ctl == "reload") { if(ctl.args_count()) { if(ctl[1] == "blacklist") { LOG_CS << "Reloading blacklist...\n"; load_blacklist(); } else { ERR_CS << "Unrecognized admin reload argument: " << ctl[1] << '\n'; } } else { LOG_CS << "Reloading all configuration...\n"; need_reload = 1; // Avoid flush timer ellapsing continue; } } else if(ctl == "setpass") { if(ctl.args_count() != 2) { ERR_CS << "Incorrect number of arguments for 'setpass'\n"; } else { const std::string& addon_id = ctl[1]; const std::string& newpass = ctl[2]; config& campaign = get_campaign(addon_id); if(!campaign) { ERR_CS << "Add-on '" << addon_id << "' not found, cannot set passphrase\n"; } else if(newpass.empty()) { // Shouldn't happen! ERR_CS << "Add-on passphrases may not be empty!\n"; } else { campaign["passphrase"] = newpass; write_config(); LOG_CS << "New passphrase set for '" << addon_id << "'\n"; } } } else { ERR_CS << "Unrecognized admin command: " << ctl.full() << '\n'; } } const time_t cur_ts = monotonic_clock(); // Write config to disk every ten minutes. if(force_flush || labs(cur_ts - last_ts) >= 10*60) { write_config(); last_ts = cur_ts; } network::process_send_queue(); sock = network::accept_connection(); if(sock) { LOG_CS << "received connection from " << network::ip_address(sock) << "\n"; } config data; while((sock = network::receive_data(data, 0)) != network::null_connection) { config::all_children_iterator i = data.ordered_begin(); if(i != data.ordered_end()) { // We only handle the first child. const config::any_child& c = *i; request_handlers_table::const_iterator j = handlers_.find(c.key); if(j != handlers_.end()) { // Call the handler. j->second(this, request(c.key, c.cfg, sock)); } else { send_error("Unrecognized [" + c.key + "] request.", sock); } } } } catch(network::error& e) { if(!e.socket) { ERR_CS << "fatal network error: " << e.message << "\n"; throw; } else { LOG_CS << "client disconnect: " << e.message << " " << network::ip_address(e.socket) << "\n"; e.disconnect(); } } catch(const config::error& e) { network::connection err_sock = 0; network::connection const * err_connection = boost::get_error_info<network::connection_info>(e); if(err_connection != NULL) { err_sock = *err_connection; } if(err_sock == 0 && sock > 0) { err_sock = sock; } if(err_sock) { ERR_CS << "client disconnect due to exception: " << e.what() << " " << network::ip_address(err_sock) << "\n"; network::disconnect(err_sock); } else { throw; } } SDL_Delay(20); } }
void server::handle_read_from_fifo(const boost::system::error_code& error, std::size_t) { if(error) { if(error == boost::asio::error::operation_aborted) // This means fifo was closed by load_config() to open another fifo return; ERR_CS << "Error reading from fifo: " << error.message() << '\n'; return; } std::istream is(&admin_cmd_); std::string cmd; std::getline(is, cmd); const control_line ctl = cmd; if(ctl == "shut_down") { LOG_CS << "Shut down requested by admin, shutting down...\n"; throw server_shutdown("Shut down via fifo command"); } else if(ctl == "readonly") { if(ctl.args_count()) { cfg_["read_only"] = read_only_ = utils::string_bool(ctl[1], true); } LOG_CS << "Read only mode: " << (read_only_ ? "enabled" : "disabled") << '\n'; } else if(ctl == "flush") { LOG_CS << "Flushing config to disk...\n"; write_config(); } else if(ctl == "reload") { if(ctl.args_count()) { if(ctl[1] == "blacklist") { LOG_CS << "Reloading blacklist...\n"; load_blacklist(); } else { ERR_CS << "Unrecognized admin reload argument: " << ctl[1] << '\n'; } } else { LOG_CS << "Reloading all configuration...\n"; load_config(); LOG_CS << "Reloaded configuration\n"; } } else if(ctl == "setpass") { if(ctl.args_count() != 2) { ERR_CS << "Incorrect number of arguments for 'setpass'\n"; } else { const std::string& addon_id = ctl[1]; const std::string& newpass = ctl[2]; config& campaign = get_campaign(addon_id); if(!campaign) { ERR_CS << "Add-on '" << addon_id << "' not found, cannot set passphrase\n"; } else if(newpass.empty()) { // Shouldn't happen! ERR_CS << "Add-on passphrases may not be empty!\n"; } else { set_passphrase(campaign, newpass); write_config(); LOG_CS << "New passphrase set for '" << addon_id << "'\n"; } } } else { ERR_CS << "Unrecognized admin command: " << ctl.full() << '\n'; } read_from_fifo(); }