You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1852 lines
50 KiB

25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
26 years ago
26 years ago
26 years ago
25 years ago
26 years ago
25 years ago
26 years ago
25 years ago
25 years ago
25 years ago
25 years ago
25 years ago
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 4 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1997-2002 The PHP Group |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 2.02 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available at through the world-wide-web at |
  10. | http://www.php.net/license/2_02.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: Zeev Suraski <zeev@zend.com> |
  16. | Harald Radi <h.radi@nme.at> |
  17. | Alan Brown <abrown@pobox.com> |
  18. +----------------------------------------------------------------------+
  19. */
  20. /*
  21. * This module implements support for COM components that support the IDispatch
  22. * interface. Both local (COM) and remote (DCOM) components can be accessed.
  23. *
  24. * Type libraries can be loaded (in order for PHP to recognize automation constants)
  25. * by specifying a typelib_file in the PHP .ini file. That file should contain
  26. * paths to type libraries, one in every line. By default, constants are registered
  27. * as case-sensitive. If you want them to be defined as case-insensitive, add
  28. * #case_insensitive or #cis at the end of the type library path.
  29. *
  30. * This is also the first module to demonstrate Zend's OO syntax overloading
  31. * capabilities. CORBA coders are invited to write a CORBA module as well!
  32. *
  33. * Zeev
  34. */
  35. /*
  36. * 28.12.2000
  37. * unicode conversion fixed by Harald Radi <h.radi@nme.at>
  38. *
  39. * now all these strange '?'s should be disapeared
  40. */
  41. /*
  42. * 28.1.2001
  43. * VARIANT datatype and pass_by_reference support
  44. */
  45. /*
  46. * 03.6.2001
  47. * Enhanced Typelib support to include a search by name
  48. */
  49. #ifdef PHP_WIN32
  50. #define _WIN32_DCOM
  51. #include <iostream.h>
  52. #include <math.h>
  53. #include "php.h"
  54. #include "php_ini.h"
  55. #include "php_COM.h"
  56. #include "php_VARIANT.h"
  57. static ITypeLib *php_COM_find_typelib(char *search_string, int mode TSRMLS_DC);
  58. static int php_COM_load_typelib(ITypeLib *TypeLib, int mode TSRMLS_DC);
  59. static int do_COM_offget(VARIANT *result, comval *array, pval *property, int cleanup TSRMLS_DC);
  60. static int do_COM_propget(VARIANT *var_result, comval *obj, pval *arg_property, int cleanup TSRMLS_DC);
  61. static void php_register_COM_class(TSRMLS_D);
  62. static void php_COM_init(int module_number TSRMLS_DC);
  63. static int le_comval;
  64. static int codepage;
  65. #ifdef _DEBUG
  66. int resourcecounter = 1;
  67. #endif
  68. function_entry COM_functions[] = {
  69. PHP_FE(com_load, NULL)
  70. PHP_FE(com_invoke, NULL)
  71. PHP_FE(com_addref, NULL)
  72. PHP_FE(com_release, NULL)
  73. PHP_FE(com_propget, NULL)
  74. PHP_FE(com_propput, NULL)
  75. PHP_FE(com_load_typelib, NULL)
  76. PHP_FE(com_isenum, NULL)
  77. PHP_FALIAS(com_get, com_propget, NULL)
  78. PHP_FALIAS(com_propset, com_propput, NULL)
  79. PHP_FALIAS(com_set, com_propput, NULL)
  80. { NULL, NULL, NULL }
  81. };
  82. static PHP_MINFO_FUNCTION(COM)
  83. {
  84. DISPLAY_INI_ENTRIES();
  85. }
  86. PHPAPI HRESULT php_COM_invoke(comval *obj, DISPID dispIdMember, WORD wFlags, DISPPARAMS FAR* pDispParams, VARIANT FAR* pVarResult, char **ErrString TSRMLS_DC)
  87. {
  88. HRESULT hr;
  89. int failed = FALSE;
  90. unsigned int ArgErr;
  91. EXCEPINFO ExceptInfo;
  92. *ErrString = NULL;
  93. /* @todo use DispInvoke here ? */
  94. if (C_ISREFD(obj)) {
  95. if (C_HASTLIB(obj)) {
  96. hr = C_TYPEINFO_VT(obj)->Invoke(C_TYPEINFO(obj), C_DISPATCH(obj), dispIdMember, wFlags, pDispParams, pVarResult, &ExceptInfo, &ArgErr);
  97. if (FAILED(hr)) {
  98. hr = C_DISPATCH_VT(obj)->Invoke(C_DISPATCH(obj), dispIdMember, &IID_NULL, LOCALE_SYSTEM_DEFAULT, wFlags, pDispParams, pVarResult, &ExceptInfo, &ArgErr);
  99. if (SUCCEEDED(hr)) {
  100. /*
  101. * ITypLib doesn't work
  102. * Release ITypeLib and fall back to IDispatch
  103. */
  104. C_TYPEINFO_VT(obj)->Release(C_TYPEINFO(obj));
  105. C_HASTLIB(obj) = FALSE;
  106. }
  107. }
  108. } else {
  109. hr = C_DISPATCH_VT(obj)->Invoke(C_DISPATCH(obj), dispIdMember, &IID_NULL, LOCALE_SYSTEM_DEFAULT, wFlags, pDispParams, pVarResult, &ExceptInfo, &ArgErr);
  110. }
  111. if (FAILED(hr)) {
  112. switch (hr) {
  113. case DISP_E_EXCEPTION: {
  114. int srclen=0;
  115. char *src=estrdup("");
  116. int desclen=0;
  117. char *desc=estrdup("");
  118. if (ExceptInfo.bstrSource)
  119. {
  120. src = php_OLECHAR_to_char(ExceptInfo.bstrSource, &srclen, codepage TSRMLS_CC);
  121. SysFreeString(ExceptInfo.bstrSource);
  122. }
  123. if (ExceptInfo.bstrDescription)
  124. {
  125. desc = php_OLECHAR_to_char(ExceptInfo.bstrDescription, &desclen, codepage TSRMLS_CC);
  126. SysFreeString(ExceptInfo.bstrDescription);
  127. }
  128. *ErrString = pemalloc(srclen+desclen+50, 1);
  129. sprintf(*ErrString, "<b>Source</b>: %s <b>Description</b>: %s", src, desc);
  130. efree(src);
  131. efree(desc);
  132. if (ExceptInfo.bstrHelpFile)
  133. {
  134. SysFreeString(ExceptInfo.bstrHelpFile);
  135. }
  136. }
  137. case DISP_E_PARAMNOTFOUND:
  138. case DISP_E_TYPEMISMATCH:
  139. *ErrString = pemalloc(25, 1);
  140. sprintf(*ErrString, "<b>Argument</b>: %d", pDispParams->cArgs-ArgErr+1);
  141. break;
  142. }
  143. }
  144. if (pVarResult && (V_VT(pVarResult) == VT_EMPTY)) {
  145. V_VT(pVarResult) = VT_I4;
  146. V_I4(pVarResult) = hr;
  147. }
  148. return hr;
  149. } else {
  150. return DISP_E_UNKNOWNINTERFACE;
  151. }
  152. }
  153. PHPAPI HRESULT php_COM_get_ids_of_names(comval *obj, OLECHAR FAR* FAR* rgszNames, DISPID FAR* rgDispId TSRMLS_DC)
  154. {
  155. HRESULT hr;
  156. if (C_ISREFD(obj)) {
  157. if (C_HASTLIB(obj)) {
  158. hr = C_TYPEINFO_VT(obj)->GetIDsOfNames(C_TYPEINFO(obj), rgszNames, 1, rgDispId);
  159. if (FAILED(hr)) {
  160. hr = C_DISPATCH_VT(obj)->GetIDsOfNames(C_DISPATCH(obj), &IID_NULL, rgszNames, 1, LOCALE_SYSTEM_DEFAULT, rgDispId);
  161. if (SUCCEEDED(hr)) {
  162. /*
  163. * ITypLib doesn't work
  164. * Release ITypeLib and fall back to IDispatch
  165. */
  166. C_TYPEINFO_VT(obj)->Release(C_TYPEINFO(obj));
  167. C_HASTLIB(obj) = FALSE;
  168. }
  169. }
  170. } else {
  171. hr = C_DISPATCH_VT(obj)->GetIDsOfNames(C_DISPATCH(obj), &IID_NULL, rgszNames, 1, LOCALE_SYSTEM_DEFAULT, rgDispId);
  172. }
  173. return hr;
  174. } else {
  175. return DISP_E_UNKNOWNINTERFACE;
  176. }
  177. }
  178. PHPAPI HRESULT php_COM_release(comval *obj TSRMLS_DC)
  179. {
  180. HRESULT hr;
  181. if (obj->refcount > 1) {
  182. C_RELEASE(obj);
  183. } else if (obj->refcount == 1) {
  184. if (C_HASTLIB(obj)) {
  185. C_TYPEINFO_VT(obj)->Release(C_TYPEINFO(obj));
  186. }
  187. if (C_HASENUM(obj)) {
  188. hr = C_ENUMVARIANT_VT(obj)->Release(C_ENUMVARIANT(obj));
  189. }
  190. hr = C_DISPATCH_VT(obj)->Release(C_DISPATCH(obj));
  191. C_RELEASE(obj);
  192. }
  193. return obj->refcount;
  194. }
  195. PHPAPI HRESULT php_COM_addref(comval *obj TSRMLS_DC)
  196. {
  197. if (C_ISREFD(obj)) {
  198. C_ADDREF(obj);
  199. }
  200. return obj->refcount;
  201. }
  202. PHPAPI HRESULT php_COM_set(comval *obj, IDispatch FAR* FAR* ppDisp, int cleanup TSRMLS_DC)
  203. {
  204. HRESULT hr = 1;
  205. DISPPARAMS dispparams;
  206. VARIANT *var_result;
  207. IDispatch FAR* pDisp;
  208. pDisp = *ppDisp;
  209. if (cleanup) {
  210. *ppDisp = NULL;
  211. }
  212. C_REFCOUNT(obj) = 1;
  213. C_DISPATCH(obj) = pDisp;
  214. C_HASTLIB(obj) = SUCCEEDED(C_DISPATCH_VT(obj)->GetTypeInfo(C_DISPATCH(obj), 0, LANG_NEUTRAL, &C_TYPEINFO(obj)));
  215. dispparams.rgvarg = NULL;
  216. dispparams.rgdispidNamedArgs = NULL;
  217. dispparams.cArgs = 0;
  218. dispparams.cNamedArgs = 0;
  219. ALLOC_VARIANT(var_result);
  220. if (C_HASENUM(obj) = SUCCEEDED(C_DISPATCH_VT(obj)->Invoke(C_DISPATCH(obj), DISPID_NEWENUM, &IID_NULL, LOCALE_SYSTEM_DEFAULT,
  221. DISPATCH_METHOD|DISPATCH_PROPERTYGET, &dispparams, var_result, NULL, NULL))) {
  222. if (V_VT(var_result) == VT_UNKNOWN) {
  223. C_HASENUM(obj) = SUCCEEDED(V_UNKNOWN(var_result)->lpVtbl->QueryInterface(V_UNKNOWN(var_result), &IID_IEnumVARIANT,
  224. (void**)&C_ENUMVARIANT(obj)));
  225. } else if (V_VT(var_result) == VT_DISPATCH) {
  226. C_HASENUM(obj) = SUCCEEDED(V_DISPATCH(var_result)->lpVtbl->QueryInterface(V_DISPATCH(var_result), &IID_IEnumVARIANT,
  227. (void**)&C_ENUMVARIANT(obj)));
  228. }
  229. }
  230. FREE_VARIANT(var_result);
  231. if (!cleanup) {
  232. hr = C_DISPATCH_VT(obj)->AddRef(C_DISPATCH(obj));
  233. }
  234. #ifdef _DEBUG
  235. obj->resourceindex = resourcecounter++;
  236. #endif
  237. return hr;
  238. }
  239. PHPAPI HRESULT php_COM_clone(comval *obj, comval *clone, int cleanup TSRMLS_DC)
  240. {
  241. HRESULT hr;
  242. C_HASTLIB(obj) = C_HASTLIB(clone);
  243. C_HASENUM(obj) = C_HASENUM(obj);
  244. C_DISPATCH(obj) = C_DISPATCH(clone);
  245. C_TYPEINFO(obj) = C_TYPEINFO(clone);
  246. if (cleanup || !C_ISREFD(obj)) {
  247. obj->refcount = clone->refcount;
  248. clone->refcount = 0;
  249. } else {
  250. if (C_HASTLIB(obj)) {
  251. C_TYPEINFO_VT(obj)->AddRef(C_TYPEINFO(obj));
  252. }
  253. if (C_HASENUM(obj)) {
  254. C_ENUMVARIANT_VT(obj)->AddRef(C_ENUMVARIANT(obj));
  255. }
  256. hr = C_DISPATCH_VT(obj)->AddRef(C_DISPATCH(obj));
  257. obj->refcount = 1;
  258. }
  259. #ifdef _DEBUG
  260. obj->resourceindex = resourcecounter++;
  261. #endif
  262. return hr;
  263. }
  264. PHPAPI char *php_COM_error_message(HRESULT hr TSRMLS_DC)
  265. {
  266. void *pMsgBuf;
  267. if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
  268. hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &pMsgBuf, 0, NULL)) {
  269. char error_string[] = "No description available";
  270. pMsgBuf = LocalAlloc(LMEM_FIXED, sizeof(error_string));
  271. memcpy(pMsgBuf, error_string, sizeof(error_string));
  272. }
  273. return pMsgBuf;
  274. }
  275. static char *php_string_from_clsid(const CLSID *clsid TSRMLS_DC)
  276. {
  277. LPOLESTR ole_clsid;
  278. char *clsid_str;
  279. StringFromCLSID(clsid, &ole_clsid);
  280. clsid_str = php_OLECHAR_to_char(ole_clsid, NULL, codepage TSRMLS_CC);
  281. LocalFree(ole_clsid);
  282. return clsid_str;
  283. }
  284. PHPAPI HRESULT php_COM_destruct(comval *obj TSRMLS_DC)
  285. {
  286. HRESULT hr = S_OK;
  287. if (C_ISREFD(obj)) {
  288. C_REFCOUNT(obj) = 1;
  289. hr = php_COM_release(obj TSRMLS_CC);
  290. }
  291. efree(obj);
  292. return hr;
  293. }
  294. static void php_comval_destructor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
  295. {
  296. php_COM_destruct(rsrc->ptr TSRMLS_CC);
  297. }
  298. static PHP_INI_MH(OnTypelibFileChange)
  299. {
  300. FILE *typelib_file;
  301. char *typelib_name_buffer;
  302. char *strtok_buf = NULL;
  303. int interactive;
  304. interactive = CG(interactive);
  305. if (!new_value || (typelib_file = VCWD_FOPEN(new_value, "r"))==NULL) {
  306. return FAILURE;
  307. }
  308. if (interactive) {
  309. printf("Loading type libraries...");
  310. fflush(stdout);
  311. }
  312. typelib_name_buffer = (char *) emalloc(sizeof(char)*1024);
  313. while (fgets(typelib_name_buffer, 1024, typelib_file)) {
  314. ITypeLib *pTL;
  315. char *typelib_name;
  316. char *modifier, *ptr;
  317. int mode = CONST_CS | CONST_PERSISTENT; /* CONST_PERSISTENT is ok here */
  318. if (typelib_name_buffer[0]==';') {
  319. continue;
  320. }
  321. typelib_name = php_strtok_r(typelib_name_buffer, "\r\n", &strtok_buf); /* get rid of newlines */
  322. if (typelib_name == NULL) {
  323. continue;
  324. }
  325. typelib_name = php_strtok_r(typelib_name, "#", &strtok_buf);
  326. modifier = php_strtok_r(NULL, "#", &strtok_buf);
  327. if (modifier != NULL) {
  328. if (!strcmp(modifier, "cis") || !strcmp(modifier, "case_insensitive")) {
  329. mode &= ~CONST_CS;
  330. }
  331. }
  332. /* Remove leading/training white spaces on search_string */
  333. while (isspace(*typelib_name)) {/* Ends on '\0' in worst case */
  334. typelib_name ++;
  335. }
  336. ptr = typelib_name + strlen(typelib_name) - 1;
  337. while ((ptr != typelib_name) && isspace(*ptr)) {
  338. *ptr = '\0';
  339. ptr--;
  340. }
  341. if (interactive) {
  342. printf("\rLoading %-60s\r", typelib_name);
  343. }
  344. if ((pTL = php_COM_find_typelib(typelib_name, mode TSRMLS_CC)) != NULL) {
  345. php_COM_load_typelib(pTL, mode TSRMLS_CC);
  346. pTL->lpVtbl->Release(pTL);
  347. }
  348. }
  349. efree(typelib_name_buffer);
  350. fclose(typelib_file);
  351. if (interactive) {
  352. printf("\r%70s\r", "");
  353. }
  354. return SUCCESS;
  355. }
  356. PHP_INI_BEGIN()
  357. PHP_INI_ENTRY_EX("com.allow_dcom", "0", PHP_INI_SYSTEM, NULL, php_ini_boolean_displayer_cb)
  358. PHP_INI_ENTRY_EX("com.autoregister_typelib", "0", PHP_INI_SYSTEM, NULL, php_ini_boolean_displayer_cb)
  359. PHP_INI_ENTRY_EX("com.autoregister_verbose", "0", PHP_INI_SYSTEM, NULL, php_ini_boolean_displayer_cb)
  360. PHP_INI_ENTRY_EX("com.autoregister_casesensitive", "1", PHP_INI_SYSTEM, NULL, php_ini_boolean_displayer_cb)
  361. PHP_INI_ENTRY("com.typelib_file", "", PHP_INI_SYSTEM, OnTypelibFileChange)
  362. PHP_INI_END()
  363. /* {{{ proto int com_load(string module_name [, string remote_host [, int codepage [, string typelib]]])
  364. Loads a COM module */
  365. PHP_FUNCTION(com_load)
  366. {
  367. pval *module_name, *code_page, *typelib = NULL, *server_name = NULL, *user_name=NULL, *password=NULL, *domain=NULL;
  368. CLSID clsid;
  369. HRESULT hr;
  370. OLECHAR *ProgID;
  371. comval *obj;
  372. char *error_message;
  373. char *clsid_str;
  374. int mode = 0;
  375. ITypeLib *pTL;
  376. CLSCTX flags = CLSCTX_SERVER;
  377. codepage = CP_ACP;
  378. switch (ZEND_NUM_ARGS()) {
  379. case 1:
  380. zend_get_parameters(ht, 1, &module_name);
  381. break;
  382. case 2:
  383. zend_get_parameters(ht, 2, &module_name, &server_name);
  384. break;
  385. case 3:
  386. zend_get_parameters(ht, 3, &module_name, &server_name, &code_page);
  387. convert_to_long_ex(&code_page);
  388. codepage = Z_LVAL_P(code_page);
  389. break;
  390. case 4:
  391. zend_get_parameters(ht, 4, &module_name, &server_name, &code_page, &typelib);
  392. convert_to_string_ex(&typelib);
  393. convert_to_long_ex(&code_page);
  394. codepage = Z_LVAL_P(code_page);
  395. break;
  396. default:
  397. ZEND_WRONG_PARAM_COUNT();
  398. }
  399. if (server_name != NULL) {
  400. /* if a server is passed, one obviously wants to instanciate a
  401. * remote server
  402. */
  403. flags = CLSCTX_REMOTE_SERVER;
  404. /* What is server name? A String or an array? */
  405. if (Z_TYPE_P(server_name) == IS_ARRAY) {
  406. pval **tmp;
  407. /* DAB: 22 Sept 2001 */
  408. /* Aha - we have a number of possible */
  409. /* arguments. They are in the hash */
  410. /* By name: Server, Domain, Username, Password */
  411. /* Flags. */
  412. /* This has been crafted to maintian maximum backward */
  413. /* compatability, If the server name is specified as a */
  414. /* string, then the function shoule behave as before */
  415. /* by defaulting username and password and using the */
  416. /* (I believe) incorrect CLSCTX_SERVER instantiation */
  417. /* paramter. However if server is specified in this array */
  418. /* then we use either CLSCTX_REMOTE_SERVER or whatever */
  419. /* flags are specified in the array */
  420. HashTable *ht = Z_ARRVAL(*server_name);
  421. if (FAILURE == zend_hash_find(ht, "Server", 7, (void **) &tmp)) {
  422. server_name = NULL;
  423. } else {
  424. server_name = *tmp;
  425. convert_to_string_ex(&server_name);
  426. /* CLSCTX_SERVER includes INPROC and LOCAL */
  427. /* SERVER. This means that any local server */
  428. /* will be instantiated BEFORE even looking */
  429. /* on a remote server. Thus if we have a */
  430. /* server name, probably we want to access */
  431. /* a remote machine or we would not have */
  432. /* bothered specifying it. So it would be */
  433. /* wrong to to connect locally. Futher, */
  434. /* unless the name passed is a GUID, there has */
  435. /* to be something to map the Prog.Id to GUID */
  436. /* and unless that has been modified to remove */
  437. /* the information about local instantiation */
  438. /* CLSCTX_SERVER would force a local instantiation */
  439. /* This setting can be overridden below if the user */
  440. /* specifies a flags element */
  441. flags = CLSCTX_REMOTE_SERVER;
  442. }
  443. if (FAILURE == zend_hash_find(ht, "Username", 9, (void **) &tmp)) {
  444. user_name = NULL;
  445. } else {
  446. user_name = *tmp;
  447. convert_to_string_ex(&user_name);
  448. }
  449. if (FAILURE == zend_hash_find(ht, "Domain", 7, (void **) &tmp)) {
  450. domain = NULL;
  451. } else {
  452. domain = *tmp;
  453. convert_to_string_ex(&domain);
  454. }
  455. if (FAILURE == zend_hash_find(ht, "Password", 9, (void **) &tmp)) {
  456. password=NULL;
  457. } else {
  458. password = *tmp;
  459. convert_to_string_ex(&password);
  460. }
  461. if (SUCCESS == zend_hash_find(ht, "Flags", 6, (void **) &tmp)) {
  462. convert_to_long_ex(tmp);
  463. flags = (CLSCTX) Z_LVAL_PP(tmp);
  464. }
  465. }
  466. if (Z_TYPE_P(server_name) == IS_NULL) {
  467. server_name = NULL;
  468. } else {
  469. if (!INI_INT("com.allow_dcom")) {
  470. php_error(E_WARNING, "DCOM is disabled");
  471. RETURN_FALSE;
  472. } else {
  473. convert_to_string_ex(&server_name);
  474. }
  475. }
  476. }
  477. ALLOC_COM(obj);
  478. convert_to_string_ex(&module_name);
  479. ProgID = php_char_to_OLECHAR(Z_STRVAL_P(module_name), Z_STRLEN_P(module_name), codepage TSRMLS_CC);
  480. /* obtain CLSID */
  481. if (FAILED(CLSIDFromString(ProgID, &clsid))) {
  482. /* Perhaps this is a Moniker? */
  483. IBindCtx *pBindCtx;
  484. IMoniker *pMoniker;
  485. ULONG ulEaten;
  486. /* @todo if (server_name) */
  487. if (!server_name) {
  488. /* @todo shouldn't the bind context be fetched on module startup and kept as a global shared instance ?
  489. * all calls to BindToObject would deliver the same instance then (as desired)
  490. * IBindCtx::RegisterObjectBound() should be called then after mkparsedisplayname()
  491. *
  492. * @todo use mkparsedisplaynameex() ?
  493. */
  494. if (SUCCEEDED(hr = CreateBindCtx(0, &pBindCtx))) {
  495. if (SUCCEEDED(hr = MkParseDisplayName(pBindCtx, ProgID, &ulEaten, &pMoniker))) {
  496. hr = pMoniker->lpVtbl->BindToObject(pMoniker, pBindCtx, NULL, &IID_IDispatch, (LPVOID *) &C_DISPATCH(obj));
  497. pMoniker->lpVtbl->Release(pMoniker);
  498. }
  499. pBindCtx->lpVtbl->Release(pBindCtx);
  500. }
  501. } else {
  502. hr = MK_E_SYNTAX;
  503. }
  504. efree(ProgID);
  505. if (FAILED(hr)) {
  506. php_COM_destruct(obj TSRMLS_CC);
  507. error_message = php_COM_error_message(hr TSRMLS_CC);
  508. php_error(E_WARNING,"Invalid ProgID, GUID string, or Moniker: %s", error_message);
  509. LocalFree(error_message);
  510. RETURN_FALSE;
  511. }
  512. } else {
  513. efree(ProgID);
  514. /* obtain IDispatch */
  515. if (!server_name) {
  516. hr = CoCreateInstance(&clsid, NULL, flags, &IID_IDispatch, (LPVOID *) &C_DISPATCH(obj));
  517. } else {
  518. COSERVERINFO server_info;
  519. MULTI_QI pResults;
  520. COAUTHIDENTITY authid;
  521. COAUTHINFO authinfo = {RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, &authid, EOAC_NONE};
  522. server_info.dwReserved1=0;
  523. server_info.dwReserved2=0;
  524. server_info.pwszName = php_char_to_OLECHAR(Z_STRVAL_P(server_name), Z_STRLEN_P(server_name), codepage TSRMLS_CC);
  525. if (user_name) {
  526. /* Z_STRVAL_P(user_name); */
  527. /* Parse Username into domain\username */
  528. authid.User = (WCHAR *) Z_STRVAL_P(user_name);
  529. authid.UserLength = Z_STRLEN_P(user_name);
  530. if (password) {
  531. authid.Password = (USHORT *) Z_STRVAL_P(password);
  532. authid.PasswordLength = Z_STRLEN_P(password);
  533. } else {
  534. authid.Password = (USHORT *) "";
  535. authid.PasswordLength = 0;
  536. }
  537. if (domain) {
  538. authid.Domain = (USHORT *) Z_STRVAL_P(domain);
  539. authid.DomainLength = Z_STRLEN_P(domain);
  540. } else {
  541. authid.Domain = (USHORT *) "";
  542. authid.DomainLength = 0;
  543. }
  544. authid.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
  545. server_info.pAuthInfo=&authinfo;
  546. } else {
  547. server_info.pAuthInfo=NULL;
  548. }
  549. pResults.pIID = &IID_IDispatch;
  550. pResults.pItf = NULL;
  551. pResults.hr = S_OK;
  552. hr=CoCreateInstanceEx(&clsid, NULL, flags, &server_info, 1, &pResults);
  553. if (SUCCEEDED(hr)) {
  554. hr = pResults.hr;
  555. C_DISPATCH(obj) = (IDispatch *) pResults.pItf;
  556. }
  557. efree(server_info.pwszName);
  558. }
  559. if (FAILED(hr)) {
  560. error_message = php_COM_error_message(hr TSRMLS_CC);
  561. clsid_str = php_string_from_clsid(&clsid TSRMLS_CC);
  562. php_error(E_WARNING,"Unable to obtain IDispatch interface for CLSID %s: %s",clsid_str,error_message);
  563. LocalFree(error_message);
  564. efree(clsid_str);
  565. php_COM_destruct(obj TSRMLS_CC);
  566. RETURN_FALSE;
  567. }
  568. }
  569. php_COM_set(obj, &C_DISPATCH(obj), TRUE TSRMLS_CC);
  570. if (INI_INT("com.autoregister_casesensitive")) {
  571. mode |= CONST_CS;
  572. }
  573. if (C_HASTLIB(obj)) {
  574. if (INI_INT("com.autoregister_typelib")) {
  575. unsigned int idx;
  576. /* @todo check if typlib isn't already loaded */
  577. if (C_TYPEINFO_VT(obj)->GetContainingTypeLib(C_TYPEINFO(obj), &pTL, &idx) == S_OK) {
  578. php_COM_load_typelib(pTL, mode TSRMLS_CC);
  579. pTL->lpVtbl->Release(pTL);
  580. }
  581. }
  582. } else {
  583. if (typelib != NULL) {
  584. ITypeLib *pTL;
  585. if ((pTL = php_COM_find_typelib(Z_STRVAL_P(typelib), mode TSRMLS_CC)) != NULL) {
  586. C_HASTLIB(obj) = SUCCEEDED(pTL->lpVtbl->GetTypeInfo(pTL, 0, &C_TYPEINFO(obj)));
  587. /* idx 0 should deliver the ITypeInfo for the IDispatch Interface */
  588. if (INI_INT("com.autoregister_typelib")) {
  589. php_COM_load_typelib(pTL, mode TSRMLS_CC);
  590. }
  591. pTL->lpVtbl->Release(pTL);
  592. }
  593. }
  594. }
  595. RETURN_LONG(zend_list_insert(obj, IS_COM));
  596. }
  597. /* }}} */
  598. int do_COM_invoke(comval *obj, pval *function_name, VARIANT *var_result, pval **arguments, int arg_count TSRMLS_DC)
  599. {
  600. DISPID dispid;
  601. DISPPARAMS dispparams;
  602. HRESULT hr;
  603. OLECHAR *funcname;
  604. SAFEARRAY *pSA;
  605. SAFEARRAYBOUND rgsabound[1];
  606. VARIANT *variant_args;
  607. char *error_message;
  608. int current_arg, current_variant;
  609. unsigned long count;
  610. if (C_HASENUM(obj) && strstr(Z_STRVAL_P(function_name), "next")) {
  611. /* Grab one argument off the stack, allocate enough
  612. * VARIANTs
  613. * Get the IEnumVariant interface and call ->Next();
  614. */
  615. switch (arg_count) {
  616. case 0:
  617. count = 1;
  618. break;
  619. case 1:
  620. convert_to_long_ex(&arguments[0]);
  621. count = Z_LVAL_P(arguments[0]);
  622. break;
  623. default:
  624. php_error(E_WARNING,"Wrong argument count to IEnumVariant::Next()");
  625. return FAILURE;
  626. }
  627. rgsabound[0].lLbound = 0;
  628. rgsabound[0].cElements = count;
  629. if ((pSA = SafeArrayCreate(VT_VARIANT, 1, rgsabound)) == NULL) {
  630. VariantInit(var_result);
  631. return FAILURE;
  632. } else {
  633. V_ARRAY(var_result) = pSA;
  634. V_VT(var_result) = VT_VARIANT|VT_ARRAY;
  635. }
  636. if (FAILED(hr = C_ENUMVARIANT_VT(obj)->Next(C_ENUMVARIANT(obj), count, pSA->pvData, &count))) {
  637. char *error_message = php_COM_error_message(hr TSRMLS_CC);
  638. php_error(E_WARNING,"IEnumVariant::Next() failed: %s", error_message);
  639. efree(error_message);
  640. VariantClear(var_result);
  641. return FAILURE;
  642. }
  643. if (count != rgsabound[0].cElements) {
  644. rgsabound[0].cElements = count;
  645. if (FAILED(SafeArrayRedim(pSA, rgsabound))) {
  646. char *error_message = php_COM_error_message(hr TSRMLS_CC);
  647. php_error(E_WARNING,"IEnumVariant::Next() failed: %s", error_message);
  648. efree(error_message);
  649. VariantClear(var_result);
  650. return FAILURE;
  651. }
  652. }
  653. /* return a single element if next() was called without count */
  654. if ((arg_count == 0) && (count == 1)) {
  655. long index[] = {0};
  656. SafeArrayGetElement(pSA, index, var_result);
  657. SafeArrayDestroy(pSA);
  658. }
  659. return SUCCESS;
  660. } else if (C_HASENUM(obj) && strstr(Z_STRVAL_P(function_name), "all")) {
  661. #define FETCH_BLOCKSIZE 10 /* fetch blocks of 10 elements */
  662. count = FETCH_BLOCKSIZE;
  663. rgsabound[0].lLbound = 0;
  664. rgsabound[0].cElements = count;
  665. if ((pSA = SafeArrayCreate(VT_VARIANT, 1, rgsabound)) == NULL) {
  666. VariantInit(var_result);
  667. return FAILURE;
  668. } else {
  669. V_ARRAY(var_result) = pSA;
  670. V_VT(var_result) = VT_VARIANT|VT_ARRAY;
  671. }
  672. /* blah*/
  673. } else if (C_HASENUM(obj) && strstr(Z_STRVAL_P(function_name), "reset")) {
  674. if (FAILED(hr = C_ENUMVARIANT_VT(obj)->Reset(C_ENUMVARIANT(obj)))) {
  675. char *error_message = php_COM_error_message(hr TSRMLS_CC);
  676. php_error(E_WARNING,"IEnumVariant::Next() failed: %s", error_message);
  677. efree(error_message);
  678. return FAILURE;
  679. }
  680. return SUCCESS;
  681. } else if (C_HASENUM(obj) && strstr(Z_STRVAL_P(function_name), "skip")) {
  682. unsigned long count;
  683. switch (arg_count) {
  684. case 0:
  685. count = 1;
  686. break;
  687. case 1:
  688. convert_to_long_ex(&arguments[0]);
  689. count = Z_LVAL_P(arguments[0]);
  690. break;
  691. default:
  692. php_error(E_WARNING,"Wrong argument count to IEnumVariant::Skip()");
  693. return FAILURE;
  694. }
  695. if (FAILED(hr = C_ENUMVARIANT_VT(obj)->Skip(C_ENUMVARIANT(obj), count))) {
  696. char *error_message = php_COM_error_message(hr TSRMLS_CC);
  697. php_error(E_WARNING,"IEnumVariant::Next() failed: %s", error_message);
  698. efree(error_message);
  699. return FAILURE;
  700. }
  701. return SUCCESS;
  702. } else {
  703. char *ErrString;
  704. funcname = php_char_to_OLECHAR(Z_STRVAL_P(function_name), Z_STRLEN_P(function_name), codepage TSRMLS_CC);
  705. hr = php_COM_get_ids_of_names(obj, &funcname, &dispid TSRMLS_CC);
  706. if (FAILED(hr)) {
  707. error_message = php_COM_error_message(hr TSRMLS_CC);
  708. php_error(E_WARNING,"Unable to lookup %s: %s", Z_STRVAL_P(function_name), error_message);
  709. LocalFree(error_message);
  710. efree(funcname);
  711. return FAILURE;
  712. }
  713. variant_args = (VARIANT *) emalloc(sizeof(VARIANT) * arg_count);
  714. for (current_arg=0; current_arg<arg_count; current_arg++) {
  715. current_variant = arg_count - current_arg - 1;
  716. php_pval_to_variant(arguments[current_arg], &variant_args[current_variant], codepage TSRMLS_CC);
  717. }
  718. dispparams.rgvarg = variant_args;
  719. dispparams.rgdispidNamedArgs = NULL;
  720. dispparams.cArgs = arg_count;
  721. dispparams.cNamedArgs = 0;
  722. hr = php_COM_invoke(obj, dispid, DISPATCH_METHOD|DISPATCH_PROPERTYGET, &dispparams, var_result, &ErrString TSRMLS_CC);
  723. efree(funcname);
  724. for (current_arg=0;current_arg<arg_count;current_arg++) {
  725. /* don't release IDispatch pointers as they are used afterwards */
  726. if (V_VT(&variant_args[current_arg]) != VT_DISPATCH) {
  727. /* @todo review this: what happens to an array of IDispatchs or a VARIANT->IDispatch */
  728. VariantClear(&variant_args[current_arg]);
  729. }
  730. }
  731. efree(variant_args);
  732. if (FAILED(hr)) {
  733. error_message = php_COM_error_message(hr TSRMLS_CC);
  734. if (ErrString) {
  735. php_error(E_WARNING,"Invoke() failed: %s %s", error_message, ErrString);
  736. pefree(ErrString, 1);
  737. } else {
  738. php_error(E_WARNING,"Invoke() failed: %s", error_message);
  739. }
  740. LocalFree(error_message);
  741. return FAILURE;
  742. }
  743. }
  744. return SUCCESS;
  745. }
  746. /* {{{ proto mixed com_invoke(int module, string handler_name [, mixed arg [, mixed ...]])
  747. Invokes a COM module */
  748. PHP_FUNCTION(com_invoke)
  749. {
  750. pval **arguments;
  751. pval *object, *function_name;
  752. comval *obj;
  753. int type;
  754. int arg_count = ZEND_NUM_ARGS();
  755. VARIANT *var_result;
  756. if (arg_count<2) {
  757. ZEND_WRONG_PARAM_COUNT();
  758. }
  759. arguments = (pval **) emalloc(sizeof(pval *)*arg_count);
  760. if (zend_get_parameters_array(ht, arg_count, arguments) == FAILURE) {
  761. RETURN_FALSE;
  762. }
  763. object = arguments[0];
  764. function_name = arguments[1];
  765. /* obtain IDispatch interface */
  766. convert_to_long(object);
  767. obj = (comval *)zend_list_find(Z_LVAL_P(object), &type);
  768. if (!obj || (type != IS_COM)) {
  769. php_error(E_WARNING,"%d is not a COM object handler", Z_STRVAL_P(function_name));
  770. RETURN_FALSE;
  771. }
  772. /* obtain property/method handler */
  773. convert_to_string_ex(&function_name);
  774. ALLOC_VARIANT(var_result);
  775. if (do_COM_invoke(obj, function_name, var_result, arguments+2, arg_count-2 TSRMLS_CC)==FAILURE) {
  776. FREE_VARIANT(var_result);
  777. efree(arguments);
  778. RETURN_FALSE;
  779. }
  780. RETVAL_VARIANT(var_result);
  781. efree(arguments);
  782. }
  783. /* }}} */
  784. /* {{{ proto mixed com_release(int module)
  785. Releases a COM object */
  786. PHP_FUNCTION(com_release)
  787. {
  788. pval *object;
  789. comval *obj;
  790. int type;
  791. int arg_count = ZEND_NUM_ARGS();
  792. if (arg_count != 1) {
  793. ZEND_WRONG_PARAM_COUNT();
  794. }
  795. if (zend_get_parameters(ht, 1, &object)==FAILURE) {
  796. RETURN_FALSE;
  797. }
  798. /* obtain IDispatch interface */
  799. convert_to_long_ex(&object);
  800. obj = (comval *)zend_list_find(Z_LVAL_P(object), &type);
  801. if (!obj || (type != IS_COM)) {
  802. php_error(E_WARNING,"%d is not a COM object handler");
  803. RETURN_FALSE;
  804. }
  805. RETURN_LONG(php_COM_release(obj TSRMLS_CC))
  806. }
  807. /* }}} */
  808. /* {{{ proto mixed com_addref(int module)
  809. Increases the reference counter on a COM object */
  810. PHP_FUNCTION(com_addref)
  811. {
  812. pval *object;
  813. comval *obj;
  814. int type;
  815. int arg_count = ZEND_NUM_ARGS();
  816. if (arg_count != 1) {
  817. ZEND_WRONG_PARAM_COUNT();
  818. }
  819. if (zend_get_parameters(ht, 1, &object)==FAILURE) {
  820. RETURN_FALSE;
  821. }
  822. /* obtain IDispatch interface */
  823. convert_to_long_ex(&object);
  824. obj = (comval *)zend_list_find(Z_LVAL_P(object), &type);
  825. if (!obj || (type != IS_COM))
  826. {
  827. php_error(E_WARNING,"%d is not a COM object handler");
  828. RETURN_FALSE;
  829. }
  830. RETURN_LONG(php_COM_addref(obj TSRMLS_CC));
  831. }
  832. /* }}} */
  833. static int do_COM_offget(VARIANT *result, comval *array, pval *property, int cleanup TSRMLS_DC)
  834. {
  835. pval function_name;
  836. int retval;
  837. ZVAL_STRINGL(&function_name, "Item", 4, 0);
  838. retval = do_COM_invoke(array, &function_name, result, &property, 1 TSRMLS_CC);
  839. if (cleanup) {
  840. php_COM_destruct(array TSRMLS_CC);
  841. }
  842. return retval;
  843. }
  844. static int do_COM_propget(VARIANT *var_result, comval *obj, pval *arg_property, int cleanup TSRMLS_DC)
  845. {
  846. DISPID dispid;
  847. HRESULT hr;
  848. OLECHAR *propname;
  849. char *error_message;
  850. DISPPARAMS dispparams;
  851. char *ErrString;
  852. /* obtain property handler */
  853. propname = php_char_to_OLECHAR(Z_STRVAL_P(arg_property), Z_STRLEN_P(arg_property), codepage TSRMLS_CC);
  854. hr = php_COM_get_ids_of_names(obj, &propname, &dispid TSRMLS_CC);
  855. if (FAILED(hr)) {
  856. error_message = php_COM_error_message(hr TSRMLS_CC);
  857. php_error(E_WARNING,"Unable to lookup %s: %s", Z_STRVAL_P(arg_property), error_message);
  858. LocalFree(error_message);
  859. efree(propname);
  860. if (cleanup) {
  861. php_COM_destruct(obj TSRMLS_CC);
  862. }
  863. return FAILURE;
  864. }
  865. dispparams.cArgs = 0;
  866. dispparams.cNamedArgs = 0;
  867. hr = php_COM_invoke(obj, dispid, DISPATCH_PROPERTYGET, &dispparams, var_result, &ErrString TSRMLS_CC);
  868. if (FAILED(hr)) {
  869. error_message = php_COM_error_message(hr TSRMLS_CC);
  870. if (ErrString) {
  871. php_error(E_WARNING,"PropGet() failed: %s %s", error_message, ErrString);
  872. pefree(ErrString, 1);
  873. } else {
  874. php_error(E_WARNING,"PropGet() failed: %s", error_message);
  875. }
  876. LocalFree(error_message);
  877. efree(propname);
  878. if (cleanup) {
  879. php_COM_destruct(obj TSRMLS_CC);
  880. }
  881. return FAILURE;
  882. }
  883. efree(propname);
  884. if (cleanup) {
  885. php_COM_destruct(obj TSRMLS_CC);
  886. }
  887. return SUCCESS;
  888. }
  889. static void do_COM_propput(pval *return_value, comval *obj, pval *arg_property, pval *value TSRMLS_DC)
  890. {
  891. DISPID dispid;
  892. HRESULT hr;
  893. OLECHAR *propname;
  894. char *error_message;
  895. VARIANT *var_result, *new_value;
  896. DISPPARAMS dispparams;
  897. DISPID mydispid = DISPID_PROPERTYPUT;
  898. char *ErrString;
  899. ALLOC_VARIANT(var_result);
  900. ALLOC_VARIANT(new_value);
  901. /* obtain property handler */
  902. propname = php_char_to_OLECHAR(Z_STRVAL_P(arg_property), Z_STRLEN_P(arg_property), codepage TSRMLS_CC);
  903. hr = php_COM_get_ids_of_names(obj, &propname, &dispid TSRMLS_CC);
  904. if (FAILED(hr)) {
  905. error_message = php_COM_error_message(hr TSRMLS_CC);
  906. php_error(E_WARNING,"Unable to lookup %s: %s", Z_STRVAL_P(arg_property), error_message);
  907. LocalFree(error_message);
  908. efree(propname);
  909. FREE_VARIANT(var_result);
  910. FREE_VARIANT(new_value);
  911. RETURN_FALSE;
  912. }
  913. php_pval_to_variant(value, new_value, codepage TSRMLS_CC);
  914. dispparams.rgvarg = new_value;
  915. dispparams.rgdispidNamedArgs = &mydispid;
  916. dispparams.cArgs = 1;
  917. dispparams.cNamedArgs = 1;
  918. hr = php_COM_invoke(obj, dispid, DISPATCH_PROPERTYPUT, &dispparams, NULL, &ErrString TSRMLS_CC);
  919. if (FAILED(hr)) {
  920. error_message = php_COM_error_message(hr TSRMLS_CC);
  921. if (ErrString) {
  922. php_error(E_WARNING,"PropPut() failed: %s %s", error_message, ErrString);
  923. pefree(ErrString, 1);
  924. } else {
  925. php_error(E_WARNING,"PropPut() failed: %s", error_message);
  926. }
  927. LocalFree(error_message);
  928. efree(propname);
  929. FREE_VARIANT(var_result);
  930. FREE_VARIANT(new_value);
  931. RETURN_FALSE;
  932. }
  933. dispparams.cArgs = 0;
  934. dispparams.cNamedArgs = 0;
  935. hr = php_COM_invoke(obj, dispid, DISPATCH_PROPERTYGET, &dispparams, var_result, &ErrString TSRMLS_CC);
  936. if (SUCCEEDED(hr)) {
  937. php_variant_to_pval(var_result, return_value, codepage TSRMLS_CC);
  938. } else {
  939. *return_value = *value;
  940. zval_copy_ctor(return_value);
  941. }
  942. if (ErrString) {
  943. pefree(ErrString, 1);
  944. }
  945. FREE_VARIANT(var_result);
  946. FREE_VARIANT(new_value);
  947. efree(propname);
  948. }
  949. /* {{{ proto mixed com_propget(int module, string property_name)
  950. Gets properties from a COM module */
  951. PHP_FUNCTION(com_propget)
  952. {
  953. pval *arg_comval, *arg_property;
  954. int type;
  955. comval *obj;
  956. VARIANT *var_result;
  957. if ((ZEND_NUM_ARGS() != 2) || (zend_get_parameters(ht, 2, &arg_comval, &arg_property) == FAILURE)) {
  958. ZEND_WRONG_PARAM_COUNT();
  959. }
  960. /* obtain IDispatch interface */
  961. convert_to_long(arg_comval);
  962. obj = (comval *)zend_list_find(Z_LVAL_P(arg_comval), &type);
  963. if (!obj || (type != IS_COM)) {
  964. php_error(E_WARNING,"%d is not a COM object handler", Z_LVAL_P(arg_comval));
  965. RETURN_FALSE;
  966. }
  967. convert_to_string_ex(&arg_property);
  968. ALLOC_VARIANT(var_result);
  969. if (do_COM_propget(var_result, obj, arg_property, FALSE TSRMLS_CC) == FAILURE) {
  970. FREE_VARIANT(var_result);
  971. RETURN_FALSE;
  972. }
  973. RETVAL_VARIANT(var_result);
  974. }
  975. /* }}} */
  976. /* {{{ proto bool com_propput(int module, string property_name, mixed value)
  977. Puts the properties for a module */
  978. PHP_FUNCTION(com_propput)
  979. {
  980. pval *arg_comval, *arg_property, *arg_value;
  981. int type;
  982. comval *obj;
  983. if (ZEND_NUM_ARGS()!=3 || zend_get_parameters(ht, 3, &arg_comval, &arg_property, &arg_value)==FAILURE) {
  984. ZEND_WRONG_PARAM_COUNT();
  985. }
  986. /* obtain comval interface */
  987. convert_to_long(arg_comval);
  988. /* obtain comval interface */
  989. obj = (comval *)zend_list_find(Z_LVAL_P(arg_comval), &type);
  990. if (!obj || (type != IS_COM)) {
  991. php_error(E_WARNING,"%d is not a COM object handler", Z_LVAL_P(arg_comval));
  992. RETURN_FALSE;
  993. }
  994. convert_to_string_ex(&arg_property);
  995. do_COM_propput(return_value, obj, arg_property, arg_value TSRMLS_CC);
  996. }
  997. /* }}} */
  998. /* {{{ proto bool com_load_typelib(string typelib_name [, int case_insensitive])
  999. Loads a Typelib */
  1000. PHP_FUNCTION(com_load_typelib)
  1001. {
  1002. pval *arg_typelib, *arg_cis;
  1003. ITypeLib *pTL;
  1004. int mode = CONST_CS;
  1005. switch (ZEND_NUM_ARGS()) {
  1006. case 1:
  1007. zend_get_parameters(ht, 1, &arg_typelib);
  1008. break;
  1009. case 2:
  1010. zend_get_parameters(ht, 2, &arg_typelib, &arg_cis);
  1011. convert_to_boolean_ex(&arg_cis);
  1012. if (Z_LVAL_P(arg_cis)) {
  1013. mode &= ~CONST_CS;
  1014. }
  1015. break;
  1016. default:
  1017. ZEND_WRONG_PARAM_COUNT();
  1018. }
  1019. convert_to_string_ex(&arg_typelib);
  1020. pTL = php_COM_find_typelib(Z_STRVAL_P(arg_typelib), mode TSRMLS_CC);
  1021. if (php_COM_load_typelib(pTL, mode TSRMLS_CC) == SUCCESS) {
  1022. pTL->lpVtbl->Release(pTL);
  1023. RETURN_TRUE;
  1024. } else {
  1025. RETURN_FALSE;
  1026. }
  1027. }
  1028. /* }}} */
  1029. PHPAPI pval php_COM_get_property_handler(zend_property_reference *property_reference)
  1030. {
  1031. zend_overloaded_element *overloaded_property;
  1032. zend_llist_element *element;
  1033. pval return_value;
  1034. pval **comval_handle;
  1035. pval *object = property_reference->object;
  1036. int type;
  1037. comval *obj, *obj_prop;
  1038. VARIANT *var_result;
  1039. TSRMLS_FETCH();
  1040. INIT_ZVAL(return_value);
  1041. ZVAL_NULL(&return_value);
  1042. /* fetch the IDispatch interface */
  1043. zend_hash_index_find(Z_OBJPROP_P(object), 0, (void **) &comval_handle);
  1044. obj = (comval *) zend_list_find(Z_LVAL_P(*comval_handle), &type);
  1045. if (!obj || (type != IS_COM)) {
  1046. return return_value;
  1047. }
  1048. ALLOC_COM(obj_prop);
  1049. ALLOC_VARIANT(var_result);
  1050. for (element=property_reference->elements_list->head; element; element=element->next) {
  1051. overloaded_property = (zend_overloaded_element *) element->data;
  1052. switch (Z_TYPE_P(overloaded_property)) {
  1053. case OE_IS_ARRAY:
  1054. if (do_COM_offget(var_result, obj, &overloaded_property->element, FALSE TSRMLS_CC) == FAILURE) {
  1055. FREE_VARIANT(var_result);
  1056. FREE_COM(obj_prop);
  1057. return return_value;
  1058. }
  1059. break;
  1060. case OE_IS_OBJECT:
  1061. if (do_COM_propget(var_result, obj, &overloaded_property->element, FALSE TSRMLS_CC) == FAILURE) {
  1062. FREE_VARIANT(var_result);
  1063. FREE_COM(obj_prop);
  1064. return return_value;
  1065. }
  1066. break;
  1067. case OE_IS_METHOD: {
  1068. FREE_VARIANT(var_result);
  1069. if (obj != obj_prop) {
  1070. FREE_COM(obj_prop);
  1071. return_value = *object;
  1072. ZVAL_ADDREF(&return_value);
  1073. } else {
  1074. RETVAL_COM(obj);
  1075. }
  1076. return return_value;
  1077. }
  1078. break;
  1079. }
  1080. if (V_VT(var_result) == VT_DISPATCH) {
  1081. if (V_DISPATCH(var_result) == NULL) {
  1082. FREE_VARIANT(var_result);
  1083. FREE_COM(obj_prop);
  1084. return return_value;
  1085. }
  1086. obj = obj_prop;
  1087. php_COM_set(obj, &V_DISPATCH(var_result), TRUE TSRMLS_CC);
  1088. } else {
  1089. php_variant_to_pval(var_result, &return_value, codepage TSRMLS_CC);
  1090. FREE_COM(obj_prop);
  1091. obj_prop = NULL;
  1092. }
  1093. VariantInit(var_result); // to protect C_DISPATCH(obj) from being freed when var_result is destructed
  1094. pval_destructor(&overloaded_property->element);
  1095. }
  1096. if (obj_prop != NULL) {
  1097. RETVAL_COM(obj);
  1098. }
  1099. FREE_VARIANT(var_result);
  1100. return return_value;
  1101. }
  1102. PHPAPI int php_COM_set_property_handler(zend_property_reference *property_reference, pval *value)
  1103. {
  1104. pval result;
  1105. zend_overloaded_element *overloaded_property;
  1106. zend_llist_element *element;
  1107. pval **comval_handle;
  1108. pval *object = property_reference->object;
  1109. comval *obj, *obj_prop;
  1110. int type;
  1111. VARIANT *var_result;
  1112. TSRMLS_FETCH();
  1113. /* fetch the IDispatch interface */
  1114. zend_hash_index_find(Z_OBJPROP_P(object), 0, (void **) &comval_handle);
  1115. obj = (comval *)zend_list_find(Z_LVAL_P(*comval_handle), &type);
  1116. if (!obj || (type != IS_COM)) {
  1117. return FAILURE;
  1118. }
  1119. ALLOC_COM(obj_prop);
  1120. ALLOC_VARIANT(var_result);
  1121. for (element=property_reference->elements_list->head; element != property_reference->elements_list->tail; element=element->next) {
  1122. overloaded_property = (zend_overloaded_element *) element->data;
  1123. switch (Z_TYPE_P(overloaded_property)) {
  1124. case OE_IS_ARRAY:
  1125. if (do_COM_offget(var_result, obj, &overloaded_property->element, FALSE TSRMLS_CC) == FAILURE) {
  1126. FREE_VARIANT(var_result);
  1127. FREE_COM(obj_prop);
  1128. return FAILURE;
  1129. }
  1130. break;
  1131. case OE_IS_OBJECT:
  1132. if (do_COM_propget(var_result, obj, &overloaded_property->element, FALSE TSRMLS_CC) == FAILURE) {
  1133. FREE_VARIANT(var_result);
  1134. FREE_COM(obj_prop);
  1135. return FAILURE;
  1136. }
  1137. break;
  1138. case OE_IS_METHOD:
  1139. /* this shouldn't happen */
  1140. return FAILURE;
  1141. break;
  1142. }
  1143. if (V_VT(var_result) == VT_DISPATCH) {
  1144. if (V_DISPATCH(var_result) == NULL) {
  1145. FREE_VARIANT(var_result);
  1146. FREE_COM(obj_prop);
  1147. return FAILURE;
  1148. }
  1149. obj = obj_prop;
  1150. php_COM_set(obj, &V_DISPATCH(var_result), TRUE TSRMLS_CC);
  1151. } else {
  1152. FREE_COM(obj_prop);
  1153. FREE_VARIANT(var_result);
  1154. return FAILURE;
  1155. }
  1156. VariantInit(var_result); // to protect C_DISPATCH(obj) from being freed when var_result is destructed
  1157. pval_destructor(&overloaded_property->element);
  1158. }
  1159. FREE_VARIANT(var_result);
  1160. overloaded_property = (zend_overloaded_element *) element->data;
  1161. do_COM_propput(&result, obj, &overloaded_property->element, value TSRMLS_CC);
  1162. FREE_COM(obj_prop);
  1163. pval_destructor(&overloaded_property->element);
  1164. return SUCCESS;
  1165. }
  1166. PHPAPI void php_COM_call_function_handler(INTERNAL_FUNCTION_PARAMETERS, zend_property_reference *property_reference)
  1167. {
  1168. pval property, **handle;
  1169. pval *object = property_reference->object;
  1170. zend_overloaded_element *function_name = (zend_overloaded_element *) property_reference->elements_list->tail->data;
  1171. comval *obj;
  1172. int type;
  1173. if (zend_llist_count(property_reference->elements_list)==1
  1174. && !strcmp(Z_STRVAL(function_name->element), "com")) {
  1175. /* constructor */
  1176. pval *object_handle;
  1177. PHP_FN(com_load)(INTERNAL_FUNCTION_PARAM_PASSTHRU);
  1178. if (!zend_is_true(return_value)) {
  1179. ZVAL_FALSE(object);
  1180. return;
  1181. }
  1182. ALLOC_ZVAL(object_handle);
  1183. *object_handle = *return_value;
  1184. pval_copy_constructor(object_handle);
  1185. INIT_PZVAL(object_handle);
  1186. zend_hash_index_update(Z_OBJPROP_P(object), 0, &object_handle, sizeof(pval *), NULL);
  1187. pval_destructor(&function_name->element);
  1188. return;
  1189. }
  1190. property = php_COM_get_property_handler(property_reference);
  1191. if (Z_TYPE(property) == IS_NULL) {
  1192. if (property.refcount == 1) {
  1193. pval_destructor(&property);
  1194. }
  1195. pval_destructor(&function_name->element);
  1196. return;
  1197. }
  1198. zend_hash_index_find(Z_OBJPROP(property), 0, (void **) &handle);
  1199. obj = (comval *)zend_list_find(Z_LVAL_PP(handle), &type);
  1200. if (!obj || (type != IS_COM)) {
  1201. pval_destructor(&property);
  1202. pval_destructor(&function_name->element);
  1203. return;
  1204. }
  1205. if (zend_llist_count(property_reference->elements_list)==1
  1206. && !strcmp(Z_STRVAL_P(&function_name->element), "release")) {
  1207. RETVAL_LONG(php_COM_release(obj TSRMLS_CC));
  1208. } else if (zend_llist_count(property_reference->elements_list)==1
  1209. && !strcmp(Z_STRVAL_P(&function_name->element), "addref")) {
  1210. RETVAL_LONG(php_COM_addref(obj TSRMLS_CC));
  1211. } else {
  1212. pval **arguments;
  1213. VARIANT *var_result;
  1214. int arg_count = ZEND_NUM_ARGS();
  1215. ALLOC_VARIANT(var_result);
  1216. arguments = (pval **) emalloc(sizeof(pval *)*arg_count);
  1217. zend_get_parameters_array(ht, arg_count, arguments);
  1218. if (do_COM_invoke(obj , &function_name->element, var_result, arguments, arg_count TSRMLS_CC) == FAILURE) {
  1219. RETVAL_FALSE;
  1220. } else {
  1221. php_variant_to_pval(var_result, return_value, codepage TSRMLS_CC);
  1222. }
  1223. FREE_VARIANT(var_result);
  1224. efree(arguments);
  1225. }
  1226. if (property.refcount == 1) {
  1227. pval_destructor(&property);
  1228. }
  1229. pval_destructor(&function_name->element);
  1230. }
  1231. static ITypeLib *php_COM_find_typelib(char *search_string, int mode TSRMLS_DC)
  1232. {
  1233. ITypeLib *TypeLib = NULL;
  1234. char *strtok_buf, *major, *minor;
  1235. CLSID clsid;
  1236. OLECHAR *p;
  1237. /* Type Libraries:
  1238. * The string we have is either:
  1239. * a) a file name
  1240. * b) a CLSID, major, minor e.g. "{00000200-0000-0010-8000-00AA006D2EA4},2,0"
  1241. * c) a Type Library name e.g. "Microsoft OLE DB ActiveX Data Objects 1.0 Library"
  1242. * Searching for the name will be more expensive that the
  1243. * other two, so we will do that when both other attempts
  1244. * fail.
  1245. */
  1246. search_string = php_strtok_r(search_string, ",", &strtok_buf);
  1247. major = php_strtok_r(NULL, ",", &strtok_buf);
  1248. minor = php_strtok_r(NULL, ",", &strtok_buf);
  1249. p = php_char_to_OLECHAR(search_string, strlen(search_string), codepage TSRMLS_CC);
  1250. /* Is the string a GUID ? */
  1251. if (!FAILED(CLSIDFromString(p, &clsid))) {
  1252. HRESULT hr;
  1253. WORD major_i = 1;
  1254. WORD minor_i = 0;
  1255. /* We have a valid GUID, check to see if a major/minor */
  1256. /* version was specified otherwise assume 1,0 */
  1257. if ((major != NULL) && (minor != NULL)) {
  1258. major_i = (WORD) atoi(major);
  1259. minor_i = (WORD) atoi(minor);
  1260. }
  1261. /* The GUID will either be a typelibrary or a CLSID */
  1262. hr = LoadRegTypeLib((REFGUID) &clsid, major_i, minor_i, LANG_NEUTRAL, &TypeLib);
  1263. /* If the LoadRegTypeLib fails, let's try to instantiate */
  1264. /* the class itself and then QI for the TypeInfo and */
  1265. /* retrieve the type info from that interface */
  1266. if (FAILED(hr) && (!major || !minor)) {
  1267. IDispatch *Dispatch;
  1268. ITypeInfo *TypeInfo;
  1269. int idx;
  1270. if (FAILED(CoCreateInstance(&clsid, NULL, CLSCTX_SERVER, &IID_IDispatch, (LPVOID *) &Dispatch))) {
  1271. efree(p);
  1272. return NULL;
  1273. }
  1274. if (FAILED(Dispatch->lpVtbl->GetTypeInfo(Dispatch, 0, LANG_NEUTRAL, &TypeInfo))) {
  1275. Dispatch->lpVtbl->Release(Dispatch);
  1276. efree(p);
  1277. return NULL;
  1278. }
  1279. Dispatch->lpVtbl->Release(Dispatch);
  1280. if (FAILED(TypeInfo->lpVtbl->GetContainingTypeLib(TypeInfo, &TypeLib, &idx))) {
  1281. TypeInfo->lpVtbl->Release(TypeInfo);
  1282. efree(p);
  1283. return NULL;
  1284. }
  1285. TypeInfo->lpVtbl->Release(TypeInfo);
  1286. }
  1287. } else {
  1288. if (FAILED(LoadTypeLib(p, &TypeLib))) {
  1289. /* Walk HKCR/TypeLib looking for the string */
  1290. /* If that succeeds, call ourself recursively */
  1291. /* using the CLSID found, else give up and bail */
  1292. HKEY hkey, hsubkey;
  1293. DWORD SubKeys, MaxSubKeyLength;
  1294. char *keyname;
  1295. register unsigned int ii, jj;
  1296. DWORD VersionCount;
  1297. char version[20]; /* All the version keys are 1.0, 4.6, ... */
  1298. char *libname;
  1299. DWORD libnamelen;
  1300. /* No Need for Unicode version any more */
  1301. efree(p);
  1302. /* Starting at HKEY_CLASSES_ROOT/TypeLib */
  1303. /* Walk all subkeys (Typelib GUIDs) looking */
  1304. /* at each version for a string match to the */
  1305. /* supplied argument */
  1306. if (ERROR_SUCCESS != RegOpenKey(HKEY_CLASSES_ROOT, "TypeLib",&hkey)) {
  1307. /* This is pretty bad - better bail */
  1308. return NULL;
  1309. }
  1310. if (ERROR_SUCCESS != RegQueryInfoKey(hkey, NULL, NULL, NULL, &SubKeys, &MaxSubKeyLength, NULL, NULL, NULL, NULL, NULL, NULL)) {
  1311. RegCloseKey(hkey);
  1312. return NULL;
  1313. }
  1314. MaxSubKeyLength++; /* \0 is not counted */
  1315. keyname = emalloc(MaxSubKeyLength);
  1316. libname = emalloc(strlen(search_string)+1);
  1317. for (ii=0;ii<SubKeys;ii++) {
  1318. if (ERROR_SUCCESS != RegEnumKey(hkey, ii, keyname, MaxSubKeyLength)) {
  1319. /* Failed - who cares */
  1320. continue;
  1321. }
  1322. if (ERROR_SUCCESS != RegOpenKey(hkey, keyname, &hsubkey)) {
  1323. /* Failed - who cares */
  1324. continue;
  1325. }
  1326. if (ERROR_SUCCESS != RegQueryInfoKey(hsubkey, NULL, NULL, NULL, &VersionCount, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) {
  1327. /* Failed - who cares */
  1328. RegCloseKey(hsubkey);
  1329. continue;
  1330. }
  1331. for (jj=0;jj<VersionCount;jj++) {
  1332. if (ERROR_SUCCESS != RegEnumKey(hsubkey, jj, version, sizeof(version))) {
  1333. /* Failed - who cares */
  1334. continue;
  1335. }
  1336. /* OK we just need to retrieve the default */
  1337. /* value for this key and see if it matches */
  1338. libnamelen = strlen(search_string)+1;
  1339. if (ERROR_SUCCESS == RegQueryValue(hsubkey, version, libname, &libnamelen)) {
  1340. if ((mode & CONST_CS) ? (strcmp(libname, search_string) == 0) : (stricmp(libname, search_string) == 0)) {
  1341. char *str;
  1342. int major, minor;
  1343. /* Found it */
  1344. RegCloseKey(hkey);
  1345. RegCloseKey(hsubkey);
  1346. efree(libname);
  1347. /* We can either open up the "win32" key and find the DLL name */
  1348. /* Or just parse the version string and pass that in */
  1349. /* The version string seems like a more portable solution */
  1350. /* Given that there is a COM on Unix */
  1351. if (2 != sscanf(version, "%d.%d", &major, &minor)) {
  1352. major = 1;
  1353. minor = 0;
  1354. }
  1355. str = emalloc(strlen(keyname)+strlen(version)+20); /* 18 == safety, 2 == extra comma and \0 */
  1356. sprintf(str, "%s,%d,%d", keyname, major, minor);
  1357. efree(keyname);
  1358. TypeLib = php_COM_find_typelib(str, mode TSRMLS_CC);
  1359. efree(str);
  1360. /* This is probbaly much harder to read and follow */
  1361. /* But it is MUCH more effiecient than trying to */
  1362. /* test for errors and leave through a single "return" */
  1363. return TypeLib;
  1364. }
  1365. } else {
  1366. /* Failed - perhaps too small abuffer */
  1367. /* But if too small, then the name does not match */
  1368. }
  1369. }
  1370. RegCloseKey(hsubkey);
  1371. }
  1372. efree(keyname);
  1373. efree(libname);
  1374. return NULL;
  1375. }
  1376. }
  1377. efree(p);
  1378. return TypeLib;
  1379. }
  1380. static int php_COM_load_typelib(ITypeLib *TypeLib, int mode TSRMLS_DC)
  1381. {
  1382. ITypeComp *TypeComp;
  1383. int i;
  1384. int interfaces;
  1385. if (NULL == TypeLib) {
  1386. return FAILURE;
  1387. }
  1388. interfaces = TypeLib->lpVtbl->GetTypeInfoCount(TypeLib);
  1389. TypeLib->lpVtbl->GetTypeComp(TypeLib, &TypeComp);
  1390. for (i=0; i<interfaces; i++) {
  1391. TYPEKIND pTKind;
  1392. TypeLib->lpVtbl->GetTypeInfoType(TypeLib, i, &pTKind);
  1393. if (pTKind==TKIND_ENUM) {
  1394. ITypeInfo *TypeInfo;
  1395. VARDESC *pVarDesc;
  1396. UINT NameCount;
  1397. int j;
  1398. #if 0
  1399. BSTR bstr_EnumId;
  1400. char *EnumId;
  1401. TypeLib->lpVtbl->GetDocumentation(TypeLib, i, &bstr_EnumId, NULL, NULL, NULL);
  1402. EnumId = php_OLECHAR_to_char(bstr_EnumId, NULL, codepage);
  1403. printf("Enumeration %d - %s:\n", i, EnumId);
  1404. efree(EnumId);
  1405. #endif
  1406. TypeLib->lpVtbl->GetTypeInfo(TypeLib, i, &TypeInfo);
  1407. j=0;
  1408. while (SUCCEEDED(TypeInfo->lpVtbl->GetVarDesc(TypeInfo, j, &pVarDesc))) {
  1409. BSTR bstr_ids;
  1410. zend_constant c;
  1411. zval exists, results;
  1412. char *const_name;
  1413. TypeInfo->lpVtbl->GetNames(TypeInfo, pVarDesc->memid, &bstr_ids, 1, &NameCount);
  1414. if (NameCount!=1) {
  1415. j++;
  1416. continue;
  1417. }
  1418. const_name = php_OLECHAR_to_char(bstr_ids, &c.name_len, codepage TSRMLS_CC);
  1419. c.name = zend_strndup(const_name, c.name_len);
  1420. efree(const_name);
  1421. c.name_len++; /* length should include the NULL */
  1422. SysFreeString(bstr_ids);
  1423. /* Before registering the contsnt, let's see if we can find it */
  1424. if (zend_get_constant(c.name, c.name_len-1, &exists TSRMLS_CC)) {
  1425. /* Oops, it already exists. No problem if it is defined as the same value */
  1426. /* Check to see if they are the same */
  1427. if (!compare_function(&results, &c.value, &exists TSRMLS_CC) && INI_INT("com.autoregister_verbose")) {
  1428. php_error(E_WARNING,"Type library value %s is already defined and has a different value", c.name);
  1429. }
  1430. free(c.name);
  1431. j++;
  1432. continue;
  1433. }
  1434. php_variant_to_pval(pVarDesc->lpvarValue, &c.value, codepage TSRMLS_CC);
  1435. if (mode & CONST_PERSISTENT) {
  1436. zval_persist(&c.value TSRMLS_CC);
  1437. mode |= CONST_EFREE_PERSISTENT;
  1438. }
  1439. c.flags = mode;
  1440. zend_register_constant(&c TSRMLS_CC);
  1441. j++;
  1442. }
  1443. TypeInfo->lpVtbl->Release(TypeInfo);
  1444. }
  1445. }
  1446. return SUCCESS;
  1447. }
  1448. /* {{{ proto bool com_isenum(object com_module)
  1449. Grabs an IEnumVariant */
  1450. PHP_FUNCTION(com_isenum)
  1451. {
  1452. pval *object;
  1453. pval **comval_handle;
  1454. comval *obj;
  1455. int type;
  1456. if (ZEND_NUM_ARGS() != 1) {
  1457. ZEND_WRONG_PARAM_COUNT();
  1458. }
  1459. zend_get_parameters(ht, 1, &object);
  1460. /* obtain IDispatch interface */
  1461. zend_hash_index_find(Z_OBJPROP_P(object), 0, (void **) &comval_handle);
  1462. obj = (comval *) zend_list_find(Z_LVAL_PP(comval_handle), &type);
  1463. if (!obj || (type != IS_COM)) {
  1464. php_error(E_WARNING,"%s is not a COM object handler", "");
  1465. RETURN_FALSE;
  1466. }
  1467. RETURN_BOOL(C_HASENUM(obj));
  1468. }
  1469. /* }}} */
  1470. static void php_register_COM_class(TSRMLS_D)
  1471. {
  1472. INIT_OVERLOADED_CLASS_ENTRY(COM_class_entry, "COM", NULL,
  1473. php_COM_call_function_handler,
  1474. php_COM_get_property_handler,
  1475. php_COM_set_property_handler);
  1476. zend_register_internal_class(&COM_class_entry TSRMLS_CC);
  1477. }
  1478. static void php_COM_init(int module_number TSRMLS_DC)
  1479. {
  1480. le_comval = zend_register_list_destructors_ex(php_comval_destructor, NULL, "COM", module_number);
  1481. php_register_COM_class(TSRMLS_C);
  1482. }
  1483. PHP_MINIT_FUNCTION(COM)
  1484. {
  1485. php_COM_init(module_number TSRMLS_CC);
  1486. php_VARIANT_init(module_number TSRMLS_CC);
  1487. REGISTER_LONG_CONSTANT("CLSCTX_INPROC_SERVER", CLSCTX_INPROC_SERVER, CONST_CS | CONST_PERSISTENT);
  1488. REGISTER_LONG_CONSTANT("CLSCTX_INPROC_HANDLER", CLSCTX_INPROC_HANDLER, CONST_CS | CONST_PERSISTENT);
  1489. REGISTER_LONG_CONSTANT("CLSCTX_LOCAL_SERVER", CLSCTX_LOCAL_SERVER, CONST_CS | CONST_PERSISTENT);
  1490. REGISTER_LONG_CONSTANT("CLSCTX_REMOTE_SERVER", CLSCTX_REMOTE_SERVER, CONST_CS | CONST_PERSISTENT);
  1491. REGISTER_LONG_CONSTANT("CLSCTX_SERVER", CLSCTX_SERVER, CONST_CS | CONST_PERSISTENT);
  1492. REGISTER_LONG_CONSTANT("CLSCTX_ALL", CLSCTX_ALL, CONST_CS | CONST_PERSISTENT);
  1493. REGISTER_INI_ENTRIES();
  1494. return SUCCESS;
  1495. }
  1496. PHP_MSHUTDOWN_FUNCTION(COM)
  1497. {
  1498. UNREGISTER_INI_ENTRIES();
  1499. return SUCCESS;
  1500. }
  1501. /* exports for external object creation */
  1502. zend_module_entry COM_module_entry = {
  1503. STANDARD_MODULE_HEADER,
  1504. "com",
  1505. COM_functions,
  1506. PHP_MINIT(COM),
  1507. PHP_MSHUTDOWN(COM),
  1508. NULL,
  1509. NULL,
  1510. PHP_MINFO(COM),
  1511. NO_VERSION_YET,
  1512. STANDARD_MODULE_PROPERTIES
  1513. };
  1514. PHPAPI int php_COM_get_le_comval()
  1515. {
  1516. return le_comval;
  1517. }
  1518. #endif
  1519. /*
  1520. * Local variables:
  1521. * tab-width: 4
  1522. * c-basic-offset: 4
  1523. * indent-tabs-mode: t
  1524. * End:
  1525. */