xref: /PHP-7.4/ext/com_dotnet/com_persist.c (revision 2adf1c4d)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 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 /* Infrastructure for working with persistent COM objects.
20  * Implements: IStream* wrapper for PHP streams.
21  * TODO: Magic __wakeup and __sleep handlers for serialization
22  * (can wait till 5.1) */
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 
28 #include "php.h"
29 #include "php_ini.h"
30 #include "ext/standard/info.h"
31 #include "php_com_dotnet.h"
32 #include "php_com_dotnet_internal.h"
33 #include "Zend/zend_exceptions.h"
34 
35 /* {{{ expose php_stream as a COM IStream */
36 
37 typedef struct {
38 	CONST_VTBL struct IStreamVtbl *lpVtbl;
39 	DWORD engine_thread;
40 	LONG refcount;
41 	php_stream *stream;
42 	zend_resource *res;
43 } php_istream;
44 
45 static int le_istream;
46 static void istream_destructor(php_istream *stm);
47 
istream_dtor(zend_resource * rsrc)48 static void istream_dtor(zend_resource *rsrc)
49 {
50 	php_istream *stm = (php_istream *)rsrc->ptr;
51 	istream_destructor(stm);
52 }
53 
54 #define FETCH_STM()	\
55 	php_istream *stm = (php_istream*)This; \
56 	if (GetCurrentThreadId() != stm->engine_thread) \
57 		return RPC_E_WRONG_THREAD;
58 
59 #define FETCH_STM_EX()	\
60 	php_istream *stm = (php_istream*)This;	\
61 	if (GetCurrentThreadId() != stm->engine_thread)	\
62 		return RPC_E_WRONG_THREAD;
63 
stm_queryinterface(IStream * This,REFIID riid,void ** ppvObject)64 static HRESULT STDMETHODCALLTYPE stm_queryinterface(
65 	IStream *This,
66 	/* [in] */ REFIID riid,
67 	/* [iid_is][out] */ void **ppvObject)
68 {
69 	FETCH_STM_EX();
70 
71 	if (IsEqualGUID(&IID_IUnknown, riid) ||
72 			IsEqualGUID(&IID_IStream, riid)) {
73 		*ppvObject = This;
74 		InterlockedIncrement(&stm->refcount);
75 		return S_OK;
76 	}
77 
78 	*ppvObject = NULL;
79 	return E_NOINTERFACE;
80 }
81 
stm_addref(IStream * This)82 static ULONG STDMETHODCALLTYPE stm_addref(IStream *This)
83 {
84 	FETCH_STM_EX();
85 
86 	return InterlockedIncrement(&stm->refcount);
87 }
88 
stm_release(IStream * This)89 static ULONG STDMETHODCALLTYPE stm_release(IStream *This)
90 {
91 	ULONG ret;
92 	FETCH_STM();
93 
94 	ret = InterlockedDecrement(&stm->refcount);
95 	if (ret == 0) {
96 		/* destroy it */
97 		if (stm->res)
98 			zend_list_delete(stm->res);
99 	}
100 	return ret;
101 }
102 
stm_read(IStream * This,void * pv,ULONG cb,ULONG * pcbRead)103 static HRESULT STDMETHODCALLTYPE stm_read(IStream *This, void *pv, ULONG cb, ULONG *pcbRead)
104 {
105 	ULONG nread;
106 	FETCH_STM();
107 
108 	nread = (ULONG)php_stream_read(stm->stream, pv, cb);
109 
110 	if (pcbRead) {
111 		*pcbRead = nread > 0 ? nread : 0;
112 	}
113 	if (nread > 0) {
114 		return S_OK;
115 	}
116 	return S_FALSE;
117 }
118 
stm_write(IStream * This,void const * pv,ULONG cb,ULONG * pcbWritten)119 static HRESULT STDMETHODCALLTYPE stm_write(IStream *This, void const *pv, ULONG cb, ULONG *pcbWritten)
120 {
121 	ssize_t nwrote;
122 	FETCH_STM();
123 
124 	nwrote = php_stream_write(stm->stream, pv, cb);
125 
126 	if (pcbWritten) {
127 		*pcbWritten = nwrote > 0 ? (ULONG)nwrote : 0;
128 	}
129 	if (nwrote > 0) {
130 		return S_OK;
131 	}
132 	return S_FALSE;
133 }
134 
stm_seek(IStream * This,LARGE_INTEGER dlibMove,DWORD dwOrigin,ULARGE_INTEGER * plibNewPosition)135 static HRESULT STDMETHODCALLTYPE stm_seek(IStream *This, LARGE_INTEGER dlibMove,
136 		DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
137 {
138 	off_t offset;
139 	int whence;
140 	int ret;
141 	FETCH_STM();
142 
143 	switch (dwOrigin) {
144 		case STREAM_SEEK_SET:	whence = SEEK_SET;	break;
145 		case STREAM_SEEK_CUR:	whence = SEEK_CUR;	break;
146 		case STREAM_SEEK_END:	whence = SEEK_END;	break;
147 		default:
148 			return STG_E_INVALIDFUNCTION;
149 	}
150 
151 	if (dlibMove.HighPart) {
152 		/* we don't support 64-bit offsets */
153 		return STG_E_INVALIDFUNCTION;
154 	}
155 
156 	offset = (off_t) dlibMove.QuadPart;
157 
158 	ret = php_stream_seek(stm->stream, offset, whence);
159 
160 	if (plibNewPosition) {
161 		plibNewPosition->QuadPart = (ULONGLONG)(ret >= 0 ? ret : 0);
162 	}
163 
164 	return ret >= 0 ? S_OK : STG_E_INVALIDFUNCTION;
165 }
166 
stm_set_size(IStream * This,ULARGE_INTEGER libNewSize)167 static HRESULT STDMETHODCALLTYPE stm_set_size(IStream *This, ULARGE_INTEGER libNewSize)
168 {
169 	FETCH_STM();
170 
171 	if (libNewSize.HighPart) {
172 		return STG_E_INVALIDFUNCTION;
173 	}
174 
175 	if (php_stream_truncate_supported(stm->stream)) {
176 		int ret = php_stream_truncate_set_size(stm->stream, (size_t)libNewSize.QuadPart);
177 
178 		if (ret == 0) {
179 			return S_OK;
180 		}
181 	}
182 
183 	return STG_E_INVALIDFUNCTION;
184 }
185 
stm_copy_to(IStream * This,IStream * pstm,ULARGE_INTEGER cb,ULARGE_INTEGER * pcbRead,ULARGE_INTEGER * pcbWritten)186 static HRESULT STDMETHODCALLTYPE stm_copy_to(IStream *This, IStream *pstm, ULARGE_INTEGER cb,
187 		ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
188 {
189 	FETCH_STM_EX();
190 
191 	return E_NOTIMPL;
192 }
193 
stm_commit(IStream * This,DWORD grfCommitFlags)194 static HRESULT STDMETHODCALLTYPE stm_commit(IStream *This, DWORD grfCommitFlags)
195 {
196 	FETCH_STM();
197 
198 	php_stream_flush(stm->stream);
199 
200 	return S_OK;
201 }
202 
stm_revert(IStream * This)203 static HRESULT STDMETHODCALLTYPE stm_revert(IStream *This)
204 {
205 	/* NOP */
206 	return S_OK;
207 }
208 
stm_lock_region(IStream * This,ULARGE_INTEGER libOffset,ULARGE_INTEGER cb,DWORD lockType)209 static HRESULT STDMETHODCALLTYPE stm_lock_region(IStream *This,
210    	ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD lockType)
211 {
212 	return STG_E_INVALIDFUNCTION;
213 }
214 
stm_unlock_region(IStream * This,ULARGE_INTEGER libOffset,ULARGE_INTEGER cb,DWORD lockType)215 static HRESULT STDMETHODCALLTYPE stm_unlock_region(IStream *This,
216 		ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD lockType)
217 {
218 	return STG_E_INVALIDFUNCTION;
219 }
220 
stm_stat(IStream * This,STATSTG * pstatstg,DWORD grfStatFlag)221 static HRESULT STDMETHODCALLTYPE stm_stat(IStream *This,
222 		STATSTG *pstatstg, DWORD grfStatFlag)
223 {
224 	return STG_E_INVALIDFUNCTION;
225 }
226 
stm_clone(IStream * This,IStream ** ppstm)227 static HRESULT STDMETHODCALLTYPE stm_clone(IStream *This, IStream **ppstm)
228 {
229 	return STG_E_INVALIDFUNCTION;
230 }
231 
232 static struct IStreamVtbl php_istream_vtbl = {
233 	stm_queryinterface,
234 	stm_addref,
235 	stm_release,
236 	stm_read,
237 	stm_write,
238 	stm_seek,
239 	stm_set_size,
240 	stm_copy_to,
241 	stm_commit,
242 	stm_revert,
243 	stm_lock_region,
244 	stm_unlock_region,
245 	stm_stat,
246 	stm_clone
247 };
248 
istream_destructor(php_istream * stm)249 static void istream_destructor(php_istream *stm)
250 {
251 	if (stm->refcount > 0) {
252 		CoDisconnectObject((IUnknown*)stm, 0);
253 	}
254 
255 	zend_list_delete(stm->stream->res);
256 
257 	CoTaskMemFree(stm);
258 }
259 /* }}} */
260 
php_com_wrapper_export_stream(php_stream * stream)261 PHP_COM_DOTNET_API IStream *php_com_wrapper_export_stream(php_stream *stream)
262 {
263 	php_istream *stm = (php_istream*)CoTaskMemAlloc(sizeof(*stm));
264 
265 	if (stm == NULL)
266 		return NULL;
267 
268 	memset(stm, 0, sizeof(*stm));
269 	stm->engine_thread = GetCurrentThreadId();
270 	stm->lpVtbl = &php_istream_vtbl;
271 	stm->refcount = 1;
272 	stm->stream = stream;
273 
274 	GC_ADDREF(stream->res);
275 	stm->res = zend_register_resource(stm, le_istream);
276 
277 	return (IStream*)stm;
278 }
279 
280 #define CPH_ME(fname, arginfo)	PHP_ME(com_persist, fname, arginfo, ZEND_ACC_PUBLIC)
281 #define CPH_SME(fname, arginfo)	PHP_ME(com_persist, fname, arginfo, ZEND_ACC_ALLOW_STATIC|ZEND_ACC_PUBLIC)
282 #define CPH_METHOD(fname)		static PHP_METHOD(com_persist, fname)
283 
284 #define CPH_FETCH()				php_com_persist_helper *helper = (php_com_persist_helper*)Z_OBJ_P(getThis());
285 
286 #define CPH_NO_OBJ()			if (helper->unk == NULL) { php_com_throw_exception(E_INVALIDARG, "No COM object is associated with this helper instance"); return; }
287 
288 typedef struct {
289 	zend_object			std;
290 	long codepage;
291 	IUnknown 			*unk;
292 	IPersistStream 		*ips;
293 	IPersistStreamInit	*ipsi;
294 	IPersistFile		*ipf;
295 } php_com_persist_helper;
296 
297 static zend_object_handlers helper_handlers;
298 static zend_class_entry *helper_ce;
299 
get_persist_stream(php_com_persist_helper * helper)300 static inline HRESULT get_persist_stream(php_com_persist_helper *helper)
301 {
302 	if (!helper->ips && helper->unk) {
303 		return IUnknown_QueryInterface(helper->unk, &IID_IPersistStream, &helper->ips);
304 	}
305 	return helper->ips ? S_OK : E_NOTIMPL;
306 }
307 
get_persist_stream_init(php_com_persist_helper * helper)308 static inline HRESULT get_persist_stream_init(php_com_persist_helper *helper)
309 {
310 	if (!helper->ipsi && helper->unk) {
311 		return IUnknown_QueryInterface(helper->unk, &IID_IPersistStreamInit, &helper->ipsi);
312 	}
313 	return helper->ipsi ? S_OK : E_NOTIMPL;
314 }
315 
get_persist_file(php_com_persist_helper * helper)316 static inline HRESULT get_persist_file(php_com_persist_helper *helper)
317 {
318 	if (!helper->ipf && helper->unk) {
319 		return IUnknown_QueryInterface(helper->unk, &IID_IPersistFile, &helper->ipf);
320 	}
321 	return helper->ipf ? S_OK : E_NOTIMPL;
322 }
323 
324 
325 /* {{{ proto string COMPersistHelper::GetCurFile()
326    Determines the filename into which an object will be saved, or false if none is set, via IPersistFile::GetCurFile */
CPH_METHOD(GetCurFileName)327 CPH_METHOD(GetCurFileName)
328 {
329 	HRESULT res;
330 	OLECHAR *olename = NULL;
331 	CPH_FETCH();
332 
333 	CPH_NO_OBJ();
334 
335 	res = get_persist_file(helper);
336 	if (helper->ipf) {
337 		res = IPersistFile_GetCurFile(helper->ipf, &olename);
338 
339 		if (res == S_OK) {
340 			size_t len;
341 			char *str = php_com_olestring_to_string(olename,
342 				   &len, helper->codepage);
343 			RETVAL_STRINGL(str, len);
344 			// TODO: avoid reallocarion???
345 			efree(str);
346 			CoTaskMemFree(olename);
347 			return;
348 		} else if (res == S_FALSE) {
349 			CoTaskMemFree(olename);
350 			RETURN_FALSE;
351 		}
352 		php_com_throw_exception(res, NULL);
353 	} else {
354 		php_com_throw_exception(res, NULL);
355 	}
356 }
357 /* }}} */
358 
359 
360 /* {{{ proto bool COMPersistHelper::SaveToFile(string filename [, bool remember])
361    Persist object data to file, via IPersistFile::Save */
CPH_METHOD(SaveToFile)362 CPH_METHOD(SaveToFile)
363 {
364 	HRESULT res;
365 	char *filename, *fullpath = NULL;
366 	size_t filename_len;
367 	zend_bool remember = TRUE;
368 	OLECHAR *olefilename = NULL;
369 	CPH_FETCH();
370 
371 	CPH_NO_OBJ();
372 
373 	res = get_persist_file(helper);
374 	if (helper->ipf) {
375 		if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "p!|b",
376 					&filename, &filename_len, &remember)) {
377 			php_com_throw_exception(E_INVALIDARG, "Invalid arguments");
378 			return;
379 		}
380 
381 		if (filename) {
382 			fullpath = expand_filepath(filename, NULL);
383 			if (!fullpath) {
384 				RETURN_FALSE;
385 			}
386 
387 			if (php_check_open_basedir(fullpath)) {
388 				efree(fullpath);
389 				RETURN_FALSE;
390 			}
391 
392 			olefilename = php_com_string_to_olestring(fullpath, strlen(fullpath), helper->codepage);
393 			efree(fullpath);
394 		}
395 		res = IPersistFile_Save(helper->ipf, olefilename, remember);
396 		if (SUCCEEDED(res)) {
397 			if (!olefilename) {
398 				res = IPersistFile_GetCurFile(helper->ipf, &olefilename);
399 				if (S_OK == res) {
400 					IPersistFile_SaveCompleted(helper->ipf, olefilename);
401 					CoTaskMemFree(olefilename);
402 					olefilename = NULL;
403 				}
404 			} else if (remember) {
405 				IPersistFile_SaveCompleted(helper->ipf, olefilename);
406 			}
407 		}
408 
409 		if (olefilename) {
410 			efree(olefilename);
411 		}
412 
413 		if (FAILED(res)) {
414 			php_com_throw_exception(res, NULL);
415 		}
416 
417 	} else {
418 		php_com_throw_exception(res, NULL);
419 	}
420 }
421 /* }}} */
422 
423 /* {{{ proto bool COMPersistHelper::LoadFromFile(string filename [, int flags])
424    Load object data from file, via IPersistFile::Load */
CPH_METHOD(LoadFromFile)425 CPH_METHOD(LoadFromFile)
426 {
427 	HRESULT res;
428 	char *filename, *fullpath;
429 	size_t filename_len;
430 	zend_long flags = 0;
431 	OLECHAR *olefilename;
432 	CPH_FETCH();
433 
434 	CPH_NO_OBJ();
435 
436 	res = get_persist_file(helper);
437 	if (helper->ipf) {
438 
439 		if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "p|l",
440 					&filename, &filename_len, &flags)) {
441 			php_com_throw_exception(E_INVALIDARG, "Invalid arguments");
442 			return;
443 		}
444 
445 		if (!(fullpath = expand_filepath(filename, NULL))) {
446 			RETURN_FALSE;
447 		}
448 
449 		if (php_check_open_basedir(fullpath)) {
450 			efree(fullpath);
451 			RETURN_FALSE;
452 		}
453 
454 		olefilename = php_com_string_to_olestring(fullpath, strlen(fullpath), helper->codepage);
455 		efree(fullpath);
456 
457 		res = IPersistFile_Load(helper->ipf, olefilename, (DWORD)flags);
458 		efree(olefilename);
459 
460 		if (FAILED(res)) {
461 			php_com_throw_exception(res, NULL);
462 		}
463 
464 	} else {
465 		php_com_throw_exception(res, NULL);
466 	}
467 }
468 /* }}} */
469 
470 /* {{{ proto int COMPersistHelper::GetMaxStreamSize()
471    Gets maximum stream size required to store the object data, via IPersistStream::GetSizeMax (or IPersistStreamInit::GetSizeMax) */
CPH_METHOD(GetMaxStreamSize)472 CPH_METHOD(GetMaxStreamSize)
473 {
474 	HRESULT res;
475 	ULARGE_INTEGER size;
476 	CPH_FETCH();
477 
478 	CPH_NO_OBJ();
479 
480 	res = get_persist_stream_init(helper);
481 	if (helper->ipsi) {
482 		res = IPersistStreamInit_GetSizeMax(helper->ipsi, &size);
483 	} else {
484 		res = get_persist_stream(helper);
485 		if (helper->ips) {
486 			res = IPersistStream_GetSizeMax(helper->ips, &size);
487 		} else {
488 			php_com_throw_exception(res, NULL);
489 			return;
490 		}
491 	}
492 
493 	if (res != S_OK) {
494 		php_com_throw_exception(res, NULL);
495 	} else {
496 		/* TODO: handle 64 bit properly */
497 		RETURN_LONG((zend_long)size.QuadPart);
498 	}
499 }
500 /* }}} */
501 
502 /* {{{ proto int COMPersistHelper::InitNew()
503    Initializes the object to a default state, via IPersistStreamInit::InitNew */
CPH_METHOD(InitNew)504 CPH_METHOD(InitNew)
505 {
506 	HRESULT res;
507 	CPH_FETCH();
508 
509 	CPH_NO_OBJ();
510 
511 	res = get_persist_stream_init(helper);
512 	if (helper->ipsi) {
513 		res = IPersistStreamInit_InitNew(helper->ipsi);
514 
515 		if (res != S_OK) {
516 			php_com_throw_exception(res, NULL);
517 		} else {
518 			RETURN_TRUE;
519 		}
520 	} else {
521 		php_com_throw_exception(res, NULL);
522 	}
523 }
524 /* }}} */
525 
526 /* {{{ proto mixed COMPersistHelper::LoadFromStream(resource stream)
527    Initializes an object from the stream where it was previously saved, via IPersistStream::Load or OleLoadFromStream */
CPH_METHOD(LoadFromStream)528 CPH_METHOD(LoadFromStream)
529 {
530 	zval *zstm;
531 	php_stream *stream;
532 	IStream *stm = NULL;
533 	HRESULT res;
534 	CPH_FETCH();
535 
536 	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zstm)) {
537 		php_com_throw_exception(E_INVALIDARG, "invalid arguments");
538 		return;
539 	}
540 
541 	php_stream_from_zval_no_verify(stream, zstm);
542 
543 	if (stream == NULL) {
544 		php_com_throw_exception(E_INVALIDARG, "expected a stream");
545 		return;
546 	}
547 
548 	stm = php_com_wrapper_export_stream(stream);
549 	if (stm == NULL) {
550 		php_com_throw_exception(E_UNEXPECTED, "failed to wrap stream");
551 		return;
552 	}
553 
554 	res = S_OK;
555 	RETVAL_TRUE;
556 
557 	if (helper->unk == NULL) {
558 		IDispatch *disp = NULL;
559 
560 		/* we need to create an object and load using OleLoadFromStream */
561 		res = OleLoadFromStream(stm, &IID_IDispatch, &disp);
562 
563 		if (SUCCEEDED(res)) {
564 			php_com_wrap_dispatch(return_value, disp, COMG(code_page));
565 		}
566 	} else {
567 		res = get_persist_stream_init(helper);
568 		if (helper->ipsi) {
569 			res = IPersistStreamInit_Load(helper->ipsi, stm);
570 		} else {
571 			res = get_persist_stream(helper);
572 			if (helper->ips) {
573 				res = IPersistStreamInit_Load(helper->ipsi, stm);
574 			}
575 		}
576 	}
577 	IStream_Release(stm);
578 
579 	if (FAILED(res)) {
580 		php_com_throw_exception(res, NULL);
581 		RETURN_NULL();
582 	}
583 }
584 /* }}} */
585 
586 /* {{{ proto int COMPersistHelper::SaveToStream(resource stream)
587    Saves the object to a stream, via IPersistStream::Save */
CPH_METHOD(SaveToStream)588 CPH_METHOD(SaveToStream)
589 {
590 	zval *zstm;
591 	php_stream *stream;
592 	IStream *stm = NULL;
593 	HRESULT res;
594 	CPH_FETCH();
595 
596 	CPH_NO_OBJ();
597 
598 	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zstm)) {
599 		php_com_throw_exception(E_INVALIDARG, "invalid arguments");
600 		return;
601 	}
602 
603 	php_stream_from_zval_no_verify(stream, zstm);
604 
605 	if (stream == NULL) {
606 		php_com_throw_exception(E_INVALIDARG, "expected a stream");
607 		return;
608 	}
609 
610 	stm = php_com_wrapper_export_stream(stream);
611 	if (stm == NULL) {
612 		php_com_throw_exception(E_UNEXPECTED, "failed to wrap stream");
613 		return;
614 	}
615 
616 	res = get_persist_stream_init(helper);
617 	if (helper->ipsi) {
618 		res = IPersistStreamInit_Save(helper->ipsi, stm, TRUE);
619 	} else {
620 		res = get_persist_stream(helper);
621 		if (helper->ips) {
622 			res = IPersistStream_Save(helper->ips, stm, TRUE);
623 		}
624 	}
625 
626 	IStream_Release(stm);
627 
628 	if (FAILED(res)) {
629 		php_com_throw_exception(res, NULL);
630 		return;
631 	}
632 
633 	RETURN_TRUE;
634 }
635 /* }}} */
636 
637 /* {{{ proto COMPersistHelper::__construct([object com_object])
638    Creates a persistence helper object, usually associated with a com_object */
CPH_METHOD(__construct)639 CPH_METHOD(__construct)
640 {
641 	php_com_dotnet_object *obj = NULL;
642 	zval *zobj = NULL;
643 	CPH_FETCH();
644 
645 	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "|O!",
646 				&zobj, php_com_variant_class_entry)) {
647 		php_com_throw_exception(E_INVALIDARG, "invalid arguments");
648 		return;
649 	}
650 
651 	if (!zobj) {
652 		return;
653 	}
654 
655 	obj = CDNO_FETCH(zobj);
656 
657 	if (V_VT(&obj->v) != VT_DISPATCH || V_DISPATCH(&obj->v) == NULL) {
658 		php_com_throw_exception(E_INVALIDARG, "parameter must represent an IDispatch COM object");
659 		return;
660 	}
661 
662 	/* it is always safe to cast an interface to IUnknown */
663 	helper->unk = (IUnknown*)V_DISPATCH(&obj->v);
664 	IUnknown_AddRef(helper->unk);
665 	helper->codepage = obj->code_page;
666 }
667 /* }}} */
668 
669 
670 
671 
672 static const zend_function_entry com_persist_helper_methods[] = {
673 	CPH_ME(__construct, NULL)
674 	CPH_ME(GetCurFileName, NULL)
675 	CPH_ME(SaveToFile, NULL)
676 	CPH_ME(LoadFromFile, NULL)
677 	CPH_ME(GetMaxStreamSize, NULL)
678 	CPH_ME(InitNew, NULL)
679 	CPH_ME(LoadFromStream, NULL)
680 	CPH_ME(SaveToStream, NULL)
681 	PHP_FE_END
682 };
683 
helper_free_storage(zend_object * obj)684 static void helper_free_storage(zend_object *obj)
685 {
686 	php_com_persist_helper *object = (php_com_persist_helper*)obj;
687 
688 	if (object->ipf) {
689 		IPersistFile_Release(object->ipf);
690 	}
691 	if (object->ips) {
692 		IPersistStream_Release(object->ips);
693 	}
694 	if (object->ipsi) {
695 		IPersistStreamInit_Release(object->ipsi);
696 	}
697 	if (object->unk) {
698 		IUnknown_Release(object->unk);
699 	}
700 	zend_object_std_dtor(&object->std);
701 }
702 
703 
helper_clone(zval * obj)704 static zend_object* helper_clone(zval *obj)
705 {
706 	php_com_persist_helper *clone, *object = (php_com_persist_helper*)Z_OBJ_P(obj);
707 
708 	clone = emalloc(sizeof(*object));
709 	memcpy(clone, object, sizeof(*object));
710 
711 	zend_object_std_init(&clone->std, object->std.ce);
712 
713 	if (clone->ipf) {
714 		IPersistFile_AddRef(clone->ipf);
715 	}
716 	if (clone->ips) {
717 		IPersistStream_AddRef(clone->ips);
718 	}
719 	if (clone->ipsi) {
720 		IPersistStreamInit_AddRef(clone->ipsi);
721 	}
722 	if (clone->unk) {
723 		IUnknown_AddRef(clone->unk);
724 	}
725 	return (zend_object*)clone;
726 }
727 
helper_new(zend_class_entry * ce)728 static zend_object* helper_new(zend_class_entry *ce)
729 {
730 	php_com_persist_helper *helper;
731 
732 	helper = emalloc(sizeof(*helper));
733 	memset(helper, 0, sizeof(*helper));
734 
735 	zend_object_std_init(&helper->std, helper_ce);
736 	helper->std.handlers = &helper_handlers;
737 
738 	return &helper->std;
739 }
740 
php_com_persist_minit(INIT_FUNC_ARGS)741 int php_com_persist_minit(INIT_FUNC_ARGS)
742 {
743 	zend_class_entry ce;
744 
745 	memcpy(&helper_handlers, &std_object_handlers, sizeof(helper_handlers));
746 	helper_handlers.free_obj = helper_free_storage;
747 	helper_handlers.clone_obj = helper_clone;
748 
749 	INIT_CLASS_ENTRY(ce, "COMPersistHelper", com_persist_helper_methods);
750 	ce.create_object = helper_new;
751 	helper_ce = zend_register_internal_class(&ce);
752 	helper_ce->ce_flags |= ZEND_ACC_FINAL;
753 
754 	le_istream = zend_register_list_destructors_ex(istream_dtor,
755 			NULL, "com_dotnet_istream_wrapper", module_number);
756 
757 	return SUCCESS;
758 }
759