aboutsummaryrefslogtreecommitdiff
path: root/src/urlencoded.c
blob: f00492b0e2f5b673d6002601dffe2ff4247f5b5f (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
#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
 */
struct automata {
    struct magi_params **list;
    char                *name;
    int                  nlen;
    int                  nsize;
    char                *data;
    int                  dlen;
    int                  dsize;
};
typedef void *(*state)(struct automata *a, char c);

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

static void add_to_list(struct automata *a)
{
    struct 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(struct automata *a, char c)
{
    if (c == '=') {
        return 0;
    } else if (c == '&' || c == ';') {
        if (!deurl(&a->data)) {
            return 0;
        }
        add_to_list(a);
        return state_parse_name;
    }
    magi_str_add(&a->data, &a->dlen, &a->dsize, c);
    return state_parse_data;
}

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