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