xref: /php-src/ext/com_dotnet/com_persist.c (revision e1c8329b)
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"
33 #include "php_com_dotnet_internal.h"
34 #include "Zend/zend_exceptions.h"
35 #include "com_persist_arginfo.h"
36 
37 /* {{{ expose php_stream as a COM IStream */
38 
39 typedef struct {
40 	CONST_VTBL struct IStreamVtbl *lpVtbl;
41 	DWORD engine_thread;
42 	LONG refcount;
43 	php_stream *stream;
44 } php_istream;
45 
46 static 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 
stm_queryinterface(IStream * This,REFIID riid,void ** ppvObject)58 static HRESULT STDMETHODCALLTYPE stm_queryinterface(
59 	IStream *This,
60 	/* [in] */ REFIID riid,
61 	/* [iid_is][out] */ void **ppvObject)
62 {
63 	FETCH_STM_EX();
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 
stm_addref(IStream * This)76 static ULONG STDMETHODCALLTYPE stm_addref(IStream *This)
77 {
78 	FETCH_STM_EX();
79 
80 	return InterlockedIncrement(&stm->refcount);
81 }
82 
stm_release(IStream * This)83 static 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 
stm_read(IStream * This,void * pv,ULONG cb,ULONG * pcbRead)96 static 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 
stm_write(IStream * This,void const * pv,ULONG cb,ULONG * pcbWritten)112 static 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 
stm_seek(IStream * This,LARGE_INTEGER dlibMove,DWORD dwOrigin,ULARGE_INTEGER * plibNewPosition)128 static 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 
stm_set_size(IStream * This,ULARGE_INTEGER libNewSize)160 static 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 
stm_copy_to(IStream * This,IStream * pstm,ULARGE_INTEGER cb,ULARGE_INTEGER * pcbRead,ULARGE_INTEGER * pcbWritten)179 static 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 
stm_commit(IStream * This,DWORD grfCommitFlags)187 static 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 
stm_revert(IStream * This)196 static HRESULT STDMETHODCALLTYPE stm_revert(IStream *This)
197 {
198 	/* NOP */
199 	return S_OK;
200 }
201 
stm_lock_region(IStream * This,ULARGE_INTEGER libOffset,ULARGE_INTEGER cb,DWORD lockType)202 static HRESULT STDMETHODCALLTYPE stm_lock_region(IStream *This,
203    	ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD lockType)
204 {
205 	return STG_E_INVALIDFUNCTION;
206 }
207 
stm_unlock_region(IStream * This,ULARGE_INTEGER libOffset,ULARGE_INTEGER cb,DWORD lockType)208 static HRESULT STDMETHODCALLTYPE stm_unlock_region(IStream *This,
209 		ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD lockType)
210 {
211 	return STG_E_INVALIDFUNCTION;
212 }
213 
stm_stat(IStream * This,STATSTG * pstatstg,DWORD grfStatFlag)214 static HRESULT STDMETHODCALLTYPE stm_stat(IStream *This,
215 		STATSTG *pstatstg, DWORD grfStatFlag)
216 {
217 	return STG_E_INVALIDFUNCTION;
218 }
219 
stm_clone(IStream * This,IStream ** ppstm)220 static HRESULT STDMETHODCALLTYPE stm_clone(IStream *This, IStream **ppstm)
221 {
222 	return STG_E_INVALIDFUNCTION;
223 }
224 
225 static 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 
istream_destructor(php_istream * stm)242 static void istream_destructor(php_istream *stm)
243 {
244 	if (stm->refcount > 0) {
245 		CoDisconnectObject((IUnknown*)stm, 0);
246 	}
247 
248 	zend_list_delete(stm->stream->res);
249 
250 	CoTaskMemFree(stm);
251 }
252 /* }}} */
253 
php_com_wrapper_export_stream(php_stream * stream)254 PHP_COM_DOTNET_API IStream *php_com_wrapper_export_stream(php_stream *stream)
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 
278 typedef struct {
279 	zend_object			std;
280 	long codepage;
281 	IUnknown 			*unk;
282 	IPersistStream 		*ips;
283 	IPersistStreamInit	*ipsi;
284 	IPersistFile		*ipf;
285 } php_com_persist_helper;
286 
287 static zend_object_handlers helper_handlers;
288 static zend_class_entry *helper_ce;
289 
get_persist_stream(php_com_persist_helper * helper)290 static 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 
get_persist_stream_init(php_com_persist_helper * helper)298 static 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 
get_persist_file(php_com_persist_helper * helper)306 static 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 */
CPH_METHOD(GetCurFileName)316 CPH_METHOD(GetCurFileName)
317 {
318 	HRESULT res;
319 	OLECHAR *olename = NULL;
320 	CPH_FETCH();
321 
322 	if (zend_parse_parameters_none() == FAILURE) {
323 		RETURN_THROWS();
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);
338 			RETURN_FALSE;
339 		}
340 		php_com_throw_exception(res, NULL);
341 	} else {
342 		php_com_throw_exception(res, NULL);
343 	}
344 }
345 /* }}} */
346 
347 
348 /* {{{ Persist object data to file, via IPersistFile::Save */
CPH_METHOD(SaveToFile)349 CPH_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 
358 	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "p!|b",
359 				&filename, &filename_len, &remember)) {
360 		RETURN_THROWS();
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) {
370 				RETURN_FALSE;
371 			}
372 
373 			if (php_check_open_basedir(fullpath)) {
374 				efree(fullpath);
375 				RETURN_FALSE;
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)) {
400 			php_com_throw_exception(res, NULL);
401 		}
402 
403 	} else {
404 		php_com_throw_exception(res, NULL);
405 	}
406 }
407 /* }}} */
408 
409 /* {{{ Load object data from file, via IPersistFile::Load */
CPH_METHOD(LoadFromFile)410 CPH_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 
419 	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "p|l",
420 				&filename, &filename_len, &flags)) {
421 		RETURN_THROWS();
422 	}
423 
424 	CPH_NO_OBJ();
425 
426 	res = get_persist_file(helper);
427 	if (helper->ipf) {
428 		if (!(fullpath = expand_filepath(filename, NULL))) {
429 			RETURN_FALSE;
430 		}
431 
432 		if (php_check_open_basedir(fullpath)) {
433 			efree(fullpath);
434 			RETURN_FALSE;
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)) {
444 			php_com_throw_exception(res, NULL);
445 		}
446 
447 	} else {
448 		php_com_throw_exception(res, NULL);
449 	}
450 }
451 /* }}} */
452 
453 /* {{{ Gets maximum stream size required to store the object data, via IPersistStream::GetSizeMax (or IPersistStreamInit::GetSizeMax) */
CPH_METHOD(GetMaxStreamSize)454 CPH_METHOD(GetMaxStreamSize)
455 {
456 	HRESULT res;
457 	ULARGE_INTEGER size;
458 	CPH_FETCH();
459 
460 	if (zend_parse_parameters_none() == FAILURE) {
461 		RETURN_THROWS();
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 {
474 			php_com_throw_exception(res, NULL);
475 			RETURN_THROWS();
476 		}
477 	}
478 
479 	if (res != S_OK) {
480 		php_com_throw_exception(res, NULL);
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 */
CPH_METHOD(InitNew)489 CPH_METHOD(InitNew)
490 {
491 	HRESULT res;
492 	CPH_FETCH();
493 
494 	if (zend_parse_parameters_none() == FAILURE) {
495 		RETURN_THROWS();
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) {
505 			php_com_throw_exception(res, NULL);
506 		} else {
507 			RETURN_TRUE;
508 		}
509 	} else {
510 		php_com_throw_exception(res, NULL);
511 	}
512 }
513 /* }}} */
514 
515 /* {{{ Initializes an object from the stream where it was previously saved, via IPersistStream::Load or OleLoadFromStream */
CPH_METHOD(LoadFromStream)516 CPH_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)) {
525 		RETURN_THROWS();
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");
532 		RETURN_THROWS();
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");
538 		RETURN_THROWS();
539 	}
540 
541 	res = S_OK;
542 	RETVAL_TRUE;
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)) {
551 			php_com_wrap_dispatch(return_value, disp, COMG(code_page));
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)) {
567 		php_com_throw_exception(res, NULL);
568 		RETURN_THROWS();
569 	}
570 }
571 /* }}} */
572 
573 /* {{{ Saves the object to a stream, via IPersistStream::Save */
CPH_METHOD(SaveToStream)574 CPH_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)) {
585 		RETURN_THROWS();
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");
592 		RETURN_THROWS();
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");
598 		RETURN_THROWS();
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)) {
614 		php_com_throw_exception(res, NULL);
615 		RETURN_THROWS();
616 	}
617 
618 	RETURN_TRUE;
619 }
620 /* }}} */
621 
622 /* {{{ Creates a persistence helper object, usually associated with a com_object */
CPH_METHOD(__construct)623 CPH_METHOD(__construct)
624 {
625 	php_com_dotnet_object *obj = NULL;
626 	zval *zobj = NULL;
627 	CPH_FETCH();
628 
629 	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "|O!",
630 				&zobj, php_com_variant_class_entry)) {
631 		RETURN_THROWS();
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");
642 		RETURN_THROWS();
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 
helper_free_storage(zend_object * obj)653 static void helper_free_storage(zend_object *obj)
654 {
655 	php_com_persist_helper *object = (php_com_persist_helper*)obj;
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 	}
669 	zend_object_std_dtor(&object->std);
670 }
671 
672 
helper_clone(zend_object * obj)673 static 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 
helper_new(zend_class_entry * ce)697 static zend_object* helper_new(zend_class_entry *ce)
698 {
699 	php_com_persist_helper *helper;
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 
php_com_persist_minit(INIT_FUNC_ARGS)709 void php_com_persist_minit(INIT_FUNC_ARGS)
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 }
719