summaryrefslogtreecommitdiffstats
path: root/game/server/whitelist.cc
blob: 0807c195b2989711714c18c8e06d93699c8bba27 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include "server/pch.hh"

#include "server/whitelist.hh"

#include "core/config/boolean.hh"
#include "core/config/string.hh"

#include "core/io/config_map.hh"

#include "core/math/crc64.hh"

#include "core/utils/string.hh"

#include "server/game.hh"
#include "server/globals.hh"

constexpr static std::string_view DEFAULT_FILENAME = "whitelist.txt";
constexpr static char SEPARATOR_CHAR = ':';

config::Boolean whitelist::enabled(false);
config::String whitelist::filename(DEFAULT_FILENAME);

static emhash8::HashMap<std::string, std::uint64_t> whitelist_map;

void whitelist::init(void)
{
    globals::server_config.add_value("whitelist.enabled", whitelist::enabled);
    globals::server_config.add_value("whitelist.filename", whitelist::filename);
}

void whitelist::init_late(void)
{
    whitelist_map.clear();

    if(!whitelist::enabled.get_value()) {
        // Not enabled, shouldn't
        // even bother with parsing
        // the whitelist file
        return;
    }

    if(utils::is_whitespace(whitelist::filename.get_value())) {
        spdlog::warn("whitelist: enabled but filename is empty, using default ({})", DEFAULT_FILENAME);
        whitelist::filename.set(DEFAULT_FILENAME);
    }

    PHYSFS_File* file = PHYSFS_openRead(whitelist::filename.c_str());

    if(file == nullptr) {
        spdlog::warn("whitelist: {}: {}", whitelist::filename.get(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
        whitelist::enabled.set_value(false);
        return;
    }

    auto source = std::string(PHYSFS_fileLength(file), char(0x00));
    PHYSFS_readBytes(file, source.data(), source.size());
    PHYSFS_close(file);

    std::istringstream stream(source);
    std::string line;

    while(std::getline(stream, line)) {
        const auto location = line.find_last_of(SEPARATOR_CHAR);

        if(location == std::string::npos) {
            // Entries that don't define a password field default
            // to the global server password; this allows easier adding
            // of guest accounts which can later be edited to use a better password
            whitelist_map[line] = server_game::password_hash;
        }
        else {
            const auto username = line.substr(0, location);
            const auto password = line.substr(location + 1);
            whitelist_map[username] = math::crc64(password);
        }
    }

    PHYSFS_close(file);
}

void whitelist::shutdown(void)
{
    // UNDONE: implement saving
}

bool whitelist::contains(std::string_view username)
{
    return whitelist_map.contains(std::string(username));
}

bool whitelist::matches(std::string_view username, std::uint64_t password_hash)
{
    const auto it = whitelist_map.find(std::string(username));

    if(it == whitelist_map.cend()) {
        // Not whitelisted, no match
        return false;
    }

    return it->second == password_hash;
}