Line data Source code
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-2014, Michael Wallner <mike@php.net> |
10 : +--------------------------------------------------------------------+
11 : */
12 :
13 : #include "php_http_api.h"
14 :
15 : #include <ext/standard/php_lcg.h>
16 :
17 : #define BOUNDARY_OPEN(body) \
18 : do {\
19 : size_t size = php_http_message_body_size(body); \
20 : if (size) { \
21 : php_stream_truncate_set_size(php_http_message_body_stream(body), size - lenof("--" PHP_HTTP_CRLF)); \
22 : php_http_message_body_append(body, ZEND_STRL(PHP_HTTP_CRLF)); \
23 : } else { \
24 : php_http_message_body_appendf(body, "--%s" PHP_HTTP_CRLF, php_http_message_body_boundary(body)); \
25 : } \
26 : } while(0)
27 :
28 : #define BOUNDARY_CLOSE(body) \
29 : php_http_message_body_appendf(body, PHP_HTTP_CRLF "--%s--" PHP_HTTP_CRLF, php_http_message_body_boundary(body))
30 :
31 : static STATUS add_recursive_fields(php_http_message_body_t *body, const char *name, zval *value);
32 : static STATUS add_recursive_files(php_http_message_body_t *body, const char *name, zval *value);
33 :
34 624 : php_http_message_body_t *php_http_message_body_init(php_http_message_body_t **body_ptr, php_stream *stream TSRMLS_DC)
35 : {
36 : php_http_message_body_t *body;
37 :
38 624 : if (body_ptr && *body_ptr) {
39 209 : body = *body_ptr;
40 209 : ++body->refcount;
41 209 : return body;
42 : }
43 :
44 415 : body = ecalloc(1, sizeof(php_http_message_body_t));
45 415 : body->refcount = 1;
46 :
47 415 : if (stream) {
48 36 : php_stream_auto_cleanup(stream);
49 36 : body->stream_id = php_stream_get_resource_id(stream);
50 36 : zend_list_addref(body->stream_id);
51 : } else {
52 379 : stream = php_stream_temp_create(TEMP_STREAM_DEFAULT, 0xffff);
53 379 : php_stream_auto_cleanup(stream);
54 379 : body->stream_id = php_stream_get_resource_id(stream);
55 : }
56 : TSRMLS_SET_CTX(body->ts);
57 :
58 415 : if (body_ptr) {
59 0 : *body_ptr = body;
60 : }
61 :
62 415 : return body;
63 : }
64 :
65 77 : unsigned php_http_message_body_addref(php_http_message_body_t *body)
66 : {
67 77 : return ++body->refcount;
68 : }
69 :
70 116 : php_http_message_body_t *php_http_message_body_copy(php_http_message_body_t *from, php_http_message_body_t *to)
71 : {
72 116 : if (from) {
73 : TSRMLS_FETCH_FROM_CTX(from->ts);
74 :
75 116 : if (to) {
76 0 : php_stream_truncate_set_size(php_http_message_body_stream(to), 0);
77 : } else {
78 116 : to = php_http_message_body_init(NULL, NULL TSRMLS_CC);
79 : }
80 116 : php_http_message_body_to_stream(from, php_http_message_body_stream(to), 0, 0);
81 :
82 116 : if (to->boundary) {
83 0 : efree(to->boundary);
84 : }
85 116 : if (from->boundary) {
86 0 : to->boundary = estrdup(from->boundary);
87 : }
88 : } else {
89 0 : to = NULL;
90 : }
91 116 : return to;
92 : }
93 :
94 701 : void php_http_message_body_free(php_http_message_body_t **body_ptr)
95 : {
96 701 : if (*body_ptr) {
97 701 : php_http_message_body_t *body = *body_ptr;
98 :
99 701 : if (!--body->refcount) {
100 : TSRMLS_FETCH_FROM_CTX(body->ts);
101 : /* NOFIXME: shows leakinfo in DEBUG mode */
102 415 : zend_list_delete(body->stream_id);
103 415 : PTR_FREE(body->boundary);
104 415 : efree(body);
105 : }
106 701 : *body_ptr = NULL;
107 : }
108 701 : }
109 :
110 447 : const php_stream_statbuf *php_http_message_body_stat(php_http_message_body_t *body)
111 : {
112 : TSRMLS_FETCH_FROM_CTX(body->ts);
113 447 : php_stream_stat(php_http_message_body_stream(body), &body->ssb);
114 447 : return &body->ssb;
115 : }
116 :
117 12 : const char *php_http_message_body_boundary(php_http_message_body_t *body)
118 : {
119 12 : if (!body->boundary) {
120 : union { double dbl; int num[2]; } data;
121 : TSRMLS_FETCH_FROM_CTX(body->ts);
122 :
123 4 : data.dbl = php_combined_lcg(TSRMLS_C);
124 4 : spprintf(&body->boundary, 0, "%x.%x", data.num[0], data.num[1]);
125 : }
126 12 : return body->boundary;
127 : }
128 :
129 57 : char *php_http_message_body_etag(php_http_message_body_t *body)
130 : {
131 : php_http_etag_t *etag;
132 57 : php_stream *s = php_http_message_body_stream(body);
133 : TSRMLS_FETCH_FROM_CTX(body->ts);
134 :
135 : /* real file or temp buffer ? */
136 57 : if (s->ops != &php_stream_temp_ops && s->ops != &php_stream_memory_ops) {
137 6 : php_stream_stat(php_http_message_body_stream(body), &body->ssb);
138 :
139 6 : if (body->ssb.sb.st_mtime) {
140 : char *etag;
141 :
142 6 : spprintf(&etag, 0, "%lx-%lx-%lx", body->ssb.sb.st_ino, body->ssb.sb.st_mtime, body->ssb.sb.st_size);
143 6 : return etag;
144 : }
145 : }
146 :
147 : /* content based */
148 51 : if ((etag = php_http_etag_init(PHP_HTTP_G->env.etag_mode TSRMLS_CC))) {
149 51 : php_http_message_body_to_callback(body, (php_http_pass_callback_t) php_http_etag_update, etag, 0, 0);
150 51 : return php_http_etag_finish(etag);
151 : }
152 :
153 0 : return NULL;
154 : }
155 :
156 21 : void php_http_message_body_to_string(php_http_message_body_t *body, char **buf, size_t *len, off_t offset, size_t forlen)
157 : {
158 21 : php_stream *s = php_http_message_body_stream(body);
159 : TSRMLS_FETCH_FROM_CTX(body->ts);
160 :
161 21 : php_stream_seek(s, offset, SEEK_SET);
162 21 : if (!forlen) {
163 21 : forlen = -1;
164 : }
165 21 : *len = php_stream_copy_to_mem(s, buf, forlen, 0);
166 21 : }
167 :
168 117 : STATUS php_http_message_body_to_stream(php_http_message_body_t *body, php_stream *dst, off_t offset, size_t forlen)
169 : {
170 117 : php_stream *s = php_http_message_body_stream(body);
171 : TSRMLS_FETCH_FROM_CTX(body->ts);
172 :
173 117 : php_stream_seek(s, offset, SEEK_SET);
174 :
175 117 : if (!forlen) {
176 117 : forlen = -1;
177 : }
178 117 : return php_stream_copy_to_stream_ex(s, dst, forlen, NULL);
179 : }
180 :
181 99 : STATUS php_http_message_body_to_callback(php_http_message_body_t *body, php_http_pass_callback_t cb, void *cb_arg, off_t offset, size_t forlen)
182 : {
183 99 : php_stream *s = php_http_message_body_stream(body);
184 99 : char *buf = emalloc(0x1000);
185 : TSRMLS_FETCH_FROM_CTX(body->ts);
186 :
187 99 : php_stream_seek(s, offset, SEEK_SET);
188 :
189 99 : if (!forlen) {
190 91 : forlen = -1;
191 : }
192 98575 : while (!php_stream_eof(s)) {
193 98466 : size_t read = php_stream_read(s, buf, MIN(forlen, 0x1000));
194 :
195 98466 : if (read) {
196 98401 : if (-1 == cb(cb_arg, buf, read)) {
197 0 : return FAILURE;
198 : }
199 : }
200 :
201 98466 : if (read < MIN(forlen, sizeof(buf))) {
202 81 : break;
203 : }
204 :
205 98385 : if (forlen && !(forlen -= read)) {
206 8 : break;
207 : }
208 : }
209 99 : efree(buf);
210 :
211 99 : return SUCCESS;
212 : }
213 :
214 142645 : size_t php_http_message_body_append(php_http_message_body_t *body, const char *buf, size_t len)
215 : {
216 : php_stream *s;
217 : size_t written;
218 : TSRMLS_FETCH_FROM_CTX(body->ts);
219 :
220 142645 : if (!(s = php_http_message_body_stream(body))) {
221 0 : return -1;
222 : }
223 :
224 142645 : if (s->ops->seek) {
225 142645 : php_stream_seek(s, 0, SEEK_END);
226 : }
227 :
228 142645 : written = php_stream_write(s, buf, len);
229 :
230 142645 : if (written != len) {
231 1 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to append %zu bytes to body; wrote %zu", len, written);
232 : }
233 :
234 142645 : return len;
235 : }
236 :
237 18 : size_t php_http_message_body_appendf(php_http_message_body_t *body, const char *fmt, ...)
238 : {
239 : va_list argv;
240 : char *print_str;
241 : size_t print_len;
242 :
243 18 : va_start(argv, fmt);
244 18 : print_len = vspprintf(&print_str, 0, fmt, argv);
245 18 : va_end(argv);
246 :
247 18 : print_len = php_http_message_body_append(body, print_str, print_len);
248 18 : efree(print_str);
249 :
250 18 : return print_len;
251 : }
252 :
253 2 : STATUS php_http_message_body_add_form(php_http_message_body_t *body, HashTable *fields, HashTable *files)
254 : {
255 : zval tmp;
256 :
257 2 : if (fields) {
258 1 : INIT_PZVAL_ARRAY(&tmp, fields);
259 1 : if (SUCCESS != add_recursive_fields(body, NULL, &tmp)) {
260 0 : return FAILURE;
261 : }
262 : }
263 2 : if (files) {
264 2 : INIT_PZVAL_ARRAY(&tmp, files);
265 2 : if (SUCCESS != add_recursive_files(body, NULL, &tmp)) {
266 0 : return FAILURE;
267 : }
268 : }
269 :
270 2 : return SUCCESS;
271 : }
272 :
273 2 : void php_http_message_body_add_part(php_http_message_body_t *body, php_http_message_t *part)
274 : {
275 : TSRMLS_FETCH_FROM_CTX(body->ts);
276 :
277 2 : BOUNDARY_OPEN(body);
278 2 : php_http_message_to_callback(part, (php_http_pass_callback_t) php_http_message_body_append, body);
279 2 : BOUNDARY_CLOSE(body);
280 2 : }
281 :
282 :
283 4 : STATUS php_http_message_body_add_form_field(php_http_message_body_t *body, const char *name, const char *value_str, size_t value_len)
284 : {
285 : char *safe_name;
286 : TSRMLS_FETCH_FROM_CTX(body->ts);
287 :
288 4 : safe_name = php_addslashes(estrdup(name), strlen(name), NULL, 1 TSRMLS_CC);
289 :
290 4 : BOUNDARY_OPEN(body);
291 4 : php_http_message_body_appendf(
292 : body,
293 : "Content-Disposition: form-data; name=\"%s\"" PHP_HTTP_CRLF
294 : "" PHP_HTTP_CRLF,
295 : safe_name
296 : );
297 4 : php_http_message_body_append(body, value_str, value_len);
298 4 : BOUNDARY_CLOSE(body);
299 :
300 4 : efree(safe_name);
301 4 : return SUCCESS;
302 : }
303 :
304 2 : STATUS php_http_message_body_add_form_file(php_http_message_body_t *body, const char *name, const char *ctype, const char *path, php_stream *in)
305 : {
306 2 : char *safe_name, *path_dup = estrdup(path), *bname;
307 : size_t bname_len;
308 : TSRMLS_FETCH_FROM_CTX(body->ts);
309 :
310 2 : safe_name = php_addslashes(estrdup(name), strlen(name), NULL, 1 TSRMLS_CC);
311 :
312 2 : php_basename(path_dup, strlen(path_dup), NULL, 0, &bname, &bname_len TSRMLS_CC);
313 :
314 2 : BOUNDARY_OPEN(body);
315 2 : php_http_message_body_appendf(
316 : body,
317 : "Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"" PHP_HTTP_CRLF
318 : "Content-Transfer-Encoding: binary" PHP_HTTP_CRLF
319 : "Content-Type: %s" PHP_HTTP_CRLF
320 : PHP_HTTP_CRLF,
321 : safe_name, bname, ctype
322 : );
323 2 : php_stream_copy_to_stream_ex(in, php_http_message_body_stream(body), PHP_STREAM_COPY_ALL, NULL);
324 2 : BOUNDARY_CLOSE(body);
325 :
326 2 : efree(safe_name);
327 2 : efree(path_dup);
328 2 : efree(bname);
329 :
330 2 : return SUCCESS;
331 : }
332 :
333 8 : static inline char *format_key(uint type, char *str, ulong num, const char *prefix) {
334 8 : char *new_key = NULL;
335 :
336 8 : if (prefix && *prefix) {
337 6 : if (type == HASH_KEY_IS_STRING) {
338 0 : spprintf(&new_key, 0, "%s[%s]", prefix, str);
339 : } else {
340 3 : spprintf(&new_key, 0, "%s[%lu]", prefix, num);
341 : }
342 5 : } else if (type == HASH_KEY_IS_STRING) {
343 4 : new_key = estrdup(str);
344 : } else {
345 1 : new_key = estrdup("");
346 : }
347 :
348 8 : return new_key;
349 : }
350 :
351 6 : static STATUS add_recursive_fields(php_http_message_body_t *body, const char *name, zval *value)
352 : {
353 8 : if (Z_TYPE_P(value) == IS_ARRAY || Z_TYPE_P(value) == IS_OBJECT) {
354 : zval **val;
355 : HashTable *ht;
356 : HashPosition pos;
357 2 : php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
358 : TSRMLS_FETCH_FROM_CTX(body->ts);
359 :
360 2 : ht = HASH_OF(value);
361 2 : if (!ht->nApplyCount) {
362 2 : ++ht->nApplyCount;
363 7 : FOREACH_KEYVAL(pos, value, key, val) {
364 5 : char *str = format_key(key.type, key.str, key.num, name);
365 5 : if (SUCCESS != add_recursive_fields(body, str, *val)) {
366 0 : efree(str);
367 0 : ht->nApplyCount--;
368 0 : return FAILURE;
369 : }
370 5 : efree(str);
371 : }
372 2 : --ht->nApplyCount;
373 : }
374 : } else {
375 4 : zval *cpy = php_http_ztyp(IS_STRING, value);
376 4 : php_http_message_body_add_form_field(body, name, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy));
377 4 : zval_ptr_dtor(&cpy);
378 : }
379 :
380 6 : return SUCCESS;
381 : }
382 :
383 3 : static STATUS add_recursive_files(php_http_message_body_t *body, const char *name, zval *value)
384 : {
385 3 : zval **zdata = NULL, **zfile, **zname, **ztype;
386 : HashTable *ht;
387 : TSRMLS_FETCH_FROM_CTX(body->ts);
388 :
389 3 : if (Z_TYPE_P(value) != IS_ARRAY && Z_TYPE_P(value) != IS_OBJECT) {
390 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected array or object (name, type, file) for message body file to add");
391 0 : return FAILURE;
392 : }
393 :
394 3 : ht = HASH_OF(value);
395 :
396 3 : if ((SUCCESS != zend_hash_find(ht, ZEND_STRS("name"), (void *) &zname))
397 2 : || (SUCCESS != zend_hash_find(ht, ZEND_STRS("type"), (void *) &ztype))
398 2 : || (SUCCESS != zend_hash_find(ht, ZEND_STRS("file"), (void *) &zfile))
399 : ) {
400 : zval **val;
401 : HashPosition pos;
402 1 : php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
403 :
404 1 : if (!ht->nApplyCount) {
405 1 : ++ht->nApplyCount;
406 2 : FOREACH_HASH_KEYVAL(pos, ht, key, val) {
407 1 : if (Z_TYPE_PP(val) == IS_ARRAY || Z_TYPE_PP(val) == IS_OBJECT) {
408 1 : char *str = format_key(key.type, key.str, key.num, name);
409 :
410 1 : if (SUCCESS != add_recursive_files(body, str, *val)) {
411 0 : efree(str);
412 0 : --ht->nApplyCount;
413 0 : return FAILURE;
414 : }
415 1 : efree(str);
416 : }
417 : }
418 1 : --ht->nApplyCount;
419 : }
420 1 : return SUCCESS;
421 : } else {
422 : php_stream *stream;
423 2 : zval *zfc = php_http_ztyp(IS_STRING, *zfile);
424 :
425 2 : if (SUCCESS == zend_hash_find(ht, ZEND_STRS("data"), (void *) &zdata)) {
426 0 : if (Z_TYPE_PP(zdata) == IS_RESOURCE) {
427 0 : php_stream_from_zval_no_verify(stream, zdata);
428 : } else {
429 0 : zval *tmp = php_http_ztyp(IS_STRING, *zdata);
430 :
431 0 : stream = php_stream_memory_open(TEMP_STREAM_READONLY, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
432 0 : zval_ptr_dtor(&tmp);
433 : }
434 : } else {
435 2 : stream = php_stream_open_wrapper(Z_STRVAL_P(zfc), "r", REPORT_ERRORS|USE_PATH, NULL);
436 : }
437 :
438 2 : if (!stream) {
439 0 : zval_ptr_dtor(&zfc);
440 0 : return FAILURE;
441 : } else {
442 2 : zval *znc = php_http_ztyp(IS_STRING, *zname), *ztc = php_http_ztyp(IS_STRING, *ztype);
443 2 : char *key = format_key(HASH_KEY_IS_STRING, Z_STRVAL_P(znc), 0, name);
444 2 : STATUS ret = php_http_message_body_add_form_file(body, key, Z_STRVAL_P(ztc), Z_STRVAL_P(zfc), stream);
445 :
446 2 : efree(key);
447 2 : zval_ptr_dtor(&znc);
448 2 : zval_ptr_dtor(&ztc);
449 2 : zval_ptr_dtor(&zfc);
450 2 : if (!zdata || Z_TYPE_PP(zdata) != IS_RESOURCE) {
451 2 : php_stream_close(stream);
452 : }
453 2 : return ret;
454 : }
455 :
456 : }
457 : }
458 :
459 : struct splitbody_arg {
460 : php_http_buffer_t buf;
461 : php_http_message_parser_t *parser;
462 : char *boundary_str;
463 : size_t boundary_len;
464 : size_t consumed;
465 : };
466 :
467 1 : static size_t splitbody(void *opaque, char *buf, size_t len TSRMLS_DC)
468 : {
469 1 : struct splitbody_arg *arg = opaque;
470 1 : const char *boundary = NULL;
471 1 : size_t consumed = 0;
472 : int first_boundary;
473 :
474 : do {
475 3 : first_boundary = !(consumed || arg->consumed);
476 :
477 3 : if ((boundary = php_http_locate_str(buf, len, arg->boundary_str + first_boundary, arg->boundary_len - first_boundary))) {
478 3 : size_t real_boundary_len = arg->boundary_len - 1, cut;
479 3 : const char *real_boundary = boundary + !first_boundary;
480 3 : int eol_len = 0;
481 :
482 3 : if (buf + len <= real_boundary + real_boundary_len) {
483 : /* if we just have enough data for the boundary, it's just a byte too less */
484 0 : arg->consumed += consumed;
485 0 : return consumed;
486 : }
487 :
488 3 : if (!first_boundary) {
489 : /* this is not the first boundary, read rest of this message */
490 2 : php_http_buffer_append(&arg->buf, buf, real_boundary - buf);
491 2 : php_http_message_parser_parse(arg->parser, &arg->buf, 0, &arg->parser->message);
492 : }
493 :
494 : /* move after the boundary */
495 3 : cut = real_boundary - buf + real_boundary_len;
496 3 : buf += cut;
497 3 : len -= cut;
498 3 : consumed += cut;
499 :
500 3 : if (buf == php_http_locate_bin_eol(buf, len, &eol_len)) {
501 : /* skip CRLF */
502 2 : buf += eol_len;
503 2 : len -= eol_len;
504 2 : consumed += eol_len;
505 :
506 2 : if (!first_boundary) {
507 : /* advance messages */
508 : php_http_message_t *msg;
509 :
510 1 : msg = php_http_message_init(NULL, 0, NULL TSRMLS_CC);
511 1 : msg->parent = arg->parser->message;
512 1 : arg->parser->message = msg;
513 : }
514 : } else {
515 : /* is this the last boundary? */
516 1 : if (*buf == '-') {
517 : /* ignore the rest */
518 1 : consumed += len;
519 1 : len = 0;
520 : } else {
521 : /* let this be garbage */
522 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Malformed multipart boundary at pos %zu", consumed);
523 0 : return -1;
524 : }
525 : }
526 : }
527 3 : } while (boundary && len);
528 :
529 : /* let there be room for the next boundary */
530 1 : if (len > arg->boundary_len) {
531 0 : consumed += len - arg->boundary_len;
532 0 : php_http_buffer_append(&arg->buf, buf, len - arg->boundary_len);
533 0 : php_http_message_parser_parse(arg->parser, &arg->buf, 0, &arg->parser->message);
534 : }
535 :
536 1 : arg->consumed += consumed;
537 1 : return consumed;
538 : }
539 :
540 1 : php_http_message_t *php_http_message_body_split(php_http_message_body_t *body, const char *boundary)
541 : {
542 1 : php_stream *s = php_http_message_body_stream(body);
543 1 : php_http_buffer_t *tmp = NULL;
544 1 : php_http_message_t *msg = NULL;
545 : struct splitbody_arg arg;
546 : TSRMLS_FETCH_FROM_CTX(body->ts);
547 :
548 1 : php_http_buffer_init(&arg.buf);
549 1 : arg.parser = php_http_message_parser_init(NULL TSRMLS_CC);
550 1 : arg.boundary_len = spprintf(&arg.boundary_str, 0, "\n--%s", boundary);
551 1 : arg.consumed = 0;
552 :
553 1 : php_stream_rewind(s);
554 4 : while (!php_stream_eof(s)) {
555 2 : php_http_buffer_passthru(&tmp, 0x1000, (php_http_buffer_pass_func_t) _php_stream_read, s, splitbody, &arg TSRMLS_CC);
556 : }
557 :
558 1 : msg = arg.parser->message;
559 1 : arg.parser->message = NULL;
560 :
561 1 : php_http_buffer_free(&tmp);
562 1 : php_http_message_parser_free(&arg.parser);
563 1 : php_http_buffer_dtor(&arg.buf);
564 1 : PTR_FREE(arg.boundary_str);
565 :
566 1 : return msg;
567 : }
568 :
569 : static zend_object_handlers php_http_message_body_object_handlers;
570 :
571 34 : zend_object_value php_http_message_body_object_new(zend_class_entry *ce TSRMLS_DC)
572 : {
573 34 : return php_http_message_body_object_new_ex(ce, NULL, NULL TSRMLS_CC);
574 : }
575 :
576 246 : zend_object_value php_http_message_body_object_new_ex(zend_class_entry *ce, php_http_message_body_t *body, php_http_message_body_object_t **ptr TSRMLS_DC)
577 : {
578 : php_http_message_body_object_t *o;
579 :
580 246 : o = ecalloc(1, sizeof(php_http_message_body_object_t));
581 246 : zend_object_std_init((zend_object *) o, php_http_message_body_class_entry TSRMLS_CC);
582 246 : object_properties_init((zend_object *) o, ce);
583 :
584 246 : if (ptr) {
585 209 : *ptr = o;
586 : }
587 :
588 246 : if (body) {
589 212 : o->body = body;
590 : }
591 :
592 246 : o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_http_message_body_object_free, NULL TSRMLS_CC);
593 246 : o->zv.handlers = &php_http_message_body_object_handlers;
594 :
595 246 : return o->zv;
596 : }
597 :
598 1 : zend_object_value php_http_message_body_object_clone(zval *object TSRMLS_DC)
599 : {
600 : zend_object_value new_ov;
601 1 : php_http_message_body_object_t *new_obj = NULL;
602 1 : php_http_message_body_object_t *old_obj = zend_object_store_get_object(object TSRMLS_CC);
603 1 : php_http_message_body_t *body = php_http_message_body_copy(old_obj->body, NULL);
604 :
605 1 : new_ov = php_http_message_body_object_new_ex(old_obj->zo.ce, body, &new_obj TSRMLS_CC);
606 1 : zend_objects_clone_members(&new_obj->zo, new_ov, &old_obj->zo, Z_OBJ_HANDLE_P(object) TSRMLS_CC);
607 :
608 1 : return new_ov;
609 : }
610 :
611 246 : void php_http_message_body_object_free(void *object TSRMLS_DC)
612 : {
613 246 : php_http_message_body_object_t *obj = object;
614 :
615 246 : php_http_message_body_free(&obj->body);
616 246 : zend_object_std_dtor((zend_object *) obj TSRMLS_CC);
617 246 : efree(obj);
618 246 : }
619 :
620 : #define PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj) \
621 : do { \
622 : if (!obj->body) { \
623 : obj->body = php_http_message_body_init(NULL, NULL TSRMLS_CC); \
624 : } \
625 : } while(0)
626 :
627 : ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody___construct, 0, 0, 0)
628 : ZEND_ARG_INFO(0, stream)
629 : ZEND_END_ARG_INFO();
630 33 : PHP_METHOD(HttpMessageBody, __construct)
631 : {
632 33 : php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
633 33 : zval *zstream = NULL;
634 : php_stream *stream;
635 :
636 33 : php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r!", &zstream), invalid_arg, return);
637 :
638 33 : if (zstream) {
639 20 : php_http_expect(php_stream_from_zval_no_verify(stream, &zstream), unexpected_val, return);
640 :
641 20 : if (obj->body) {
642 0 : php_http_message_body_free(&obj->body);
643 : }
644 20 : obj->body = php_http_message_body_init(NULL, stream TSRMLS_CC);
645 : }
646 : }
647 :
648 : ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody___toString, 0, 0, 0)
649 : ZEND_END_ARG_INFO();
650 21 : PHP_METHOD(HttpMessageBody, __toString)
651 : {
652 21 : if (SUCCESS == zend_parse_parameters_none()) {
653 21 : php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
654 : char *str;
655 : size_t len;
656 :
657 21 : PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
658 :
659 21 : php_http_message_body_to_string(obj->body, &str, &len, 0, 0);
660 21 : if (str) {
661 19 : RETURN_STRINGL(str, len, 0);
662 : }
663 : }
664 2 : RETURN_EMPTY_STRING();
665 : }
666 :
667 : ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_unserialize, 0, 0, 1)
668 : ZEND_ARG_INFO(0, serialized)
669 : ZEND_END_ARG_INFO();
670 1 : PHP_METHOD(HttpMessageBody, unserialize)
671 : {
672 : char *us_str;
673 : int us_len;
674 :
675 1 : if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &us_str, &us_len)) {
676 1 : php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
677 1 : php_stream *s = php_stream_memory_open(0, us_str, us_len);
678 :
679 1 : obj->body = php_http_message_body_init(NULL, s TSRMLS_CC);
680 : }
681 1 : }
682 :
683 : ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_toStream, 0, 0, 1)
684 : ZEND_ARG_INFO(0, stream)
685 : ZEND_ARG_INFO(0, offset)
686 : ZEND_ARG_INFO(0, maxlen)
687 : ZEND_END_ARG_INFO();
688 1 : PHP_METHOD(HttpMessageBody, toStream)
689 : {
690 : zval *zstream;
691 1 : long offset = 0, forlen = 0;
692 :
693 1 : if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|ll", &zstream, &offset, &forlen)) {
694 : php_stream *stream;
695 1 : php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
696 :
697 1 : PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
698 :
699 2 : php_stream_from_zval(stream, &zstream);
700 1 : php_http_message_body_to_stream(obj->body, stream, offset, forlen);
701 1 : RETURN_ZVAL(getThis(), 1, 0);
702 : }
703 : }
704 :
705 : ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_toCallback, 0, 0, 1)
706 : ZEND_ARG_INFO(0, callback)
707 : ZEND_ARG_INFO(0, offset)
708 : ZEND_ARG_INFO(0, maxlen)
709 : ZEND_END_ARG_INFO();
710 1 : PHP_METHOD(HttpMessageBody, toCallback)
711 : {
712 : php_http_pass_fcall_arg_t fcd;
713 1 : long offset = 0, forlen = 0;
714 :
715 1 : if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f|ll", &fcd.fci, &fcd.fcc, &offset, &forlen)) {
716 1 : php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
717 :
718 1 : PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
719 :
720 1 : fcd.fcz = getThis();
721 1 : Z_ADDREF_P(fcd.fcz);
722 : TSRMLS_SET_CTX(fcd.ts);
723 :
724 1 : php_http_message_body_to_callback(obj->body, php_http_pass_fcall_callback, &fcd, offset, forlen);
725 1 : zend_fcall_info_args_clear(&fcd.fci, 1);
726 :
727 1 : zval_ptr_dtor(&fcd.fcz);
728 1 : RETURN_ZVAL(getThis(), 1, 0);
729 : }
730 : }
731 :
732 : ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_getResource, 0, 0, 0)
733 : ZEND_END_ARG_INFO();
734 13 : PHP_METHOD(HttpMessageBody, getResource)
735 : {
736 13 : if (SUCCESS == zend_parse_parameters_none()) {
737 13 : php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
738 :
739 13 : PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
740 :
741 13 : zend_list_addref(obj->body->stream_id);
742 13 : RETVAL_RESOURCE(obj->body->stream_id);
743 : }
744 13 : }
745 :
746 : ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_getBoundary, 0, 0, 0)
747 : ZEND_END_ARG_INFO();
748 2 : PHP_METHOD(HttpMessageBody, getBoundary)
749 : {
750 2 : if (SUCCESS == zend_parse_parameters_none()) {
751 2 : php_http_message_body_object_t * obj = zend_object_store_get_object(getThis() TSRMLS_CC);
752 :
753 2 : PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
754 :
755 2 : if (obj->body->boundary) {
756 1 : RETURN_STRING(obj->body->boundary, 1);
757 : }
758 : }
759 : }
760 :
761 : ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_append, 0, 0, 1)
762 : ZEND_ARG_INFO(0, string)
763 : ZEND_END_ARG_INFO();
764 131087 : PHP_METHOD(HttpMessageBody, append)
765 : {
766 : char *str;
767 : int len;
768 : php_http_message_body_object_t *obj;
769 :
770 131087 : php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len), invalid_arg, return);
771 :
772 131087 : obj = zend_object_store_get_object(getThis() TSRMLS_CC);
773 :
774 131087 : PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
775 :
776 131087 : php_http_expect(len == php_http_message_body_append(obj->body, str, len), runtime, return);
777 :
778 131087 : RETURN_ZVAL(getThis(), 1, 0);
779 : }
780 :
781 : ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_addForm, 0, 0, 0)
782 : ZEND_ARG_ARRAY_INFO(0, fields, 1)
783 : ZEND_ARG_ARRAY_INFO(0, files, 1)
784 : ZEND_END_ARG_INFO();
785 2 : PHP_METHOD(HttpMessageBody, addForm)
786 : {
787 2 : HashTable *fields = NULL, *files = NULL;
788 : php_http_message_body_object_t *obj;
789 :
790 2 : php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|h!h!", &fields, &files), invalid_arg, return);
791 :
792 2 : obj = zend_object_store_get_object(getThis() TSRMLS_CC);
793 :
794 2 : PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
795 :
796 2 : php_http_expect(SUCCESS == php_http_message_body_add_form(obj->body, fields, files), runtime, return);
797 :
798 2 : RETURN_ZVAL(getThis(), 1, 0);
799 : }
800 :
801 : ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_addPart, 0, 0, 1)
802 : ZEND_ARG_OBJ_INFO(0, message, http\\Message, 0)
803 : ZEND_END_ARG_INFO();
804 2 : PHP_METHOD(HttpMessageBody, addPart)
805 : {
806 : zval *zobj;
807 : php_http_message_body_object_t *obj;
808 : php_http_message_object_t *mobj;
809 : zend_error_handling zeh;
810 :
811 2 : php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &zobj, php_http_message_class_entry), invalid_arg, return);
812 :
813 2 : obj = zend_object_store_get_object(getThis() TSRMLS_CC);
814 2 : mobj = zend_object_store_get_object(zobj TSRMLS_CC);
815 :
816 2 : PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
817 :
818 2 : zend_replace_error_handling(EH_THROW, php_http_exception_runtime_class_entry, &zeh TSRMLS_CC);
819 2 : php_http_message_body_add_part(obj->body, mobj->message);
820 2 : zend_restore_error_handling(&zeh TSRMLS_CC);
821 :
822 2 : if (!EG(exception)) {
823 2 : RETURN_ZVAL(getThis(), 1, 0);
824 : }
825 : }
826 :
827 : ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_etag, 0, 0, 0)
828 : ZEND_END_ARG_INFO();
829 45 : PHP_METHOD(HttpMessageBody, etag)
830 : {
831 45 : if (SUCCESS == zend_parse_parameters_none()) {
832 45 : php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
833 : char *etag;
834 :
835 45 : PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
836 :
837 45 : if ((etag = php_http_message_body_etag(obj->body))) {
838 45 : RETURN_STRING(etag, 0);
839 : } else {
840 0 : RETURN_FALSE;
841 : }
842 : }
843 : }
844 :
845 : ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_stat, 0, 0, 0)
846 : ZEND_ARG_INFO(0, field)
847 : ZEND_END_ARG_INFO();
848 5 : PHP_METHOD(HttpMessageBody, stat)
849 : {
850 5 : char *field_str = NULL;
851 5 : int field_len = 0;
852 :
853 5 : if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &field_str, &field_len)) {
854 5 : php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
855 : const php_stream_statbuf *sb;
856 :
857 5 : PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj);
858 :
859 5 : if ((sb = php_http_message_body_stat(obj->body))) {
860 5 : if (field_str && field_len) {
861 4 : switch (*field_str) {
862 : case 's':
863 : case 'S':
864 5 : RETURN_LONG(sb->sb.st_size);
865 : break;
866 : case 'a':
867 : case 'A':
868 1 : RETURN_LONG(sb->sb.st_atime);
869 : break;
870 : case 'm':
871 : case 'M':
872 1 : RETURN_LONG(sb->sb.st_mtime);
873 : break;
874 : case 'c':
875 : case 'C':
876 1 : RETURN_LONG(sb->sb.st_ctime);
877 : break;
878 : default:
879 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown stat field: '%s' (should be one of [s]ize, [a]time, [m]time or [c]time)", field_str);
880 0 : break;
881 : }
882 0 : } else {
883 1 : object_init(return_value);
884 1 : add_property_long_ex(return_value, ZEND_STRS("size"), sb->sb.st_size TSRMLS_CC);
885 1 : add_property_long_ex(return_value, ZEND_STRS("atime"), sb->sb.st_atime TSRMLS_CC);
886 1 : add_property_long_ex(return_value, ZEND_STRS("mtime"), sb->sb.st_mtime TSRMLS_CC);
887 1 : add_property_long_ex(return_value, ZEND_STRS("ctime"), sb->sb.st_ctime TSRMLS_CC);
888 : }
889 : }
890 : }
891 : }
892 :
893 : static zend_function_entry php_http_message_body_methods[] = {
894 : PHP_ME(HttpMessageBody, __construct, ai_HttpMessageBody___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
895 : PHP_ME(HttpMessageBody, __toString, ai_HttpMessageBody___toString, ZEND_ACC_PUBLIC)
896 : PHP_MALIAS(HttpMessageBody, toString, __toString, ai_HttpMessageBody___toString, ZEND_ACC_PUBLIC)
897 : PHP_MALIAS(HttpMessageBody, serialize, __toString, ai_HttpMessageBody___toString, ZEND_ACC_PUBLIC)
898 : PHP_ME(HttpMessageBody, unserialize, ai_HttpMessageBody_unserialize, ZEND_ACC_PUBLIC)
899 : PHP_ME(HttpMessageBody, toStream, ai_HttpMessageBody_toStream, ZEND_ACC_PUBLIC)
900 : PHP_ME(HttpMessageBody, toCallback, ai_HttpMessageBody_toCallback, ZEND_ACC_PUBLIC)
901 : PHP_ME(HttpMessageBody, getResource, ai_HttpMessageBody_getResource, ZEND_ACC_PUBLIC)
902 : PHP_ME(HttpMessageBody, getBoundary, ai_HttpMessageBody_getBoundary, ZEND_ACC_PUBLIC)
903 : PHP_ME(HttpMessageBody, append, ai_HttpMessageBody_append, ZEND_ACC_PUBLIC)
904 : PHP_ME(HttpMessageBody, addForm, ai_HttpMessageBody_addForm, ZEND_ACC_PUBLIC)
905 : PHP_ME(HttpMessageBody, addPart, ai_HttpMessageBody_addPart, ZEND_ACC_PUBLIC)
906 : PHP_ME(HttpMessageBody, etag, ai_HttpMessageBody_etag, ZEND_ACC_PUBLIC)
907 : PHP_ME(HttpMessageBody, stat, ai_HttpMessageBody_stat, ZEND_ACC_PUBLIC)
908 : EMPTY_FUNCTION_ENTRY
909 : };
910 :
911 : zend_class_entry *php_http_message_body_class_entry;
912 :
913 374 : PHP_MINIT_FUNCTION(http_message_body)
914 : {
915 374 : zend_class_entry ce = {0};
916 :
917 374 : INIT_NS_CLASS_ENTRY(ce, "http\\Message", "Body", php_http_message_body_methods);
918 374 : php_http_message_body_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
919 374 : php_http_message_body_class_entry->create_object = php_http_message_body_object_new;
920 374 : memcpy(&php_http_message_body_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
921 374 : php_http_message_body_object_handlers.clone_obj = php_http_message_body_object_clone;
922 374 : zend_class_implements(php_http_message_body_class_entry TSRMLS_CC, 1, zend_ce_serializable);
923 :
924 374 : return SUCCESS;
925 : }
926 :
927 : /*
928 : * Local variables:
929 : * tab-width: 4
930 : * c-basic-offset: 4
931 : * End:
932 : * vim600: noet sw=4 ts=4 fdm=marker
933 : * vim<600: noet sw=4 ts=4
934 : */
|