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