/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "binaryany.hxx" #include "bridge.hxx" #include "currentcontext.hxx" #include "specialfunctionids.hxx" #include "writer.hxx" namespace binaryurp { Writer::Item::Item() : request(false) , setter(false) , exception(false) , setCurrentContextMode(false) {} Writer::Item::Item( rtl::ByteSequence theTid, OUString theOid, css::uno::TypeDescription theType, css::uno::TypeDescription theMember, std::vector< BinaryAny >&& inArguments, css::uno::UnoInterfaceReference theCurrentContext): tid(std::move(theTid)), oid(std::move(theOid)), type(std::move(theType)), member(std::move(theMember)), currentContext(std::move(theCurrentContext)), arguments(std::move(inArguments)), request(true), setter(false), exception(false), setCurrentContextMode(false) {} Writer::Item::Item( rtl::ByteSequence theTid, css::uno::TypeDescription theMember, bool theSetter, bool theException, BinaryAny theReturnValue, std::vector< BinaryAny >&& outArguments, bool theSetCurrentContextMode): tid(std::move(theTid)), member(std::move(theMember)), returnValue(std::move(theReturnValue)), arguments(std::move(outArguments)), request(false), setter(theSetter), exception(theException), setCurrentContextMode(theSetCurrentContextMode) {} Writer::Writer(rtl::Reference< Bridge > const & bridge): Thread("binaryurpWriter"), bridge_(bridge), marshal_(bridge, state_), stop_(false) { assert(bridge.is()); } void Writer::sendDirectRequest( rtl::ByteSequence const & tid, OUString const & oid, css::uno::TypeDescription const & type, css::uno::TypeDescription const & member, std::vector< BinaryAny > const & inArguments) { assert(!unblocked_.check()); sendRequest( tid, oid, type, member, inArguments, false, css::uno::UnoInterfaceReference()); } void Writer::sendDirectReply( rtl::ByteSequence const & tid, css::uno::TypeDescription const & member, bool exception, BinaryAny const & returnValue, std::vector< BinaryAny > const & outArguments) { assert(!unblocked_.check()); sendReply(tid, member, false, exception, returnValue,outArguments); } void Writer::queueRequest( rtl::ByteSequence const & tid, OUString const & oid, css::uno::TypeDescription const & type, css::uno::TypeDescription const & member, std::vector< BinaryAny >&& inArguments) { css::uno::UnoInterfaceReference cc(current_context::get()); std::lock_guard g(mutex_); queue_.emplace_back(tid, oid, type, member, std::move(inArguments), cc); items_.set(); } void Writer::queueReply( rtl::ByteSequence const & tid, css::uno::TypeDescription const & member, bool setter, bool exception, BinaryAny const & returnValue, std::vector< BinaryAny >&& outArguments, bool setCurrentContextMode) { std::lock_guard g(mutex_); queue_.emplace_back( tid, member, setter, exception, returnValue, std::move(outArguments), setCurrentContextMode); items_.set(); } void Writer::unblock() { // Assumes that osl::Condition::set works as a memory barrier, so that // changes made by preceding sendDirectRequest/Reply calls are visible to // subsequent sendRequest/Reply calls: unblocked_.set(); } void Writer::stop() { { std::lock_guard g(mutex_); stop_ = true; } unblocked_.set(); items_.set(); } Writer::~Writer() {} void Writer::execute() { try { unblocked_.wait(); for (;;) { items_.wait(); Item item; { std::lock_guard g(mutex_); if (stop_) { return; } assert(!queue_.empty()); item = queue_.front(); queue_.pop_front(); if (queue_.empty()) { items_.reset(); } } if (item.request) { sendRequest( item.tid, item.oid, item.type, item.member, item.arguments, (item.oid != "UrpProtocolProperties" && !item.member.equals( css::uno::TypeDescription( u"com.sun.star.uno.XInterface::release"_ustr)) && bridge_->isCurrentContextMode()), item.currentContext); } else { sendReply( item.tid, item.member, item.setter, item.exception, item.returnValue, item.arguments); if (item.setCurrentContextMode) { bridge_->setCurrentContextMode(); } } } } catch (const css::uno::Exception & e) { SAL_INFO("binaryurp", "caught " << e); } catch (const std::exception & e) { SAL_INFO("binaryurp", "caught C++ exception " << e.what()); } bridge_->terminate(false); bridge_.clear(); } void Writer::sendRequest( rtl::ByteSequence const & tid, OUString const & oid, css::uno::TypeDescription const & type, css::uno::TypeDescription const & member, std::vector< BinaryAny > const & inArguments, bool currentContextMode, css::uno::UnoInterfaceReference const & currentContext) { assert(tid.getLength() != 0); assert(!oid.isEmpty()); assert(member.is()); css::uno::TypeDescription t(type); sal_Int32 functionId = 0; bool bForceSynchronous = false; member.makeComplete(); switch (member.get()->eTypeClass) { case typelib_TypeClass_INTERFACE_ATTRIBUTE: { typelib_InterfaceAttributeTypeDescription * atd = reinterpret_cast< typelib_InterfaceAttributeTypeDescription * >( member.get()); assert(atd->pInterface != nullptr); if (!t.is()) { t = css::uno::TypeDescription(&atd->pInterface->aBase); } t.makeComplete(); functionId = atd->pInterface->pMapMemberIndexToFunctionIndex[ atd->aBase.nPosition]; if (!inArguments.empty()) { // setter ++functionId; } break; } case typelib_TypeClass_INTERFACE_METHOD: { typelib_InterfaceMethodTypeDescription * mtd = reinterpret_cast< typelib_InterfaceMethodTypeDescription * >( member.get()); assert(mtd->pInterface != nullptr); if (!t.is()) { t = css::uno::TypeDescription(&mtd->pInterface->aBase); } t.makeComplete(); functionId = mtd->pInterface->pMapMemberIndexToFunctionIndex[ mtd->aBase.nPosition]; bForceSynchronous = mtd->bOneWay && functionId != SPECIAL_FUNCTION_ID_RELEASE; break; } default: assert(false); // this cannot happen break; } assert(functionId >= 0); if (functionId > SAL_MAX_UINT16) { throw css::uno::RuntimeException(u"function ID too large for URP"_ustr); } std::vector< unsigned char > buf; bool newType = !(lastType_.is() && t.equals(lastType_)); bool newOid = oid != lastOid_; bool newTid = tid != lastTid_; if (newType || newOid || newTid || bForceSynchronous || functionId > 0x3FFF) // > 14 bit function ID { Marshal::write8( &buf, (0xC0 | (newType ? 0x20 : 0) | (newOid ? 0x10 : 0) | (newTid ? 0x08 : 0) | (functionId > 0xFF ? 0x04 : 0) | (bForceSynchronous ? 0x01 : 0))); // bit 7: LONGHEADER, bit 6: REQUEST, bit 5: NEWTYPE, bit 4: NEWOID, // bit 3: NEWTID, bit 2: FUNCTIONID16, bit 0: MOREFLAGS if (bForceSynchronous) { Marshal::write8(&buf, 0xC0); // bit 7: MUSTREPLY, bit 6: SYNCHRONOUS } if (functionId <= 0xFF) { Marshal::write8(&buf, static_cast< sal_uInt8 >(functionId)); } else { Marshal::write16(&buf, static_cast< sal_uInt16 >(functionId)); } if (newType) { marshal_.writeType(&buf, t); } if (newOid) { marshal_.writeOid(&buf, oid); } if (newTid) { marshal_.writeTid(&buf, tid); } } else if (functionId <= 0x3F) { // <= 6 bit function ID Marshal::write8(&buf, static_cast< sal_uInt8 >(functionId)); // bit 7: !LONGHEADER, bit 6: !FUNCTIONID14 } else { Marshal::write8( &buf, static_cast< sal_uInt8 >(0x40 | (functionId >> 8))); // bit 7: !LONGHEADER, bit 6: FUNCTIONID14 Marshal::write8(&buf, functionId & 0xFF); } if (currentContextMode) { css::uno::UnoInterfaceReference cc(currentContext); marshal_.writeValue( &buf, css::uno::TypeDescription( cppu::UnoType< css::uno::Reference< css::uno::XCurrentContext > >::get()), BinaryAny( css::uno::TypeDescription( cppu::UnoType< css::uno::Reference< css::uno::XCurrentContext > >::get()), &cc.m_pUnoI)); } switch (member.get()->eTypeClass) { case typelib_TypeClass_INTERFACE_ATTRIBUTE: if (!inArguments.empty()) { // setter assert(inArguments.size() == 1); marshal_.writeValue( &buf, css::uno::TypeDescription( reinterpret_cast< typelib_InterfaceAttributeTypeDescription * >( member.get())-> pAttributeTypeRef), inArguments.front()); } break; case typelib_TypeClass_INTERFACE_METHOD: { typelib_InterfaceMethodTypeDescription * mtd = reinterpret_cast< typelib_InterfaceMethodTypeDescription * >( member.get()); std::vector< BinaryAny >::const_iterator i(inArguments.begin()); for (sal_Int32 j = 0; j != mtd->nParams; ++j) { if (mtd->pParams[j].bIn) { marshal_.writeValue( &buf, css::uno::TypeDescription(mtd->pParams[j].pTypeRef), *i++); } } assert(i == inArguments.end()); break; } default: assert(false); // this cannot happen break; } sendMessage(buf); lastType_ = std::move(t); lastOid_ = oid; lastTid_ = tid; } void Writer::sendReply( rtl::ByteSequence const & tid, css::uno::TypeDescription const & member, bool setter, bool exception, BinaryAny const & returnValue, std::vector< BinaryAny > const & outArguments) { assert(tid.getLength() != 0); assert(member.is()); assert(member.get()->bComplete); std::vector< unsigned char > buf; bool newTid = tid != lastTid_; Marshal::write8(&buf, 0x80 | (exception ? 0x20 : 0) | (newTid ? 0x08 : 0)); // bit 7: LONGHEADER; bit 6: !REQUEST; bit 5: EXCEPTION; bit 3: NEWTID if (newTid) { marshal_.writeTid(&buf, tid); } if (exception) { marshal_.writeValue( &buf, css::uno::TypeDescription(cppu::UnoType< css::uno::Any >::get()), returnValue); } else { switch (member.get()->eTypeClass) { case typelib_TypeClass_INTERFACE_ATTRIBUTE: if (!setter) { marshal_.writeValue( &buf, css::uno::TypeDescription( reinterpret_cast< typelib_InterfaceAttributeTypeDescription * >( member.get())-> pAttributeTypeRef), returnValue); } break; case typelib_TypeClass_INTERFACE_METHOD: { typelib_InterfaceMethodTypeDescription * mtd = reinterpret_cast< typelib_InterfaceMethodTypeDescription * >( member.get()); marshal_.writeValue( &buf, css::uno::TypeDescription(mtd->pReturnTypeRef), returnValue); std::vector< BinaryAny >::const_iterator i( outArguments.begin()); for (sal_Int32 j = 0; j != mtd->nParams; ++j) { if (mtd->pParams[j].bOut) { marshal_.writeValue( &buf, css::uno::TypeDescription(mtd->pParams[j].pTypeRef), *i++); } } assert(i == outArguments.end()); break; } default: assert(false); // this cannot happen break; } } sendMessage(buf); lastTid_ = tid; bridge_->decrementCalls(); } void Writer::sendMessage(std::vector< unsigned char > const & buffer) { std::vector< unsigned char > header; if (buffer.size() > SAL_MAX_UINT32) { throw css::uno::RuntimeException( u"message too large for URP"_ustr); } Marshal::write32(&header, static_cast< sal_uInt32 >(buffer.size())); Marshal::write32(&header, 1); assert(!buffer.empty()); unsigned char const * p = buffer.data(); std::vector< unsigned char >::size_type n = buffer.size(); assert(header.size() <= SAL_MAX_INT32); /*static_*/assert(SAL_MAX_INT32 <= std::numeric_limits::max()); std::size_t k = SAL_MAX_INT32 - header.size(); if (n < k) { k = n; } css::uno::Sequence s(header.size() + k); assert(!header.empty()); std::memcpy(s.getArray(), header.data(), header.size()); for (;;) { std::memcpy(s.getArray() + s.getLength() - k, p, k); try { bridge_->getConnection()->write(s); } catch (const css::io::IOException & e) { css::uno::Any exc(cppu::getCaughtException()); throw css::lang::WrappedTargetRuntimeException( "Binary URP write raised IO exception: " + e.Message, css::uno::Reference< css::uno::XInterface >(), exc); } n -= k; if (n == 0) { break; } p += k; k = SAL_MAX_INT32; if (n < k) { k = n; } s.realloc(k); } } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */