more tweaks
[m6w6/ext-http] / http_cache_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-2005, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 /* $Id$ */
14
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
18 #include "php.h"
19
20 #include "SAPI.h"
21 #include "php_streams.h"
22 #include "php_output.h"
23 #include "ext/standard/md5.h"
24 #include "ext/standard/sha1.h"
25
26 #include "php_http.h"
27 #include "php_http_std_defs.h"
28 #include "php_http_api.h"
29 #include "php_http_cache_api.h"
30 #include "php_http_send_api.h"
31 #include "php_http_date_api.h"
32
33 #ifdef HTTP_HAVE_MHASH
34 # include <mhash.h>
35 #endif
36
37 ZEND_EXTERN_MODULE_GLOBALS(http);
38
39 PHP_MINIT_FUNCTION(http_cache)
40 {
41 HTTP_LONG_CONSTANT("HTTP_ETAG_MD5", HTTP_ETAG_MD5);
42 HTTP_LONG_CONSTANT("HTTP_ETAG_SHA1", HTTP_ETAG_SHA1);
43 HTTP_LONG_CONSTANT("HTTP_ETAG_CRC32", HTTP_ETAG_CRC32);
44
45 #ifdef HTTP_HAVE_MHASH
46 {
47 int l, i, c = mhash_count();
48
49 for (i = 0; i <= c; ++i) {
50 char const_name[256] = {0};
51 const char *hash_name = mhash_get_hash_name_static(i);
52
53 if (hash_name) {
54 l = snprintf(const_name, 255, "HTTP_ETAG_MHASH_%s", hash_name);
55 zend_register_long_constant(const_name, l + 1, i, CONST_CS|CONST_PERSISTENT, module_number TSRMLS_CC);
56 }
57 }
58 }
59 #endif
60
61 return SUCCESS;
62 }
63
64 /* {{{ char *http_etag(void *, size_t, http_send_mode) */
65 PHP_HTTP_API char *_http_etag(const void *data_ptr, size_t data_len, http_send_mode data_mode TSRMLS_DC)
66 {
67 void *ctx = http_etag_init();
68
69 if (data_mode == SEND_DATA) {
70 http_etag_update(ctx, data_ptr, data_len);
71 } else {
72 STATUS ss = FAILURE;
73 php_stream_statbuf ssb;
74
75 if (data_mode == SEND_RSRC) {
76 ss = php_stream_stat((php_stream *) data_ptr, &ssb);
77 } else {
78 ss = php_stream_stat_path((char *) data_ptr, &ssb);
79 }
80
81 if (SUCCESS != ss) {
82 http_etag_free(&ctx);
83 return NULL;
84 } else {
85 size_t ssb_len;
86 char ssb_buf[128] = {0};
87
88 ssb_len = snprintf(ssb_buf, 127, "%ld=%ld=%ld", (long) ssb.sb.st_mtime,
89 (long) ssb.sb.st_ino,
90 (long) ssb.sb.st_size);
91 http_etag_update(ctx, ssb_buf, ssb_len);
92 }
93 }
94
95 return http_etag_finish(&ctx);
96 }
97 /* }}} */
98
99 /* {{{ time_t http_last_modified(void *, http_send_mode) */
100 PHP_HTTP_API time_t _http_last_modified(const void *data_ptr, http_send_mode data_mode TSRMLS_DC)
101 {
102 php_stream_statbuf ssb;
103
104 switch (data_mode)
105 {
106 case SEND_DATA: return time(NULL);
107 case SEND_RSRC: return php_stream_stat((php_stream *) data_ptr, &ssb) ? 0 : ssb.sb.st_mtime;
108 default: return php_stream_stat_path((char *) data_ptr, &ssb) ? 0 : ssb.sb.st_mtime;
109 }
110 }
111 /* }}} */
112
113 /* {{{ zend_bool http_match_last_modified(char *, time_t) */
114 PHP_HTTP_API zend_bool _http_match_last_modified_ex(const char *entry, time_t t, zend_bool enforce_presence TSRMLS_DC)
115 {
116 zend_bool retval;
117 zval *zmodified;
118 char *modified, *chr_ptr;
119
120 HTTP_GSC(zmodified, entry, !enforce_presence);
121
122 modified = estrndup(Z_STRVAL_P(zmodified), Z_STRLEN_P(zmodified));
123 if (chr_ptr = strrchr(modified, ';')) {
124 chr_ptr = 0;
125 }
126 retval = (t <= http_parse_date(modified));
127 efree(modified);
128 return retval;
129 }
130 /* }}} */
131
132 /* {{{ zend_bool http_match_etag(char *, char *) */
133 PHP_HTTP_API zend_bool _http_match_etag_ex(const char *entry, const char *etag, zend_bool enforce_presence TSRMLS_DC)
134 {
135 zval *zetag;
136 char *quoted_etag;
137 zend_bool result;
138
139 HTTP_GSC(zetag, entry, !enforce_presence);
140
141 if (NULL != strchr(Z_STRVAL_P(zetag), '*')) {
142 return 1;
143 }
144
145 spprintf(&quoted_etag, 0, "\"%s\"", etag);
146 if (!strchr(Z_STRVAL_P(zetag), ',')) {
147 result = !strcmp(Z_STRVAL_P(zetag), quoted_etag);
148 } else {
149 result = (NULL != strstr(Z_STRVAL_P(zetag), quoted_etag));
150 }
151 efree(quoted_etag);
152
153 return result;
154 }
155 /* }}} */
156
157 /* {{{ STATUS http_cache_last_modified(time_t, time_t, char *, size_t) */
158 PHP_HTTP_API STATUS _http_cache_last_modified(time_t last_modified,
159 time_t send_modified, const char *cache_control, size_t cc_len TSRMLS_DC)
160 {
161 char *sent_header = NULL;
162
163 if (SG(headers_sent)) {
164 return FAILURE;
165 }
166
167 if (cc_len && (SUCCESS != http_send_cache_control(cache_control, cc_len))) {
168 return FAILURE;
169 }
170
171 if (SUCCESS != http_send_last_modified_ex(send_modified, &sent_header)) {
172 return FAILURE;
173 }
174
175 if (http_match_last_modified("HTTP_IF_MODIFIED_SINCE", last_modified)) {
176 http_exit_ex(304, sent_header, NULL, 0);
177 } else {
178 STR_FREE(sent_header);
179 }
180
181 return SUCCESS;
182 }
183 /* }}} */
184
185 /* {{{ STATUS http_cache_etag(char *, size_t, char *, size_t) */
186 PHP_HTTP_API STATUS _http_cache_etag(const char *etag, size_t etag_len,
187 const char *cache_control, size_t cc_len TSRMLS_DC)
188 {
189 char *sent_header = NULL;
190
191 if (SG(headers_sent)) {
192 return FAILURE;
193 }
194
195 if (cc_len && (SUCCESS != http_send_cache_control(cache_control, cc_len))) {
196 return FAILURE;
197 }
198
199 if (etag_len) {
200 if (SUCCESS != http_send_etag_ex(etag, etag_len, &sent_header)) {
201 return FAILURE;
202 }
203 if (http_match_etag("HTTP_IF_NONE_MATCH", etag)) {
204 http_exit_ex(304, sent_header, NULL, 0);
205 } else {
206 STR_FREE(sent_header);
207 }
208 return SUCCESS;
209 }
210
211 /* start ob_etaghandler */
212 return http_start_ob_etaghandler();
213 }
214 /* }}} */
215
216 PHP_HTTP_API STATUS _http_start_ob_etaghandler(TSRMLS_D)
217 {
218 /* already running? */
219 if (php_ob_handler_used("ob_etaghandler" TSRMLS_CC)) {
220 http_error(HE_WARNING, HTTP_E_RUNTIME, "ob_etaghandler can only be used once");
221 return FAILURE;
222 }
223
224 HTTP_G(etag).started = 1;
225 return php_start_ob_buffer_named("ob_etaghandler", HTTP_G(send).buffer_size, 1 TSRMLS_CC);
226 }
227
228 PHP_HTTP_API zend_bool _http_interrupt_ob_etaghandler(TSRMLS_D)
229 {
230 if (HTTP_G(etag).started) {
231 HTTP_G(etag).started = 0;
232 http_etag_free(&HTTP_G(etag).ctx);
233 return 1;
234 }
235 return 0;
236 }
237
238 /* {{{ void http_ob_etaghandler(char *, uint, char **, uint *, int) */
239 void _http_ob_etaghandler(char *output, uint output_len,
240 char **handled_output, uint *handled_output_len, int mode TSRMLS_DC)
241 {
242 /* passthru */
243 *handled_output_len = output_len;
244 *handled_output = estrndup(output, output_len);
245
246 /* are we supposed to run? */
247 if (HTTP_G(etag).started) {
248 /* initialize the etag context */
249 if (mode & PHP_OUTPUT_HANDLER_START) {
250 HTTP_G(etag).ctx = http_etag_init();
251 }
252
253 /* update */
254 http_etag_update(HTTP_G(etag).ctx, output, output_len);
255
256 /* finish */
257 if (mode & PHP_OUTPUT_HANDLER_END) {
258 char *sent_header = NULL;
259 char *etag = http_etag_finish(&HTTP_G(etag).ctx);
260
261 http_send_cache_control(HTTP_DEFAULT_CACHECONTROL, lenof(HTTP_DEFAULT_CACHECONTROL));
262 http_send_etag_ex(etag, strlen(etag), &sent_header);
263
264 if (http_match_etag("HTTP_IF_NONE_MATCH", etag)) {
265 http_exit_ex(304, sent_header, etag, 0);
266 }
267
268 STR_FREE(sent_header);
269 STR_FREE(etag);
270 }
271 }
272 }
273 /* }}} */
274
275 /*
276 * Local variables:
277 * tab-width: 4
278 * c-basic-offset: 4
279 * End:
280 * vim600: sw=4 ts=4 fdm=marker
281 * vim<600: sw=4 ts=4
282 */
283