publish_campaign.cpp

Go to the documentation of this file.
00001 /* $Id: publish_campaign.cpp 23842 2008-02-16 08:47:16Z mordante $ */
00002 /*
00003    Copyright (C) 2003 - 2008 by David White <dave@whitevine.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License version 2
00008    or at your option any later version.
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY.
00011 
00012    See the COPYING file for more details.
00013 */
00014 
00015 #include "global.hpp"
00016 
00017 #include "config.hpp"
00018 #include "filesystem.hpp"
00019 #include "publish_campaign.hpp"
00020 #include "serialization/parser.hpp"
00021 
00022 #include <algorithm>
00023 #include <cstring>
00024 
00025 static const std::string& campaign_dir()
00026 {
00027     static const std::string res = get_user_data_dir() + "/data/campaigns";
00028     return res;
00029 }
00030 
00031 static void setup_dirs()
00032 {
00033     make_directory(get_user_data_dir() + "/data");
00034     make_directory(campaign_dir());
00035 }
00036 
00037 void get_campaign_info(const std::string& campaign_name, config& cfg)
00038 {
00039     // Cope with old-style or new-style file organization 
00040     std::string exterior = campaign_dir() + "/" + campaign_name + ".pbl";
00041     std::string interior = campaign_dir() + "/" + campaign_name + "/_server.pbl";
00042     std::string pbl_file;
00043 
00044     if (file_exists(exterior))
00045         pbl_file = exterior;
00046     else
00047         pbl_file = interior;
00048 
00049     scoped_istream stream = istream_file(pbl_file);
00050     read(cfg, *stream);
00051 }
00052 
00053 void set_campaign_info(const std::string& campaign_name, const config& cfg)
00054 {
00055     scoped_ostream stream = ostream_file(campaign_dir() + "/" + campaign_name + ".pbl");
00056     write(*stream, cfg);
00057 }
00058 
00059 std::vector<std::string> available_campaigns()
00060 {
00061     std::vector<std::string> res;
00062 
00063     std::vector<std::string> files, dirs;
00064     get_files_in_dir(campaign_dir(),&files,&dirs);
00065 
00066     for(std::vector<std::string>::const_iterator i = dirs.begin(); i != dirs.end(); ++i) {
00067         const std::string external_cfg_file = *i + ".cfg";
00068         const std::string internal_cfg_file = *i + "/_main.cfg";
00069         const std::string external_pbl_file = *i + ".pbl";
00070         const std::string internal_pbl_file = *i + "/_server.pbl";
00071         if((std::find(files.begin(),files.end(),external_cfg_file) != files.end() || file_exists(campaign_dir() + "/" + internal_cfg_file)) &&
00072            (std::find(files.begin(),files.end(),external_pbl_file) != files.end() || (file_exists(campaign_dir() + "/" + internal_pbl_file)))) {
00073             res.push_back(*i);
00074         }
00075     }
00076 
00077     return res;
00078 }
00079 
00080 /// Return the names of all installed campaigns.
00081 std::vector<std::string> installed_campaigns()
00082 {
00083     std::vector<std::string> res;
00084 
00085     std::vector<std::string> files, dirs;
00086     get_files_in_dir(campaign_dir(),&files,&dirs);
00087 
00088     for(std::vector<std::string>::const_iterator i = dirs.begin(); i != dirs.end(); ++i) {
00089         const std::string external_cfg_file = *i + ".cfg";
00090         const std::string internal_cfg_file = *i + "/_main.cfg";
00091         if(std::find(files.begin(),files.end(),external_cfg_file) != files.end() || file_exists(campaign_dir() + "/" + internal_cfg_file)) {
00092             res.push_back(*i);
00093         }
00094     }
00095 
00096     return res;
00097 }
00098 
00099 // Return a vector of detected scripts.
00100 std::vector<config *> find_scripts(const config &cfg, std::string extension)
00101 {
00102     std::vector<config *> python_scripts;
00103     const config::child_list& dirs = cfg.get_children("dir");
00104     config::child_list::const_iterator i;
00105     for(i = dirs.begin(); i != dirs.end(); ++i) {
00106         const config::child_list& files = (**i).get_children("file");
00107         config::child_list::const_iterator j;
00108         for(j = files.begin(); j != files.end(); ++j) {
00109             std::string filename = (**j)["name"].str();
00110             if (filename.length() > extension.length()) {
00111                 if (filename.substr(filename.length() - extension.length()) ==
00112                     extension) {
00113                     python_scripts.push_back(*j);
00114                 }
00115             }
00116         }
00117         // Recursively look for files in sub directories.
00118         std::vector<config *> childs = find_scripts(**i, extension);
00119         python_scripts.insert(python_scripts.end(),
00120             childs.begin(), childs.end());
00121     }
00122     return python_scripts;
00123 }
00124 
00125 namespace {
00126 
00127 const char escape_char = 1;
00128 
00129 }
00130 
00131 static bool needs_escaping(char c) { return c == 0 || c == escape_char; }
00132 
00133 static std::string encode_binary(const std::string& str)
00134 {
00135     std::string res;
00136     res.resize(str.size());
00137     size_t n = 0;
00138     for(std::string::const_iterator j = str.begin(); j != str.end(); ++j) {
00139         if(needs_escaping(*j)) {
00140             res.resize(res.size()+1);
00141             res[n++] = escape_char;
00142             res[n++] = *j + 1;
00143         } else {
00144             res[n++] = *j;
00145         }
00146     }
00147 
00148     return res;
00149 }
00150 
00151 static std::string unencode_binary(const std::string& str)
00152 {
00153     std::string res;
00154     res.resize(str.size());
00155 
00156     size_t n = 0;
00157     for(std::string::const_iterator j = str.begin(); j != str.end(); ++j) {
00158         if(*j == escape_char && j+1 != str.end()) {
00159             ++j;
00160             res[n++] = *j - 1;
00161             res.resize(res.size()-1);
00162         } else {
00163             res[n++] = *j;
00164         }
00165     }
00166 
00167     return res;
00168 }
00169 
00170 static std::pair<std::vector<std::string>, std::vector<std::string> > read_ignore_patterns(const std::string& campaign_name) {
00171     std::pair<std::vector<std::string>, std::vector<std::string> > patterns;
00172     std::string exterior = campaign_dir() + "/" + campaign_name + ".ign";
00173     std::string interior = campaign_dir() + "/" + campaign_name + "/_server.ign";
00174     std::string ign_file;
00175     if (file_exists(interior)) {
00176         ign_file = interior;
00177     } else if (file_exists(exterior)) {
00178         ign_file = exterior;
00179     } else { /* default patterns */
00180         patterns.first.push_back("*~");
00181         patterns.first.push_back("*-bak");
00182         patterns.first.push_back("*.pbl");
00183         patterns.first.push_back("*.ign");
00184         /* 
00185          * Prevent certain potential security compromises.
00186          * The idea is to stop bad guys from uploading things
00187          * that could become trojans if an unsuspoecting user 
00188          * downloads them.
00189          */
00190         patterns.first.push_back("*.exe");
00191         patterns.first.push_back("*.bat");
00192         patterns.first.push_back("*.com");
00193         patterns.first.push_back("*.scr");
00194         patterns.first.push_back("*.sh");
00195         return patterns;
00196     }
00197     std::istream *stream = istream_file(ign_file);
00198     std::string line;
00199     while (std::getline(*stream, line)) {
00200         size_t l = line.size();
00201         if (line[l - 1] == '/') { // directory; we strip the last /
00202             patterns.second.push_back(line.substr(0, l - 1));
00203         } else { // file
00204             patterns.first.push_back(line);
00205         }
00206     }
00207     return patterns;
00208 }
00209 
00210 static void archive_file(const std::string& path, const std::string& fname, config& cfg)
00211 {
00212     cfg["name"] = fname;
00213     cfg["contents"] = encode_binary(read_file(path + '/' + fname));
00214 }
00215 
00216 static void archive_dir(const std::string& path, const std::string& dirname, config& cfg, std::pair<std::vector<std::string>, std::vector<std::string> >& ignore_patterns)
00217 {
00218     cfg["name"] = dirname;
00219     const std::string dir = path + '/' + dirname;
00220 
00221     std::vector<std::string> files, dirs;
00222     get_files_in_dir(dir,&files,&dirs);
00223     for(std::vector<std::string>::const_iterator i = files.begin(); i != files.end(); ++i) {
00224         bool valid = true;
00225         for(std::vector<std::string>::const_iterator p = ignore_patterns.first.begin(); p != ignore_patterns.first.end(); ++p) {
00226             if (utils::wildcard_string_match(*i, *p)) {
00227                 valid = false;
00228                 break;
00229             }
00230         }
00231         if (valid) {
00232             archive_file(dir,*i,cfg.add_child("file"));
00233         }
00234     }
00235 
00236     for(std::vector<std::string>::const_iterator j = dirs.begin(); j != dirs.end(); ++j) {
00237         bool valid = true;
00238         for(std::vector<std::string>::const_iterator p = ignore_patterns.second.begin(); p != ignore_patterns.second.end(); ++p) {
00239             if (utils::wildcard_string_match(*j, *p)) {
00240                 valid = false;
00241                 break;
00242             }
00243         }
00244         if (valid) {
00245             archive_dir(dir,*j,cfg.add_child("dir"),ignore_patterns);
00246         }
00247     }
00248 }
00249 
00250 void archive_campaign(const std::string& campaign_name, config& cfg)
00251 {
00252     std::pair<std::vector<std::string>, std::vector<std::string> > ignore_patterns;
00253     // External .cfg may not exist; newer campaigns have a _main.cfg
00254     std::string external_cfg = campaign_name + ".cfg";
00255     if (file_exists(campaign_dir() + "/" + external_cfg))
00256         archive_file(campaign_dir(), external_cfg,cfg.add_child("file"));
00257     ignore_patterns = read_ignore_patterns(campaign_name);
00258     archive_dir(campaign_dir(),campaign_name,cfg.add_child("dir"),ignore_patterns);
00259 }
00260 
00261 static void unarchive_file(const std::string& path, const config& cfg)
00262 {
00263     write_file(path + '/' + cfg["name"].str(), unencode_binary(cfg["contents"]));
00264 }
00265 
00266 static void unarchive_dir(const std::string& path, const config& cfg)
00267 {
00268     std::string dir;
00269     if (cfg["name"].empty())
00270         dir = path;
00271     else
00272         dir = path + '/' + cfg["name"].str();
00273 
00274     make_directory(dir);
00275 
00276     const config::child_list& dirs = cfg.get_children("dir");
00277     for(config::child_list::const_iterator i = dirs.begin(); i != dirs.end(); ++i) {
00278         unarchive_dir(dir,**i);
00279     }
00280 
00281     const config::child_list& files = cfg.get_children("file");
00282     for(config::child_list::const_iterator j = files.begin(); j != files.end(); ++j) {
00283         unarchive_file(dir,**j);
00284     }
00285 }
00286 
00287 void unarchive_campaign(const config& cfg)
00288 {
00289     setup_dirs();
00290     unarchive_dir(campaign_dir(),cfg);
00291 }
00292 
00293 static bool two_dots(char a, char b) { return a == '.' && b == '.'; }
00294 
00295 bool campaign_name_legal(const std::string& name)
00296 {
00297     if(name == "" || strlen(name.c_str()) == 0 || name == "." ||
00298        std::find(name.begin(),name.end(),'/') != name.end() ||
00299        std::find(name.begin(),name.end(),'\\') != name.end() ||
00300        std::find(name.begin(),name.end(),':') != name.end() ||
00301        std::adjacent_find(name.begin(),name.end(),two_dots) != name.end()) {
00302         return false;
00303     } else {
00304         return true;
00305     }
00306 }
00307 
00308 bool check_names_legal(const config& dir)
00309 {
00310         const config::child_list& files = dir.get_children("file");
00311         for(config::child_list::const_iterator i = files.begin(); i != files.end(); ++i) {
00312                 if (!campaign_name_legal((**i)["name"])) return false;
00313         }
00314         const config::child_list& dirs = dir.get_children("dir");
00315         {
00316             for(config::child_list::const_iterator i = dirs.begin(); i != dirs.end(); ++i) {
00317                     if (!campaign_name_legal((**i)["name"])) return false;
00318                     if (!check_names_legal(**i)) return false;
00319             }
00320         }
00321         return true;
00322 }

Generated by doxygen 1.5.5 on 23 May 2008 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs