Coverage Report

Created: 2022-10-14 11:19

/src/php-src/main/streams/userspace.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
   +----------------------------------------------------------------------+
3
   | Copyright (c) The PHP Group                                          |
4
   +----------------------------------------------------------------------+
5
   | This source file is subject to version 3.01 of the PHP license,      |
6
   | that is bundled with this package in the file LICENSE, and is        |
7
   | available through the world-wide-web at the following url:           |
8
   | http://www.php.net/license/3_01.txt                                  |
9
   | If you did not receive a copy of the PHP license and are unable to   |
10
   | obtain it through the world-wide-web, please send a note to          |
11
   | license@php.net so we can mail you a copy immediately.               |
12
   +----------------------------------------------------------------------+
13
   | Authors: Wez Furlong <wez@thebrainroom.com>                          |
14
   |          Sara Golemon <pollita@php.net>                              |
15
   +----------------------------------------------------------------------+
16
*/
17
18
#include "php.h"
19
#include "php_globals.h"
20
#include "ext/standard/file.h"
21
#include "ext/standard/flock_compat.h"
22
#ifdef HAVE_SYS_FILE_H
23
#include <sys/file.h>
24
#endif
25
#include <stddef.h>
26
27
#if HAVE_UTIME
28
# ifdef PHP_WIN32
29
#  include <sys/utime.h>
30
# else
31
#  include <utime.h>
32
# endif
33
#endif
34
35
static int le_protocols;
36
37
struct php_user_stream_wrapper {
38
  char * protoname;
39
  char * classname;
40
  zend_class_entry *ce;
41
  php_stream_wrapper wrapper;
42
};
43
44
static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char *filename, const char *mode, int options, zend_string **opened_path, php_stream_context *context STREAMS_DC);
45
static int user_wrapper_stat_url(php_stream_wrapper *wrapper, const char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context);
46
static int user_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int options, php_stream_context *context);
47
static int user_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from, const char *url_to, int options, php_stream_context *context);
48
static int user_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url, int mode, int options, php_stream_context *context);
49
static int user_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options, php_stream_context *context);
50
static int user_wrapper_metadata(php_stream_wrapper *wrapper, const char *url, int option, void *value, php_stream_context *context);
51
static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char *filename, const char *mode,
52
    int options, zend_string **opened_path, php_stream_context *context STREAMS_DC);
53
54
static const php_stream_wrapper_ops user_stream_wops = {
55
  user_wrapper_opener,
56
  NULL, /* close - the streams themselves know how */
57
  NULL, /* stat - the streams themselves know how */
58
  user_wrapper_stat_url,
59
  user_wrapper_opendir,
60
  "user-space",
61
  user_wrapper_unlink,
62
  user_wrapper_rename,
63
  user_wrapper_mkdir,
64
  user_wrapper_rmdir,
65
  user_wrapper_metadata
66
};
67
68
69
static void stream_wrapper_dtor(zend_resource *rsrc)
70
0
{
71
0
  struct php_user_stream_wrapper * uwrap = (struct php_user_stream_wrapper*)rsrc->ptr;
72
73
0
  efree(uwrap->protoname);
74
0
  efree(uwrap->classname);
75
0
  efree(uwrap);
76
0
}
77
78
79
PHP_MINIT_FUNCTION(user_streams)
80
3.64k
{
81
3.64k
  le_protocols = zend_register_list_destructors_ex(stream_wrapper_dtor, NULL, "stream factory", 0);
82
3.64k
  if (le_protocols == FAILURE)
83
0
    return FAILURE;
84
85
3.64k
  REGISTER_LONG_CONSTANT("STREAM_USE_PATH",       USE_PATH, CONST_CS|CONST_PERSISTENT);
86
3.64k
  REGISTER_LONG_CONSTANT("STREAM_IGNORE_URL",     IGNORE_URL, CONST_CS|CONST_PERSISTENT);
87
3.64k
  REGISTER_LONG_CONSTANT("STREAM_REPORT_ERRORS",    REPORT_ERRORS, CONST_CS|CONST_PERSISTENT);
88
3.64k
  REGISTER_LONG_CONSTANT("STREAM_MUST_SEEK",      STREAM_MUST_SEEK, CONST_CS|CONST_PERSISTENT);
89
90
3.64k
  REGISTER_LONG_CONSTANT("STREAM_URL_STAT_LINK",    PHP_STREAM_URL_STAT_LINK,   CONST_CS|CONST_PERSISTENT);
91
3.64k
  REGISTER_LONG_CONSTANT("STREAM_URL_STAT_QUIET",   PHP_STREAM_URL_STAT_QUIET,    CONST_CS|CONST_PERSISTENT);
92
3.64k
  REGISTER_LONG_CONSTANT("STREAM_MKDIR_RECURSIVE",  PHP_STREAM_MKDIR_RECURSIVE,   CONST_CS|CONST_PERSISTENT);
93
94
3.64k
  REGISTER_LONG_CONSTANT("STREAM_IS_URL", PHP_STREAM_IS_URL,    CONST_CS|CONST_PERSISTENT);
95
96
3.64k
  REGISTER_LONG_CONSTANT("STREAM_OPTION_BLOCKING",  PHP_STREAM_OPTION_BLOCKING,   CONST_CS|CONST_PERSISTENT);
97
3.64k
  REGISTER_LONG_CONSTANT("STREAM_OPTION_READ_TIMEOUT",  PHP_STREAM_OPTION_READ_TIMEOUT,   CONST_CS|CONST_PERSISTENT);
98
3.64k
  REGISTER_LONG_CONSTANT("STREAM_OPTION_READ_BUFFER", PHP_STREAM_OPTION_READ_BUFFER,    CONST_CS|CONST_PERSISTENT);
99
3.64k
  REGISTER_LONG_CONSTANT("STREAM_OPTION_WRITE_BUFFER",  PHP_STREAM_OPTION_WRITE_BUFFER,   CONST_CS|CONST_PERSISTENT);
100
101
3.64k
  REGISTER_LONG_CONSTANT("STREAM_BUFFER_NONE",    PHP_STREAM_BUFFER_NONE,     CONST_CS|CONST_PERSISTENT);
102
3.64k
  REGISTER_LONG_CONSTANT("STREAM_BUFFER_LINE",    PHP_STREAM_BUFFER_LINE,     CONST_CS|CONST_PERSISTENT);
103
3.64k
  REGISTER_LONG_CONSTANT("STREAM_BUFFER_FULL",    PHP_STREAM_BUFFER_FULL,     CONST_CS|CONST_PERSISTENT);
104
105
3.64k
  REGISTER_LONG_CONSTANT("STREAM_CAST_AS_STREAM",   PHP_STREAM_AS_STDIO,      CONST_CS|CONST_PERSISTENT);
106
3.64k
  REGISTER_LONG_CONSTANT("STREAM_CAST_FOR_SELECT",  PHP_STREAM_AS_FD_FOR_SELECT,    CONST_CS|CONST_PERSISTENT);
107
108
3.64k
  REGISTER_LONG_CONSTANT("STREAM_META_TOUCH",     PHP_STREAM_META_TOUCH,      CONST_CS|CONST_PERSISTENT);
109
3.64k
  REGISTER_LONG_CONSTANT("STREAM_META_OWNER",     PHP_STREAM_META_OWNER,      CONST_CS|CONST_PERSISTENT);
110
3.64k
  REGISTER_LONG_CONSTANT("STREAM_META_OWNER_NAME",  PHP_STREAM_META_OWNER_NAME,   CONST_CS|CONST_PERSISTENT);
111
3.64k
  REGISTER_LONG_CONSTANT("STREAM_META_GROUP",     PHP_STREAM_META_GROUP,      CONST_CS|CONST_PERSISTENT);
112
3.64k
  REGISTER_LONG_CONSTANT("STREAM_META_GROUP_NAME",  PHP_STREAM_META_GROUP_NAME,   CONST_CS|CONST_PERSISTENT);
113
3.64k
  REGISTER_LONG_CONSTANT("STREAM_META_ACCESS",    PHP_STREAM_META_ACCESS,     CONST_CS|CONST_PERSISTENT);
114
3.64k
  return SUCCESS;
115
3.64k
}
116
117
struct _php_userstream_data {
118
  struct php_user_stream_wrapper * wrapper;
119
  zval object;
120
};
121
typedef struct _php_userstream_data php_userstream_data_t;
122
123
/* names of methods */
124
#define USERSTREAM_OPEN   "stream_open"
125
#define USERSTREAM_CLOSE  "stream_close"
126
#define USERSTREAM_READ   "stream_read"
127
#define USERSTREAM_WRITE  "stream_write"
128
#define USERSTREAM_FLUSH  "stream_flush"
129
#define USERSTREAM_SEEK   "stream_seek"
130
#define USERSTREAM_TELL   "stream_tell"
131
#define USERSTREAM_EOF    "stream_eof"
132
#define USERSTREAM_STAT   "stream_stat"
133
#define USERSTREAM_STATURL  "url_stat"
134
#define USERSTREAM_UNLINK "unlink"
135
#define USERSTREAM_RENAME "rename"
136
#define USERSTREAM_MKDIR  "mkdir"
137
#define USERSTREAM_RMDIR  "rmdir"
138
#define USERSTREAM_DIR_OPEN   "dir_opendir"
139
#define USERSTREAM_DIR_READ   "dir_readdir"
140
#define USERSTREAM_DIR_REWIND "dir_rewinddir"
141
#define USERSTREAM_DIR_CLOSE  "dir_closedir"
142
#define USERSTREAM_LOCK     "stream_lock"
143
#define USERSTREAM_CAST   "stream_cast"
144
#define USERSTREAM_SET_OPTION "stream_set_option"
145
#define USERSTREAM_TRUNCATE "stream_truncate"
146
0
#define USERSTREAM_METADATA "stream_metadata"
147
148
/* {{{ class should have methods like these:
149
150
  function stream_open($path, $mode, $options, &$opened_path)
151
  {
152
      return true/false;
153
  }
154
155
  function stream_read($count)
156
  {
157
      return false on error;
158
    else return string;
159
  }
160
161
  function stream_write($data)
162
  {
163
      return false on error;
164
    else return count written;
165
  }
166
167
  function stream_close()
168
  {
169
  }
170
171
  function stream_flush()
172
  {
173
    return true/false;
174
  }
175
176
  function stream_seek($offset, $whence)
177
  {
178
    return true/false;
179
  }
180
181
  function stream_tell()
182
  {
183
    return (int)$position;
184
  }
185
186
  function stream_eof()
187
  {
188
    return true/false;
189
  }
190
191
  function stream_stat()
192
  {
193
    return array( just like that returned by fstat() );
194
  }
195
196
  function stream_cast($castas)
197
  {
198
    if ($castas == STREAM_CAST_FOR_SELECT) {
199
      return $this->underlying_stream;
200
    }
201
    return false;
202
  }
203
204
  function stream_set_option($option, $arg1, $arg2)
205
  {
206
    switch($option) {
207
    case STREAM_OPTION_BLOCKING:
208
      $blocking = $arg1;
209
      ...
210
    case STREAM_OPTION_READ_TIMEOUT:
211
      $sec = $arg1;
212
      $usec = $arg2;
213
      ...
214
    case STREAM_OPTION_WRITE_BUFFER:
215
      $mode = $arg1;
216
      $size = $arg2;
217
      ...
218
    default:
219
      return false;
220
    }
221
  }
222
223
  function url_stat(string $url, int $flags)
224
  {
225
    return array( just like that returned by stat() );
226
  }
227
228
  function unlink(string $url)
229
  {
230
    return true / false;
231
  }
232
233
  function rename(string $from, string $to)
234
  {
235
    return true / false;
236
  }
237
238
  function mkdir($dir, $mode, $options)
239
  {
240
    return true / false;
241
  }
242
243
  function rmdir($dir, $options)
244
  {
245
    return true / false;
246
  }
247
248
  function dir_opendir(string $url, int $options)
249
  {
250
    return true / false;
251
  }
252
253
  function dir_readdir()
254
  {
255
    return string next filename in dir ;
256
  }
257
258
  function dir_closedir()
259
  {
260
    release dir related resources;
261
  }
262
263
  function dir_rewinddir()
264
  {
265
    reset to start of dir list;
266
  }
267
268
  function stream_lock($operation)
269
  {
270
    return true / false;
271
  }
272
273
  function stream_truncate($new_size)
274
  {
275
    return true / false;
276
  }
277
278
  }}} **/
279
280
static void user_stream_create_object(struct php_user_stream_wrapper *uwrap, php_stream_context *context, zval *object)
281
0
{
282
0
  if (uwrap->ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) {
283
0
    ZVAL_UNDEF(object);
284
0
    return;
285
0
  }
286
287
  /* create an instance of our class */
288
0
  if (object_init_ex(object, uwrap->ce) == FAILURE) {
289
0
    ZVAL_UNDEF(object);
290
0
    return;
291
0
  }
292
293
0
  if (context) {
294
0
    add_property_resource(object, "context", context->res);
295
0
    GC_ADDREF(context->res);
296
0
  } else {
297
0
    add_property_null(object, "context");
298
0
  }
299
300
0
  if (uwrap->ce->constructor) {
301
0
    zend_fcall_info fci;
302
0
    zend_fcall_info_cache fcc;
303
0
    zval retval;
304
305
0
    fci.size = sizeof(fci);
306
0
    ZVAL_UNDEF(&fci.function_name);
307
0
    fci.object = Z_OBJ_P(object);
308
0
    fci.retval = &retval;
309
0
    fci.param_count = 0;
310
0
    fci.params = NULL;
311
0
    fci.no_separation = 1;
312
313
0
    fcc.function_handler = uwrap->ce->constructor;
314
0
    fcc.called_scope = Z_OBJCE_P(object);
315
0
    fcc.object = Z_OBJ_P(object);
316
317
0
    if (zend_call_function(&fci, &fcc) == FAILURE) {
318
0
      php_error_docref(NULL, E_WARNING, "Could not execute %s::%s()", ZSTR_VAL(uwrap->ce->name), ZSTR_VAL(uwrap->ce->constructor->common.function_name));
319
0
      zval_ptr_dtor(object);
320
0
      ZVAL_UNDEF(object);
321
0
    } else {
322
0
      zval_ptr_dtor(&retval);
323
0
    }
324
0
  }
325
0
}
326
327
static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char *filename, const char *mode,
328
                     int options, zend_string **opened_path, php_stream_context *context STREAMS_DC)
329
0
{
330
0
  struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
331
0
  php_userstream_data_t *us;
332
0
  zval zretval, zfuncname;
333
0
  zval args[4];
334
0
  int call_result;
335
0
  php_stream *stream = NULL;
336
0
  zend_bool old_in_user_include;
337
338
  /* Try to catch bad usage without preventing flexibility */
339
0
  if (FG(user_stream_current_filename) != NULL && strcmp(filename, FG(user_stream_current_filename)) == 0) {
340
0
    php_stream_wrapper_log_error(wrapper, options, "infinite recursion prevented");
341
0
    return NULL;
342
0
  }
343
0
  FG(user_stream_current_filename) = filename;
344
345
  /* if the user stream was registered as local and we are in include context,
346
    we add allow_url_include restrictions to allow_url_fopen ones */
347
  /* we need only is_url == 0 here since if is_url == 1 and remote wrappers
348
    were restricted we wouldn't get here */
349
0
  old_in_user_include = PG(in_user_include);
350
0
  if(uwrap->wrapper.is_url == 0 &&
351
0
    (options & STREAM_OPEN_FOR_INCLUDE) &&
352
0
    !PG(allow_url_include)) {
353
0
    PG(in_user_include) = 1;
354
0
  }
355
356
0
  us = emalloc(sizeof(*us));
357
0
  us->wrapper = uwrap;
358
359
0
  user_stream_create_object(uwrap, context, &us->object);
360
0
  if (Z_TYPE(us->object) == IS_UNDEF) {
361
0
    FG(user_stream_current_filename) = NULL;
362
0
    PG(in_user_include) = old_in_user_include;
363
0
    efree(us);
364
0
    return NULL;
365
0
  }
366
367
  /* call it's stream_open method - set up params first */
368
0
  ZVAL_STRING(&args[0], filename);
369
0
  ZVAL_STRING(&args[1], mode);
370
0
  ZVAL_LONG(&args[2], options);
371
0
  ZVAL_NEW_REF(&args[3], &EG(uninitialized_zval));
372
373
0
  ZVAL_STRING(&zfuncname, USERSTREAM_OPEN);
374
375
0
  zend_try {
376
0
    call_result = call_user_function_ex(NULL,
377
0
        Z_ISUNDEF(us->object)? NULL : &us->object,
378
0
        &zfuncname,
379
0
        &zretval,
380
0
        4, args,
381
0
        0, NULL );
382
0
  } zend_catch {
383
0
    FG(user_stream_current_filename) = NULL;
384
0
    zend_bailout();
385
0
  } zend_end_try();
386
387
0
  if (call_result == SUCCESS && Z_TYPE(zretval) != IS_UNDEF && zval_is_true(&zretval)) {
388
    /* the stream is now open! */
389
0
    stream = php_stream_alloc_rel(&php_stream_userspace_ops, us, 0, mode);
390
391
    /* if the opened path is set, copy it out */
392
0
    if (Z_ISREF(args[3]) && Z_TYPE_P(Z_REFVAL(args[3])) == IS_STRING && opened_path) {
393
0
      *opened_path = zend_string_copy(Z_STR_P(Z_REFVAL(args[3])));
394
0
    }
395
396
    /* set wrapper data to be a reference to our object */
397
0
    ZVAL_COPY(&stream->wrapperdata, &us->object);
398
0
  } else {
399
0
    php_stream_wrapper_log_error(wrapper, options, "\"%s::" USERSTREAM_OPEN "\" call failed",
400
0
      us->wrapper->classname);
401
0
  }
402
403
  /* destroy everything else */
404
0
  if (stream == NULL) {
405
0
    zval_ptr_dtor(&us->object);
406
0
    ZVAL_UNDEF(&us->object);
407
0
    efree(us);
408
0
  }
409
0
  zval_ptr_dtor(&zretval);
410
0
  zval_ptr_dtor(&zfuncname);
411
0
  zval_ptr_dtor(&args[3]);
412
0
  zval_ptr_dtor(&args[2]);
413
0
  zval_ptr_dtor(&args[1]);
414
0
  zval_ptr_dtor(&args[0]);
415
416
0
  FG(user_stream_current_filename) = NULL;
417
418
0
  PG(in_user_include) = old_in_user_include;
419
0
  return stream;
420
0
}
421
422
static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char *filename, const char *mode,
423
    int options, zend_string **opened_path, php_stream_context *context STREAMS_DC)
424
0
{
425
0
  struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
426
0
  php_userstream_data_t *us;
427
0
  zval zretval, zfuncname;
428
0
  zval args[2];
429
0
  int call_result;
430
0
  php_stream *stream = NULL;
431
432
  /* Try to catch bad usage without preventing flexibility */
433
0
  if (FG(user_stream_current_filename) != NULL && strcmp(filename, FG(user_stream_current_filename)) == 0) {
434
0
    php_stream_wrapper_log_error(wrapper, options, "infinite recursion prevented");
435
0
    return NULL;
436
0
  }
437
0
  FG(user_stream_current_filename) = filename;
438
439
0
  us = emalloc(sizeof(*us));
440
0
  us->wrapper = uwrap;
441
442
0
  user_stream_create_object(uwrap, context, &us->object);
443
0
  if (Z_TYPE(us->object) == IS_UNDEF) {
444
0
    FG(user_stream_current_filename) = NULL;
445
0
    efree(us);
446
0
    return NULL;
447
0
  }
448
449
  /* call it's dir_open method - set up params first */
450
0
  ZVAL_STRING(&args[0], filename);
451
0
  ZVAL_LONG(&args[1], options);
452
453
0
  ZVAL_STRING(&zfuncname, USERSTREAM_DIR_OPEN);
454
455
0
  call_result = call_user_function_ex(NULL,
456
0
      Z_ISUNDEF(us->object)? NULL : &us->object,
457
0
      &zfuncname,
458
0
      &zretval,
459
0
      2, args,
460
0
      0, NULL );
461
462
0
  if (call_result == SUCCESS && Z_TYPE(zretval) != IS_UNDEF && zval_is_true(&zretval)) {
463
    /* the stream is now open! */
464
0
    stream = php_stream_alloc_rel(&php_stream_userspace_dir_ops, us, 0, mode);
465
466
    /* set wrapper data to be a reference to our object */
467
0
    ZVAL_COPY(&stream->wrapperdata, &us->object);
468
0
  } else {
469
0
    php_stream_wrapper_log_error(wrapper, options, "\"%s::" USERSTREAM_DIR_OPEN "\" call failed",
470
0
      us->wrapper->classname);
471
0
  }
472
473
  /* destroy everything else */
474
0
  if (stream == NULL) {
475
0
    zval_ptr_dtor(&us->object);
476
0
    ZVAL_UNDEF(&us->object);
477
0
    efree(us);
478
0
  }
479
0
  zval_ptr_dtor(&zretval);
480
481
0
  zval_ptr_dtor(&zfuncname);
482
0
  zval_ptr_dtor(&args[1]);
483
0
  zval_ptr_dtor(&args[0]);
484
485
0
  FG(user_stream_current_filename) = NULL;
486
487
0
  return stream;
488
0
}
489
490
491
/* {{{ proto bool stream_wrapper_register(string protocol, string classname[, int flags])
492
   Registers a custom URL protocol handler class */
493
PHP_FUNCTION(stream_wrapper_register)
494
0
{
495
0
  zend_string *protocol, *classname;
496
0
  struct php_user_stream_wrapper * uwrap;
497
0
  zend_resource *rsrc;
498
0
  zend_long flags = 0;
499
500
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS|l", &protocol, &classname, &flags) == FAILURE) {
501
0
    RETURN_THROWS();
502
0
  }
503
504
0
  uwrap = (struct php_user_stream_wrapper *)ecalloc(1, sizeof(*uwrap));
505
0
  uwrap->protoname = estrndup(ZSTR_VAL(protocol), ZSTR_LEN(protocol));
506
0
  uwrap->classname = estrndup(ZSTR_VAL(classname), ZSTR_LEN(classname));
507
0
  uwrap->wrapper.wops = &user_stream_wops;
508
0
  uwrap->wrapper.abstract = uwrap;
509
0
  uwrap->wrapper.is_url = ((flags & PHP_STREAM_IS_URL) != 0);
510
511
0
  rsrc = zend_register_resource(uwrap, le_protocols);
512
513
0
  if ((uwrap->ce = zend_lookup_class(classname)) != NULL) {
514
0
    if (php_register_url_stream_wrapper_volatile(protocol, &uwrap->wrapper) == SUCCESS) {
515
0
      RETURN_TRUE;
516
0
    } else {
517
      /* We failed.  But why? */
518
0
      if (zend_hash_exists(php_stream_get_url_stream_wrappers_hash(), protocol)) {
519
0
        php_error_docref(NULL, E_WARNING, "Protocol %s:// is already defined.", ZSTR_VAL(protocol));
520
0
      } else {
521
        /* Hash doesn't exist so it must have been an invalid protocol scheme */
522
0
        php_error_docref(NULL, E_WARNING, "Invalid protocol scheme specified. Unable to register wrapper class %s to %s://", ZSTR_VAL(classname), ZSTR_VAL(protocol));
523
0
      }
524
0
    }
525
0
  } else {
526
0
    php_error_docref(NULL, E_WARNING, "Class '%s' is undefined", ZSTR_VAL(classname));
527
0
  }
528
529
0
  zend_list_delete(rsrc);
530
0
  RETURN_FALSE;
531
0
}
532
/* }}} */
533
534
/* {{{ proto bool stream_wrapper_unregister(string protocol)
535
  Unregister a wrapper for the life of the current request. */
536
PHP_FUNCTION(stream_wrapper_unregister)
537
0
{
538
0
  zend_string *protocol;
539
540
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &protocol) == FAILURE) {
541
0
    RETURN_THROWS();
542
0
  }
543
544
0
  if (php_unregister_url_stream_wrapper_volatile(protocol) == FAILURE) {
545
    /* We failed */
546
0
    php_error_docref(NULL, E_WARNING, "Unable to unregister protocol %s://", ZSTR_VAL(protocol));
547
0
    RETURN_FALSE;
548
0
  }
549
550
0
  RETURN_TRUE;
551
0
}
552
/* }}} */
553
554
/* {{{ proto bool stream_wrapper_restore(string protocol)
555
  Restore the original protocol handler, overriding if necessary */
556
PHP_FUNCTION(stream_wrapper_restore)
557
0
{
558
0
  zend_string *protocol;
559
0
  php_stream_wrapper *wrapper;
560
0
  HashTable *global_wrapper_hash;
561
562
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &protocol) == FAILURE) {
563
0
    RETURN_THROWS();
564
0
  }
565
566
0
  global_wrapper_hash = php_stream_get_url_stream_wrappers_hash_global();
567
0
  if (php_stream_get_url_stream_wrappers_hash() == global_wrapper_hash) {
568
0
    php_error_docref(NULL, E_NOTICE, "%s:// was never changed, nothing to restore", ZSTR_VAL(protocol));
569
0
    RETURN_TRUE;
570
0
  }
571
572
0
  if ((wrapper = zend_hash_find_ptr(global_wrapper_hash, protocol)) == NULL) {
573
0
    php_error_docref(NULL, E_WARNING, "%s:// never existed, nothing to restore", ZSTR_VAL(protocol));
574
0
    RETURN_FALSE;
575
0
  }
576
577
  /* A failure here could be okay given that the protocol might have been merely unregistered */
578
0
  php_unregister_url_stream_wrapper_volatile(protocol);
579
580
0
  if (php_register_url_stream_wrapper_volatile(protocol, wrapper) == FAILURE) {
581
0
    php_error_docref(NULL, E_WARNING, "Unable to restore original %s:// wrapper", ZSTR_VAL(protocol));
582
0
    RETURN_FALSE;
583
0
  }
584
585
0
  RETURN_TRUE;
586
0
}
587
/* }}} */
588
589
static ssize_t php_userstreamop_write(php_stream *stream, const char *buf, size_t count)
590
0
{
591
0
  zval func_name;
592
0
  zval retval;
593
0
  int call_result;
594
0
  php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
595
0
  zval args[1];
596
0
  ssize_t didwrite;
597
598
0
  assert(us != NULL);
599
600
0
  ZVAL_STRINGL(&func_name, USERSTREAM_WRITE, sizeof(USERSTREAM_WRITE)-1);
601
602
0
  ZVAL_STRINGL(&args[0], (char*)buf, count);
603
604
0
  call_result = call_user_function_ex(NULL,
605
0
      Z_ISUNDEF(us->object)? NULL : &us->object,
606
0
      &func_name,
607
0
      &retval,
608
0
      1, args,
609
0
      0, NULL);
610
0
  zval_ptr_dtor(&args[0]);
611
0
  zval_ptr_dtor(&func_name);
612
613
0
  if (EG(exception)) {
614
0
    return -1;
615
0
  }
616
617
0
  if (call_result == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
618
0
    if (Z_TYPE(retval) == IS_FALSE) {
619
0
      didwrite = -1;
620
0
    } else {
621
0
      convert_to_long(&retval);
622
0
      didwrite = Z_LVAL(retval);
623
0
    }
624
0
  } else {
625
0
    php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_WRITE " is not implemented!",
626
0
        us->wrapper->classname);
627
0
    didwrite = -1;
628
0
  }
629
630
  /* don't allow strange buffer overruns due to bogus return */
631
0
  if (didwrite > 0 && didwrite > count) {
632
0
    php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_WRITE " wrote " ZEND_LONG_FMT " bytes more data than requested (" ZEND_LONG_FMT " written, " ZEND_LONG_FMT " max)",
633
0
        us->wrapper->classname,
634
0
        (zend_long)(didwrite - count), (zend_long)didwrite, (zend_long)count);
635
0
    didwrite = count;
636
0
  }
637
638
0
  zval_ptr_dtor(&retval);
639
640
0
  return didwrite;
641
0
}
642
643
static ssize_t php_userstreamop_read(php_stream *stream, char *buf, size_t count)
644
0
{
645
0
  zval func_name;
646
0
  zval retval;
647
0
  zval args[1];
648
0
  int call_result;
649
0
  size_t didread = 0;
650
0
  php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
651
652
0
  assert(us != NULL);
653
654
0
  ZVAL_STRINGL(&func_name, USERSTREAM_READ, sizeof(USERSTREAM_READ)-1);
655
656
0
  ZVAL_LONG(&args[0], count);
657
658
0
  call_result = call_user_function_ex(NULL,
659
0
      Z_ISUNDEF(us->object)? NULL : &us->object,
660
0
      &func_name,
661
0
      &retval,
662
0
      1, args,
663
0
      0, NULL);
664
665
0
  zval_ptr_dtor(&args[0]);
666
0
  zval_ptr_dtor(&func_name);
667
668
0
  if (EG(exception)) {
669
0
    return -1;
670
0
  }
671
672
0
  if (call_result == FAILURE) {
673
0
    php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_READ " is not implemented!",
674
0
        us->wrapper->classname);
675
0
    return -1;
676
0
  }
677
678
0
  if (Z_TYPE(retval) == IS_FALSE) {
679
0
    return -1;
680
0
  }
681
682
0
  if (!try_convert_to_string(&retval)) {
683
0
    return -1;
684
0
  }
685
686
0
  didread = Z_STRLEN(retval);
687
0
  if (didread > 0) {
688
0
    if (didread > count) {
689
0
      php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_READ " - read " ZEND_LONG_FMT " bytes more data than requested (" ZEND_LONG_FMT " read, " ZEND_LONG_FMT " max) - excess data will be lost",
690
0
          us->wrapper->classname, (zend_long)(didread - count), (zend_long)didread, (zend_long)count);
691
0
      didread = count;
692
0
    }
693
0
    memcpy(buf, Z_STRVAL(retval), didread);
694
0
  }
695
696
0
  zval_ptr_dtor(&retval);
697
0
  ZVAL_UNDEF(&retval);
698
699
  /* since the user stream has no way of setting the eof flag directly, we need to ask it if we hit eof */
700
701
0
  ZVAL_STRINGL(&func_name, USERSTREAM_EOF, sizeof(USERSTREAM_EOF)-1);
702
0
  call_result = call_user_function(NULL,
703
0
      Z_ISUNDEF(us->object)? NULL : &us->object,
704
0
      &func_name,
705
0
      &retval,
706
0
      0, NULL);
707
0
  zval_ptr_dtor(&func_name);
708
709
0
  if (EG(exception)) {
710
0
    stream->eof = 1;
711
0
    return -1;
712
0
  }
713
714
0
  if (call_result == SUCCESS && Z_TYPE(retval) != IS_UNDEF && zval_is_true(&retval)) {
715
0
    stream->eof = 1;
716
0
  } else if (call_result == FAILURE) {
717
0
    php_error_docref(NULL, E_WARNING,
718
0
        "%s::" USERSTREAM_EOF " is not implemented! Assuming EOF",
719
0
        us->wrapper->classname);
720
721
0
    stream->eof = 1;
722
0
  }
723
724
0
  zval_ptr_dtor(&retval);
725
726
0
  return didread;
727
0
}
728
729
static int php_userstreamop_close(php_stream *stream, int close_handle)
730
0
{
731
0
  zval func_name;
732
0
  zval retval;
733
0
  php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
734
735
0
  assert(us != NULL);
736
737
0
  ZVAL_STRINGL(&func_name, USERSTREAM_CLOSE, sizeof(USERSTREAM_CLOSE)-1);
738
739
0
  call_user_function(NULL,
740
0
      Z_ISUNDEF(us->object)? NULL : &us->object,
741
0
      &func_name,
742
0
      &retval,
743
0
      0, NULL);
744
745
0
  zval_ptr_dtor(&retval);
746
0
  zval_ptr_dtor(&func_name);
747
748
0
  zval_ptr_dtor(&us->object);
749
0
  ZVAL_UNDEF(&us->object);
750
751
0
  efree(us);
752
753
0
  return 0;
754
0
}
755
756
static int php_userstreamop_flush(php_stream *stream)
757
0
{
758
0
  zval func_name;
759
0
  zval retval;
760
0
  int call_result;
761
0
  php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
762
763
0
  assert(us != NULL);
764
765
0
  ZVAL_STRINGL(&func_name, USERSTREAM_FLUSH, sizeof(USERSTREAM_FLUSH)-1);
766
767
0
  call_result = call_user_function(NULL,
768
0
      Z_ISUNDEF(us->object)? NULL : &us->object,
769
0
      &func_name,
770
0
      &retval,
771
0
      0, NULL);
772
773
0
  if (call_result == SUCCESS && Z_TYPE(retval) != IS_UNDEF && zval_is_true(&retval))
774
0
    call_result = 0;
775
0
  else
776
0
    call_result = -1;
777
778
0
  zval_ptr_dtor(&retval);
779
0
  zval_ptr_dtor(&func_name);
780
781
0
  return call_result;
782
0
}
783
784
static int php_userstreamop_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffs)
785
0
{
786
0
  zval func_name;
787
0
  zval retval;
788
0
  int call_result, ret;
789
0
  php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
790
0
  zval args[2];
791
792
0
  assert(us != NULL);
793
794
0
  ZVAL_STRINGL(&func_name, USERSTREAM_SEEK, sizeof(USERSTREAM_SEEK)-1);
795
796
0
  ZVAL_LONG(&args[0], offset);
797
0
  ZVAL_LONG(&args[1], whence);
798
799
0
  call_result = call_user_function_ex(NULL,
800
0
      Z_ISUNDEF(us->object)? NULL : &us->object,
801
0
      &func_name,
802
0
      &retval,
803
0
      2, args,
804
0
      0, NULL);
805
806
0
  zval_ptr_dtor(&args[0]);
807
0
  zval_ptr_dtor(&args[1]);
808
0
  zval_ptr_dtor(&func_name);
809
810
0
  if (call_result == FAILURE) {
811
    /* stream_seek is not implemented, so disable seeks for this stream */
812
0
    stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
813
    /* there should be no retval to clean up */
814
815
0
    zval_ptr_dtor(&retval);
816
817
0
    return -1;
818
0
  } else if (call_result == SUCCESS && Z_TYPE(retval) != IS_UNDEF && zval_is_true(&retval)) {
819
0
    ret = 0;
820
0
  } else {
821
0
    ret = -1;
822
0
  }
823
824
0
  zval_ptr_dtor(&retval);
825
0
  ZVAL_UNDEF(&retval);
826
827
0
  if (ret) {
828
0
    return ret;
829
0
  }
830
831
  /* now determine where we are */
832
0
  ZVAL_STRINGL(&func_name, USERSTREAM_TELL, sizeof(USERSTREAM_TELL)-1);
833
834
0
  call_result = call_user_function(NULL,
835
0
    Z_ISUNDEF(us->object)? NULL : &us->object,
836
0
    &func_name,
837
0
    &retval,
838
0
    0, NULL);
839
840
0
  if (call_result == SUCCESS && Z_TYPE(retval) == IS_LONG) {
841
0
    *newoffs = Z_LVAL(retval);
842
0
    ret = 0;
843
0
  } else if (call_result == FAILURE) {
844
0
    php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_TELL " is not implemented!", us->wrapper->classname);
845
0
    ret = -1;
846
0
  } else {
847
0
    ret = -1;
848
0
  }
849
850
0
  zval_ptr_dtor(&retval);
851
0
  zval_ptr_dtor(&func_name);
852
0
  return ret;
853
0
}
854
855
/* parse the return value from one of the stat functions and store the
856
 * relevant fields into the statbuf provided */
857
static int statbuf_from_array(zval *array, php_stream_statbuf *ssb)
858
0
{
859
0
  zval *elem;
860
861
0
#define STAT_PROP_ENTRY_EX(name, name2)                        \
862
0
  if (NULL != (elem = zend_hash_str_find(Z_ARRVAL_P(array), #name, sizeof(#name)-1))) {     \
863
0
    ssb->sb.st_##name2 = zval_get_long(elem);                                                      \
864
0
  }
865
866
0
#define STAT_PROP_ENTRY(name) STAT_PROP_ENTRY_EX(name,name)
867
868
0
  memset(ssb, 0, sizeof(php_stream_statbuf));
869
0
  STAT_PROP_ENTRY(dev);
870
0
  STAT_PROP_ENTRY(ino);
871
0
  STAT_PROP_ENTRY(mode);
872
0
  STAT_PROP_ENTRY(nlink);
873
0
  STAT_PROP_ENTRY(uid);
874
0
  STAT_PROP_ENTRY(gid);
875
0
#if HAVE_STRUCT_STAT_ST_RDEV
876
0
  STAT_PROP_ENTRY(rdev);
877
0
#endif
878
0
  STAT_PROP_ENTRY(size);
879
0
  STAT_PROP_ENTRY(atime);
880
0
  STAT_PROP_ENTRY(mtime);
881
0
  STAT_PROP_ENTRY(ctime);
882
0
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
883
0
  STAT_PROP_ENTRY(blksize);
884
0
#endif
885
0
#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
886
0
  STAT_PROP_ENTRY(blocks);
887
0
#endif
888
889
0
#undef STAT_PROP_ENTRY
890
0
#undef STAT_PROP_ENTRY_EX
891
0
  return SUCCESS;
892
0
}
893
894
static int php_userstreamop_stat(php_stream *stream, php_stream_statbuf *ssb)
895
0
{
896
0
  zval func_name;
897
0
  zval retval;
898
0
  int call_result;
899
0
  php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
900
0
  int ret = -1;
901
902
0
  ZVAL_STRINGL(&func_name, USERSTREAM_STAT, sizeof(USERSTREAM_STAT)-1);
903
904
0
  call_result = call_user_function(NULL,
905
0
      Z_ISUNDEF(us->object)? NULL : &us->object,
906
0
      &func_name,
907
0
      &retval,
908
0
      0, NULL);
909
910
0
  if (call_result == SUCCESS && Z_TYPE(retval) == IS_ARRAY) {
911
0
    if (SUCCESS == statbuf_from_array(&retval, ssb))
912
0
      ret = 0;
913
0
  } else {
914
0
    if (call_result == FAILURE) {
915
0
      php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_STAT " is not implemented!",
916
0
          us->wrapper->classname);
917
0
    }
918
0
  }
919
920
0
  zval_ptr_dtor(&retval);
921
0
  zval_ptr_dtor(&func_name);
922
923
0
  return ret;
924
0
}
925
926
927
0
static int php_userstreamop_set_option(php_stream *stream, int option, int value, void *ptrparam) {
928
0
  zval func_name;
929
0
  zval retval;
930
0
  int call_result;
931
0
  php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
932
0
  int ret = PHP_STREAM_OPTION_RETURN_NOTIMPL;
933
0
  zval args[3];
934
935
0
  switch (option) {
936
0
  case PHP_STREAM_OPTION_CHECK_LIVENESS:
937
0
    ZVAL_STRINGL(&func_name, USERSTREAM_EOF, sizeof(USERSTREAM_EOF)-1);
938
0
    call_result = call_user_function(NULL, Z_ISUNDEF(us->object)? NULL : &us->object, &func_name, &retval, 0, NULL);
939
0
    if (call_result == SUCCESS && (Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE)) {
940
0
      ret = zval_is_true(&retval) ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;
941
0
    } else {
942
0
      ret = PHP_STREAM_OPTION_RETURN_ERR;
943
0
      php_error_docref(NULL, E_WARNING,
944
0
          "%s::" USERSTREAM_EOF " is not implemented! Assuming EOF",
945
0
          us->wrapper->classname);
946
0
    }
947
0
    zval_ptr_dtor(&retval);
948
0
    zval_ptr_dtor(&func_name);
949
0
    break;
950
951
0
  case PHP_STREAM_OPTION_LOCKING:
952
0
    ZVAL_LONG(&args[0], 0);
953
954
0
    if (value & LOCK_NB) {
955
0
      Z_LVAL_P(&args[0]) |= PHP_LOCK_NB;
956
0
    }
957
0
    switch(value & ~LOCK_NB) {
958
0
    case LOCK_SH:
959
0
      Z_LVAL_P(&args[0]) |= PHP_LOCK_SH;
960
0
      break;
961
0
    case LOCK_EX:
962
0
      Z_LVAL_P(&args[0]) |= PHP_LOCK_EX;
963
0
      break;
964
0
    case LOCK_UN:
965
0
      Z_LVAL_P(&args[0]) |= PHP_LOCK_UN;
966
0
      break;
967
0
    }
968
969
    /* TODO wouldblock */
970
0
    ZVAL_STRINGL(&func_name, USERSTREAM_LOCK, sizeof(USERSTREAM_LOCK)-1);
971
972
0
    call_result = call_user_function_ex(NULL,
973
0
            Z_ISUNDEF(us->object)? NULL : &us->object,
974
0
            &func_name,
975
0
            &retval,
976
0
            1, args, 0, NULL);
977
978
0
    if (call_result == SUCCESS && (Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE)) {
979
0
      ret = (Z_TYPE(retval) == IS_FALSE);
980
0
    } else if (call_result == FAILURE) {
981
0
      if (value == 0) {
982
          /* lock support test (TODO: more check) */
983
0
        ret = PHP_STREAM_OPTION_RETURN_OK;
984
0
      } else {
985
0
        php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_LOCK " is not implemented!",
986
0
                 us->wrapper->classname);
987
0
        ret = PHP_STREAM_OPTION_RETURN_ERR;
988
0
      }
989
0
    }
990
991
0
    zval_ptr_dtor(&retval);
992
0
    zval_ptr_dtor(&func_name);
993
0
    zval_ptr_dtor(&args[0]);
994
0
    break;
995
996
0
  case PHP_STREAM_OPTION_TRUNCATE_API:
997
0
    ZVAL_STRINGL(&func_name, USERSTREAM_TRUNCATE, sizeof(USERSTREAM_TRUNCATE)-1);
998
999
0
    switch (value) {
1000
0
    case PHP_STREAM_TRUNCATE_SUPPORTED:
1001
0
      if (zend_is_callable_ex(&func_name,
1002
0
          Z_ISUNDEF(us->object)? NULL : Z_OBJ(us->object),
1003
0
          0, NULL, NULL, NULL))
1004
0
        ret = PHP_STREAM_OPTION_RETURN_OK;
1005
0
      else
1006
0
        ret = PHP_STREAM_OPTION_RETURN_ERR;
1007
0
      break;
1008
1009
0
    case PHP_STREAM_TRUNCATE_SET_SIZE: {
1010
0
      ptrdiff_t new_size = *(ptrdiff_t*) ptrparam;
1011
0
      if (new_size >= 0 && new_size <= (ptrdiff_t)LONG_MAX) {
1012
0
        ZVAL_LONG(&args[0], (zend_long)new_size);
1013
0
        call_result = call_user_function_ex(NULL,
1014
0
                Z_ISUNDEF(us->object)? NULL : &us->object,
1015
0
                &func_name,
1016
0
                &retval,
1017
0
                1, args, 0, NULL);
1018
0
        if (call_result == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
1019
0
          if (Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE) {
1020
0
            ret = (Z_TYPE(retval) == IS_TRUE) ? PHP_STREAM_OPTION_RETURN_OK :
1021
0
                         PHP_STREAM_OPTION_RETURN_ERR;
1022
0
          } else {
1023
0
            php_error_docref(NULL, E_WARNING,
1024
0
                "%s::" USERSTREAM_TRUNCATE " did not return a boolean!",
1025
0
                us->wrapper->classname);
1026
0
          }
1027
0
        } else {
1028
0
          php_error_docref(NULL, E_WARNING,
1029
0
              "%s::" USERSTREAM_TRUNCATE " is not implemented!",
1030
0
              us->wrapper->classname);
1031
0
        }
1032
0
        zval_ptr_dtor(&retval);
1033
0
        zval_ptr_dtor(&args[0]);
1034
0
      } else { /* bad new size */
1035
0
        ret = PHP_STREAM_OPTION_RETURN_ERR;
1036
0
      }
1037
0
      break;
1038
0
    }
1039
0
    }
1040
0
    zval_ptr_dtor(&func_name);
1041
0
    break;
1042
1043
0
  case PHP_STREAM_OPTION_READ_BUFFER:
1044
0
  case PHP_STREAM_OPTION_WRITE_BUFFER:
1045
0
  case PHP_STREAM_OPTION_READ_TIMEOUT:
1046
0
  case PHP_STREAM_OPTION_BLOCKING: {
1047
1048
0
    ZVAL_STRINGL(&func_name, USERSTREAM_SET_OPTION, sizeof(USERSTREAM_SET_OPTION)-1);
1049
1050
0
    ZVAL_LONG(&args[0], option);
1051
0
    ZVAL_NULL(&args[1]);
1052
0
    ZVAL_NULL(&args[2]);
1053
1054
0
    switch(option) {
1055
0
    case PHP_STREAM_OPTION_READ_BUFFER:
1056
0
    case PHP_STREAM_OPTION_WRITE_BUFFER:
1057
0
      ZVAL_LONG(&args[1], value);
1058
0
      if (ptrparam) {
1059
0
        ZVAL_LONG(&args[2], *(long *)ptrparam);
1060
0
      } else {
1061
0
        ZVAL_LONG(&args[2], BUFSIZ);
1062
0
      }
1063
0
      break;
1064
0
    case PHP_STREAM_OPTION_READ_TIMEOUT: {
1065
0
      struct timeval tv = *(struct timeval*)ptrparam;
1066
0
      ZVAL_LONG(&args[1], tv.tv_sec);
1067
0
      ZVAL_LONG(&args[2], tv.tv_usec);
1068
0
      break;
1069
0
      }
1070
0
    case PHP_STREAM_OPTION_BLOCKING:
1071
0
      ZVAL_LONG(&args[1], value);
1072
0
      break;
1073
0
    default:
1074
0
      break;
1075
0
    }
1076
1077
0
    call_result = call_user_function_ex(NULL,
1078
0
      Z_ISUNDEF(us->object)? NULL : &us->object,
1079
0
      &func_name,
1080
0
      &retval,
1081
0
      3, args, 0, NULL);
1082
1083
0
    if (call_result == FAILURE) {
1084
0
      php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_SET_OPTION " is not implemented!",
1085
0
          us->wrapper->classname);
1086
0
      ret = PHP_STREAM_OPTION_RETURN_ERR;
1087
0
    } else if (zend_is_true(&retval)) {
1088
0
      ret = PHP_STREAM_OPTION_RETURN_OK;
1089
0
    } else {
1090
0
      ret = PHP_STREAM_OPTION_RETURN_ERR;
1091
0
    }
1092
1093
0
    zval_ptr_dtor(&retval);
1094
0
    zval_ptr_dtor(&args[2]);
1095
0
    zval_ptr_dtor(&args[1]);
1096
0
    zval_ptr_dtor(&args[0]);
1097
0
    zval_ptr_dtor(&func_name);
1098
1099
0
    break;
1100
0
    }
1101
0
  }
1102
1103
0
  return ret;
1104
0
}
1105
1106
1107
static int user_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int options, php_stream_context *context)
1108
0
{
1109
0
  struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
1110
0
  zval zfuncname, zretval;
1111
0
  zval args[1];
1112
0
  int call_result;
1113
0
  zval object;
1114
0
  int ret = 0;
1115
1116
  /* create an instance of our class */
1117
0
  user_stream_create_object(uwrap, context, &object);
1118
0
  if (Z_TYPE(object) == IS_UNDEF) {
1119
0
    return ret;
1120
0
  }
1121
1122
  /* call the unlink method */
1123
0
  ZVAL_STRING(&args[0], url);
1124
1125
0
  ZVAL_STRING(&zfuncname, USERSTREAM_UNLINK);
1126
1127
0
  call_result = call_user_function_ex(NULL,
1128
0
      &object,
1129
0
      &zfuncname,
1130
0
      &zretval,
1131
0
      1, args,
1132
0
      0, NULL );
1133
1134
0
  if (call_result == SUCCESS && (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE)) {
1135
0
    ret = (Z_TYPE(zretval) == IS_TRUE);
1136
0
  } else if (call_result == FAILURE) {
1137
0
    php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_UNLINK " is not implemented!", uwrap->classname);
1138
0
  }
1139
1140
  /* clean up */
1141
0
  zval_ptr_dtor(&object);
1142
0
  zval_ptr_dtor(&zretval);
1143
0
  zval_ptr_dtor(&zfuncname);
1144
1145
0
  zval_ptr_dtor(&args[0]);
1146
1147
0
  return ret;
1148
0
}
1149
1150
static int user_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from, const char *url_to,
1151
                 int options, php_stream_context *context)
1152
0
{
1153
0
  struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
1154
0
  zval zfuncname, zretval;
1155
0
  zval args[2];
1156
0
  int call_result;
1157
0
  zval object;
1158
0
  int ret = 0;
1159
1160
  /* create an instance of our class */
1161
0
  user_stream_create_object(uwrap, context, &object);
1162
0
  if (Z_TYPE(object) == IS_UNDEF) {
1163
0
    return ret;
1164
0
  }
1165
1166
  /* call the rename method */
1167
0
  ZVAL_STRING(&args[0], url_from);
1168
0
  ZVAL_STRING(&args[1], url_to);
1169
1170
0
  ZVAL_STRING(&zfuncname, USERSTREAM_RENAME);
1171
1172
0
  call_result = call_user_function_ex(NULL,
1173
0
      &object,
1174
0
      &zfuncname,
1175
0
      &zretval,
1176
0
      2, args,
1177
0
      0, NULL );
1178
1179
0
  if (call_result == SUCCESS && (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE)) {
1180
0
    ret = (Z_TYPE(zretval) == IS_TRUE);
1181
0
  } else if (call_result == FAILURE) {
1182
0
    php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_RENAME " is not implemented!", uwrap->classname);
1183
0
  }
1184
1185
  /* clean up */
1186
0
  zval_ptr_dtor(&object);
1187
0
  zval_ptr_dtor(&zretval);
1188
1189
0
  zval_ptr_dtor(&zfuncname);
1190
0
  zval_ptr_dtor(&args[1]);
1191
0
  zval_ptr_dtor(&args[0]);
1192
1193
0
  return ret;
1194
0
}
1195
1196
static int user_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url, int mode,
1197
                int options, php_stream_context *context)
1198
0
{
1199
0
  struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
1200
0
  zval zfuncname, zretval;
1201
0
  zval args[3];
1202
0
  int call_result;
1203
0
  zval object;
1204
0
  int ret = 0;
1205
1206
  /* create an instance of our class */
1207
0
  user_stream_create_object(uwrap, context, &object);
1208
0
  if (Z_TYPE(object) == IS_UNDEF) {
1209
0
    return ret;
1210
0
  }
1211
1212
  /* call the mkdir method */
1213
0
  ZVAL_STRING(&args[0], url);
1214
0
  ZVAL_LONG(&args[1], mode);
1215
0
  ZVAL_LONG(&args[2], options);
1216
1217
0
  ZVAL_STRING(&zfuncname, USERSTREAM_MKDIR);
1218
1219
0
  call_result = call_user_function_ex(NULL,
1220
0
      &object,
1221
0
      &zfuncname,
1222
0
      &zretval,
1223
0
      3, args,
1224
0
      0, NULL );
1225
1226
0
  if (call_result == SUCCESS && (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE)) {
1227
0
    ret = (Z_TYPE(zretval) == IS_TRUE);
1228
0
  } else if (call_result == FAILURE) {
1229
0
    php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_MKDIR " is not implemented!", uwrap->classname);
1230
0
  }
1231
1232
  /* clean up */
1233
0
  zval_ptr_dtor(&object);
1234
0
  zval_ptr_dtor(&zretval);
1235
1236
0
  zval_ptr_dtor(&zfuncname);
1237
0
  zval_ptr_dtor(&args[2]);
1238
0
  zval_ptr_dtor(&args[1]);
1239
0
  zval_ptr_dtor(&args[0]);
1240
1241
0
  return ret;
1242
0
}
1243
1244
static int user_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url,
1245
                int options, php_stream_context *context)
1246
0
{
1247
0
  struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
1248
0
  zval zfuncname, zretval;
1249
0
  zval args[2];
1250
0
  int call_result;
1251
0
  zval object;
1252
0
  int ret = 0;
1253
1254
  /* create an instance of our class */
1255
0
  user_stream_create_object(uwrap, context, &object);
1256
0
  if (Z_TYPE(object) == IS_UNDEF) {
1257
0
    return ret;
1258
0
  }
1259
1260
  /* call the rmdir method */
1261
0
  ZVAL_STRING(&args[0], url);
1262
0
  ZVAL_LONG(&args[1], options);
1263
1264
0
  ZVAL_STRING(&zfuncname, USERSTREAM_RMDIR);
1265
1266
0
  call_result = call_user_function_ex(NULL,
1267
0
      &object,
1268
0
      &zfuncname,
1269
0
      &zretval,
1270
0
      2, args,
1271
0
      0, NULL );
1272
1273
0
  if (call_result == SUCCESS && (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE)) {
1274
0
    ret = (Z_TYPE(zretval) == IS_TRUE);
1275
0
  } else if (call_result == FAILURE) {
1276
0
    php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_RMDIR " is not implemented!", uwrap->classname);
1277
0
  }
1278
1279
  /* clean up */
1280
0
  zval_ptr_dtor(&object);
1281
0
  zval_ptr_dtor(&zretval);
1282
1283
0
  zval_ptr_dtor(&zfuncname);
1284
0
  zval_ptr_dtor(&args[1]);
1285
0
  zval_ptr_dtor(&args[0]);
1286
1287
0
  return ret;
1288
0
}
1289
1290
static int user_wrapper_metadata(php_stream_wrapper *wrapper, const char *url, int option,
1291
                 void *value, php_stream_context *context)
1292
0
{
1293
0
  struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
1294
0
  zval zfuncname, zretval;
1295
0
  zval args[3];
1296
0
  int call_result;
1297
0
  zval object;
1298
0
  int ret = 0;
1299
1300
0
  switch(option) {
1301
0
    case PHP_STREAM_META_TOUCH:
1302
0
      array_init(&args[2]);
1303
0
      if(value) {
1304
0
        struct utimbuf *newtime = (struct utimbuf *)value;
1305
0
        add_index_long(&args[2], 0, newtime->modtime);
1306
0
        add_index_long(&args[2], 1, newtime->actime);
1307
0
      }
1308
0
      break;
1309
0
    case PHP_STREAM_META_GROUP:
1310
0
    case PHP_STREAM_META_OWNER:
1311
0
    case PHP_STREAM_META_ACCESS:
1312
0
      ZVAL_LONG(&args[2], *(long *)value);
1313
0
      break;
1314
0
    case PHP_STREAM_META_GROUP_NAME:
1315
0
    case PHP_STREAM_META_OWNER_NAME:
1316
0
      ZVAL_STRING(&args[2], value);
1317
0
      break;
1318
0
    default:
1319
0
      php_error_docref(NULL, E_WARNING, "Unknown option %d for " USERSTREAM_METADATA, option);
1320
0
      zval_ptr_dtor(&args[2]);
1321
0
      return ret;
1322
0
  }
1323
1324
  /* create an instance of our class */
1325
0
  user_stream_create_object(uwrap, context, &object);
1326
0
  if (Z_TYPE(object) == IS_UNDEF) {
1327
0
    zval_ptr_dtor(&args[2]);
1328
0
    return ret;
1329
0
  }
1330
1331
  /* call the mkdir method */
1332
0
  ZVAL_STRING(&args[0], url);
1333
0
  ZVAL_LONG(&args[1], option);
1334
1335
0
  ZVAL_STRING(&zfuncname, USERSTREAM_METADATA);
1336
1337
0
  call_result = call_user_function_ex(NULL,
1338
0
      &object,
1339
0
      &zfuncname,
1340
0
      &zretval,
1341
0
      3, args,
1342
0
      0, NULL );
1343
1344
0
  if (call_result == SUCCESS && (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE)) {
1345
0
    ret = Z_TYPE(zretval) == IS_TRUE;
1346
0
  } else if (call_result == FAILURE) {
1347
0
    php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_METADATA " is not implemented!", uwrap->classname);
1348
0
  }
1349
1350
  /* clean up */
1351
0
  zval_ptr_dtor(&object);
1352
0
  zval_ptr_dtor(&zretval);
1353
1354
0
  zval_ptr_dtor(&zfuncname);
1355
0
  zval_ptr_dtor(&args[0]);
1356
0
  zval_ptr_dtor(&args[1]);
1357
0
  zval_ptr_dtor(&args[2]);
1358
1359
0
  return ret;
1360
0
}
1361
1362
1363
static int user_wrapper_stat_url(php_stream_wrapper *wrapper, const char *url, int flags,
1364
                 php_stream_statbuf *ssb, php_stream_context *context)
1365
0
{
1366
0
  struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
1367
0
  zval zfuncname, zretval;
1368
0
  zval args[2];
1369
0
  int call_result;
1370
0
  zval object;
1371
0
  int ret = -1;
1372
1373
  /* create an instance of our class */
1374
0
  user_stream_create_object(uwrap, context, &object);
1375
0
  if (Z_TYPE(object) == IS_UNDEF) {
1376
0
    return ret;
1377
0
  }
1378
1379
  /* call it's stat_url method - set up params first */
1380
0
  ZVAL_STRING(&args[0], url);
1381
0
  ZVAL_LONG(&args[1], flags);
1382
1383
0
  ZVAL_STRING(&zfuncname, USERSTREAM_STATURL);
1384
1385
0
  call_result = call_user_function_ex(NULL,
1386
0
      &object,
1387
0
      &zfuncname,
1388
0
      &zretval,
1389
0
      2, args,
1390
0
      0, NULL );
1391
1392
0
  if (call_result == SUCCESS && Z_TYPE(zretval) == IS_ARRAY) {
1393
    /* We got the info we needed */
1394
0
    if (SUCCESS == statbuf_from_array(&zretval, ssb))
1395
0
      ret = 0;
1396
0
  } else {
1397
0
    if (call_result == FAILURE) {
1398
0
      php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_STATURL " is not implemented!",
1399
0
          uwrap->classname);
1400
0
    }
1401
0
  }
1402
1403
  /* clean up */
1404
0
  zval_ptr_dtor(&object);
1405
0
  zval_ptr_dtor(&zretval);
1406
1407
0
  zval_ptr_dtor(&zfuncname);
1408
0
  zval_ptr_dtor(&args[1]);
1409
0
  zval_ptr_dtor(&args[0]);
1410
1411
0
  return ret;
1412
1413
0
}
1414
1415
static ssize_t php_userstreamop_readdir(php_stream *stream, char *buf, size_t count)
1416
0
{
1417
0
  zval func_name;
1418
0
  zval retval;
1419
0
  int call_result;
1420
0
  size_t didread = 0;
1421
0
  php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
1422
0
  php_stream_dirent *ent = (php_stream_dirent*)buf;
1423
1424
  /* avoid problems if someone mis-uses the stream */
1425
0
  if (count != sizeof(php_stream_dirent))
1426
0
    return -1;
1427
1428
0
  ZVAL_STRINGL(&func_name, USERSTREAM_DIR_READ, sizeof(USERSTREAM_DIR_READ)-1);
1429
1430
0
  call_result = call_user_function(NULL,
1431
0
      Z_ISUNDEF(us->object)? NULL : &us->object,
1432
0
      &func_name,
1433
0
      &retval,
1434
0
      0, NULL);
1435
1436
0
  if (call_result == SUCCESS && Z_TYPE(retval) != IS_FALSE && Z_TYPE(retval) != IS_TRUE) {
1437
0
    convert_to_string(&retval);
1438
0
    PHP_STRLCPY(ent->d_name, Z_STRVAL(retval), sizeof(ent->d_name), Z_STRLEN(retval));
1439
1440
0
    didread = sizeof(php_stream_dirent);
1441
0
  } else if (call_result == FAILURE) {
1442
0
    php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_DIR_READ " is not implemented!",
1443
0
        us->wrapper->classname);
1444
0
  }
1445
1446
0
  zval_ptr_dtor(&retval);
1447
0
  zval_ptr_dtor(&func_name);
1448
1449
0
  return didread;
1450
0
}
1451
1452
static int php_userstreamop_closedir(php_stream *stream, int close_handle)
1453
0
{
1454
0
  zval func_name;
1455
0
  zval retval;
1456
0
  php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
1457
1458
0
  assert(us != NULL);
1459
1460
0
  ZVAL_STRINGL(&func_name, USERSTREAM_DIR_CLOSE, sizeof(USERSTREAM_DIR_CLOSE)-1);
1461
1462
0
  call_user_function(NULL,
1463
0
      Z_ISUNDEF(us->object)? NULL : &us->object,
1464
0
      &func_name,
1465
0
      &retval,
1466
0
      0, NULL);
1467
1468
0
  zval_ptr_dtor(&retval);
1469
0
  zval_ptr_dtor(&func_name);
1470
0
  zval_ptr_dtor(&us->object);
1471
0
  ZVAL_UNDEF(&us->object);
1472
1473
0
  efree(us);
1474
1475
0
  return 0;
1476
0
}
1477
1478
static int php_userstreamop_rewinddir(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffs)
1479
0
{
1480
0
  zval func_name;
1481
0
  zval retval;
1482
0
  php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
1483
1484
0
  ZVAL_STRINGL(&func_name, USERSTREAM_DIR_REWIND, sizeof(USERSTREAM_DIR_REWIND)-1);
1485
1486
0
  call_user_function(NULL,
1487
0
      Z_ISUNDEF(us->object)? NULL : &us->object,
1488
0
      &func_name,
1489
0
      &retval,
1490
0
      0, NULL);
1491
1492
0
  zval_ptr_dtor(&retval);
1493
0
  zval_ptr_dtor(&func_name);
1494
1495
0
  return 0;
1496
1497
0
}
1498
1499
static int php_userstreamop_cast(php_stream *stream, int castas, void **retptr)
1500
0
{
1501
0
  php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
1502
0
  zval func_name;
1503
0
  zval retval;
1504
0
  zval args[1];
1505
0
  php_stream * intstream = NULL;
1506
0
  int call_result;
1507
0
  int ret = FAILURE;
1508
1509
0
  ZVAL_STRINGL(&func_name, USERSTREAM_CAST, sizeof(USERSTREAM_CAST)-1);
1510
1511
0
  switch(castas) {
1512
0
  case PHP_STREAM_AS_FD_FOR_SELECT:
1513
0
    ZVAL_LONG(&args[0], PHP_STREAM_AS_FD_FOR_SELECT);
1514
0
    break;
1515
0
  default:
1516
0
    ZVAL_LONG(&args[0], PHP_STREAM_AS_STDIO);
1517
0
    break;
1518
0
  }
1519
1520
0
  call_result = call_user_function_ex(NULL,
1521
0
      Z_ISUNDEF(us->object)? NULL : &us->object,
1522
0
      &func_name,
1523
0
      &retval,
1524
0
      1, args, 0, NULL);
1525
1526
0
  do {
1527
0
    if (call_result == FAILURE) {
1528
0
      php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_CAST " is not implemented!",
1529
0
          us->wrapper->classname);
1530
0
      break;
1531
0
    }
1532
0
    if (!zend_is_true(&retval)) {
1533
0
      break;
1534
0
    }
1535
0
    php_stream_from_zval_no_verify(intstream, &retval);
1536
0
    if (!intstream) {
1537
0
      php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_CAST " must return a stream resource",
1538
0
          us->wrapper->classname);
1539
0
      break;
1540
0
    }
1541
0
    if (intstream == stream) {
1542
0
      php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_CAST " must not return itself",
1543
0
          us->wrapper->classname);
1544
0
      intstream = NULL;
1545
0
      break;
1546
0
    }
1547
0
    ret = php_stream_cast(intstream, castas, retptr, 1);
1548
0
  } while (0);
1549
1550
0
  zval_ptr_dtor(&retval);
1551
0
  zval_ptr_dtor(&func_name);
1552
0
  zval_ptr_dtor(&args[0]);
1553
1554
0
  return ret;
1555
0
}
1556
1557
const php_stream_ops php_stream_userspace_ops = {
1558
  php_userstreamop_write, php_userstreamop_read,
1559
  php_userstreamop_close, php_userstreamop_flush,
1560
  "user-space",
1561
  php_userstreamop_seek,
1562
  php_userstreamop_cast,
1563
  php_userstreamop_stat,
1564
  php_userstreamop_set_option,
1565
};
1566
1567
const php_stream_ops php_stream_userspace_dir_ops = {
1568
  NULL, /* write */
1569
  php_userstreamop_readdir,
1570
  php_userstreamop_closedir,
1571
  NULL, /* flush */
1572
  "user-space-dir",
1573
  php_userstreamop_rewinddir,
1574
  NULL, /* cast */
1575
  NULL, /* stat */
1576
  NULL  /* set_option */
1577
};