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