LibreOffice Module sd (master) 1
Server.cxx
Go to the documentation of this file.
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
10#include <algorithm>
11#include <utility>
12#include <vector>
13
14#include <officecfg/Office/Impress.hxx>
15
16#include <com/sun/star/container/XNameAccess.hpp>
17#include <com/sun/star/container/XNameContainer.hpp>
18#include <com/sun/star/uno/Sequence.hxx>
19#include <com/sun/star/lang/XSingleServiceFactory.hpp>
20
24#include <sal/log.hxx>
25#include <vcl/svapp.hxx>
26#include <osl/socket.hxx>
27
28#include <sddll.hxx>
29
30#include "DiscoveryService.hxx"
31#include "Listener.hxx"
32#include <RemoteServer.hxx>
33#include "BluetoothServer.hxx"
34#include "Communicator.hxx"
36
37using namespace sd;
38using namespace ::com::sun::star;
39using namespace ::com::sun::star::uno;
40using namespace ::com::sun::star::beans;
41using namespace ::com::sun::star::container;
42using namespace ::com::sun::star::lang;
43using namespace ::osl;
44using namespace ::comphelper;
45
46namespace sd {
53 {
55 OUString mPin;
56
57 ClientInfoInternal( const OUString& rName,
58 BufferedStreamSocket *pSocket,
59 OUString aPin ):
60 ClientInfo( rName, false ),
61 mpStreamSocket( pSocket ),
62 mPin(std::move( aPin )) {}
63 };
64}
65
66RemoteServer::RemoteServer() :
67 Thread( "RemoteServerThread" )
68{
69 SAL_INFO( "sdremote", "Instantiated RemoteServer" );
70}
71
73{
74}
75
77{
78 SAL_INFO( "sdremote", "RemoteServer::execute called" );
79 osl::SocketAddr aAddr( "0.0.0.0", PORT );
80 if ( !mSocket.bind( aAddr ) )
81 {
82 SAL_WARN( "sdremote", "bind failed" << mSocket.getErrorAsString() );
83 spServer = nullptr;
84 return;
85 }
86
87 if ( !mSocket.listen(3) )
88 {
89 SAL_WARN( "sdremote", "listen failed" << mSocket.getErrorAsString() );
90 spServer = nullptr;
91 return;
92 }
93 while ( true )
94 {
95 StreamSocket aSocket;
96 SAL_INFO( "sdremote", "waiting on accept" );
97 if ( mSocket.acceptConnection( aSocket ) == osl_Socket_Error )
98 {
99 SAL_WARN( "sdremote", "accept failed" << mSocket.getErrorAsString() );
100 spServer = nullptr;
101 return; // Closed, or other issue.
102 }
103 BufferedStreamSocket *pSocket = new BufferedStreamSocket( aSocket);
104 handleAcceptedConnection( pSocket );
105 }
106 SAL_INFO( "sdremote", "shutting down RemoteServer" );
107 spServer = nullptr; // Object is destroyed when Thread::execute() ends.
108}
109
111{
112 OString aLine;
113 if ( ! ( pSocket->readLine( aLine)
114 && aLine == "LO_SERVER_CLIENT_PAIR"
115 && pSocket->readLine( aLine ) ) )
116 {
117 SAL_INFO( "sdremote", "client failed to send LO_SERVER_CLIENT_PAIR, ignoring" );
118 delete pSocket;
119 return;
120 }
121
122 OString aName( aLine );
123
124 if ( ! pSocket->readLine( aLine ) )
125 {
126 delete pSocket;
127 return;
128 }
129 OString aPin( aLine );
130
131 SocketAddr aClientAddr;
132 pSocket->getPeerAddr( aClientAddr );
133
134 do
135 {
136 // Read off any additional non-empty lines
137 // We know that we at least have the empty termination line to read.
138 if ( ! pSocket->readLine( aLine ) ) {
139 delete pSocket;
140 return;
141 }
142 }
143 while ( aLine.getLength() > 0 );
144
145 MutexGuard aGuard( sDataMutex );
146 std::shared_ptr< ClientInfoInternal > pClient =
147 std::make_shared<ClientInfoInternal>(
148 OStringToOUString( aName, RTL_TEXTENCODING_UTF8 ),
149 pSocket, OStringToOUString( aPin, RTL_TEXTENCODING_UTF8 ) );
150 mAvailableClients.push_back( pClient );
151
152 // Check if we already have this server.
153 Reference< XNameAccess > const xConfig = officecfg::Office::Impress::Misc::AuthorisedRemotes::get();
154 const Sequence< OUString > aNames = xConfig->getElementNames();
155 for ( const auto& rName : aNames )
156 {
157 if ( rName == pClient->mName )
158 {
159 Reference<XNameAccess> xSetItem( xConfig->getByName(rName), UNO_QUERY );
160 Any axPin(xSetItem->getByName("PIN"));
161 OUString sPin;
162 axPin >>= sPin;
163
164 if ( sPin == pClient->mPin ) {
165 SAL_INFO( "sdremote", "client found on validated list -- connecting" );
166 connectClient( pClient, sPin );
167 return;
168 }
169 }
170 }
171
172 // Pin not found so inform the client.
173 SAL_INFO( "sdremote", "client not found on validated list" );
174 pSocket->write( "LO_SERVER_VALIDATING_PIN\n\n",
175 strlen( "LO_SERVER_VALIDATING_PIN\n\n" ) );
176}
177
180::std::vector<Communicator*> sd::RemoteServer::sCommunicators;
181
183{
184 if (spServer)
185 return;
186
187 spServer = new RemoteServer();
188 spServer->launch();
189
190#ifdef ENABLE_SDREMOTE_BLUETOOTH
192#endif
193}
194
195void RemoteServer::presentationStarted( const css::uno::Reference<
196 css::presentation::XSlideShowController > &rController )
197{
198 if ( !spServer )
199 return;
200 MutexGuard aGuard( sDataMutex );
201 for ( const auto& rpCommunicator : sCommunicators )
202 {
203 rpCommunicator->presentationStarted( rController );
204 }
205}
207{
208 if ( !spServer )
209 return;
210 MutexGuard aGuard( sDataMutex );
211 for ( const auto& rpCommunicator : sCommunicators )
212 {
213 rpCommunicator->disposeListener();
214 }
215}
216
218{
219 if ( !spServer )
220 return;
221 MutexGuard aGuard( sDataMutex );
222 auto aIt = std::find(sCommunicators.begin(), sCommunicators.end(), mCommunicator);
223 if (aIt != sCommunicators.end())
224 sCommunicators.erase( aIt );
225}
226
227std::vector< std::shared_ptr< ClientInfo > > RemoteServer::getClients()
228{
229 SAL_INFO( "sdremote", "RemoteServer::getClients() called" );
230 std::vector< std::shared_ptr< ClientInfo > > aClients;
231 if ( spServer )
232 {
233 MutexGuard aGuard( sDataMutex );
234 aClients.assign( spServer->mAvailableClients.begin(),
236 }
237 else
238 {
239 SAL_INFO( "sdremote", "No remote server instance => no remote clients" );
240 }
241 // We also need to provide authorised clients (no matter whether or not
242 // they are actually available), so that they can be de-authorised if
243 // necessary. We specifically want these to be at the end of the list
244 // since the user is more likely to be trying to connect a new remote
245 // than removing an existing remote.
246 // We can also be sure that pre-authorised clients will not be on the
247 // available clients list, as they get automatically connected if seen.
248 // TODO: we should probably add some sort of extra labelling to mark
249 // authorised AND connected client.
250 Reference< XNameAccess > const xConfig = officecfg::Office::Impress::Misc::AuthorisedRemotes::get();
251 const Sequence< OUString > aNames = xConfig->getElementNames();
252 std::transform(aNames.begin(), aNames.end(), std::back_inserter(aClients),
253 [](const OUString& rName) -> std::shared_ptr<ClientInfo> {
254 return std::make_shared<ClientInfo>(rName, true); });
255
256 return aClients;
257}
258
259bool RemoteServer::connectClient( const std::shared_ptr< ClientInfo >& pClient, std::u16string_view aPin )
260{
261 SAL_INFO( "sdremote", "RemoteServer::connectClient called" );
262 if ( !spServer )
263 return false;
264
265 ClientInfoInternal* apClient = dynamic_cast< ClientInfoInternal* >( pClient.get() );
266 if ( !apClient )
267 // could happen if we try to "connect" an already authorised client
268 {
269 return false;
270 }
271
272 if ( apClient->mPin == aPin )
273 {
274 // Save in settings first
275 std::shared_ptr< ConfigurationChanges > aChanges = ConfigurationChanges::create();
276 Reference< XNameContainer > const xConfig = officecfg::Office::Impress::Misc::AuthorisedRemotes::get( aChanges );
277
278 Reference<XSingleServiceFactory> xChildFactory (
279 xConfig, UNO_QUERY);
280 Reference<XNameReplace> xChild( xChildFactory->createInstance(), UNO_QUERY);
281 Any aValue;
282 if (xChild.is())
283 {
284 // Check whether the client is already saved
285 Sequence< OUString > aNames = xConfig->getElementNames();
286 if (comphelper::findValue(aNames, apClient->mName) != -1)
287 xConfig->replaceByName( apClient->mName, Any( xChild ) );
288 else
289 xConfig->insertByName( apClient->mName, Any( xChild ) );
290 aValue <<= apClient->mPin;
291 xChild->replaceByName("PIN", aValue);
292 aChanges->commit();
293 }
294
295 Communicator* pCommunicator = new Communicator( std::unique_ptr<IBluetoothSocket>(apClient->mpStreamSocket) );
296 MutexGuard aGuard( sDataMutex );
297
298 sCommunicators.push_back( pCommunicator );
299
300 auto aIt = std::find(spServer->mAvailableClients.begin(), spServer->mAvailableClients.end(), pClient);
301 if (aIt != spServer->mAvailableClients.end())
302 spServer->mAvailableClients.erase( aIt );
303 pCommunicator->launch();
304 return true;
305 }
306 else
307 {
308 return false;
309 }
310}
311
312void RemoteServer::deauthoriseClient( const std::shared_ptr< ClientInfo >& pClient )
313{
314 // TODO: we probably want to forcefully disconnect at this point too?
315 // But possibly via a separate function to allow just disconnecting from
316 // the UI.
317
318 SAL_INFO( "sdremote", "RemoteServer::deauthoriseClient called" );
319
320 if ( !pClient->mbIsAlreadyAuthorised )
321 // We can't remove unauthorised clients from the authorised list...
322 {
323 return;
324 }
325
326 std::shared_ptr< ConfigurationChanges > aChanges = ConfigurationChanges::create();
327 Reference< XNameContainer > const xConfig =
328 officecfg::Office::Impress::Misc::AuthorisedRemotes::get( aChanges );
329
330 xConfig->removeByName( pClient->mName );
331 aChanges->commit();
332}
333
334void SdDLL::RegisterRemotes()
335{
336 SAL_INFO( "sdremote", "SdDLL::RegisterRemotes called" );
337
338 // The remote server is likely of no use in headless mode. And as only
339 // one instance of the server can actually own the appropriate ports its
340 // probably best to not even try to do so from our headless instance
341 // (i.e. as to avoid blocking expected usage).
342 // It could perhaps be argued that we would still need the remote
343 // server for tiled rendering of presentations, but even then this
344 // implementation would not be of much use, i.e. would be controlling
345 // the purely imaginary headless presentation -- instead we'd need
346 // to have some sort of mechanism of plugging in our tiled rendering
347 // client to be controlled by the remote server, or provide an
348 // alternative implementation.
350 return;
351
352 if ( !officecfg::Office::Impress::Misc::Start::EnableSdremote::get() )
353 return;
354
357}
358
360{
361 // FIXME: we could also enable listening on our WiFi
362 // socket here to significantly reduce the attack surface.
363#ifdef ENABLE_SDREMOTE_BLUETOOTH
365#endif
366}
367
369{
370#ifdef ENABLE_SDREMOTE_BLUETOOTH
372#endif
373}
374/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
#define PORT
The port for use for the main communication between LibO and remote control app.
static bool IsHeadlessModeEnabled()
static void restoreDiscoverable()
restore the state of discoverability from before ensureDiscoverable
static void setup(std::vector< Communicator * > *pCommunicators)
static void ensureDiscoverable()
ensure that Bluetooth discoverability is on
[A wrapper for an osl StreamSocket to allow reading lines.
virtual sal_Int32 write(const void *pBuffer, sal_uInt32 n) override
Write a number of bytes.
void getPeerAddr(osl::SocketAddr &)
virtual sal_Int32 readLine(OString &aLine) override
Blocks until a line is read.
Class used for communication with one single client, dealing with all tasks specific to this client.
static SD_DLLPUBLIC bool connectClient(const std::shared_ptr< ClientInfo > &pClient, std::u16string_view aPin)
Definition: Server.cxx:259
static void setup()
Definition: Server.cxx:182
static RemoteServer * spServer
void handleAcceptedConnection(BufferedStreamSocket *pSocket)
Definition: Server.cxx:110
static SD_DLLPUBLIC void restoreDiscoverable()
restore the state of discoverability from before ensureDiscoverable
Definition: Server.cxx:368
::osl::Mutex sDataMutex
static void presentationStopped()
Definition: Server.cxx:206
osl::AcceptorSocket mSocket
static ::std::vector< Communicator * > sCommunicators
static SD_DLLPUBLIC std::vector< std::shared_ptr< ClientInfo > > getClients()
Definition: Server.cxx:227
void execute() override
Definition: Server.cxx:76
static SD_DLLPUBLIC void deauthoriseClient(const std::shared_ptr< ClientInfo > &pClient)
Definition: Server.cxx:312
::std::vector< std::shared_ptr< ClientInfoInternal > > mAvailableClients
static void removeCommunicator(Communicator const *pCommunicator)
Definition: Server.cxx:217
virtual ~RemoteServer() override
Definition: Server.cxx:72
static SD_DLLPUBLIC void ensureDiscoverable()
ensure that discoverability (eg. for Bluetooth) is enabled
Definition: Server.cxx:359
static void presentationStarted(const css::uno::Reference< css::presentation::XSlideShowController > &rController)
Definition: Server.cxx:195
OUString aName
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
sal_Int32 findValue(const css::uno::Sequence< T1 > &_rList, const T2 &_rValue)
Used to keep track of clients that have attempted to connect, but haven't yet been approved.
Definition: Server.cxx:53
ClientInfoInternal(const OUString &rName, BufferedStreamSocket *pSocket, OUString aPin)
Definition: Server.cxx:57
BufferedStreamSocket * mpStreamSocket
Definition: Server.cxx:54