1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2013 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 | Harald Radi <h.radi@nme.at> |
17 +----------------------------------------------------------------------+
18 */
19
20 /* $Id$ */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include "php.h"
27 #include "php_ini.h"
28 #include "ext/standard/info.h"
29 #include "php_com_dotnet.h"
30 #include "php_com_dotnet_internal.h"
31
32
33 /* The search string can be either:
34 * a) a file name
35 * b) a CLSID, major, minor e.g. "{00000200-0000-0010-8000-00AA006D2EA4},2,0"
36 * c) a Type Library name e.g. "Microsoft OLE DB ActiveX Data Objects 1.0 Library"
37 */
php_com_load_typelib(char * search_string,int codepage TSRMLS_DC)38 PHP_COM_DOTNET_API ITypeLib *php_com_load_typelib(char *search_string, int codepage TSRMLS_DC)
39 {
40 ITypeLib *TL = NULL;
41 char *strtok_buf, *major, *minor;
42 CLSID clsid;
43 OLECHAR *p;
44 HRESULT hr;
45
46 search_string = php_strtok_r(search_string, ",", &strtok_buf);
47
48 if (search_string == NULL) {
49 return NULL;
50 }
51
52 major = php_strtok_r(NULL, ",", &strtok_buf);
53 minor = php_strtok_r(NULL, ",", &strtok_buf);
54
55 p = php_com_string_to_olestring(search_string, strlen(search_string), codepage TSRMLS_CC);
56
57 if (SUCCEEDED(CLSIDFromString(p, &clsid))) {
58 WORD major_i = 1, minor_i = 0;
59
60 /* pick up the major/minor numbers; if none specified, default to 1,0 */
61 if (major && minor) {
62 major_i = (WORD)atoi(major);
63 minor_i = (WORD)atoi(minor);
64 }
65
66 /* Load the TypeLib by GUID */
67 hr = LoadRegTypeLib((REFGUID)&clsid, major_i, minor_i, LANG_NEUTRAL, &TL);
68
69 /* if that failed, assumed that the GUID is actually a CLSID and
70 * attemp to get the library via an instance of that class */
71 if (FAILED(hr) && (major == NULL || minor == NULL)) {
72 IDispatch *disp = NULL;
73 ITypeInfo *info = NULL;
74 int idx;
75
76 if (SUCCEEDED(hr = CoCreateInstance(&clsid, NULL, CLSCTX_SERVER, &IID_IDispatch, (LPVOID*)&disp)) &&
77 SUCCEEDED(hr = IDispatch_GetTypeInfo(disp, 0, LANG_NEUTRAL, &info))) {
78 hr = ITypeInfo_GetContainingTypeLib(info, &TL, &idx);
79 }
80
81 if (info) {
82 ITypeInfo_Release(info);
83 }
84 if (disp) {
85 IDispatch_Release(disp);
86 }
87 }
88 } else {
89 /* Try to load it from a file; if it fails, do a really painful search of
90 * the registry */
91 if (FAILED(LoadTypeLib(p, &TL))) {
92 HKEY hkey, hsubkey;
93 DWORD SubKeys, MaxSubKeyLength;
94 char *keyname;
95 unsigned int i, j;
96 DWORD VersionCount;
97 char version[20];
98 char *libname;
99 DWORD libnamelen;
100
101 if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, "TypeLib", 0, KEY_READ, &hkey) &&
102 ERROR_SUCCESS == RegQueryInfoKey(hkey, NULL, NULL, NULL, &SubKeys,
103 &MaxSubKeyLength, NULL, NULL, NULL, NULL, NULL, NULL)) {
104
105 MaxSubKeyLength++; /* make room for NUL */
106 keyname = emalloc(MaxSubKeyLength);
107 libname = emalloc(strlen(search_string) + 1);
108
109 for (i = 0; i < SubKeys && TL == NULL; i++) {
110 if (ERROR_SUCCESS == RegEnumKey(hkey, i, keyname, MaxSubKeyLength) &&
111 ERROR_SUCCESS == RegOpenKeyEx(hkey, keyname, 0, KEY_READ, &hsubkey)) {
112 if (ERROR_SUCCESS == RegQueryInfoKey(hsubkey, NULL, NULL, NULL, &VersionCount,
113 NULL, NULL, NULL, NULL, NULL, NULL, NULL)) {
114 for (j = 0; j < VersionCount; j++) {
115 if (ERROR_SUCCESS != RegEnumKey(hsubkey, j, version, sizeof(version))) {
116 continue;
117 }
118 /* get the default value for this key and compare */
119 libnamelen = strlen(search_string)+1;
120 if (ERROR_SUCCESS == RegQueryValue(hsubkey, version, libname, &libnamelen)) {
121 if (0 == stricmp(libname, search_string)) {
122 char *str = NULL;
123 int major_tmp, minor_tmp;
124
125 /* fetch the GUID and add the version numbers */
126 if (2 != sscanf(version, "%d.%d", &major_tmp, &minor_tmp)) {
127 major_tmp = 1;
128 minor_tmp = 0;
129 }
130 spprintf(&str, 0, "%s,%d,%d", keyname, major_tmp, minor_tmp);
131 /* recurse */
132 TL = php_com_load_typelib(str, codepage TSRMLS_CC);
133
134 efree(str);
135 break;
136 }
137 }
138 }
139 }
140 RegCloseKey(hsubkey);
141 }
142 }
143 RegCloseKey(hkey);
144 efree(keyname);
145 efree(libname);
146 }
147 }
148 }
149
150 efree(p);
151
152 return TL;
153 }
154
155 /* Given a type-library, merge it into the current engine state */
php_com_import_typelib(ITypeLib * TL,int mode,int codepage TSRMLS_DC)156 PHP_COM_DOTNET_API int php_com_import_typelib(ITypeLib *TL, int mode, int codepage TSRMLS_DC)
157 {
158 int i, j, interfaces;
159 TYPEKIND pTKind;
160 ITypeInfo *TypeInfo;
161 VARDESC *pVarDesc;
162 UINT NameCount;
163 BSTR bstr_ids;
164 zend_constant c;
165 zval exists, results, value;
166 char *const_name;
167
168 if (TL == NULL) {
169 return FAILURE;
170 }
171
172 interfaces = ITypeLib_GetTypeInfoCount(TL);
173 for (i = 0; i < interfaces; i++) {
174 ITypeLib_GetTypeInfoType(TL, i, &pTKind);
175 if (pTKind == TKIND_ENUM) {
176 ITypeLib_GetTypeInfo(TL, i, &TypeInfo);
177 for (j = 0; ; j++) {
178 if (FAILED(ITypeInfo_GetVarDesc(TypeInfo, j, &pVarDesc))) {
179 break;
180 }
181 ITypeInfo_GetNames(TypeInfo, pVarDesc->memid, &bstr_ids, 1, &NameCount);
182 if (NameCount != 1) {
183 ITypeInfo_ReleaseVarDesc(TypeInfo, pVarDesc);
184 continue;
185 }
186
187 const_name = php_com_olestring_to_string(bstr_ids, &c.name_len, codepage TSRMLS_CC);
188 c.name = zend_strndup(const_name, c.name_len);
189 efree(const_name);
190 c.name_len++; /* include NUL */
191 SysFreeString(bstr_ids);
192
193 /* sanity check for the case where the constant is already defined */
194 if (zend_get_constant(c.name, c.name_len - 1, &exists TSRMLS_CC)) {
195 if (COMG(autoreg_verbose) && !compare_function(&results, &c.value, &exists TSRMLS_CC)) {
196 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type library constant %s is already defined", c.name);
197 }
198 free(c.name);
199 ITypeInfo_ReleaseVarDesc(TypeInfo, pVarDesc);
200 continue;
201 }
202
203 /* register the constant */
204 php_com_zval_from_variant(&value, pVarDesc->lpvarValue, codepage TSRMLS_CC);
205 if (Z_TYPE(value) == IS_LONG) {
206 c.flags = mode;
207 c.value.type = IS_LONG;
208 c.value.value.lval = Z_LVAL(value);
209 c.module_number = 0;
210 zend_register_constant(&c TSRMLS_CC);
211 }
212 ITypeInfo_ReleaseVarDesc(TypeInfo, pVarDesc);
213 }
214 ITypeInfo_Release(TypeInfo);
215 }
216 }
217 return SUCCESS;
218 }
219
220 /* Type-library stuff */
php_com_typelibrary_dtor(void * pDest)221 void php_com_typelibrary_dtor(void *pDest)
222 {
223 ITypeLib **Lib = (ITypeLib**)pDest;
224 ITypeLib_Release(*Lib);
225 }
226
php_com_load_typelib_via_cache(char * search_string,int codepage,int * cached TSRMLS_DC)227 PHP_COM_DOTNET_API ITypeLib *php_com_load_typelib_via_cache(char *search_string,
228 int codepage, int *cached TSRMLS_DC)
229 {
230 ITypeLib **TLp;
231 ITypeLib *TL;
232 char *name_dup;
233 int l;
234
235 l = strlen(search_string);
236
237 if (zend_ts_hash_find(&php_com_typelibraries, search_string, l+1,
238 (void**)&TLp) == SUCCESS) {
239 *cached = 1;
240 /* add a reference for the caller */
241 ITypeLib_AddRef(*TLp);
242 return *TLp;
243 }
244
245 *cached = 0;
246 name_dup = estrndup(search_string, l);
247 TL = php_com_load_typelib(name_dup, codepage TSRMLS_CC);
248 efree(name_dup);
249
250 if (TL) {
251 if (SUCCESS == zend_ts_hash_update(&php_com_typelibraries,
252 search_string, l+1, (void*)&TL, sizeof(ITypeLib*), NULL)) {
253 /* add a reference for the hash table */
254 ITypeLib_AddRef(TL);
255 }
256 }
257
258 return TL;
259 }
260
php_com_locate_typeinfo(char * typelibname,php_com_dotnet_object * obj,char * dispname,int sink TSRMLS_DC)261 ITypeInfo *php_com_locate_typeinfo(char *typelibname, php_com_dotnet_object *obj, char *dispname, int sink TSRMLS_DC)
262 {
263 ITypeInfo *typeinfo = NULL;
264 ITypeLib *typelib = NULL;
265 int gotguid = 0;
266 GUID iid;
267
268 if (obj) {
269 if (dispname == NULL && sink) {
270 IProvideClassInfo2 *pci2;
271 IProvideClassInfo *pci;
272
273 if (SUCCEEDED(IDispatch_QueryInterface(V_DISPATCH(&obj->v), &IID_IProvideClassInfo2, (void**)&pci2))) {
274 gotguid = SUCCEEDED(IProvideClassInfo2_GetGUID(pci2, GUIDKIND_DEFAULT_SOURCE_DISP_IID, &iid));
275 IProvideClassInfo2_Release(pci2);
276 }
277 if (!gotguid && SUCCEEDED(IDispatch_QueryInterface(V_DISPATCH(&obj->v), &IID_IProvideClassInfo, (void**)&pci))) {
278 /* examine the available interfaces */
279 /* TODO: write some code here */
280 php_error_docref(NULL TSRMLS_CC, E_WARNING, "IProvideClassInfo: this code not yet written!");
281 IProvideClassInfo_Release(pci);
282 }
283 } else if (dispname == NULL) {
284 if (obj->typeinfo) {
285 ITypeInfo_AddRef(obj->typeinfo);
286 return obj->typeinfo;
287 } else {
288 IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &typeinfo);
289 if (typeinfo) {
290 return typeinfo;
291 }
292 }
293 } else if (dispname && obj->typeinfo) {
294 unsigned int idx;
295 /* get the library from the object; the rest will be dealt with later */
296 ITypeInfo_GetContainingTypeLib(obj->typeinfo, &typelib, &idx);
297 } else if (typelibname == NULL) {
298 IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &typeinfo);
299 if (dispname) {
300 unsigned int idx;
301 /* get the library from the object; the rest will be dealt with later */
302 ITypeInfo_GetContainingTypeLib(typeinfo, &typelib, &idx);
303
304 if (typelib) {
305 ITypeInfo_Release(typeinfo);
306 typeinfo = NULL;
307 }
308 }
309 }
310 } else if (typelibname) {
311 /* Fetch the typelibrary and use that to look things up */
312 typelib = php_com_load_typelib(typelibname, CP_THREAD_ACP TSRMLS_CC);
313 }
314
315 if (!gotguid && dispname && typelib) {
316 unsigned short cfound;
317 MEMBERID memid;
318 OLECHAR *olename = php_com_string_to_olestring(dispname, strlen(dispname), CP_ACP TSRMLS_CC);
319
320 cfound = 1;
321 if (FAILED(ITypeLib_FindName(typelib, olename, 0, &typeinfo, &memid, &cfound)) || cfound == 0) {
322 CLSID coclass;
323 ITypeInfo *coinfo;
324
325 /* assume that it might be a progid instead */
326 if (SUCCEEDED(CLSIDFromProgID(olename, &coclass)) &&
327 SUCCEEDED(ITypeLib_GetTypeInfoOfGuid(typelib, &coclass, &coinfo))) {
328
329 /* enumerate implemented interfaces and pick the one as indicated by sink */
330 TYPEATTR *attr;
331 int i;
332
333 ITypeInfo_GetTypeAttr(coinfo, &attr);
334
335 for (i = 0; i < attr->cImplTypes; i++) {
336 HREFTYPE rt;
337 int tf;
338
339 if (FAILED(ITypeInfo_GetImplTypeFlags(coinfo, i, &tf))) {
340 continue;
341 }
342
343 if ((sink && tf == (IMPLTYPEFLAG_FSOURCE|IMPLTYPEFLAG_FDEFAULT)) ||
344 (!sink && (tf & IMPLTYPEFLAG_FSOURCE) == 0)) {
345
346 /* flags match what we are looking for */
347
348 if (SUCCEEDED(ITypeInfo_GetRefTypeOfImplType(coinfo, i, &rt)))
349 if (SUCCEEDED(ITypeInfo_GetRefTypeInfo(coinfo, rt, &typeinfo)))
350 break;
351
352 }
353 }
354
355 ITypeInfo_ReleaseTypeAttr(coinfo, attr);
356 ITypeInfo_Release(coinfo);
357 }
358 }
359
360
361 efree(olename);
362 } else if (gotguid) {
363 ITypeLib_GetTypeInfoOfGuid(typelib, &iid, &typeinfo);
364 }
365
366 if (typelib) {
367 ITypeLib_Release(typelib);
368 }
369
370 return typeinfo;
371 }
372
373 static const struct {
374 VARTYPE vt;
375 const char *name;
376 } vt_names[] = {
377 { VT_NULL, "VT_NULL" },
378 { VT_EMPTY, "VT_EMPTY" },
379 { VT_UI1, "VT_UI1" },
380 { VT_I2, "VT_I2" },
381 { VT_I4, "VT_I4" },
382 { VT_R4, "VT_R4" },
383 { VT_R8, "VT_R8" },
384 { VT_BOOL, "VT_BOOL" },
385 { VT_ERROR, "VT_ERROR" },
386 { VT_CY, "VT_CY" },
387 { VT_DATE, "VT_DATE" },
388 { VT_BSTR, "VT_BSTR" },
389 { VT_DECIMAL, "VT_DECIMAL" },
390 { VT_UNKNOWN, "VT_UNKNOWN" },
391 { VT_DISPATCH, "VT_DISPATCH" },
392 { VT_VARIANT, "VT_VARIANT" },
393 { VT_I1, "VT_I1" },
394 { VT_UI2, "VT_UI2" },
395 { VT_UI4, "VT_UI4" },
396 { VT_INT, "VT_INT" },
397 { VT_UINT, "VT_UINT" },
398 { VT_ARRAY, "VT_ARRAY" },
399 { VT_BYREF, "VT_BYREF" },
400 { VT_VOID, "VT_VOID" },
401 { VT_PTR, "VT_PTR" },
402 { VT_HRESULT, "VT_HRESULT" },
403 { VT_SAFEARRAY, "VT_SAFEARRAY" },
404 { 0, NULL }
405 };
406
vt_to_string(VARTYPE vt)407 static inline const char *vt_to_string(VARTYPE vt)
408 {
409 int i;
410 for (i = 0; vt_names[i].name != NULL; i++) {
411 if (vt_names[i].vt == vt)
412 return vt_names[i].name;
413 }
414 return "?";
415 }
416
php_com_string_from_clsid(const CLSID * clsid,int codepage TSRMLS_DC)417 static char *php_com_string_from_clsid(const CLSID *clsid, int codepage TSRMLS_DC)
418 {
419 LPOLESTR ole_clsid;
420 char *clsid_str;
421
422 StringFromCLSID(clsid, &ole_clsid);
423 clsid_str = php_com_olestring_to_string(ole_clsid, NULL, codepage TSRMLS_CC);
424 LocalFree(ole_clsid);
425
426 return clsid_str;
427 }
428
429
php_com_process_typeinfo(ITypeInfo * typeinfo,HashTable * id_to_name,int printdef,GUID * guid,int codepage TSRMLS_DC)430 int php_com_process_typeinfo(ITypeInfo *typeinfo, HashTable *id_to_name, int printdef, GUID *guid, int codepage TSRMLS_DC)
431 {
432 TYPEATTR *attr;
433 FUNCDESC *func;
434 int i;
435 OLECHAR *olename;
436 char *ansiname = NULL;
437 unsigned int ansinamelen;
438 int ret = 0;
439
440 if (FAILED(ITypeInfo_GetTypeAttr(typeinfo, &attr))) {
441 return 0;
442 }
443
444 /* verify that it is suitable */
445 if (id_to_name == NULL || attr->typekind == TKIND_DISPATCH) {
446
447 if (guid) {
448 memcpy(guid, &attr->guid, sizeof(GUID));
449 }
450
451 if (printdef) {
452 char *guidstring;
453
454 ITypeInfo_GetDocumentation(typeinfo, MEMBERID_NIL, &olename, NULL, NULL, NULL);
455 ansiname = php_com_olestring_to_string(olename, &ansinamelen, codepage TSRMLS_CC);
456 SysFreeString(olename);
457
458 guidstring = php_com_string_from_clsid(&attr->guid, codepage TSRMLS_CC);
459 php_printf("class %s { /* GUID=%s */\n", ansiname, guidstring);
460 efree(guidstring);
461
462 efree(ansiname);
463 }
464
465 if (id_to_name) {
466 zend_hash_init(id_to_name, 0, NULL, ZVAL_PTR_DTOR, 0);
467 }
468
469 /* So we've got the dispatch interface; lets list the event methods */
470 for (i = 0; i < attr->cFuncs; i++) {
471 zval *tmp;
472 DISPID lastid = 0; /* for props */
473 int isprop;
474
475 if (FAILED(ITypeInfo_GetFuncDesc(typeinfo, i, &func)))
476 break;
477
478 isprop = (func->invkind & DISPATCH_PROPERTYGET || func->invkind & DISPATCH_PROPERTYPUT);
479
480 if (!isprop || lastid != func->memid) {
481
482 lastid = func->memid;
483
484 ITypeInfo_GetDocumentation(typeinfo, func->memid, &olename, NULL, NULL, NULL);
485 ansiname = php_com_olestring_to_string(olename, &ansinamelen, codepage TSRMLS_CC);
486 SysFreeString(olename);
487
488 if (printdef) {
489 int j;
490 char *funcdesc;
491 unsigned int funcdesclen, cnames = 0;
492 BSTR *names;
493
494 names = (BSTR*)safe_emalloc((func->cParams + 1), sizeof(BSTR), 0);
495
496 ITypeInfo_GetNames(typeinfo, func->memid, names, func->cParams + 1, &cnames);
497 /* first element is the function name */
498 SysFreeString(names[0]);
499
500 php_printf("\t/* DISPID=%d */\n", func->memid);
501
502 if (func->elemdescFunc.tdesc.vt != VT_VOID) {
503 php_printf("\t/* %s [%d] */\n",
504 vt_to_string(func->elemdescFunc.tdesc.vt),
505 func->elemdescFunc.tdesc.vt
506 );
507 }
508
509 if (isprop) {
510
511 ITypeInfo_GetDocumentation(typeinfo, func->memid, NULL, &olename, NULL, NULL);
512 if (olename) {
513 funcdesc = php_com_olestring_to_string(olename, &funcdesclen, codepage TSRMLS_CC);
514 SysFreeString(olename);
515 php_printf("\t/* %s */\n", funcdesc);
516 efree(funcdesc);
517 }
518
519 php_printf("\tvar $%s;\n\n", ansiname);
520
521 } else {
522 /* a function */
523
524 php_printf("\tfunction %s(\n", ansiname);
525
526 for (j = 0; j < func->cParams; j++) {
527 ELEMDESC *elem = &func->lprgelemdescParam[j];
528
529 php_printf("\t\t/* %s [%d] ", vt_to_string(elem->tdesc.vt), elem->tdesc.vt);
530
531 if (elem->paramdesc.wParamFlags & PARAMFLAG_FIN)
532 php_printf("[in]");
533 if (elem->paramdesc.wParamFlags & PARAMFLAG_FOUT)
534 php_printf("[out]");
535
536 if (elem->tdesc.vt == VT_PTR) {
537 /* what does it point to ? */
538 php_printf(" --> %s [%d] ",
539 vt_to_string(elem->tdesc.lptdesc->vt),
540 elem->tdesc.lptdesc->vt
541 );
542 }
543
544 /* when we handle prop put and get, this will look nicer */
545 if (j+1 < (int)cnames) {
546 funcdesc = php_com_olestring_to_string(names[j+1], &funcdesclen, codepage TSRMLS_CC);
547 SysFreeString(names[j+1]);
548 } else {
549 funcdesc = "???";
550 }
551
552 php_printf(" */ %s%s%c\n",
553 elem->tdesc.vt == VT_PTR ? "&$" : "$",
554 funcdesc,
555 j == func->cParams - 1 ? ' ' : ','
556 );
557
558 if (j+1 < (int)cnames) {
559 efree(funcdesc);
560 }
561 }
562
563 php_printf("\t\t)\n\t{\n");
564
565 ITypeInfo_GetDocumentation(typeinfo, func->memid, NULL, &olename, NULL, NULL);
566 if (olename) {
567 funcdesc = php_com_olestring_to_string(olename, &funcdesclen, codepage TSRMLS_CC);
568 SysFreeString(olename);
569 php_printf("\t\t/* %s */\n", funcdesc);
570 efree(funcdesc);
571 }
572
573 php_printf("\t}\n");
574 }
575
576 efree(names);
577 }
578
579 if (id_to_name) {
580 zend_str_tolower(ansiname, ansinamelen);
581 MAKE_STD_ZVAL(tmp);
582 ZVAL_STRINGL(tmp, ansiname, ansinamelen, 0);
583 zend_hash_index_update(id_to_name, func->memid, (void*)&tmp, sizeof(zval *), NULL);
584 }
585 }
586 ITypeInfo_ReleaseFuncDesc(typeinfo, func);
587 }
588
589 if (printdef) {
590 php_printf("}\n");
591 }
592
593 ret = 1;
594 } else {
595 zend_error(E_WARNING, "That's not a dispatchable interface!! type kind = %08x", attr->typekind);
596 }
597
598 ITypeInfo_ReleaseTypeAttr(typeinfo, attr);
599
600 return ret;
601 }
602
603
604