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"]); }
bool enter_create_mode(game_display& disp, const config& game_config, saved_game& state, jump_to_campaign_info jump_to_campaign, bool local_players_only) { bool configure_canceled = false; do { ng::create_engine create_eng(disp, state); create_eng.set_current_level_type(ng::level::TYPE::SP_CAMPAIGN); std::vector<ng::create_engine::level_ptr> campaigns( create_eng.get_levels_by_type_unfiltered(ng::level::TYPE::SP_CAMPAIGN)); if (campaigns.empty()) { gui2::show_error_message(disp.video(), _("No campaigns are available.\n")); return false; } bool use_deterministic_mode = false; // No campaign selected from command line if (jump_to_campaign.campaign_id_.empty() == true) { gui2::tcampaign_selection dlg(create_eng); try { dlg.show(disp.video()); } catch(twml_exception& e) { e.show(disp); return false; } if(dlg.get_retval() != gui2::twindow::OK) { return false; } use_deterministic_mode = dlg.get_deterministic(); } else { // don't reset the campaign_id_ so we can know // if we should quit the game or return to the main menu // checking for valid campaign name bool not_found = true; for(size_t i = 0; i < campaigns.size(); ++i) { if (campaigns[i]->data()["id"] == jump_to_campaign.campaign_id_) { create_eng.set_current_level(i); not_found = false; break; } } // didn't find any campaign with that id if (not_found) { //TODO: use ERR_NG or similar std::cerr<<"No such campaign id to jump to: ["<<jump_to_campaign.campaign_id_<<"]\n"; return false; } } std::string random_mode = use_deterministic_mode ? "deterministic" : ""; state.classification().random_mode = random_mode; std::string selected_difficulty = create_eng.select_campaign_difficulty(jump_to_campaign.difficulty_); if (selected_difficulty == "FAIL") return false; if (selected_difficulty == "CANCEL") { if (jump_to_campaign.campaign_id_.empty() == false) { jump_to_campaign.campaign_id_ = ""; } // canceled difficulty dialog, relaunch the campaign selection dialog return enter_create_mode(disp, game_config, state, jump_to_campaign, local_players_only); } create_eng.prepare_for_era_and_mods(); create_eng.prepare_for_campaign(selected_difficulty); if(!jump_to_campaign.scenario_id_.empty()) { state.set_carryover_sides_start( config_of("next_scenario", jump_to_campaign.scenario_id_) ); } create_eng.prepare_for_new_level(); create_eng.get_parameters(); if(!state.valid()) { //TODO: use ERR_NG or similar std::cerr << "Cannot load scenario with id=" << state.get_scenario_id() << "\n"; return false; } configure_canceled = !enter_configure_mode(disp, game_config_manager::get()->game_config(), state, local_players_only); } while (configure_canceled); return true; }
/** Retrieves a campaign by id if found, or a null config otherwise. */ config& get_campaign(const std::string& id) { return campaigns().find_child("campaign", "name", id); }
void server::handle_request_campaign_list(const server::request& req) { LOG_CS << "sending campaign list to " << req.addr << " using gzip"; time_t epoch = time(NULL); config campaign_list; campaign_list["timestamp"] = epoch; if(req.cfg["times_relative_to"] != "now") { epoch = 0; } bool before_flag = false; time_t before = epoch; try { before = before + lexical_cast<time_t>(req.cfg["before"]); before_flag = true; } catch(bad_lexical_cast) {} bool after_flag = false; time_t after = epoch; try { after = after + lexical_cast<time_t>(req.cfg["after"]); after_flag = true; } catch(bad_lexical_cast) {} const std::string& name = req.cfg["name"]; const std::string& lang = req.cfg["language"]; BOOST_FOREACH(const config& i, campaigns().child_range("campaign")) { if(!name.empty() && name != i["name"]) { continue; } const std::string& tm = i["timestamp"]; if(before_flag && (tm.empty() || lexical_cast_default<time_t>(tm, 0) >= before)) { continue; } if(after_flag && (tm.empty() || lexical_cast_default<time_t>(tm, 0) <= after)) { continue; } if(!lang.empty()) { bool found = false; BOOST_FOREACH(const config& j, i.child_range("translation")) { if(j["language"] == lang) { found = true; break; } } if(!found) { continue; } } campaign_list.add_child("campaign", i); }
void server::handle_upload(const server::request& req) { const config& upload = req.cfg; LOG_CS << "uploading campaign '" << upload["name"] << "' from " << req.addr << ".\n"; config data = upload.child("data"); const std::string& name = upload["name"]; config *campaign = nullptr; bool passed_name_utf8_check = false; try { const std::string& lc_name = utf8::lowercase(name); passed_name_utf8_check = true; for(config& c : campaigns().child_range("campaign")) { if(utf8::lowercase(c["name"]) == lc_name) { campaign = &c; break; } } } catch(const utf8::invalid_utf8_exception&) { if(!passed_name_utf8_check) { LOG_CS << "Upload aborted - invalid_utf8_exception caught on handle_upload() check 1, " << "the add-on pbl info contains invalid UTF-8\n"; send_error("Add-on rejected: The add-on name contains an invalid UTF-8 sequence.", req.sock); } else { LOG_CS << "Upload aborted - invalid_utf8_exception caught on handle_upload() check 2, " << "the internal add-ons list contains invalid UTF-8\n"; send_error("Server error: The server add-ons list is damaged.", req.sock); } return; } if(read_only_) { LOG_CS << "Upload aborted - uploads not permitted in read-only mode.\n"; send_error("Add-on rejected: The server is currently in read-only mode.", req.sock); } else if(!data) { LOG_CS << "Upload aborted - no add-on data.\n"; send_error("Add-on rejected: No add-on data was supplied.", req.sock); } else if(!addon_name_legal(upload["name"])) { LOG_CS << "Upload aborted - invalid add-on name.\n"; send_error("Add-on rejected: The name of the add-on is invalid.", req.sock); } else if(is_text_markup_char(upload["name"].str()[0])) { LOG_CS << "Upload aborted - add-on name starts with an illegal formatting character.\n"; send_error("Add-on rejected: The name of the add-on starts with an illegal formatting character.", req.sock); } else if(upload["title"].empty()) { LOG_CS << "Upload aborted - no add-on title specified.\n"; send_error("Add-on rejected: You did not specify the title of the add-on in the pbl file!", req.sock); } else if(is_text_markup_char(upload["title"].str()[0])) { LOG_CS << "Upload aborted - add-on title starts with an illegal formatting character.\n"; send_error("Add-on rejected: The title of the add-on starts with an illegal formatting character.", req.sock); } else if(get_addon_type(upload["type"]) == ADDON_UNKNOWN) { LOG_CS << "Upload aborted - unknown add-on type specified.\n"; send_error("Add-on rejected: You did not specify a known type for the add-on in the pbl file! (See PblWML: wiki.wesnoth.org/PblWML)", req.sock); } else if(upload["author"].empty()) { LOG_CS << "Upload aborted - no add-on author specified.\n"; send_error("Add-on rejected: You did not specify the author(s) of the add-on in the pbl file!", req.sock); } else if(upload["version"].empty()) { LOG_CS << "Upload aborted - no add-on version specified.\n"; send_error("Add-on rejected: You did not specify the version of the add-on in the pbl file!", req.sock); } else if(upload["description"].empty()) { LOG_CS << "Upload aborted - no add-on description specified.\n"; send_error("Add-on rejected: You did not specify a description of the add-on in the pbl file!", req.sock); } else if(upload["email"].empty()) { LOG_CS << "Upload aborted - no add-on email specified.\n"; send_error("Add-on rejected: You did not specify your email address in the pbl file!", req.sock); } else if(!check_names_legal(data)) { LOG_CS << "Upload aborted - invalid file names in add-on data.\n"; send_error("Add-on rejected: The add-on contains an illegal file or directory name." " File or directory names may not contain whitespace or any of the following characters: '/ \\ : ~'", req.sock); } else if(campaign && !authenticate(*campaign, upload["passphrase"])) { LOG_CS << "Upload aborted - incorrect passphrase.\n"; send_error("Add-on rejected: The add-on already exists, and your passphrase was incorrect.", req.sock); } else { const time_t upload_ts = time(nullptr); LOG_CS << "Upload is owner upload.\n"; try { if(blacklist_.is_blacklisted(name, upload["title"].str(), upload["description"].str(), upload["author"].str(), req.addr, upload["email"].str())) { LOG_CS << "Upload denied - blacklisted add-on information.\n"; send_error("Add-on upload denied. Please contact the server administration for assistance.", req.sock); return; } } catch(const utf8::invalid_utf8_exception&) { LOG_CS << "Upload aborted - the add-on pbl info contains invalid UTF-8 and cannot be " << "checked against the blacklist\n"; send_error("Add-on rejected: The add-on publish information contains an invalid UTF-8 sequence.", req.sock); return; } const bool existing_upload = campaign != nullptr; std::string message = "Add-on accepted."; if(campaign == nullptr) { campaign = &campaigns().add_child("campaign"); (*campaign)["original_timestamp"] = upload_ts; } (*campaign)["title"] = upload["title"]; (*campaign)["name"] = upload["name"]; (*campaign)["filename"] = "data/" + upload["name"].str(); (*campaign)["author"] = upload["author"]; (*campaign)["description"] = upload["description"]; (*campaign)["version"] = upload["version"]; (*campaign)["icon"] = upload["icon"]; (*campaign)["translate"] = upload["translate"]; (*campaign)["dependencies"] = upload["dependencies"]; (*campaign)["upload_ip"] = req.addr; (*campaign)["type"] = upload["type"]; (*campaign)["email"] = upload["email"]; if(!existing_upload) { set_passphrase(*campaign, upload["passphrase"]); } if((*campaign)["downloads"].empty()) { (*campaign)["downloads"] = 0; } (*campaign)["timestamp"] = upload_ts; int uploads = (*campaign)["uploads"].to_int() + 1; (*campaign)["uploads"] = uploads; (*campaign).clear_children("feedback"); if(const config& url_params = upload.child("feedback")) { (*campaign).add_child("feedback", url_params); } const std::string& filename = (*campaign)["filename"].str(); data["title"] = (*campaign)["title"]; data["name"] = ""; data["campaign_name"] = (*campaign)["name"]; data["author"] = (*campaign)["author"]; data["description"] = (*campaign)["description"]; data["version"] = (*campaign)["version"]; data["timestamp"] = (*campaign)["timestamp"]; data["original_timestamp"] = (*campaign)["original_timestamp"]; data["icon"] = (*campaign)["icon"]; data["type"] = (*campaign)["type"]; (*campaign).clear_children("translation"); find_translations(data, *campaign); add_license(data); { filesystem::scoped_ostream campaign_file = filesystem::ostream_file(filename); config_writer writer(*campaign_file, true, compress_level_); writer.write(data); } (*campaign)["size"] = filesystem::file_size(filename); write_config(); send_message(message, req.sock); fire("hook_post_upload", upload["name"]); } }
void server::handle_request_campaign_list(const server::request& req) { LOG_CS << "sending campaign list to " << req.addr << " using gzip\n"; time_t epoch = time(nullptr); config campaign_list; campaign_list["timestamp"] = epoch; if(req.cfg["times_relative_to"] != "now") { epoch = 0; } bool before_flag = false; time_t before = epoch; try { before = before + lexical_cast<time_t>(req.cfg["before"]); before_flag = true; } catch(bad_lexical_cast) {} bool after_flag = false; time_t after = epoch; try { after = after + lexical_cast<time_t>(req.cfg["after"]); after_flag = true; } catch(bad_lexical_cast) {} const std::string& name = req.cfg["name"]; const std::string& lang = req.cfg["language"]; for(const config& i : campaigns().child_range("campaign")) { if(!name.empty() && name != i["name"]) { continue; } const std::string& tm = i["timestamp"]; if(before_flag && (tm.empty() || lexical_cast_default<time_t>(tm, 0) >= before)) { continue; } if(after_flag && (tm.empty() || lexical_cast_default<time_t>(tm, 0) <= after)) { continue; } if(!lang.empty()) { bool found = false; for(const config& j : i.child_range("translation")) { if(j["language"] == lang) { found = true; break; } } if(!found) { continue; } } campaign_list.add_child("campaign", i); } for(config& j : campaign_list.child_range("campaign")) { j["passphrase"] = ""; j["passhash"] = ""; j["passsalt"] = ""; j["upload_ip"] = ""; j["email"] = ""; j["feedback_url"] = ""; // Build a feedback_url string attribute from the // internal [feedback] data. const config& url_params = j.child_or_empty("feedback"); if(!url_params.empty() && !feedback_url_format_.empty()) { j["feedback_url"] = format_addon_feedback_url(feedback_url_format_, url_params); } // Clients don't need to see the original data, so discard it. j.clear_children("feedback"); } config response; response.add_child("campaigns", campaign_list); std::ostringstream ostr; write(ostr, response); std::string wml = ostr.str(); simple_wml::document doc(wml.c_str(), simple_wml::INIT_STATIC); doc.compress(); async_send_doc(req.sock, doc, std::bind(&server::handle_new_client, this, _1)); }