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 | Author: Wez Furlong <wez@thebrainroom.com> |
14 +----------------------------------------------------------------------+
15 */
16
17 /* This module implements a SafeArray proxy which is used internally
18 * by the engine when resolving multi-dimensional array accesses on
19 * SafeArray types.
20 * In addition, the proxy is now able to handle properties of COM objects
21 * that smell like PHP arrays.
22 * */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include "php.h"
29 #include "php_ini.h"
30 #include "ext/standard/info.h"
31 #include "php_com_dotnet.h"
32 #include "php_com_dotnet_internal.h"
33 #include "Zend/zend_exceptions.h"
34
35 typedef struct {
36 zend_object std;
37 /* the object we a proxying for; we hold a refcount to it */
38 php_com_dotnet_object *obj;
39
40 /* how many dimensions we are indirecting to get into this element */
41 LONG dimensions;
42
43 /* this is an array whose size_is(dimensions) */
44 zval *indices;
45
46 } php_com_saproxy;
47
48 typedef struct {
49 zend_object_iterator iter;
50 zval proxy_obj;
51 zval data;
52 php_com_saproxy *proxy;
53 LONG key;
54 LONG imin, imax;
55 LONG *indices;
56 } php_com_saproxy_iter;
57
58 #define SA_FETCH(zv) (php_com_saproxy*)Z_OBJ_P(zv)
59
clone_indices(php_com_saproxy * dest,php_com_saproxy * src,int ndims)60 static inline void clone_indices(php_com_saproxy *dest, php_com_saproxy *src, int ndims)
61 {
62 int i;
63
64 for (i = 0; i < ndims; i++) {
65 ZVAL_DUP(&dest->indices[i], &src->indices[i]);
66 }
67 }
68
saproxy_property_read(zend_object * object,zend_string * member,int type,void ** cache_slot,zval * rv)69 static zval *saproxy_property_read(zend_object *object, zend_string *member, int type, void **cache_slot, zval *rv)
70 {
71 ZVAL_NULL(rv);
72
73 php_com_throw_exception(E_INVALIDARG, "safearray has no properties");
74
75 return rv;
76 }
77
saproxy_property_write(zend_object * object,zend_string * member,zval * value,void ** cache_slot)78 static zval *saproxy_property_write(zend_object *object, zend_string *member, zval *value, void **cache_slot)
79 {
80 php_com_throw_exception(E_INVALIDARG, "safearray has no properties");
81 return value;
82 }
83
saproxy_read_dimension(zend_object * object,zval * offset,int type,zval * rv)84 static zval *saproxy_read_dimension(zend_object *object, zval *offset, int type, zval *rv)
85 {
86 php_com_saproxy *proxy = (php_com_saproxy*) object;
87 UINT dims, i;
88 SAFEARRAY *sa;
89 LONG ubound, lbound;
90 HRESULT res;
91
92 ZVAL_NULL(rv);
93
94 if (V_VT(&proxy->obj->v) == VT_DISPATCH) {
95 VARIANT v;
96 zval *args;
97
98 /* prop-get using first dimension as the property name,
99 * all subsequent dimensions and the offset as parameters */
100
101 args = safe_emalloc(proxy->dimensions + 1, sizeof(zval), 0);
102
103 for (i = 1; i < (UINT) proxy->dimensions; i++) {
104 args[i-1] = proxy->indices[i];
105 }
106 ZVAL_COPY_VALUE(&args[i-1], offset);
107
108 if (!try_convert_to_string(&proxy->indices[0])) {
109 efree(args);
110 return rv;
111 }
112 VariantInit(&v);
113
114 res = php_com_do_invoke(proxy->obj, Z_STRVAL(proxy->indices[0]),
115 Z_STRLEN(proxy->indices[0]), DISPATCH_METHOD|DISPATCH_PROPERTYGET, &v,
116 proxy->dimensions, args, 0);
117
118 efree(args);
119
120 if (res == SUCCESS) {
121 php_com_zval_from_variant(rv, &v, proxy->obj->code_page);
122 VariantClear(&v);
123 } else if (res == DISP_E_BADPARAMCOUNT) {
124 /* return another proxy */
125 php_com_saproxy_create(object, rv, offset);
126 }
127
128 return rv;
129
130 } else if (!V_ISARRAY(&proxy->obj->v)) {
131 php_com_throw_exception(E_INVALIDARG, "invalid read from com proxy object");
132 return rv;
133 }
134
135 /* the SafeArray case */
136
137 /* offset/index must be an integer */
138 convert_to_long(offset);
139
140 sa = V_ARRAY(&proxy->obj->v);
141 dims = SafeArrayGetDim(sa);
142
143 if ((UINT) proxy->dimensions >= dims) {
144 /* too many dimensions */
145 php_com_throw_exception(E_INVALIDARG, "too many dimensions!");
146 return rv;
147 }
148
149 /* bounds check */
150 SafeArrayGetLBound(sa, proxy->dimensions, &lbound);
151 SafeArrayGetUBound(sa, proxy->dimensions, &ubound);
152
153 if (Z_LVAL_P(offset) < lbound || Z_LVAL_P(offset) > ubound) {
154 php_com_throw_exception(DISP_E_BADINDEX, "index out of bounds");
155 return rv;
156 }
157
158 if (dims - 1 == proxy->dimensions) {
159 LONG *indices;
160 VARTYPE vt;
161 VARIANT v;
162
163 VariantInit(&v);
164
165 /* we can return a real value */
166 indices = safe_emalloc(dims, sizeof(LONG), 0);
167
168 /* copy indices from proxy */
169 for (i = 0; i < dims; i++) {
170 convert_to_long(&proxy->indices[i]);
171 indices[i] = (LONG)Z_LVAL(proxy->indices[i]);
172 }
173
174 /* add user-supplied index */
175 indices[dims-1] = (LONG)Z_LVAL_P(offset);
176
177 /* now fetch the value */
178 if (FAILED(SafeArrayGetVartype(sa, &vt)) || vt == VT_EMPTY) {
179 vt = V_VT(&proxy->obj->v) & ~VT_ARRAY;
180 }
181
182 if (vt == VT_VARIANT) {
183 res = SafeArrayGetElement(sa, indices, &v);
184 } else {
185 V_VT(&v) = vt;
186 res = SafeArrayGetElement(sa, indices, &v.lVal);
187 }
188
189 efree(indices);
190
191 if (SUCCEEDED(res)) {
192 php_com_wrap_variant(rv, &v, proxy->obj->code_page);
193 } else {
194 php_com_throw_exception(res, NULL);
195 }
196
197 VariantClear(&v);
198
199 } else {
200 /* return another proxy */
201 php_com_saproxy_create(object, rv, offset);
202 }
203
204 return rv;
205 }
206
saproxy_write_dimension(zend_object * object,zval * offset,zval * value)207 static void saproxy_write_dimension(zend_object *object, zval *offset, zval *value)
208 {
209 php_com_saproxy *proxy = (php_com_saproxy*) object;
210 UINT dims, i;
211 HRESULT res;
212 VARIANT v;
213
214 if (V_VT(&proxy->obj->v) == VT_DISPATCH) {
215 /* We do a prop-set using the first dimension as the property name,
216 * all subsequent dimensions and offset as parameters, with value as
217 * the final value */
218 zval *args = safe_emalloc(proxy->dimensions + 2, sizeof(zval), 0);
219
220 for (i = 1; i < (UINT) proxy->dimensions; i++) {
221 ZVAL_COPY_VALUE(&args[i-1], &proxy->indices[i]);
222 }
223 ZVAL_COPY_VALUE(&args[i-1], offset);
224 ZVAL_COPY_VALUE(&args[i], value);
225
226 if (!try_convert_to_string(&proxy->indices[0])) {
227 efree(args);
228 return;
229 }
230 VariantInit(&v);
231 if (SUCCESS == php_com_do_invoke(proxy->obj, Z_STRVAL(proxy->indices[0]),
232 Z_STRLEN(proxy->indices[0]), DISPATCH_PROPERTYPUT, &v, proxy->dimensions + 1,
233 args, 0)) {
234 VariantClear(&v);
235 }
236
237 efree(args);
238
239 } else if (V_ISARRAY(&proxy->obj->v)) {
240 LONG *indices;
241 VARTYPE vt;
242
243 dims = SafeArrayGetDim(V_ARRAY(&proxy->obj->v));
244 indices = safe_emalloc(dims, sizeof(LONG), 0);
245 /* copy indices from proxy */
246 for (i = 0; i < dims; i++) {
247 convert_to_long(&proxy->indices[i]);
248 indices[i] = (LONG)Z_LVAL(proxy->indices[i]);
249 }
250
251 /* add user-supplied index */
252 convert_to_long(offset);
253 indices[dims-1] = (LONG)Z_LVAL_P(offset);
254
255 if (FAILED(SafeArrayGetVartype(V_ARRAY(&proxy->obj->v), &vt)) || vt == VT_EMPTY) {
256 vt = V_VT(&proxy->obj->v) & ~VT_ARRAY;
257 }
258
259 VariantInit(&v);
260 php_com_variant_from_zval(&v, value, proxy->obj->code_page);
261
262 if (V_VT(&v) != vt) {
263 VariantChangeType(&v, &v, 0, vt);
264 }
265
266 if (vt == VT_VARIANT) {
267 res = SafeArrayPutElement(V_ARRAY(&proxy->obj->v), indices, &v);
268 } else {
269 res = SafeArrayPutElement(V_ARRAY(&proxy->obj->v), indices, &v.lVal);
270 }
271
272 efree(indices);
273 VariantClear(&v);
274
275 if (FAILED(res)) {
276 php_com_throw_exception(res, NULL);
277 }
278 } else {
279 php_com_throw_exception(E_NOTIMPL, "invalid write to com proxy object");
280 }
281 }
282
saproxy_property_exists(zend_object * object,zend_string * member,int check_empty,void ** cache_slot)283 static int saproxy_property_exists(zend_object *object, zend_string *member, int check_empty, void **cache_slot)
284 {
285 /* no properties */
286 return 0;
287 }
288
saproxy_dimension_exists(zend_object * object,zval * member,int check_empty)289 static int saproxy_dimension_exists(zend_object *object, zval *member, int check_empty)
290 {
291 /* TODO Add support */
292 zend_throw_error(NULL, "Cannot check dimension on a COM object");
293 return 0;
294 }
295
saproxy_property_delete(zend_object * object,zend_string * member,void ** cache_slot)296 static void saproxy_property_delete(zend_object *object, zend_string *member, void **cache_slot)
297 {
298 zend_throw_error(NULL, "Cannot delete properties from a COM object");
299 }
300
saproxy_dimension_delete(zend_object * object,zval * offset)301 static void saproxy_dimension_delete(zend_object *object, zval *offset)
302 {
303 zend_throw_error(NULL, "Cannot delete dimension from a COM object");
304 }
305
saproxy_properties_get(zend_object * object)306 static HashTable *saproxy_properties_get(zend_object *object)
307 {
308 /* no properties */
309 return NULL;
310 }
311
saproxy_method_get(zend_object ** object,zend_string * name,const zval * key)312 static zend_function *saproxy_method_get(zend_object **object, zend_string *name, const zval *key)
313 {
314 /* no methods */
315 return NULL;
316 }
317
saproxy_constructor_get(zend_object * object)318 static zend_function *saproxy_constructor_get(zend_object *object)
319 {
320 /* user cannot instantiate */
321 return NULL;
322 }
323
saproxy_class_name_get(const zend_object * object)324 static zend_string* saproxy_class_name_get(const zend_object *object)
325 {
326 return zend_string_copy(php_com_saproxy_class_entry->name);
327 }
328
saproxy_objects_compare(zval * object1,zval * object2)329 static int saproxy_objects_compare(zval *object1, zval *object2)
330 {
331 ZEND_COMPARE_OBJECTS_FALLBACK(object1, object2);
332 return -1;
333 }
334
saproxy_object_cast(zend_object * readobj,zval * writeobj,int type)335 static int saproxy_object_cast(zend_object *readobj, zval *writeobj, int type)
336 {
337 return FAILURE;
338 }
339
saproxy_count_elements(zend_object * object,zend_long * count)340 static int saproxy_count_elements(zend_object *object, zend_long *count)
341 {
342 php_com_saproxy *proxy = (php_com_saproxy*) object;
343 LONG ubound, lbound;
344
345 if (!V_ISARRAY(&proxy->obj->v)) {
346 return FAILURE;
347 }
348
349 SafeArrayGetLBound(V_ARRAY(&proxy->obj->v), proxy->dimensions, &lbound);
350 SafeArrayGetUBound(V_ARRAY(&proxy->obj->v), proxy->dimensions, &ubound);
351
352 *count = ubound - lbound + 1;
353
354 return SUCCESS;
355 }
356
saproxy_free_storage(zend_object * object)357 static void saproxy_free_storage(zend_object *object)
358 {
359 php_com_saproxy *proxy = (php_com_saproxy *)object;
360 //??? int i;
361 //???
362 //??? for (i = 0; i < proxy->dimensions; i++) {
363 //??? if (proxy->indices) {
364 //??? FREE_ZVAL(proxy->indices[i]);
365 //??? }
366 //??? }
367
368 OBJ_RELEASE(&proxy->obj->zo);
369
370 zend_object_std_dtor(object);
371
372 efree(proxy->indices);
373 }
374
saproxy_clone(zend_object * object)375 static zend_object* saproxy_clone(zend_object *object)
376 {
377 php_com_saproxy *proxy = (php_com_saproxy *) object;
378 php_com_saproxy *cloneproxy;
379
380 cloneproxy = emalloc(sizeof(*cloneproxy));
381 memcpy(cloneproxy, proxy, sizeof(*cloneproxy));
382
383 GC_ADDREF(&cloneproxy->obj->zo);
384 cloneproxy->indices = safe_emalloc(cloneproxy->dimensions, sizeof(zval), 0);
385 clone_indices(cloneproxy, proxy, proxy->dimensions);
386
387 return &cloneproxy->std;
388 }
389
390 zend_object_handlers php_com_saproxy_handlers = {
391 0,
392 saproxy_free_storage,
393 zend_objects_destroy_object,
394 saproxy_clone,
395 saproxy_property_read,
396 saproxy_property_write,
397 saproxy_read_dimension,
398 saproxy_write_dimension,
399 NULL,
400 saproxy_property_exists,
401 saproxy_property_delete,
402 saproxy_dimension_exists,
403 saproxy_dimension_delete,
404 saproxy_properties_get,
405 saproxy_method_get,
406 saproxy_constructor_get,
407 saproxy_class_name_get,
408 saproxy_object_cast,
409 saproxy_count_elements,
410 NULL, /* get_debug_info */
411 NULL, /* get_closure */
412 NULL, /* get_gc */
413 NULL, /* do_operation */
414 saproxy_objects_compare, /* compare */
415 NULL, /* get_properties_for */
416 };
417
php_com_saproxy_create(zend_object * com_object,zval * proxy_out,zval * index)418 int php_com_saproxy_create(zend_object *com_object, zval *proxy_out, zval *index)
419 {
420 php_com_saproxy *proxy, *rel = NULL;
421
422 proxy = ecalloc(1, sizeof(*proxy));
423 proxy->dimensions = 1;
424
425 if (com_object->ce == php_com_saproxy_class_entry) {
426 rel = (php_com_saproxy*) com_object;
427 proxy->obj = rel->obj;
428 proxy->dimensions += rel->dimensions;
429 } else {
430 proxy->obj = (php_com_dotnet_object*) com_object;
431 }
432
433 GC_ADDREF(&proxy->obj->zo);
434 proxy->indices = safe_emalloc(proxy->dimensions, sizeof(zval), 0);
435
436 if (rel) {
437 clone_indices(proxy, rel, rel->dimensions);
438 }
439
440 ZVAL_DUP(&proxy->indices[proxy->dimensions-1], index);
441
442 zend_object_std_init(&proxy->std, php_com_saproxy_class_entry);
443 proxy->std.handlers = &php_com_saproxy_handlers;
444 ZVAL_OBJ(proxy_out, &proxy->std);
445
446 return 1;
447 }
448
449 /* iterator */
450
saproxy_iter_dtor(zend_object_iterator * iter)451 static void saproxy_iter_dtor(zend_object_iterator *iter)
452 {
453 php_com_saproxy_iter *I = (php_com_saproxy_iter*)Z_PTR(iter->data);
454
455 zval_ptr_dtor(&I->proxy_obj);
456
457 efree(I->indices);
458 efree(I);
459 }
460
saproxy_iter_valid(zend_object_iterator * iter)461 static int saproxy_iter_valid(zend_object_iterator *iter)
462 {
463 php_com_saproxy_iter *I = (php_com_saproxy_iter*)Z_PTR(iter->data);
464
465 return (I->key < I->imax) ? SUCCESS : FAILURE;
466 }
467
saproxy_iter_get_data(zend_object_iterator * iter)468 static zval* saproxy_iter_get_data(zend_object_iterator *iter)
469 {
470 php_com_saproxy_iter *I = (php_com_saproxy_iter*)Z_PTR(iter->data);
471 VARIANT v;
472 VARTYPE vt;
473 SAFEARRAY *sa;
474
475 I->indices[I->proxy->dimensions-1] = I->key;
476
477 sa = V_ARRAY(&I->proxy->obj->v);
478
479 if (FAILED(SafeArrayGetVartype(sa, &vt)) || vt == VT_EMPTY) {
480 vt = V_VT(&I->proxy->obj->v) & ~VT_ARRAY;
481 }
482
483 VariantInit(&v);
484 if (vt == VT_VARIANT) {
485 SafeArrayGetElement(sa, I->indices, &v);
486 } else {
487 V_VT(&v) = vt;
488 SafeArrayGetElement(sa, I->indices, &v.lVal);
489 }
490
491 ZVAL_NULL(&I->data);
492 php_com_wrap_variant(&I->data, &v, I->proxy->obj->code_page);
493 VariantClear(&v);
494
495 return &I->data;
496 }
497
saproxy_iter_get_key(zend_object_iterator * iter,zval * key)498 static void saproxy_iter_get_key(zend_object_iterator *iter, zval *key)
499 {
500 php_com_saproxy_iter *I = (php_com_saproxy_iter*)Z_PTR(iter->data);
501
502 if (I->key == -1) {
503 ZVAL_NULL(key);
504 } else {
505 ZVAL_LONG(key, I->key);
506 }
507 }
508
saproxy_iter_move_forwards(zend_object_iterator * iter)509 static void saproxy_iter_move_forwards(zend_object_iterator *iter)
510 {
511 php_com_saproxy_iter *I = (php_com_saproxy_iter*)Z_PTR(iter->data);
512
513 if (++I->key >= I->imax) {
514 I->key = -1;
515 }
516 }
517
518 static const zend_object_iterator_funcs saproxy_iter_funcs = {
519 saproxy_iter_dtor,
520 saproxy_iter_valid,
521 saproxy_iter_get_data,
522 saproxy_iter_get_key,
523 saproxy_iter_move_forwards,
524 NULL,
525 NULL, /* get_gc */
526 };
527
528
php_com_saproxy_iter_get(zend_class_entry * ce,zval * object,int by_ref)529 zend_object_iterator *php_com_saproxy_iter_get(zend_class_entry *ce, zval *object, int by_ref)
530 {
531 php_com_saproxy *proxy = SA_FETCH(object);
532 php_com_saproxy_iter *I;
533 int i;
534
535 if (by_ref) {
536 zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
537 return NULL;
538 }
539
540 I = ecalloc(1, sizeof(*I));
541 I->iter.funcs = &saproxy_iter_funcs;
542 Z_PTR(I->iter.data) = I;
543
544 I->proxy = proxy;
545 Z_ADDREF_P(object);
546 ZVAL_OBJ(&I->proxy_obj, Z_OBJ_P(object));
547
548 I->indices = safe_emalloc(proxy->dimensions + 1, sizeof(LONG), 0);
549 for (i = 0; i < proxy->dimensions; i++) {
550 convert_to_long(&proxy->indices[i]);
551 I->indices[i] = (LONG)Z_LVAL(proxy->indices[i]);
552 }
553
554 SafeArrayGetLBound(V_ARRAY(&proxy->obj->v), proxy->dimensions, &I->imin);
555 SafeArrayGetUBound(V_ARRAY(&proxy->obj->v), proxy->dimensions, &I->imax);
556
557 I->key = I->imin;
558
559 return &I->iter;
560 }
561