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