xref: /PHP-7.1/ext/com_dotnet/com_saproxy.c (revision 03f3b847)
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