aboutsummaryrefslogtreecommitdiff
path: root/src/urlencoded.c
blob: b0891dcc19c4892d4486165aacc274908583b61d (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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#include "urlencoded.h"

#include "tools.h"
#include <ctype.h>
#include <stdlib.h>
#include <string.h>


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Local Shortcuts
 */
 static int is_hex(char c)
{
    return isdigit(c) || strchr("abcdef", tolower(c));
}

/* Call only if is_hex(c). */
static int from_hex(char c)
{
    if (isdigit(c)) {
        return c - '0';
    } else {
        return tolower(c) - 'a' + 10;
    }
}


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * URL Decoding
 */
static int deurl(char **data)
{
    char *val = *data;
    int   ti;
    int   ci;
    if (!val) {
        *data  = malloc(1);
        **data = 0;
        return 1;
    }
    for (ti = 0, ci = 0; val[ci]; ++ti, ++ci) {
        if (val[ci] == '%') {
            if (!is_hex(val[ci + 1]) || !is_hex(val[ci + 2])) {
                return 0;
            }
            /* Since chars can be signed, arithmetics are not safe. */
            val[ti] = from_hex(val[ci + 2]) |     /* 0000xxxx */
                      from_hex(val[ci + 1]) << 4; /* XXXXxxxx */
            ci += 2; /* Two extra characters are readed from code. */
        } else if (val[ci] == '+') {
            val[ti] = ' ';
        } else {
            val[ti] = val[ci];
        }
    }
    val[ti] = 0;
    return 1;
}


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Urlencoded Automata
 */
typedef struct automata automata;
typedef void (*state)(automata *a, char c);
struct automata {
    state         s;
    magi_params **list;
    char         *name;
    int           nlen;
    int           nsize;
    char         *data;
    int           dlen;
    int           dsize;
};

static void state_parse_data(automata *a, char c);
static void state_parse_name(automata *a, char c)
{
    if (c == '&' || c == ';') {
        a->s = 0;
        return;
    }
    if (c == '=') {
        if (!deurl(&a->name)) {
            a->s = 0;
            return;
        }
        a->s = state_parse_data;
        return;
    }
    magi_str_add(&a->name, &a->nlen, &a->nsize, c);
}

static void add_to_list(automata *a)
{
    magi_param param;
    param.name = a->name;
    param.data = a->data;
    magi_params_add(a->list, &param);
    a->name = 0;
    a->data = 0;
}

static void state_parse_data(automata *a, char c)
{
    if (c == '=') {
        a->s = 0;
        return;
    }
    if (c == '&' || c == ';') {
        if (!deurl(&a->data)) {
            a->s = 0;
            return;
        }
        add_to_list(a);
        a->s = state_parse_name;
        return;
    }
    magi_str_add(&a->data, &a->dlen, &a->dsize, c);
}

magi_error magi_parse_urlencoded(magi_params **list, const char *encoded)
{
    automata a = { state_parse_name, 0, 0, 0, 0, 0, 0, 0 };
    a.list     = list;
    *list      = 0;
    if (!encoded || !*encoded) {
        return 0;
    }
    for (; *encoded && a.s; ++encoded) {
        a.s(&a, *encoded);
    }
    if (a.s == state_parse_name || !a.s || !deurl(&a.data)) {
        return magi_error_urlenc;
    }
    add_to_list(&a);
    return 0;
}