1 /*
2 * The ManaPlus Client
3 * Copyright (C) 2016-2019 The ManaPlus Developers
4 * Copyright (C) 2019-2021 Andrei Karas
5 *
6 * This file is part of The ManaPlus Client.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "resources/db/groupdb.h"
23
24 #include "configuration.h"
25
26 #include "being/localplayer.h"
27
28 #include "utils/checkutils.h"
29
30 #ifdef TMWA_SUPPORT
31 #include "net/net.h"
32 #endif // TMWA_SUPPORT
33
34 #include "resources/beingcommon.h"
35 #include "resources/groupinfo.h"
36
37 #include "debug.h"
38
39 namespace
40 {
41 GroupDb::GroupInfos mGroups;
42 const GroupInfo mEmptyGroup;
43 bool mLoaded = false;
44 } // namespace
45
load()46 void GroupDb::load()
47 {
48 if (mLoaded)
49 unload();
50
51 logger->log1("Initializing group database...");
52
53 loadXmlFile(paths.getStringValue("groupsFile"), SkipError_false);
54 loadXmlFile(paths.getStringValue("groupsPatchFile"), SkipError_true);
55 loadXmlDir("groupsPatchDir", loadXmlFile)
56 mLoaded = true;
57 }
58
59 #define servercommandFirst(name) \
60 if (str == #name) \
61 return ServerCommandType::name; \
62 else
63 #define servercommand(name) \
64 if (str == #name) \
65 return ServerCommandType::name; \
66 else
67 #define servercommand2(name1, name2) \
68 if (str == #name2) \
69 return ServerCommandType::name1; \
70 else
71
parseCommand(const std::string & str,const int id)72 static ServerCommandTypeT parseCommand(const std::string &str,
73 const int id)
74 {
75 #include "resources/servercommands.inc"
76 {
77 reportAlways("GroupsDb: unknown command name: '%s' in group id '%d'.",
78 str.c_str(),
79 id)
80 }
81
82 return ServerCommandType::Max;
83 }
84
85 SERVERCOMMANDS_VOID
86 #undef servercommandFirst
87 #undef servercommand
88 #undef servercommand2
89
90 #define serverpermissionFirst(name) \
91 if (str == #name) \
92 return ServerPermissionType::name; \
93 else
94 #define serverpermission(name) \
95 if (str == #name) \
96 return ServerPermissionType::name; \
97 else
98
parsePermission(const std::string & str,const int id)99 static ServerPermissionTypeT parsePermission(const std::string &str,
100 const int id)
101 {
102 #include "resources/serverpermissions.inc"
103 {
104 reportAlways("GroupsDb: unknown permission name: "
105 "'%s' in group id '%d'.",
106 str.c_str(),
107 id)
108 }
109
110 return ServerPermissionType::Max;
111 }
112
113 SERVERPERMISSION_VOID
114 #undef serverpermissionFirst
115 #undef serverpermission
116
parseUseFlag(const std::string & str,const int id)117 static ServerCommandEnable::Type parseUseFlag(const std::string &str,
118 const int id)
119 {
120 if (str == "self")
121 {
122 return ServerCommandEnable::Self;
123 }
124 else if (str == "other")
125 {
126 return ServerCommandEnable::Other;
127 }
128 else if (str == "both")
129 {
130 return ServerCommandEnable::Both;
131 }
132 else if (str == "false")
133 {
134 return ServerCommandEnable::No;
135 }
136 else
137 {
138 reportAlways("GroupsDb: unknown use flag: '%s' in group id '%d'."
139 "Possible values 'self', 'other', 'both'.",
140 str.c_str(),
141 id)
142 return ServerCommandEnable::No;
143 }
144 }
145
146 static void loadCommands(XmlNodePtr rootNode,
147 const int id,
148 GroupInfo *groupInfo) A_NONNULL(3);
loadCommands(XmlNodePtr rootNode,const int id,GroupInfo * groupInfo)149 static void loadCommands(XmlNodePtr rootNode,
150 const int id,
151 GroupInfo *groupInfo)
152 {
153 for_each_xml_child_node(node, rootNode)
154 {
155 if (xmlNameEqual(node, "command"))
156 {
157 const std::string nameStr = XML::getProperty(node,
158 "name",
159 "");
160 const std::string useStr = XML::getProperty(node,
161 "use",
162 "");
163 ServerCommandTypeT name = parseCommand(nameStr, id);
164 if (name == ServerCommandType::Max)
165 continue;
166 ServerCommandEnable::Type useFlag = parseUseFlag(useStr, id);
167 if (useFlag == ServerCommandEnable::No)
168 continue;
169 groupInfo->mCommands[CAST_SIZE(name)] = useFlag;
170 }
171 }
172 }
173
174 static void loadPermissions(XmlNodePtr rootNode,
175 const int id,
176 GroupInfo *groupInfo) A_NONNULL(3);
loadPermissions(XmlNodePtr rootNode,const int id,GroupInfo * groupInfo)177 static void loadPermissions(XmlNodePtr rootNode,
178 const int id,
179 GroupInfo *groupInfo)
180 {
181 for_each_xml_child_node(node, rootNode)
182 {
183 if (xmlNameEqual(node, "permission"))
184 {
185 const std::string nameStr = XML::getProperty(node,
186 "name",
187 "");
188 ServerPermissionTypeT perm = parsePermission(nameStr, id);
189 if (perm == ServerPermissionType::Max)
190 continue;
191 if (!XML::getBoolProperty(node,
192 "enable",
193 true))
194 {
195 continue;
196 }
197 groupInfo->mPermissions[CAST_SIZE(perm)] = Enable_true;
198 }
199 }
200 }
201
202 static void loadSubNodes(XmlNodePtr groupNode,
203 const int id,
204 GroupInfo *groupInfo) A_NONNULL(3);
loadSubNodes(XmlNodePtr groupNode,const int id,GroupInfo * groupInfo)205 static void loadSubNodes(XmlNodePtr groupNode,
206 const int id,
207 GroupInfo *groupInfo)
208 {
209 for_each_xml_child_node(node, groupNode)
210 {
211 if (xmlNameEqual(node, "commands"))
212 loadCommands(node, id, groupInfo);
213 if (xmlNameEqual(node, "permissions"))
214 loadPermissions(node, id, groupInfo);
215 }
216 }
217
218 static void parseInherit(XmlNodePtr groupNode,
219 const int id,
220 GroupInfo *groupInfo) A_NONNULL(3);
parseInherit(XmlNodePtr groupNode,const int id,GroupInfo * groupInfo)221 static void parseInherit(XmlNodePtr groupNode,
222 const int id,
223 GroupInfo *groupInfo)
224 {
225 const std::string inherit = XML::langProperty(groupNode,
226 "inherit",
227 "");
228 STD_VECTOR<int> parts;
229 splitToIntVector(parts, inherit, ',');
230 FOR_EACH (STD_VECTOR<int>::const_iterator, it, parts)
231 {
232 const int id2 = *it;
233 GroupDb::GroupInfos::const_iterator it2 = mGroups.find(id2);
234 if (it2 == mGroups.end())
235 {
236 reportAlways("Unknown inherit group id '%d' in group '%d'",
237 id2,
238 id)
239 continue;
240 }
241 GroupInfo *const groupInfo2 = (*it2).second;
242 for (size_t f = 0; f < CAST_SIZE(ServerCommandType::Max); f ++)
243 {
244 ServerCommandEnable::Type enable = groupInfo2->mCommands[f];
245 if (enable != ServerCommandEnable::No)
246 groupInfo->mCommands[f] = enable;
247 }
248 for (size_t f = 0; f < CAST_SIZE(ServerPermissionType::Max); f ++)
249 {
250 if (groupInfo2->mPermissions[f] == Enable_true)
251 groupInfo->mPermissions[f] = Enable_true;
252 }
253 }
254 }
255
loadXmlFile(const std::string & fileName,const SkipError skipError)256 void GroupDb::loadXmlFile(const std::string &fileName,
257 const SkipError skipError)
258 {
259 XML::Document doc(fileName,
260 UseVirtFs_true,
261 skipError);
262 XmlNodeConstPtrConst rootNode = doc.rootNode();
263
264 if (rootNode == nullptr ||
265 !xmlNameEqual(rootNode, "groups"))
266 {
267 if (skipError == SkipError_true)
268 {
269 logger->log("GroupsDb: Error while loading %s!",
270 fileName.c_str());
271 }
272 else
273 {
274 reportAlways("GroupsDb: Error while loading %s!",
275 fileName.c_str())
276 }
277 return;
278 }
279
280 for_each_xml_child_node(node, rootNode)
281 {
282 if (xmlNameEqual(node, "include"))
283 {
284 const std::string name = XML::getProperty(node, "name", "");
285 if (!name.empty())
286 loadXmlFile(name, skipError);
287 continue;
288 }
289 if (xmlNameEqual(node, "group"))
290 {
291 const int id = XML::getProperty(node,
292 "id",
293 -1);
294 if (id < 0)
295 {
296 reportAlways("Empty id field in GroupDb")
297 continue;
298 }
299 GroupInfosIter it = mGroups.find(id);
300 GroupInfo *group = nullptr;
301 if (it != mGroups.end())
302 {
303 reportAlways("GroupDb: group with id %d already added",
304 id)
305 group = (*it).second;
306 }
307 else
308 {
309 group = new GroupInfo;
310 mGroups[id] = group;
311 }
312 group->name = XML::langProperty(node,
313 "name",
314 "");
315 group->longName = XML::langProperty(node,
316 "longName",
317 "");
318 group->badge = XML::langProperty(node,
319 "badge",
320 paths.getStringValue("gmbadge"));
321 group->showBadge = XML::getBoolProperty(node,
322 "showBadge",
323 false);
324 group->highlightName = XML::getBoolProperty(node,
325 "highlightName",
326 false);
327 loadSubNodes(node, id, group);
328 parseInherit(node, id, group);
329 }
330 }
331 }
332
unload()333 void GroupDb::unload()
334 {
335 logger->log1("Unloading group database...");
336 FOR_EACH (GroupInfosIter, it, mGroups)
337 {
338 delete (*it).second;
339 }
340 mGroups.clear();
341 mLoaded = false;
342 }
343
getName(const int id)344 const std::string &GroupDb::getName(const int id)
345 {
346 GroupInfos::const_iterator it = mGroups.find(id);
347 if (it == mGroups.end())
348 {
349 reportAlways("Unknown group id requested: %d", id)
350 return mEmptyGroup.name;
351 }
352 return (*it).second->name;
353 }
354
getLongName(const int id)355 const std::string &GroupDb::getLongName(const int id)
356 {
357 GroupInfos::const_iterator it = mGroups.find(id);
358 if (it == mGroups.end())
359 {
360 reportAlways("Unknown group id requested: %d", id)
361 return mEmptyGroup.longName;
362 }
363 return (*it).second->longName;
364 }
365
getShowBadge(const int id)366 bool GroupDb::getShowBadge(const int id)
367 {
368 GroupInfos::const_iterator it = mGroups.find(id);
369 if (it == mGroups.end())
370 {
371 reportAlways("Unknown group id requested: %d", id)
372 return mEmptyGroup.showBadge;
373 }
374 return (*it).second->showBadge;
375 }
376
getHighlightName(const int id)377 bool GroupDb::getHighlightName(const int id)
378 {
379 GroupInfos::const_iterator it = mGroups.find(id);
380 if (it == mGroups.end())
381 {
382 reportAlways("Unknown group id requested: %d", id)
383 return mEmptyGroup.highlightName;
384 }
385 return (*it).second->highlightName;
386 }
387
getBadge(const int id)388 const std::string &GroupDb::getBadge(const int id)
389 {
390 GroupInfos::const_iterator it = mGroups.find(id);
391 if (it == mGroups.end())
392 {
393 reportAlways("Unknown group id requested: %d", id)
394 return mEmptyGroup.badge;
395 }
396 return (*it).second->badge;
397 }
398
getGroup(const int id)399 const GroupInfo *GroupDb::getGroup(const int id)
400 {
401 GroupInfos::const_iterator it = mGroups.find(id);
402 if (it == mGroups.end())
403 {
404 reportAlways("Unknown group id requested: %d", id)
405 return &mEmptyGroup;
406 }
407 return (*it).second;
408 }
409
isAllowCommand(const ServerCommandTypeT command)410 bool GroupDb::isAllowCommand(const ServerCommandTypeT command)
411 {
412 if (localPlayer == nullptr)
413 return false;
414 const int groupId = localPlayer->getGroupId();
415 const GroupInfo *const group = GroupDb::getGroup(groupId);
416
417 #ifdef TMWA_SUPPORT
418 // allow any commands for legacy if group > 0
419 if (Net::getNetworkType() == ServerType::TMWATHENA &&
420 localPlayer->isGM())
421 {
422 return true;
423 }
424 #endif
425 if (group->mPermissions[CAST_SIZE(ServerPermissionType::all_commands)] ==
426 Enable_true)
427 {
428 return true;
429 }
430 const ServerCommandEnable::Type enabled =
431 group->mCommands[CAST_SIZE(command)];
432 return (enabled & ServerCommandEnable::Self) != 0;
433 }
434
435 #ifdef UNITTESTS
getGroups()436 GroupDb::GroupInfos &GroupDb::getGroups()
437 {
438 return mGroups;
439 }
440 #endif // UNITTESTS
441