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