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