- relicense with a BSD style license
[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 (cc_len && (SUCCESS != http_send_cache_control(cache_control, cc_len))) {
164 return FAILURE;
165 }
166
167 if (SUCCESS != http_send_last_modified_ex(send_modified, &sent_header)) {
168 return FAILURE;
169 }
170
171 if (http_match_last_modified("HTTP_IF_MODIFIED_SINCE", last_modified)) {
172 http_exit_ex(304, sent_header, NULL, 0);
173 } else {
174 STR_FREE(sent_header);
175 }
176
177 return SUCCESS;
178 }
179 /* }}} */
180
181 /* {{{ STATUS http_cache_etag(char *, size_t, char *, size_t) */
182 PHP_HTTP_API STATUS _http_cache_etag(const char *etag, size_t etag_len,
183 const char *cache_control, size_t cc_len TSRMLS_DC)
184 {
185 char *sent_header = NULL;
186
187 if (cc_len && (SUCCESS != http_send_cache_control(cache_control, cc_len))) {
188 return FAILURE;
189 }
190
191 if (etag_len) {
192 if (SUCCESS != http_send_etag_ex(etag, etag_len, &sent_header)) {
193 return FAILURE;
194 }
195 if (http_match_etag("HTTP_IF_NONE_MATCH", etag)) {
196 http_exit_ex(304, sent_header, NULL, 0);
197 } else {
198 STR_FREE(sent_header);
199 }
200 return SUCCESS;
201 }
202
203 /* start ob_etaghandler */
204 return http_start_ob_etaghandler();
205 }
206 /* }}} */
207
208 PHP_HTTP_API STATUS _http_start_ob_etaghandler(TSRMLS_D)
209 {
210 /* already running? */
211 if (php_ob_handler_used("ob_etaghandler" TSRMLS_CC)) {
212 http_error(HE_WARNING, HTTP_E_RUNTIME, "ob_etaghandler can only be used once");
213 return FAILURE;
214 }
215
216 HTTP_G(etag).started = 1;
217 return php_start_ob_buffer_named("ob_etaghandler", HTTP_G(send).buffer_size, 1 TSRMLS_CC);
218 }
219
220 PHP_HTTP_API zend_bool _http_interrupt_ob_etaghandler(TSRMLS_D)
221 {
222 if (HTTP_G(etag).started) {
223 HTTP_G(etag).started = 0;
224 http_etag_free(&HTTP_G(etag).ctx);
225 return 1;
226 }
227 return 0;
228 }
229
230 /* {{{ void http_ob_etaghandler(char *, uint, char **, uint *, int) */
231 void _http_ob_etaghandler(char *output, uint output_len,
232 char **handled_output, uint *handled_output_len, int mode TSRMLS_DC)
233 {
234 /* passthru */
235 *handled_output_len = output_len;
236 *handled_output = estrndup(output, output_len);
237
238 /* are we supposed to run? */
239 if (HTTP_G(etag).started) {
240 /* initialize the etag context */
241 if (mode & PHP_OUTPUT_HANDLER_START) {
242 HTTP_G(etag).ctx = http_etag_init();
243 }
244
245 /* update */
246 http_etag_update(HTTP_G(etag).ctx, output, output_len);
247
248 /* finish */
249 if (mode & PHP_OUTPUT_HANDLER_END) {
250 char *sent_header = NULL;
251 char *etag = http_etag_finish(&HTTP_G(etag).ctx);
252
253 http_send_cache_control(HTTP_DEFAULT_CACHECONTROL, lenof(HTTP_DEFAULT_CACHECONTROL));
254 http_send_etag_ex(etag, strlen(etag), &sent_header);
255
256 if (http_match_etag("HTTP_IF_NONE_MATCH", etag)) {
257 http_exit_ex(304, sent_header, etag, 0);
258 }
259
260 STR_FREE(sent_header);
261 STR_FREE(etag);
262 }
263 }
264 }
265 /* }}} */
266
267 /*
268 * Local variables:
269 * tab-width: 4
270 * c-basic-offset: 4
271 * End:
272 * vim600: sw=4 ts=4 fdm=marker
273 * vim<600: sw=4 ts=4
274 */
275