xref: /php-src/ext/com_dotnet/com_persist.c (revision 700fbca5)
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: 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(ZEND_THIS);
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 			zend_string *str = php_com_olestring_to_string(olename, helper->codepage);
341 			CoTaskMemFree(olename);
342 			RETURN_STR(str);
343 		} else if (res == S_FALSE) {
344 			CoTaskMemFree(olename);
345 			RETURN_FALSE;
346 		}
347 		php_com_throw_exception(res, NULL);
348 	} else {
349 		php_com_throw_exception(res, NULL);
350 	}
351 }
352 /* }}} */
353 
354 
355 /* {{{ Persist object data to file, via IPersistFile::Save */
CPH_METHOD(SaveToFile)356 CPH_METHOD(SaveToFile)
357 {
358 	HRESULT res;
359 	char *filename, *fullpath = NULL;
360 	size_t filename_len;
361 	bool remember = TRUE;
362 	OLECHAR *olefilename = NULL;
363 	CPH_FETCH();
364 
365 	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "p!|b",
366 				&filename, &filename_len, &remember)) {
367 		RETURN_THROWS();
368 	}
369 
370 	CPH_NO_OBJ();
371 
372 	res = get_persist_file(helper);
373 	if (helper->ipf) {
374 		if (filename) {
375 			fullpath = expand_filepath(filename, NULL);
376 			if (!fullpath) {
377 				RETURN_FALSE;
378 			}
379 
380 			if (php_check_open_basedir(fullpath)) {
381 				efree(fullpath);
382 				RETURN_FALSE;
383 			}
384 
385 			olefilename = php_com_string_to_olestring(fullpath, strlen(fullpath), helper->codepage);
386 			efree(fullpath);
387 		}
388 		res = IPersistFile_Save(helper->ipf, olefilename, remember);
389 		if (SUCCEEDED(res)) {
390 			if (!olefilename) {
391 				res = IPersistFile_GetCurFile(helper->ipf, &olefilename);
392 				if (S_OK == res) {
393 					IPersistFile_SaveCompleted(helper->ipf, olefilename);
394 					CoTaskMemFree(olefilename);
395 					olefilename = NULL;
396 				}
397 			} else if (remember) {
398 				IPersistFile_SaveCompleted(helper->ipf, olefilename);
399 			}
400 		}
401 
402 		if (olefilename) {
403 			efree(olefilename);
404 		}
405 
406 		if (FAILED(res)) {
407 			php_com_throw_exception(res, NULL);
408 		}
409 
410 	} else {
411 		php_com_throw_exception(res, NULL);
412 	}
413 }
414 /* }}} */
415 
416 /* {{{ Load object data from file, via IPersistFile::Load */
CPH_METHOD(LoadFromFile)417 CPH_METHOD(LoadFromFile)
418 {
419 	HRESULT res;
420 	char *filename, *fullpath;
421 	size_t filename_len;
422 	zend_long flags = 0;
423 	OLECHAR *olefilename;
424 	CPH_FETCH();
425 
426 	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "p|l",
427 				&filename, &filename_len, &flags)) {
428 		RETURN_THROWS();
429 	}
430 
431 	CPH_NO_OBJ();
432 
433 	res = get_persist_file(helper);
434 	if (helper->ipf) {
435 		if (!(fullpath = expand_filepath(filename, NULL))) {
436 			RETURN_FALSE;
437 		}
438 
439 		if (php_check_open_basedir(fullpath)) {
440 			efree(fullpath);
441 			RETURN_FALSE;
442 		}
443 
444 		olefilename = php_com_string_to_olestring(fullpath, strlen(fullpath), helper->codepage);
445 		efree(fullpath);
446 
447 		res = IPersistFile_Load(helper->ipf, olefilename, (DWORD)flags);
448 		efree(olefilename);
449 
450 		if (FAILED(res)) {
451 			php_com_throw_exception(res, NULL);
452 		}
453 
454 	} else {
455 		php_com_throw_exception(res, NULL);
456 	}
457 }
458 /* }}} */
459 
460 /* {{{ Gets maximum stream size required to store the object data, via IPersistStream::GetSizeMax (or IPersistStreamInit::GetSizeMax) */
CPH_METHOD(GetMaxStreamSize)461 CPH_METHOD(GetMaxStreamSize)
462 {
463 	HRESULT res;
464 	ULARGE_INTEGER size;
465 	CPH_FETCH();
466 
467 	if (zend_parse_parameters_none() == FAILURE) {
468 		RETURN_THROWS();
469 	}
470 
471 	CPH_NO_OBJ();
472 
473 	res = get_persist_stream_init(helper);
474 	if (helper->ipsi) {
475 		res = IPersistStreamInit_GetSizeMax(helper->ipsi, &size);
476 	} else {
477 		res = get_persist_stream(helper);
478 		if (helper->ips) {
479 			res = IPersistStream_GetSizeMax(helper->ips, &size);
480 		} else {
481 			php_com_throw_exception(res, NULL);
482 			RETURN_THROWS();
483 		}
484 	}
485 
486 	if (res != S_OK) {
487 		php_com_throw_exception(res, NULL);
488 	} else {
489 		/* TODO: handle 64 bit properly */
490 		RETURN_LONG((zend_long)size.QuadPart);
491 	}
492 }
493 /* }}} */
494 
495 /* {{{ Initializes the object to a default state, via IPersistStreamInit::InitNew */
CPH_METHOD(InitNew)496 CPH_METHOD(InitNew)
497 {
498 	HRESULT res;
499 	CPH_FETCH();
500 
501 	if (zend_parse_parameters_none() == FAILURE) {
502 		RETURN_THROWS();
503 	}
504 
505 	CPH_NO_OBJ();
506 
507 	res = get_persist_stream_init(helper);
508 	if (helper->ipsi) {
509 		res = IPersistStreamInit_InitNew(helper->ipsi);
510 
511 		if (res != S_OK) {
512 			php_com_throw_exception(res, NULL);
513 		} else {
514 			RETURN_TRUE;
515 		}
516 	} else {
517 		php_com_throw_exception(res, NULL);
518 	}
519 }
520 /* }}} */
521 
522 /* {{{ Initializes an object from the stream where it was previously saved, via IPersistStream::Load or OleLoadFromStream */
CPH_METHOD(LoadFromStream)523 CPH_METHOD(LoadFromStream)
524 {
525 	zval *zstm;
526 	php_stream *stream;
527 	IStream *stm = NULL;
528 	HRESULT res;
529 	CPH_FETCH();
530 
531 	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zstm)) {
532 		RETURN_THROWS();
533 	}
534 
535 	php_stream_from_zval_no_verify(stream, zstm);
536 
537 	if (stream == NULL) {
538 		php_com_throw_exception(E_INVALIDARG, "expected a stream");
539 		RETURN_THROWS();
540 	}
541 
542 	stm = php_com_wrapper_export_stream(stream);
543 	if (stm == NULL) {
544 		php_com_throw_exception(E_UNEXPECTED, "failed to wrap stream");
545 		RETURN_THROWS();
546 	}
547 
548 	res = S_OK;
549 	RETVAL_TRUE;
550 
551 	if (helper->unk == NULL) {
552 		IDispatch *disp = NULL;
553 
554 		/* we need to create an object and load using OleLoadFromStream */
555 		res = OleLoadFromStream(stm, &IID_IDispatch, &disp);
556 
557 		if (SUCCEEDED(res)) {
558 			php_com_wrap_dispatch(return_value, disp, COMG(code_page));
559 		}
560 	} else {
561 		res = get_persist_stream_init(helper);
562 		if (helper->ipsi) {
563 			res = IPersistStreamInit_Load(helper->ipsi, stm);
564 		} else {
565 			res = get_persist_stream(helper);
566 			if (helper->ips) {
567 				res = IPersistStreamInit_Load(helper->ipsi, stm);
568 			}
569 		}
570 	}
571 	IStream_Release(stm);
572 
573 	if (FAILED(res)) {
574 		php_com_throw_exception(res, NULL);
575 		RETURN_THROWS();
576 	}
577 }
578 /* }}} */
579 
580 /* {{{ Saves the object to a stream, via IPersistStream::Save */
CPH_METHOD(SaveToStream)581 CPH_METHOD(SaveToStream)
582 {
583 	zval *zstm;
584 	php_stream *stream;
585 	IStream *stm = NULL;
586 	HRESULT res;
587 	CPH_FETCH();
588 
589 	CPH_NO_OBJ();
590 
591 	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zstm)) {
592 		RETURN_THROWS();
593 	}
594 
595 	php_stream_from_zval_no_verify(stream, zstm);
596 
597 	if (stream == NULL) {
598 		php_com_throw_exception(E_INVALIDARG, "expected a stream");
599 		RETURN_THROWS();
600 	}
601 
602 	stm = php_com_wrapper_export_stream(stream);
603 	if (stm == NULL) {
604 		php_com_throw_exception(E_UNEXPECTED, "failed to wrap stream");
605 		RETURN_THROWS();
606 	}
607 
608 	res = get_persist_stream_init(helper);
609 	if (helper->ipsi) {
610 		res = IPersistStreamInit_Save(helper->ipsi, stm, TRUE);
611 	} else {
612 		res = get_persist_stream(helper);
613 		if (helper->ips) {
614 			res = IPersistStream_Save(helper->ips, stm, TRUE);
615 		}
616 	}
617 
618 	IStream_Release(stm);
619 
620 	if (FAILED(res)) {
621 		php_com_throw_exception(res, NULL);
622 		RETURN_THROWS();
623 	}
624 
625 	RETURN_TRUE;
626 }
627 /* }}} */
628 
629 /* {{{ Creates a persistence helper object, usually associated with a com_object */
CPH_METHOD(__construct)630 CPH_METHOD(__construct)
631 {
632 	php_com_dotnet_object *obj = NULL;
633 	zval *zobj = NULL;
634 	CPH_FETCH();
635 
636 	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "|O!",
637 				&zobj, php_com_variant_class_entry)) {
638 		RETURN_THROWS();
639 	}
640 
641 	if (!zobj) {
642 		return;
643 	}
644 
645 	obj = CDNO_FETCH(zobj);
646 
647 	if (V_VT(&obj->v) != VT_DISPATCH || V_DISPATCH(&obj->v) == NULL) {
648 		php_com_throw_exception(E_INVALIDARG, "parameter must represent an IDispatch COM object");
649 		RETURN_THROWS();
650 	}
651 
652 	/* it is always safe to cast an interface to IUnknown */
653 	helper->unk = (IUnknown*)V_DISPATCH(&obj->v);
654 	IUnknown_AddRef(helper->unk);
655 	helper->codepage = obj->code_page;
656 }
657 /* }}} */
658 
659 
helper_free_storage(zend_object * obj)660 static void helper_free_storage(zend_object *obj)
661 {
662 	php_com_persist_helper *object = (php_com_persist_helper*)obj;
663 
664 	if (object->ipf) {
665 		IPersistFile_Release(object->ipf);
666 	}
667 	if (object->ips) {
668 		IPersistStream_Release(object->ips);
669 	}
670 	if (object->ipsi) {
671 		IPersistStreamInit_Release(object->ipsi);
672 	}
673 	if (object->unk) {
674 		IUnknown_Release(object->unk);
675 	}
676 	zend_object_std_dtor(&object->std);
677 }
678 
679 
helper_clone(zend_object * obj)680 static zend_object* helper_clone(zend_object *obj)
681 {
682 	php_com_persist_helper *clone, *object = (php_com_persist_helper*) obj;
683 
684 	clone = emalloc(sizeof(*object));
685 	memcpy(clone, object, sizeof(*object));
686 
687 	zend_object_std_init(&clone->std, object->std.ce);
688 
689 	if (clone->ipf) {
690 		IPersistFile_AddRef(clone->ipf);
691 	}
692 	if (clone->ips) {
693 		IPersistStream_AddRef(clone->ips);
694 	}
695 	if (clone->ipsi) {
696 		IPersistStreamInit_AddRef(clone->ipsi);
697 	}
698 	if (clone->unk) {
699 		IUnknown_AddRef(clone->unk);
700 	}
701 	return (zend_object*)clone;
702 }
703 
helper_new(zend_class_entry * ce)704 static zend_object* helper_new(zend_class_entry *ce)
705 {
706 	php_com_persist_helper *helper;
707 
708 	helper = emalloc(sizeof(*helper));
709 	memset(helper, 0, sizeof(*helper));
710 
711 	zend_object_std_init(&helper->std, helper_ce);
712 
713 	return &helper->std;
714 }
715 
php_com_persist_minit(INIT_FUNC_ARGS)716 void php_com_persist_minit(INIT_FUNC_ARGS)
717 {
718 	memcpy(&helper_handlers, &std_object_handlers, sizeof(helper_handlers));
719 	helper_handlers.free_obj = helper_free_storage;
720 	helper_handlers.clone_obj = helper_clone;
721 
722 	helper_ce = register_class_COMPersistHelper();
723 	helper_ce->create_object = helper_new;
724 	helper_ce->default_object_handlers = &helper_handlers;
725 
726 	le_istream = zend_register_list_destructors_ex(istream_dtor,
727 			NULL, "com_dotnet_istream_wrapper", module_number);
728 }
729