summaryrefslogtreecommitdiff
path: root/src/query_parser.cc
blob: d957063f8fdc8fdf58df7be342c6f2223312fc35 (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
#include "common.hh"

#include "query_parser.hh"

namespace stuff {

namespace {

bool hex(char in, uint8_t* out) {
    if (in >= '0' && in <= '9') {
        *out = in - '0';
    } else if (in >= 'A' && in <= 'F') {
        *out = 10 + in - 'A';
    } else if (in >= 'a' && in <= 'f') {
        *out = 10 + in - 'a';
    } else {
        return false;
    }
    return true;
}

}  // namespace

bool QueryParser::parse(const std::string& in,
                        std::map<std::string, std::string>* out) {
    out->clear();
    if (in.empty()) return true;
    auto i = in.begin();
    if (*i == '?' || *i == '&') {
        ++i;
    }
    std::string key, value;
    bool have_key = false;
    while (true) {
        char c;
        if (i == in.end() || *i == '&') {
            if (!have_key && key.empty()) return false;
            (*out)[key] = value;
            if (i == in.end()) return true;
            have_key = false;
            key.clear();
            value.clear();
            if (++i == in.end()) return true;
            continue;
        } else if (*i == '=') {
            if (have_key) return false;
            if (key.empty()) return false;
            have_key = true;
            ++i;
            continue;
        } else if (*i == '+') {
            c = ' ';
        } else if (*i == '%') {
            if (++i == in.end()) return false;
            uint8_t h, l;
            if (!hex(*i, &h)) return false;
            if (++i == in.end()) return false;
            if (!hex(*i, &l)) return false;
            c = (h << 4) | l;
        } else if ((*i >= 'a' && *i <= 'z') ||
                   (*i >= '@' && *i <= 'Z') ||
                   (*i >= '0' && *i <= ';') ||
                   (*i >= '\'' && *i <= '*') ||
                   (*i >= ',' && *i <= '.') ||
                   *i == '!' || *i == '$' ||
                   *i == '_' || *i == '~') {
            c = *i;
        } else {
            // Character not allowed
            return false;
        }
        if (have_key) {
            value.push_back(c);
        } else {
            key.push_back(c);
        }
        ++i;
    }
}


}  // namespace stuff