+#define _WIN32_DCOM
+
+#include "../handler.h"
+#include "../php_rpc.h"
+
#include "com.h"
+#include "com_wrapper.h"
+#include "conversion.h"
+
+#include <oleauto.h>
+
/* protos */
-static int com_hash(rpc_string, rpc_string *, int, char *, int);
-static int com_name(rpc_string, rpc_string *, int);
+static int com_hash(rpc_string, rpc_string *, void *, int, char *, int);
+static int com_name(rpc_string, rpc_string *, void *, int);
static int com_ctor(rpc_string, void **, int , zval ***);
static int com_dtor(void **);
static int com_call(rpc_string, void **, zval **, int, zval ***);
static int com_unset_property(rpc_string, void **);
static int com_get_properties(HashTable **, void **);
+static PHP_INI_MH(OnTypelibFileChange);
+
+
+/* globals */
+static IBindCtx *pBindCtx;
+
/* register rpc callback function */
RPC_REGISTER_HANDLERS_START(com)
PHP_INI_ENTRY_EX("com.autoregister_typelib", "0", PHP_INI_SYSTEM, NULL, php_ini_boolean_displayer_cb)
PHP_INI_ENTRY_EX("com.autoregister_verbose", "0", PHP_INI_SYSTEM, NULL, php_ini_boolean_displayer_cb)
PHP_INI_ENTRY_EX("com.autoregister_casesensitive", "1", PHP_INI_SYSTEM, NULL, php_ini_boolean_displayer_cb)
+PHP_INI_ENTRY("com.typelib_file", "", PHP_INI_SYSTEM, OnTypelibFileChange)
RPC_INI_END()
/* register userspace functions */
ZEND_FALIAS(release, com_release, NULL)
RPC_METHOD_ENTRY_END()
+
/* init function that is called before the class is registered
* so you can do any tricky stuff in here
*/
RPC_INIT_FUNCTION(com)
{
+ CreateBindCtx(0, &pBindCtx);
+}
+
+RPC_SHUTDOWN_FUNCTION(com)
+{
+ pBindCtx->lpVtbl->Release(pBindCtx);
}
/* rpc handler functions */
-static int com_hash(rpc_string name, rpc_string *hash, int num_args, char *arg_types, int type)
+static int com_hash(rpc_string name, rpc_string *hash, void *data, int num_args, char *arg_types, int type)
{
- hash->str = strdup(name.str);
- hash->len = name.len;
+ OLECHAR *olestr = php_char_to_OLECHAR(name.str, name.len, CP_ACP, FALSE);
- return SUCCESS;
+ switch (type) {
+ case CLASS:
+ {
+ CLSID *clsid = malloc(sizeof(CLSID));
+
+ if (FAILED(CLSIDFromString(olestr, clsid))) {
+ /* Perhaps this is a Moniker? */
+ free(clsid);
+
+ hash->str = strdup(name.str);
+ hash->len = name.len;
+ } else {
+ hash->str = (char *) clsid;
+ /* str is actually not a string but a CLSID struct, thus set len to 0.
+ * nevertheless clsid is freed by the rpc_string_dtor
+ */
+ hash->len = 0;
+ }
+
+ efree(olestr);
+
+ return SUCCESS;
+ }
+
+ case METHOD:
+ case PROPERTY:
+ {
+ DISPID *dispid = malloc(sizeof(DISPID));
+
+ if(SUCCEEDED(php_COM_get_ids_of_names((comval *) data, olestr, dispid))) {
+ hash->str = (char *) dispid;
+ /* str is actually not a string but a DISPID struct, thus set len to 0.
+ * nevertheless dispid is freed by the rpc_string_dtor
+ */
+ hash->len = 0;
+
+ efree(olestr);
+
+ return SUCCESS;
+ } else {
+ free(dispid);
+ efree(olestr);
+
+ return FAILURE;
+ }
+ }
+ }
+
+ efree(olestr);
+
+ return FAILURE;
}
-static int com_name(rpc_string hash, rpc_string *name, int type)
+static int com_name(rpc_string hash, rpc_string *name, void *data, int type)
{
- name->str = strdup(hash.str);
- name->len = hash.len;
+ if (hash.len != 0) {
+ /* not a GUID, perhaps a Moniker */
+ name->str = strdup(hash.str);
+ name->len = hash.len;
- return SUCCESS;
+ return SUCCESS;
+ } else {
+ switch (type) {
+ case CLASS:
+ {
+ OLECHAR *olestr;
+
+ StringFromCLSID((CLSID *) hash.str, &olestr);
+ name->str = php_OLECHAR_to_char(olestr, &(name->len), CP_ACP, TRUE);
+ CoTaskMemFree(olestr);
+
+ return SUCCESS;
+ }
+
+ case METHOD:
+ case PROPERTY:
+ /* not used yet */
+ break;
+ }
+ }
+
+ return FAILURE;
}
static int com_ctor(rpc_string class_name, void **data, int num_args, zval **args[])
{
+ zval **server_name = NULL;
+ zval **code_page = NULL;
+ zval **typelib = NULL;
+ zval **user_name=NULL;
+ zval **password=NULL;
+ zval **domain=NULL;
+ int mode = 0;
+ comval *obj;
+ HRESULT hr;
+ CLSCTX flags = CLSCTX_SERVER;
+
+ switch (num_args) {
+ case 3:
+ typelib = args[2];
+ convert_to_string_ex(typelib);
+ /* break missing intentionally */
+ case 2:
+ code_page = args[1];
+ convert_to_long_ex(code_page);
+ /* break missing intentionally */
+ case 1:
+ server_name = args[0];
+ /* break missing intentionally */
+ break;
+
+ case 0:
+ /* nothing to do */
+ break;
+
+ default:
+ /* exception */
+ return FAILURE;
+ }
+
+ if (server_name != NULL) {
+ /* What is server name? A String or an array? */
+ if (Z_TYPE_PP(server_name) == IS_NULL) {
+ server_name = NULL;
+ } else if (Z_TYPE_PP(server_name) == IS_ARRAY) {
+ zval **tmp;
+ /* Aha - we have a number of possible arguments.
+ * They are in the hash By name: Server, Domain, Username, Password
+ * Flags.
+ * This has been crafted to maintian maximum backward compatability.
+ * If the server name is specified as a string, then the function
+ * should behave as before by defaulting username and password and
+ * using the (I believe) incorrect CLSCTX_SERVER instantiation
+ * paramter. However if server is specified in this array then we
+ * use either CLSCTX_REMOTE_SERVER or whatever flags are specified
+ * in the array
+ */
+ HashTable *ht = Z_ARRVAL_PP(server_name);
+ if (FAILURE == zend_hash_find(ht, "Server", 7, (void **) &tmp)) {
+ server_name = NULL;
+ } else {
+ server_name = tmp;
+ convert_to_string_ex(server_name);
+ /* CLSCTX_SERVER includes INPROC and LOCAL SERVER. This means
+ * that any local server will be instantiated BEFORE even
+ * looking on a remote server. Thus if we have a server name,
+ * probably we want to access a remote machine or we would not
+ * have bothered specifying it. So it would be wrong to to
+ * connect locally. Futher, unless the name passed is a GUID,
+ * there has to be something to map the Prog.Id to GUID and
+ * unless that has been modified to remove the information
+ * about local instantiation CLSCTX_SERVER would force a local
+ * instantiation This setting can be overridden below if the
+ * user specifies a flags element */
+ flags = CLSCTX_REMOTE_SERVER;
+ }
+ if (FAILURE == zend_hash_find(ht, "username", 9, (void **) &tmp)) {
+ user_name = NULL;
+ } else {
+ user_name = tmp;
+ convert_to_string_ex(user_name);
+ }
+ if (FAILURE == zend_hash_find(ht, "domain", 7, (void **) &tmp)) {
+ domain = NULL;
+ } else {
+ domain = tmp;
+ convert_to_string_ex(domain);
+ }
+ if (FAILURE == zend_hash_find(ht, "password", 9, (void **) &tmp)) {
+ password=NULL;
+ } else {
+ password = tmp;
+ convert_to_string_ex(password);
+ }
+ if (SUCCESS == zend_hash_find(ht, "flags", 6, (void **) &tmp)) {
+ convert_to_long_ex(tmp);
+ flags = (CLSCTX) Z_LVAL_PP(tmp);
+ }
+ }
+
+ if (server_name != NULL) {
+ if (!INI_INT("com.allow_dcom")) {
+ rpc_error(E_WARNING, "DCOM is disabled");
+ return FAILURE;
+ } else {
+ flags = CLSCTX_REMOTE_SERVER;
+ convert_to_string_ex(server_name);
+ }
+ }
+ }
+
+ ALLOC_COM(obj);
+ *data = obj;
+
+ if (code_page != NULL) {
+ C_CODEPAGE(obj) = Z_LVAL_PP(code_page);
+ }
+
+ if (class_name.len) {
+ /* Perhaps this is a Moniker? */
+ IMoniker *pMoniker;
+ ULONG ulEaten;
+
+ if (server_name) {
+ hr = MK_E_SYNTAX;
+ } else {
+ OLECHAR *olestr = php_char_to_OLECHAR(class_name.str, class_name.len, obj->codepage, FALSE);
+
+ if (SUCCEEDED(hr = MkParseDisplayNameEx(pBindCtx, olestr, &ulEaten, &pMoniker))) {
+ hr = pMoniker->lpVtbl->BindToObject(pMoniker, pBindCtx, NULL, &IID_IDispatch, (LPVOID *) &C_DISPATCH(obj));
+ pMoniker->lpVtbl->Release(pMoniker);
+ }
+
+ efree(olestr);
+ }
+
+ if (FAILED(hr)) {
+ char *error_message;
+
+ php_COM_destruct(obj);
+ error_message = php_COM_error_message(hr);
+ rpc_error(E_WARNING,"Invalid ProgID, GUID string, or Moniker: %s", error_message);
+ LocalFree(error_message);
+
+ return FAILURE;
+ }
+ } else {
+ /* obtain IDispatch */
+ if (!server_name) {
+ hr = CoCreateInstance((CLSID *) class_name.str, NULL, flags, &IID_IDispatch, (LPVOID *) &C_DISPATCH(obj));
+ } else {
+ COSERVERINFO server_info;
+ MULTI_QI pResults;
+ 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};
+
+ server_info.dwReserved1=0;
+ server_info.dwReserved2=0;
+ server_info.pwszName = php_char_to_OLECHAR(Z_STRVAL_PP(server_name), Z_STRLEN_PP(server_name), obj->codepage, FALSE);
+ if (user_name) {
+ /* Parse Username into domain\username */
+ authid.User = (WCHAR *) Z_STRVAL_PP(user_name);
+ authid.UserLength = Z_STRLEN_PP(user_name);
+ if (password) {
+ authid.Password = (USHORT *) Z_STRVAL_PP(password);
+ authid.PasswordLength = Z_STRLEN_PP(password);
+ } else {
+ authid.Password = (USHORT *) "";
+ authid.PasswordLength = 0;
+ }
+ if (domain) {
+ authid.Domain = (USHORT *) Z_STRVAL_PP(domain);
+ authid.DomainLength = Z_STRLEN_PP(domain);
+ } else {
+ authid.Domain = (USHORT *) "";
+ authid.DomainLength = 0;
+ }
+ authid.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
+
+ server_info.pAuthInfo=&authinfo;
+ } else {
+ server_info.pAuthInfo=NULL;
+ }
+
+ pResults.pIID = &IID_IDispatch;
+ pResults.pItf = NULL;
+ pResults.hr = S_OK;
+ hr=CoCreateInstanceEx((CLSID *) class_name.str, NULL, flags, &server_info, 1, &pResults);
+ if (SUCCEEDED(hr)) {
+ hr = pResults.hr;
+ C_DISPATCH(obj) = (IDispatch *) pResults.pItf;
+ }
+ efree(server_info.pwszName);
+ }
+
+ if (FAILED(hr)) {
+ char *error_message, *clsid;
+
+ php_COM_destruct(obj);
+ error_message = php_COM_error_message(hr);
+ clsid = php_COM_string_from_CLSID((CLSID *)class_name.str);
+ rpc_error(E_WARNING,"Unable to obtain IDispatch interface for CLSID %s: %s", clsid, error_message);
+ LocalFree(error_message);
+ efree(clsid);
+
+ return FAILURE;
+ }
+ }
+
+ php_COM_set(obj, &C_DISPATCH(obj), TRUE);
+
+ if (INI_INT("com.autoregister_casesensitive")) {
+ mode |= CONST_CS;
+ }
+
+ if (C_HASTLIB(obj)) {
+ if (INI_INT("com.autoregister_typelib")) {
+ ITypeLib *pTL;
+ unsigned int idx;
+
+ /* @todo check if typlib isn't already loaded */
+ if (C_TYPEINFO_VT(obj)->GetContainingTypeLib(C_TYPEINFO(obj), &pTL, &idx) == S_OK) {
+ php_COM_load_typelib(pTL, mode);
+ pTL->lpVtbl->Release(pTL);
+ }
+ }
+ } else {
+ if (typelib != NULL) {
+ ITypeLib *pTL;
+
+ if ((pTL = php_COM_find_typelib(Z_STRVAL_PP(typelib), mode)) != NULL) {
+ C_HASTLIB(obj) = SUCCEEDED(pTL->lpVtbl->GetTypeInfo(pTL, 0, &C_TYPEINFO(obj)));
+ /* idx 0 should deliver the ITypeInfo for the IDispatch Interface */
+ if (INI_INT("com.autoregister_typelib")) {
+ php_COM_load_typelib(pTL, mode);
+ }
+ pTL->lpVtbl->Release(pTL);
+ }
+ }
+ }
+
+#if 0
+ RETURN_RESOURCE(zend_list_insert(obj, IS_COM));
+#endif
+
return SUCCESS;
}
ZEND_FUNCTION(com_release)
{
-}
\ No newline at end of file
+}
+
+/* ini callbacks */
+static PHP_INI_MH(OnTypelibFileChange)
+{
+ FILE *typelib_file;
+ char *typelib_name_buffer;
+ char *strtok_buf = NULL;
+ int interactive;
+ interactive = CG(interactive);
+
+ if (!new_value || (typelib_file = VCWD_FOPEN(new_value, "r"))==NULL) {
+ return FAILURE;
+ }
+
+ if (interactive) {
+ printf("Loading type libraries...");
+ fflush(stdout);
+ }
+
+ typelib_name_buffer = (char *) emalloc(sizeof(char)*1024);
+
+ while (fgets(typelib_name_buffer, 1024, typelib_file)) {
+ ITypeLib *pTL;
+ char *typelib_name;
+ char *modifier, *ptr;
+ int mode = CONST_CS | CONST_PERSISTENT; /* CONST_PERSISTENT is ok here */
+
+ if (typelib_name_buffer[0]==';') {
+ continue;
+ }
+ typelib_name = php_strtok_r(typelib_name_buffer, "\r\n", &strtok_buf); /* get rid of newlines */
+ if (typelib_name == NULL) {
+ continue;
+ }
+ typelib_name = php_strtok_r(typelib_name, "#", &strtok_buf);
+ modifier = php_strtok_r(NULL, "#", &strtok_buf);
+ if (modifier != NULL) {
+ if (!strcmp(modifier, "cis") || !strcmp(modifier, "case_insensitive")) {
+ mode &= ~CONST_CS;
+ }
+ }
+
+ /* Remove leading/training white spaces on search_string */
+ while (isspace(*typelib_name)) {/* Ends on '\0' in worst case */
+ typelib_name ++;
+ }
+ ptr = typelib_name + strlen(typelib_name) - 1;
+ while ((ptr != typelib_name) && isspace(*ptr)) {
+ *ptr = '\0';
+ ptr--;
+ }
+
+ if (interactive) {
+ printf("\rLoading %-60s\r", typelib_name);
+ }
+
+ if ((pTL = php_COM_find_typelib(typelib_name, mode)) != NULL) {
+ php_COM_load_typelib(pTL, mode);
+ pTL->lpVtbl->Release(pTL);
+ }
+ }
+
+ efree(typelib_name_buffer);
+ fclose(typelib_file);
+
+ if (interactive) {
+ printf("\r%70s\r", "");
+ }
+
+ return SUCCESS;
+}