/* +----------------------------------------------------------------------+ | PHP Version 4 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2003 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.0 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_0.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Wez Furlong | +----------------------------------------------------------------------+ */ /* $Id$ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" #include "php_com_dotnet.h" #include "php_com_dotnet_internal.h" #include "Zend/zend_default_classes.h" /* {{{ com_create_instance - ctor for COM class */ PHP_FUNCTION(com_create_instance) { zval *object = getThis(); zval *server_params = NULL; php_com_dotnet_object *obj; char *module_name, *typelib_name = NULL, *server_name = NULL; char *user_name = NULL, *domain_name = NULL, *password = NULL; long module_name_len, typelib_name_len, server_name_len, user_name_len, domain_name_len, password_len; OLECHAR *moniker; CLSID clsid; CLSCTX ctx = CLSCTX_SERVER; HRESULT res = E_FAIL; int mode = COMG(autoreg_case_sensitive) ? CONST_CS : 0; ITypeLib *TL = NULL; obj = CDNO_FETCH(object); if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "s|s!ls", &module_name, &module_name_len, &server_name, &server_name_len, &obj->code_page, &typelib_name, &typelib_name_len) && FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "sa|ls", &module_name, &module_name_len, &server_params, &obj->code_page, &typelib_name, &typelib_name_len)) { php_com_throw_exception("Could not create COM object - invalid arguments!" TSRMLS_CC); ZVAL_NULL(object); return; } if (server_name) { ctx = CLSCTX_REMOTE_SERVER; } else if (server_params) { zval **tmp; /* decode the data from the array */ if (SUCCESS == zend_hash_find(HASH_OF(server_params), "Server", sizeof("Server"), (void**)&tmp)) { convert_to_string_ex(tmp); server_name = Z_STRVAL_PP(tmp); server_name_len = Z_STRLEN_PP(tmp); ctx = CLSCTX_REMOTE_SERVER; } if (SUCCESS == zend_hash_find(HASH_OF(server_params), "Username", sizeof("Username"), (void**)&tmp)) { convert_to_string_ex(tmp); user_name = Z_STRVAL_PP(tmp); user_name_len = Z_STRLEN_PP(tmp); } if (SUCCESS == zend_hash_find(HASH_OF(server_params), "Password", sizeof("Password"), (void**)&tmp)) { convert_to_string_ex(tmp); password = Z_STRVAL_PP(tmp); password_len = Z_STRLEN_PP(tmp); } if (SUCCESS == zend_hash_find(HASH_OF(server_params), "Domain", sizeof("Domain"), (void**)&tmp)) { convert_to_string_ex(tmp); domain_name = Z_STRVAL_PP(tmp); domain_name_len = Z_STRLEN_PP(tmp); } if (SUCCESS == zend_hash_find(HASH_OF(server_params), "Flags", sizeof("Flags"), (void**)&tmp)) { convert_to_long_ex(tmp); ctx = (CLSCTX)Z_LVAL_PP(tmp); } } if (server_name && !COMG(allow_dcom)) { php_com_throw_exception("DCOM has been disabled by your administrator [com.allow_dcom=0]" TSRMLS_CC); return; } moniker = php_com_string_to_olestring(module_name, module_name_len, obj->code_page TSRMLS_CC); if (FAILED(CLSIDFromString(moniker, &clsid))) { /* try to use it as a moniker */ IBindCtx *pBindCtx = NULL; IMoniker *pMoniker = NULL; ULONG ulEaten; if (server_name == NULL) { res = MK_E_SYNTAX; } else if (SUCCEEDED(res = CreateBindCtx(0, &pBindCtx)) && SUCCEEDED(res = MkParseDisplayName(pBindCtx, moniker, &ulEaten, &pMoniker))) { res = IMoniker_BindToObject(pMoniker, pBindCtx, NULL, &IID_IDispatch, (LPVOID*)&V_DISPATCH(&obj->v)); if (SUCCEEDED(res)) { V_VT(&obj->v) = VT_DISPATCH; } IMoniker_Release(pMoniker); } if (pBindCtx) { IBindCtx_Release(pBindCtx); } } else if (server_name) { COSERVERINFO info; MULTI_QI qi; COAUTHIDENTITY authid; COAUTHINFO authinfo = { RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, &authid, EOAC_NONE }; info.dwReserved1 = 0; info.dwReserved2 = 0; info.pwszName = php_com_string_to_olestring(server_name, server_name_len, obj->code_page TSRMLS_CC); if (user_name) { authid.User = php_com_string_to_olestring(user_name, -1, obj->code_page TSRMLS_CC); authid.UserLength = user_name_len; if (password) { authid.Password = (OLECHAR*)password; authid.PasswordLength = password_len; } else { authid.Password = (OLECHAR*)""; authid.PasswordLength = 0; } if (domain_name) { authid.Domain = (OLECHAR*)domain_name; authid.DomainLength = domain_name_len; } else { authid.Domain = (OLECHAR*)""; authid.DomainLength = 0; } authid.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI; info.pAuthInfo = &authinfo; } else { info.pAuthInfo = NULL; } qi.pIID = &IID_IDispatch; qi.pItf = NULL; qi.hr = S_OK; res = CoCreateInstanceEx(&clsid, NULL, ctx, &info, 1, &qi); efree(info.pwszName); if (SUCCEEDED(res)) { res = qi.hr; V_DISPATCH(&obj->v) = (IDispatch*)qi.pItf; V_VT(&obj->v) = VT_DISPATCH; } } else { res = CoCreateInstance(&clsid, NULL, CLSCTX_SERVER, &IID_IDispatch, (LPVOID*)&V_DISPATCH(&obj->v)); if (SUCCEEDED(res)) { V_VT(&obj->v) = VT_DISPATCH; } } efree(moniker); if (FAILED(res)) { char *werr, *msg; werr = php_win_err(res); spprintf(&msg, 0, "Failed to create COM object `%s': %s", module_name, werr); LocalFree(werr); php_com_throw_exception(msg TSRMLS_CC); efree(msg); ZVAL_NULL(object); return; } /* we got the object and it lives ! */ /* see if it has TypeInfo available */ if (FAILED(IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &obj->typeinfo)) && typelib_name) { /* load up the library from the named file */ int cached; TL = php_com_load_typelib_via_cache(typelib_name, mode, obj->code_page, &cached TSRMLS_CC); if (TL) { if (COMG(autoreg_on) && !cached) { php_com_import_typelib(TL, mode, obj->code_page TSRMLS_CC); } /* cross your fingers... there is no guarantee that this ITypeInfo * instance has any relation to this IDispatch instance... */ ITypeLib_GetTypeInfo(TL, 0, &obj->typeinfo); ITypeLib_Release(TL); } } else if (obj->typeinfo && COMG(autoreg_on)) { int idx; if (SUCCEEDED(ITypeInfo_GetContainingTypeLib(obj->typeinfo, &TL, &idx))) { /* check if the library is already in the cache by getting its name */ BSTR name; if (SUCCEEDED(ITypeLib_GetDocumentation(TL, -1, &name, NULL, NULL, NULL))) { typelib_name = php_com_olestring_to_string(name, &typelib_name_len, obj->code_page TSRMLS_CC); if (SUCCESS == zend_ts_hash_add(&php_com_typelibraries, typelib_name, typelib_name_len+1, (void*)&TL, sizeof(ITypeLib*), NULL)) { php_com_import_typelib(TL, mode, obj->code_page TSRMLS_CC); /* add a reference for the hash */ ITypeLib_AddRef(TL); } } else { /* try it anyway */ php_com_import_typelib(TL, mode, obj->code_page TSRMLS_CC); } ITypeLib_Release(TL); } } } /* }}} */ /* Performs an Invoke on the given com object. * returns a failure code and creates an exception if there was an error */ HRESULT php_com_invoke_helper(php_com_dotnet_object *obj, DISPID id_member, WORD flags, DISPPARAMS *disp_params, VARIANT *v TSRMLS_DC) { HRESULT hr; unsigned int arg_err; EXCEPINFO e; if (obj->typeinfo) { hr = ITypeInfo_Invoke(obj->typeinfo, V_DISPATCH(&obj->v), id_member, flags, disp_params, v, &e, &arg_err); if (FAILED(hr) && (hr != DISP_E_EXCEPTION)) { hr = IDispatch_Invoke(V_DISPATCH(&obj->v), id_member, &IID_NULL, LOCALE_SYSTEM_DEFAULT, flags, disp_params, v, &e, &arg_err); if (SUCCEEDED(hr)) { /* fall back on using IDispatch directly */ ITypeInfo_Release(obj->typeinfo); obj->typeinfo = NULL; } } } else { hr = IDispatch_Invoke(V_DISPATCH(&obj->v), id_member, &IID_NULL, LOCALE_SYSTEM_DEFAULT, flags, disp_params, v, &e, &arg_err); } if (FAILED(hr)) { char *source = NULL, *desc = NULL, *msg = NULL; int source_len, desc_len; switch (hr) { case DISP_E_EXCEPTION: if (e.bstrSource) { source = php_com_olestring_to_string(e.bstrSource, &source_len, obj->code_page TSRMLS_CC); SysFreeString(e.bstrSource); } if (e.bstrDescription) { desc = php_com_olestring_to_string(e.bstrDescription, &desc_len, obj->code_page TSRMLS_CC); SysFreeString(e.bstrDescription); } if (PG(html_errors)) { spprintf(&msg, 0, "Source: %s
Description: %s", source ? source : "Unknown", desc ? desc : "Unknown"); } else { spprintf(&msg, 0, "Source: %s\nDescription: %s", source ? source : "Unknown", desc ? desc : "Unknown"); } if (desc) { efree(desc); } if (source) { efree(source); } if (e.bstrHelpFile) { SysFreeString(e.bstrHelpFile); } break; case DISP_E_PARAMNOTFOUND: case DISP_E_TYPEMISMATCH: desc = php_win_err(hr); spprintf(&msg, 0, "Parameter %d: %s", arg_err, desc); LocalFree(desc); break; default: desc = php_win_err(hr); spprintf(&msg, 0, "Error %s", desc); LocalFree(desc); break; } if (msg) { php_com_throw_exception(msg TSRMLS_CC); efree(msg); } } return hr; } /* map an ID to a name */ HRESULT php_com_get_id_of_name(php_com_dotnet_object *obj, char *name, int namelen, DISPID *dispid TSRMLS_DC) { OLECHAR *olename; HRESULT hr; olename = php_com_string_to_olestring(name, namelen, obj->code_page TSRMLS_CC); if (obj->typeinfo) { hr = ITypeInfo_GetIDsOfNames(obj->typeinfo, &olename, 1, dispid); if (FAILED(hr)) { hr = IDispatch_GetIDsOfNames(V_DISPATCH(&obj->v), &IID_NULL, &olename, 1, LOCALE_SYSTEM_DEFAULT, dispid); if (SUCCEEDED(hr)) { /* fall back on IDispatch direct */ ITypeInfo_Release(obj->typeinfo); obj->typeinfo = NULL; } } } else { hr = IDispatch_GetIDsOfNames(V_DISPATCH(&obj->v), &IID_NULL, &olename, 1, LOCALE_SYSTEM_DEFAULT, dispid); } efree(olename); return hr; } /* the core of COM */ int php_com_do_invoke_by_id(php_com_dotnet_object *obj, DISPID dispid, WORD flags, VARIANT *v, int nargs, zval **args TSRMLS_DC) { DISPID altdispid; DISPPARAMS disp_params; HRESULT hr; VARIANT *vargs = NULL; int i; if (nargs) { vargs = (VARIANT*)emalloc(sizeof(VARIANT) * nargs); } /* Invoke'd args are in reverse order */ for (i = 0; i < nargs; i++) { php_com_variant_from_zval(&vargs[i], args[nargs - i - 1], obj->code_page TSRMLS_CC); } disp_params.cArgs = nargs; disp_params.cNamedArgs = 0; disp_params.rgvarg = vargs; disp_params.rgdispidNamedArgs = NULL; if (flags & DISPATCH_PROPERTYPUT) { altdispid = DISPID_PROPERTYPUT; disp_params.rgdispidNamedArgs = &altdispid; disp_params.cNamedArgs = 1; } /* this will create an exception if needed */ hr = php_com_invoke_helper(obj, dispid, flags, &disp_params, v TSRMLS_CC); /* release variants */ if (vargs) { for (i = 0; i < nargs; i++) { VariantClear(&vargs[i]); } efree(vargs); } return SUCCEEDED(hr) ? SUCCESS : FAILURE; } int php_com_do_invoke(php_com_dotnet_object *obj, char *name, int namelen, WORD flags, VARIANT *v, int nargs, zval **args TSRMLS_DC) { DISPID dispid; HRESULT hr; char *winerr = NULL; char *msg = NULL; hr = php_com_get_id_of_name(obj, name, namelen, &dispid TSRMLS_CC); if (FAILED(hr)) { winerr = php_win_err(hr); spprintf(&msg, 0, "Unable to lookup `%s': %s", name, winerr); LocalFree(winerr); php_com_throw_exception(msg TSRMLS_CC); efree(msg); return FAILURE; } return php_com_do_invoke_by_id(obj, dispid, flags, v, nargs, args TSRMLS_CC); }