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