| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| #include "sal/config.h" |
| |
| #include <cstdlib> |
| #include <new> |
| #include <vector> |
| |
| #include "boost/noncopyable.hpp" |
| #include "com/sun/star/io/IOException.hpp" |
| #include "com/sun/star/uno/Reference.hxx" |
| #include "com/sun/star/uno/RuntimeException.hpp" |
| #include "com/sun/star/uno/Sequence.hxx" |
| #include "com/sun/star/uno/XInterface.hpp" |
| #include "cppu/unotype.hxx" |
| #include "osl/diagnose.h" |
| #include "rtl/byteseq.hxx" |
| #include "rtl/ref.hxx" |
| #include "rtl/textcvt.h" |
| #include "rtl/textenc.h" |
| #include "rtl/ustring.h" |
| #include "rtl/ustring.hxx" |
| #include "sal/types.h" |
| #include "typelib/typeclass.h" |
| #include "typelib/typedescription.h" |
| #include "typelib/typedescription.hxx" |
| #include "uno/any2.h" |
| #include "uno/data.h" |
| #include "uno/dispatcher.hxx" |
| |
| #include "binaryany.hxx" |
| #include "bridge.hxx" |
| #include "cache.hxx" |
| #include "readerstate.hxx" |
| #include "unmarshal.hxx" |
| |
| namespace binaryurp { |
| |
| namespace { |
| |
| namespace css = com::sun::star; |
| |
| void * allocate(sal_Size size) { |
| void * p = rtl_allocateMemory(size); |
| if (p == 0) { |
| throw std::bad_alloc(); |
| } |
| return p; |
| } |
| |
| std::vector< BinaryAny >::iterator copyMemberValues( |
| css::uno::TypeDescription const & type, |
| std::vector< BinaryAny >::iterator const & it, void * buffer) throw () |
| { |
| OSL_ASSERT( |
| type.is() && |
| (type.get()->eTypeClass == typelib_TypeClass_STRUCT || |
| type.get()->eTypeClass == typelib_TypeClass_EXCEPTION) && |
| buffer != 0); |
| type.makeComplete(); |
| std::vector< BinaryAny >::iterator i(it); |
| typelib_CompoundTypeDescription * ctd = |
| reinterpret_cast< typelib_CompoundTypeDescription * >(type.get()); |
| if (ctd->pBaseTypeDescription != 0) { |
| i = copyMemberValues( |
| css::uno::TypeDescription(&ctd->pBaseTypeDescription->aBase), i, |
| buffer); |
| } |
| for (sal_Int32 j = 0; j != ctd->nMembers; ++j) { |
| uno_type_copyData( |
| static_cast< char * >(buffer) + ctd->pMemberOffsets[j], |
| const_cast< void * >( |
| i++->getValue(css::uno::TypeDescription(ctd->ppTypeRefs[j]))), |
| ctd->ppTypeRefs[j], 0); |
| } |
| return i; |
| } |
| |
| } |
| |
| Unmarshal::Unmarshal( |
| rtl::Reference< Bridge > const & bridge, ReaderState & state, |
| css::uno::Sequence< sal_Int8 > const & buffer): |
| bridge_(bridge), state_(state), buffer_(buffer) |
| { |
| data_ = reinterpret_cast< sal_uInt8 const * >(buffer_.getConstArray()); |
| end_ = data_ + buffer_.getLength(); |
| } |
| |
| Unmarshal::~Unmarshal() {} |
| |
| sal_uInt8 Unmarshal::read8() { |
| check(1); |
| return *data_++; |
| } |
| |
| sal_uInt16 Unmarshal::read16() { |
| check(2); |
| sal_uInt16 n = static_cast< sal_uInt16 >(*data_++) << 8; |
| return n | *data_++; |
| } |
| |
| sal_uInt32 Unmarshal::read32() { |
| check(4); |
| sal_uInt32 n = static_cast< sal_uInt32 >(*data_++) << 24; |
| n |= static_cast< sal_uInt32 >(*data_++) << 16; |
| n |= static_cast< sal_uInt32 >(*data_++) << 8; |
| return n | *data_++; |
| } |
| |
| css::uno::TypeDescription Unmarshal::readType() { |
| sal_uInt8 flags = read8(); |
| typelib_TypeClass tc = static_cast< typelib_TypeClass >(flags & 0x7F); |
| switch (tc) { |
| case typelib_TypeClass_VOID: |
| case typelib_TypeClass_BOOLEAN: |
| case typelib_TypeClass_BYTE: |
| case typelib_TypeClass_SHORT: |
| case typelib_TypeClass_UNSIGNED_SHORT: |
| case typelib_TypeClass_LONG: |
| case typelib_TypeClass_UNSIGNED_LONG: |
| case typelib_TypeClass_HYPER: |
| case typelib_TypeClass_UNSIGNED_HYPER: |
| case typelib_TypeClass_FLOAT: |
| case typelib_TypeClass_DOUBLE: |
| case typelib_TypeClass_CHAR: |
| case typelib_TypeClass_STRING: |
| case typelib_TypeClass_TYPE: |
| case typelib_TypeClass_ANY: |
| if ((flags & 0x80) != 0) { |
| throw css::io::IOException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "binaryurp::Unmarshal: cache flag of simple type is" |
| " set")), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| return css::uno::TypeDescription( |
| *typelib_static_type_getByTypeClass( |
| static_cast< typelib_TypeClass >(tc))); |
| case typelib_TypeClass_SEQUENCE: |
| case typelib_TypeClass_ENUM: |
| case typelib_TypeClass_STRUCT: |
| case typelib_TypeClass_EXCEPTION: |
| case typelib_TypeClass_INTERFACE: |
| { |
| sal_uInt16 idx = readCacheIndex(); |
| if ((flags & 0x80) == 0) { |
| if (idx == cache::ignore || !state_.typeCache[idx].is()) { |
| throw css::io::IOException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "binaryurp::Unmarshal: unknown type cache" |
| " index")), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| return state_.typeCache[idx]; |
| } else { |
| css::uno::TypeDescription t(readString()); |
| if (!t.is() || |
| t.get()->eTypeClass != static_cast< typelib_TypeClass >(tc)) |
| { |
| throw css::io::IOException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "binaryurp::Unmarshal: type with unknown" |
| " name")), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| for (css::uno::TypeDescription t2(t); |
| t2.get()->eTypeClass == typelib_TypeClass_SEQUENCE;) |
| { |
| t2.makeComplete(); |
| t2 = css::uno::TypeDescription( |
| reinterpret_cast< typelib_IndirectTypeDescription * >( |
| t2.get())->pType); |
| if (!t2.is()) { |
| throw css::io::IOException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "binaryurp::Unmarshal: sequence type with" |
| " unknown component type")), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| switch (t2.get()->eTypeClass) { |
| case typelib_TypeClass_VOID: |
| case typelib_TypeClass_EXCEPTION: |
| throw css::io::IOException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "binaryurp::Unmarshal: sequence type with" |
| " bad component type")), |
| css::uno::Reference< css::uno::XInterface >()); |
| default: |
| break; |
| } |
| } |
| if (idx != cache::ignore) { |
| state_.typeCache[idx] = t; |
| } |
| return t; |
| } |
| } |
| default: |
| throw css::io::IOException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "binaryurp::Unmarshal: type of unknown type class")), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| } |
| |
| rtl::OUString Unmarshal::readOid() { |
| rtl::OUString oid(readString()); |
| for (sal_Int32 i = 0; i != oid.getLength(); ++i) { |
| if (oid[i] > 0x7F) { |
| throw css::io::IOException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "binaryurp::Unmarshal: OID contains non-ASCII" |
| " character")), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| } |
| sal_uInt16 idx = readCacheIndex(); |
| if (oid.isEmpty() && idx != cache::ignore) { |
| if ( state_.oidCache[idx].isEmpty() ) { |
| throw css::io::IOException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "binaryurp::Unmarshal: unknown OID cache index")), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| return state_.oidCache[idx]; |
| } |
| if (idx != cache::ignore) { |
| state_.oidCache[idx] = oid; |
| } |
| return oid; |
| } |
| |
| rtl::ByteSequence Unmarshal::readTid() { |
| rtl::ByteSequence tid( |
| *static_cast< sal_Sequence * const * >( |
| readSequence( |
| css::uno::TypeDescription( |
| cppu::UnoType< css::uno::Sequence< sal_Int8 > >::get())). |
| getValue( |
| css::uno::TypeDescription( |
| cppu::UnoType< css::uno::Sequence< sal_Int8 > >::get())))); |
| sal_uInt16 idx = readCacheIndex(); |
| if (tid.getLength() == 0) { |
| if (idx == cache::ignore || state_.tidCache[idx].getLength() == 0) { |
| throw css::io::IOException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "binaryurp::Unmarshal: unknown TID cache index")), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| return state_.tidCache[idx]; |
| } |
| if (idx != cache::ignore) { |
| state_.tidCache[idx] = tid; |
| } |
| return tid; |
| } |
| |
| BinaryAny Unmarshal::readValue(css::uno::TypeDescription const & type) { |
| OSL_ASSERT(type.is()); |
| switch (type.get()->eTypeClass) { |
| default: |
| std::abort(); // this cannot happen |
| // pseudo fall-through to avoid compiler warnings |
| case typelib_TypeClass_VOID: |
| return BinaryAny(); |
| case typelib_TypeClass_BOOLEAN: |
| { |
| sal_uInt8 v = read8(); |
| if (v > 1) { |
| throw css::io::IOException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "binaryurp::Unmarshal: boolean of unknown value")), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| return BinaryAny(type, &v); |
| } |
| case typelib_TypeClass_BYTE: |
| { |
| sal_uInt8 v = read8(); |
| return BinaryAny(type, &v); |
| } |
| case typelib_TypeClass_SHORT: |
| case typelib_TypeClass_UNSIGNED_SHORT: |
| case typelib_TypeClass_CHAR: |
| { |
| sal_uInt16 v = read16(); |
| return BinaryAny(type, &v); |
| } |
| case typelib_TypeClass_LONG: |
| case typelib_TypeClass_UNSIGNED_LONG: |
| case typelib_TypeClass_FLOAT: |
| { |
| sal_uInt32 v = read32(); |
| return BinaryAny(type, &v); |
| } |
| case typelib_TypeClass_HYPER: |
| case typelib_TypeClass_UNSIGNED_HYPER: |
| case typelib_TypeClass_DOUBLE: |
| { |
| sal_uInt64 v = read64(); |
| return BinaryAny(type, &v); |
| } |
| case typelib_TypeClass_STRING: |
| { |
| rtl::OUString v(readString()); |
| return BinaryAny(type, &v.pData); |
| } |
| case typelib_TypeClass_TYPE: |
| { |
| css::uno::TypeDescription v(readType()); |
| typelib_TypeDescription * p = v.get(); |
| return BinaryAny(type, &p); |
| } |
| case typelib_TypeClass_ANY: |
| { |
| css::uno::TypeDescription t(readType()); |
| if (t.get()->eTypeClass == typelib_TypeClass_ANY) { |
| throw css::io::IOException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "binaryurp::Unmarshal: any of type ANY")), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| return readValue(t); |
| } |
| case typelib_TypeClass_SEQUENCE: |
| type.makeComplete(); |
| return readSequence(type); |
| case typelib_TypeClass_ENUM: |
| { |
| sal_Int32 v = static_cast< sal_Int32 >(read32()); |
| type.makeComplete(); |
| typelib_EnumTypeDescription * etd = |
| reinterpret_cast< typelib_EnumTypeDescription * >(type.get()); |
| bool found = false; |
| for (sal_Int32 i = 0; i != etd->nEnumValues; ++i) { |
| if (etd->pEnumValues[i] == v) { |
| found = true; |
| break; |
| } |
| } |
| if (!found) { |
| throw css::io::IOException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "binaryurp::Unmarshal: unknown enum value")), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| return BinaryAny(type, &v); |
| } |
| case typelib_TypeClass_STRUCT: |
| case typelib_TypeClass_EXCEPTION: |
| { |
| std::vector< BinaryAny > as; |
| readMemberValues(type, &as); |
| void * buf = allocate(type.get()->nSize); |
| copyMemberValues(type, as.begin(), buf); |
| uno_Any raw; |
| raw.pType = reinterpret_cast< typelib_TypeDescriptionReference * >( |
| type.get()); |
| raw.pData = buf; |
| raw.pReserved = 0; |
| return BinaryAny(raw); |
| } |
| case typelib_TypeClass_INTERFACE: |
| { |
| css::uno::UnoInterfaceReference obj( |
| bridge_->registerIncomingInterface(readOid(), type)); |
| return BinaryAny(type, &obj.m_pUnoI); |
| } |
| } |
| } |
| |
| void Unmarshal::done() const { |
| if (data_ != end_) { |
| throw css::io::IOException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "binaryurp::Unmarshal: block contains excess data")), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| } |
| |
| void Unmarshal::check(sal_Int32 size) const { |
| if (end_ - data_ < size) { |
| throw css::io::IOException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "binaryurp::Unmarshal: trying to read past end of block")), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| } |
| |
| sal_uInt32 Unmarshal::readCompressed() { |
| sal_uInt8 n = read8(); |
| return n == 0xFF ? read32() : n; |
| } |
| |
| sal_uInt16 Unmarshal::readCacheIndex() { |
| sal_uInt16 idx = read16(); |
| if (idx >= cache::size && idx != cache::ignore) { |
| throw css::io::IOException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "binaryurp::Unmarshal: cache index out of range")), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| return idx; |
| } |
| |
| sal_uInt64 Unmarshal::read64() { |
| check(8); |
| sal_uInt64 n = static_cast< sal_uInt64 >(*data_++) << 56; |
| n |= static_cast< sal_uInt64 >(*data_++) << 48; |
| n |= static_cast< sal_uInt64 >(*data_++) << 40; |
| n |= static_cast< sal_uInt64 >(*data_++) << 32; |
| n |= static_cast< sal_uInt64 >(*data_++) << 24; |
| n |= static_cast< sal_uInt64 >(*data_++) << 16; |
| n |= static_cast< sal_uInt64 >(*data_++) << 8; |
| return n | *data_++; |
| } |
| |
| rtl::OUString Unmarshal::readString() { |
| sal_uInt32 n = readCompressed(); |
| if (n > SAL_MAX_INT32) { |
| throw css::uno::RuntimeException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "binaryurp::Unmarshal: string size too large")), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| check(static_cast< sal_Int32 >(n)); |
| rtl::OUString s; |
| if (!rtl_convertStringToUString( |
| &s.pData, reinterpret_cast< char const * >(data_), |
| static_cast< sal_Int32 >(n), RTL_TEXTENCODING_UTF8, |
| (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR | |
| RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR | |
| RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR))) |
| { |
| throw css::io::IOException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "binaryurp::Unmarshal: string does not contain UTF-8")), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| data_ += n; |
| return s; |
| } |
| |
| BinaryAny Unmarshal::readSequence(css::uno::TypeDescription const & type) { |
| OSL_ASSERT( |
| type.is() && type.get()->eTypeClass == typelib_TypeClass_SEQUENCE); |
| sal_uInt32 n = readCompressed(); |
| if (n > SAL_MAX_INT32) { |
| throw css::uno::RuntimeException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "binaryurp::Unmarshal: sequence size too large")), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| if (n == 0) { |
| return BinaryAny(type, 0); |
| } |
| css::uno::TypeDescription ctd( |
| reinterpret_cast< typelib_IndirectTypeDescription * >( |
| type.get())->pType); |
| if (ctd.get()->eTypeClass == typelib_TypeClass_BYTE) { |
| check(static_cast< sal_Int32 >(n)); |
| rtl::ByteSequence s( |
| reinterpret_cast< sal_Int8 const * >(data_), |
| static_cast< sal_Int32 >(n)); |
| data_ += n; |
| sal_Sequence * p = s.getHandle(); |
| return BinaryAny(type, &p); |
| } |
| std::vector< BinaryAny > as; |
| for (sal_uInt32 i = 0; i != n; ++i) { |
| as.push_back(readValue(ctd)); |
| } |
| OSL_ASSERT(ctd.get()->nSize >= 0); |
| sal_uInt64 size = static_cast< sal_uInt64 >(n) * |
| static_cast< sal_uInt64 >(ctd.get()->nSize); |
| // sal_uInt32 * sal_Int32 -> sal_uInt64 cannot overflow |
| if (size > SAL_MAX_SIZE - SAL_SEQUENCE_HEADER_SIZE) { |
| throw css::uno::RuntimeException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "binaryurp::Unmarshal: sequence size too large")), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| void * buf = allocate( |
| SAL_SEQUENCE_HEADER_SIZE + static_cast< sal_Size >(size)); |
| static_cast< sal_Sequence * >(buf)->nRefCount = 0; |
| static_cast< sal_Sequence * >(buf)->nElements = |
| static_cast< sal_Int32 >(n); |
| for (sal_uInt32 i = 0; i != n; ++i) { |
| uno_copyData( |
| static_cast< sal_Sequence * >(buf)->elements + i * ctd.get()->nSize, |
| const_cast< void * >(as[i].getValue(ctd)), ctd.get(), 0); |
| } |
| return BinaryAny(type, reinterpret_cast< sal_Sequence ** >(&buf)); |
| } |
| |
| void Unmarshal::readMemberValues( |
| css::uno::TypeDescription const & type, std::vector< BinaryAny > * values) |
| { |
| OSL_ASSERT( |
| type.is() && |
| (type.get()->eTypeClass == typelib_TypeClass_STRUCT || |
| type.get()->eTypeClass == typelib_TypeClass_EXCEPTION) && |
| values != 0); |
| type.makeComplete(); |
| typelib_CompoundTypeDescription * ctd = |
| reinterpret_cast< typelib_CompoundTypeDescription * >(type.get()); |
| if (ctd->pBaseTypeDescription != 0) { |
| readMemberValues( |
| css::uno::TypeDescription(&ctd->pBaseTypeDescription->aBase), |
| values); |
| } |
| for (sal_Int32 i = 0; i != ctd->nMembers; ++i) { |
| values->push_back( |
| readValue(css::uno::TypeDescription(ctd->ppTypeRefs[i]))); |
| } |
| } |
| |
| } |