revert previous commit as it should be done on trunk

This commit is contained in:
Jérémy Rosen 2006-12-16 21:39:31 +00:00
parent 1d37f923ab
commit e2d4f1ca20

View file

@ -9,7 +9,7 @@
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
*/
#include "config.hpp"
#include "filesystem.hpp"
@ -28,337 +28,318 @@
namespace {
config construct_message(const std::string& msg)
{
config cfg;
cfg.add_child("message")["message"] = msg;
LOG_CS << "MESSAGE: "<<msg<<"\n";
return cfg;
config construct_message(const std::string& msg)
{
config cfg;
cfg.add_child("message")["message"] = msg;
return cfg;
}
config construct_error(const std::string& msg)
{
config cfg;
cfg.add_child("error")["message"] = msg;
return cfg;
}
class campaign_server
{
public:
explicit campaign_server(const std::string& cfgfile);
void run();
private:
int load_config(); // return the server port
const config& campaigns() const { return *cfg_.child("campaigns"); }
config& campaigns() { return *cfg_.child("campaigns"); }
config cfg_;
const std::string file_;
const network::manager net_manager_;
const network::server_manager server_manager_;
};
int campaign_server::load_config()
{
scoped_istream stream = istream_file(file_);
read(cfg_, *stream);
return lexical_cast_default<int>(cfg_["port"], 15004);
}
campaign_server::campaign_server(const std::string& cfgfile)
: file_(cfgfile), net_manager_(5), server_manager_(load_config())
{
if(cfg_.child("campaigns") == NULL) {
cfg_.add_child("campaigns");
}
}
config construct_error(const std::string& msg)
{
config cfg;
cfg.add_child("error")["message"] = msg;
LOG_CS << "ERROR: "<<msg<<"\n";
return cfg;
}
class campaign_server
{
public:
explicit campaign_server(const std::string& cfgfile);
void run();
private:
int load_config(); // return the server port
const config& campaigns() const { return *cfg_.child("campaigns"); }
config& campaigns() { return *cfg_.child("campaigns"); }
config cfg_;
const std::string file_;
const network::manager net_manager_;
const network::server_manager server_manager_;
};
int campaign_server::load_config()
{
scoped_istream stream = istream_file(file_);
read(cfg_, *stream);
return lexical_cast_default<int>(cfg_["port"], 15004);
}
campaign_server::campaign_server(const std::string& cfgfile)
: file_(cfgfile), net_manager_(5), server_manager_(load_config())
{
if(cfg_.child("campaigns") == NULL) {
cfg_.add_child("campaigns");
void find_translations(const config& cfg, config& campaign)
{
const config::child_list& dirs = cfg.get_children("dir");
for(config::child_list::const_iterator i = dirs.begin(); i != dirs.end(); ++i) {
if((const t_string)(**i)["name"] == "LC_MESSAGES") {
config* language = &campaign.add_child("translation");
(*language)["language"] = cfg["name"];
}
}
else {
find_translations(**i, campaign);
}
}
}
void find_translations(const config& cfg, config& campaign)
{
const config::child_list& dirs = cfg.get_children("dir");
for(config::child_list::const_iterator i = dirs.begin(); i != dirs.end(); ++i) {
if((const t_string)(**i)["name"] == "LC_MESSAGES") {
config* language = &campaign.add_child("translation");
(*language)["language"] = cfg["name"];
// Given an uploaded campaign, rename all .py files to .py.unchecked, unless
// the contents are the same as a .py file in an existing campaign.
// This means, a .py.unchecked file can be approved by simply renaming it
// on the CS, and it will remain approved as long as it is not changed (but
// it can be moved/renamed). If the .py file changes, it needs to be approved
// again.
std::string check_python_scripts(config &data, std::string filename)
{
std::vector<config *> python_scripts = find_scripts(data, ".py");
if (!python_scripts.empty()) {
// Campaign contains python scripts.
config old_campaign;
scoped_istream stream = istream_file(filename);
read_compressed(old_campaign, *stream);
std::vector<config *> old_scripts = find_scripts(old_campaign, ".py");
std::string script_names = "";
std::vector<config *>::iterator i, j;
// Go through all newly uploaded python scripts.
for (i = python_scripts.begin(); i != python_scripts.end(); ++i) {
bool already = false;
// Compare to existing, approved scripts.
for (j = old_scripts.begin(); j != old_scripts.end(); ++j) {
if ((**i)["contents"] != (**j)["contents"]) continue;
already = true;
break;
}
else {
find_translations(**i, campaign);
if (!already) {
script_names += "\n" + (**i)["name"];
(**i)["name"] += ".unchecked";
}
}
if (script_names != "")
return "\nScripts awaiting approval:\n" + script_names;
}
return "";
}
// Given an uploaded campaign, rename all .py files to .py.unchecked, unless
// the contents are the same as a .py file in an existing campaign.
// This means, a .py.unchecked file can be approved by simply renaming it
// on the CS, and it will remain approved as long as it is not changed (but
// it can be moved/renamed). If the .py file changes, it needs to be approved
// again.
std::string check_python_scripts(config &data, std::string filename)
{
std::vector<config *> python_scripts = find_scripts(data, ".py");
if (!python_scripts.empty()) {
// Campaign contains python scripts.
config old_campaign;
scoped_istream stream = istream_file(filename);
read_compressed(old_campaign, *stream);
std::vector<config *> old_scripts = find_scripts(old_campaign, ".py");
std::string script_names = "";
std::vector<config *>::iterator i, j;
// Go through all newly uploaded python scripts.
for (i = python_scripts.begin(); i != python_scripts.end(); ++i) {
bool already = false;
// Compare to existing, approved scripts.
for (j = old_scripts.begin(); j != old_scripts.end(); ++j) {
if ((**i)["contents"] != (**j)["contents"]) continue;
already = true;
break;
}
if (!already) {
script_names += " " + (**i)["name"];
(**i)["name"] += ".unchecked";
}
// Add a file COPYING.txt with the GPL to an uploaded campaign.
void add_license(config &data)
{
config *dir = data.find_child("dir", "name", data["campaign_name"]);
// No top-level directory? Hm..
if (!dir) return;
// Don't add if it already exists.
if (dir->find_child("file", "name", "COPYING.txt")) return;
if (dir->find_child("file", "name", "COPYING")) return;
// Copy over COPYING.txt
std::string contents = read_file("data/COPYING.txt");
config &copying = dir->add_child("file");
copying["name"] = "COPYING.txt";
copying["contents"] = contents;
}
void campaign_server::run()
{
for(int increment = 0; ; ++increment) {
try {
//write config to disk every ten minutes
if((increment%(60*10*2)) == 0) {
scoped_ostream cfgfile = ostream_file(file_);
write(*cfgfile, cfg_);
}
if (script_names != "")
return " Scripts awaiting approval: " + script_names;
}
return "";
}
// Add a file COPYING.txt with the GPL to an uploaded campaign.
void add_license(config &data)
{
config *dir = data.find_child("dir", "name", data["campaign_name"]);
// No top-level directory? Hm..
if (!dir) return;
network::process_send_queue();
// Don't add if it already exists.
if (dir->find_child("file", "name", "COPYING.txt")) return;
if (dir->find_child("file", "name", "COPYING")) return;
network::connection sock = network::accept_connection();
if(sock) {
LOG_CS << "received connection from " << network::ip_address(sock) << "\n";
}
// Copy over COPYING.txt
std::string contents = read_file("data/COPYING.txt");
config &copying = dir->add_child("file");
copying["name"] = "COPYING.txt";
copying["contents"] = contents;
}
void campaign_server::run()
{
for(int increment = 0; ; ++increment) {
try {
//write config to disk every ten minutes
if((increment%(60*10*2)) == 0) {
scoped_ostream cfgfile = ostream_file(file_);
write(*cfgfile, cfg_);
}
network::process_send_queue();
network::connection sock = network::accept_connection();
if(sock) {
LOG_CS << "received connection from " << network::ip_address(sock) << "\n";
}
config data;
while((sock = network::receive_data(data)) != network::null_connection) {
if(const config* req = data.child("request_campaign_list")) {
LOG_CS << "sending campaign list to " << network::ip_address(sock);
time_t epoch = time(NULL);
config campaign_list;
(campaign_list)["timestamp"] = lexical_cast<std::string>(epoch);
if((const t_string)(*req)["times_relative_to"] != "now") {
epoch = 0;
config data;
while((sock = network::receive_data(data)) != network::null_connection) {
if(const config* req = data.child("request_campaign_list")) {
time_t epoch = time(NULL);
config campaign_list;
(campaign_list)["timestamp"] = lexical_cast<std::string>(epoch);
if((const t_string)(*req)["times_relative_to"] != "now") {
epoch = 0;
}
int before_flag = 0;
time_t before = epoch;
if((const t_string)(*req)["before"] != "") {
try {
before = before + lexical_cast<time_t>((*req)["before"]);
before_flag = 1;
}
int before_flag = 0;
time_t before = epoch;
if((const t_string)(*req)["before"] != "") {
try {
before = before + lexical_cast<time_t>((*req)["before"]);
before_flag = 1;
}
catch(bad_lexical_cast) {
}
}
int after_flag = 0;
time_t after = epoch;
if((const t_string)(*req)["after"] != "") {
try {
after = after + lexical_cast<time_t>((*req)["after"]);
after_flag = 1;
}
catch(bad_lexical_cast) {
}
}
config::child_list cmps = campaigns().get_children("campaign");
for(config::child_list::iterator i = cmps.begin(); i != cmps.end(); ++i) {
if((const t_string)(*req)["name"] != "" && (*req)["name"] != (**i)["name"]) continue;
if(before_flag && ((const t_string)(**i)["timestamp"] == "" || lexical_cast_default<time_t>((**i)["timestamp"],0) >= before)) continue;
if(after_flag && ((const t_string)(**i)["timestamp"] == "" || lexical_cast_default<time_t>((**i)["timestamp"],0) <= after)) continue;
int found = 1;
if((const t_string)(*req)["language"] != "") {
found = 0;
config::child_list translation = (**i).get_children("translation");
for(config::child_list::iterator j = translation.begin(); j != translation.end(); ++j) {
if((*req)["language"] == (**j)["language"]) {
found = 1;
break;
}
}
}
if(found == 0) continue;
campaign_list.add_child("campaign", (**i));
}
cmps = campaign_list.get_children("campaign");
for(config::child_list::iterator i = cmps.begin(); i != cmps.end(); ++i) {
(**i)["passphrase"] = "";
}
config response;
response.add_child("campaigns",campaign_list);
network::send_data(response,sock);
LOG_CS << " Done\n";
} else if(const config* req = data.child("request_campaign")) {
LOG_CS << "sending campaign " << (*req)["name"] << " to " << network::ip_address(sock);
config* const campaign = campaigns().find_child("campaign","name",(*req)["name"]);
if(campaign == NULL) {
network::send_data(construct_error("Add-on not found."),sock);
LOG_CS << " not found\n";
} else {
config cfg;
scoped_istream stream = istream_file((*campaign)["filename"]);
read_compressed(cfg, *stream);
add_license(cfg);
network::queue_data(cfg,sock);
const int downloads = lexical_cast_default<int>((*campaign)["downloads"],0)+1;
(*campaign)["downloads"] = lexical_cast<std::string>(downloads);
LOG_CS << " Done\n";
}
} else if(data.child("request_terms") != NULL) {
LOG_CS << "sending terms " << network::ip_address(sock);
network::send_data(construct_message("All add-ons uploaded to this server must be licensed under the terms of the GNU General Public License (GPL). By uploading content to this server, you certify that you have the right to place the content under the conditions of the GPL, and choose to do so."),sock);
LOG_CS << " Done\n";
} else if(config* upload = data.child("upload")) {
LOG_CS << "uploading campaign " << network::ip_address(sock);
config* data = upload->child("data");
config* campaign = campaigns().find_child("campaign","name",(*upload)["name"]);
if(data == NULL) {
network::send_data(construct_error("No add-on data was supplied."),sock);
LOG_CS << " Done2\n";
} else if(campaign != NULL && (*campaign)["passphrase"] != (*upload)["passphrase"]) {
network::send_data(construct_error("The add-on already exists, and your passphrase was incorrect."),sock);
LOG_CS << " Done3\n";
} else if(campaign_name_legal((*upload)["name"]) == false) {
network::send_data(construct_error("The name of the add-on is invalid"),sock);
LOG_CS << " Done4\n";
} else if(check_names_legal(*data) == false) {
network::send_data(construct_error("The add-on contains an illegal file or directory name."),sock);
LOG_CS << " Done5\n";
} else {
std::string message = "Add-on accepted.";
if(campaign == NULL) {
campaign = &campaigns().add_child("campaign");
}
(*campaign)["title"] = (*upload)["title"];
(*campaign)["name"] = (*upload)["name"];
(*campaign)["filename"] = (*upload)["name"];
(*campaign)["passphrase"] = (*upload)["passphrase"];
(*campaign)["author"] = (*upload)["author"];
(*campaign)["description"] = (*upload)["description"];
(*campaign)["version"] = (*upload)["version"];
(*campaign)["icon"] = (*upload)["icon"];
if((*campaign)["downloads"].empty()) {
(*campaign)["downloads"] = "0";
}
(*campaign)["timestamp"] = lexical_cast<std::string>(time(NULL));
std::string filename = (*campaign)["filename"];
(*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)["icon"] = (*campaign)["icon"];
(*campaign).clear_children("translation");
find_translations(*data, *campaign);
// Campaigns which have python="allowed" are not checked
if ((*campaign)["python"] != "allowed") {
message += check_python_scripts(*data, filename);
}
scoped_ostream campaign_file = ostream_file(filename);
write_compressed(*campaign_file, *data);
(*campaign)["size"] = lexical_cast<std::string>(
file_size(filename));
scoped_ostream cfgfile = ostream_file(file_);
write(*cfgfile, cfg_);
network::send_data(construct_message(message),sock);
LOG_CS << " Done\n";
}
} else if(const config* erase = data.child("delete")) {
LOG_CS << "deleting campaign " << network::ip_address(sock);
config* const campaign = campaigns().find_child("campaign","name",(*erase)["name"]);
if(campaign == NULL) {
network::send_data(construct_error("The add-on does not exist."),sock);
LOG_CS << " Done\n";
continue;
}
if((*campaign)["passphrase"] != (*erase)["passphrase"]) {
network::send_data(construct_error("The passphrase is incorrect."),sock);
LOG_CS << " Done2n";
continue;
}
//erase the campaign
write_file((*campaign)["filename"],"");
remove((*campaign)["filename"].c_str());
const config::child_list& campaigns_list = campaigns().get_children("campaign");
const size_t index = std::find(campaigns_list.begin(),campaigns_list.end(),campaign) - campaigns_list.begin();
campaigns().remove_child("campaign",index);
scoped_ostream cfgfile = ostream_file(file_);
write(*cfgfile, cfg_);
network::send_data(construct_message("Add-on deleted."),sock);
LOG_CS << " Done3";
} else if(const config* cpass = data.child("change_passphrase")) {
config* campaign = campaigns().find_child("campaign","name",(*cpass)["name"]);
if(campaign == NULL) {
network::send_data(construct_error("No add-on with that name exists."),sock);
} else if((*campaign)["passphrase"] != (*cpass)["passphrase"]) {
network::send_data(construct_error("Your old passphrase was incorrect."),sock);
} else if((const t_string)(*cpass)["new_passphrase"] == "") {
network::send_data(construct_error("No new passphrase was supplied."),sock);
} else {
(*campaign)["passphrase"] = (*cpass)["new_passphrase"];
scoped_ostream cfgfile = ostream_file(file_);
write(*cfgfile, cfg_);
network::send_data(construct_message("Passphrase changed."),sock);
catch(bad_lexical_cast) {
}
}
}
} catch(network::error& e) {
if(!e.socket) {
LOG_CS << "fatal network error\n";
break;
} else {
e.disconnect();
}
} catch(config::error& e) {
LOG_CS << "error in receiving data...\n";
}
int after_flag = 0;
time_t after = epoch;
if((const t_string)(*req)["after"] != "") {
try {
after = after + lexical_cast<time_t>((*req)["after"]);
after_flag = 1;
}
catch(bad_lexical_cast) {
}
}
config::child_list cmps = campaigns().get_children("campaign");
for(config::child_list::iterator i = cmps.begin(); i != cmps.end(); ++i) {
if((const t_string)(*req)["name"] != "" && (*req)["name"] != (**i)["name"]) continue;
if(before_flag && ((const t_string)(**i)["timestamp"] == "" || lexical_cast_default<time_t>((**i)["timestamp"],0) >= before)) continue;
if(after_flag && ((const t_string)(**i)["timestamp"] == "" || lexical_cast_default<time_t>((**i)["timestamp"],0) <= after)) continue;
int found = 1;
if((const t_string)(*req)["language"] != "") {
found = 0;
config::child_list translation = (**i).get_children("translation");
for(config::child_list::iterator j = translation.begin(); j != translation.end(); ++j) {
if((*req)["language"] == (**j)["language"]) {
found = 1;
break;
}
}
}
if(found == 0) continue;
campaign_list.add_child("campaign", (**i));
}
cmps = campaign_list.get_children("campaign");
for(config::child_list::iterator i = cmps.begin(); i != cmps.end(); ++i) {
(**i)["passphrase"] = "";
}
SDL_Delay(500);
config response;
response.add_child("campaigns",campaign_list);
network::send_data(response,sock);
} else if(const config* req = data.child("request_campaign")) {
config* const campaign = campaigns().find_child("campaign","name",(*req)["name"]);
if(campaign == NULL) {
network::send_data(construct_error("Add-on not found."),sock);
} else {
config cfg;
scoped_istream stream = istream_file((*campaign)["filename"]);
read_compressed(cfg, *stream);
add_license(cfg);
network::queue_data(cfg,sock);
const int downloads = lexical_cast_default<int>((*campaign)["downloads"],0)+1;
(*campaign)["downloads"] = lexical_cast<std::string>(downloads);
}
} else if(data.child("request_terms") != NULL) {
network::send_data(construct_message("All add-ons uploaded to this server must be licensed under the terms of the GNU General Public License (GPL). By uploading content to this server, you certify that you have the right to place the content under the conditions of the GPL, and choose to do so."),sock);
} else if(config* upload = data.child("upload")) {
config* data = upload->child("data");
config* campaign = campaigns().find_child("campaign","name",(*upload)["name"]);
if(data == NULL) {
network::send_data(construct_error("No add-on data was supplied."),sock);
} else if(campaign != NULL && (*campaign)["passphrase"] != (*upload)["passphrase"]) {
network::send_data(construct_error("The add-on already exists, and your passphrase was incorrect."),sock);
} else if(campaign_name_legal((*upload)["name"]) == false) {
network::send_data(construct_error("The name of the add-on is invalid"),sock);
} else if(check_names_legal(*data) == false) {
network::send_data(construct_error("The add-on contains an illegal file or directory name."),sock);
} else {
std::string message = "Add-on accepted.";
if(campaign == NULL) {
campaign = &campaigns().add_child("campaign");
}
(*campaign)["title"] = (*upload)["title"];
(*campaign)["name"] = (*upload)["name"];
(*campaign)["filename"] = (*upload)["name"];
(*campaign)["passphrase"] = (*upload)["passphrase"];
(*campaign)["author"] = (*upload)["author"];
(*campaign)["description"] = (*upload)["description"];
(*campaign)["version"] = (*upload)["version"];
(*campaign)["icon"] = (*upload)["icon"];
if((*campaign)["downloads"].empty()) {
(*campaign)["downloads"] = "0";
}
(*campaign)["timestamp"] = lexical_cast<std::string>(time(NULL));
std::string filename = (*campaign)["filename"];
(*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)["icon"] = (*campaign)["icon"];
(*campaign).clear_children("translation");
find_translations(*data, *campaign);
// Campaigns which have python="allowed" are not checked
if ((*campaign)["python"] != "allowed") {
message += check_python_scripts(*data, filename);
}
scoped_ostream campaign_file = ostream_file(filename);
write_compressed(*campaign_file, *data);
(*campaign)["size"] = lexical_cast<std::string>(
file_size(filename));
scoped_ostream cfgfile = ostream_file(file_);
write(*cfgfile, cfg_);
network::send_data(construct_message(message),sock);
}
} else if(const config* erase = data.child("delete")) {
config* const campaign = campaigns().find_child("campaign","name",(*erase)["name"]);
if(campaign == NULL) {
network::send_data(construct_error("The add-on does not exist."),sock);
continue;
}
if((*campaign)["passphrase"] != (*erase)["passphrase"]) {
network::send_data(construct_error("The passphrase is incorrect."),sock);
continue;
}
//erase the campaign
write_file((*campaign)["filename"],"");
remove((*campaign)["filename"].c_str());
const config::child_list& campaigns_list = campaigns().get_children("campaign");
const size_t index = std::find(campaigns_list.begin(),campaigns_list.end(),campaign) - campaigns_list.begin();
campaigns().remove_child("campaign",index);
scoped_ostream cfgfile = ostream_file(file_);
write(*cfgfile, cfg_);
network::send_data(construct_message("Add-on deleted."),sock);
} else if(const config* cpass = data.child("change_passphrase")) {
config* campaign = campaigns().find_child("campaign","name",(*cpass)["name"]);
if(campaign == NULL) {
network::send_data(construct_error("No add-on with that name exists."),sock);
} else if((*campaign)["passphrase"] != (*cpass)["passphrase"]) {
network::send_data(construct_error("Your old passphrase was incorrect."),sock);
} else if((const t_string)(*cpass)["new_passphrase"] == "") {
network::send_data(construct_error("No new passphrase was supplied."),sock);
} else {
(*campaign)["passphrase"] = (*cpass)["new_passphrase"];
scoped_ostream cfgfile = ostream_file(file_);
write(*cfgfile, cfg_);
network::send_data(construct_message("Passphrase changed."),sock);
}
}
}
} catch(network::error& e) {
if(!e.socket) {
LOG_CS << "fatal network error\n";
break;
} else {
e.disconnect();
}
} catch(config::error& e) {
LOG_CS << "error in receiving data...\n";
}
SDL_Delay(500);
}
}
}