]> granicus.if.org Git - apache/blob - server/apreq_parser_urlencoded.c
eventMPM:
[apache] / server / apreq_parser_urlencoded.c
1 /*
2 **  Licensed to the Apache Software Foundation (ASF) under one or more
3 ** contributor license agreements.  See the NOTICE file distributed with
4 ** this work for additional information regarding copyright ownership.
5 ** The ASF licenses this file to You under the Apache License, Version 2.0
6 ** (the "License"); you may not use this file except in compliance with
7 ** the License.  You may obtain a copy of the License at
8 **
9 **      http://www.apache.org/licenses/LICENSE-2.0
10 **
11 **  Unless required by applicable law or agreed to in writing, software
12 **  distributed under the License is distributed on an "AS IS" BASIS,
13 **  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 **  See the License for the specific language governing permissions and
15 **  limitations under the License.
16 */
17
18 #include "apreq_parser.h"
19 #include "apreq_util.h"
20 #include "apreq_error.h"
21
22
23 #define PARSER_STATUS_CHECK(PREFIX)   do {         \
24     if (ctx->status == PREFIX##_ERROR)             \
25         return APREQ_ERROR_GENERAL;                \
26     else if (ctx->status == PREFIX##_COMPLETE)     \
27         return APR_SUCCESS;                        \
28     else if (bb == NULL)                           \
29         return APR_INCOMPLETE;                     \
30 } while (0);
31
32
33
34 struct url_ctx {
35     apr_bucket_brigade *bb;
36     apr_size_t          nlen;
37     apr_size_t          vlen;
38     enum {
39         URL_NAME,
40         URL_VALUE,
41         URL_COMPLETE,
42         URL_ERROR
43     }                   status;
44 };
45
46
47 /******************** application/x-www-form-urlencoded ********************/
48
49 static apr_status_t split_urlword(apreq_param_t **p, apr_pool_t *pool,
50                                   apr_bucket_brigade *bb,
51                                   apr_size_t nlen,
52                                   apr_size_t vlen)
53 {
54     apreq_param_t *param;
55     apreq_value_t *v;
56     apr_bucket *e, *f;
57     apr_status_t s;
58     struct iovec vec[APREQ_DEFAULT_NELTS];
59     apr_array_header_t arr;
60     apr_size_t mark;
61     apreq_charset_t charset;
62
63     if (nlen == 0)
64         return APR_EBADARG;
65
66     param = apreq_param_make(pool, NULL, nlen, NULL, vlen);
67     *(const apreq_value_t **)&v = &param->v;
68
69     arr.pool     = pool;
70     arr.elt_size = sizeof(struct iovec);
71     arr.nelts    = 0;
72     arr.nalloc   = APREQ_DEFAULT_NELTS;
73     arr.elts     = (char *)vec;
74
75     ++nlen, ++vlen;
76     e = APR_BRIGADE_FIRST(bb);
77
78     while (!APR_BUCKET_IS_EOS(e)) {
79         struct iovec *iov = apr_array_push(&arr);
80         apr_size_t len;
81         s = apr_bucket_read(e, (const char **)&iov->iov_base,
82                             &len, APR_BLOCK_READ);
83         if (s != APR_SUCCESS)
84             return s;
85
86         iov->iov_len = len;
87         nlen -= len;
88
89         e = APR_BUCKET_NEXT(e);
90
91         if (nlen == 0) {
92             iov->iov_len--;
93             break;
94         }
95     }
96
97     mark = arr.nelts;
98
99     while (!APR_BUCKET_IS_EOS(e)) {
100         struct iovec *iov = apr_array_push(&arr);
101         apr_size_t len;
102         s = apr_bucket_read(e, (const char **)&iov->iov_base,
103                             &len, APR_BLOCK_READ);
104         if (s != APR_SUCCESS)
105             return s;
106
107         iov->iov_len = len;
108         vlen -= len;
109
110         e = APR_BUCKET_NEXT(e);
111
112         if (vlen == 0) {
113             iov->iov_len--;
114             break;
115         }
116
117     }
118
119     s = apreq_decodev(v->data, &vlen,
120                       (struct iovec *)arr.elts + mark, arr.nelts - mark);
121     if (s != APR_SUCCESS)
122         return s;
123
124     charset = apreq_charset_divine(v->data, vlen);
125
126     v->name = v->data + vlen + 1;
127     v->dlen = vlen;
128
129     s = apreq_decodev(v->name, &nlen, (struct iovec *)arr.elts, mark);
130     if (s != APR_SUCCESS)
131         return s;
132
133     switch (apreq_charset_divine(v->name, nlen)) {
134     case APREQ_CHARSET_UTF8:
135         if (charset == APREQ_CHARSET_ASCII)
136             charset = APREQ_CHARSET_UTF8;
137     case APREQ_CHARSET_ASCII:
138         break;
139
140     case APREQ_CHARSET_LATIN1:
141         if (charset != APREQ_CHARSET_CP1252)
142             charset = APREQ_CHARSET_LATIN1;
143         break;
144     case APREQ_CHARSET_CP1252:
145         charset = APREQ_CHARSET_CP1252;
146     }
147
148     v->nlen = nlen;
149
150     while ((f = APR_BRIGADE_FIRST(bb)) != e)
151         apr_bucket_delete(f);
152
153     apreq_param_tainted_on(param);
154     apreq_param_charset_set(param, charset);
155     *p = param;
156     return APR_SUCCESS;
157 }
158
159 APREQ_DECLARE_PARSER(apreq_parse_urlencoded)
160 {
161     apr_pool_t *pool = parser->pool;
162     apr_bucket *e;
163     struct url_ctx *ctx;
164
165     if (parser->ctx == NULL) {
166         ctx = apr_pcalloc(pool, sizeof *ctx);
167         ctx->bb = apr_brigade_create(pool, parser->bucket_alloc);
168         parser->ctx = ctx;
169         ctx->status = URL_NAME;
170     }
171     else
172         ctx = parser->ctx;
173
174     PARSER_STATUS_CHECK(URL);
175     e = APR_BRIGADE_LAST(ctx->bb);
176     APR_BRIGADE_CONCAT(ctx->bb, bb);
177
178  parse_url_brigade:
179
180     for (e  = APR_BUCKET_NEXT(e);
181          e != APR_BRIGADE_SENTINEL(ctx->bb);
182          e  = APR_BUCKET_NEXT(e))
183     {
184         apreq_param_t *param;
185         apr_size_t off = 0, dlen;
186         const char *data;
187         apr_status_t s;
188
189         if (APR_BUCKET_IS_EOS(e)) {
190             if (ctx->status == URL_NAME) {
191                 s = APR_SUCCESS;
192             }
193             else {
194                 s = split_urlword(&param, pool, ctx->bb, ctx->nlen, ctx->vlen);
195                 if (parser->hook != NULL && s == APR_SUCCESS)
196                     s = apreq_hook_run(parser->hook, param, NULL);
197
198                 if (s == APR_SUCCESS) {
199                     apreq_value_table_add(&param->v, t);
200                     ctx->status = URL_COMPLETE;
201                 }
202                 else {
203                     ctx->status = URL_ERROR;
204                 }
205             }
206
207             APR_BRIGADE_CONCAT(bb, ctx->bb);
208             return s;
209         }
210
211         s = apr_bucket_read(e, &data, &dlen, APR_BLOCK_READ);
212         if ( s != APR_SUCCESS ) {
213             ctx->status = URL_ERROR;
214             return s;
215         }
216
217     parse_url_bucket:
218
219         switch (ctx->status) {
220
221         case URL_NAME:
222             while (off < dlen) {
223                 switch (data[off++]) {
224                 case '=':
225                     apr_bucket_split(e, off);
226                     dlen -= off;
227                     data += off;
228                     off = 0;
229                     e = APR_BUCKET_NEXT(e);
230                     ctx->status = URL_VALUE;
231                     goto parse_url_bucket;
232                 default:
233                     ++ctx->nlen;
234                 }
235             }
236             break;
237
238         case URL_VALUE:
239             while (off < dlen) {
240
241                 switch (data[off++]) {
242                 case '&':
243                 case ';':
244                     apr_bucket_split(e, off);
245                     s = split_urlword(&param, pool, ctx->bb,
246                                       ctx->nlen, ctx->vlen);
247                     if (parser->hook != NULL && s == APR_SUCCESS)
248                         s = apreq_hook_run(parser->hook, param, NULL);
249
250                     if (s != APR_SUCCESS) {
251                         ctx->status = URL_ERROR;
252                         return s;
253                     }
254
255                     apreq_value_table_add(&param->v, t);
256                     ctx->status = URL_NAME;
257                     ctx->nlen = 0;
258                     ctx->vlen = 0;
259                     e = APR_BRIGADE_SENTINEL(ctx->bb);
260                     goto parse_url_brigade;
261
262                 default:
263                     ++ctx->vlen;
264                 }
265             }
266             break;
267         default:
268             ; /* not reached */
269         }
270     }
271     apreq_brigade_setaside(ctx->bb, pool);
272     return APR_INCOMPLETE;
273 }
274
275