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