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