The Battle for Wesnoth  1.17.0-dev
addon_utils.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2021
3  by David White <dave@whitevine.net>
4  Copyright (C) 2013 - 2015 by Iris Morelle <shadowm2006@gmail.com>
5  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
6 
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY.
13 
14  See the COPYING file for more details.
15 */
16 
18 
19 #include "config.hpp"
20 #include "filesystem.hpp"
21 #include "game_config.hpp"
22 #include "log.hpp"
24 #include "addon/validation.hpp"
25 
26 #include <boost/algorithm/string.hpp>
27 
28 static lg::log_domain log_network("network");
29 #define LOG_CS LOG_STREAM_NAMELESS(err, log_network)
30 
31 namespace {
32 
33 typedef std::map<std::string, std::string> plain_string_map;
34 
35 /**
36  * Quick and dirty alternative to @a utils::interpolate_variables_into_string
37  * that doesn't require formula AI code. It is definitely NOT safe for normal
38  * use since it doesn't do strict checks on where variable placeholders
39  * ("$foobar") end and doesn't support pipe ("|") terminators.
40  *
41  * @param str The format string.
42  * @param symbols The symbols table.
43  */
44 std::string fast_interpolate_variables_into_string(const std::string &str, const plain_string_map * const symbols)
45 {
46  std::string res = str;
47 
48  if(symbols) {
49  for(const plain_string_map::value_type& sym : *symbols) {
50  boost::replace_all(res, "$" + sym.first, sym.second);
51  }
52  }
53 
54  return res;
55 }
56 
57 } // end anonymous namespace
58 
59 namespace campaignd {
60 
61 // Markup characters recognized by GUI1 code. These must be
62 // the same as the constants defined in marked-up_text.cpp.
63 const std::string illegal_markup_chars = "*`~{^}|@#<&";
64 
65 std::string format_addon_feedback_url(const std::string& format, const config& params)
66 {
67  if(!format.empty() && !params.empty()) {
68  plain_string_map escaped;
69 
71 
72  // Percent-encode parameter values for URL interpolation. This is
73  // VERY important since otherwise people could e.g. alter query
74  // strings from the format string.
75  for(const config::attribute& a : attrs) {
76  escaped[a.first] = utils::urlencode(a.second.str());
77  }
78 
79  // FIXME: We cannot use utils::interpolate_variables_into_string
80  // because it is implemented using a lot of formula AI junk
81  // that really doesn't belong in campaignd.
82  const std::string& res =
83  fast_interpolate_variables_into_string(format, &escaped);
84 
85  if(res != format) {
86  return res;
87  }
88 
89  // If we get here, that means that no interpolation took place; in
90  // that case, the parameters table probably contains entries that
91  // do not match the format string expectations.
92  }
93 
94  return std::string();
95 }
96 
97 void support_translation(config& addon, const std::string& locale_id)
98 {
99  config* locale = &addon.find_child("translation", "language", locale_id);
100  if(!*locale) {
101  locale = &addon.add_child("translation");
102  (*locale)["language"] = locale_id;
103  }
104  (*locale)["supported"] = true;
105 }
106 
107 void find_translations(const config& base_dir, config& addon)
108 {
109  for(const config& file : base_dir.child_range("file")) {
110  const std::string& fn = file["name"].str();
111  if(filesystem::ends_with(fn, ".po")) {
112  support_translation(addon, filesystem::base_name(fn, true));
113  }
114  }
115 
116  for(const config &dir : base_dir.child_range("dir"))
117  {
118  if(dir["name"] == "LC_MESSAGES") {
119  support_translation(addon, base_dir["name"]);
120  } else {
121  find_translations(dir, addon);
122  }
123  }
124 }
125 
126 void add_license(config& cfg)
127 {
128  config& dir = cfg.find_child("dir", "name", cfg["campaign_name"]);
129 
130  // No top-level directory? Hm..
131  if(!dir) {
132  return;
133  }
134 
135  // Don't add if it already exists.
136  if(dir.find_child("file", "name", "COPYING.txt")
137  || dir.find_child("file", "name", "COPYING")
138  || dir.find_child("file", "name", "copying.txt")
139  || dir.find_child("file", "name", "Copying.txt")
140  || dir.find_child("file", "name", "COPYING.TXT"))
141  {
142  return;
143  }
144 
145  // Copy over COPYING.txt
146  const std::string& contents = filesystem::read_file("COPYING.txt");
147  if (contents.empty()) {
148  LOG_CS << "Could not find COPYING.txt, path is \"" << game_config::path << "\"\n";
149  return;
150  }
151 
152  config& copying = dir.add_child("file");
153  copying["name"] = "COPYING.txt";
154  copying["contents"] = contents;
155 }
156 
157 std::map<version_info, config> get_version_map(config& addon)
158 {
159  std::map<version_info, config> version_map;
160 
161  for(config& version : addon.child_range("version")) {
162  version_map.emplace(version_info(version["version"]), version);
163  }
164 
165  return version_map;
166 }
167 
168 bool data_apply_removelist(config& data, const config& removelist)
169 {
170  for(const config& f : removelist.child_range("file")) {
171  data.remove_children("file", [&f](const config& d) { return f["name"] == d["name"]; });
172  }
173 
174  for(const config& dir : removelist.child_range("dir")) {
175  config& data_dir = data.find_child("dir", "name", dir["name"]);
176  if(data_dir && !data_apply_removelist(data_dir, dir)) {
177  // Delete empty directories
178  data.remove_children("dir", [&dir](const config& d) { return dir["name"] == d["name"]; });
179  }
180  }
181 
182  return data.has_child("file") || data.has_child("dir");
183 }
184 
185 void data_apply_addlist(config& data, const config& addlist)
186 {
187  for(const config& f : addlist.child_range("file")) {
188  // Just add it since we have already checked the data for duplicates
189  data.add_child("file", f);
190  }
191 
192  for(const config& dir : addlist.child_range("dir")) {
193  config* data_dir = &data.find_child("dir", "name", dir["name"]);
194  if(!*data_dir) {
195  data_dir = &data.add_child("dir");
196  (*data_dir)["name"] = dir["name"];
197  }
198  data_apply_addlist(*data_dir, dir);
199  }
200 }
201 
202 } // end namespace campaignd
std::string urlencode(const std::string &str)
Percent-escape characters in a UTF-8 string intended to be part of a URL.
config & find_child(config_key_type key, const std::string &name, const std::string &value)
Returns the first child of tag key with a name attribute containing value.
Definition: config.cpp:885
void support_translation(config &addon, const std::string &locale_id)
Definition: addon_utils.cpp:97
#define a
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:394
bool ends_with(const std::string &str, const std::string &suffix)
std::map< version_info, config > get_version_map(config &addon)
child_itors child_range(config_key_type key)
Definition: config.cpp:344
attribute_map::value_type attribute
Definition: config.hpp:222
bool data_apply_removelist(config &data, const config &removelist)
#define d
Definitions for the interface to Wesnoth Markup Language (WML).
void data_apply_addlist(config &data, const config &addlist)
const_attr_itors attribute_range() const
Definition: config.cpp:858
void add_license(config &cfg)
Adds a COPYING.txt file with the full text of the GNU GPL to an add-on.
static lg::log_domain log_network("network")
std::string path
Definition: game_config.cpp:39
boost::iterator_range< const_attribute_iterator > const_attr_itors
Definition: config.hpp:282
std::string read_file(const std::string &fname)
Basic disk I/O - read file.
Definition: filesystem.cpp:998
Declarations for File-IO.
Represents version numbers.
config & add_child(config_key_type key)
Definition: config.cpp:514
#define LOG_CS
Definition: addon_utils.cpp:29
std::string base_name(const std::string &file, const bool remove_extension)
Returns the base filename of a file, with directory name stripped.
#define f
Standard logging facilities (interface).
void find_translations(const config &base_dir, config &addon)
Scans an add-on archive directory for translations.
const std::string illegal_markup_chars
Markup characters recognized by GUI1 code.
Definition: addon_utils.cpp:63
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:61
std::string format_addon_feedback_url(const std::string &format, const config &params)
Format a feedback URL for an add-on.
Definition: addon_utils.cpp:65
bool empty() const
Definition: config.cpp:941
void remove_children(config_key_type key, std::function< bool(const config &)> p)
Removes all children with tag key for which p returns true.
Definition: config.cpp:745