The Battle for Wesnoth  1.15.0-dev
manager.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2008 by David White <dave@whitevine.net>
3  2008 - 2015 by Iris Morelle <shadowm2006@gmail.com>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #include "addon/manager.hpp"
17 #include "addon/manager_ui.hpp"
18 #include "filesystem.hpp"
19 #include "preferences/game.hpp"
20 #include "gettext.hpp"
24 #include "gui/dialogs/message.hpp"
27 #include "gui/widgets/retval.hpp"
28 #include "log.hpp"
29 #include "serialization/parser.hpp"
30 #include "game_version.hpp"
32 #include "addon/client.hpp"
33 
34 #include <boost/algorithm/string.hpp>
35 
36 static lg::log_domain log_config("config");
37 #define ERR_CFG LOG_STREAM(err , log_config)
38 #define LOG_CFG LOG_STREAM(info, log_config)
39 #define WRN_CFG LOG_STREAM(warn, log_config)
40 
41 static lg::log_domain log_filesystem("filesystem");
42 #define ERR_FS LOG_STREAM(err , log_filesystem)
43 
44 static lg::log_domain log_network("network");
45 #define ERR_NET LOG_STREAM(err , log_network)
46 #define LOG_NET LOG_STREAM(info, log_network)
47 
48 namespace {
49  std::string get_pbl_file_path(const std::string& addon_name)
50  {
51  const std::string& parentd = filesystem::get_addons_dir();
52  // Allow .pbl files directly in the addon dir
53  const std::string exterior = parentd + "/" + addon_name + ".pbl";
54  const std::string interior = parentd + "/" + addon_name + "/_server.pbl";
55  return filesystem::file_exists(exterior) ? exterior : interior;
56  }
57 
58  inline std::string get_info_file_path(const std::string& addon_name)
59  {
60  return filesystem::get_addons_dir() + "/" + addon_name + "/_info.cfg";
61  }
62 }
63 
64 bool have_addon_in_vcs_tree(const std::string& addon_name)
65 {
66  static const std::string parentd = filesystem::get_addons_dir();
67  return
68  filesystem::file_exists(parentd+"/"+addon_name+"/.svn") ||
69  filesystem::file_exists(parentd+"/"+addon_name+"/.git") ||
70  filesystem::file_exists(parentd+"/"+addon_name+"/.hg");
71 }
72 
73 bool have_addon_pbl_info(const std::string& addon_name)
74 {
75  return filesystem::file_exists(get_pbl_file_path(addon_name));
76 }
77 
78 config get_addon_pbl_info(const std::string& addon_name)
79 {
80  config cfg;
81  const std::string& pbl_path = get_pbl_file_path(addon_name);
82  try {
84  read(cfg, *stream);
85  } catch(const config::error& e) {
86  throw invalid_pbl_exception(pbl_path, e.message);
87  }
88 
89  return cfg;
90 }
91 
92 void set_addon_pbl_info(const std::string& addon_name, const config& cfg)
93 {
94  filesystem::scoped_ostream stream = filesystem::ostream_file(get_pbl_file_path(addon_name));
95  write(*stream, cfg);
96 }
97 
98 bool have_addon_install_info(const std::string& addon_name)
99 {
100  return filesystem::file_exists(get_info_file_path(addon_name));
101 }
102 
103 void get_addon_install_info(const std::string& addon_name, config& cfg)
104 {
105  const std::string& info_path = get_info_file_path(addon_name);
107  try {
108  read(cfg, *stream);
109  } catch(const config::error& e) {
110  ERR_CFG << "Failed to read add-on installation information for '"
111  << addon_name << "' from " << info_path << ":\n"
112  << e.message << std::endl;
113  }
114 }
115 
116 bool remove_local_addon(const std::string& addon)
117 {
118  const std::string addon_dir = filesystem::get_addons_dir() + "/" + addon;
119 
120  LOG_CFG << "removing local add-on: " << addon << '\n';
121 
122  if(filesystem::file_exists(addon_dir) && !filesystem::delete_directory(addon_dir, true)) {
123  ERR_CFG << "Failed to delete directory/file: " << addon_dir << '\n';
124  ERR_CFG << "removal of add-on " << addon << " failed!" << std::endl;
125  return false;
126  }
127  return true;
128 }
129 
130 std::vector<std::string> available_addons()
131 {
132  std::vector<std::string> res;
133  std::vector<std::string> files, dirs;
134  const std::string parentd = filesystem::get_addons_dir();
135  filesystem::get_files_in_dir(parentd,&files,&dirs);
136 
137  for(std::vector<std::string>::const_iterator i = dirs.begin(); i != dirs.end(); ++i) {
138  if (filesystem::file_exists(parentd + "/" + *i + "/_main.cfg") && have_addon_pbl_info(*i)) {
139  res.push_back(*i);
140  }
141  }
142 
143  return res;
144 }
145 
146 std::vector<std::string> installed_addons()
147 {
148  std::vector<std::string> res;
149  const std::string parentd = filesystem::get_addons_dir();
150  std::vector<std::string> files, dirs;
151  filesystem::get_files_in_dir(parentd,&files,&dirs);
152 
153  for(std::vector<std::string>::const_iterator i = dirs.begin(); i != dirs.end(); ++i) {
154  if(filesystem::file_exists(parentd + "/" + *i + "/_main.cfg")) {
155  res.push_back(*i);
156  }
157  }
158 
159  return res;
160 }
161 
162 bool is_addon_installed(const std::string& addon_name)
163 {
164  const std::string namestem = filesystem::get_addons_dir() + "/" + addon_name;
165  return filesystem::file_exists(namestem + "/_main.cfg");
166 }
167 
168 static inline bool IsCR(const char& c)
169 {
170  return c == '\x0D';
171 }
172 
173 static std::string strip_cr(std::string str, bool strip)
174 {
175  if(!strip)
176  return str;
177  std::string::iterator new_end = std::remove_if(str.begin(), str.end(), IsCR);
178  str.erase(new_end, str.end());
179  return str;
180 }
181 
182 static filesystem::blacklist_pattern_list read_ignore_patterns(const std::string& addon_name)
183 {
184  const std::string parentd = filesystem::get_addons_dir();
185  const std::string ign_file = parentd + "/" + addon_name + "/_server.ign";
186 
188  LOG_CFG << "searching for .ign file for '" << addon_name << "'...\n";
189  if (!filesystem::file_exists(ign_file)) {
190  LOG_CFG << "no .ign file found for '" << addon_name << "'\n"
191  << "using default ignore patterns...\n";
193  }
194  LOG_CFG << "found .ign file: " << ign_file << '\n';
195  auto stream = filesystem::istream_file(ign_file);
196  std::string line;
197  while (std::getline(*stream, line)) {
198  boost::trim(line);
199  const std::size_t l = line.size();
200  // .gitignore & WML like comments
201  if (l == 0 || !line.compare(0,2,"# ")) continue;
202  if (line[l - 1] == '/') { // directory; we strip the last /
203  patterns.add_directory_pattern(line.substr(0, l - 1));
204  } else { // file
205  patterns.add_file_pattern(line);
206  }
207  }
208  return patterns;
209 }
210 
211 static void archive_file(const std::string& path, const std::string& fname, config& cfg)
212 {
213  cfg["name"] = fname;
214  const bool is_cfg = (fname.size() > 4 ? (fname.substr(fname.size() - 4) == ".cfg") : false);
215  cfg["contents"] = encode_binary(strip_cr(filesystem::read_file(path + '/' + fname),is_cfg));
216 }
217 
218 static void archive_dir(const std::string& path, const std::string& dirname, config& cfg, const filesystem::blacklist_pattern_list& ignore_patterns)
219 {
220  cfg["name"] = dirname;
221  const std::string dir = path + '/' + dirname;
222 
223  std::vector<std::string> files, dirs;
224  filesystem::get_files_in_dir(dir,&files,&dirs);
225  for(const std::string& name : files) {
226  bool valid = !filesystem::looks_like_pbl(name) && !ignore_patterns.match_file(name);
227  if (valid) {
228  archive_file(dir,name,cfg.add_child("file"));
229  }
230  }
231 
232  for(const std::string& name : dirs) {
233  bool valid = !ignore_patterns.match_dir(name);
234  if (valid) {
235  archive_dir(dir,name,cfg.add_child("dir"),ignore_patterns);
236  }
237  }
238 }
239 
240 void archive_addon(const std::string& addon_name, config& cfg)
241 {
242  const std::string parentd = filesystem::get_addons_dir();
243 
244  filesystem::blacklist_pattern_list ignore_patterns(read_ignore_patterns(addon_name));
245  archive_dir(parentd, addon_name, cfg.add_child("dir"), ignore_patterns);
246 }
247 
248 static void unarchive_file(const std::string& path, const config& cfg)
249 {
250  filesystem::write_file(path + '/' + cfg["name"].str(), unencode_binary(cfg["contents"]));
251 }
252 
253 static void unarchive_dir(const std::string& path, const config& cfg)
254 {
255  std::string dir;
256  if (cfg["name"].empty())
257  dir = path;
258  else
259  dir = path + '/' + cfg["name"].str();
260 
262 
263  for(const config &d : cfg.child_range("dir")) {
264  unarchive_dir(dir, d);
265  }
266 
267  for(const config &f : cfg.child_range("file")) {
268  unarchive_file(dir, f);
269  }
270 }
271 
272 void unarchive_addon(const config& cfg)
273 {
274  const std::string parentd = filesystem::get_addons_dir();
275  unarchive_dir(parentd, cfg);
276 }
277 
278 namespace {
279  std::map< std::string, version_info > version_info_cache;
280 } // end unnamed namespace 5
281 
283 {
284  version_info_cache.clear();
285 
286  LOG_CFG << "refreshing add-on versions cache\n";
287 
288  const std::vector<std::string>& addons = installed_addons();
289  if(addons.empty()) {
290  return;
291  }
292 
293  std::vector<std::string> addon_info_files(addons.size());
294 
295  std::transform(addons.begin(), addons.end(),
296  addon_info_files.begin(), get_info_file_path);
297 
298  for(std::size_t i = 0; i < addon_info_files.size(); ++i) {
299  assert(i < addons.size());
300 
301  const std::string& addon = addons[i];
302  const std::string& info_file = addon_info_files[i];
303 
304  if(filesystem::file_exists(info_file)) {
305  config cfg;
306  get_addon_install_info(addon, cfg);
307 
308  const config& info_cfg = cfg.child("info");
309  if(!info_cfg) {
310  continue;
311  }
312 
313  const std::string& version = info_cfg["version"].str();
314  LOG_CFG << "cached add-on version: " << addon << " [" << version << "]\n";
315 
316  version_info_cache[addon] = version;
317  } else if (!have_addon_pbl_info(addon) && !have_addon_in_vcs_tree(addon)) {
318  // Don't print the warning if the user is clearly the author
319  WRN_CFG << "add-on '" << addon << "' has no _info.cfg; cannot read version info" << std::endl;
320  }
321  }
322 }
323 
324 version_info get_addon_version_info(const std::string& addon)
325 {
326  static const version_info nil;
327  std::map< std::string, version_info >::iterator entry = version_info_cache.find(addon);
328  return entry != version_info_cache.end() ? entry->second : nil;
329 }
bool delete_directory(const std::string &dirname, const bool keep_pbl)
Definition: filesystem.cpp:836
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:423
Interfaces for manipulating version numbers of engine, add-ons, etc.
bool looks_like_pbl(const std::string &file)
void add_directory_pattern(const std::string &pattern)
Definition: filesystem.hpp:87
static bool file_exists(const fs::path &fpath)
Definition: filesystem.cpp:300
Exception thrown when the WML parser fails to read a .pbl file.
Definition: manager.hpp:30
std::string encode_binary(const std::string &str)
Definition: validation.cpp:274
std::string unencode_binary(const std::string &str)
Definition: validation.cpp:292
bool is_addon_installed(const std::string &addon_name)
Check whether the specified add-on is currently installed.
Definition: manager.cpp:162
child_itors child_range(config_key_type key)
Definition: config.cpp:366
#define ERR_CFG
Definition: manager.cpp:37
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error)
Definition: filesystem.cpp:894
static bool IsCR(const char &c)
Definition: manager.cpp:168
void get_addon_install_info(const std::string &addon_name, config &cfg)
Gets the installation info (_info.cfg) for an add-on.
Definition: manager.cpp:103
static std::string strip_cr(std::string str, bool strip)
Definition: manager.cpp:173
#define d
static lg::log_domain log_network("network")
bool have_addon_in_vcs_tree(const std::string &addon_name)
Returns true if the specified add-ons appear to be managed by a &#39;supported&#39; VCS.
Definition: manager.cpp:64
version_info get_addon_version_info(const std::string &addon)
Returns a particular installed add-on&#39;s version information.
Definition: manager.cpp:324
std::vector< std::string > available_addons()
Returns a list of local add-ons that can be published.
Definition: manager.cpp:130
#define LOG_CFG
Definition: manager.cpp:38
bool have_addon_install_info(const std::string &addon_name)
Returns true if there is a local installation info (_info.cfg) file for the add-on.
Definition: manager.cpp:98
static void archive_file(const std::string &path, const std::string &fname, config &cfg)
Definition: manager.cpp:211
void write(std::ostream &out, const configr_of &cfg, unsigned int level)
Definition: parser.cpp:749
bool match_file(const std::string &name) const
Definition: filesystem.hpp:70
static void unarchive_file(const std::string &path, const config &cfg)
Definition: manager.cpp:248
const t_string name
void read(config &cfg, std::istream &in, abstract_validator *validator)
Definition: parser.cpp:612
void write_file(const std::string &fname, const std::string &data)
Throws io_exception if an error occurs.
Definition: filesystem.cpp:955
void set_addon_pbl_info(const std::string &addon_name, const config &cfg)
Definition: manager.cpp:92
std::unique_ptr< std::istream > scoped_istream
Definition: filesystem.hpp:39
std::vector< std::string > installed_addons()
Retrieves the names of all installed add-ons.
Definition: manager.cpp:146
void refresh_addon_version_info_cache()
Refreshes the per-session cache of add-on&#39;s version information structs.
Definition: manager.cpp:282
bool remove_local_addon(const std::string &addon)
Definition: manager.cpp:116
std::string read_file(const std::string &fname)
Basic disk I/O - read file.
Definition: filesystem.cpp:886
config get_addon_pbl_info(const std::string &addon_name)
Gets the publish information for an add-on.
Definition: manager.cpp:78
void add_file_pattern(const std::string &pattern)
Definition: filesystem.hpp:82
static lg::log_domain log_filesystem("filesystem")
std::unique_ptr< std::ostream > scoped_ostream
Definition: filesystem.hpp:40
void get_files_in_dir(const std::string &dir, std::vector< std::string > *files, std::vector< std::string > *dirs, file_name_option mode, file_filter_option filter, file_reorder_option reorder, file_tree_checksum *checksum)
Populates &#39;files&#39; with all the files and &#39;dirs&#39; with all the directories in dir.
Definition: filesystem.cpp:386
std::size_t i
Definition: function.cpp:933
bool have_addon_pbl_info(const std::string &addon_name)
Returns true if there&#39;s a local .pbl file stored for the specified add-on.
Definition: manager.cpp:73
void archive_addon(const std::string &addon_name, config &cfg)
Archives an add-on into a config object for campaignd transactions.
Definition: manager.cpp:240
static lg::log_domain log_config("config")
bool make_directory(const std::string &dirname)
Definition: filesystem.cpp:825
static filesystem::blacklist_pattern_list read_ignore_patterns(const std::string &addon_name)
Definition: manager.cpp:182
Declarations for File-IO.
static void archive_dir(const std::string &path, const std::string &dirname, config &cfg, const filesystem::blacklist_pattern_list &ignore_patterns)
Definition: manager.cpp:218
Represents version numbers.
config & add_child(config_key_type key)
Definition: config.cpp:479
#define f
#define WRN_CFG
Definition: manager.cpp:39
void unarchive_addon(const config &cfg)
Definition: manager.cpp:272
std::string get_addons_dir()
Standard logging facilities (interface).
std::string message
Definition: exceptions.hpp:31
#define e
bool match_dir(const std::string &name) const
Definition: filesystem.hpp:76
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
mock_char c
std::string path
File path.
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
static void unarchive_dir(const std::string &path, const config &cfg)
Definition: manager.cpp:253
static const blacklist_pattern_list default_blacklist
Definition: filesystem.hpp:99
filesystem::scoped_ostream ostream_file(const std::string &fname, bool create_directory)
Definition: filesystem.cpp:932