The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
manager.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2008 by David White <[email protected]>
3  2008 - 2015 by Ignacio R. Morelle <[email protected]>
4  Part of the Battle for Wesnoth Project http://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 "global.hpp"
17 
18 #include "addon/manager.hpp"
19 #include "addon/manager_ui.hpp"
20 #include "dialogs.hpp"
21 #include "filesystem.hpp"
22 #include "formatter.hpp"
23 #include "game_preferences.hpp"
24 #include "gettext.hpp"
29 #include "gui/dialogs/message.hpp"
33 #include "gui/widgets/settings.hpp"
34 #include "gui/widgets/window.hpp"
35 #include "log.hpp"
36 #include "marked-up_text.hpp"
37 #include "serialization/parser.hpp"
38 #include "version.hpp"
39 #include "wml_separators.hpp"
40 #include "formula/string_utils.hpp"
41 #include "addon/client.hpp"
42 
43 static lg::log_domain log_config("config");
44 #define ERR_CFG LOG_STREAM(err , log_config)
45 #define LOG_CFG LOG_STREAM(info, log_config)
46 #define WRN_CFG LOG_STREAM(warn, log_config)
47 
48 static lg::log_domain log_filesystem("filesystem");
49 #define ERR_FS LOG_STREAM(err , log_filesystem)
50 
51 static lg::log_domain log_network("network");
52 #define ERR_NET LOG_STREAM(err , log_network)
53 #define LOG_NET LOG_STREAM(info, log_network)
54 
55 namespace {
56  std::string get_pbl_file_path(const std::string& addon_name)
57  {
58  const std::string& parentd = filesystem::get_addons_dir();
59  // Allow .pbl files directly in the addon dir
60  const std::string exterior = parentd + "/" + addon_name + ".pbl";
61  const std::string interior = parentd + "/" + addon_name + "/_server.pbl";
62  return filesystem::file_exists(exterior) ? exterior : interior;
63  }
64 
65  inline std::string get_info_file_path(const std::string& addon_name)
66  {
67  return filesystem::get_addons_dir() + "/" + addon_name + "/_info.cfg";
68  }
69 }
70 
71 bool have_addon_in_vcs_tree(const std::string& addon_name)
72 {
73  static const std::string parentd = filesystem::get_addons_dir();
74  return
75  filesystem::file_exists(parentd+"/"+addon_name+"/.svn") ||
76  filesystem::file_exists(parentd+"/"+addon_name+"/.git") ||
77  filesystem::file_exists(parentd+"/"+addon_name+"/.hg");
78 }
79 
80 bool have_addon_pbl_info(const std::string& addon_name)
81 {
82  return filesystem::file_exists(get_pbl_file_path(addon_name));
83 }
84 
85 void get_addon_pbl_info(const std::string& addon_name, config& cfg)
86 {
87  const std::string& pbl_path = get_pbl_file_path(addon_name);
88  try {
90  read(cfg, *stream);
91  } catch(const config::error& e) {
92  throw invalid_pbl_exception(pbl_path, e.message);
93  }
94 }
95 
96 void set_addon_pbl_info(const std::string& addon_name, const config& cfg)
97 {
98  filesystem::scoped_ostream stream = filesystem::ostream_file(get_pbl_file_path(addon_name));
99  write(*stream, cfg);
100 }
101 
102 bool have_addon_install_info(const std::string& addon_name)
103 {
104  return filesystem::file_exists(get_info_file_path(addon_name));
105 }
106 
107 void get_addon_install_info(const std::string& addon_name, config& cfg)
108 {
109  const std::string& info_path = get_info_file_path(addon_name);
111  try {
112  read(cfg, *stream);
113  } catch(const config::error& e) {
114  ERR_CFG << "Failed to read add-on installation information for '"
115  << addon_name << "' from " << info_path << ":\n"
116  << e.message << std::endl;
117  }
118 }
119 
120 bool remove_local_addon(const std::string& addon)
121 {
122  const std::string addon_dir = filesystem::get_addons_dir() + "/" + addon;
123 
124  LOG_CFG << "removing local add-on: " << addon << '\n';
125 
126  if(filesystem::file_exists(addon_dir) && !filesystem::delete_directory(addon_dir, true)) {
127  ERR_CFG << "Failed to delete directory/file: " << addon_dir << '\n';
128  ERR_CFG << "removal of add-on " << addon << " failed!" << std::endl;
129  return false;
130  }
131  return true;
132 }
133 
134 std::vector<std::string> available_addons()
135 {
136  std::vector<std::string> res;
137  std::vector<std::string> files, dirs;
138  const std::string parentd = filesystem::get_addons_dir();
139  filesystem::get_files_in_dir(parentd,&files,&dirs);
140 
141  for(std::vector<std::string>::const_iterator i = dirs.begin(); i != dirs.end(); ++i) {
142  if (filesystem::file_exists(parentd + "/" + *i + "/_main.cfg") && have_addon_pbl_info(*i)) {
143  res.push_back(*i);
144  }
145  }
146 
147  return res;
148 }
149 
150 std::vector<std::string> installed_addons()
151 {
152  std::vector<std::string> res;
153  const std::string parentd = filesystem::get_addons_dir();
154  std::vector<std::string> files, dirs;
155  filesystem::get_files_in_dir(parentd,&files,&dirs);
156 
157  for(std::vector<std::string>::const_iterator i = dirs.begin(); i != dirs.end(); ++i) {
158  if(filesystem::file_exists(parentd + "/" + *i + "/_main.cfg")) {
159  res.push_back(*i);
160  }
161  }
162 
163  return res;
164 }
165 
166 bool is_addon_installed(const std::string& addon_name)
167 {
168  const std::string namestem = filesystem::get_addons_dir() + "/" + addon_name;
169  return filesystem::file_exists(namestem + "/_main.cfg");
170 }
171 
172 static inline bool IsCR(const char& c)
173 {
174  return c == '\x0D';
175 }
176 
178 {
179  if(!strip)
180  return str;
181  std::string::iterator new_end = std::remove_if(str.begin(), str.end(), IsCR);
182  str.erase(new_end, str.end());
183  return str;
184 }
185 
186 namespace {
187  void append_default_ignore_patterns(std::pair<std::vector<std::string>, std::vector<std::string> >& patterns)
188  {
189  std::vector<std::string>& files = patterns.first;
190  std::vector<std::string>& dirs = patterns.second;
191 
192  /* Don't upload dot-files/dirs, which are hidden files in
193  UNIX platforms */
194  files.push_back(".*");
195  dirs.push_back(".*");
196  /* MacOS X metadata-like cruft (http://floatingsun.net/2007/02/07/whats-with-__macosx-in-zip-files/) */
197  dirs.push_back("__MACOSX");
198 
199  files.push_back("#*#");
200  files.push_back("*~");
201  files.push_back("*-bak");
202  files.push_back("*.swp");
203  files.push_back("*.pbl");
204  files.push_back("*.ign");
205  files.push_back("_info.cfg");
206  files.push_back("*.exe");
207  files.push_back("*.bat");
208  files.push_back("*.cmd");
209  files.push_back("*.com");
210  files.push_back("*.scr");
211  files.push_back("*.sh");
212  files.push_back("*.js");
213  files.push_back("*.vbs");
214  files.push_back("*.o");
215  /* Remove junk created by certain file manager ;) */
216  files.push_back("Thumbs.db");
217  /* Eclipse plugin */
218  files.push_back("*.wesnoth");
219  files.push_back("*.project");
220  }
221 }
222 
223 static std::pair<std::vector<std::string>, std::vector<std::string> > read_ignore_patterns(const std::string& addon_name)
224 {
225  const std::string parentd = filesystem::get_addons_dir();
226  const std::string ign_file = parentd + "/" + addon_name + "/_server.ign";
227 
228  std::pair<std::vector<std::string>, std::vector<std::string> > patterns;
229  LOG_CFG << "searching for .ign file for '" << addon_name << "'...\n";
230  if (!filesystem::file_exists(ign_file)) {
231  LOG_CFG << "no .ign file found for '" << addon_name << "'\n"
232  << "inserting default ignore patterns...\n";
233  append_default_ignore_patterns(patterns);
234  return patterns; // just default patterns
235  }
236  LOG_CFG << "found .ign file: " << ign_file << '\n';
237  std::istream *stream = filesystem::istream_file(ign_file);
238  std::string line;
239  while (std::getline(*stream, line)) {
240  utils::strip(line);
241  const size_t l = line.size();
242  // .gitignore & WML like comments
243  if (l == 0 || !line.compare(0,2,"# ")) continue;
244  if (line[l - 1] == '/') { // directory; we strip the last /
245  patterns.second.push_back(line.substr(0, l - 1));
246  } else { // file
247  patterns.first.push_back(line);
248  }
249  }
250  return patterns;
251 }
252 
253 static void archive_file(const std::string& path, const std::string& fname, config& cfg)
254 {
255  cfg["name"] = fname;
256  const bool is_cfg = (fname.size() > 4 ? (fname.substr(fname.size() - 4) == ".cfg") : false);
257  cfg["contents"] = encode_binary(strip_cr(filesystem::read_file(path + '/' + fname),is_cfg));
258 }
259 
260 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)
261 {
262  cfg["name"] = dirname;
263  const std::string dir = path + '/' + dirname;
264 
265  std::vector<std::string> files, dirs;
266  filesystem::get_files_in_dir(dir,&files,&dirs);
267  for(std::vector<std::string>::const_iterator i = files.begin(); i != files.end(); ++i) {
269  for(std::vector<std::string>::const_iterator p = ignore_patterns.first.begin(); p != ignore_patterns.first.end(); ++p) {
270  if (utils::wildcard_string_match(*i, *p)) {
271  valid = false;
272  break;
273  }
274  }
275  if (valid) {
276  archive_file(dir,*i,cfg.add_child("file"));
277  }
278  }
279 
280  for(std::vector<std::string>::const_iterator j = dirs.begin(); j != dirs.end(); ++j) {
281  bool valid = true;
282  for(std::vector<std::string>::const_iterator p = ignore_patterns.second.begin(); p != ignore_patterns.second.end(); ++p) {
283  if (utils::wildcard_string_match(*j, *p)) {
284  valid = false;
285  break;
286  }
287  }
288  if (valid) {
289  archive_dir(dir,*j,cfg.add_child("dir"),ignore_patterns);
290  }
291  }
292 }
293 
294 void archive_addon(const std::string& addon_name, config& cfg)
295 {
296  const std::string parentd = filesystem::get_addons_dir();
297 
298  std::pair<std::vector<std::string>, std::vector<std::string> > ignore_patterns;
299  ignore_patterns = read_ignore_patterns(addon_name);
300  archive_dir(parentd, addon_name, cfg.add_child("dir"), ignore_patterns);
301 }
302 
303 static void unarchive_file(const std::string& path, const config& cfg)
304 {
305  filesystem::write_file(path + '/' + cfg["name"].str(), unencode_binary(cfg["contents"]));
306 }
307 
308 static void unarchive_dir(const std::string& path, const config& cfg)
309 {
310  std::string dir;
311  if (cfg["name"].empty())
312  dir = path;
313  else
314  dir = path + '/' + cfg["name"].str();
315 
317 
318  for(const config &d : cfg.child_range("dir")) {
319  unarchive_dir(dir, d);
320  }
321 
322  for(const config &f : cfg.child_range("file")) {
323  unarchive_file(dir, f);
324  }
325 }
326 
327 void unarchive_addon(const config& cfg)
328 {
329  const std::string parentd = filesystem::get_addons_dir();
330  unarchive_dir(parentd, cfg);
331 }
332 
333 namespace {
334  std::map< std::string, version_info > version_info_cache;
335 } // end unnamed namespace 5
336 
338 {
339  version_info_cache.clear();
340 
341  LOG_CFG << "refreshing add-on versions cache\n";
342 
343  const std::vector<std::string>& addons = installed_addons();
344  if(addons.empty()) {
345  return;
346  }
347 
348  std::vector<std::string> addon_info_files(addons.size());
349 
350  std::transform(addons.begin(), addons.end(),
351  addon_info_files.begin(), get_info_file_path);
352 
353  for(size_t i = 0; i < addon_info_files.size(); ++i) {
354  assert(i < addons.size());
355 
356  const std::string& addon = addons[i];
357  const std::string& info_file = addon_info_files[i];
358 
359  if(filesystem::file_exists(info_file)) {
360  config cfg;
361  get_addon_install_info(addon, cfg);
362 
363  const config& info_cfg = cfg.child("info");
364  if(!info_cfg) {
365  continue;
366  }
367 
368  const std::string& version = info_cfg["version"].str();
369  LOG_CFG << "cached add-on version: " << addon << " [" << version << "]\n";
370 
371  version_info_cache[addon] = version;
372  } else if (!have_addon_pbl_info(addon) && !have_addon_in_vcs_tree(addon)) {
373  // Don't print the warning if the user is clearly the author
374  WRN_CFG << "add-on '" << addon << "' has no _info.cfg; cannot read version info" << std::endl;
375  }
376  }
377 }
378 
380 {
381  static const version_info nil;
382  std::map< std::string, version_info >::iterator entry = version_info_cache.find(addon);
383  return entry != version_info_cache.end() ? entry->second : nil;
384 }
child_itors child_range(const std::string &key)
Definition: config.cpp:613
bool looks_like_pbl(const std::string &file)
const GLfloat * c
Definition: glew.h:12741
Exception thrown when the WML parser fails to read a .pbl file.
Definition: manager.hpp:31
std::string encode_binary(const std::string &str)
Definition: validation.cpp:120
std::string unencode_binary(const std::string &str)
Definition: validation.cpp:138
This file contains the window object, this object is a top level container which has the event manage...
bool is_addon_installed(const std::string &addon_name)
Check whether the specified add-on is currently installed.
Definition: manager.cpp:166
#define ERR_CFG
Definition: manager.cpp:44
static bool IsCR(const char &c)
Definition: manager.cpp:172
bool wildcard_string_match(const std::string &str, const std::string &match)
Match using '*' as any number of characters (including none), and '?' as any one character.
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:107
static std::string strip_cr(std::string str, bool strip)
Definition: manager.cpp:177
#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 'supported' VCS.
Definition: manager.cpp:71
version_info get_addon_version_info(const std::string &addon)
Returns a particular installed add-on's version information.
Definition: manager.cpp:379
GLdouble l
Definition: glew.h:6966
std::vector< std::string > available_addons()
Returns a list of local add-ons that can be published.
Definition: manager.cpp:134
std::string & strip(std::string &str)
Remove whitespace from the front and back of the string 'str'.
GLuint GLenum GLenum transform
Definition: glew.h:11418
GLuint GLuint stream
Definition: glew.h:5239
#define LOG_CFG
Definition: manager.cpp:45
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:102
static void archive_file(const std::string &path, const std::string &fname, config &cfg)
Definition: manager.cpp:253
GLsizei const char ** path
Definition: glew.h:4654
static void unarchive_file(const std::string &path, const config &cfg)
Definition: manager.cpp:303
This file contains the settings handling of the widget library.
void write_file(const std::string &fname, const std::string &data)
Throws io_exception if an error occurs.
std::istream * istream_file(const std::string &fname, bool treat_failure_as_error=true)
void set_addon_pbl_info(const std::string &addon_name, const config &cfg)
Definition: manager.cpp:96
std::ostream * ostream_file(std::string const &fname, bool create_directory=true)
config & add_child(const std::string &key)
Definition: config.cpp:743
std::vector< std::string > installed_addons()
Retrieves the names of all installed add-ons.
Definition: manager.cpp:150
void refresh_addon_version_info_cache()
Refreshes the per-session cache of add-on's version information structs.
Definition: manager.cpp:337
bool remove_local_addon(const std::string &addon)
Definition: manager.cpp:120
GLfloat GLfloat p
Definition: glew.h:12766
std::string read_file(const std::string &fname)
Basic disk I/O - read file.
static lg::log_domain log_filesystem("filesystem")
GLuint res
Definition: glew.h:9258
void get_files_in_dir(const std::string &dir, std::vector< std::string > *files, std::vector< std::string > *dirs=nullptr, file_name_option mode=FILE_NAME_ONLY, file_filter_option filter=NO_FILTER, file_reorder_option reorder=DONT_REORDER, file_tree_checksum *checksum=nullptr)
Populates 'files' with all the files and 'dirs' with all the directories in dir.
bool have_addon_pbl_info(const std::string &addon_name)
Returns true if there's a local .pbl file stored for the specified add-on.
Definition: manager.cpp:80
void archive_addon(const std::string &addon_name, config &cfg)
Archives an add-on into a config object for campaignd transactions.
Definition: manager.cpp:294
static lg::log_domain log_config("config")
bool make_directory(const std::string &dirname)
size_t i
Definition: function.cpp:1057
Declarations for File-IO.
void read(config &cfg, std::istream &in, abstract_validator *validator)
Definition: parser.cpp:400
Represents version numbers.
Definition: version.hpp:44
bool delete_directory(const std::string &dirname, const bool keep_pbl=false)
void get_addon_pbl_info(const std::string &addon_name, config &cfg)
Gets the publish information for an add-on.
Definition: manager.cpp:85
#define WRN_CFG
Definition: manager.cpp:46
void unarchive_addon(const config &cfg)
Definition: manager.cpp:327
std::string get_addons_dir()
config & child(const std::string &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:658
Standard logging facilities (interface).
std::string message
Definition: exceptions.hpp:29
#define e
static std::pair< std::vector< std::string >, std::vector< std::string > > read_ignore_patterns(const std::string &addon_name)
Definition: manager.cpp:223
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
Interfaces for manipulating version numbers of engine, add-ons, etc.
bool file_exists(const std::string &name)
Returns true if a file or directory with such name already exists.
void write(std::ostream &out, configr_of const &cfg, unsigned int level)
Definition: parser.cpp:621
const std::string version
Definition: game_config.cpp:48
GLsizei const GLcharARB ** string
Definition: glew.h:4503
static void unarchive_dir(const std::string &path, const config &cfg)
Definition: manager.cpp:308
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)
Definition: manager.cpp:260
const std::string valid
Little parts of regex templates used to parse Wml annoations.
GLclampf f
Definition: glew.h:3024