1 /*
2  * Portions taken from libapreq 1.33.
3  * Copyright 2007 The Apache Software Foundation.
4  * Used under the Apache License v2.0.
5  * http://search.cpan.org/~stas/libapreq-1.33/
6  */
7 
8 #ifndef __USE_GNU
9 #define __USE_GNU
10 #endif
11 #include <string.h>
12 #include <ctype.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 
16 #include "parser.h"
17 
18 static
19 void
req_plustospace(char * str)20 req_plustospace(char* str)
21 {
22     register int x;
23     for(x=0;str[x];x++)
24         if(str[x] == '+')
25             str[x] = ' ';
26 }
27 
28 static
29 char
x2c(char * what)30 x2c(char* what)
31 {
32     register char digit;
33 
34     digit = ((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0'));
35     digit *= 16;
36     digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0'));
37     return digit;
38 }
39 
40 static
41 unsigned int
utf8_convert(char * str)42 utf8_convert(char* str)
43 {
44     long x = 0;
45     int i = 0;
46     while (i < 4 ) {
47         if ( isxdigit(str[i]) != 0 ) {
48             if( isdigit(str[i]) != 0 ) {
49                 x = x * 16 + str[i] - '0';
50             }
51             else {
52                 str[i] = tolower( str[i] );
53                 x = x * 16 + str[i] - 'a' + 10;
54             }
55         }
56         else {
57             return 0;
58         }
59         i++;
60     }
61     if(i < 3)
62         return 0;
63     return (x);
64 }
65 
66 static
67 int
unescape_url_u(char * url)68 unescape_url_u(char* url)
69 {
70     register int x, y, badesc, badpath;
71 
72     badesc = 0;
73     badpath = 0;
74     for (x = 0, y = 0; url[y]; ++x, ++y) {
75         if (url[y] != '%'){
76             url[x] = url[y];
77         }
78         else {
79             if(url[y + 1] == 'u' || url[y + 1] == 'U'){
80                 unsigned int c = utf8_convert(&url[y + 2]);
81                 y += 5;
82                 if(c < 0x80){
83                     url[x] = c;
84                 }
85                 else if(c < 0x800) {
86                     url[x] = 0xc0 | (c >> 6);
87                     url[++x] = 0x80 | (c & 0x3f);
88                 }
89                 else if(c < 0x10000){
90                     url[x] = (0xe0 | (c >> 12));
91                     url[++x] = (0x80 | ((c >> 6) & 0x3f));
92                     url[++x] = (0x80 | (c & 0x3f));
93                 }
94                 else if(c < 0x200000){
95                     url[x] = 0xf0 | (c >> 18);
96                     url[++x] = 0x80 | ((c >> 12) & 0x3f);
97                     url[++x] = 0x80 | ((c >> 6) & 0x3f);
98                     url[++x] = 0x80 | (c & 0x3f);
99                 }
100                 else if(c < 0x4000000){
101                     url[x] = 0xf8 | (c >> 24);
102                     url[++x] = 0x80 | ((c >> 18) & 0x3f);
103                     url[++x] = 0x80 | ((c >> 12) & 0x3f);
104                     url[++x] = 0x80 | ((c >> 6) & 0x3f);
105                     url[++x] = 0x80 | (c & 0x3f);
106                 }
107                 else if(c < 0x8000000){
108                     url[x] = 0xfe | (c >> 30);
109                     url[++x] = 0x80 | ((c >> 24) & 0x3f);
110                     url[++x] = 0x80 | ((c >> 18) & 0x3f);
111                     url[++x] = 0x80 | ((c >> 12) & 0x3f);
112                     url[++x] = 0x80 | ((c >> 6) & 0x3f);
113                     url[++x] = 0x80 | (c & 0x3f);
114                 }
115             }
116             else {
117                 if (!isxdigit(url[y + 1]) || !isxdigit(url[y + 2])) {
118                     badesc = 1;
119                     url[x] = '%';
120                 }
121                 else {
122                     url[x] = x2c(&url[y + 1]);
123                     y += 2;
124                     if (url[x] == '/' || url[x] == '\0')
125                         badpath = 1;
126                 }
127             }
128         }
129     }
130     url[x] = '\0';
131     if (badesc)
132         return 0;
133     else if (badpath)
134         return 0;
135     else
136         return 1;
137 }
138 
139 static
140 char*
_strndup(char * str,size_t len)141 _strndup(char* str, size_t len)
142 {
143     char *dup = (char*) malloc(len+1);
144     if (dup) {
145         strncpy(dup, str, len);
146         dup[len] = '\0';
147     }
148     return dup;
149 }
150 
151 
152 static
153 char*
urlword(char ** line)154 urlword(char** line)
155 {
156     char* res = 0;
157     char* pos = *line;
158     char ch;
159 
160     while ( (ch = *pos) != '\0' && ch != ';' && ch != '&') {
161         ++pos;
162     }
163 
164     res = _strndup(*line, pos - *line);
165 
166     while (ch == ';' || ch == '&') {
167         ++pos;
168         ch = *pos;
169     }
170 
171     *line = pos;
172 
173     return res;
174 }
175 
176 char*
getword(char ** line,char stop)177 getword(char** line, char stop)
178 {
179     char* pos = *line;
180     int len;
181     char* res;
182 
183     while ((*pos != stop) && *pos) {
184         ++pos;
185     }
186 
187     len = pos - *line;
188     res = (char*)malloc(len + 1);
189     memcpy(res, *line, len);
190     res[len] = 0;
191 
192     if (stop) {
193         while (*pos == stop) {
194             ++pos;
195         }
196     }
197     *line = pos;
198 
199     return res;
200 }
201 
202 SV*
_split_to_parms(char * data)203 _split_to_parms(char* data)
204 {
205     char* val;
206     HV* hash = 0;
207 
208     while (*data && (val = urlword(&data))) {
209         char* val_orig = val;
210         char* key = getword(&val, '=');
211 
212         req_plustospace((char*)key);
213         unescape_url_u((char*)key);
214         req_plustospace((char*)val);
215         unescape_url_u((char*)val);
216 
217         if (!hash) {
218             hash = newHV();
219         }
220 
221         int klen = strlen(key);
222         SV* newval = newSVpv(val, 0);
223 
224         if (hv_exists(hash, key, klen)) {
225             /* this param already exists */
226 
227             SV** entry = hv_fetch(hash, key, klen, 0);
228             if (!entry) {
229                 return 0;
230             }
231 
232             if (SvROK(*entry) && SvTYPE(SvRV(*entry)) == SVt_PVAV) {
233                 /* already an arrayref, just push to the end */
234                 av_push((AV*) SvRV(*entry), newval);
235             }
236             else {
237                 /* just a scalar; wrap the new and old values in an arrayref */
238                 SV* values[2] = { *entry, newval };
239                 AV* array = av_make(2, values);      /* this copies the SVs... */
240                 SvREFCNT_dec(newval);                /* ... so destroy the original. */
241                 SV* aref = newRV_noinc((SV*) array); /* create an array ref... */
242                 hv_store(hash, key, klen, aref, 0);  /* ... and stash it in the hash */
243             }
244         }
245         else {
246             /* no existing param, pop this one in */
247             hv_store(hash, key, klen, newval, 0);
248         }
249 
250         free(key);
251         free(val_orig);
252     }
253 
254     return hash ? newRV_noinc((SV*) hash) : 0;
255 }
256