xref: /PHP-7.3/ext/com_dotnet/com_com.c (revision 75ac3f1c)
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 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 
23 #include "php.h"
24 #include "php_ini.h"
25 #include "ext/standard/info.h"
26 #include "php_com_dotnet.h"
27 #include "php_com_dotnet_internal.h"
28 #include "Zend/zend_exceptions.h"
29 
30 /* {{{ com_create_instance - ctor for COM class */
PHP_FUNCTION(com_create_instance)31 PHP_FUNCTION(com_create_instance)
32 {
33 	zval *object = getThis();
34 	zval *server_params = NULL;
35 	php_com_dotnet_object *obj;
36 	char *module_name, *typelib_name = NULL, *server_name = NULL;
37 	char *user_name = NULL, *domain_name = NULL, *password = NULL;
38 	size_t module_name_len = 0, typelib_name_len = 0, server_name_len = 0,
39 		user_name_len, domain_name_len, password_len;
40 	OLECHAR *moniker;
41 	CLSID clsid;
42 	CLSCTX ctx = CLSCTX_SERVER;
43 	HRESULT res = E_FAIL;
44 	int mode = COMG(autoreg_case_sensitive) ? CONST_CS : 0;
45 	ITypeLib *TL = NULL;
46 	COSERVERINFO	info;
47 	COAUTHIDENTITY	authid = {0};
48 	COAUTHINFO		authinfo = {
49 		RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL,
50 		RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE,
51 		&authid, EOAC_NONE
52 	};
53 	zend_long cp = GetACP();
54 	const struct php_win32_cp *cp_it;
55 
56 	php_com_initialize();
57 	obj = CDNO_FETCH(object);
58 
59 	if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
60 			ZEND_NUM_ARGS(), "s|s!ls",
61 			&module_name, &module_name_len, &server_name, &server_name_len,
62 			&cp, &typelib_name, &typelib_name_len) &&
63 		FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
64 			ZEND_NUM_ARGS(), "sa/|ls",
65 			&module_name, &module_name_len, &server_params, &cp,
66 			&typelib_name, &typelib_name_len)) {
67 
68 		php_com_throw_exception(E_INVALIDARG, "Could not create COM object - invalid arguments!");
69 		return;
70 	}
71 
72 	cp_it = php_win32_cp_get_by_id((DWORD)cp);
73 	if (!cp_it) {
74 		php_com_throw_exception(E_INVALIDARG, "Could not create COM object - invalid codepage!");
75 		return;
76 	}
77 	obj->code_page = (int)cp;
78 
79 	if (server_name) {
80 		ctx = CLSCTX_REMOTE_SERVER;
81 	} else if (server_params) {
82 		zval *tmp;
83 
84 		/* decode the data from the array */
85 
86 		if (NULL != (tmp = zend_hash_str_find(Z_ARRVAL_P(server_params),
87 				"Server", sizeof("Server")-1))) {
88 			convert_to_string_ex(tmp);
89 			server_name = Z_STRVAL_P(tmp);
90 			server_name_len = Z_STRLEN_P(tmp);
91 			ctx = CLSCTX_REMOTE_SERVER;
92 		}
93 
94 		if (NULL != (tmp = zend_hash_str_find(Z_ARRVAL_P(server_params),
95 				"Username", sizeof("Username")-1))) {
96 			convert_to_string_ex(tmp);
97 			user_name = Z_STRVAL_P(tmp);
98 			user_name_len = Z_STRLEN_P(tmp);
99 		}
100 
101 		if (NULL != (tmp = zend_hash_str_find(Z_ARRVAL_P(server_params),
102 				"Password", sizeof("Password")-1))) {
103 			convert_to_string_ex(tmp);
104 			password = Z_STRVAL_P(tmp);
105 			password_len = Z_STRLEN_P(tmp);
106 		}
107 
108 		if (NULL != (tmp = zend_hash_str_find(Z_ARRVAL_P(server_params),
109 				"Domain", sizeof("Domain")-1))) {
110 			convert_to_string_ex(tmp);
111 			domain_name = Z_STRVAL_P(tmp);
112 			domain_name_len = Z_STRLEN_P(tmp);
113 		}
114 
115 		if (NULL != (tmp = zend_hash_str_find(Z_ARRVAL_P(server_params),
116 				"Flags", sizeof("Flags")-1))) {
117 			ctx = (CLSCTX)zval_get_long(tmp);
118 		}
119 	}
120 
121 	if (server_name && !COMG(allow_dcom)) {
122 		php_com_throw_exception(E_ERROR, "DCOM has been disabled by your administrator [com.allow_dcom=0]");
123 		return;
124 	}
125 
126 	moniker = php_com_string_to_olestring(module_name, module_name_len, obj->code_page);
127 
128 	/* if instantiating a remote object, either directly, or via
129 	 * a moniker, fill in the relevant info */
130 	if (server_name) {
131 		info.dwReserved1 = 0;
132 		info.dwReserved2 = 0;
133 		info.pwszName = php_com_string_to_olestring(server_name, server_name_len, obj->code_page);
134 
135 		if (user_name) {
136 			authid.User = (OLECHAR*)user_name;
137 			authid.UserLength = (ULONG)user_name_len;
138 
139 			if (password) {
140 				authid.Password = (OLECHAR*)password;
141 				authid.PasswordLength = (ULONG)password_len;
142 			} else {
143 				authid.Password = (OLECHAR*)"";
144 				authid.PasswordLength = 0;
145 			}
146 
147 			if (domain_name) {
148 				authid.Domain = (OLECHAR*)domain_name;
149 				authid.DomainLength = (ULONG)domain_name_len;
150 			} else {
151 				authid.Domain = (OLECHAR*)"";
152 				authid.DomainLength = 0;
153 			}
154 			authid.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
155 			info.pAuthInfo = &authinfo;
156 		} else {
157 			info.pAuthInfo = NULL;
158 		}
159 	}
160 
161 	if (FAILED(CLSIDFromString(moniker, &clsid))) {
162 		/* try to use it as a moniker */
163 		IBindCtx *pBindCtx = NULL;
164 		IMoniker *pMoniker = NULL;
165 		ULONG ulEaten;
166 		BIND_OPTS2 bopt = {0};
167 
168 		if (SUCCEEDED(res = CreateBindCtx(0, &pBindCtx))) {
169 			if (server_name) {
170 				/* fill in the remote server info.
171 				 * MSDN docs indicate that this might be ignored in
172 				 * current win32 implementations, but at least we are
173 				 * doing the right thing in readiness for the day that
174 				 * it does work */
175 				bopt.cbStruct = sizeof(bopt);
176 				IBindCtx_GetBindOptions(pBindCtx, (BIND_OPTS*)&bopt);
177 				bopt.pServerInfo = &info;
178 				/* apparently, GetBindOptions will only ever return
179 				 * a regular BIND_OPTS structure.  My gut feeling is
180 				 * that it will modify the size field to reflect that
181 				 * so lets be safe and set it to the BIND_OPTS2 size
182 				 * again */
183 				bopt.cbStruct = sizeof(bopt);
184 				IBindCtx_SetBindOptions(pBindCtx, (BIND_OPTS*)&bopt);
185 			}
186 
187 			if (SUCCEEDED(res = MkParseDisplayName(pBindCtx, moniker, &ulEaten, &pMoniker))) {
188 				res = IMoniker_BindToObject(pMoniker, pBindCtx,
189 					NULL, &IID_IDispatch, (LPVOID*)&V_DISPATCH(&obj->v));
190 
191 				if (SUCCEEDED(res)) {
192 					V_VT(&obj->v) = VT_DISPATCH;
193 				}
194 
195 				IMoniker_Release(pMoniker);
196 			}
197 		}
198 		if (pBindCtx) {
199 			IBindCtx_Release(pBindCtx);
200 		}
201 	} else if (server_name) {
202 		MULTI_QI		qi;
203 
204 		qi.pIID = &IID_IDispatch;
205 		qi.pItf = NULL;
206 		qi.hr = S_OK;
207 
208 		res = CoCreateInstanceEx(&clsid, NULL, ctx, &info, 1, &qi);
209 
210 		if (SUCCEEDED(res)) {
211 			res = qi.hr;
212 			V_DISPATCH(&obj->v) = (IDispatch*)qi.pItf;
213 			V_VT(&obj->v) = VT_DISPATCH;
214 		}
215 	} else {
216 		res = CoCreateInstance(&clsid, NULL, CLSCTX_SERVER, &IID_IDispatch, (LPVOID*)&V_DISPATCH(&obj->v));
217 		if (SUCCEEDED(res)) {
218 			V_VT(&obj->v) = VT_DISPATCH;
219 		}
220 	}
221 
222 	if (server_name) {
223 		if (info.pwszName) efree(info.pwszName);
224 	}
225 
226 	efree(moniker);
227 
228 	if (FAILED(res)) {
229 		char *werr, *msg;
230 
231 		werr = php_win32_error_to_msg(res);
232 		spprintf(&msg, 0, "Failed to create COM object `%s': %s", module_name, werr);
233 		LocalFree(werr);
234 
235 		php_com_throw_exception(res, msg);
236 		efree(msg);
237 		return;
238 	}
239 
240 	/* we got the object and it lives ! */
241 
242 	/* see if it has TypeInfo available */
243 	if (FAILED(IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &obj->typeinfo)) && typelib_name) {
244 		/* load up the library from the named file */
245 		int cached;
246 
247 		TL = php_com_load_typelib_via_cache(typelib_name, obj->code_page, &cached);
248 
249 		if (TL) {
250 			if (COMG(autoreg_on)) {
251 				php_com_import_typelib(TL, mode, obj->code_page);
252 			}
253 
254 			/* cross your fingers... there is no guarantee that this ITypeInfo
255 			 * instance has any relation to this IDispatch instance... */
256 			ITypeLib_GetTypeInfo(TL, 0, &obj->typeinfo);
257 			ITypeLib_Release(TL);
258 		}
259 	} else if (obj->typeinfo && COMG(autoreg_on)) {
260 		UINT idx;
261 
262 		if (SUCCEEDED(ITypeInfo_GetContainingTypeLib(obj->typeinfo, &TL, &idx))) {
263 			/* check if the library is already in the cache by getting its name */
264 			BSTR name;
265 
266 			if (SUCCEEDED(ITypeLib_GetDocumentation(TL, -1, &name, NULL, NULL, NULL))) {
267 				typelib_name = php_com_olestring_to_string(name, &typelib_name_len, obj->code_page);
268 
269 				if (NULL != zend_ts_hash_str_add_ptr(&php_com_typelibraries, typelib_name, typelib_name_len, TL)) {
270 					php_com_import_typelib(TL, mode, obj->code_page);
271 
272 					/* add a reference for the hash */
273 					ITypeLib_AddRef(TL);
274 				}
275 
276 			} else {
277 				/* try it anyway */
278 				php_com_import_typelib(TL, mode, obj->code_page);
279 			}
280 
281 			ITypeLib_Release(TL);
282 		}
283 	}
284 
285 }
286 /* }}} */
287 
288 /* {{{ proto object com_get_active_object(string progid [, int code_page ])
289    Returns a handle to an already running instance of a COM object */
PHP_FUNCTION(com_get_active_object)290 PHP_FUNCTION(com_get_active_object)
291 {
292 	CLSID clsid;
293 	char *module_name;
294 	size_t module_name_len;
295 	zend_long code_page = COMG(code_page);
296 	IUnknown *unk = NULL;
297 	IDispatch *obj = NULL;
298 	HRESULT res;
299 	OLECHAR *module = NULL;
300 
301 	php_com_initialize();
302 	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s|l",
303 				&module_name, &module_name_len, &code_page)) {
304 		php_com_throw_exception(E_INVALIDARG, "Invalid arguments!");
305 		return;
306 	}
307 
308 	module = php_com_string_to_olestring(module_name, module_name_len, (int)code_page);
309 
310 	res = CLSIDFromString(module, &clsid);
311 
312 	if (FAILED(res)) {
313 		php_com_throw_exception(res, NULL);
314 	} else {
315 		res = GetActiveObject(&clsid, NULL, &unk);
316 
317 		if (FAILED(res)) {
318 			php_com_throw_exception(res, NULL);
319 		} else {
320 			res = IUnknown_QueryInterface(unk, &IID_IDispatch, &obj);
321 
322 			if (FAILED(res)) {
323 				php_com_throw_exception(res, NULL);
324 			} else if (obj) {
325 				/* we got our dispatchable object */
326 				php_com_wrap_dispatch(return_value, obj, (int)code_page);
327 			}
328 		}
329 	}
330 
331 	if (obj) {
332 		IDispatch_Release(obj);
333 	}
334 	if (unk) {
335 		IUnknown_Release(obj);
336 	}
337 	efree(module);
338 }
339 /* }}} */
340 
341 /* Performs an Invoke on the given com object.
342  * returns a failure code and creates an exception if there was an error */
php_com_invoke_helper(php_com_dotnet_object * obj,DISPID id_member,WORD flags,DISPPARAMS * disp_params,VARIANT * v,int silent,int allow_noarg)343 HRESULT php_com_invoke_helper(php_com_dotnet_object *obj, DISPID id_member,
344 		WORD flags, DISPPARAMS *disp_params, VARIANT *v, int silent, int allow_noarg)
345 {
346 	HRESULT hr;
347 	unsigned int arg_err;
348 	EXCEPINFO e = {0};
349 
350 	hr = IDispatch_Invoke(V_DISPATCH(&obj->v), id_member,
351 		&IID_NULL, LOCALE_SYSTEM_DEFAULT, flags, disp_params, v, &e, &arg_err);
352 
353 	if (silent == 0 && FAILED(hr)) {
354 		char *source = NULL, *desc = NULL, *msg = NULL;
355 		size_t source_len, desc_len;
356 
357 		switch (hr) {
358 			case DISP_E_EXCEPTION:
359 				if (e.bstrSource) {
360 					source = php_com_olestring_to_string(e.bstrSource, &source_len, obj->code_page);
361 					SysFreeString(e.bstrSource);
362 				}
363 				if (e.bstrDescription) {
364 					desc = php_com_olestring_to_string(e.bstrDescription, &desc_len, obj->code_page);
365 					SysFreeString(e.bstrDescription);
366 				}
367 				if (PG(html_errors)) {
368 					spprintf(&msg, 0, "<b>Source:</b> %s<br/><b>Description:</b> %s",
369 						source ? source : "Unknown",
370 						desc ? desc : "Unknown");
371 				} else {
372 					spprintf(&msg, 0, "Source: %s\nDescription: %s",
373 						source ? source : "Unknown",
374 						desc ? desc : "Unknown");
375 				}
376 				if (desc) {
377 					efree(desc);
378 				}
379 				if (source) {
380 					efree(source);
381 				}
382 				if (e.bstrHelpFile) {
383 					SysFreeString(e.bstrHelpFile);
384 				}
385 				break;
386 
387 			case DISP_E_PARAMNOTFOUND:
388 			case DISP_E_TYPEMISMATCH:
389 				desc = php_win32_error_to_msg(hr);
390 				spprintf(&msg, 0, "Parameter %d: %s", arg_err, desc);
391 				LocalFree(desc);
392 				break;
393 
394 			case DISP_E_BADPARAMCOUNT:
395 				if ((disp_params->cArgs + disp_params->cNamedArgs == 0) && (allow_noarg == 1)) {
396 					/* if getting a property and they are missing all parameters,
397 					 * we want to create a proxy object for them; so lets not create an
398 					 * exception here */
399 					msg = NULL;
400 					break;
401 				}
402 				/* else fall through */
403 
404 			default:
405 				desc = php_win32_error_to_msg(hr);
406 				spprintf(&msg, 0, "Error [0x%08x] %s", hr, desc);
407 				LocalFree(desc);
408 				break;
409 		}
410 
411 		if (msg) {
412 			php_com_throw_exception(hr, msg);
413 			efree(msg);
414 		}
415 	}
416 
417 	return hr;
418 }
419 
420 /* map an ID to a name */
php_com_get_id_of_name(php_com_dotnet_object * obj,char * name,size_t namelen,DISPID * dispid)421 HRESULT php_com_get_id_of_name(php_com_dotnet_object *obj, char *name,
422 		size_t namelen, DISPID *dispid)
423 {
424 	OLECHAR *olename;
425 	HRESULT hr;
426 	zval *tmp;
427 
428 	if (namelen == -1) {
429 		namelen = strlen(name);
430 	}
431 
432 	if (obj->id_of_name_cache && NULL != (tmp = zend_hash_str_find(obj->id_of_name_cache, name, namelen))) {
433 		*dispid = (DISPID)Z_LVAL_P(tmp);
434 		return S_OK;
435 	}
436 
437 	olename = php_com_string_to_olestring(name, namelen, obj->code_page);
438 
439 	if (obj->typeinfo) {
440 		hr = ITypeInfo_GetIDsOfNames(obj->typeinfo, &olename, 1, dispid);
441 		if (FAILED(hr)) {
442 			HRESULT hr1 = hr;
443 			hr = IDispatch_GetIDsOfNames(V_DISPATCH(&obj->v), &IID_NULL, &olename, 1, LOCALE_SYSTEM_DEFAULT, dispid);
444 			if (SUCCEEDED(hr) && hr1 != E_NOTIMPL) {
445 				/* fall back on IDispatch direct */
446 				ITypeInfo_Release(obj->typeinfo);
447 				obj->typeinfo = NULL;
448 			}
449 		}
450 	} else {
451 		hr = IDispatch_GetIDsOfNames(V_DISPATCH(&obj->v), &IID_NULL, &olename, 1, LOCALE_SYSTEM_DEFAULT, dispid);
452 	}
453 	efree(olename);
454 
455 	if (SUCCEEDED(hr)) {
456 		zval tmp;
457 
458 		/* cache the mapping */
459 		if (!obj->id_of_name_cache) {
460 			ALLOC_HASHTABLE(obj->id_of_name_cache);
461 			zend_hash_init(obj->id_of_name_cache, 2, NULL, NULL, 0);
462 		}
463 		ZVAL_LONG(&tmp, *dispid);
464 		zend_hash_str_update(obj->id_of_name_cache, name, namelen, &tmp);
465 	}
466 
467 	return hr;
468 }
469 
470 /* the core of COM */
php_com_do_invoke_byref(php_com_dotnet_object * obj,zend_internal_function * f,WORD flags,VARIANT * v,int nargs,zval * args)471 int php_com_do_invoke_byref(php_com_dotnet_object *obj, zend_internal_function *f,
472 		WORD flags,	VARIANT *v, int nargs, zval *args)
473 {
474 	DISPID dispid, altdispid;
475 	DISPPARAMS disp_params;
476 	HRESULT hr;
477 	VARIANT *vargs = NULL, *byref_vals = NULL;
478 	int i, byref_count = 0, j;
479 
480 	/* assumption: that the active function (f) is the function we generated for the engine */
481 	if (!f) {
482 		return FAILURE;
483 	}
484 
485 	hr = php_com_get_id_of_name(obj, f->function_name->val, f->function_name->len, &dispid);
486 
487 	if (FAILED(hr)) {
488 		char *winerr = NULL;
489 		char *msg = NULL;
490 		winerr = php_win32_error_to_msg(hr);
491 		spprintf(&msg, 0, "Unable to lookup `%s': %s", f->function_name->val, winerr);
492 		LocalFree(winerr);
493 		php_com_throw_exception(hr, msg);
494 		efree(msg);
495 		return FAILURE;
496 	}
497 
498 
499 	if (nargs) {
500 		vargs = (VARIANT*)safe_emalloc(sizeof(VARIANT), nargs, 0);
501 	}
502 
503 	if (f->arg_info) {
504 		for (i = 0; i < nargs; i++) {
505 			if (f->arg_info[nargs - i - 1].pass_by_reference) {
506 				byref_count++;
507 			}
508 		}
509 	}
510 
511 	if (byref_count) {
512 		byref_vals = (VARIANT*)safe_emalloc(sizeof(VARIANT), byref_count, 0);
513 		for (j = 0, i = 0; i < nargs; i++) {
514 			if (f->arg_info[nargs - i - 1].pass_by_reference) {
515 				/* put the value into byref_vals instead */
516 				php_com_variant_from_zval(&byref_vals[j], &args[nargs - i - 1], obj->code_page);
517 
518 				/* if it is already byref, "move" it into the vargs array, otherwise
519 				 * make vargs a reference to this value */
520 				if (V_VT(&byref_vals[j]) & VT_BYREF) {
521 					memcpy(&vargs[i], &byref_vals[j], sizeof(vargs[i]));
522 					VariantInit(&byref_vals[j]); /* leave the variant slot empty to simplify cleanup */
523 				} else {
524 					VariantInit(&vargs[i]);
525 					V_VT(&vargs[i]) = V_VT(&byref_vals[j]) | VT_BYREF;
526 					/* union magic ensures that this works out */
527 					vargs[i].byref = &V_UINT(&byref_vals[j]);
528 				}
529 				j++;
530 			} else {
531 				php_com_variant_from_zval(&vargs[i], &args[nargs - i - 1], obj->code_page);
532 			}
533 		}
534 
535 	} else {
536 		/* Invoke'd args are in reverse order */
537 		for (i = 0; i < nargs; i++) {
538 			php_com_variant_from_zval(&vargs[i], &args[nargs - i - 1], obj->code_page);
539 		}
540 	}
541 
542 	disp_params.cArgs = nargs;
543 	disp_params.cNamedArgs = 0;
544 	disp_params.rgvarg = vargs;
545 	disp_params.rgdispidNamedArgs = NULL;
546 
547 	if (flags & DISPATCH_PROPERTYPUT) {
548 		altdispid = DISPID_PROPERTYPUT;
549 		disp_params.rgdispidNamedArgs = &altdispid;
550 		disp_params.cNamedArgs = 1;
551 	}
552 
553 	/* this will create an exception if needed */
554 	hr = php_com_invoke_helper(obj, dispid, flags, &disp_params, v, 0, 0);
555 
556 	/* release variants */
557 	if (vargs) {
558 		if (f && f->arg_info) {
559 			for (i = 0, j = 0; i < nargs; i++) {
560 				/* if this was byref, update the zval */
561 				if (f->arg_info[nargs - i - 1].pass_by_reference) {
562 					zval *arg = &args[nargs - i - 1];
563 
564 					ZVAL_DEREF(arg);
565 					zval_ptr_dtor(arg);
566 					ZVAL_NULL(arg);
567 
568 					/* if the variant is pointing at the byref_vals, we need to map
569 					 * the pointee value as a zval; otherwise, the value is pointing
570 					 * into an existing PHP variant record */
571 					if (V_VT(&vargs[i]) & VT_BYREF) {
572 						if (vargs[i].byref == &V_UINT(&byref_vals[j])) {
573 							/* copy that value */
574 							php_com_zval_from_variant(arg, &byref_vals[j], obj->code_page);
575 						}
576 					} else {
577 						/* not sure if this can ever happen; the variant we marked as BYREF
578 						 * is no longer BYREF - copy its value */
579 						php_com_zval_from_variant(arg, &vargs[i], obj->code_page);
580 					}
581 					VariantClear(&byref_vals[j]);
582 					j++;
583 				}
584 				VariantClear(&vargs[i]);
585 			}
586 		} else {
587 			for (i = 0, j = 0; i < nargs; i++) {
588 				VariantClear(&vargs[i]);
589 			}
590 		}
591 		efree(vargs);
592 		if (byref_vals) efree(byref_vals);
593 	}
594 
595 	return SUCCEEDED(hr) ? SUCCESS : FAILURE;
596 }
597 
598 
599 
php_com_do_invoke_by_id(php_com_dotnet_object * obj,DISPID dispid,WORD flags,VARIANT * v,int nargs,zval * args,int silent,int allow_noarg)600 int php_com_do_invoke_by_id(php_com_dotnet_object *obj, DISPID dispid,
601 		WORD flags,	VARIANT *v, int nargs, zval *args, int silent, int allow_noarg)
602 {
603 	DISPID altdispid;
604 	DISPPARAMS disp_params;
605 	HRESULT hr;
606 	VARIANT *vargs = NULL;
607 	int i;
608 
609 	if (nargs) {
610 		vargs = (VARIANT*)safe_emalloc(sizeof(VARIANT), nargs, 0);
611 	}
612 
613 	/* Invoke'd args are in reverse order */
614 	for (i = 0; i < nargs; i++) {
615 		php_com_variant_from_zval(&vargs[i], &args[nargs - i - 1], obj->code_page);
616 	}
617 
618 	disp_params.cArgs = nargs;
619 	disp_params.cNamedArgs = 0;
620 	disp_params.rgvarg = vargs;
621 	disp_params.rgdispidNamedArgs = NULL;
622 
623 	if (flags & DISPATCH_PROPERTYPUT) {
624 		altdispid = DISPID_PROPERTYPUT;
625 		disp_params.rgdispidNamedArgs = &altdispid;
626 		disp_params.cNamedArgs = 1;
627 	}
628 
629 	/* this will create an exception if needed */
630 	hr = php_com_invoke_helper(obj, dispid, flags, &disp_params, v, silent, allow_noarg);
631 
632 	/* release variants */
633 	if (vargs) {
634 		for (i = 0; i < nargs; i++) {
635 			VariantClear(&vargs[i]);
636 		}
637 		efree(vargs);
638 	}
639 
640 	/* a bit of a hack this, but it's needed for COM array access. */
641 	if (hr == DISP_E_BADPARAMCOUNT)
642 		return hr;
643 
644 	return SUCCEEDED(hr) ? SUCCESS : FAILURE;
645 }
646 
php_com_do_invoke(php_com_dotnet_object * obj,char * name,size_t namelen,WORD flags,VARIANT * v,int nargs,zval * args,int allow_noarg)647 int php_com_do_invoke(php_com_dotnet_object *obj, char *name, size_t namelen,
648 		WORD flags,	VARIANT *v, int nargs, zval *args, int allow_noarg)
649 {
650 	DISPID dispid;
651 	HRESULT hr;
652 	char *winerr = NULL;
653 	char *msg = NULL;
654 
655 	hr = php_com_get_id_of_name(obj, name, namelen, &dispid);
656 
657 	if (FAILED(hr)) {
658 		winerr = php_win32_error_to_msg(hr);
659 		spprintf(&msg, 0, "Unable to lookup `%s': %s", name, winerr);
660 		LocalFree(winerr);
661 		php_com_throw_exception(hr, msg);
662 		efree(msg);
663 		return FAILURE;
664 	}
665 
666 	return php_com_do_invoke_by_id(obj, dispid, flags, v, nargs, args, 0, allow_noarg);
667 }
668 
669 /* {{{ proto string com_create_guid()
670    Generate a globally unique identifier (GUID) */
PHP_FUNCTION(com_create_guid)671 PHP_FUNCTION(com_create_guid)
672 {
673 	GUID retval;
674 	OLECHAR *guid_string;
675 
676 	if (zend_parse_parameters_none() == FAILURE) {
677 		return;
678 	}
679 
680 	php_com_initialize();
681 	if (CoCreateGuid(&retval) == S_OK && StringFromCLSID(&retval, &guid_string) == S_OK) {
682 		size_t len;
683 		char *str;
684 
685 		str = php_com_olestring_to_string(guid_string, &len, CP_ACP);
686 		RETVAL_STRINGL(str, len);
687 		// TODO: avoid reallocation ???
688 		efree(str);
689 
690 		CoTaskMemFree(guid_string);
691 	} else {
692 		RETURN_FALSE;
693 	}
694 }
695 /* }}} */
696 
697 /* {{{ proto bool com_event_sink(object comobject, object sinkobject [, mixed sinkinterface])
698    Connect events from a COM object to a PHP object */
PHP_FUNCTION(com_event_sink)699 PHP_FUNCTION(com_event_sink)
700 {
701 	zval *object, *sinkobject, *sink=NULL;
702 	char *dispname = NULL, *typelibname = NULL;
703 	php_com_dotnet_object *obj;
704 	ITypeInfo *typeinfo = NULL;
705 
706 	RETVAL_FALSE;
707 
708 	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "Oo|z/",
709 			&object, php_com_variant_class_entry, &sinkobject, &sink)) {
710 		RETURN_FALSE;
711 	}
712 
713 	php_com_initialize();
714 	obj = CDNO_FETCH(object);
715 
716 	if (sink && Z_TYPE_P(sink) == IS_ARRAY) {
717 		/* 0 => typelibname, 1 => dispname */
718 		zval *tmp;
719 
720 		if ((tmp = zend_hash_index_find(Z_ARRVAL_P(sink), 0)) != NULL && Z_TYPE_P(tmp) == IS_STRING)
721 			typelibname = Z_STRVAL_P(tmp);
722 		if ((tmp = zend_hash_index_find(Z_ARRVAL_P(sink), 1)) != NULL && Z_TYPE_P(tmp) == IS_STRING)
723 			dispname = Z_STRVAL_P(tmp);
724 	} else if (sink != NULL) {
725 		convert_to_string(sink);
726 		dispname = Z_STRVAL_P(sink);
727 	}
728 
729 	typeinfo = php_com_locate_typeinfo(typelibname, obj, dispname, 1);
730 
731 	if (typeinfo) {
732 		HashTable *id_to_name;
733 
734 		ALLOC_HASHTABLE(id_to_name);
735 
736 		if (php_com_process_typeinfo(typeinfo, id_to_name, 0, &obj->sink_id, obj->code_page)) {
737 
738 			/* Create the COM wrapper for this sink */
739 			obj->sink_dispatch = php_com_wrapper_export_as_sink(sinkobject, &obj->sink_id, id_to_name);
740 
741 			/* Now hook it up to the source */
742 			php_com_object_enable_event_sink(obj, TRUE);
743 			RETVAL_TRUE;
744 
745 		} else {
746 			FREE_HASHTABLE(id_to_name);
747 		}
748 	}
749 
750 	if (typeinfo) {
751 		ITypeInfo_Release(typeinfo);
752 	}
753 
754 }
755 /* }}} */
756 
757 /* {{{ proto bool com_print_typeinfo(object comobject | string typelib, string dispinterface, bool wantsink)
758    Print out a PHP class definition for a dispatchable interface */
PHP_FUNCTION(com_print_typeinfo)759 PHP_FUNCTION(com_print_typeinfo)
760 {
761 	zval *arg1;
762 	char *ifacename = NULL;
763 	char *typelibname = NULL;
764 	size_t ifacelen;
765 	zend_bool wantsink = 0;
766 	php_com_dotnet_object *obj = NULL;
767 	ITypeInfo *typeinfo;
768 
769 	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "z/|s!b", &arg1, &ifacename,
770 				&ifacelen, &wantsink)) {
771 		RETURN_FALSE;
772 	}
773 
774 	php_com_initialize();
775 	if (Z_TYPE_P(arg1) == IS_OBJECT) {
776 		CDNO_FETCH_VERIFY(obj, arg1);
777 	} else {
778 		convert_to_string(arg1);
779 		typelibname = Z_STRVAL_P(arg1);
780 	}
781 
782 	typeinfo = php_com_locate_typeinfo(typelibname, obj, ifacename, wantsink ? 1 : 0);
783 	if (typeinfo) {
784 		php_com_process_typeinfo(typeinfo, NULL, 1, NULL, obj ? obj->code_page : COMG(code_page));
785 		ITypeInfo_Release(typeinfo);
786 		RETURN_TRUE;
787 	} else {
788 		zend_error(E_WARNING, "Unable to find typeinfo using the parameters supplied");
789 	}
790 	RETURN_FALSE;
791 }
792 /* }}} */
793 
794 /* {{{ proto bool com_message_pump([int timeoutms])
795    Process COM messages, sleeping for up to timeoutms milliseconds */
PHP_FUNCTION(com_message_pump)796 PHP_FUNCTION(com_message_pump)
797 {
798 	zend_long timeoutms = 0;
799 	MSG msg;
800 	DWORD result;
801 
802 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &timeoutms) == FAILURE)
803 		RETURN_FALSE;
804 
805 	php_com_initialize();
806 	result = MsgWaitForMultipleObjects(0, NULL, FALSE, (DWORD)timeoutms, QS_ALLINPUT);
807 
808 	if (result == WAIT_OBJECT_0) {
809 		while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
810 			TranslateMessage(&msg);
811 			DispatchMessage(&msg);
812 		}
813 		/* we processed messages */
814 		RETVAL_TRUE;
815 	} else {
816 		/* we did not process messages (timed out) */
817 		RETVAL_FALSE;
818 	}
819 }
820 /* }}} */
821 
822 /* {{{ proto bool com_load_typelib(string typelib_name [, bool case_insensitive])
823    Loads a Typelibrary and registers its constants */
PHP_FUNCTION(com_load_typelib)824 PHP_FUNCTION(com_load_typelib)
825 {
826 	char *name;
827 	size_t namelen;
828 	ITypeLib *pTL = NULL;
829 	zend_bool cs = TRUE;
830 	int codepage = COMG(code_page);
831 	int cached = 0;
832 
833 	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s|b", &name, &namelen, &cs)) {
834 		return;
835 	}
836 
837 	RETVAL_FALSE;
838 
839 	php_com_initialize();
840 	pTL = php_com_load_typelib_via_cache(name, codepage, &cached);
841 	if (pTL) {
842 		if (php_com_import_typelib(pTL, cs ? CONST_CS : 0, codepage) == SUCCESS) {
843 			RETVAL_TRUE;
844 		}
845 
846 		ITypeLib_Release(pTL);
847 		pTL = NULL;
848 	}
849 }
850 /* }}} */
851 
852 
853 
854 /*
855  * Local variables:
856  * tab-width: 4
857  * c-basic-offset: 4
858  * End:
859  * vim600: noet sw=4 ts=4 fdm=marker
860  * vim<600: noet sw=4 ts=4
861  */
862