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-2007, Michael Wallner <mike@php.net> |
10 : +--------------------------------------------------------------------+
11 : */
12 :
13 : /* $Id: http_send_api.c,v 1.70 2007/02/07 11:50:27 mike Exp $ */
14 :
15 : #define HTTP_WANT_SAPI
16 : #define HTTP_WANT_ZLIB
17 : #define HTTP_WANT_MAGIC
18 : #include "php_http.h"
19 :
20 : #include "php_streams.h"
21 : #include "ext/standard/php_lcg.h"
22 :
23 : #include "php_http_api.h"
24 : #include "php_http_cache_api.h"
25 : #include "php_http_date_api.h"
26 : #include "php_http_encoding_api.h"
27 : #include "php_http_headers_api.h"
28 : #include "php_http_send_api.h"
29 :
30 : /* {{{ http_flush() */
31 : #define http_flush(d, l) _http_flush(NULL, (d), (l) TSRMLS_CC)
32 : static inline void _http_flush(void *nothing, const char *data, size_t data_len TSRMLS_DC)
33 75 : {
34 75 : PHPWRITE(data, data_len);
35 : /* we really only need to flush when throttling is enabled,
36 : because we push the data as fast as possible anyway if not */
37 75 : if (HTTP_G->send.throttle_delay >= HTTP_DIFFSEC) {
38 20 : if (OG(ob_nesting_level)) {
39 0 : php_end_ob_buffer(1, 1 TSRMLS_CC);
40 : }
41 20 : if (!OG(implicit_flush)) {
42 20 : sapi_flush(TSRMLS_C);
43 : }
44 20 : http_sleep(HTTP_G->send.throttle_delay);
45 : }
46 75 : }
47 : /* }}} */
48 :
49 : /* {{{ http_send_response_start */
50 : #define http_send_response_start(b, cl) _http_send_response_start((b), (cl) TSRMLS_CC)
51 : static inline void _http_send_response_start(void **buffer, size_t content_length TSRMLS_DC)
52 26 : {
53 : int encoding;
54 :
55 26 : if ((encoding = http_encoding_response_start(content_length, 0))) {
56 : #ifdef HTTP_HAVE_ZLIB
57 2 : *((http_encoding_stream **) buffer) = http_encoding_deflate_stream_init(NULL,
58 : (encoding == HTTP_ENCODING_GZIP) ?
59 : HTTP_DEFLATE_TYPE_GZIP : HTTP_DEFLATE_TYPE_ZLIB);
60 : #endif
61 : }
62 : /* flush headers */
63 26 : sapi_flush(TSRMLS_C);
64 26 : }
65 : /* }}} */
66 :
67 : /* {{{ http_send_response_data_plain */
68 : #define http_send_response_data_plain(b, d, dl) _http_send_response_data_plain((b), (d), (dl) TSRMLS_CC)
69 : static inline void _http_send_response_data_plain(void **buffer, const char *data, size_t data_len TSRMLS_DC)
70 73 : {
71 75 : if (HTTP_G->send.deflate.response && HTTP_G->send.deflate.encoding) {
72 : #ifdef HTTP_HAVE_ZLIB
73 : char *encoded;
74 : size_t encoded_len;
75 2 : http_encoding_stream *s = *((http_encoding_stream **) buffer);
76 :
77 2 : http_encoding_deflate_stream_update(s, data, data_len, &encoded, &encoded_len);
78 2 : if (HTTP_G->send.buffer_size) {
79 0 : phpstr_chunked_output((phpstr **) &s->storage, encoded, encoded_len, HTTP_G->send.buffer_size, _http_flush, NULL TSRMLS_CC);
80 : } else {
81 2 : http_flush(encoded, encoded_len);
82 : }
83 2 : efree(encoded);
84 : #else
85 : http_error(HE_ERROR, HTTP_E_RESPONSE, "Attempt to send GZIP response despite being able to do so; please report this bug");
86 : #endif
87 71 : } else if (HTTP_G->send.buffer_size) {
88 20 : phpstr_chunked_output((phpstr **) buffer, data, data_len, HTTP_G->send.buffer_size, _http_flush, NULL TSRMLS_CC);
89 : } else {
90 51 : http_flush(data, data_len);
91 : }
92 73 : }
93 : /* }}} */
94 :
95 : /* {{{ http_send_response_data_fetch */
96 : #define http_send_response_data_fetch(b, d, l, m, s, e) _http_send_response_data_fetch((b), (d), (l), (m), (s), (e) TSRMLS_CC)
97 : static inline void _http_send_response_data_fetch(void **buffer, const void *data, size_t data_len, http_send_mode mode, size_t begin, size_t end TSRMLS_DC)
98 32 : {
99 32 : long bsz, got, len = end - begin;
100 :
101 32 : if (!(bsz = HTTP_G->send.buffer_size)) {
102 31 : bsz = HTTP_SENDBUF_SIZE;
103 : }
104 :
105 32 : switch (mode) {
106 : case SEND_RSRC: {
107 15 : php_stream *s = (php_stream *) data;
108 15 : if (SUCCESS == php_stream_seek(s, begin, SEEK_SET)) {
109 15 : char *buf = emalloc(bsz);
110 :
111 45 : while (len > 0) {
112 15 : got = php_stream_read(s, buf, MIN(len, bsz));
113 15 : http_send_response_data_plain(buffer, buf, got);
114 15 : len -= got;
115 : }
116 :
117 15 : efree(buf);
118 : }
119 15 : break;
120 : }
121 : case SEND_DATA: {
122 17 : const char *buf = ((const char *) data) + begin;
123 70 : while (len > 0) {
124 36 : got = MIN(len, bsz);
125 36 : http_send_response_data_plain(buffer, buf, got);
126 36 : len -= got;
127 36 : buf += got;
128 : }
129 : break;
130 : }
131 : EMPTY_SWITCH_DEFAULT_CASE();
132 : }
133 32 : }
134 : /* }}} */
135 :
136 : /* {{{ http_send_response_finish */
137 : #define http_send_response_finish(b) _http_send_response_finish((b) TSRMLS_CC)
138 : static inline void _http_send_response_finish(void **buffer TSRMLS_DC)
139 26 : {
140 28 : if (HTTP_G->send.deflate.response && HTTP_G->send.deflate.encoding) {
141 : #ifdef HTTP_HAVE_ZLIB
142 2 : char *encoded = NULL;
143 2 : size_t encoded_len = 0;
144 2 : http_encoding_stream *s = *((http_encoding_stream **) buffer);
145 :
146 2 : http_encoding_deflate_stream_finish(s, &encoded, &encoded_len);
147 2 : if (HTTP_G->send.buffer_size) {
148 0 : phpstr_chunked_output((phpstr **) &s->storage, encoded, encoded_len, 0, _http_flush, NULL TSRMLS_CC);
149 : } else {
150 2 : http_flush(encoded, encoded_len);
151 : }
152 2 : http_encoding_deflate_stream_free(&s);
153 2 : STR_FREE(encoded);
154 : #else
155 : http_error(HE_ERROR, HTTP_E_RESPONSE, "Attempt to send GZIP response despite being able to do so; please report this bug");
156 : #endif
157 24 : } else if (HTTP_G->send.buffer_size) {
158 1 : phpstr_chunked_output((phpstr **) buffer, NULL, 0, 0, _http_flush, NULL TSRMLS_CC);
159 : }
160 26 : }
161 : /* }}} */
162 :
163 : /* {{{ */
164 : PHP_MINIT_FUNCTION(http_send)
165 220 : {
166 220 : HTTP_LONG_CONSTANT("HTTP_REDIRECT", HTTP_REDIRECT);
167 220 : HTTP_LONG_CONSTANT("HTTP_REDIRECT_PERM", HTTP_REDIRECT_PERM);
168 220 : HTTP_LONG_CONSTANT("HTTP_REDIRECT_FOUND", HTTP_REDIRECT_FOUND);
169 220 : HTTP_LONG_CONSTANT("HTTP_REDIRECT_POST", HTTP_REDIRECT_POST);
170 220 : HTTP_LONG_CONSTANT("HTTP_REDIRECT_PROXY", HTTP_REDIRECT_PROXY);
171 220 : HTTP_LONG_CONSTANT("HTTP_REDIRECT_TEMP", HTTP_REDIRECT_TEMP);
172 :
173 220 : return SUCCESS;
174 : }
175 : /* }}} */
176 :
177 : /* {{{ http_find_header */
178 : typedef struct {
179 : const char *h;
180 : size_t l;
181 : } http_response_header_t;
182 :
183 : static int http_find_header(void *data, void *arg)
184 3 : {
185 3 : http_response_header_t *h = arg;
186 3 : sapi_header_struct *s = data;
187 :
188 3 : return (!strncasecmp(s->header, h->h, h->l)) && s->header[h->l] == ':';
189 : }
190 : /* }}} */
191 :
192 : /* {{{ void http_hide_header(char *) */
193 : PHP_HTTP_API void _http_hide_header_ex(const char *name, size_t name_len TSRMLS_DC)
194 1 : {
195 1 : http_response_header_t h = {name, name_len};
196 1 : zend_llist_del_element(&SG(sapi_headers).headers, (void *) &h, http_find_header);
197 1 : }
198 : /* }}} */
199 :
200 : /* {{{ void http_send_header_zval(char*, zval **, zend_bool) */
201 : PHP_HTTP_API void _http_send_header_zval_ex(const char *name, size_t name_len, zval **val, zend_bool replace TSRMLS_DC)
202 0 : {
203 0 : if (!val || !*val || Z_TYPE_PP(val) == IS_NULL || (Z_TYPE_PP(val) == IS_STRING && !Z_STRLEN_PP(val))) {
204 0 : http_hide_header_ex(name, name_len);
205 0 : } else if (Z_TYPE_PP(val) == IS_ARRAY || Z_TYPE_PP(val) == IS_OBJECT) {
206 0 : zend_bool first = replace;
207 : zval **data;
208 : HashPosition pos;
209 :
210 0 : FOREACH_HASH_VAL(pos, HASH_OF(*val), data) {
211 0 : zval *orig = *data;
212 :
213 0 : convert_to_string_ex(data);
214 0 : http_send_header_ex(name, name_len, Z_STRVAL_PP(data), Z_STRLEN_PP(data), first, NULL);
215 0 : if (orig != *data) {
216 0 : zval_ptr_dtor(data);
217 : }
218 0 : first = 0;
219 : }
220 : } else {
221 0 : zval *orig = *val;
222 :
223 0 : convert_to_string_ex(val);
224 0 : http_send_header_ex(name, name_len, Z_STRVAL_PP(val), Z_STRLEN_PP(val), replace, NULL);
225 0 : if (orig != *val) {
226 0 : zval_ptr_dtor(val);
227 : }
228 : }
229 0 : }
230 : /* }}} */
231 :
232 : /* {{{ STATUS http_send_header(char *, char *, zend_bool) */
233 : PHP_HTTP_API STATUS _http_send_header_ex(const char *name, size_t name_len, const char *value, size_t value_len, zend_bool replace, char **sent_header TSRMLS_DC)
234 41 : {
235 : STATUS ret;
236 :
237 82 : if (value && value_len) {
238 41 : size_t header_len = sizeof(": ") + name_len + value_len + 1;
239 41 : char *header = emalloc(header_len + 1);
240 :
241 41 : header[header_len] = '\0';
242 41 : header_len = snprintf(header, header_len, "%s: %s", name, value);
243 41 : ret = http_send_header_string_ex(header, header_len, replace);
244 41 : if (sent_header) {
245 2 : *sent_header = header;
246 : } else {
247 39 : efree(header);
248 : }
249 : } else {
250 0 : http_hide_header_ex(name, name_len);
251 0 : ret = SUCCESS;
252 : }
253 41 : return ret;
254 : }
255 : /* }}} */
256 :
257 : /* {{{ STATUS http_send_status_header(int, char *) */
258 : PHP_HTTP_API STATUS _http_send_status_header_ex(int status, const char *header, size_t header_len, zend_bool replace TSRMLS_DC)
259 130 : {
260 : STATUS ret;
261 130 : sapi_header_line h = {(char *) header, header_len, status};
262 130 : if (SUCCESS != (ret = sapi_header_op(replace ? SAPI_HEADER_REPLACE : SAPI_HEADER_ADD, &h TSRMLS_CC))) {
263 0 : http_error_ex(HE_WARNING, HTTP_E_HEADER, "Could not send header: %s (%d)", header, status);
264 : }
265 130 : return ret;
266 : }
267 : /* }}} */
268 :
269 : /* {{{ STATUS http_send_last_modified(int) */
270 : PHP_HTTP_API STATUS _http_send_last_modified_ex(time_t t, char **sent_header TSRMLS_DC)
271 5 : {
272 : STATUS ret;
273 5 : char *date = http_date(t);
274 :
275 5 : if (!date) {
276 0 : return FAILURE;
277 : }
278 :
279 5 : ret = http_send_header_ex("Last-Modified", lenof("Last-Modified"), date, strlen(date), 1, sent_header);
280 5 : efree(date);
281 :
282 : /* remember */
283 5 : HTTP_G->send.last_modified = t;
284 :
285 5 : return ret;
286 : }
287 : /* }}} */
288 :
289 : /* {{{ STATUS http_send_etag(char *, size_t) */
290 : PHP_HTTP_API STATUS _http_send_etag_ex(const char *etag, size_t etag_len, char **sent_header TSRMLS_DC)
291 16 : {
292 : STATUS status;
293 : char *etag_header;
294 : size_t etag_header_len;
295 :
296 16 : if (!etag_len){
297 0 : http_error_ex(HE_WARNING, HTTP_E_HEADER, "Attempt to send empty ETag (previous: %s)\n", HTTP_G->send.unquoted_etag);
298 0 : return FAILURE;
299 : }
300 :
301 16 : etag_header_len = spprintf(&etag_header, 0, "ETag: \"%s\"", etag);
302 16 : status = http_send_header_string_ex(etag_header, etag_header_len, 1);
303 :
304 : /* remember */
305 16 : STR_SET(HTTP_G->send.unquoted_etag, estrndup(etag, etag_len));
306 :
307 16 : if (sent_header) {
308 13 : *sent_header = etag_header;
309 : } else {
310 3 : efree(etag_header);
311 : }
312 :
313 16 : return status;
314 : }
315 : /* }}} */
316 :
317 : /* {{{ STATUS http_send_content_type(char *, size_t) */
318 : PHP_HTTP_API STATUS _http_send_content_type(const char *content_type, size_t ct_len TSRMLS_DC)
319 16 : {
320 16 : HTTP_CHECK_CONTENT_TYPE(content_type, return FAILURE);
321 :
322 : /* remember for multiple ranges */
323 16 : STR_FREE(HTTP_G->send.content_type);
324 16 : HTTP_G->send.content_type = estrndup(content_type, ct_len);
325 :
326 16 : return http_send_header_ex("Content-Type", lenof("Content-Type"), content_type, ct_len, 1, NULL);
327 : }
328 : /* }}} */
329 :
330 : /* {{{ STATUS http_send_content_disposition(char *, size_t, zend_bool) */
331 : PHP_HTTP_API STATUS _http_send_content_disposition(const char *filename, size_t f_len, zend_bool send_inline TSRMLS_DC)
332 0 : {
333 : STATUS status;
334 : char *cd_header;
335 :
336 0 : if (send_inline) {
337 0 : cd_header = ecalloc(1, sizeof("Content-Disposition: inline; filename=\"\"") + f_len);
338 0 : sprintf(cd_header, "Content-Disposition: inline; filename=\"%s\"", filename);
339 : } else {
340 0 : cd_header = ecalloc(1, sizeof("Content-Disposition: attachment; filename=\"\"") + f_len);
341 0 : sprintf(cd_header, "Content-Disposition: attachment; filename=\"%s\"", filename);
342 : }
343 :
344 0 : status = http_send_header_string(cd_header);
345 0 : efree(cd_header);
346 0 : return status;
347 : }
348 : /* }}} */
349 :
350 : /* {{{ STATUS http_send(void *, size_t, http_send_mode) */
351 : PHP_HTTP_API STATUS _http_send_ex(const void *data_ptr, size_t data_size, http_send_mode data_mode, zend_bool no_cache TSRMLS_DC)
352 30 : {
353 30 : void *s = NULL;
354 : HashTable ranges;
355 : http_range_status range_status;
356 :
357 30 : if (!data_ptr) {
358 0 : return FAILURE;
359 : }
360 30 : if (!data_size) {
361 0 : return SUCCESS;
362 : }
363 :
364 : /* enable partial dl and resume */
365 30 : http_send_header_string("Accept-Ranges: bytes");
366 :
367 30 : zend_hash_init(&ranges, 0, NULL, ZVAL_PTR_DTOR, 0);
368 30 : range_status = http_get_request_ranges(&ranges, data_size);
369 :
370 30 : switch (range_status) {
371 : case RANGE_ERR:
372 : {
373 2 : zend_hash_destroy(&ranges);
374 2 : http_send_status(416);
375 2 : return FAILURE;
376 : }
377 : case RANGE_OK:
378 : {
379 : /* Range Request - only send ranges if entity hasn't changed */
380 16 : if ( http_got_server_var("HTTP_IF_RANGE") &&
381 : !http_match_etag("HTTP_IF_RANGE", HTTP_G->send.unquoted_etag) &&
382 : !http_match_last_modified("HTTP_IF_RANGE", HTTP_G->send.last_modified)) {
383 : /* fallthrough to send full entity with 200 Ok */
384 1 : no_cache = 1;
385 14 : } else if ( !http_match_etag_ex("HTTP_IF_MATCH", HTTP_G->send.unquoted_etag, 0) ||
386 : !http_match_last_modified_ex("HTTP_IF_UNMODIFIED_SINCE", HTTP_G->send.last_modified, 0) ||
387 : !http_match_last_modified_ex("HTTP_UNLESS_MODIFIED_SINCE", HTTP_G->send.last_modified, 0)) {
388 : /* 412 Precondition failed */
389 1 : zend_hash_destroy(&ranges);
390 1 : http_send_status(412);
391 1 : return FAILURE;
392 13 : } else if (zend_hash_num_elements(&ranges) == 1) {
393 : /* single range */
394 : zval **range, **begin, **end;
395 :
396 9 : if ( SUCCESS != zend_hash_index_find(&ranges, 0, (void *) &range) ||
397 : SUCCESS != zend_hash_index_find(Z_ARRVAL_PP(range), 0, (void *) &begin) ||
398 : SUCCESS != zend_hash_index_find(Z_ARRVAL_PP(range), 1, (void *) &end)) {
399 : /* this should never happen */
400 0 : zend_hash_destroy(&ranges);
401 0 : http_send_status(500);
402 0 : return FAILURE;
403 : } else {
404 : char range_header_str[256];
405 : size_t range_header_len;
406 :
407 9 : range_header_len = snprintf(range_header_str, sizeof(range_header_str), "Content-Range: bytes %ld-%ld/%zu", Z_LVAL_PP(begin), Z_LVAL_PP(end), data_size);
408 9 : http_send_status_header_ex(206, range_header_str, range_header_len, 1);
409 9 : http_send_response_start(&s, Z_LVAL_PP(end)-Z_LVAL_PP(begin)+1);
410 9 : http_send_response_data_fetch(&s, data_ptr, data_size, data_mode, Z_LVAL_PP(begin), Z_LVAL_PP(end) + 1);
411 9 : http_send_response_finish(&s);
412 9 : zend_hash_destroy(&ranges);
413 9 : return SUCCESS;
414 : }
415 : } else {
416 : /* multi range */
417 : HashPosition pos;
418 : zval **range, **begin, **end;
419 4 : const char *content_type = HTTP_G->send.content_type;
420 : char boundary_str[32], range_header_str[256];
421 : size_t boundary_len, range_header_len;
422 :
423 4 : boundary_len = snprintf(boundary_str, sizeof(boundary_str), "%lu%0.9f", (ulong) HTTP_G->request.time, (float) php_combined_lcg(TSRMLS_C));
424 4 : range_header_len = snprintf(range_header_str, sizeof(range_header_str), "Content-Type: multipart/byteranges; boundary=%s", boundary_str);
425 :
426 4 : http_send_status_header_ex(206, range_header_str, range_header_len, 1);
427 4 : http_send_response_start(&s, 0);
428 :
429 4 : if (!content_type) {
430 0 : content_type = "application/x-octetstream";
431 : }
432 :
433 14 : FOREACH_HASH_VAL(pos, &ranges, range) {
434 10 : if ( SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(range), 0, (void *) &begin) &&
435 : SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(range), 1, (void *) &end)) {
436 : char preface_str[512];
437 : size_t preface_len;
438 :
439 : #define HTTP_RANGE_PREFACE \
440 : HTTP_CRLF "--%s" \
441 : HTTP_CRLF "Content-Type: %s" \
442 : HTTP_CRLF "Content-Range: bytes %ld-%ld/%zu" \
443 : HTTP_CRLF HTTP_CRLF
444 :
445 10 : preface_len = snprintf(preface_str, sizeof(preface_str), HTTP_RANGE_PREFACE, boundary_str, content_type, Z_LVAL_PP(begin), Z_LVAL_PP(end), data_size);
446 10 : http_send_response_data_plain(&s, preface_str, preface_len);
447 10 : http_send_response_data_fetch(&s, data_ptr, data_size, data_mode, Z_LVAL_PP(begin), Z_LVAL_PP(end) + 1);
448 : }
449 : }
450 :
451 4 : http_send_response_data_plain(&s, HTTP_CRLF "--", lenof(HTTP_CRLF "--"));
452 4 : http_send_response_data_plain(&s, boundary_str, boundary_len);
453 4 : http_send_response_data_plain(&s, "--", lenof("--"));
454 :
455 4 : http_send_response_finish(&s);
456 4 : zend_hash_destroy(&ranges);
457 4 : return SUCCESS;
458 : }
459 : }
460 : case RANGE_NO:
461 : {
462 14 : zend_hash_destroy(&ranges);
463 :
464 : /* send 304 Not Modified if etag matches - DON'T return on ETag generation failure */
465 14 : if (!no_cache && (http_interrupt_ob_etaghandler() || (HTTP_G->send.unquoted_etag != NULL))) {
466 7 : char *etag = NULL;
467 :
468 7 : if (HTTP_G->send.unquoted_etag) {
469 3 : etag = estrdup(HTTP_G->send.unquoted_etag);
470 : }
471 :
472 7 : if (etag || (etag = http_etag(data_ptr, data_size, data_mode))) {
473 7 : char *sent_header = NULL;
474 :
475 7 : http_send_etag_ex(etag, strlen(etag), &sent_header);
476 7 : if (http_match_etag("HTTP_IF_NONE_MATCH", etag)) {
477 1 : return http_exit_ex(304, sent_header, NULL, 0);
478 : } else {
479 6 : STR_FREE(sent_header);
480 : /* no caching for Last-Modified if ETags really don't match */
481 6 : no_cache = http_got_server_var("HTTP_IF_NONE_MATCH");
482 : }
483 6 : efree(etag);
484 : }
485 : }
486 :
487 : /* send 304 Not Modified if last modified matches */
488 13 : if (!no_cache && http_match_last_modified("HTTP_IF_MODIFIED_SINCE", HTTP_G->send.last_modified)) {
489 0 : char *sent_header = NULL;
490 0 : http_send_last_modified_ex(HTTP_G->send.last_modified, &sent_header);
491 0 : return http_exit_ex(304, sent_header, NULL, 0);
492 : }
493 :
494 : /* send full response */
495 13 : http_send_response_start(&s, data_size);
496 13 : http_send_response_data_fetch(&s, data_ptr, data_size, data_mode, 0, data_size);
497 13 : http_send_response_finish(&s);
498 13 : return SUCCESS;
499 : }
500 : }
501 0 : return FAILURE;
502 : }
503 : /* }}} */
504 :
505 : /* {{{ STATUS http_send_stream(php_stream *) */
506 : PHP_HTTP_API STATUS _http_send_stream_ex(php_stream *file, zend_bool close_stream, zend_bool no_cache TSRMLS_DC)
507 15 : {
508 : STATUS status;
509 : php_stream_statbuf ssb;
510 : int orig_flags;
511 :
512 15 : if ((!file) || php_stream_stat(file, &ssb)) {
513 1 : char *defct = sapi_get_default_content_type(TSRMLS_C);
514 :
515 1 : http_hide_header("Content-Disposition");
516 1 : http_send_content_type(defct, strlen(defct));
517 1 : http_error(HE_WARNING, HTTP_E_RESPONSE, "File not found; stat failed");
518 1 : STR_FREE(defct);
519 :
520 1 : if (HTTP_G->send.not_found_404) {
521 1 : http_exit_ex(404, NULL, estrdup("File not found\n"), 0);
522 : }
523 0 : return FAILURE;
524 : }
525 :
526 14 : orig_flags = file->flags;
527 14 : file->flags |= PHP_STREAM_FLAG_NO_BUFFER;
528 14 : status = http_send_ex(file, ssb.sb.st_size, SEND_RSRC, no_cache);
529 14 : file->flags = orig_flags;
530 :
531 14 : if (close_stream) {
532 14 : php_stream_close(file);
533 : }
534 :
535 14 : return status;
536 : }
537 : /* }}} */
538 :
539 : /* {{{ char *http_guess_content_type(char *magic_file, long magic_mode, void *data, size_t size, http_send_mode mode) */
540 : PHP_HTTP_API char *_http_guess_content_type(const char *magicfile, long magicmode, void *data_ptr, size_t data_len, http_send_mode data_mode TSRMLS_DC)
541 0 : {
542 0 : char *ct = NULL;
543 :
544 : #ifdef HTTP_HAVE_MAGIC
545 : struct magic_set *magic = NULL;
546 :
547 : HTTP_CHECK_OPEN_BASEDIR(magicfile, return NULL);
548 :
549 : if (!data_ptr) {
550 : http_error(HE_WARNING, HTTP_E_INVALID_PARAM, "Supplied payload is empty");
551 : } else if (!(magic = magic_open(magicmode &~ MAGIC_MIME))) {
552 : http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Invalid magic mode: %ld", magicmode);
553 : } else if (-1 == magic_load(magic, magicfile)) {
554 : http_error_ex(HE_WARNING, HTTP_E_RUNTIME, "Failed to load magic database '%s' (%s)", magicfile, magic_error(magic));
555 : } else {
556 : const char *ctype = NULL;
557 :
558 : magic_setflags(magic, magicmode);
559 :
560 : switch (data_mode) {
561 : case SEND_RSRC:
562 : {
563 : char *buffer;
564 : size_t b_len;
565 :
566 : b_len = php_stream_copy_to_mem(data_ptr, &buffer, 65536, 0);
567 : ctype = magic_buffer(magic, buffer, b_len);
568 : efree(buffer);
569 : break;
570 : }
571 :
572 : case SEND_DATA:
573 : ctype = magic_buffer(magic, data_ptr, data_len);
574 : break;
575 :
576 : default:
577 : HTTP_CHECK_OPEN_BASEDIR(data_ptr, magic_close(magic); return NULL);
578 : ctype = magic_file(magic, data_ptr);
579 : break;
580 : }
581 :
582 : if (ctype) {
583 : ct = estrdup(ctype);
584 : } else {
585 : http_error_ex(HE_WARNING, HTTP_E_RUNTIME, "Failed to guess Content-Type: %s", magic_error(magic));
586 : }
587 : }
588 : if (magic) {
589 : magic_close(magic);
590 : }
591 : #else
592 0 : http_error(HE_WARNING, HTTP_E_RUNTIME, "Cannot guess Content-Type; libmagic not available");
593 : #endif
594 :
595 0 : return ct;
596 : }
597 : /* }}} */
598 :
599 : /*
600 : * Local variables:
601 : * tab-width: 4
602 : * c-basic-offset: 4
603 : * End:
604 : * vim600: sw=4 ts=4 fdm=marker
605 : * vim<600: sw=4 ts=4
606 : */
607 :
|