- Fix crash with non string property
[m6w6/ext-http] / http_request_method_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-2010, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 /* $Id$ */
14
15 #define HTTP_WANT_CURL
16 #include "php_http.h"
17
18 #include "php_http_api.h"
19 #include "php_http_request_api.h"
20 #include "php_http_request_method_api.h"
21
22 #if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_CURL) && !defined(WONKY)
23 # include "php_http_request_object.h"
24 #endif
25
26 /* {{{ char *http_request_methods[] */
27 static const char *const http_request_methods[] = {
28 "UNKNOWN",
29 /* HTTP/1.1 */
30 "GET",
31 "HEAD",
32 "POST",
33 "PUT",
34 "DELETE",
35 "OPTIONS",
36 "TRACE",
37 "CONNECT",
38 /* WebDAV - RFC 2518 */
39 "PROPFIND",
40 "PROPPATCH",
41 "MKCOL",
42 "COPY",
43 "MOVE",
44 "LOCK",
45 "UNLOCK",
46 /* WebDAV Versioning - RFC 3253 */
47 "VERSION-CONTROL",
48 "REPORT",
49 "CHECKOUT",
50 "CHECKIN",
51 "UNCHECKOUT",
52 "MKWORKSPACE",
53 "UPDATE",
54 "LABEL",
55 "MERGE",
56 "BASELINE-CONTROL",
57 "MKACTIVITY",
58 /* WebDAV Access Control - RFC 3744 */
59 "ACL",
60 NULL
61 };
62 /* }}} */
63
64 /* {{{ */
65 PHP_MINIT_FUNCTION(http_request_method)
66 {
67 /* HTTP/1.1 */
68 HTTP_LONG_CONSTANT("HTTP_METH_GET", HTTP_GET);
69 HTTP_LONG_CONSTANT("HTTP_METH_HEAD", HTTP_HEAD);
70 HTTP_LONG_CONSTANT("HTTP_METH_POST", HTTP_POST);
71 HTTP_LONG_CONSTANT("HTTP_METH_PUT", HTTP_PUT);
72 HTTP_LONG_CONSTANT("HTTP_METH_DELETE", HTTP_DELETE);
73 HTTP_LONG_CONSTANT("HTTP_METH_OPTIONS", HTTP_OPTIONS);
74 HTTP_LONG_CONSTANT("HTTP_METH_TRACE", HTTP_TRACE);
75 HTTP_LONG_CONSTANT("HTTP_METH_CONNECT", HTTP_CONNECT);
76 /* WebDAV - RFC 2518 */
77 HTTP_LONG_CONSTANT("HTTP_METH_PROPFIND", HTTP_PROPFIND);
78 HTTP_LONG_CONSTANT("HTTP_METH_PROPPATCH", HTTP_PROPPATCH);
79 HTTP_LONG_CONSTANT("HTTP_METH_MKCOL", HTTP_MKCOL);
80 HTTP_LONG_CONSTANT("HTTP_METH_COPY", HTTP_COPY);
81 HTTP_LONG_CONSTANT("HTTP_METH_MOVE", HTTP_MOVE);
82 HTTP_LONG_CONSTANT("HTTP_METH_LOCK", HTTP_LOCK);
83 HTTP_LONG_CONSTANT("HTTP_METH_UNLOCK", HTTP_UNLOCK);
84 /* WebDAV Versioning - RFC 3253 */
85 HTTP_LONG_CONSTANT("HTTP_METH_VERSION_CONTROL", HTTP_VERSION_CONTROL);
86 HTTP_LONG_CONSTANT("HTTP_METH_REPORT", HTTP_REPORT);
87 HTTP_LONG_CONSTANT("HTTP_METH_CHECKOUT", HTTP_CHECKOUT);
88 HTTP_LONG_CONSTANT("HTTP_METH_CHECKIN", HTTP_CHECKIN);
89 HTTP_LONG_CONSTANT("HTTP_METH_UNCHECKOUT", HTTP_UNCHECKOUT);
90 HTTP_LONG_CONSTANT("HTTP_METH_MKWORKSPACE", HTTP_MKWORKSPACE);
91 HTTP_LONG_CONSTANT("HTTP_METH_UPDATE", HTTP_UPDATE);
92 HTTP_LONG_CONSTANT("HTTP_METH_LABEL", HTTP_LABEL);
93 HTTP_LONG_CONSTANT("HTTP_METH_MERGE", HTTP_MERGE);
94 HTTP_LONG_CONSTANT("HTTP_METH_BASELINE_CONTROL", HTTP_BASELINE_CONTROL);
95 HTTP_LONG_CONSTANT("HTTP_METH_MKACTIVITY", HTTP_MKACTIVITY);
96 /* WebDAV Access Control - RFC 3744 */
97 HTTP_LONG_CONSTANT("HTTP_METH_ACL", HTTP_ACL);
98
99 return SUCCESS;
100 }
101
102 static void free_method(void *el)
103 {
104 efree(*(char **)el);
105 }
106
107 static void unregister_method(const char *name TSRMLS_DC)
108 {
109 char *ptr, tmp[sizeof("HTTP_METH_") + HTTP_REQUEST_METHOD_MAXLEN] = "HTTP_METH_";
110
111 strlcpy(tmp + lenof("HTTP_METH_"), name, HTTP_REQUEST_METHOD_MAXLEN);
112 for (ptr = tmp + lenof("HTTP_METH_"); *ptr; ++ptr) {
113 if (*ptr == '-') {
114 *ptr = '_';
115 }
116 }
117
118 #if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_CURL) && !defined(WONKY)
119 if (SUCCESS != zend_hash_del(&http_request_object_ce->constants_table, tmp + lenof("HTTP_"), strlen(tmp + lenof("HTTP_")) + 1)) {
120 http_error_ex(HE_NOTICE, HTTP_E_REQUEST_METHOD, "Could not unregister request method: HttpRequest::%s", tmp + lenof("HTTP_"));
121 }
122 #endif
123 if (SUCCESS != zend_hash_del(EG(zend_constants), tmp, strlen(tmp) + 1)) {
124 http_error_ex(HE_NOTICE, HTTP_E_REQUEST_METHOD, "Could not unregister request method: %s", tmp);
125 }
126 }
127
128 PHP_RINIT_FUNCTION(http_request_method)
129 {
130 HashTable ht;
131
132 zend_hash_init(&HTTP_G->request.methods.registered, 0, NULL, free_method, 0);
133 #define HTTP_METH_REG(m) \
134 { \
135 char *_m=estrdup(m); \
136 zend_hash_next_index_insert(&HTTP_G->request.methods.registered, (void *) &_m, sizeof(char *), NULL); \
137 }
138 HTTP_METH_REG("UNKNOWN");
139 /* HTTP/1.1 */
140 HTTP_METH_REG("GET");
141 HTTP_METH_REG("HEAD");
142 HTTP_METH_REG("POST");
143 HTTP_METH_REG("PUT");
144 HTTP_METH_REG("DELETE");
145 HTTP_METH_REG("OPTIONS");
146 HTTP_METH_REG("TRACE");
147 HTTP_METH_REG("CONNECT");
148 /* WebDAV - RFC 2518 */
149 HTTP_METH_REG("PROPFIND");
150 HTTP_METH_REG("PROPPATCH");
151 HTTP_METH_REG("MKCOL");
152 HTTP_METH_REG("COPY");
153 HTTP_METH_REG("MOVE");
154 HTTP_METH_REG("LOCK");
155 HTTP_METH_REG("UNLOCK");
156 /* WebDAV Versioning - RFC 3253 */
157 HTTP_METH_REG("VERSION-CONTROL");
158 HTTP_METH_REG("REPORT");
159 HTTP_METH_REG("CHECKOUT");
160 HTTP_METH_REG("CHECKIN");
161 HTTP_METH_REG("UNCHECKOUT");
162 HTTP_METH_REG("MKWORKSPACE");
163 HTTP_METH_REG("UPDATE");
164 HTTP_METH_REG("LABEL");
165 HTTP_METH_REG("MERGE");
166 HTTP_METH_REG("BASELINE-CONTROL");
167 HTTP_METH_REG("MKACTIVITY");
168 /* WebDAV Access Control - RFC 3744 */
169 HTTP_METH_REG("ACL");
170
171 zend_hash_init(&ht, 0, NULL, ZVAL_PTR_DTOR, 0);
172 if (*HTTP_G->request.methods.custom && SUCCESS == http_parse_params(HTTP_G->request.methods.custom, HTTP_PARAMS_DEFAULT, &ht)) {
173 HashPosition pos;
174 zval **val;
175
176 FOREACH_HASH_VAL(pos, &ht, val) {
177 if (Z_TYPE_PP(val) == IS_STRING) {
178 http_request_method_register(Z_STRVAL_PP(val), Z_STRLEN_PP(val));
179 }
180 }
181 }
182 zend_hash_destroy(&ht);
183
184 return SUCCESS;
185 }
186
187 PHP_RSHUTDOWN_FUNCTION(http_request_method)
188 {
189 char **name;
190 int i, c = zend_hash_next_free_element(&HTTP_G->request.methods.registered);
191
192 for (i = HTTP_MAX_REQUEST_METHOD; i < c; ++i) {
193 if (SUCCESS == zend_hash_index_find(&HTTP_G->request.methods.registered, i, (void *) &name)) {
194 unregister_method(*name TSRMLS_CC);
195 }
196 }
197
198 zend_hash_destroy(&HTTP_G->request.methods.registered);
199 return SUCCESS;
200 }
201
202 #define http_request_method_cncl(m, c) _http_request_method_cncl_ex((m), strlen(m), (c) TSRMLS_CC)
203 #define http_request_method_cncl_ex(m, l, c) _http_request_method_cncl_ex((m), (l), (c) TSRMLS_CC)
204 static STATUS _http_request_method_cncl_ex(const char *method_name, int method_name_len, char **cnst TSRMLS_DC)
205 {
206 int i;
207 char *cncl;
208
209 if (method_name_len >= HTTP_REQUEST_METHOD_MAXLEN) {
210 http_error_ex(HE_WARNING, HTTP_E_REQUEST_METHOD, "Request method too long (%s)", method_name);
211 }
212 cncl = emalloc(method_name_len + 1);
213
214 for (i = 0; i < method_name_len; ++i) {
215 switch (method_name[i]) {
216 case '-':
217 cncl[i] = '-';
218 break;
219
220 default:
221 if (!HTTP_IS_CTYPE(alnum, method_name[i])) {
222 efree(cncl);
223 http_error_ex(HE_WARNING, HTTP_E_REQUEST_METHOD, "Request method contains illegal characters (%s)", method_name);
224 return FAILURE;
225 }
226 cncl[i] = HTTP_TO_CTYPE(upper, method_name[i]);
227 break;
228 }
229 }
230 cncl[method_name_len] = '\0';
231
232 *cnst = cncl;
233 return SUCCESS;
234 }
235
236 PHP_HTTP_API const char *_http_request_method_name(http_request_method m TSRMLS_DC)
237 {
238 char **name;
239
240 if (SUCCESS == zend_hash_index_find(&HTTP_G->request.methods.registered, m, (void *) &name)) {
241 return *name;
242 }
243 return "UNKNOWN";
244 }
245
246 PHP_HTTP_API int _http_request_method_exists(int by_name, http_request_method id_num, const char *id_str TSRMLS_DC)
247 {
248 char *id_dup;
249
250 if (by_name && (SUCCESS == http_request_method_cncl(id_str, &id_dup))) {
251 char **name;
252 HashPosition pos;
253 HashKey key = initHashKey(0);
254
255 FOREACH_HASH_KEYVAL(pos, &HTTP_G->request.methods.registered, key, name) {
256 if (key.type == HASH_KEY_IS_LONG && !strcmp(*name, id_dup)) {
257 efree(id_dup);
258 return key.num;
259 }
260 }
261 efree(id_dup);
262 } else if (zend_hash_index_exists(&HTTP_G->request.methods.registered, id_num)){
263 return id_num;
264 }
265 return 0;
266 }
267
268 PHP_HTTP_API int _http_request_method_register(const char *method_str, int method_len TSRMLS_DC)
269 {
270 char *method_dup, *ptr, tmp[sizeof("HTTP_METH_") + HTTP_REQUEST_METHOD_MAXLEN] = "HTTP_METH_";
271 int method_num = http_request_method_exists(1, 0, method_str);
272
273 if (!method_num && (SUCCESS == http_request_method_cncl_ex(method_str, method_len, &method_dup))) {
274 method_num = zend_hash_next_free_element(&HTTP_G->request.methods.registered);
275 zend_hash_index_update(&HTTP_G->request.methods.registered, method_num, (void *) &method_dup, sizeof(char *), NULL);
276
277 strlcpy(tmp + lenof("HTTP_METH_"), method_dup, HTTP_REQUEST_METHOD_MAXLEN);
278 for (ptr = tmp + lenof("HTTP_METH_"); *ptr; ++ptr) {
279 if (*ptr == '-') {
280 *ptr = '_';
281 }
282 }
283
284 zend_register_long_constant(tmp, strlen(tmp) + 1, method_num, CONST_CS, http_module_number TSRMLS_CC);
285 #if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_CURL) && !defined(WONKY)
286 zend_declare_class_constant_long(http_request_object_ce, tmp + lenof("HTTP_"), strlen(tmp + lenof("HTTP_")), method_num TSRMLS_CC);
287 #endif
288 }
289
290 return method_num;
291 }
292
293 PHP_HTTP_API STATUS _http_request_method_unregister(int method TSRMLS_DC)
294 {
295 char **name;
296
297 if (HTTP_STD_REQUEST_METHOD(method)) {
298 http_error_ex(HE_WARNING, HTTP_E_REQUEST_METHOD, "Standard request methods cannot be unregistered");
299 return FAILURE;
300 }
301
302 if (SUCCESS != zend_hash_index_find(&HTTP_G->request.methods.registered, method, (void *) &name)) {
303 http_error_ex(HE_NOTICE, HTTP_E_REQUEST_METHOD, "Custom request method with id %d does not exist", method);
304 return FAILURE;
305 }
306
307 unregister_method(*name TSRMLS_CC);
308
309 zend_hash_index_del(&HTTP_G->request.methods.registered, method);
310 return SUCCESS;
311 }
312
313 /*
314 * Local variables:
315 * tab-width: 4
316 * c-basic-offset: 4
317 * End:
318 * vim600: noet sw=4 ts=4 fdm=marker
319 * vim<600: noet sw=4 ts=4
320 */
321