1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <cassert>
23 #include <cstddef>
24 #include <cstring>
25 #include <exception>
26 #include <limits>
27 #include <vector>
28 
29 #include <com/sun/star/connection/XConnection.hpp>
30 #include <com/sun/star/io/IOException.hpp>
31 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
32 #include <com/sun/star/uno/XCurrentContext.hpp>
33 #include <cppuhelper/exc_hlp.hxx>
34 #include <osl/mutex.hxx>
35 #include <sal/log.hxx>
36 #include <uno/dispatcher.hxx>
37 
38 #include "binaryany.hxx"
39 #include "bridge.hxx"
40 #include "currentcontext.hxx"
41 #include "specialfunctionids.hxx"
42 #include "writer.hxx"
43 
44 namespace binaryurp {
45 
Item()46 Writer::Item::Item()
47     : request(false)
48     , setter(false)
49     , exception(false)
50     , setCurrentContextMode(false)
51 {}
52 
Item(rtl::ByteSequence const & theTid,OUString const & theOid,css::uno::TypeDescription const & theType,css::uno::TypeDescription const & theMember,std::vector<BinaryAny> const & inArguments,css::uno::UnoInterfaceReference const & theCurrentContext)53 Writer::Item::Item(
54     rtl::ByteSequence const & theTid, OUString const & theOid,
55     css::uno::TypeDescription const & theType,
56     css::uno::TypeDescription const & theMember,
57     std::vector< BinaryAny > const & inArguments,
58     css::uno::UnoInterfaceReference const & theCurrentContext):
59     request(true), tid(theTid), oid(theOid), type(theType), member(theMember),
60     setter(false), arguments(inArguments), exception(false),
61     currentContext(theCurrentContext), setCurrentContextMode(false)
62 {}
63 
Item(rtl::ByteSequence const & theTid,css::uno::TypeDescription const & theMember,bool theSetter,bool theException,BinaryAny const & theReturnValue,std::vector<BinaryAny> const & outArguments,bool theSetCurrentContextMode)64 Writer::Item::Item(
65     rtl::ByteSequence const & theTid,
66     css::uno::TypeDescription const & theMember, bool theSetter,
67     bool theException, BinaryAny const & theReturnValue,
68     std::vector< BinaryAny > const & outArguments,
69     bool theSetCurrentContextMode):
70     request(false), tid(theTid), member(theMember), setter(theSetter),
71     arguments(outArguments), exception(theException),
72     returnValue(theReturnValue), setCurrentContextMode(theSetCurrentContextMode)
73 {}
74 
Writer(rtl::Reference<Bridge> const & bridge)75 Writer::Writer(rtl::Reference< Bridge > const  & bridge):
76     Thread("binaryurpWriter"), bridge_(bridge), marshal_(bridge, state_),
77     stop_(false)
78 {
79     assert(bridge.is());
80 }
81 
sendDirectRequest(rtl::ByteSequence const & tid,OUString const & oid,css::uno::TypeDescription const & type,css::uno::TypeDescription const & member,std::vector<BinaryAny> const & inArguments)82 void Writer::sendDirectRequest(
83     rtl::ByteSequence const & tid, OUString const & oid,
84     css::uno::TypeDescription const & type,
85     css::uno::TypeDescription const & member,
86     std::vector< BinaryAny > const & inArguments)
87 {
88     assert(!unblocked_.check());
89     sendRequest(
90         tid, oid, type, member, inArguments, false,
91         css::uno::UnoInterfaceReference());
92 }
93 
sendDirectReply(rtl::ByteSequence const & tid,css::uno::TypeDescription const & member,bool exception,BinaryAny const & returnValue,std::vector<BinaryAny> const & outArguments)94 void Writer::sendDirectReply(
95     rtl::ByteSequence const & tid, css::uno::TypeDescription const & member,
96     bool exception, BinaryAny const & returnValue,
97     std::vector< BinaryAny > const & outArguments)
98 {
99     assert(!unblocked_.check());
100     sendReply(tid, member, false, exception, returnValue,outArguments);
101 }
102 
queueRequest(rtl::ByteSequence const & tid,OUString const & oid,css::uno::TypeDescription const & type,css::uno::TypeDescription const & member,std::vector<BinaryAny> const & inArguments)103 void Writer::queueRequest(
104     rtl::ByteSequence const & tid, OUString const & oid,
105     css::uno::TypeDescription const & type,
106     css::uno::TypeDescription const & member,
107     std::vector< BinaryAny > const & inArguments)
108 {
109     css::uno::UnoInterfaceReference cc(current_context::get());
110     osl::MutexGuard g(mutex_);
111     queue_.emplace_back(tid, oid, type, member, inArguments, cc);
112     items_.set();
113 }
114 
queueReply(rtl::ByteSequence const & tid,com::sun::star::uno::TypeDescription const & member,bool setter,bool exception,BinaryAny const & returnValue,std::vector<BinaryAny> const & outArguments,bool setCurrentContextMode)115 void Writer::queueReply(
116     rtl::ByteSequence const & tid,
117     com::sun::star::uno::TypeDescription const & member, bool setter,
118     bool exception, BinaryAny const & returnValue,
119     std::vector< BinaryAny > const & outArguments, bool setCurrentContextMode)
120 {
121     osl::MutexGuard g(mutex_);
122     queue_.emplace_back(
123             tid, member, setter, exception, returnValue, outArguments,
124             setCurrentContextMode);
125     items_.set();
126 }
127 
unblock()128 void Writer::unblock() {
129     // Assumes that osl::Condition::set works as a memory barrier, so that
130     // changes made by preceding sendDirectRequest/Reply calls are visible to
131     // subsequent sendRequest/Reply calls:
132     unblocked_.set();
133 }
134 
stop()135 void Writer::stop() {
136     {
137         osl::MutexGuard g(mutex_);
138         stop_ = true;
139     }
140     unblocked_.set();
141     items_.set();
142 }
143 
~Writer()144 Writer::~Writer() {}
145 
execute()146 void Writer::execute() {
147     try {
148         unblocked_.wait();
149         for (;;) {
150             items_.wait();
151             Item item;
152             {
153                 osl::MutexGuard g(mutex_);
154                 if (stop_) {
155                     return;
156                 }
157                 assert(!queue_.empty());
158                 item = queue_.front();
159                 queue_.pop_front();
160                 if (queue_.empty()) {
161                     items_.reset();
162                 }
163             }
164             if (item.request) {
165                 sendRequest(
166                     item.tid, item.oid, item.type, item.member, item.arguments,
167                     (item.oid != "UrpProtocolProperties" &&
168                      !item.member.equals(
169                          css::uno::TypeDescription(
170                              "com.sun.star.uno.XInterface::release")) &&
171                      bridge_->isCurrentContextMode()),
172                     item.currentContext);
173             } else {
174                 sendReply(
175                     item.tid, item.member, item.setter, item.exception,
176                     item.returnValue, item.arguments);
177                 if (item.setCurrentContextMode) {
178                     bridge_->setCurrentContextMode();
179                 }
180             }
181         }
182     } catch (const css::uno::Exception & e) {
183         SAL_INFO("binaryurp", "caught " << e);
184     } catch (const std::exception & e) {
185         SAL_INFO("binaryurp", "caught C++ exception " << e.what());
186     }
187     bridge_->terminate(false);
188     bridge_.clear();
189 }
190 
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)191 void Writer::sendRequest(
192     rtl::ByteSequence const & tid, OUString const & oid,
193     css::uno::TypeDescription const & type,
194     css::uno::TypeDescription const & member,
195     std::vector< BinaryAny > const & inArguments, bool currentContextMode,
196     css::uno::UnoInterfaceReference const & currentContext)
197 {
198     assert(tid.getLength() != 0);
199     assert(!oid.isEmpty());
200     assert(member.is());
201     css::uno::TypeDescription t(type);
202     sal_Int32 functionId = 0;
203     bool bForceSynchronous = false;
204     member.makeComplete();
205     switch (member.get()->eTypeClass) {
206     case typelib_TypeClass_INTERFACE_ATTRIBUTE:
207         {
208             typelib_InterfaceAttributeTypeDescription * atd =
209                 reinterpret_cast< typelib_InterfaceAttributeTypeDescription * >(
210                     member.get());
211             assert(atd->pInterface != nullptr);
212             if (!t.is()) {
213                 t = css::uno::TypeDescription(&atd->pInterface->aBase);
214             }
215             t.makeComplete();
216             functionId = atd->pInterface->pMapMemberIndexToFunctionIndex[
217                 atd->aBase.nPosition];
218             if (!inArguments.empty()) { // setter
219                 ++functionId;
220             }
221             break;
222         }
223     case typelib_TypeClass_INTERFACE_METHOD:
224         {
225             typelib_InterfaceMethodTypeDescription * mtd =
226                 reinterpret_cast< typelib_InterfaceMethodTypeDescription * >(
227                     member.get());
228             assert(mtd->pInterface != nullptr);
229             if (!t.is()) {
230                 t = css::uno::TypeDescription(&mtd->pInterface->aBase);
231             }
232             t.makeComplete();
233             functionId = mtd->pInterface->pMapMemberIndexToFunctionIndex[
234                 mtd->aBase.nPosition];
235             bForceSynchronous = mtd->bOneWay &&
236                 functionId != SPECIAL_FUNCTION_ID_RELEASE;
237             break;
238         }
239     default:
240         assert(false); // this cannot happen
241         break;
242     }
243     assert(functionId >= 0);
244     if (functionId > SAL_MAX_UINT16) {
245         throw css::uno::RuntimeException("function ID too large for URP");
246     }
247     std::vector< unsigned char > buf;
248     bool newType = !(lastType_.is() && t.equals(lastType_));
249     bool newOid = oid != lastOid_;
250     bool newTid = tid != lastTid_;
251     if (newType || newOid || newTid || bForceSynchronous || functionId > 0x3FFF)
252         // > 14 bit function ID
253     {
254         Marshal::write8(
255             &buf,
256             (0xC0 | (newType ? 0x20 : 0) | (newOid ? 0x10 : 0) |
257              (newTid ? 0x08 : 0) | (functionId > 0xFF ? 0x04 : 0) |
258              (bForceSynchronous ? 0x01 : 0)));
259             // bit 7: LONGHEADER, bit 6: REQUEST, bit 5: NEWTYPE, bit 4: NEWOID,
260             // bit 3: NEWTID, bit 2: FUNCTIONID16, bit 0: MOREFLAGS
261         if (bForceSynchronous) {
262             Marshal::write8(&buf, 0xC0); // bit 7: MUSTREPLY, bit 6: SYNCHRONOUS
263         }
264         if (functionId <= 0xFF) {
265             Marshal::write8(&buf, static_cast< sal_uInt8 >(functionId));
266         } else {
267             Marshal::write16(&buf, static_cast< sal_uInt16 >(functionId));
268         }
269         if (newType) {
270             marshal_.writeType(&buf, t);
271         }
272         if (newOid) {
273             marshal_.writeOid(&buf, oid);
274         }
275         if (newTid) {
276             marshal_.writeTid(&buf, tid);
277         }
278     } else if (functionId <= 0x3F) { // <= 6 bit function ID
279         Marshal::write8(&buf, static_cast< sal_uInt8 >(functionId));
280             // bit 7: !LONGHEADER, bit 6: !FUNCTIONID14
281     } else {
282         Marshal::write8(
283             &buf, static_cast< sal_uInt8 >(0x40 | (functionId >> 8)));
284             // bit 7: !LONGHEADER, bit 6: FUNCTIONID14
285         Marshal::write8(&buf, functionId & 0xFF);
286     }
287     if (currentContextMode) {
288         css::uno::UnoInterfaceReference cc(currentContext);
289         marshal_.writeValue(
290             &buf,
291             css::uno::TypeDescription(
292                 cppu::UnoType<
293                     css::uno::Reference< css::uno::XCurrentContext > >::get()),
294             BinaryAny(
295                 css::uno::TypeDescription(
296                     cppu::UnoType<
297                         css::uno::Reference<
298                             css::uno::XCurrentContext > >::get()),
299                 &cc.m_pUnoI));
300     }
301     switch (member.get()->eTypeClass) {
302     case typelib_TypeClass_INTERFACE_ATTRIBUTE:
303         if (!inArguments.empty()) { // setter
304             assert(inArguments.size() == 1);
305             marshal_.writeValue(
306                 &buf,
307                 css::uno::TypeDescription(
308                     reinterpret_cast<
309                         typelib_InterfaceAttributeTypeDescription * >(
310                             member.get())->
311                     pAttributeTypeRef),
312                 inArguments.front());
313         }
314         break;
315     case typelib_TypeClass_INTERFACE_METHOD:
316         {
317             typelib_InterfaceMethodTypeDescription * mtd =
318                 reinterpret_cast< typelib_InterfaceMethodTypeDescription * >(
319                     member.get());
320             std::vector< BinaryAny >::const_iterator i(inArguments.begin());
321             for (sal_Int32 j = 0; j != mtd->nParams; ++j) {
322                 if (mtd->pParams[j].bIn) {
323                     marshal_.writeValue(
324                         &buf,
325                         css::uno::TypeDescription(mtd->pParams[j].pTypeRef),
326                         *i++);
327                 }
328             }
329             assert(i == inArguments.end());
330             break;
331         }
332     default:
333         assert(false); // this cannot happen
334         break;
335     }
336     sendMessage(buf);
337     lastType_ = t;
338     lastOid_ = oid;
339     lastTid_ = tid;
340 }
341 
sendReply(rtl::ByteSequence const & tid,com::sun::star::uno::TypeDescription const & member,bool setter,bool exception,BinaryAny const & returnValue,std::vector<BinaryAny> const & outArguments)342 void Writer::sendReply(
343     rtl::ByteSequence const & tid,
344     com::sun::star::uno::TypeDescription const & member, bool setter,
345     bool exception, BinaryAny const & returnValue,
346     std::vector< BinaryAny > const & outArguments)
347 {
348     assert(tid.getLength() != 0);
349     assert(member.is());
350     assert(member.get()->bComplete);
351     std::vector< unsigned char > buf;
352     bool newTid = tid != lastTid_;
353     Marshal::write8(&buf, 0x80 | (exception ? 0x20 : 0) | (newTid ? 0x08 : 0));
354         // bit 7: LONGHEADER; bit 6: !REQUEST; bit 5: EXCEPTION; bit 3: NEWTID
355     if (newTid) {
356         marshal_.writeTid(&buf, tid);
357     }
358     if (exception) {
359         marshal_.writeValue(
360             &buf,
361             css::uno::TypeDescription(cppu::UnoType< css::uno::Any >::get()),
362             returnValue);
363     } else {
364         switch (member.get()->eTypeClass) {
365         case typelib_TypeClass_INTERFACE_ATTRIBUTE:
366             if (!setter) {
367                 marshal_.writeValue(
368                     &buf,
369                     css::uno::TypeDescription(
370                         reinterpret_cast<
371                             typelib_InterfaceAttributeTypeDescription * >(
372                                 member.get())->
373                         pAttributeTypeRef),
374                     returnValue);
375             }
376             break;
377         case typelib_TypeClass_INTERFACE_METHOD:
378             {
379                 typelib_InterfaceMethodTypeDescription * mtd =
380                     reinterpret_cast<
381                         typelib_InterfaceMethodTypeDescription * >(
382                             member.get());
383                 marshal_.writeValue(
384                     &buf, css::uno::TypeDescription(mtd->pReturnTypeRef),
385                     returnValue);
386                 std::vector< BinaryAny >::const_iterator i(
387                     outArguments.begin());
388                 for (sal_Int32 j = 0; j != mtd->nParams; ++j) {
389                     if (mtd->pParams[j].bOut) {
390                         marshal_.writeValue(
391                             &buf,
392                             css::uno::TypeDescription(mtd->pParams[j].pTypeRef),
393                             *i++);
394                     }
395                 }
396                 assert(i == outArguments.end());
397                 break;
398             }
399         default:
400             assert(false); // this cannot happen
401             break;
402         }
403     }
404     sendMessage(buf);
405     lastTid_ = tid;
406     bridge_->decrementCalls();
407 }
408 
sendMessage(std::vector<unsigned char> const & buffer)409 void Writer::sendMessage(std::vector< unsigned char > const & buffer) {
410     std::vector< unsigned char > header;
411     if (buffer.size() > SAL_MAX_UINT32) {
412         throw css::uno::RuntimeException(
413             "message too large for URP");
414     }
415     Marshal::write32(&header, static_cast< sal_uInt32 >(buffer.size()));
416     Marshal::write32(&header, 1);
417     assert(!buffer.empty());
418     unsigned char const * p = buffer.data();
419     std::vector< unsigned char >::size_type n = buffer.size();
420     assert(header.size() <= SAL_MAX_INT32);
421     /*static_*/assert(SAL_MAX_INT32 <= std::numeric_limits<std::size_t>::max());
422     std::size_t k = SAL_MAX_INT32 - header.size();
423     if (n < k) {
424         k = n;
425     }
426     css::uno::Sequence<sal_Int8> s(header.size() + k);
427     assert(!header.empty());
428     std::memcpy(s.getArray(), header.data(), header.size());
429     for (;;) {
430         std::memcpy(s.getArray() + s.getLength() - k, p, k);
431         try {
432             bridge_->getConnection()->write(s);
433         } catch (const css::io::IOException & e) {
434             css::uno::Any exc(cppu::getCaughtException());
435             throw css::lang::WrappedTargetRuntimeException(
436                 "Binary URP write raised IO exception: " + e.Message,
437                 css::uno::Reference< css::uno::XInterface >(), exc);
438         }
439         n -= k;
440         if (n == 0) {
441             break;
442         }
443         p += k;
444         k = SAL_MAX_INT32;
445         if (n < k) {
446             k = n;
447         }
448         s.realloc(k);
449     }
450 }
451 
452 }
453 
454 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
455