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