The Battle for Wesnoth  1.17.0-dev
filesystem_common.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2017 - 2021
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #include <fstream>
16 
17 #include "filesystem.hpp"
18 #include "wesconfig.h"
19 
20 #include "config.hpp"
21 #include "game_config.hpp"
22 #include "log.hpp"
25 
26 #include <boost/algorithm/string.hpp>
27 
28 static lg::log_domain log_filesystem("filesystem");
29 #define LOG_FS LOG_STREAM(info, log_filesystem)
30 #define ERR_FS LOG_STREAM(err, log_filesystem)
31 
32 namespace filesystem
33 {
34 
35 bool is_legal_user_file_name(const std::string& name, bool allow_whitespace)
36 {
37  //
38  // IMPORTANT NOTE:
39  //
40  // If you modify this function you must be aware that it is used by the
41  // add-on server validation routines both on the client and server sides.
42  // The addition or removal of any criteria here should be carefully
43  // evaluated with this in mind.
44  //
45 
46  if(name.empty() || name.back() == '.' || name.find("..") != std::string::npos || name.size() > 255) {
47  return false;
48  }
49 
50  // Reserved DOS device names on Windows.
51  static const std::set<std::string> dos_device_names = {
52  // Hardware devices
53  "NUL", "CON", "AUX", "PRN",
54  "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
55  "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
56  // Console API pseudo-devices
57  "CONIN$", "CONOUT$",
58  };
59 
60  // We can't use filesystem::base_name() here, because it returns the
61  // filename up to the *last* dot. "CON.foo.bar" is still redirected to
62  // "CON" on Windows, although "foo.CON.bar" and "foo.bar.CON" are not.
63  //
64  // Do also note that we're relying on the char-by-char check further below
65  // to flag the name as illegal if it contains a colon ':', the reason
66  // being that is valid to refer to DOS device names with a trailing colon
67  // (e.g. "CON:" is synonymous with "CON").
68 
69  const auto& first_name =
70  boost::algorithm::to_upper_copy(name.substr(0, name.find('.')), std::locale::classic());
71 
72  if(dos_device_names.count(first_name)) {
73  return false;
74  }
75 
76  const auto& name_ucs4 = unicode_cast<std::u32string>(name);
77  if(name != unicode_cast<std::string>(name_ucs4)){
78  return false; // name is an invalid UTF-8 sequence
79  }
80 
81  return name_ucs4.end() == std::find_if(name_ucs4.begin(), name_ucs4.end(), [=](char32_t c)
82  {
83  switch(c) {
84  case ' ':
85  return !allow_whitespace;
86  case '"':
87  case '*':
88  case '/':
89  case ':':
90  case '<':
91  case '>':
92  case '?':
93  case '\\':
94  case '|':
95  case '~':
96  case 0x7F: // DEL
97  return true;
98  default:
99  return c < 0x20 || // C0 control characters
100  (c >= 0x80 && c < 0xA0) || // C1 control characters
101  (c >= 0xD800 && c < 0xE000); // surrogate pairs
102  }
103  });
104 }
105 
106 void blacklist_pattern_list::remove_blacklisted_files_and_dirs(std::vector<std::string>& files, std::vector<std::string>& directories) const
107 {
108  files.erase(
109  std::remove_if(files.begin(), files.end(), [this](const std::string& name) { return match_file(name); }),
110  files.end());
111  directories.erase(
112  std::remove_if(directories.begin(), directories.end(), [this](const std::string& name) { return match_dir(name); }),
113  directories.end());
114 }
115 
116 bool blacklist_pattern_list::match_file(const std::string& name) const
117 {
118  return std::any_of(file_patterns_.begin(), file_patterns_.end(),
119  std::bind(&utils::wildcard_string_match, std::ref(name), std::placeholders::_1));
120 }
121 
122 bool blacklist_pattern_list::match_dir(const std::string& name) const
123 {
124  return std::any_of(directory_patterns_.begin(), directory_patterns_.end(),
125  std::bind(&utils::wildcard_string_match, std::ref(name), std::placeholders::_1));
126 }
127 
128 std::string get_prefs_file()
129 {
130  return get_user_config_dir() + "/preferences";
131 }
132 
133 std::string get_credentials_file()
134 {
135  return get_user_config_dir() + "/credentials";
136 }
137 
139 {
140 #ifdef HAS_RELATIVE_DEFPREF
142 #else
144 #endif
145 }
146 
147 std::string get_save_index_file()
148 {
149  return get_user_data_dir() + "/save_index";
150 }
151 
152 std::string get_saves_dir()
153 {
154  const std::string dir_path = get_user_data_dir() + "/saves";
155  return get_dir(dir_path);
156 }
157 
158 std::string get_addons_dir()
159 {
160  const std::string dir_path = get_user_data_dir() + "/data/add-ons";
161  return get_dir(dir_path);
162 }
163 
164 std::string get_intl_dir()
165 {
166 #ifdef _WIN32
167  return game_config::path + "/" LOCALEDIR;
168 #else
169 
170 #ifdef USE_INTERNAL_DATA
171  return get_cwd() + "/" LOCALEDIR;
172 #endif
173 
174 #if HAS_RELATIVE_LOCALEDIR
175  std::string res = game_config::path + "/" LOCALEDIR;
176 #else
177  std::string res = LOCALEDIR;
178 #endif
179 
180  return res;
181 #endif
182 }
183 
184 std::string get_screenshot_dir()
185 {
186  const std::string dir_path = get_user_data_dir() + "/screenshots";
187  return get_dir(dir_path);
188 }
189 
190 bool looks_like_pbl(const std::string& file)
191 {
192  return utils::wildcard_string_match(utf8::lowercase(file), "*.pbl");
193 }
194 
196  : nfiles(0), sum_size(0), modified(0)
197 {}
198 
200  nfiles (cfg["nfiles"].to_size_t()),
201  sum_size(cfg["size"].to_size_t()),
202  modified(cfg["modified"].to_time_t())
203 {
204 }
205 
207 {
208  cfg["nfiles"] = nfiles;
209  cfg["size"] = sum_size;
210  cfg["modified"] = modified;
211 }
212 
214 {
215  return nfiles == rhs.nfiles && sum_size == rhs.sum_size &&
216  modified == rhs.modified;
217 }
218 
219 bool ends_with(const std::string& str, const std::string& suffix)
220 {
221  return str.size() >= suffix.size() && std::equal(suffix.begin(),suffix.end(),str.end()-suffix.size());
222 }
223 
224 std::string read_map(const std::string& name)
225 {
226  std::string res;
227  std::string map_location = get_wml_location(name);
228  if(map_location.empty()) {
229  // If this is an add-on or campaign that's set the [binary_path] for its image directory,
230  // automatically check for a sibling maps directory.
231  map_location = get_binary_file_location("maps", name);
232  }
233  if(!map_location.empty()) {
234  res = read_file(map_location);
235  }
236 
237  if(res.empty()) {
238  res = read_file(get_user_data_dir() + "/editor/maps/" + name);
239  }
240 
241  return res;
242 }
243 
244 static void get_file_tree_checksum_internal(const std::string& path, file_tree_checksum& res)
245 {
246 
247  std::vector<std::string> dirs;
249 
250  for(std::vector<std::string>::const_iterator j = dirs.begin(); j != dirs.end(); ++j) {
252  }
253 }
254 
256 {
257  static file_tree_checksum checksum;
258  if (reset)
259  checksum.reset();
260  if(checksum.nfiles == 0) {
261  get_file_tree_checksum_internal("data/",checksum);
262  get_file_tree_checksum_internal(get_user_data_dir() + "/data/",checksum);
263  LOG_FS << "calculated data tree checksum: "
264  << checksum.nfiles << " files; "
265  << checksum.sum_size << " bytes" << std::endl;
266  }
267 
268  return checksum;
269 }
270 
271 }
bool is_legal_user_file_name(const std::string &name, bool allow_whitespace=true)
Returns whether the given filename is a legal name for a user-created file.
static void get_file_tree_checksum_internal(const std::string &path, file_tree_checksum &res)
bool looks_like_pbl(const std::string &file)
bool ends_with(const std::string &str, const std::string &suffix)
ucs4_convert_impl::enableif< TD, typename TS::value_type >::type unicode_cast(const TS &source)
bool wildcard_string_match(const std::string &str, const std::string &match)
Match using &#39;*&#39; as any number of characters (including none), &#39;+&#39; as one or more characters, and &#39;?&#39; as any one character.
std::string get_screenshot_dir()
std::string get_binary_file_location(const std::string &type, const std::string &filename)
Returns a complete path to the actual file of a given type or an empty string if the file isn&#39;t prese...
std::string get_saves_dir()
Definitions for the interface to Wesnoth Markup Language (WML).
void remove_blacklisted_files_and_dirs(std::vector< std::string > &files, std::vector< std::string > &directories) const
static bfs::path get_dir(const bfs::path &dirpath)
Definition: filesystem.cpp:274
std::string get_cwd()
Definition: filesystem.cpp:879
bool match_file(const std::string &name) const
std::string get_user_data_dir()
Definition: filesystem.cpp:792
std::vector< std::string > directory_patterns_
Definition: filesystem.hpp:88
std::string get_intl_dir()
void get_files_in_dir(const std::string &dir, std::vector< std::string > *files, std::vector< std::string > *dirs, name_mode mode, filter_mode filter, reorder_mode reorder, file_tree_checksum *checksum)
Get a list of all files and/or directories in a given directory.
Definition: filesystem.cpp:349
std::string path
Definition: game_config.cpp:39
std::vector< std::string > file_patterns_
Definition: filesystem.hpp:87
bool operator==(const file_tree_checksum &rhs) const
std::string get_default_prefs_file()
std::string read_file(const std::string &fname)
Basic disk I/O - read file.
Definition: filesystem.cpp:998
std::string read_map(const std::string &name)
Encapsulates the map of the game.
Definition: location.hpp:38
Some defines: VERSION, PACKAGE, MIN_SAVEGAME_VERSION.
#define LOG_FS
std::string get_wml_location(const std::string &filename, const std::string &current_dir)
Returns a complete path to the actual WML file or directory or an empty string if the file isn&#39;t pres...
Declarations for File-IO.
std::string lowercase(const std::string &s)
Returns a lowercased version of the string.
Definition: unicode.cpp:52
const file_tree_checksum & data_tree_checksum(bool reset=false)
Get the time at which the data/ tree was last modified at.
std::string get_user_config_dir()
Definition: filesystem.cpp:763
std::string get_addons_dir()
std::string get_credentials_file()
Standard logging facilities (interface).
static lg::log_domain log_filesystem("filesystem")
std::string get_prefs_file()
bool match_dir(const std::string &name) const
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:61
mock_char c
std::string get_save_index_file()
#define LOCALEDIR
Definition: wesconfig.h:19
std::string default_preferences_path
Definition: game_config.cpp:45