- spawn off http_cookie_api
[m6w6/ext-http] / http_cookie_api.c
1 /*
2 +--------------------------------------------------------------------+
3 | PECL :: http |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2004-2006, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 /* $Id$ */
14
15 #include "php_http.h"
16 #include "php_http_api.h"
17 #include "php_http_date_api.h"
18 #include "php_http_cookie_api.h"
19
20 PHP_MINIT_FUNCTION(http_cookie)
21 {
22 HTTP_LONG_CONSTANT("HTTP_COOKIE_PARSE_RAW", HTTP_COOKIE_PARSE_RAW);
23 HTTP_LONG_CONSTANT("HTTP_COOKIE_SECURE", HTTP_COOKIE_SECURE);
24 HTTP_LONG_CONSTANT("HTTP_COOKIE_HTTPONLY", HTTP_COOKIE_HTTPONLY);
25
26 return SUCCESS;
27 }
28
29 PHP_HTTP_API http_cookie_list *_http_cookie_list_init(http_cookie_list *list TSRMLS_DC)
30 {
31 if (!list) {
32 list = emalloc(sizeof(http_cookie_list));
33 }
34
35 zend_hash_init(&list->cookies, 0, NULL, ZVAL_PTR_DTOR, 0);
36 zend_hash_init(&list->extras, 0, NULL, ZVAL_PTR_DTOR, 0);
37
38 list->path = NULL;
39 list->domain = NULL;
40 list->expires = 0;
41 list->flags = 0;
42
43 return list;
44 }
45
46 PHP_HTTP_API void _http_cookie_list_dtor(http_cookie_list *list TSRMLS_DC)
47 {
48 if (list) {
49 zend_hash_destroy(&list->cookies);
50 zend_hash_destroy(&list->extras);
51
52 STR_SET(list->path, NULL);
53 STR_SET(list->domain, NULL);
54 }
55 }
56
57 PHP_HTTP_API void _http_cookie_list_free(http_cookie_list **list TSRMLS_DC)
58 {
59 if (list) {
60 http_cookie_list_dtor(*list);
61 efree(*list);
62 *list = NULL;
63 }
64 }
65
66 PHP_HTTP_API const char *_http_cookie_list_get_cookie(http_cookie_list *list, const char *name, size_t name_len TSRMLS_DC)
67 {
68 zval **cookie = NULL;
69 if ((SUCCESS != zend_hash_find(&list->cookies, name, name_len + 1, (void **) &cookie)) || (Z_TYPE_PP(cookie) != IS_STRING)) {
70 return NULL;
71 }
72 return Z_STRVAL_PP(cookie);
73 }
74
75 PHP_HTTP_API const char *_http_cookie_list_get_extra(http_cookie_list *list, const char *name, size_t name_len TSRMLS_DC)
76 {
77 zval **extra = NULL;
78 if ((SUCCESS != zend_hash_find(&list->extras,name, name_len + 1, (void **) &extra)) || (Z_TYPE_PP(extra) != IS_STRING)) {
79 return NULL;
80 }
81 return Z_STRVAL_PP(extra);
82 }
83
84 PHP_HTTP_API void _http_cookie_list_add_cookie(http_cookie_list *list, const char *name, size_t name_len, const char *value, size_t value_len TSRMLS_DC)
85 {
86 zval *cookie_value;
87 MAKE_STD_ZVAL(cookie_value);
88 ZVAL_STRINGL(cookie_value, estrndup(value, value_len), value_len, 0);
89 zend_hash_update(&list->cookies, name, name_len + 1, (void *) &cookie_value, sizeof(zval *), NULL);
90 }
91
92 PHP_HTTP_API void _http_cookie_list_add_extra(http_cookie_list *list, const char *name, size_t name_len, const char *value, size_t value_len TSRMLS_DC)
93 {
94 zval *cookie_value;
95 MAKE_STD_ZVAL(cookie_value);
96 ZVAL_STRINGL(cookie_value, estrndup(value, value_len), value_len, 0);
97 zend_hash_update(&list->extras, name, name_len + 1, (void *) &cookie_value, sizeof(zval *), NULL);
98 }
99
100 #define http_cookie_list_set_item_ex(l, i, il, v, vl, f, a) _http_cookie_list_set_item_ex((l), (i), (il), (v), (vl), (f), (a) TSRMLS_CC)
101 static inline void _http_cookie_list_set_item_ex(http_cookie_list *list, const char *item, int item_len, const char *value, int value_len, long flags, char **allowed_extras TSRMLS_DC)
102 {
103 char *key = estrndup(item, item_len);
104
105 if (!strcasecmp(key, "path")) {
106 STR_SET(list->path, estrndup(value, value_len));
107 } else if (!strcasecmp(key, "domain")) {
108 STR_SET(list->domain, estrndup(value, value_len));
109 } else if (!strcasecmp(key, "expires")) {
110 const char *date = estrndup(value, value_len);
111 list->expires = http_parse_date(date);
112 efree(date);
113 } else if (!strcasecmp(key, "secure")) {
114 list->flags |= HTTP_COOKIE_SECURE;
115 } else if (!strcasecmp(key, "httpOnly")) {
116 list->flags |= HTTP_COOKIE_HTTPONLY;
117 } else {
118 /* check for extra */
119 if (allowed_extras) {
120 for (; *allowed_extras; ++allowed_extras) {
121 if (!strcasecmp(key, *allowed_extras)) {
122 http_cookie_list_add_extra(list, key, item_len, value, value_len);
123
124 efree(key);
125 return;
126 }
127 }
128 }
129 /* new cookie */
130 http_cookie_list_add_cookie(list, key, item_len, value, value_len);
131 }
132 efree(key);
133 }
134
135
136 #define ST_QUOTE 1
137 #define ST_VALUE 2
138 #define ST_KEY 3
139 #define ST_ASSIGN 4
140 #define ST_ADD 5
141
142 /* {{{ http_cookie_list *http_parse_cookie(char *, long) */
143 PHP_HTTP_API http_cookie_list *_http_parse_cookie_ex(http_cookie_list *list, const char *string, long flags, char **allowed_extras TSRMLS_DC)
144 {
145 int free_list = !list, st = ST_KEY, keylen = 0, vallen = 0;
146 char *s, *c, *key = NULL, *val = NULL;
147
148 list = http_cookie_list_init(list);
149
150 c = s = estrdup(string);
151 for(;;) {
152 #if 0
153 char *tk = NULL, *tv = NULL;
154
155 if (key) {
156 if (keylen) {
157 tk= estrndup(key, keylen);
158 } else {
159 tk = ecalloc(1, 7);
160 memcpy(tk, key, 3);
161 tk[3]='.'; tk[4]='.'; tk[5]='.';
162 }
163 }
164 if (val) {
165 if (vallen) {
166 tv = estrndup(val, vallen);
167 } else {
168 tv = ecalloc(1, 7);
169 memcpy(tv, val, 3);
170 tv[3]='.'; tv[4]='.'; tv[5]='.';
171 }
172 }
173 fprintf(stderr, "[%6s] %c \"%s=%s\"\n",
174 (
175 st == ST_QUOTE ? "QUOTE" :
176 st == ST_VALUE ? "VALUE" :
177 st == ST_KEY ? "KEY" :
178 st == ST_ASSIGN ? "ASSIGN" :
179 st == ST_ADD ? "ADD":
180 "HUH?"
181 ), *c, tk, tv
182 );
183 STR_FREE(tk); STR_FREE(tv);
184 #endif
185 switch (st)
186 {
187 case ST_QUOTE:
188 if (*c == '"') {
189 if (*(c-1) != '\\') {
190 st = ST_ADD;
191 } else {
192 memmove(c-1, c, strlen(c)+1);
193 }
194 } else {
195 if (!val) {
196 val = c;
197 }
198 if (!*c) {
199 --val;
200 st = ST_ADD;
201 }
202 }
203 break;
204
205 case ST_VALUE:
206 switch (*c)
207 {
208 case '"':
209 if (!val) {
210 st = ST_QUOTE;
211 }
212 break;
213
214 case ' ':
215 break;
216
217 case ';':
218 if (!*(c+1)) {
219 goto add;
220 }
221 case '\0':
222 st = ST_ADD;
223 break;
224
225 default:
226 if (!val) {
227 val = c;
228 }
229 break;
230 }
231 break;
232
233 case ST_KEY:
234 switch (*c)
235 {
236 case ',':
237 case '\r':
238 case '\n':
239 case '\t':
240 case '\013':
241 case '\014':
242 goto failure;
243 break;
244
245 case '=':
246 if (key) {
247 keylen = c - key;
248 st = ST_VALUE;
249 } else {
250 goto failure;
251 }
252 break;
253
254 case ' ':
255 if (key) {
256 keylen = c - key;
257 st = ST_ASSIGN;
258 }
259 break;
260
261 case ';':
262 case '\0':
263 if (key) {
264 keylen = c - key;
265 st = ST_ADD;
266 }
267 break;
268
269 default:
270 if (!key) {
271 key = c;
272 }
273 break;
274 }
275 break;
276
277 case ST_ASSIGN:
278 if (*c == '=') {
279 st = ST_VALUE;
280 } else if (!*c || *c == ';') {
281 st = ST_ADD;
282 } else if (*c != ' ') {
283 goto failure;
284 }
285 break;
286
287 case ST_ADD:
288 add:
289 if (val) {
290 vallen = c - val;
291 if (*c) --vallen;
292 while (val[vallen-1] == ' ') --vallen;
293 } else {
294 val = "";
295 vallen = 0;
296 }
297 http_cookie_list_set_item_ex(list, key, keylen, val, vallen, flags, allowed_extras);
298 st = ST_KEY;
299 key = val = NULL;
300 keylen = vallen = 0;
301 break;
302 }
303
304 if (*c) {
305 ++c;
306 } else if (st == ST_ADD) {
307 goto add;
308 } else {
309 break;
310 }
311 }
312
313 efree(s);
314 return list;
315
316 failure:
317 http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Unexpected character (%c) at pos %tu of %zu", *c, c-s, strlen(s));
318 if (free_list) {
319 http_cookie_list_free(&list);
320 } else {
321 http_cookie_list_dtor(list);
322 }
323 efree(s);
324 return FAILURE;
325 }
326 /* }}} */
327
328 PHP_HTTP_API void _http_cookie_list_tostruct(http_cookie_list *list, zval *strct TSRMLS_DC)
329 {
330 zval array, *cookies, *extras;
331
332 INIT_ZARR(array, HASH_OF(strct));
333
334 MAKE_STD_ZVAL(cookies);
335 array_init(cookies);
336 zend_hash_copy(Z_ARRVAL_P(cookies), &list->cookies, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
337 add_assoc_zval(&array, "cookies", cookies);
338
339 MAKE_STD_ZVAL(extras);
340 array_init(extras);
341 zend_hash_copy(Z_ARRVAL_P(extras), &list->extras, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
342 add_assoc_zval(&array, "extras", extras);
343
344 add_assoc_long(&array, "flags", list->flags);
345 add_assoc_long(&array, "expires", (long) list->expires);
346 add_assoc_string(&array, "path", list->path?list->path:"", 1);
347 add_assoc_string(&array, "domain", list->domain?list->domain:"", 1);
348 }
349
350 /*
351 * Local variables:
352 * tab-width: 4
353 * c-basic-offset: 4
354 * End:
355 * vim600: noet sw=4 ts=4 fdm=marker
356 * vim<600: noet sw=4 ts=4
357 */