1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 5 |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1997-2007 The PHP Group |
6 : +----------------------------------------------------------------------+
7 : | This source file is subject to version 3.01 of the PHP license, |
8 : | that is bundled with this package in the file LICENSE, and is |
9 : | available through the world-wide-web at the following url: |
10 : | http://www.php.net/license/3_01.txt |
11 : | If you did not receive a copy of the PHP license and are unable to |
12 : | obtain it through the world-wide-web, please send a note to |
13 : | license@php.net so we can mail you a copy immediately. |
14 : +----------------------------------------------------------------------+
15 : | Authors: Wez Furlong <wez@thebrainroom.com> |
16 : +----------------------------------------------------------------------+
17 : */
18 :
19 : /* $Id: cast.c,v 1.12.2.1.2.1 2007/01/01 09:36:11 sebastian Exp $ */
20 :
21 : #define _GNU_SOURCE
22 : #include "php.h"
23 : #include "php_globals.h"
24 : #include "php_network.h"
25 : #include "php_open_temporary_file.h"
26 : #include "ext/standard/file.h"
27 : #include <stddef.h>
28 : #include <fcntl.h>
29 :
30 : #include "php_streams_int.h"
31 :
32 : /* Under BSD, emulate fopencookie using funopen */
33 : #if HAVE_FUNOPEN
34 : typedef struct {
35 : int (*reader)(void *, char *, int);
36 : int (*writer)(void *, const char *, int);
37 : fpos_t (*seeker)(void *, fpos_t, int);
38 : int (*closer)(void *);
39 : } COOKIE_IO_FUNCTIONS_T;
40 :
41 : FILE *fopencookie(void *cookie, const char *mode, COOKIE_IO_FUNCTIONS_T *funcs)
42 : {
43 : return funopen(cookie, funcs->reader, funcs->writer, funcs->seeker, funcs->closer);
44 : }
45 : # define HAVE_FOPENCOOKIE 1
46 : # define PHP_STREAM_COOKIE_FUNCTIONS &stream_cookie_functions
47 : #elif HAVE_FOPENCOOKIE
48 : # define PHP_STREAM_COOKIE_FUNCTIONS stream_cookie_functions
49 : #endif
50 :
51 : /* {{{ STDIO with fopencookie */
52 : #if HAVE_FUNOPEN
53 : /* use our fopencookie emulation */
54 : static int stream_cookie_reader(void *cookie, char *buffer, int size)
55 : {
56 : int ret;
57 : TSRMLS_FETCH();
58 : ret = php_stream_read((php_stream*)cookie, buffer, size);
59 : return ret;
60 : }
61 :
62 : static int stream_cookie_writer(void *cookie, const char *buffer, int size)
63 : {
64 : TSRMLS_FETCH();
65 : return php_stream_write((php_stream *)cookie, (char *)buffer, size);
66 : }
67 :
68 : static fpos_t stream_cookie_seeker(void *cookie, off_t position, int whence)
69 : {
70 : TSRMLS_FETCH();
71 : return (fpos_t)php_stream_seek((php_stream *)cookie, position, whence);
72 : }
73 :
74 : static int stream_cookie_closer(void *cookie)
75 : {
76 : php_stream *stream = (php_stream*)cookie;
77 : TSRMLS_FETCH();
78 :
79 : /* prevent recursion */
80 : stream->fclose_stdiocast = PHP_STREAM_FCLOSE_NONE;
81 : return php_stream_close(stream);
82 : }
83 :
84 : #elif HAVE_FOPENCOOKIE
85 : static ssize_t stream_cookie_reader(void *cookie, char *buffer, size_t size)
86 0 : {
87 : ssize_t ret;
88 : TSRMLS_FETCH();
89 0 : ret = php_stream_read(((php_stream *)cookie), buffer, size);
90 0 : return ret;
91 : }
92 :
93 : static ssize_t stream_cookie_writer(void *cookie, const char *buffer, size_t size)
94 0 : {
95 : TSRMLS_FETCH();
96 0 : return php_stream_write(((php_stream *)cookie), (char *)buffer, size);
97 : }
98 :
99 : #ifdef COOKIE_SEEKER_USES_OFF64_T
100 : static int stream_cookie_seeker(void *cookie, __off64_t *position, int whence)
101 0 : {
102 : TSRMLS_FETCH();
103 :
104 0 : *position = php_stream_seek((php_stream *)cookie, (off_t)*position, whence);
105 :
106 0 : if (*position == -1)
107 0 : return -1;
108 0 : return 0;
109 : }
110 : #else
111 : static int stream_cookie_seeker(void *cookie, off_t position, int whence)
112 : {
113 : TSRMLS_FETCH();
114 : return php_stream_seek((php_stream *)cookie, position, whence);
115 : }
116 : #endif
117 :
118 : static int stream_cookie_closer(void *cookie)
119 0 : {
120 0 : php_stream *stream = (php_stream*)cookie;
121 : TSRMLS_FETCH();
122 :
123 : /* prevent recursion */
124 0 : stream->fclose_stdiocast = PHP_STREAM_FCLOSE_NONE;
125 0 : return php_stream_close(stream);
126 : }
127 : #endif /* elif HAVE_FOPENCOOKIE */
128 :
129 : #if HAVE_FOPENCOOKIE
130 : static COOKIE_IO_FUNCTIONS_T stream_cookie_functions =
131 : {
132 : stream_cookie_reader, stream_cookie_writer,
133 : stream_cookie_seeker, stream_cookie_closer
134 : };
135 : #else
136 : /* TODO: use socketpair() to emulate fopencookie, as suggested by Hartmut ? */
137 : #endif
138 : /* }}} */
139 :
140 : /* {{{ php_stream_cast */
141 : PHPAPI int _php_stream_cast(php_stream *stream, int castas, void **ret, int show_err TSRMLS_DC)
142 7192 : {
143 7192 : int flags = castas & PHP_STREAM_CAST_MASK;
144 7192 : castas &= ~PHP_STREAM_CAST_MASK;
145 :
146 : /* synchronize our buffer (if possible) */
147 7192 : if (ret && castas != PHP_STREAM_AS_FD_FOR_SELECT) {
148 0 : php_stream_flush(stream);
149 0 : if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) {
150 : off_t dummy;
151 :
152 0 : stream->ops->seek(stream, stream->position, SEEK_SET, &dummy TSRMLS_CC);
153 0 : stream->readpos = stream->writepos = 0;
154 : }
155 : }
156 :
157 : /* filtered streams can only be cast as stdio, and only when fopencookie is present */
158 :
159 7192 : if (castas == PHP_STREAM_AS_STDIO) {
160 0 : if (stream->stdiocast) {
161 0 : if (ret) {
162 0 : *(FILE**)ret = stream->stdiocast;
163 : }
164 0 : goto exit_success;
165 : }
166 :
167 : /* if the stream is a stdio stream let's give it a chance to respond
168 : * first, to avoid doubling up the layers of stdio with an fopencookie */
169 0 : if (php_stream_is(stream, PHP_STREAM_IS_STDIO) &&
170 : stream->ops->cast &&
171 : !php_stream_is_filtered(stream) &&
172 : stream->ops->cast(stream, castas, ret TSRMLS_CC) == SUCCESS)
173 : {
174 0 : goto exit_success;
175 : }
176 :
177 : #if HAVE_FOPENCOOKIE
178 : /* if just checking, say yes we can be a FILE*, but don't actually create it yet */
179 0 : if (ret == NULL)
180 0 : goto exit_success;
181 :
182 0 : *(FILE**)ret = fopencookie(stream, stream->mode, PHP_STREAM_COOKIE_FUNCTIONS);
183 :
184 0 : if (*ret != NULL) {
185 : off_t pos;
186 :
187 0 : stream->fclose_stdiocast = PHP_STREAM_FCLOSE_FOPENCOOKIE;
188 :
189 : /* If the stream position is not at the start, we need to force
190 : * the stdio layer to believe it's real location. */
191 0 : pos = php_stream_tell(stream);
192 0 : if (pos > 0)
193 0 : fseek(*ret, pos, SEEK_SET);
194 :
195 0 : goto exit_success;
196 : }
197 :
198 : /* must be either:
199 : a) programmer error
200 : b) no memory
201 : -> lets bail
202 : */
203 0 : php_error_docref(NULL TSRMLS_CC, E_ERROR, "fopencookie failed");
204 0 : return FAILURE;
205 : #endif
206 :
207 : if (!php_stream_is_filtered(stream) && stream->ops->cast && stream->ops->cast(stream, castas, NULL TSRMLS_CC) == SUCCESS) {
208 : if (FAILURE == stream->ops->cast(stream, castas, ret TSRMLS_CC)) {
209 : return FAILURE;
210 : }
211 : goto exit_success;
212 : } else if (flags & PHP_STREAM_CAST_TRY_HARD) {
213 : php_stream *newstream;
214 :
215 : newstream = php_stream_fopen_tmpfile();
216 : if (newstream) {
217 : size_t copied = php_stream_copy_to_stream(stream, newstream, PHP_STREAM_COPY_ALL);
218 :
219 : if (copied == 0) {
220 : php_stream_close(newstream);
221 : } else {
222 : int retcode = php_stream_cast(newstream, castas | flags, ret, show_err);
223 :
224 : if (retcode == SUCCESS)
225 : rewind(*(FILE**)ret);
226 :
227 : /* do some specialized cleanup */
228 : if ((flags & PHP_STREAM_CAST_RELEASE)) {
229 : php_stream_free(stream, PHP_STREAM_FREE_CLOSE_CASTED);
230 : }
231 :
232 : return retcode;
233 : }
234 : }
235 : }
236 : }
237 :
238 7192 : if (php_stream_is_filtered(stream)) {
239 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot cast a filtered stream on this system");
240 0 : return FAILURE;
241 7192 : } else if (stream->ops->cast && stream->ops->cast(stream, castas, ret TSRMLS_CC) == SUCCESS) {
242 7192 : goto exit_success;
243 : }
244 :
245 0 : if (show_err) {
246 : /* these names depend on the values of the PHP_STREAM_AS_XXX defines in php_streams.h */
247 : static const char *cast_names[4] = {
248 : "STDIO FILE*", "File Descriptor", "Socket Descriptor", "select()able descriptor"
249 : };
250 :
251 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot represent a stream of type %s as a %s",
252 : stream->ops->label,
253 : cast_names[castas]
254 : );
255 : }
256 :
257 0 : return FAILURE;
258 :
259 7192 : exit_success:
260 :
261 7192 : if ((stream->writepos - stream->readpos) > 0 &&
262 : stream->fclose_stdiocast != PHP_STREAM_FCLOSE_FOPENCOOKIE &&
263 : (flags & PHP_STREAM_CAST_INTERNAL) == 0) {
264 : /* the data we have buffered will be lost to the third party library that
265 : * will be accessing the stream. Emit a warning so that the end-user will
266 : * know that they should try something else */
267 :
268 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING,
269 : "%ld bytes of buffered data lost during stream conversion!",
270 : (long)(stream->writepos - stream->readpos));
271 : }
272 :
273 7192 : if (castas == PHP_STREAM_AS_STDIO && ret)
274 0 : stream->stdiocast = *(FILE**)ret;
275 :
276 7192 : if (flags & PHP_STREAM_CAST_RELEASE) {
277 0 : php_stream_free(stream, PHP_STREAM_FREE_CLOSE_CASTED);
278 : }
279 :
280 7192 : return SUCCESS;
281 :
282 : }
283 : /* }}} */
284 :
285 : /* {{{ php_stream_open_wrapper_as_file */
286 : PHPAPI FILE * _php_stream_open_wrapper_as_file(char *path, char *mode, int options, char **opened_path STREAMS_DC TSRMLS_DC)
287 0 : {
288 0 : FILE *fp = NULL;
289 0 : php_stream *stream = NULL;
290 :
291 0 : stream = php_stream_open_wrapper_rel(path, mode, options|STREAM_WILL_CAST, opened_path);
292 :
293 0 : if (stream == NULL)
294 0 : return NULL;
295 :
296 0 : if (php_stream_cast(stream, PHP_STREAM_AS_STDIO|PHP_STREAM_CAST_TRY_HARD|PHP_STREAM_CAST_RELEASE,
297 : (void**)&fp, REPORT_ERRORS) == FAILURE)
298 : {
299 0 : php_stream_close(stream);
300 0 : if (opened_path && *opened_path)
301 0 : efree(*opened_path);
302 0 : return NULL;
303 : }
304 0 : return fp;
305 : }
306 : /* }}} */
307 :
308 : /* {{{ php_stream_make_seekable */
309 : PHPAPI int _php_stream_make_seekable(php_stream *origstream, php_stream **newstream, int flags STREAMS_DC TSRMLS_DC)
310 0 : {
311 : assert(newstream != NULL);
312 :
313 0 : *newstream = NULL;
314 :
315 0 : if (((flags & PHP_STREAM_FORCE_CONVERSION) == 0) && origstream->ops->seek != NULL) {
316 0 : *newstream = origstream;
317 0 : return PHP_STREAM_UNCHANGED;
318 : }
319 :
320 : /* Use a tmpfile and copy the old streams contents into it */
321 :
322 0 : if (flags & PHP_STREAM_PREFER_STDIO)
323 0 : *newstream = php_stream_fopen_tmpfile();
324 : else
325 0 : *newstream = php_stream_temp_new();
326 :
327 0 : if (*newstream == NULL)
328 0 : return PHP_STREAM_FAILED;
329 :
330 0 : if (php_stream_copy_to_stream(origstream, *newstream, PHP_STREAM_COPY_ALL) == 0) {
331 0 : php_stream_close(*newstream);
332 0 : *newstream = NULL;
333 0 : return PHP_STREAM_CRITICAL;
334 : }
335 :
336 0 : php_stream_close(origstream);
337 0 : php_stream_seek(*newstream, 0, SEEK_SET);
338 :
339 0 : return PHP_STREAM_RELEASED;
340 : }
341 : /* }}} */
342 :
343 : /*
344 : * Local variables:
345 : * tab-width: 4
346 : * c-basic-offset: 4
347 : * End:
348 : * vim600: noet sw=4 ts=4 fdm=marker
349 : * vim<600: noet sw=4 ts=4
350 : */
|