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