1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2014 The PHP Group                                |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Author: Wez Furlong  <wez@thebrainroom.com>                          |
16   +----------------------------------------------------------------------+
17 */
18
19/* $Id$ */
20
21#ifdef HAVE_CONFIG_H
22#include "config.h"
23#endif
24
25#include "php.h"
26#include "php_ini.h"
27#include "ext/standard/info.h"
28#include "php_com_dotnet.h"
29#include "php_com_dotnet_internal.h"
30#include "Zend/zend_exceptions.h"
31
32/* {{{ com_create_instance - ctor for COM class */
33PHP_FUNCTION(com_create_instance)
34{
35    zval *object = getThis();
36    zval *server_params = NULL;
37    php_com_dotnet_object *obj;
38    char *module_name, *typelib_name = NULL, *server_name = NULL;
39    char *user_name = NULL, *domain_name = NULL, *password = NULL;
40    int module_name_len, typelib_name_len, server_name_len,
41        user_name_len, domain_name_len, password_len;
42    OLECHAR *moniker;
43    CLSID clsid;
44    CLSCTX ctx = CLSCTX_SERVER;
45    HRESULT res = E_FAIL;
46    int mode = COMG(autoreg_case_sensitive) ? CONST_CS : 0;
47    ITypeLib *TL = NULL;
48    COSERVERINFO    info;
49    COAUTHIDENTITY  authid = {0};
50    COAUTHINFO      authinfo = {
51        RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL,
52        RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE,
53        &authid, EOAC_NONE
54    };
55
56    php_com_initialize(TSRMLS_C);
57    obj = CDNO_FETCH(object);
58
59    if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
60            ZEND_NUM_ARGS() TSRMLS_CC, "s|s!ls",
61            &module_name, &module_name_len, &server_name, &server_name_len,
62            &obj->code_page, &typelib_name, &typelib_name_len) &&
63        FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
64            ZEND_NUM_ARGS() TSRMLS_CC, "sa|ls",
65            &module_name, &module_name_len, &server_params, &obj->code_page,
66            &typelib_name, &typelib_name_len)) {
67
68        php_com_throw_exception(E_INVALIDARG, "Could not create COM object - invalid arguments!" TSRMLS_CC);
69        ZVAL_NULL(object);
70        return;
71    }
72
73    if (server_name) {
74        ctx = CLSCTX_REMOTE_SERVER;
75    } else if (server_params) {
76        zval **tmp;
77
78        /* decode the data from the array */
79
80        if (SUCCESS == zend_hash_find(HASH_OF(server_params),
81                "Server", sizeof("Server"), (void**)&tmp)) {
82            convert_to_string_ex(tmp);
83            server_name = Z_STRVAL_PP(tmp);
84            server_name_len = Z_STRLEN_PP(tmp);
85            ctx = CLSCTX_REMOTE_SERVER;
86        }
87
88        if (SUCCESS == zend_hash_find(HASH_OF(server_params),
89                "Username", sizeof("Username"), (void**)&tmp)) {
90            convert_to_string_ex(tmp);
91            user_name = Z_STRVAL_PP(tmp);
92            user_name_len = Z_STRLEN_PP(tmp);
93        }
94
95        if (SUCCESS == zend_hash_find(HASH_OF(server_params),
96                "Password", sizeof("Password"), (void**)&tmp)) {
97            convert_to_string_ex(tmp);
98            password = Z_STRVAL_PP(tmp);
99            password_len = Z_STRLEN_PP(tmp);
100        }
101
102        if (SUCCESS == zend_hash_find(HASH_OF(server_params),
103                "Domain", sizeof("Domain"), (void**)&tmp)) {
104            convert_to_string_ex(tmp);
105            domain_name = Z_STRVAL_PP(tmp);
106            domain_name_len = Z_STRLEN_PP(tmp);
107        }
108
109        if (SUCCESS == zend_hash_find(HASH_OF(server_params),
110                "Flags", sizeof("Flags"), (void**)&tmp)) {
111            convert_to_long_ex(tmp);
112            ctx = (CLSCTX)Z_LVAL_PP(tmp);
113        }
114    }
115
116    if (server_name && !COMG(allow_dcom)) {
117        php_com_throw_exception(E_ERROR, "DCOM has been disabled by your administrator [com.allow_dcom=0]" TSRMLS_CC);
118        return;
119    }
120
121    moniker = php_com_string_to_olestring(module_name, module_name_len, obj->code_page TSRMLS_CC);
122
123    /* if instantiating a remote object, either directly, or via
124     * a moniker, fill in the relevant info */
125    if (server_name) {
126        info.dwReserved1 = 0;
127        info.dwReserved2 = 0;
128        info.pwszName = php_com_string_to_olestring(server_name, server_name_len, obj->code_page TSRMLS_CC);
129
130        if (user_name) {
131            authid.User = php_com_string_to_olestring(user_name, -1, obj->code_page TSRMLS_CC);
132            authid.UserLength = user_name_len;
133
134            if (password) {
135                authid.Password = (OLECHAR*)password;
136                authid.PasswordLength = password_len;
137            } else {
138                authid.Password = (OLECHAR*)"";
139                authid.PasswordLength = 0;
140            }
141
142            if (domain_name) {
143                authid.Domain = (OLECHAR*)domain_name;
144                authid.DomainLength = domain_name_len;
145            } else {
146                authid.Domain = (OLECHAR*)"";
147                authid.DomainLength = 0;
148            }
149            authid.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
150            info.pAuthInfo = &authinfo;
151        } else {
152            info.pAuthInfo = NULL;
153        }
154    }
155
156    if (FAILED(CLSIDFromString(moniker, &clsid))) {
157        /* try to use it as a moniker */
158        IBindCtx *pBindCtx = NULL;
159        IMoniker *pMoniker = NULL;
160        ULONG ulEaten;
161        BIND_OPTS2 bopt = {0};
162
163        if (SUCCEEDED(res = CreateBindCtx(0, &pBindCtx))) {
164            if (server_name) {
165                /* fill in the remote server info.
166                 * MSDN docs indicate that this might be ignored in
167                 * current win32 implementations, but at least we are
168                 * doing the right thing in readiness for the day that
169                 * it does work */
170                bopt.cbStruct = sizeof(bopt);
171                IBindCtx_GetBindOptions(pBindCtx, (BIND_OPTS*)&bopt);
172                bopt.pServerInfo = &info;
173                /* apparently, GetBindOptions will only ever return
174                 * a regular BIND_OPTS structure.  My gut feeling is
175                 * that it will modify the size field to reflect that
176                 * so lets be safe and set it to the BIND_OPTS2 size
177                 * again */
178                bopt.cbStruct = sizeof(bopt);
179                IBindCtx_SetBindOptions(pBindCtx, (BIND_OPTS*)&bopt);
180            }
181
182            if (SUCCEEDED(res = MkParseDisplayName(pBindCtx, moniker, &ulEaten, &pMoniker))) {
183                res = IMoniker_BindToObject(pMoniker, pBindCtx,
184                    NULL, &IID_IDispatch, (LPVOID*)&V_DISPATCH(&obj->v));
185
186                if (SUCCEEDED(res)) {
187                    V_VT(&obj->v) = VT_DISPATCH;
188                }
189
190                IMoniker_Release(pMoniker);
191            }
192        }
193        if (pBindCtx) {
194            IBindCtx_Release(pBindCtx);
195        }
196    } else if (server_name) {
197        MULTI_QI        qi;
198
199        qi.pIID = &IID_IDispatch;
200        qi.pItf = NULL;
201        qi.hr = S_OK;
202
203        res = CoCreateInstanceEx(&clsid, NULL, ctx, &info, 1, &qi);
204
205        if (SUCCEEDED(res)) {
206            res = qi.hr;
207            V_DISPATCH(&obj->v) = (IDispatch*)qi.pItf;
208            V_VT(&obj->v) = VT_DISPATCH;
209        }
210    } else {
211        res = CoCreateInstance(&clsid, NULL, CLSCTX_SERVER, &IID_IDispatch, (LPVOID*)&V_DISPATCH(&obj->v));
212        if (SUCCEEDED(res)) {
213            V_VT(&obj->v) = VT_DISPATCH;
214        }
215    }
216
217    if (server_name) {
218        STR_FREE((char*)info.pwszName);
219        STR_FREE((char*)authid.User);
220    }
221
222    efree(moniker);
223
224    if (FAILED(res)) {
225        char *werr, *msg;
226
227        werr = php_win32_error_to_msg(res);
228        spprintf(&msg, 0, "Failed to create COM object `%s': %s", module_name, werr);
229        LocalFree(werr);
230
231        php_com_throw_exception(res, msg TSRMLS_CC);
232        efree(msg);
233        ZVAL_NULL(object);
234        return;
235    }
236
237    /* we got the object and it lives ! */
238
239    /* see if it has TypeInfo available */
240    if (FAILED(IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &obj->typeinfo)) && typelib_name) {
241        /* load up the library from the named file */
242        int cached;
243
244        TL = php_com_load_typelib_via_cache(typelib_name, obj->code_page, &cached TSRMLS_CC);
245
246        if (TL) {
247            if (COMG(autoreg_on) && !cached) {
248                php_com_import_typelib(TL, mode, obj->code_page TSRMLS_CC);
249            }
250
251            /* cross your fingers... there is no guarantee that this ITypeInfo
252             * instance has any relation to this IDispatch instance... */
253            ITypeLib_GetTypeInfo(TL, 0, &obj->typeinfo);
254            ITypeLib_Release(TL);
255        }
256    } else if (obj->typeinfo && COMG(autoreg_on)) {
257        int idx;
258
259        if (SUCCEEDED(ITypeInfo_GetContainingTypeLib(obj->typeinfo, &TL, &idx))) {
260            /* check if the library is already in the cache by getting its name */
261            BSTR name;
262
263            if (SUCCEEDED(ITypeLib_GetDocumentation(TL, -1, &name, NULL, NULL, NULL))) {
264                typelib_name = php_com_olestring_to_string(name, &typelib_name_len, obj->code_page TSRMLS_CC);
265
266                if (SUCCESS == zend_ts_hash_add(&php_com_typelibraries, typelib_name, typelib_name_len+1, (void*)&TL, sizeof(ITypeLib*), NULL)) {
267                    php_com_import_typelib(TL, mode, obj->code_page TSRMLS_CC);
268
269                    /* add a reference for the hash */
270                    ITypeLib_AddRef(TL);
271                }
272
273            } else {
274                /* try it anyway */
275                php_com_import_typelib(TL, mode, obj->code_page TSRMLS_CC);
276            }
277
278            ITypeLib_Release(TL);
279        }
280    }
281
282}
283/* }}} */
284
285/* {{{ proto object com_get_active_object(string progid [, int code_page ])
286   Returns a handle to an already running instance of a COM object */
287PHP_FUNCTION(com_get_active_object)
288{
289    CLSID clsid;
290    char *module_name;
291    int module_name_len;
292    long code_page = COMG(code_page);
293    IUnknown *unk = NULL;
294    IDispatch *obj = NULL;
295    HRESULT res;
296    OLECHAR *module = NULL;
297
298    php_com_initialize(TSRMLS_C);
299    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l",
300                &module_name, &module_name_len, &code_page)) {
301        php_com_throw_exception(E_INVALIDARG, "Invalid arguments!" TSRMLS_CC);
302        return;
303    }
304
305    module = php_com_string_to_olestring(module_name, module_name_len, code_page TSRMLS_CC);
306
307    res = CLSIDFromString(module, &clsid);
308
309    if (FAILED(res)) {
310        php_com_throw_exception(res, NULL TSRMLS_CC);
311    } else {
312        res = GetActiveObject(&clsid, NULL, &unk);
313
314        if (FAILED(res)) {
315            php_com_throw_exception(res, NULL TSRMLS_CC);
316        } else {
317            res = IUnknown_QueryInterface(unk, &IID_IDispatch, &obj);
318
319            if (FAILED(res)) {
320                php_com_throw_exception(res, NULL TSRMLS_CC);
321            } else if (obj) {
322                /* we got our dispatchable object */
323                php_com_wrap_dispatch(return_value, obj, code_page TSRMLS_CC);
324            }
325        }
326    }
327
328    if (obj) {
329        IDispatch_Release(obj);
330    }
331    if (unk) {
332        IUnknown_Release(obj);
333    }
334    efree(module);
335}
336/* }}} */
337
338/* Performs an Invoke on the given com object.
339 * returns a failure code and creates an exception if there was an error */
340HRESULT php_com_invoke_helper(php_com_dotnet_object *obj, DISPID id_member,
341        WORD flags, DISPPARAMS *disp_params, VARIANT *v, int silent, int allow_noarg TSRMLS_DC)
342{
343    HRESULT hr;
344    unsigned int arg_err;
345    EXCEPINFO e = {0};
346
347    hr = IDispatch_Invoke(V_DISPATCH(&obj->v), id_member,
348        &IID_NULL, LOCALE_SYSTEM_DEFAULT, flags, disp_params, v, &e, &arg_err);
349
350    if (silent == 0 && FAILED(hr)) {
351        char *source = NULL, *desc = NULL, *msg = NULL;
352        int source_len, desc_len;
353
354        switch (hr) {
355            case DISP_E_EXCEPTION:
356                if (e.bstrSource) {
357                    source = php_com_olestring_to_string(e.bstrSource, &source_len, obj->code_page TSRMLS_CC);
358                    SysFreeString(e.bstrSource);
359                }
360                if (e.bstrDescription) {
361                    desc = php_com_olestring_to_string(e.bstrDescription, &desc_len, obj->code_page TSRMLS_CC);
362                    SysFreeString(e.bstrDescription);
363                }
364                if (PG(html_errors)) {
365                    spprintf(&msg, 0, "<b>Source:</b> %s<br/><b>Description:</b> %s",
366                        source ? source : "Unknown",
367                        desc ? desc : "Unknown");
368                } else {
369                    spprintf(&msg, 0, "Source: %s\nDescription: %s",
370                        source ? source : "Unknown",
371                        desc ? desc : "Unknown");
372                }
373                if (desc) {
374                    efree(desc);
375                }
376                if (source) {
377                    efree(source);
378                }
379                if (e.bstrHelpFile) {
380                    SysFreeString(e.bstrHelpFile);
381                }
382                break;
383
384            case DISP_E_PARAMNOTFOUND:
385            case DISP_E_TYPEMISMATCH:
386                desc = php_win32_error_to_msg(hr);
387                spprintf(&msg, 0, "Parameter %d: %s", arg_err, desc);
388                LocalFree(desc);
389                break;
390
391            case DISP_E_BADPARAMCOUNT:
392                if ((disp_params->cArgs + disp_params->cNamedArgs == 0) && (allow_noarg == 1)) {
393                    /* if getting a property and they are missing all parameters,
394                     * we want to create a proxy object for them; so lets not create an
395                     * exception here */
396                    msg = NULL;
397                    break;
398                }
399                /* else fall through */
400
401            default:
402                desc = php_win32_error_to_msg(hr);
403                spprintf(&msg, 0, "Error [0x%08x] %s", hr, desc);
404                LocalFree(desc);
405                break;
406        }
407
408        if (msg) {
409            php_com_throw_exception(hr, msg TSRMLS_CC);
410            efree(msg);
411        }
412    }
413
414    return hr;
415}
416
417/* map an ID to a name */
418HRESULT php_com_get_id_of_name(php_com_dotnet_object *obj, char *name,
419        int namelen, DISPID *dispid TSRMLS_DC)
420{
421    OLECHAR *olename;
422    HRESULT hr;
423    DISPID *dispid_ptr;
424
425    if (namelen == -1) {
426        namelen = strlen(name);
427    }
428
429    if (obj->id_of_name_cache && SUCCESS == zend_hash_find(obj->id_of_name_cache, name, namelen, (void**)&dispid_ptr)) {
430        *dispid = *dispid_ptr;
431        return S_OK;
432    }
433
434    olename = php_com_string_to_olestring(name, namelen, obj->code_page TSRMLS_CC);
435
436    if (obj->typeinfo) {
437        hr = ITypeInfo_GetIDsOfNames(obj->typeinfo, &olename, 1, dispid);
438        if (FAILED(hr)) {
439            hr = IDispatch_GetIDsOfNames(V_DISPATCH(&obj->v), &IID_NULL, &olename, 1, LOCALE_SYSTEM_DEFAULT, dispid);
440            if (SUCCEEDED(hr)) {
441                /* fall back on IDispatch direct */
442                ITypeInfo_Release(obj->typeinfo);
443                obj->typeinfo = NULL;
444            }
445        }
446    } else {
447        hr = IDispatch_GetIDsOfNames(V_DISPATCH(&obj->v), &IID_NULL, &olename, 1, LOCALE_SYSTEM_DEFAULT, dispid);
448    }
449    efree(olename);
450
451    if (SUCCEEDED(hr)) {
452        /* cache the mapping */
453        if (!obj->id_of_name_cache) {
454            ALLOC_HASHTABLE(obj->id_of_name_cache);
455            zend_hash_init(obj->id_of_name_cache, 2, NULL, NULL, 0);
456        }
457        zend_hash_update(obj->id_of_name_cache, name, namelen, dispid, sizeof(*dispid), NULL);
458    }
459
460    return hr;
461}
462
463/* the core of COM */
464int php_com_do_invoke_byref(php_com_dotnet_object *obj, char *name, int namelen,
465        WORD flags, VARIANT *v, int nargs, zval ***args TSRMLS_DC)
466{
467    DISPID dispid, altdispid;
468    DISPPARAMS disp_params;
469    HRESULT hr;
470    VARIANT *vargs = NULL, *byref_vals = NULL;
471    int i, byref_count = 0, j;
472    zend_internal_function *f = (zend_internal_function*)EG(current_execute_data)->function_state.function;
473
474    /* assumption: that the active function (f) is the function we generated for the engine */
475    if (!f || f->arg_info == NULL) {
476       f = NULL;
477    }
478
479    hr = php_com_get_id_of_name(obj, name, namelen, &dispid TSRMLS_CC);
480
481    if (FAILED(hr)) {
482        char *winerr = NULL;
483        char *msg = NULL;
484        winerr = php_win32_error_to_msg(hr);
485        spprintf(&msg, 0, "Unable to lookup `%s': %s", name, winerr);
486        LocalFree(winerr);
487        php_com_throw_exception(hr, msg TSRMLS_CC);
488        efree(msg);
489        return FAILURE;
490    }
491
492
493    if (nargs) {
494        vargs = (VARIANT*)safe_emalloc(sizeof(VARIANT), nargs, 0);
495    }
496
497    if (f) {
498        for (i = 0; i < nargs; i++) {
499            if (f->arg_info[nargs - i - 1].pass_by_reference) {
500                byref_count++;
501            }
502        }
503    }
504
505    if (byref_count) {
506        byref_vals = (VARIANT*)safe_emalloc(sizeof(VARIANT), byref_count, 0);
507        for (j = 0, i = 0; i < nargs; i++) {
508            if (f->arg_info[nargs - i - 1].pass_by_reference) {
509                /* put the value into byref_vals instead */
510                php_com_variant_from_zval(&byref_vals[j], *args[nargs - i - 1], obj->code_page TSRMLS_CC);
511
512                /* if it is already byref, "move" it into the vargs array, otherwise
513                 * make vargs a reference to this value */
514                if (V_VT(&byref_vals[j]) & VT_BYREF) {
515                    memcpy(&vargs[i], &byref_vals[j], sizeof(vargs[i]));
516                    VariantInit(&byref_vals[j]); /* leave the variant slot empty to simplify cleanup */
517                } else {
518                    VariantInit(&vargs[i]);
519                    V_VT(&vargs[i]) = V_VT(&byref_vals[j]) | VT_BYREF;
520                    /* union magic ensures that this works out */
521                    vargs[i].byref = &V_UINT(&byref_vals[j]);
522                }
523                j++;
524            } else {
525                php_com_variant_from_zval(&vargs[i], *args[nargs - i - 1], obj->code_page TSRMLS_CC);
526            }
527        }
528
529    } else {
530        /* Invoke'd args are in reverse order */
531        for (i = 0; i < nargs; i++) {
532            php_com_variant_from_zval(&vargs[i], *args[nargs - i - 1], obj->code_page TSRMLS_CC);
533        }
534    }
535
536    disp_params.cArgs = nargs;
537    disp_params.cNamedArgs = 0;
538    disp_params.rgvarg = vargs;
539    disp_params.rgdispidNamedArgs = NULL;
540
541    if (flags & DISPATCH_PROPERTYPUT) {
542        altdispid = DISPID_PROPERTYPUT;
543        disp_params.rgdispidNamedArgs = &altdispid;
544        disp_params.cNamedArgs = 1;
545    }
546
547    /* this will create an exception if needed */
548    hr = php_com_invoke_helper(obj, dispid, flags, &disp_params, v, 0, 0 TSRMLS_CC);
549
550    /* release variants */
551    if (vargs) {
552        for (i = 0, j = 0; i < nargs; i++) {
553            /* if this was byref, update the zval */
554            if (f && f->arg_info[nargs - i - 1].pass_by_reference) {
555                SEPARATE_ZVAL_IF_NOT_REF(args[nargs - i - 1]);
556
557                /* if the variant is pointing at the byref_vals, we need to map
558                 * the pointee value as a zval; otherwise, the value is pointing
559                 * into an existing PHP variant record */
560                if (V_VT(&vargs[i]) & VT_BYREF) {
561                    if (vargs[i].byref == &V_UINT(&byref_vals[j])) {
562                        /* copy that value */
563                        php_com_zval_from_variant(*args[nargs - i - 1], &byref_vals[j],
564                            obj->code_page TSRMLS_CC);
565                    }
566                } else {
567                    /* not sure if this can ever happen; the variant we marked as BYREF
568                     * is no longer BYREF - copy its value */
569                    php_com_zval_from_variant(*args[nargs - i - 1], &vargs[i],
570                        obj->code_page TSRMLS_CC);
571                }
572                VariantClear(&byref_vals[j]);
573                j++;
574            }
575            VariantClear(&vargs[i]);
576        }
577        efree(vargs);
578    }
579
580    return SUCCEEDED(hr) ? SUCCESS : FAILURE;
581}
582
583
584
585int php_com_do_invoke_by_id(php_com_dotnet_object *obj, DISPID dispid,
586        WORD flags, VARIANT *v, int nargs, zval **args, int silent, int allow_noarg TSRMLS_DC)
587{
588    DISPID altdispid;
589    DISPPARAMS disp_params;
590    HRESULT hr;
591    VARIANT *vargs = NULL;
592    int i;
593
594    if (nargs) {
595        vargs = (VARIANT*)safe_emalloc(sizeof(VARIANT), nargs, 0);
596    }
597
598    /* Invoke'd args are in reverse order */
599    for (i = 0; i < nargs; i++) {
600        php_com_variant_from_zval(&vargs[i], args[nargs - i - 1], obj->code_page TSRMLS_CC);
601    }
602
603    disp_params.cArgs = nargs;
604    disp_params.cNamedArgs = 0;
605    disp_params.rgvarg = vargs;
606    disp_params.rgdispidNamedArgs = NULL;
607
608    if (flags & DISPATCH_PROPERTYPUT) {
609        altdispid = DISPID_PROPERTYPUT;
610        disp_params.rgdispidNamedArgs = &altdispid;
611        disp_params.cNamedArgs = 1;
612    }
613
614    /* this will create an exception if needed */
615    hr = php_com_invoke_helper(obj, dispid, flags, &disp_params, v, silent, allow_noarg TSRMLS_CC);
616
617    /* release variants */
618    if (vargs) {
619        for (i = 0; i < nargs; i++) {
620            VariantClear(&vargs[i]);
621        }
622        efree(vargs);
623    }
624
625    /* a bit of a hack this, but it's needed for COM array access. */
626    if (hr == DISP_E_BADPARAMCOUNT)
627        return hr;
628
629    return SUCCEEDED(hr) ? SUCCESS : FAILURE;
630}
631
632int php_com_do_invoke(php_com_dotnet_object *obj, char *name, int namelen,
633        WORD flags, VARIANT *v, int nargs, zval **args, int allow_noarg TSRMLS_DC)
634{
635    DISPID dispid;
636    HRESULT hr;
637    char *winerr = NULL;
638    char *msg = NULL;
639
640    hr = php_com_get_id_of_name(obj, name, namelen, &dispid TSRMLS_CC);
641
642    if (FAILED(hr)) {
643        winerr = php_win32_error_to_msg(hr);
644        spprintf(&msg, 0, "Unable to lookup `%s': %s", name, winerr);
645        LocalFree(winerr);
646        php_com_throw_exception(hr, msg TSRMLS_CC);
647        efree(msg);
648        return FAILURE;
649    }
650
651    return php_com_do_invoke_by_id(obj, dispid, flags, v, nargs, args, 0, allow_noarg TSRMLS_CC);
652}
653
654/* {{{ proto string com_create_guid()
655   Generate a globally unique identifier (GUID) */
656PHP_FUNCTION(com_create_guid)
657{
658    GUID retval;
659    OLECHAR *guid_string;
660
661    if (zend_parse_parameters_none() == FAILURE) {
662        return;
663    }
664
665    php_com_initialize(TSRMLS_C);
666    if (CoCreateGuid(&retval) == S_OK && StringFromCLSID(&retval, &guid_string) == S_OK) {
667        Z_TYPE_P(return_value) = IS_STRING;
668        Z_STRVAL_P(return_value) = php_com_olestring_to_string(guid_string, &Z_STRLEN_P(return_value), CP_ACP TSRMLS_CC);
669
670        CoTaskMemFree(guid_string);
671    } else {
672        RETURN_FALSE;
673    }
674}
675/* }}} */
676
677/* {{{ proto bool com_event_sink(object comobject, object sinkobject [, mixed sinkinterface])
678   Connect events from a COM object to a PHP object */
679PHP_FUNCTION(com_event_sink)
680{
681    zval *object, *sinkobject, *sink=NULL;
682    char *dispname = NULL, *typelibname = NULL;
683    zend_bool gotguid = 0;
684    php_com_dotnet_object *obj;
685    ITypeInfo *typeinfo = NULL;
686
687    RETVAL_FALSE;
688
689    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Oo|z/",
690            &object, php_com_variant_class_entry, &sinkobject, &sink)) {
691        RETURN_FALSE;
692    }
693
694    php_com_initialize(TSRMLS_C);
695    obj = CDNO_FETCH(object);
696
697    if (sink && Z_TYPE_P(sink) == IS_ARRAY) {
698        /* 0 => typelibname, 1 => dispname */
699        zval **tmp;
700
701        if (zend_hash_index_find(Z_ARRVAL_P(sink), 0, (void**)&tmp) == SUCCESS)
702            typelibname = Z_STRVAL_PP(tmp);
703        if (zend_hash_index_find(Z_ARRVAL_P(sink), 1, (void**)&tmp) == SUCCESS)
704            dispname = Z_STRVAL_PP(tmp);
705    } else if (sink != NULL) {
706        convert_to_string(sink);
707        dispname = Z_STRVAL_P(sink);
708    }
709
710    typeinfo = php_com_locate_typeinfo(typelibname, obj, dispname, 1 TSRMLS_CC);
711
712    if (typeinfo) {
713        HashTable *id_to_name;
714
715        ALLOC_HASHTABLE(id_to_name);
716
717        if (php_com_process_typeinfo(typeinfo, id_to_name, 0, &obj->sink_id, obj->code_page TSRMLS_CC)) {
718
719            /* Create the COM wrapper for this sink */
720            obj->sink_dispatch = php_com_wrapper_export_as_sink(sinkobject, &obj->sink_id, id_to_name TSRMLS_CC);
721
722            /* Now hook it up to the source */
723            php_com_object_enable_event_sink(obj, TRUE TSRMLS_CC);
724            RETVAL_TRUE;
725
726        } else {
727            FREE_HASHTABLE(id_to_name);
728        }
729    }
730
731    if (typeinfo) {
732        ITypeInfo_Release(typeinfo);
733    }
734
735}
736/* }}} */
737
738/* {{{ proto bool com_print_typeinfo(object comobject | string typelib, string dispinterface, bool wantsink)
739   Print out a PHP class definition for a dispatchable interface */
740PHP_FUNCTION(com_print_typeinfo)
741{
742    zval *arg1;
743    char *ifacename = NULL;
744    char *typelibname = NULL;
745    int ifacelen;
746    zend_bool wantsink = 0;
747    php_com_dotnet_object *obj = NULL;
748    ITypeInfo *typeinfo;
749
750    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/|s!b", &arg1, &ifacename,
751                &ifacelen, &wantsink)) {
752        RETURN_FALSE;
753    }
754
755    php_com_initialize(TSRMLS_C);
756    if (Z_TYPE_P(arg1) == IS_OBJECT) {
757        CDNO_FETCH_VERIFY(obj, arg1);
758    } else {
759        convert_to_string(arg1);
760        typelibname = Z_STRVAL_P(arg1);
761    }
762
763    typeinfo = php_com_locate_typeinfo(typelibname, obj, ifacename, wantsink ? 1 : 0 TSRMLS_CC);
764    if (typeinfo) {
765        php_com_process_typeinfo(typeinfo, NULL, 1, NULL, obj ? obj->code_page : COMG(code_page) TSRMLS_CC);
766        ITypeInfo_Release(typeinfo);
767        RETURN_TRUE;
768    } else {
769        zend_error(E_WARNING, "Unable to find typeinfo using the parameters supplied");
770    }
771    RETURN_FALSE;
772}
773/* }}} */
774
775/* {{{ proto bool com_message_pump([int timeoutms])
776   Process COM messages, sleeping for up to timeoutms milliseconds */
777PHP_FUNCTION(com_message_pump)
778{
779    long timeoutms = 0;
780    MSG msg;
781    DWORD result;
782
783    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &timeoutms) == FAILURE)
784        RETURN_FALSE;
785
786    php_com_initialize(TSRMLS_C);
787    result = MsgWaitForMultipleObjects(0, NULL, FALSE, timeoutms, QS_ALLINPUT);
788
789    if (result == WAIT_OBJECT_0) {
790        while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
791            TranslateMessage(&msg);
792            DispatchMessage(&msg);
793        }
794        /* we processed messages */
795        RETVAL_TRUE;
796    } else {
797        /* we did not process messages (timed out) */
798        RETVAL_FALSE;
799    }
800}
801/* }}} */
802
803/* {{{ proto bool com_load_typelib(string typelib_name [, int case_insensitive])
804   Loads a Typelibrary and registers its constants */
805PHP_FUNCTION(com_load_typelib)
806{
807    char *name;
808    int namelen;
809    ITypeLib *pTL = NULL;
810    zend_bool cs = TRUE;
811    int codepage = COMG(code_page);
812    int cached = 0;
813
814    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &name, &namelen, &cs)) {
815        return;
816    }
817
818    RETVAL_FALSE;
819
820    php_com_initialize(TSRMLS_C);
821    pTL = php_com_load_typelib_via_cache(name, codepage, &cached TSRMLS_CC);
822    if (pTL) {
823        if (cached) {
824            RETVAL_TRUE;
825        } else if (php_com_import_typelib(pTL, cs ? CONST_CS : 0, codepage TSRMLS_CC) == SUCCESS) {
826            RETVAL_TRUE;
827        }
828
829        ITypeLib_Release(pTL);
830        pTL = NULL;
831    }
832}
833/* }}} */
834
835
836
837/*
838 * Local variables:
839 * tab-width: 4
840 * c-basic-offset: 4
841 * End:
842 * vim600: noet sw=4 ts=4 fdm=marker
843 * vim<600: noet sw=4 ts=4
844 */
845