php-src/ext/com_dotnet/com_com.c
Wez Furlong 6df5d5ba20 Add new COM (and .Net) extension for php5.
Not yet complete, but should work for most people.
2003-08-14 16:49:56 +00:00

430 lines
12 KiB
C

/*
+----------------------------------------------------------------------+
| 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 <wez@thebrainroom.com> |
+----------------------------------------------------------------------+
*/
/* $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, "<b>Source:</b> %s<br/><b>Description:</b> %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);
}