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