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