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/settings.hpp"
28 #include "gui/widgets/retval.hpp"
29 #include "log.hpp"
30 #include "serialization/parser.hpp"
31 #include "game_version.hpp"
33 #include "addon/client.hpp"
34 
35 #include <boost/algorithm/string.hpp>
36 
37 static lg::log_domain log_config("config");
38 #define ERR_CFG LOG_STREAM(err , log_config)
39 #define LOG_CFG LOG_STREAM(info, log_config)
40 #define WRN_CFG LOG_STREAM(warn, log_config)
41 
42 static lg::log_domain log_filesystem("filesystem");
43 #define ERR_FS LOG_STREAM(err , log_filesystem)
44 
45 static lg::log_domain log_network("network");
46 #define ERR_NET LOG_STREAM(err , log_network)
47 #define LOG_NET LOG_STREAM(info, log_network)
48 
49 namespace {
50  std::string get_pbl_file_path(const std::string& addon_name)
51  {
52  const std::string& parentd = filesystem::get_addons_dir();
53  // Allow .pbl files directly in the addon dir
54  const std::string exterior = parentd + "/" + addon_name + ".pbl";
55  const std::string interior = parentd + "/" + addon_name + "/_server.pbl";
56  return filesystem::file_exists(exterior) ? exterior : interior;
57  }
58 
59  inline std::string get_info_file_path(const std::string& addon_name)
60  {
61  return filesystem::get_addons_dir() + "/" + addon_name + "/_info.cfg";
62  }
63 }
64 
65 bool have_addon_in_vcs_tree(const std::string& addon_name)
66 {
67  static const std::string parentd = filesystem::get_addons_dir();
68  return
69  filesystem::file_exists(parentd+"/"+addon_name+"/.svn") ||
70  filesystem::file_exists(parentd+"/"+addon_name+"/.git") ||
71  filesystem::file_exists(parentd+"/"+addon_name+"/.hg");
72 }
73 
74 bool have_addon_pbl_info(const std::string& addon_name)
75 {
76  return filesystem::file_exists(get_pbl_file_path(addon_name));
77 }
78 
79 config get_addon_pbl_info(const std::string& addon_name)
80 {
81  config cfg;
82  const std::string& pbl_path = get_pbl_file_path(addon_name);
83  try {
85  read(cfg, *stream);
86  } catch(const config::error& e) {
87  throw invalid_pbl_exception(pbl_path, e.message);
88  }
89 
90  return cfg;
91 }
92 
93 void set_addon_pbl_info(const std::string& addon_name, const config& cfg)
94 {
95  filesystem::scoped_ostream stream = filesystem::ostream_file(get_pbl_file_path(addon_name));
96  write(*stream, cfg);
97 }
98 
99 bool have_addon_install_info(const std::string& addon_name)
100 {
101  return filesystem::file_exists(get_info_file_path(addon_name));
102 }
103 
104 void get_addon_install_info(const std::string& addon_name, config& cfg)
105 {
106  const std::string& info_path = get_info_file_path(addon_name);
108  try {
109  read(cfg, *stream);
110  } catch(const config::error& e) {
111  ERR_CFG << "Failed to read add-on installation information for '"
112  << addon_name << "' from " << info_path << ":\n"
113  << e.message << std::endl;
114  }
115 }
116 
117 bool remove_local_addon(const std::string& addon)
118 {
119  const std::string addon_dir = filesystem::get_addons_dir() + "/" + addon;
120 
121  LOG_CFG << "removing local add-on: " << addon << '\n';
122 
123  if(filesystem::file_exists(addon_dir) && !filesystem::delete_directory(addon_dir, true)) {
124  ERR_CFG << "Failed to delete directory/file: " << addon_dir << '\n';
125  ERR_CFG << "removal of add-on " << addon << " failed!" << std::endl;
126  return false;
127  }
128  return true;
129 }
130 
131 std::vector<std::string> available_addons()
132 {
133  std::vector<std::string> res;
134  std::vector<std::string> files, dirs;
135  const std::string parentd = filesystem::get_addons_dir();
136  filesystem::get_files_in_dir(parentd,&files,&dirs);
137 
138  for(std::vector<std::string>::const_iterator i = dirs.begin(); i != dirs.end(); ++i) {
139  if (filesystem::file_exists(parentd + "/" + *i + "/_main.cfg") && have_addon_pbl_info(*i)) {
140  res.push_back(*i);
141  }
142  }
143 
144  return res;
145 }
146 
147 std::vector<std::string> installed_addons()
148 {
149  std::vector<std::string> res;
150  const std::string parentd = filesystem::get_addons_dir();
151  std::vector<std::string> files, dirs;
152  filesystem::get_files_in_dir(parentd,&files,&dirs);
153 
154  for(std::vector<std::string>::const_iterator i = dirs.begin(); i != dirs.end(); ++i) {
155  if(filesystem::file_exists(parentd + "/" + *i + "/_main.cfg")) {
156  res.push_back(*i);
157  }
158  }
159 
160  return res;
161 }
162 
163 bool is_addon_installed(const std::string& addon_name)
164 {
165  const std::string namestem = filesystem::get_addons_dir() + "/" + addon_name;
166  return filesystem::file_exists(namestem + "/_main.cfg");
167 }
168 
169 static inline bool IsCR(const char& c)
170 {
171  return c == '\x0D';
172 }
173 
174 static std::string strip_cr(std::string str, bool strip)
175 {
176  if(!strip)
177  return str;
178  std::string::iterator new_end = std::remove_if(str.begin(), str.end(), IsCR);
179  str.erase(new_end, str.end());
180  return str;
181 }
182 
183 static filesystem::blacklist_pattern_list read_ignore_patterns(const std::string& addon_name)
184 {
185  const std::string parentd = filesystem::get_addons_dir();
186  const std::string ign_file = parentd + "/" + addon_name + "/_server.ign";
187 
189  LOG_CFG << "searching for .ign file for '" << addon_name << "'...\n";
190  if (!filesystem::file_exists(ign_file)) {
191  LOG_CFG << "no .ign file found for '" << addon_name << "'\n"
192  << "using default ignore patterns...\n";
194  }
195  LOG_CFG << "found .ign file: " << ign_file << '\n';
196  auto stream = filesystem::istream_file(ign_file);
197  std::string line;
198  while (std::getline(*stream, line)) {
199  boost::trim(line);
200  const std::size_t l = line.size();
201  // .gitignore & WML like comments
202  if (l == 0 || !line.compare(0,2,"# ")) continue;
203  if (line[l - 1] == '/') { // directory; we strip the last /
204  patterns.add_directory_pattern(line.substr(0, l - 1));
205  } else { // file
206  patterns.add_file_pattern(line);
207  }
208  }
209  return patterns;
210 }
211 
212 static void archive_file(const std::string& path, const std::string& fname, config& cfg)
213 {
214  cfg["name"] = fname;
215  const bool is_cfg = (fname.size() > 4 ? (fname.substr(fname.size() - 4) == ".cfg") : false);
216  cfg["contents"] = encode_binary(strip_cr(filesystem::read_file(path + '/' + fname),is_cfg));
217 }
218 
219 static void archive_dir(const std::string& path, const std::string& dirname, config& cfg, const filesystem::blacklist_pattern_list& ignore_patterns)
220 {
221  cfg["name"] = dirname;
222  const std::string dir = path + '/' + dirname;
223 
224  std::vector<std::string> files, dirs;
225  filesystem::get_files_in_dir(dir,&files,&dirs);
226  for(const std::string& name : files) {
227  bool valid = !filesystem::looks_like_pbl(name) && !ignore_patterns.match_file(name);
228  if (valid) {
229  archive_file(dir,name,cfg.add_child("file"));
230  }
231  }
232 
233  for(const std::string& name : dirs) {
234  bool valid = !ignore_patterns.match_dir(name);
235  if (valid) {
236  archive_dir(dir,name,cfg.add_child("dir"),ignore_patterns);
237  }
238  }
239 }
240 
241 void archive_addon(const std::string& addon_name, config& cfg)
242 {
243  const std::string parentd = filesystem::get_addons_dir();
244 
245  filesystem::blacklist_pattern_list ignore_patterns(read_ignore_patterns(addon_name));
246  archive_dir(parentd, addon_name, cfg.add_child("dir"), ignore_patterns);
247 }
248 
249 static void unarchive_file(const std::string& path, const config& cfg)
250 {
251  filesystem::write_file(path + '/' + cfg["name"].str(), unencode_binary(cfg["contents"]));
252 }
253 
254 static void unarchive_dir(const std::string& path, const config& cfg)
255 {
256  std::string dir;
257  if (cfg["name"].empty())
258  dir = path;
259  else
260  dir = path + '/' + cfg["name"].str();
261 
263 
264  for(const config &d : cfg.child_range("dir")) {
265  unarchive_dir(dir, d);
266  }
267 
268  for(const config &f : cfg.child_range("file")) {
269  unarchive_file(dir, f);
270  }
271 }
272 
273 void unarchive_addon(const config& cfg)
274 {
275  const std::string parentd = filesystem::get_addons_dir();
276  unarchive_dir(parentd, cfg);
277 }
278 
279 namespace {
280  std::map< std::string, version_info > version_info_cache;
281 } // end unnamed namespace 5
282 
284 {
285  version_info_cache.clear();
286 
287  LOG_CFG << "refreshing add-on versions cache\n";
288 
289  const std::vector<std::string>& addons = installed_addons();
290  if(addons.empty()) {
291  return;
292  }
293 
294  std::vector<std::string> addon_info_files(addons.size());
295 
296  std::transform(addons.begin(), addons.end(),
297  addon_info_files.begin(), get_info_file_path);
298 
299  for(std::size_t i = 0; i < addon_info_files.size(); ++i) {
300  assert(i < addons.size());
301 
302  const std::string& addon = addons[i];
303  const std::string& info_file = addon_info_files[i];
304 
305  if(filesystem::file_exists(info_file)) {
306  config cfg;
307  get_addon_install_info(addon, cfg);
308 
309  const config& info_cfg = cfg.child("info");
310  if(!info_cfg) {
311  continue;
312  }
313 
314  const std::string& version = info_cfg["version"].str();
315  LOG_CFG << "cached add-on version: " << addon << " [" << version << "]\n";
316 
317  version_info_cache[addon] = version;
318  } else if (!have_addon_pbl_info(addon) && !have_addon_in_vcs_tree(addon)) {
319  // Don't print the warning if the user is clearly the author
320  WRN_CFG << "add-on '" << addon << "' has no _info.cfg; cannot read version info" << std::endl;
321  }
322  }
323 }
324 
325 version_info get_addon_version_info(const std::string& addon)
326 {
327  static const version_info nil;
328  std::map< std::string, version_info >::iterator entry = version_info_cache.find(addon);
329  return entry != version_info_cache.end() ? entry->second : nil;
330 }
bool delete_directory(const std::string &dirname, const bool keep_pbl)
Definition: filesystem.cpp:807
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
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:273
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:271
std::string unencode_binary(const std::string &str)
Definition: validation.cpp:291
bool is_addon_installed(const std::string &addon_name)
Check whether the specified add-on is currently installed.
Definition: manager.cpp:163
child_itors child_range(config_key_type key)
Definition: config.cpp:366
#define ERR_CFG
Definition: manager.cpp:38
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error)
Definition: filesystem.cpp:865
static bool IsCR(const char &c)
Definition: manager.cpp:169
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:104
static std::string strip_cr(std::string str, bool strip)
Definition: manager.cpp:174
#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:65
version_info get_addon_version_info(const std::string &addon)
Returns a particular installed add-on&#39;s version information.
Definition: manager.cpp:325
std::vector< std::string > available_addons()
Returns a list of local add-ons that can be published.
Definition: manager.cpp:131
#define LOG_CFG
Definition: manager.cpp:39
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:99
static void archive_file(const std::string &path, const std::string &fname, config &cfg)
Definition: manager.cpp:212
static const char * name(const std::vector< SDL_Joystick *> &joysticks, const std::size_t index)
Definition: joystick.cpp:48
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:249
This file contains the settings handling of the widget library.
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:926
void set_addon_pbl_info(const std::string &addon_name, const config &cfg)
Definition: manager.cpp:93
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:147
std::string path
Definition: game_config.cpp:39
void refresh_addon_version_info_cache()
Refreshes the per-session cache of add-on&#39;s version information structs.
Definition: manager.cpp:283
bool remove_local_addon(const std::string &addon)
Definition: manager.cpp:117
std::string read_file(const std::string &fname)
Basic disk I/O - read file.
Definition: filesystem.cpp:857
config get_addon_pbl_info(const std::string &addon_name)
Gets the publish information for an add-on.
Definition: manager.cpp:79
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:357
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:74
void archive_addon(const std::string &addon_name, config &cfg)
Archives an add-on into a config object for campaignd transactions.
Definition: manager.cpp:241
static lg::log_domain log_config("config")
bool make_directory(const std::string &dirname)
Definition: filesystem.cpp:796
static filesystem::blacklist_pattern_list read_ignore_patterns(const std::string &addon_name)
Definition: manager.cpp:183
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:219
Represents version numbers.
config & add_child(config_key_type key)
Definition: config.cpp:479
#define f
#define WRN_CFG
Definition: manager.cpp:40
void unarchive_addon(const config &cfg)
Definition: manager.cpp:273
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:92
mock_char c
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
static void unarchive_dir(const std::string &path, const config &cfg)
Definition: manager.cpp:254
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:903