php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
com_persist.c
Go to the documentation of this file.
1/*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Wez Furlong <wez@thebrainroom.com> |
14 +----------------------------------------------------------------------+
15 */
16
17/* Infrastructure for working with persistent COM objects.
18 * Implements: IStream* wrapper for PHP streams.
19 * TODO:
20 * - Magic __wakeup and __sleep handlers for serialization.
21 * - Track the stream and dispatch instances in a global list to make sure
22 * they are destroyed when a fatal error occurs.
23 */
24
25#ifdef HAVE_CONFIG_H
26#include <config.h>
27#endif
28
29#include "php.h"
30#include "php_ini.h"
31#include "ext/standard/info.h"
32#include "php_com_dotnet.h"
35#include "com_persist_arginfo.h"
36
37/* {{{ expose php_stream as a COM IStream */
38
39typedef struct {
40 CONST_VTBL struct IStreamVtbl *lpVtbl;
45
46static void istream_destructor(php_istream *stm);
47
48#define FETCH_STM() \
49 php_istream *stm = (php_istream*)This; \
50 if (GetCurrentThreadId() != stm->engine_thread) \
51 return RPC_E_WRONG_THREAD;
52
53#define FETCH_STM_EX() \
54 php_istream *stm = (php_istream*)This; \
55 if (GetCurrentThreadId() != stm->engine_thread) \
56 return RPC_E_WRONG_THREAD;
57
58static HRESULT STDMETHODCALLTYPE stm_queryinterface(
59 IStream *This,
60 /* [in] */ REFIID riid,
61 /* [iid_is][out] */ void **ppvObject)
62{
64
65 if (IsEqualGUID(&IID_IUnknown, riid) ||
66 IsEqualGUID(&IID_IStream, riid)) {
67 *ppvObject = This;
68 InterlockedIncrement(&stm->refcount);
69 return S_OK;
70 }
71
72 *ppvObject = NULL;
73 return E_NOINTERFACE;
74}
75
76static ULONG STDMETHODCALLTYPE stm_addref(IStream *This)
77{
79
80 return InterlockedIncrement(&stm->refcount);
81}
82
83static ULONG STDMETHODCALLTYPE stm_release(IStream *This)
84{
85 ULONG ret;
86 FETCH_STM();
87
88 ret = InterlockedDecrement(&stm->refcount);
89 if (ret == 0) {
90 /* destroy it */
91 istream_destructor(stm);
92 }
93 return ret;
94}
95
96static HRESULT STDMETHODCALLTYPE stm_read(IStream *This, void *pv, ULONG cb, ULONG *pcbRead)
97{
98 ULONG nread;
99 FETCH_STM();
100
101 nread = (ULONG)php_stream_read(stm->stream, pv, cb);
102
103 if (pcbRead) {
104 *pcbRead = nread > 0 ? nread : 0;
105 }
106 if (nread > 0) {
107 return S_OK;
108 }
109 return S_FALSE;
110}
111
112static HRESULT STDMETHODCALLTYPE stm_write(IStream *This, void const *pv, ULONG cb, ULONG *pcbWritten)
113{
114 ssize_t nwrote;
115 FETCH_STM();
116
117 nwrote = php_stream_write(stm->stream, pv, cb);
118
119 if (pcbWritten) {
120 *pcbWritten = nwrote > 0 ? (ULONG)nwrote : 0;
121 }
122 if (nwrote > 0) {
123 return S_OK;
124 }
125 return S_FALSE;
126}
127
128static HRESULT STDMETHODCALLTYPE stm_seek(IStream *This, LARGE_INTEGER dlibMove,
129 DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
130{
131 off_t offset;
132 int whence;
133 int ret;
134 FETCH_STM();
135
136 switch (dwOrigin) {
137 case STREAM_SEEK_SET: whence = SEEK_SET; break;
138 case STREAM_SEEK_CUR: whence = SEEK_CUR; break;
139 case STREAM_SEEK_END: whence = SEEK_END; break;
140 default:
141 return STG_E_INVALIDFUNCTION;
142 }
143
144 if (dlibMove.HighPart) {
145 /* we don't support 64-bit offsets */
146 return STG_E_INVALIDFUNCTION;
147 }
148
149 offset = (off_t) dlibMove.QuadPart;
150
151 ret = php_stream_seek(stm->stream, offset, whence);
152
153 if (plibNewPosition) {
154 plibNewPosition->QuadPart = (ULONGLONG)(ret >= 0 ? ret : 0);
155 }
156
157 return ret >= 0 ? S_OK : STG_E_INVALIDFUNCTION;
158}
159
160static HRESULT STDMETHODCALLTYPE stm_set_size(IStream *This, ULARGE_INTEGER libNewSize)
161{
162 FETCH_STM();
163
164 if (libNewSize.HighPart) {
165 return STG_E_INVALIDFUNCTION;
166 }
167
168 if (php_stream_truncate_supported(stm->stream)) {
169 int ret = php_stream_truncate_set_size(stm->stream, (size_t)libNewSize.QuadPart);
170
171 if (ret == 0) {
172 return S_OK;
173 }
174 }
175
176 return STG_E_INVALIDFUNCTION;
177}
178
179static HRESULT STDMETHODCALLTYPE stm_copy_to(IStream *This, IStream *pstm, ULARGE_INTEGER cb,
180 ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
181{
182 FETCH_STM_EX();
183
184 return E_NOTIMPL;
185}
186
187static HRESULT STDMETHODCALLTYPE stm_commit(IStream *This, DWORD grfCommitFlags)
188{
189 FETCH_STM();
190
191 php_stream_flush(stm->stream);
192
193 return S_OK;
194}
195
196static HRESULT STDMETHODCALLTYPE stm_revert(IStream *This)
197{
198 /* NOP */
199 return S_OK;
200}
201
202static HRESULT STDMETHODCALLTYPE stm_lock_region(IStream *This,
203 ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD lockType)
204{
205 return STG_E_INVALIDFUNCTION;
206}
207
208static HRESULT STDMETHODCALLTYPE stm_unlock_region(IStream *This,
209 ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD lockType)
210{
211 return STG_E_INVALIDFUNCTION;
212}
213
214static HRESULT STDMETHODCALLTYPE stm_stat(IStream *This,
215 STATSTG *pstatstg, DWORD grfStatFlag)
216{
217 return STG_E_INVALIDFUNCTION;
218}
219
220static HRESULT STDMETHODCALLTYPE stm_clone(IStream *This, IStream **ppstm)
221{
222 return STG_E_INVALIDFUNCTION;
223}
224
225static struct IStreamVtbl php_istream_vtbl = {
226 stm_queryinterface,
227 stm_addref,
228 stm_release,
229 stm_read,
230 stm_write,
231 stm_seek,
232 stm_set_size,
233 stm_copy_to,
234 stm_commit,
235 stm_revert,
236 stm_lock_region,
237 stm_unlock_region,
238 stm_stat,
239 stm_clone
240};
241
242static void istream_destructor(php_istream *stm)
243{
244 if (stm->refcount > 0) {
245 CoDisconnectObject((IUnknown*)stm, 0);
246 }
247
249
250 CoTaskMemFree(stm);
251}
252/* }}} */
253
255{
256 php_istream *stm = (php_istream*)CoTaskMemAlloc(sizeof(*stm));
257
258 if (stm == NULL)
259 return NULL;
260
261 memset(stm, 0, sizeof(*stm));
262 stm->engine_thread = GetCurrentThreadId();
263 stm->lpVtbl = &php_istream_vtbl;
264 stm->refcount = 1;
265 stm->stream = stream;
266
267 GC_ADDREF(stream->res);
268
269 return (IStream*)stm;
270}
271
272#define CPH_METHOD(fname) PHP_METHOD(COMPersistHelper, fname)
273
274#define CPH_FETCH() php_com_persist_helper *helper = (php_com_persist_helper*)Z_OBJ_P(ZEND_THIS);
275
276#define CPH_NO_OBJ() if (helper->unk == NULL) { php_com_throw_exception(E_INVALIDARG, "No COM object is associated with this helper instance"); RETURN_THROWS(); }
277
278typedef struct {
281 IUnknown *unk;
282 IPersistStream *ips;
283 IPersistStreamInit *ipsi;
284 IPersistFile *ipf;
286
287static zend_object_handlers helper_handlers;
288static zend_class_entry *helper_ce;
289
290static inline HRESULT get_persist_stream(php_com_persist_helper *helper)
291{
292 if (!helper->ips && helper->unk) {
293 return IUnknown_QueryInterface(helper->unk, &IID_IPersistStream, &helper->ips);
294 }
295 return helper->ips ? S_OK : E_NOTIMPL;
296}
297
298static inline HRESULT get_persist_stream_init(php_com_persist_helper *helper)
299{
300 if (!helper->ipsi && helper->unk) {
301 return IUnknown_QueryInterface(helper->unk, &IID_IPersistStreamInit, &helper->ipsi);
302 }
303 return helper->ipsi ? S_OK : E_NOTIMPL;
304}
305
306static inline HRESULT get_persist_file(php_com_persist_helper *helper)
307{
308 if (!helper->ipf && helper->unk) {
309 return IUnknown_QueryInterface(helper->unk, &IID_IPersistFile, &helper->ipf);
310 }
311 return helper->ipf ? S_OK : E_NOTIMPL;
312}
313
314
315/* {{{ Determines the filename into which an object will be saved, or false if none is set, via IPersistFile::GetCurFile */
316CPH_METHOD(GetCurFileName)
317{
318 HRESULT res;
319 OLECHAR *olename = NULL;
320 CPH_FETCH();
321
324 }
325
326 CPH_NO_OBJ();
327
328 res = get_persist_file(helper);
329 if (helper->ipf) {
330 res = IPersistFile_GetCurFile(helper->ipf, &olename);
331
332 if (res == S_OK) {
333 zend_string *str = php_com_olestring_to_string(olename, helper->codepage);
334 CoTaskMemFree(olename);
335 RETURN_STR(str);
336 } else if (res == S_FALSE) {
337 CoTaskMemFree(olename);
339 }
341 } else {
343 }
344}
345/* }}} */
346
347
348/* {{{ Persist object data to file, via IPersistFile::Save */
349CPH_METHOD(SaveToFile)
350{
351 HRESULT res;
352 char *filename, *fullpath = NULL;
353 size_t filename_len;
354 bool remember = TRUE;
355 OLECHAR *olefilename = NULL;
356 CPH_FETCH();
357
359 &filename, &filename_len, &remember)) {
361 }
362
363 CPH_NO_OBJ();
364
365 res = get_persist_file(helper);
366 if (helper->ipf) {
367 if (filename) {
368 fullpath = expand_filepath(filename, NULL);
369 if (!fullpath) {
371 }
372
373 if (php_check_open_basedir(fullpath)) {
374 efree(fullpath);
376 }
377
378 olefilename = php_com_string_to_olestring(fullpath, strlen(fullpath), helper->codepage);
379 efree(fullpath);
380 }
381 res = IPersistFile_Save(helper->ipf, olefilename, remember);
382 if (SUCCEEDED(res)) {
383 if (!olefilename) {
384 res = IPersistFile_GetCurFile(helper->ipf, &olefilename);
385 if (S_OK == res) {
386 IPersistFile_SaveCompleted(helper->ipf, olefilename);
387 CoTaskMemFree(olefilename);
388 olefilename = NULL;
389 }
390 } else if (remember) {
391 IPersistFile_SaveCompleted(helper->ipf, olefilename);
392 }
393 }
394
395 if (olefilename) {
396 efree(olefilename);
397 }
398
399 if (FAILED(res)) {
401 }
402
403 } else {
405 }
406}
407/* }}} */
408
409/* {{{ Load object data from file, via IPersistFile::Load */
410CPH_METHOD(LoadFromFile)
411{
412 HRESULT res;
413 char *filename, *fullpath;
414 size_t filename_len;
415 zend_long flags = 0;
416 OLECHAR *olefilename;
417 CPH_FETCH();
418
420 &filename, &filename_len, &flags)) {
422 }
423
424 CPH_NO_OBJ();
425
426 res = get_persist_file(helper);
427 if (helper->ipf) {
428 if (!(fullpath = expand_filepath(filename, NULL))) {
430 }
431
432 if (php_check_open_basedir(fullpath)) {
433 efree(fullpath);
435 }
436
437 olefilename = php_com_string_to_olestring(fullpath, strlen(fullpath), helper->codepage);
438 efree(fullpath);
439
440 res = IPersistFile_Load(helper->ipf, olefilename, (DWORD)flags);
441 efree(olefilename);
442
443 if (FAILED(res)) {
445 }
446
447 } else {
449 }
450}
451/* }}} */
452
453/* {{{ Gets maximum stream size required to store the object data, via IPersistStream::GetSizeMax (or IPersistStreamInit::GetSizeMax) */
454CPH_METHOD(GetMaxStreamSize)
455{
456 HRESULT res;
457 ULARGE_INTEGER size;
458 CPH_FETCH();
459
462 }
463
464 CPH_NO_OBJ();
465
466 res = get_persist_stream_init(helper);
467 if (helper->ipsi) {
468 res = IPersistStreamInit_GetSizeMax(helper->ipsi, &size);
469 } else {
470 res = get_persist_stream(helper);
471 if (helper->ips) {
472 res = IPersistStream_GetSizeMax(helper->ips, &size);
473 } else {
476 }
477 }
478
479 if (res != S_OK) {
481 } else {
482 /* TODO: handle 64 bit properly */
483 RETURN_LONG((zend_long)size.QuadPart);
484 }
485}
486/* }}} */
487
488/* {{{ Initializes the object to a default state, via IPersistStreamInit::InitNew */
490{
491 HRESULT res;
492 CPH_FETCH();
493
496 }
497
498 CPH_NO_OBJ();
499
500 res = get_persist_stream_init(helper);
501 if (helper->ipsi) {
502 res = IPersistStreamInit_InitNew(helper->ipsi);
503
504 if (res != S_OK) {
506 } else {
508 }
509 } else {
511 }
512}
513/* }}} */
514
515/* {{{ Initializes an object from the stream where it was previously saved, via IPersistStream::Load or OleLoadFromStream */
516CPH_METHOD(LoadFromStream)
517{
518 zval *zstm;
519 php_stream *stream;
520 IStream *stm = NULL;
521 HRESULT res;
522 CPH_FETCH();
523
524 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zstm)) {
526 }
527
528 php_stream_from_zval_no_verify(stream, zstm);
529
530 if (stream == NULL) {
531 php_com_throw_exception(E_INVALIDARG, "expected a stream");
533 }
534
535 stm = php_com_wrapper_export_stream(stream);
536 if (stm == NULL) {
537 php_com_throw_exception(E_UNEXPECTED, "failed to wrap stream");
539 }
540
541 res = S_OK;
543
544 if (helper->unk == NULL) {
545 IDispatch *disp = NULL;
546
547 /* we need to create an object and load using OleLoadFromStream */
548 res = OleLoadFromStream(stm, &IID_IDispatch, &disp);
549
550 if (SUCCEEDED(res)) {
552 }
553 } else {
554 res = get_persist_stream_init(helper);
555 if (helper->ipsi) {
556 res = IPersistStreamInit_Load(helper->ipsi, stm);
557 } else {
558 res = get_persist_stream(helper);
559 if (helper->ips) {
560 res = IPersistStreamInit_Load(helper->ipsi, stm);
561 }
562 }
563 }
564 IStream_Release(stm);
565
566 if (FAILED(res)) {
569 }
570}
571/* }}} */
572
573/* {{{ Saves the object to a stream, via IPersistStream::Save */
574CPH_METHOD(SaveToStream)
575{
576 zval *zstm;
577 php_stream *stream;
578 IStream *stm = NULL;
579 HRESULT res;
580 CPH_FETCH();
581
582 CPH_NO_OBJ();
583
584 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zstm)) {
586 }
587
588 php_stream_from_zval_no_verify(stream, zstm);
589
590 if (stream == NULL) {
591 php_com_throw_exception(E_INVALIDARG, "expected a stream");
593 }
594
595 stm = php_com_wrapper_export_stream(stream);
596 if (stm == NULL) {
597 php_com_throw_exception(E_UNEXPECTED, "failed to wrap stream");
599 }
600
601 res = get_persist_stream_init(helper);
602 if (helper->ipsi) {
603 res = IPersistStreamInit_Save(helper->ipsi, stm, TRUE);
604 } else {
605 res = get_persist_stream(helper);
606 if (helper->ips) {
607 res = IPersistStream_Save(helper->ips, stm, TRUE);
608 }
609 }
610
611 IStream_Release(stm);
612
613 if (FAILED(res)) {
616 }
617
619}
620/* }}} */
621
622/* {{{ Creates a persistence helper object, usually associated with a com_object */
623CPH_METHOD(__construct)
624{
626 zval *zobj = NULL;
627 CPH_FETCH();
628
632 }
633
634 if (!zobj) {
635 return;
636 }
637
638 obj = CDNO_FETCH(zobj);
639
640 if (V_VT(&obj->v) != VT_DISPATCH || V_DISPATCH(&obj->v) == NULL) {
641 php_com_throw_exception(E_INVALIDARG, "parameter must represent an IDispatch COM object");
643 }
644
645 /* it is always safe to cast an interface to IUnknown */
646 helper->unk = (IUnknown*)V_DISPATCH(&obj->v);
647 IUnknown_AddRef(helper->unk);
648 helper->codepage = obj->code_page;
649}
650/* }}} */
651
652
653static void helper_free_storage(zend_object *obj)
654{
656
657 if (object->ipf) {
658 IPersistFile_Release(object->ipf);
659 }
660 if (object->ips) {
661 IPersistStream_Release(object->ips);
662 }
663 if (object->ipsi) {
664 IPersistStreamInit_Release(object->ipsi);
665 }
666 if (object->unk) {
667 IUnknown_Release(object->unk);
668 }
670}
671
672
673static zend_object* helper_clone(zend_object *obj)
674{
675 php_com_persist_helper *clone, *object = (php_com_persist_helper*) obj;
676
677 clone = emalloc(sizeof(*object));
678 memcpy(clone, object, sizeof(*object));
679
680 zend_object_std_init(&clone->std, object->std.ce);
681
682 if (clone->ipf) {
683 IPersistFile_AddRef(clone->ipf);
684 }
685 if (clone->ips) {
686 IPersistStream_AddRef(clone->ips);
687 }
688 if (clone->ipsi) {
689 IPersistStreamInit_AddRef(clone->ipsi);
690 }
691 if (clone->unk) {
692 IUnknown_AddRef(clone->unk);
693 }
694 return (zend_object*)clone;
695}
696
697static zend_object* helper_new(zend_class_entry *ce)
698{
700
701 helper = emalloc(sizeof(*helper));
702 memset(helper, 0, sizeof(*helper));
703
704 zend_object_std_init(&helper->std, helper_ce);
705
706 return &helper->std;
707}
708
710{
711 memcpy(&helper_handlers, &std_object_handlers, sizeof(helper_handlers));
712 helper_handlers.free_obj = helper_free_storage;
713 helper_handlers.clone_obj = helper_clone;
714
715 helper_ce = register_class_COMPersistHelper();
716 helper_ce->create_object = helper_new;
717 helper_ce->default_object_handlers = &helper_handlers;
718}
char * cb
Definition assert.c:26
zend_class_entry * php_com_variant_class_entry
const VT_DISPATCH
void php_com_throw_exception(HRESULT code, char *message)
Definition com_misc.c:28
PHP_COM_DOTNET_API void php_com_wrap_dispatch(zval *z, IDispatch *disp, int codepage)
Definition com_misc.c:45
PHP_COM_DOTNET_API zend_string * php_com_olestring_to_string(OLECHAR *olestring, int codepage)
Definition com_olechar.c:70
PHP_COM_DOTNET_API OLECHAR * php_com_string_to_olestring(const char *string, size_t string_len, int codepage)
Definition com_olechar.c:29
void php_com_persist_minit(INIT_FUNC_ARGS)
#define CPH_FETCH()
#define CPH_METHOD(fname)
#define FETCH_STM()
Definition com_persist.c:48
PHP_COM_DOTNET_API IStream * php_com_wrapper_export_stream(php_stream *stream)
#define CPH_NO_OBJ()
#define FETCH_STM_EX()
Definition com_persist.c:53
#define DWORD
Definition exif.c:1762
new_type size
Definition ffi.c:4365
zend_string * res
Definition ffi.c:4692
memcpy(ptr1, ptr2, size)
memset(ptr, 0, type->size)
const SEEK_CUR
Definition file.stub.php:16
const SEEK_END
Definition file.stub.php:21
PHPAPI int php_check_open_basedir(const char *path)
PHPAPI char * expand_filepath(const char *filepath, char *real_path)
zend_long offset
size_t filename_len
#define TRUE
Definition gd_gd.c:7
#define SEEK_SET
Definition gd_io_file.c:20
#define NULL
Definition gdcache.h:45
#define COMG(v)
int code_page
#define PHP_COM_DOTNET_API
#define CDNO_FETCH(zv)
struct _php_com_dotnet_object php_com_dotnet_object
#define php_stream_truncate_supported(stream)
struct _php_stream php_stream
Definition php_streams.h:96
#define php_stream_read(stream, buf, count)
#define php_stream_seek(stream, offset, whence)
#define php_stream_flush(stream)
#define php_stream_truncate_set_size(stream, size)
#define php_stream_from_zval_no_verify(xstr, pzval)
#define php_stream_write(stream, buf, count)
zend_resource * res
IPersistStream * ips
IPersistStreamInit * ipsi
CONST_VTBL struct IStreamVtbl * lpVtbl
Definition com_persist.c:40
DWORD engine_thread
Definition com_persist.c:41
php_stream * stream
Definition com_persist.c:43
ZEND_API zend_result zend_parse_parameters(uint32_t num_args, const char *type_spec,...)
Definition zend_API.c:1300
#define ZEND_NUM_ARGS()
Definition zend_API.h:530
#define RETURN_FALSE
Definition zend_API.h:1058
#define zend_parse_parameters_none()
Definition zend_API.h:353
#define RETURN_LONG(l)
Definition zend_API.h:1037
#define RETURN_THROWS()
Definition zend_API.h:1060
#define RETVAL_TRUE
Definition zend_API.h:1033
#define RETURN_STR(s)
Definition zend_API.h:1039
#define RETURN_TRUE
Definition zend_API.h:1059
#define efree(ptr)
Definition zend_alloc.h:155
#define emalloc(size)
Definition zend_alloc.h:151
struct _zval_struct zval
strlen(string $string)
ZEND_API zend_result ZEND_FASTCALL zend_list_delete(zend_resource *res)
Definition zend_list.c:46
int32_t zend_long
Definition zend_long.h:42
struct _zend_string zend_string
#define INIT_FUNC_ARGS
ZEND_API const zend_object_handlers std_object_handlers
ZEND_API void ZEND_FASTCALL zend_object_std_init(zend_object *object, zend_class_entry *ce)
ZEND_API void zend_object_std_dtor(zend_object *object)
struct _zend_class_entry zend_class_entry
struct _zend_object zend_object
#define GC_ADDREF(p)
Definition zend_types.h:709
@ FAILURE
Definition zend_types.h:61
struct _zend_object_handlers zend_object_handlers
Definition zend_types.h:88
zval * return_value
object
zval * ret
zend_object * zobj