aboutsummaryrefslogtreecommitdiff
path: root/src/urlencoded.c
blob: 9d596edac4b86fd53862d2c3e43080be0f84c3b6 (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
#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 {
    magi_param_list **list;
    magi_str          name;
    magi_str          data;
} automata;
typedef void *(*state)(automata *a, char c);

static void *state_parse_data(automata *a, char c);
static void *state_parse_name(automata *a, char c)
{
    if (c == '&' || c == ';') {
        return 0;
    }
    if (c == '=') {
        if (!deurl(&a->name.data)) {
            return 0;
        }
        return state_parse_name;
    }
    magi_str_add(&a->name, c);
    return a->name.size ? state_parse_name : 0;
}

static int add_to_list(automata *a)
{
    magi_param param;
    param.name = a->name.data;
    param.data = a->data.data;
    if (!magi_param_list_add(a->list, param)) {
        return 0;
    }
    a->name.data = 0;
    a->data.data = 0;
    return 1;
}

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

magi_error magi_urlencoded(magi_param_list **list, const char *encoded)
{
    st       state;
    automata a = { 0, { 0, 0 }, 1, 0 };
    a.list     = list;
    *list      = 0;
    if (!encoded || !*encoded) {
        return 0;
    }
    for (; *encoded; ++encoded) {
        s = s(&a, *encoded);
        if (!s) {
            return auto_free(a);
        }
    }
    if (state == st_name || !state || !end_data(&a)) {
        free(a.param.name);
        free(a.param.data);
        return auto_free(a);
    }
    return 0;
}