7fc25cf845773623d67cb00a96f054ad6739fe43
[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
22 #include "php.h"
23 #include "php_streams.h"
24 #include "php_output.h"
25 #include "ext/standard/md5.h"
26
27 #include "php_http.h"
28 #include "php_http_std_defs.h"
29 #include "php_http_api.h"
30 #include "php_http_cache_api.h"
31 #include "php_http_send_api.h"
32 #include "php_http_date_api.h"
33
34 ZEND_EXTERN_MODULE_GLOBALS(http);
35
36 /* {{{ char *http_etag(void *, size_t, http_send_mode) */
37 PHP_HTTP_API char *_http_etag(const void *data_ptr, size_t data_len, http_send_mode data_mode TSRMLS_DC)
38 {
39 char ssb_buf[128] = {0};
40 unsigned char digest[16];
41 PHP_MD5_CTX ctx;
42 char *new_etag = ecalloc(1, 33);
43
44 PHP_MD5Init(&ctx);
45
46 switch (data_mode)
47 {
48 case SEND_DATA:
49 PHP_MD5Update(&ctx, data_ptr, data_len);
50 break;
51
52 case SEND_RSRC:
53 if (!HTTP_G(ssb).sb.st_ino) {
54 if (php_stream_stat((php_stream *) data_ptr, &HTTP_G(ssb))) {
55 return NULL;
56 }
57 }
58 snprintf(ssb_buf, 127, "%ld=%ld=%ld",
59 HTTP_G(ssb).sb.st_mtime,
60 HTTP_G(ssb).sb.st_ino,
61 HTTP_G(ssb).sb.st_size
62 );
63 PHP_MD5Update(&ctx, ssb_buf, strlen(ssb_buf));
64 break;
65
66 default:
67 efree(new_etag);
68 return NULL;
69 break;
70 }
71
72 PHP_MD5Final(digest, &ctx);
73 make_digest(new_etag, digest);
74
75 return new_etag;
76 }
77 /* }}} */
78
79 /* {{{ time_t http_last_modified(void *, http_send_mode) */
80 PHP_HTTP_API time_t _http_last_modified(const void *data_ptr, http_send_mode data_mode TSRMLS_DC)
81 {
82 switch (data_mode)
83 {
84 case SEND_DATA:
85 {
86 return time(NULL);
87 }
88
89 case SEND_RSRC:
90 {
91 php_stream_stat((php_stream *) data_ptr, &HTTP_G(ssb));
92 return HTTP_G(ssb).sb.st_mtime;
93 }
94
95 default:
96 {
97 php_stream_stat_path(Z_STRVAL_P((zval *) data_ptr), &HTTP_G(ssb));
98 return HTTP_G(ssb).sb.st_mtime;
99 }
100 }
101 }
102 /* }}} */
103
104 /* {{{ zend_bool http_match_last_modified(char *, time_t) */
105 PHP_HTTP_API zend_bool _http_match_last_modified_ex(const char *entry, time_t t, zend_bool enforce_presence TSRMLS_DC)
106 {
107 zend_bool retval;
108 zval *zmodified;
109 char *modified, *chr_ptr;
110
111 HTTP_GSC(zmodified, entry, !enforce_presence);
112
113 modified = estrndup(Z_STRVAL_P(zmodified), Z_STRLEN_P(zmodified));
114 if (chr_ptr = strrchr(modified, ';')) {
115 chr_ptr = 0;
116 }
117 retval = (t <= http_parse_date(modified));
118 efree(modified);
119 return retval;
120 }
121 /* }}} */
122
123 /* {{{ zend_bool http_match_etag(char *, char *) */
124 PHP_HTTP_API zend_bool _http_match_etag_ex(const char *entry, const char *etag, zend_bool enforce_presence TSRMLS_DC)
125 {
126 zval *zetag;
127 char *quoted_etag;
128 zend_bool result;
129
130 HTTP_GSC(zetag, entry, !enforce_presence);
131
132 if (NULL != strchr(Z_STRVAL_P(zetag), '*')) {
133 return 1;
134 }
135
136 quoted_etag = (char *) emalloc(strlen(etag) + 3);
137 sprintf(quoted_etag, "\"%s\"", etag);
138
139 if (!strchr(Z_STRVAL_P(zetag), ',')) {
140 result = !strcmp(Z_STRVAL_P(zetag), quoted_etag);
141 } else {
142 result = (NULL != strstr(Z_STRVAL_P(zetag), quoted_etag));
143 }
144 efree(quoted_etag);
145 return result;
146 }
147 /* }}} */
148
149 /* {{{ STATUS http_cache_last_modified(time_t, time_t, char *, size_t) */
150 PHP_HTTP_API STATUS _http_cache_last_modified(time_t last_modified,
151 time_t send_modified, const char *cache_control, size_t cc_len TSRMLS_DC)
152 {
153 if (cc_len && (SUCCESS != http_send_cache_control(cache_control, cc_len))) {
154 return FAILURE;
155 }
156
157 if (SUCCESS != http_send_last_modified(send_modified)) {
158 return FAILURE;
159 }
160
161 if (http_match_last_modified("HTTP_IF_MODIFIED_SINCE", last_modified)) {
162 return http_cache_exit();
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 if (cc_len && (SUCCESS != http_send_cache_control(cache_control, cc_len))) {
174 return FAILURE;
175 }
176
177 if (etag_len) {
178 if (SUCCESS != http_send_etag(etag, etag_len)) {
179 return FAILURE;
180 }
181 if (!http_match_etag("HTTP_IF_NONE_MATCH", etag)) {
182 return SUCCESS;
183 }
184 return http_cache_exit();
185 }
186
187 /* if no etag is given and we didn't already start ob_etaghandler -- start it */
188 if (HTTP_G(etag_started)) {
189 return SUCCESS;
190 }
191
192 if (HTTP_G(etag_started) = (SUCCESS == php_start_ob_buffer_named("ob_etaghandler", HTTP_SENDBUF_SIZE, 1 TSRMLS_CC))) {
193 return SUCCESS;
194 } else {
195 return FAILURE;
196 }
197
198 }
199 /* }}} */
200
201 /* {{{ STATUS http_cache_exit() */
202 PHP_HTTP_API STATUS _http_cache_exit(TSRMLS_D)
203 {
204 if (SUCCESS != http_send_status(304)) {
205 http_error(E_WARNING, HTTP_E_HEADER, "Could not send 304 Not Modified");
206 return FAILURE;
207 }
208 /* TODO: cache_log */
209 zend_bailout();
210 return SUCCESS; /* fake */
211 }
212 /* }}} */
213
214 /* {{{ void http_ob_etaghandler(char *, uint, char **, uint *, int) */
215 PHP_HTTP_API void _http_ob_etaghandler(char *output, uint output_len,
216 char **handled_output, uint *handled_output_len, int mode TSRMLS_DC)
217 {
218 char etag[33] = { 0 };
219 unsigned char digest[16];
220
221 if (mode & PHP_OUTPUT_HANDLER_START) {
222 HTTP_G(etag_started) = 1;
223 PHP_MD5Init(&HTTP_G(etag_md5));
224 }
225
226 PHP_MD5Update(&HTTP_G(etag_md5), output, output_len);
227
228 if (mode & PHP_OUTPUT_HANDLER_END) {
229 PHP_MD5Final(digest, &HTTP_G(etag_md5));
230
231 /* just do that if desired */
232 if (HTTP_G(etag_started)) {
233 make_digest(etag, digest);
234 http_send_header("Cache-Control: " HTTP_DEFAULT_CACHECONTROL);
235 http_send_etag(etag, 32);
236
237 if (http_match_etag("HTTP_IF_NONE_MATCH", etag)) {
238 http_cache_exit();
239 }
240 }
241 }
242
243 *handled_output_len = output_len;
244 *handled_output = estrndup(output, output_len);
245 }
246 /* }}} */
247
248 /*
249 * Local variables:
250 * tab-width: 4
251 * c-basic-offset: 4
252 * End:
253 * vim600: sw=4 ts=4 fdm=marker
254 * vim<600: sw=4 ts=4
255 */
256