Coverage Report

Created: 2025-06-13 06:43

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