- ditch http_split_response()
[m6w6/ext-http] / http_cache_api.c
1 /*
2 +----------------------------------------------------------------------+
3 | PECL :: http |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.0 of the PHP license, that |
6 | is bundled with this package in the file LICENSE, and is available |
7 | through the world-wide-web at http://www.php.net/license/3_0.txt. |
8 | If you did not receive a copy of the PHP license and are unable to |
9 | obtain it through the world-wide-web, please send a note to |
10 | license@php.net so we can mail you a copy immediately. |
11 +----------------------------------------------------------------------+
12 | Copyright (c) 2004-2005 Michael Wallner <mike@php.net> |
13 +----------------------------------------------------------------------+
14 */
15
16 /* $Id$ */
17
18 #ifdef HAVE_CONFIG_H
19 # include "config.h"
20 #endif
21 #include "php.h"
22
23 #include "SAPI.h"
24 #include "php_streams.h"
25 #include "php_output.h"
26 #include "ext/standard/md5.h"
27
28 #include "php_http.h"
29 #include "php_http_std_defs.h"
30 #include "php_http_api.h"
31 #include "php_http_cache_api.h"
32 #include "php_http_send_api.h"
33 #include "php_http_date_api.h"
34
35 ZEND_EXTERN_MODULE_GLOBALS(http);
36
37 /* {{{ char *http_etag(void *, size_t, http_send_mode) */
38 PHP_HTTP_API char *_http_etag(const void *data_ptr, size_t data_len, http_send_mode data_mode TSRMLS_DC)
39 {
40 php_stream_statbuf ssb;
41 char ssb_buf[128] = {0};
42 unsigned char digest[16];
43 PHP_MD5_CTX ctx;
44 char *new_etag = ecalloc(1, 33);
45
46 PHP_MD5Init(&ctx);
47
48 switch (data_mode)
49 {
50 case SEND_DATA:
51 PHP_MD5Update(&ctx, data_ptr, data_len);
52 break;
53
54 case SEND_RSRC:
55 {
56 if (php_stream_stat((php_stream *) data_ptr, &ssb)) {
57 efree(new_etag);
58 return NULL;
59 }
60
61 snprintf(ssb_buf, 127, "%ld=%ld=%ld", ssb.sb.st_mtime, ssb.sb.st_ino, ssb.sb.st_size);
62 PHP_MD5Update(&ctx, ssb_buf, strlen(ssb_buf));
63 }
64 break;
65
66 default:
67 {
68 if (php_stream_stat_path((char *) data_ptr, &ssb)) {
69 efree(new_etag);
70 return NULL;
71 }
72
73 snprintf(ssb_buf, 127, "%ld=%ld=%ld", ssb.sb.st_mtime, ssb.sb.st_ino, ssb.sb.st_size);
74 PHP_MD5Update(&ctx, ssb_buf, strlen(ssb_buf));
75 }
76 break;
77 }
78
79 PHP_MD5Final(digest, &ctx);
80 make_digest(new_etag, digest);
81
82 return new_etag;
83 }
84 /* }}} */
85
86 /* {{{ time_t http_last_modified(void *, http_send_mode) */
87 PHP_HTTP_API time_t _http_last_modified(const void *data_ptr, http_send_mode data_mode TSRMLS_DC)
88 {
89 php_stream_statbuf ssb;
90
91 switch (data_mode)
92 {
93 case SEND_DATA: return time(NULL);
94 case SEND_RSRC: return php_stream_stat((php_stream *) data_ptr, &ssb) ? 0 : ssb.sb.st_mtime;
95 default: return php_stream_stat_path((char *) data_ptr, &ssb) ? 0 : ssb.sb.st_mtime;
96 }
97 }
98 /* }}} */
99
100 /* {{{ zend_bool http_match_last_modified(char *, time_t) */
101 PHP_HTTP_API zend_bool _http_match_last_modified_ex(const char *entry, time_t t, zend_bool enforce_presence TSRMLS_DC)
102 {
103 zend_bool retval;
104 zval *zmodified;
105 char *modified, *chr_ptr;
106
107 HTTP_GSC(zmodified, entry, !enforce_presence);
108
109 modified = estrndup(Z_STRVAL_P(zmodified), Z_STRLEN_P(zmodified));
110 if (chr_ptr = strrchr(modified, ';')) {
111 chr_ptr = 0;
112 }
113 retval = (t <= http_parse_date(modified));
114 efree(modified);
115 return retval;
116 }
117 /* }}} */
118
119 /* {{{ zend_bool http_match_etag(char *, char *) */
120 PHP_HTTP_API zend_bool _http_match_etag_ex(const char *entry, const char *etag, zend_bool enforce_presence TSRMLS_DC)
121 {
122 zval *zetag;
123 char *quoted_etag;
124 zend_bool result;
125
126 HTTP_GSC(zetag, entry, !enforce_presence);
127
128 if (NULL != strchr(Z_STRVAL_P(zetag), '*')) {
129 return 1;
130 }
131
132 quoted_etag = (char *) emalloc(strlen(etag) + 3);
133 sprintf(quoted_etag, "\"%s\"", etag);
134
135 if (!strchr(Z_STRVAL_P(zetag), ',')) {
136 result = !strcmp(Z_STRVAL_P(zetag), quoted_etag);
137 } else {
138 result = (NULL != strstr(Z_STRVAL_P(zetag), quoted_etag));
139 }
140 efree(quoted_etag);
141 return result;
142 }
143 /* }}} */
144
145 /* {{{ STATUS http_cache_last_modified(time_t, time_t, char *, size_t) */
146 PHP_HTTP_API STATUS _http_cache_last_modified(time_t last_modified,
147 time_t send_modified, const char *cache_control, size_t cc_len TSRMLS_DC)
148 {
149 char *sent_header = NULL;
150
151 if (cc_len && (SUCCESS != http_send_cache_control(cache_control, cc_len))) {
152 return FAILURE;
153 }
154
155 if (SUCCESS != http_send_last_modified_ex(send_modified, &sent_header)) {
156 return FAILURE;
157 }
158
159 if (http_match_last_modified("HTTP_IF_MODIFIED_SINCE", last_modified)) {
160 http_exit_ex(304, sent_header, NULL, 0);
161 } else {
162 STR_FREE(sent_header);
163 }
164
165 return SUCCESS;
166 }
167 /* }}} */
168
169 /* {{{ STATUS http_cache_etag(char *, size_t, char *, size_t) */
170 PHP_HTTP_API STATUS _http_cache_etag(const char *etag, size_t etag_len,
171 const char *cache_control, size_t cc_len TSRMLS_DC)
172 {
173 char *sent_header = NULL;
174
175 if (cc_len && (SUCCESS != http_send_cache_control(cache_control, cc_len))) {
176 return FAILURE;
177 }
178
179 if (etag_len) {
180 if (SUCCESS != http_send_etag_ex(etag, etag_len, &sent_header)) {
181 return FAILURE;
182 }
183 if (http_match_etag("HTTP_IF_NONE_MATCH", etag)) {
184 http_exit_ex(304, sent_header, NULL, 0);
185 } else {
186 STR_FREE(sent_header);
187 }
188 return SUCCESS;
189 }
190
191 /* if no etag is given and we didn't already start ob_etaghandler -- start it */
192 if (HTTP_G(etag).started) {
193 return SUCCESS;
194 }
195
196 if (HTTP_G(etag).started = (SUCCESS == php_start_ob_buffer_named("ob_etaghandler", HTTP_SENDBUF_SIZE, 1 TSRMLS_CC))) {
197 return SUCCESS;
198 } else {
199 return FAILURE;
200 }
201 }
202 /* }}} */
203
204 /* {{{ void http_ob_etaghandler(char *, uint, char **, uint *, int) */
205 PHP_HTTP_API void _http_ob_etaghandler(char *output, uint output_len,
206 char **handled_output, uint *handled_output_len, int mode TSRMLS_DC)
207 {
208 char etag[33] = { 0 };
209 unsigned char digest[16];
210
211 if (mode & PHP_OUTPUT_HANDLER_START) {
212 HTTP_G(etag).started = 1;
213 PHP_MD5Init(&HTTP_G(etag).md5ctx);
214 }
215
216 PHP_MD5Update(&HTTP_G(etag).md5ctx, output, output_len);
217
218 if (mode & PHP_OUTPUT_HANDLER_END) {
219 PHP_MD5Final(digest, &HTTP_G(etag).md5ctx);
220
221 /* just do that if desired */
222 if (HTTP_G(etag).started) {
223 char *sent_header = NULL;
224
225 make_digest(etag, digest);
226 http_send_cache_control(HTTP_DEFAULT_CACHECONTROL, lenof(HTTP_DEFAULT_CACHECONTROL));
227 http_send_etag_ex(etag, 32, &sent_header);
228
229 if (http_match_etag("HTTP_IF_NONE_MATCH", etag)) {
230 http_exit_ex(304, sent_header, NULL, 0);
231 } else {
232 STR_FREE(sent_header);
233 }
234 }
235 }
236
237 *handled_output_len = output_len;
238 *handled_output = estrndup(output, output_len);
239 }
240 /* }}} */
241
242 /*
243 * Local variables:
244 * tab-width: 4
245 * c-basic-offset: 4
246 * End:
247 * vim600: sw=4 ts=4 fdm=marker
248 * vim<600: sw=4 ts=4
249 */
250