GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/resources/db/groupdb.cpp Lines: 15 148 10.1 %
Date: 2021-03-17 Branches: 4 230 1.7 %

Line Branch Exec Source
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
1
    GroupDb::GroupInfos mGroups;
42
1
    const GroupInfo mEmptyGroup;
43
    bool mLoaded = false;
44
}  // namespace
45
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
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
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
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);
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);
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);
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);
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
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
333
204
void GroupDb::unload()
334
{
335
204
    logger->log1("Unloading group database...");
336
408
    FOR_EACH (GroupInfosIter, it, mGroups)
337
    {
338
        delete (*it).second;
339
    }
340
204
    mGroups.clear();
341
204
    mLoaded = false;
342
204
}
343
344
1
const std::string &GroupDb::getName(const int id)
345
{
346
2
    GroupInfos::const_iterator it = mGroups.find(id);
347
1
    if (it == mGroups.end())
348
    {
349
        reportAlways("Unknown group id requested: %d", id)
350
        return mEmptyGroup.name;
351
    }
352
1
    return (*it).second->name;
353
}
354
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
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
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
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
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
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
436
1
GroupDb::GroupInfos &GroupDb::getGroups()
437
{
438
1
    return mGroups;
439

3
}
440
#endif  // UNITTESTS