php-src/ext/com_dotnet/com_persist.c
Peter Kokot 8d3f8ca12a Remove unused Git attributes ident
The $Id$ keywords were used in Subversion where they can be substituted
with filename, last revision number change, last changed date, and last
user who changed it.

In Git this functionality is different and can be done with Git attribute
ident. These need to be defined manually for each file in the
.gitattributes file and are afterwards replaced with 40-character
hexadecimal blob object name which is based only on the particular file
contents.

This patch simplifies handling of $Id$ keywords by removing them since
they are not used anymore.
2018-07-25 00:53:25 +02:00

777 lines
18 KiB
C

/*
+----------------------------------------------------------------------+
| PHP Version 7 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2018 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: Wez Furlong <wez@thebrainroom.com> |
+----------------------------------------------------------------------+
*/
/* Infrastructure for working with persistent COM objects.
* Implements: IStream* wrapper for PHP streams.
* TODO: Magic __wakeup and __sleep handlers for serialization
* (can wait till 5.1) */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_com_dotnet.h"
#include "php_com_dotnet_internal.h"
#include "Zend/zend_exceptions.h"
/* {{{ expose php_stream as a COM IStream */
typedef struct {
CONST_VTBL struct IStreamVtbl *lpVtbl;
DWORD engine_thread;
LONG refcount;
php_stream *stream;
zend_resource *res;
} php_istream;
static int le_istream;
static void istream_destructor(php_istream *stm);
static void istream_dtor(zend_resource *rsrc)
{
php_istream *stm = (php_istream *)rsrc->ptr;
istream_destructor(stm);
}
#define FETCH_STM() \
php_istream *stm = (php_istream*)This; \
if (GetCurrentThreadId() != stm->engine_thread) \
return RPC_E_WRONG_THREAD;
#define FETCH_STM_EX() \
php_istream *stm = (php_istream*)This; \
if (GetCurrentThreadId() != stm->engine_thread) \
return RPC_E_WRONG_THREAD;
static HRESULT STDMETHODCALLTYPE stm_queryinterface(
IStream *This,
/* [in] */ REFIID riid,
/* [iid_is][out] */ void **ppvObject)
{
FETCH_STM_EX();
if (IsEqualGUID(&IID_IUnknown, riid) ||
IsEqualGUID(&IID_IStream, riid)) {
*ppvObject = This;
InterlockedIncrement(&stm->refcount);
return S_OK;
}
*ppvObject = NULL;
return E_NOINTERFACE;
}
static ULONG STDMETHODCALLTYPE stm_addref(IStream *This)
{
FETCH_STM_EX();
return InterlockedIncrement(&stm->refcount);
}
static ULONG STDMETHODCALLTYPE stm_release(IStream *This)
{
ULONG ret;
FETCH_STM();
ret = InterlockedDecrement(&stm->refcount);
if (ret == 0) {
/* destroy it */
if (stm->res)
zend_list_delete(stm->res);
}
return ret;
}
static HRESULT STDMETHODCALLTYPE stm_read(IStream *This, void *pv, ULONG cb, ULONG *pcbRead)
{
ULONG nread;
FETCH_STM();
nread = (ULONG)php_stream_read(stm->stream, pv, cb);
if (pcbRead) {
*pcbRead = nread > 0 ? nread : 0;
}
if (nread > 0) {
return S_OK;
}
return S_FALSE;
}
static HRESULT STDMETHODCALLTYPE stm_write(IStream *This, void const *pv, ULONG cb, ULONG *pcbWritten)
{
ULONG nwrote;
FETCH_STM();
nwrote = (ULONG)php_stream_write(stm->stream, pv, cb);
if (pcbWritten) {
*pcbWritten = nwrote > 0 ? nwrote : 0;
}
if (nwrote > 0) {
return S_OK;
}
return S_FALSE;
}
static HRESULT STDMETHODCALLTYPE stm_seek(IStream *This, LARGE_INTEGER dlibMove,
DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
{
off_t offset;
int whence;
int ret;
FETCH_STM();
switch (dwOrigin) {
case STREAM_SEEK_SET: whence = SEEK_SET; break;
case STREAM_SEEK_CUR: whence = SEEK_CUR; break;
case STREAM_SEEK_END: whence = SEEK_END; break;
default:
return STG_E_INVALIDFUNCTION;
}
if (dlibMove.HighPart) {
/* we don't support 64-bit offsets */
return STG_E_INVALIDFUNCTION;
}
offset = (off_t) dlibMove.QuadPart;
ret = php_stream_seek(stm->stream, offset, whence);
if (plibNewPosition) {
plibNewPosition->QuadPart = (ULONGLONG)(ret >= 0 ? ret : 0);
}
return ret >= 0 ? S_OK : STG_E_INVALIDFUNCTION;
}
static HRESULT STDMETHODCALLTYPE stm_set_size(IStream *This, ULARGE_INTEGER libNewSize)
{
FETCH_STM();
if (libNewSize.HighPart) {
return STG_E_INVALIDFUNCTION;
}
if (php_stream_truncate_supported(stm->stream)) {
int ret = php_stream_truncate_set_size(stm->stream, (size_t)libNewSize.QuadPart);
if (ret == 0) {
return S_OK;
}
}
return STG_E_INVALIDFUNCTION;
}
static HRESULT STDMETHODCALLTYPE stm_copy_to(IStream *This, IStream *pstm, ULARGE_INTEGER cb,
ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
{
FETCH_STM_EX();
return E_NOTIMPL;
}
static HRESULT STDMETHODCALLTYPE stm_commit(IStream *This, DWORD grfCommitFlags)
{
FETCH_STM();
php_stream_flush(stm->stream);
return S_OK;
}
static HRESULT STDMETHODCALLTYPE stm_revert(IStream *This)
{
/* NOP */
return S_OK;
}
static HRESULT STDMETHODCALLTYPE stm_lock_region(IStream *This,
ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD lockType)
{
return STG_E_INVALIDFUNCTION;
}
static HRESULT STDMETHODCALLTYPE stm_unlock_region(IStream *This,
ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD lockType)
{
return STG_E_INVALIDFUNCTION;
}
static HRESULT STDMETHODCALLTYPE stm_stat(IStream *This,
STATSTG *pstatstg, DWORD grfStatFlag)
{
return STG_E_INVALIDFUNCTION;
}
static HRESULT STDMETHODCALLTYPE stm_clone(IStream *This, IStream **ppstm)
{
return STG_E_INVALIDFUNCTION;
}
static struct IStreamVtbl php_istream_vtbl = {
stm_queryinterface,
stm_addref,
stm_release,
stm_read,
stm_write,
stm_seek,
stm_set_size,
stm_copy_to,
stm_commit,
stm_revert,
stm_lock_region,
stm_unlock_region,
stm_stat,
stm_clone
};
static void istream_destructor(php_istream *stm)
{
if (stm->res) {
zend_resource *res = stm->res;
stm->res = NULL;
zend_list_delete(res);
return;
}
if (stm->refcount > 0) {
CoDisconnectObject((IUnknown*)stm, 0);
}
zend_list_delete(stm->stream->res);
CoTaskMemFree(stm);
}
/* }}} */
PHP_COM_DOTNET_API IStream *php_com_wrapper_export_stream(php_stream *stream)
{
php_istream *stm = (php_istream*)CoTaskMemAlloc(sizeof(*stm));
zval *tmp;
if (stm == NULL)
return NULL;
memset(stm, 0, sizeof(*stm));
stm->engine_thread = GetCurrentThreadId();
stm->lpVtbl = &php_istream_vtbl;
stm->refcount = 1;
stm->stream = stream;
GC_ADDREF(stream->res);
tmp = zend_list_insert(stm, le_istream);
stm->res = Z_RES_P(tmp);
return (IStream*)stm;
}
#define CPH_ME(fname, arginfo) PHP_ME(com_persist, fname, arginfo, ZEND_ACC_PUBLIC)
#define CPH_SME(fname, arginfo) PHP_ME(com_persist, fname, arginfo, ZEND_ACC_ALLOW_STATIC|ZEND_ACC_PUBLIC)
#define CPH_METHOD(fname) static PHP_METHOD(com_persist, fname)
#define CPH_FETCH() php_com_persist_helper *helper = (php_com_persist_helper*)Z_OBJ_P(getThis());
#define CPH_NO_OBJ() if (helper->unk == NULL) { php_com_throw_exception(E_INVALIDARG, "No COM object is associated with this helper instance"); return; }
typedef struct {
zend_object std;
long codepage;
IUnknown *unk;
IPersistStream *ips;
IPersistStreamInit *ipsi;
IPersistFile *ipf;
} php_com_persist_helper;
static zend_object_handlers helper_handlers;
static zend_class_entry *helper_ce;
static inline HRESULT get_persist_stream(php_com_persist_helper *helper)
{
if (!helper->ips && helper->unk) {
return IUnknown_QueryInterface(helper->unk, &IID_IPersistStream, &helper->ips);
}
return helper->ips ? S_OK : E_NOTIMPL;
}
static inline HRESULT get_persist_stream_init(php_com_persist_helper *helper)
{
if (!helper->ipsi && helper->unk) {
return IUnknown_QueryInterface(helper->unk, &IID_IPersistStreamInit, &helper->ipsi);
}
return helper->ipsi ? S_OK : E_NOTIMPL;
}
static inline HRESULT get_persist_file(php_com_persist_helper *helper)
{
if (!helper->ipf && helper->unk) {
return IUnknown_QueryInterface(helper->unk, &IID_IPersistFile, &helper->ipf);
}
return helper->ipf ? S_OK : E_NOTIMPL;
}
/* {{{ proto string COMPersistHelper::GetCurFile()
Determines the filename into which an object will be saved, or false if none is set, via IPersistFile::GetCurFile */
CPH_METHOD(GetCurFileName)
{
HRESULT res;
OLECHAR *olename = NULL;
CPH_FETCH();
CPH_NO_OBJ();
res = get_persist_file(helper);
if (helper->ipf) {
res = IPersistFile_GetCurFile(helper->ipf, &olename);
if (res == S_OK) {
size_t len;
char *str = php_com_olestring_to_string(olename,
&len, helper->codepage);
RETVAL_STRINGL(str, len);
// TODO: avoid reallocarion???
efree(str);
CoTaskMemFree(olename);
return;
} else if (res == S_FALSE) {
CoTaskMemFree(olename);
RETURN_FALSE;
}
php_com_throw_exception(res, NULL);
} else {
php_com_throw_exception(res, NULL);
}
}
/* }}} */
/* {{{ proto bool COMPersistHelper::SaveToFile(string filename [, bool remember])
Persist object data to file, via IPersistFile::Save */
CPH_METHOD(SaveToFile)
{
HRESULT res;
char *filename, *fullpath = NULL;
size_t filename_len;
zend_bool remember = TRUE;
OLECHAR *olefilename = NULL;
CPH_FETCH();
CPH_NO_OBJ();
res = get_persist_file(helper);
if (helper->ipf) {
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "p!|b",
&filename, &filename_len, &remember)) {
php_com_throw_exception(E_INVALIDARG, "Invalid arguments");
return;
}
if (filename) {
fullpath = expand_filepath(filename, NULL);
if (!fullpath) {
RETURN_FALSE;
}
if (php_check_open_basedir(fullpath)) {
efree(fullpath);
RETURN_FALSE;
}
olefilename = php_com_string_to_olestring(filename, strlen(fullpath), helper->codepage);
efree(fullpath);
}
res = IPersistFile_Save(helper->ipf, olefilename, remember);
if (SUCCEEDED(res)) {
if (!olefilename) {
res = IPersistFile_GetCurFile(helper->ipf, &olefilename);
if (S_OK == res) {
IPersistFile_SaveCompleted(helper->ipf, olefilename);
CoTaskMemFree(olefilename);
olefilename = NULL;
}
} else if (remember) {
IPersistFile_SaveCompleted(helper->ipf, olefilename);
}
}
if (olefilename) {
efree(olefilename);
}
if (FAILED(res)) {
php_com_throw_exception(res, NULL);
}
} else {
php_com_throw_exception(res, NULL);
}
}
/* }}} */
/* {{{ proto bool COMPersistHelper::LoadFromFile(string filename [, int flags])
Load object data from file, via IPersistFile::Load */
CPH_METHOD(LoadFromFile)
{
HRESULT res;
char *filename, *fullpath;
size_t filename_len;
zend_long flags = 0;
OLECHAR *olefilename;
CPH_FETCH();
CPH_NO_OBJ();
res = get_persist_file(helper);
if (helper->ipf) {
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "p|l",
&filename, &filename_len, &flags)) {
php_com_throw_exception(E_INVALIDARG, "Invalid arguments");
return;
}
if (!(fullpath = expand_filepath(filename, NULL))) {
RETURN_FALSE;
}
if (php_check_open_basedir(fullpath)) {
efree(fullpath);
RETURN_FALSE;
}
olefilename = php_com_string_to_olestring(fullpath, strlen(fullpath), helper->codepage);
efree(fullpath);
res = IPersistFile_Load(helper->ipf, olefilename, (DWORD)flags);
efree(olefilename);
if (FAILED(res)) {
php_com_throw_exception(res, NULL);
}
} else {
php_com_throw_exception(res, NULL);
}
}
/* }}} */
/* {{{ proto int COMPersistHelper::GetMaxStreamSize()
Gets maximum stream size required to store the object data, via IPersistStream::GetSizeMax (or IPersistStreamInit::GetSizeMax) */
CPH_METHOD(GetMaxStreamSize)
{
HRESULT res;
ULARGE_INTEGER size;
CPH_FETCH();
CPH_NO_OBJ();
res = get_persist_stream_init(helper);
if (helper->ipsi) {
res = IPersistStreamInit_GetSizeMax(helper->ipsi, &size);
} else {
res = get_persist_stream(helper);
if (helper->ips) {
res = IPersistStream_GetSizeMax(helper->ips, &size);
} else {
php_com_throw_exception(res, NULL);
return;
}
}
if (res != S_OK) {
php_com_throw_exception(res, NULL);
} else {
/* TODO: handle 64 bit properly */
RETURN_LONG((zend_long)size.QuadPart);
}
}
/* }}} */
/* {{{ proto int COMPersistHelper::InitNew()
Initializes the object to a default state, via IPersistStreamInit::InitNew */
CPH_METHOD(InitNew)
{
HRESULT res;
CPH_FETCH();
CPH_NO_OBJ();
res = get_persist_stream_init(helper);
if (helper->ipsi) {
res = IPersistStreamInit_InitNew(helper->ipsi);
if (res != S_OK) {
php_com_throw_exception(res, NULL);
} else {
RETURN_TRUE;
}
} else {
php_com_throw_exception(res, NULL);
}
}
/* }}} */
/* {{{ proto mixed COMPersistHelper::LoadFromStream(resource stream)
Initializes an object from the stream where it was previously saved, via IPersistStream::Load or OleLoadFromStream */
CPH_METHOD(LoadFromStream)
{
zval *zstm;
php_stream *stream;
IStream *stm = NULL;
HRESULT res;
CPH_FETCH();
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zstm)) {
php_com_throw_exception(E_INVALIDARG, "invalid arguments");
return;
}
php_stream_from_zval_no_verify(stream, zstm);
if (stream == NULL) {
php_com_throw_exception(E_INVALIDARG, "expected a stream");
return;
}
stm = php_com_wrapper_export_stream(stream);
if (stm == NULL) {
php_com_throw_exception(E_UNEXPECTED, "failed to wrap stream");
return;
}
res = S_OK;
RETVAL_TRUE;
if (helper->unk == NULL) {
IDispatch *disp = NULL;
/* we need to create an object and load using OleLoadFromStream */
res = OleLoadFromStream(stm, &IID_IDispatch, &disp);
if (SUCCEEDED(res)) {
php_com_wrap_dispatch(return_value, disp, COMG(code_page));
}
} else {
res = get_persist_stream_init(helper);
if (helper->ipsi) {
res = IPersistStreamInit_Load(helper->ipsi, stm);
} else {
res = get_persist_stream(helper);
if (helper->ips) {
res = IPersistStreamInit_Load(helper->ipsi, stm);
}
}
}
IStream_Release(stm);
if (FAILED(res)) {
php_com_throw_exception(res, NULL);
RETURN_NULL();
}
}
/* }}} */
/* {{{ proto int COMPersistHelper::SaveToStream(resource stream)
Saves the object to a stream, via IPersistStream::Save */
CPH_METHOD(SaveToStream)
{
zval *zstm;
php_stream *stream;
IStream *stm = NULL;
HRESULT res;
CPH_FETCH();
CPH_NO_OBJ();
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zstm)) {
php_com_throw_exception(E_INVALIDARG, "invalid arguments");
return;
}
php_stream_from_zval_no_verify(stream, zstm);
if (stream == NULL) {
php_com_throw_exception(E_INVALIDARG, "expected a stream");
return;
}
stm = php_com_wrapper_export_stream(stream);
if (stm == NULL) {
php_com_throw_exception(E_UNEXPECTED, "failed to wrap stream");
return;
}
res = get_persist_stream_init(helper);
if (helper->ipsi) {
res = IPersistStreamInit_Save(helper->ipsi, stm, TRUE);
} else {
res = get_persist_stream(helper);
if (helper->ips) {
res = IPersistStream_Save(helper->ips, stm, TRUE);
}
}
IStream_Release(stm);
if (FAILED(res)) {
php_com_throw_exception(res, NULL);
return;
}
RETURN_TRUE;
}
/* }}} */
/* {{{ proto COMPersistHelper::__construct([object com_object])
Creates a persistence helper object, usually associated with a com_object */
CPH_METHOD(__construct)
{
php_com_dotnet_object *obj = NULL;
zval *zobj = NULL;
CPH_FETCH();
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "|O!",
&zobj, php_com_variant_class_entry)) {
php_com_throw_exception(E_INVALIDARG, "invalid arguments");
return;
}
if (!zobj) {
return;
}
obj = CDNO_FETCH(zobj);
if (V_VT(&obj->v) != VT_DISPATCH || V_DISPATCH(&obj->v) == NULL) {
php_com_throw_exception(E_INVALIDARG, "parameter must represent an IDispatch COM object");
return;
}
/* it is always safe to cast an interface to IUnknown */
helper->unk = (IUnknown*)V_DISPATCH(&obj->v);
IUnknown_AddRef(helper->unk);
helper->codepage = obj->code_page;
}
/* }}} */
static const zend_function_entry com_persist_helper_methods[] = {
CPH_ME(__construct, NULL)
CPH_ME(GetCurFileName, NULL)
CPH_ME(SaveToFile, NULL)
CPH_ME(LoadFromFile, NULL)
CPH_ME(GetMaxStreamSize, NULL)
CPH_ME(InitNew, NULL)
CPH_ME(LoadFromStream, NULL)
CPH_ME(SaveToStream, NULL)
PHP_FE_END
};
static void helper_free_storage(zend_object *obj)
{
php_com_persist_helper *object = (php_com_persist_helper*)obj;
if (object->ipf) {
IPersistFile_Release(object->ipf);
}
if (object->ips) {
IPersistStream_Release(object->ips);
}
if (object->ipsi) {
IPersistStreamInit_Release(object->ipsi);
}
if (object->unk) {
IUnknown_Release(object->unk);
}
zend_object_std_dtor(&object->std);
}
static zend_object* helper_clone(zval *obj)
{
php_com_persist_helper *clone, *object = (php_com_persist_helper*)Z_OBJ_P(obj);
clone = emalloc(sizeof(*object));
memcpy(clone, object, sizeof(*object));
zend_object_std_init(&clone->std, object->std.ce);
if (clone->ipf) {
IPersistFile_AddRef(clone->ipf);
}
if (clone->ips) {
IPersistStream_AddRef(clone->ips);
}
if (clone->ipsi) {
IPersistStreamInit_AddRef(clone->ipsi);
}
if (clone->unk) {
IUnknown_AddRef(clone->unk);
}
return (zend_object*)clone;
}
static zend_object* helper_new(zend_class_entry *ce)
{
php_com_persist_helper *helper;
helper = emalloc(sizeof(*helper));
memset(helper, 0, sizeof(*helper));
zend_object_std_init(&helper->std, helper_ce);
helper->std.handlers = &helper_handlers;
return &helper->std;
}
int php_com_persist_minit(INIT_FUNC_ARGS)
{
zend_class_entry ce;
memcpy(&helper_handlers, &std_object_handlers, sizeof(helper_handlers));
helper_handlers.free_obj = helper_free_storage;
helper_handlers.clone_obj = helper_clone;
INIT_CLASS_ENTRY(ce, "COMPersistHelper", com_persist_helper_methods);
ce.create_object = helper_new;
helper_ce = zend_register_internal_class(&ce);
helper_ce->ce_flags |= ZEND_ACC_FINAL;
le_istream = zend_register_list_destructors_ex(istream_dtor,
NULL, "com_dotnet_istream_wrapper", module_number);
return SUCCESS;
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/