377 lines
12 KiB
C++
377 lines
12 KiB
C++
/* -*- 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/.
|
|
*/
|
|
|
|
#include <algorithm>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <officecfg/Office/Impress.hxx>
|
|
#include <officecfg/Office/Security.hxx>
|
|
|
|
#include <com/sun/star/container/XNameAccess.hpp>
|
|
#include <com/sun/star/container/XNameContainer.hpp>
|
|
#include <com/sun/star/uno/Sequence.hxx>
|
|
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
|
|
|
|
#include <comphelper/processfactory.hxx>
|
|
#include <comphelper/configuration.hxx>
|
|
#include <comphelper/sequence.hxx>
|
|
#include <sal/log.hxx>
|
|
#include <vcl/svapp.hxx>
|
|
#include <osl/socket.hxx>
|
|
|
|
#include <sddll.hxx>
|
|
|
|
#include "DiscoveryService.hxx"
|
|
#include "Listener.hxx"
|
|
#include <RemoteServer.hxx>
|
|
#include "BluetoothServer.hxx"
|
|
#include "Communicator.hxx"
|
|
#include "BufferedStreamSocket.hxx"
|
|
|
|
using namespace sd;
|
|
using namespace ::com::sun::star;
|
|
using namespace ::com::sun::star::uno;
|
|
using namespace ::com::sun::star::container;
|
|
using namespace ::com::sun::star::lang;
|
|
using namespace ::osl;
|
|
using namespace ::comphelper;
|
|
|
|
namespace sd {
|
|
/**
|
|
* Used to keep track of clients that have attempted to connect, but haven't
|
|
* yet been approved.
|
|
*/
|
|
struct ClientInfoInternal:
|
|
ClientInfo
|
|
{
|
|
BufferedStreamSocket *mpStreamSocket;
|
|
OUString mPin;
|
|
|
|
ClientInfoInternal( const OUString& rName,
|
|
BufferedStreamSocket *pSocket,
|
|
OUString aPin ):
|
|
ClientInfo( rName, false ),
|
|
mpStreamSocket( pSocket ),
|
|
mPin(std::move( aPin )) {}
|
|
};
|
|
}
|
|
|
|
IPRemoteServer::IPRemoteServer()
|
|
: Thread("IPRemoteServerThread")
|
|
{
|
|
SAL_INFO("sdremote", "Instantiated IPRemoteServer");
|
|
}
|
|
|
|
IPRemoteServer::~IPRemoteServer()
|
|
{
|
|
}
|
|
|
|
void IPRemoteServer::execute()
|
|
{
|
|
SAL_INFO("sdremote", "IPRemoteServer::execute called");
|
|
osl::SocketAddr aAddr( u"0.0.0.0"_ustr, PORT );
|
|
if ( !mSocket.bind( aAddr ) )
|
|
{
|
|
SAL_WARN( "sdremote", "bind failed" << mSocket.getErrorAsString() );
|
|
spServer = nullptr;
|
|
return;
|
|
}
|
|
|
|
if ( !mSocket.listen(3) )
|
|
{
|
|
SAL_WARN( "sdremote", "listen failed" << mSocket.getErrorAsString() );
|
|
spServer = nullptr;
|
|
return;
|
|
}
|
|
while ( true )
|
|
{
|
|
StreamSocket aSocket;
|
|
SAL_INFO( "sdremote", "waiting on accept" );
|
|
if ( mSocket.acceptConnection( aSocket ) == osl_Socket_Error )
|
|
{
|
|
SAL_WARN( "sdremote", "accept failed" << mSocket.getErrorAsString() );
|
|
break; // Closed, or other issue.
|
|
}
|
|
BufferedStreamSocket *pSocket = new BufferedStreamSocket( aSocket);
|
|
handleAcceptedConnection( pSocket );
|
|
}
|
|
SAL_INFO("sdremote", "shutting down IPRemoteServer");
|
|
spServer = nullptr; // Object is destroyed when Thread::execute() ends.
|
|
}
|
|
|
|
void IPRemoteServer::handleAcceptedConnection( BufferedStreamSocket *pSocket )
|
|
{
|
|
OString aLine;
|
|
if ( ! ( pSocket->readLine( aLine)
|
|
&& aLine == "LO_SERVER_CLIENT_PAIR"
|
|
&& pSocket->readLine( aLine ) ) )
|
|
{
|
|
SAL_INFO( "sdremote", "client failed to send LO_SERVER_CLIENT_PAIR, ignoring" );
|
|
delete pSocket;
|
|
return;
|
|
}
|
|
|
|
OString aName( aLine );
|
|
|
|
if ( ! pSocket->readLine( aLine ) )
|
|
{
|
|
delete pSocket;
|
|
return;
|
|
}
|
|
OString aPin( aLine );
|
|
|
|
SocketAddr aClientAddr;
|
|
pSocket->getPeerAddr( aClientAddr );
|
|
|
|
do
|
|
{
|
|
// Read off any additional non-empty lines
|
|
// We know that we at least have the empty termination line to read.
|
|
if ( ! pSocket->readLine( aLine ) ) {
|
|
delete pSocket;
|
|
return;
|
|
}
|
|
}
|
|
while ( aLine.getLength() > 0 );
|
|
|
|
MutexGuard aGuard(RemoteServer::sDataMutex);
|
|
std::shared_ptr< ClientInfoInternal > pClient =
|
|
std::make_shared<ClientInfoInternal>(
|
|
OStringToOUString( aName, RTL_TEXTENCODING_UTF8 ),
|
|
pSocket, OStringToOUString( aPin, RTL_TEXTENCODING_UTF8 ) );
|
|
mAvailableClients.push_back( pClient );
|
|
|
|
// Check if we already have this server.
|
|
Reference< XNameAccess > const xConfig = officecfg::Office::Impress::Misc::AuthorisedRemotes::get();
|
|
const Sequence< OUString > aNames = xConfig->getElementNames();
|
|
for ( const auto& rName : aNames )
|
|
{
|
|
if ( rName == pClient->mName )
|
|
{
|
|
Reference<XNameAccess> xSetItem( xConfig->getByName(rName), UNO_QUERY );
|
|
Any axPin(xSetItem->getByName(u"PIN"_ustr));
|
|
OUString sPin;
|
|
axPin >>= sPin;
|
|
|
|
if ( sPin == pClient->mPin ) {
|
|
SAL_INFO( "sdremote", "client found on validated list -- connecting" );
|
|
connectClient( pClient, sPin );
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Pin not found so inform the client.
|
|
SAL_INFO( "sdremote", "client not found on validated list" );
|
|
pSocket->write( "LO_SERVER_VALIDATING_PIN\n\n",
|
|
strlen( "LO_SERVER_VALIDATING_PIN\n\n" ) );
|
|
}
|
|
|
|
IPRemoteServer *sd::IPRemoteServer::spServer = nullptr;
|
|
::osl::Mutex sd::RemoteServer::sDataMutex;
|
|
::std::vector<Communicator*> sd::RemoteServer::sCommunicators;
|
|
|
|
void IPRemoteServer::setup()
|
|
{
|
|
if (spServer)
|
|
return;
|
|
|
|
spServer = new IPRemoteServer();
|
|
spServer->launch();
|
|
}
|
|
|
|
void RemoteServer::presentationStarted( const css::uno::Reference<
|
|
css::presentation::XSlideShowController > &rController )
|
|
{
|
|
// note this can be invoked even when there is no IPRemoteServer instance
|
|
// but there are communicators belonging to a BluetoothServer
|
|
MutexGuard aGuard( sDataMutex );
|
|
for ( const auto& rpCommunicator : sCommunicators )
|
|
{
|
|
rpCommunicator->presentationStarted( rController );
|
|
}
|
|
}
|
|
void RemoteServer::presentationStopped()
|
|
{
|
|
MutexGuard aGuard( sDataMutex );
|
|
for ( const auto& rpCommunicator : sCommunicators )
|
|
{
|
|
rpCommunicator->disposeListener();
|
|
}
|
|
}
|
|
|
|
void RemoteServer::removeCommunicator( Communicator const * mCommunicator )
|
|
{
|
|
MutexGuard aGuard( sDataMutex );
|
|
auto aIt = std::find(sCommunicators.begin(), sCommunicators.end(), mCommunicator);
|
|
if (aIt != sCommunicators.end())
|
|
sCommunicators.erase( aIt );
|
|
}
|
|
|
|
std::vector<std::shared_ptr<ClientInfo>> IPRemoteServer::getClients()
|
|
{
|
|
SAL_INFO( "sdremote", "IPRemoteServer::getClients() called" );
|
|
std::vector< std::shared_ptr< ClientInfo > > aClients;
|
|
if ( spServer )
|
|
{
|
|
MutexGuard aGuard(RemoteServer::sDataMutex);
|
|
aClients.assign( spServer->mAvailableClients.begin(),
|
|
spServer->mAvailableClients.end() );
|
|
}
|
|
else
|
|
{
|
|
SAL_INFO( "sdremote", "No remote server instance => no remote clients" );
|
|
}
|
|
// We also need to provide authorised clients (no matter whether or not
|
|
// they are actually available), so that they can be de-authorised if
|
|
// necessary. We specifically want these to be at the end of the list
|
|
// since the user is more likely to be trying to connect a new remote
|
|
// than removing an existing remote.
|
|
// We can also be sure that pre-authorised clients will not be on the
|
|
// available clients list, as they get automatically connected if seen.
|
|
// TODO: we should probably add some sort of extra labelling to mark
|
|
// authorised AND connected client.
|
|
Reference< XNameAccess > const xConfig = officecfg::Office::Impress::Misc::AuthorisedRemotes::get();
|
|
const Sequence< OUString > aNames = xConfig->getElementNames();
|
|
std::transform(aNames.begin(), aNames.end(), std::back_inserter(aClients),
|
|
[](const OUString& rName) -> std::shared_ptr<ClientInfo> {
|
|
return std::make_shared<ClientInfo>(rName, true); });
|
|
|
|
return aClients;
|
|
}
|
|
|
|
bool IPRemoteServer::connectClient(const std::shared_ptr<ClientInfo>& pClient, std::u16string_view aPin)
|
|
{
|
|
SAL_INFO("sdremote", "IPRemoteServer::connectClient called");
|
|
if ( !spServer )
|
|
return false;
|
|
|
|
ClientInfoInternal* apClient = dynamic_cast< ClientInfoInternal* >( pClient.get() );
|
|
if ( !apClient )
|
|
// could happen if we try to "connect" an already authorised client
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( apClient->mPin == aPin )
|
|
{
|
|
// Save in settings first
|
|
std::shared_ptr< ConfigurationChanges > aChanges = ConfigurationChanges::create();
|
|
Reference< XNameContainer > const xConfig = officecfg::Office::Impress::Misc::AuthorisedRemotes::get( aChanges );
|
|
|
|
Reference<XSingleServiceFactory> xChildFactory (
|
|
xConfig, UNO_QUERY);
|
|
Reference<XNameReplace> xChild( xChildFactory->createInstance(), UNO_QUERY);
|
|
Any aValue;
|
|
if (xChild.is())
|
|
{
|
|
// Check whether the client is already saved
|
|
Sequence< OUString > aNames = xConfig->getElementNames();
|
|
if (comphelper::findValue(aNames, apClient->mName) != -1)
|
|
xConfig->replaceByName( apClient->mName, Any( xChild ) );
|
|
else
|
|
xConfig->insertByName( apClient->mName, Any( xChild ) );
|
|
aValue <<= apClient->mPin;
|
|
xChild->replaceByName(u"PIN"_ustr, aValue);
|
|
aChanges->commit();
|
|
}
|
|
|
|
Communicator* pCommunicator = new Communicator( std::unique_ptr<IBluetoothSocket>(apClient->mpStreamSocket) );
|
|
MutexGuard aGuard(RemoteServer::sDataMutex);
|
|
|
|
RemoteServer::sCommunicators.push_back( pCommunicator );
|
|
|
|
auto aIt = std::find(spServer->mAvailableClients.begin(), spServer->mAvailableClients.end(), pClient);
|
|
if (aIt != spServer->mAvailableClients.end())
|
|
spServer->mAvailableClients.erase( aIt );
|
|
pCommunicator->launch();
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void IPRemoteServer::deauthoriseClient(const std::shared_ptr<ClientInfo>& pClient)
|
|
{
|
|
// TODO: we probably want to forcefully disconnect at this point too?
|
|
// But possibly via a separate function to allow just disconnecting from
|
|
// the UI.
|
|
|
|
SAL_INFO("sdremote", "IPRemoteServer::deauthoriseClient called");
|
|
|
|
if ( !pClient->mbIsAlreadyAuthorised )
|
|
// We can't remove unauthorised clients from the authorised list...
|
|
{
|
|
return;
|
|
}
|
|
|
|
std::shared_ptr< ConfigurationChanges > aChanges = ConfigurationChanges::create();
|
|
Reference< XNameContainer > const xConfig =
|
|
officecfg::Office::Impress::Misc::AuthorisedRemotes::get( aChanges );
|
|
|
|
xConfig->removeByName( pClient->mName );
|
|
aChanges->commit();
|
|
}
|
|
|
|
void SdDLL::RegisterRemotes()
|
|
{
|
|
SAL_INFO( "sdremote", "SdDLL::RegisterRemotes called" );
|
|
|
|
// The remote server is likely of no use in headless mode. And as only
|
|
// one instance of the server can actually own the appropriate ports its
|
|
// probably best to not even try to do so from our headless instance
|
|
// (i.e. as to avoid blocking expected usage).
|
|
// It could perhaps be argued that we would still need the remote
|
|
// server for tiled rendering of presentations, but even then this
|
|
// implementation would not be of much use, i.e. would be controlling
|
|
// the purely imaginary headless presentation -- instead we'd need
|
|
// to have some sort of mechanism of plugging in our tiled rendering
|
|
// client to be controlled by the remote server, or provide an
|
|
// alternative implementation.
|
|
if ( Application::IsHeadlessModeEnabled() )
|
|
return;
|
|
|
|
if ( !officecfg::Office::Impress::Misc::Start::EnableSdremote::get() )
|
|
return;
|
|
|
|
#ifdef ENABLE_SDREMOTE_BLUETOOTH
|
|
sd::BluetoothServer::setup( &RemoteServer::sCommunicators );
|
|
#endif
|
|
|
|
if (!officecfg::Office::Security::Net::AllowInsecureImpressRemoteWiFi::get())
|
|
{
|
|
SAL_WARN("desktop", "Impress remote WiFi is disabled by configuration");
|
|
return;
|
|
}
|
|
|
|
// this is the IP/WiFi server
|
|
sd::IPRemoteServer::setup();
|
|
// assumption is that BluetoothServer doesn't need DiscoveryService
|
|
sd::DiscoveryService::setup();
|
|
}
|
|
|
|
void RemoteServer::ensureDiscoverable()
|
|
{
|
|
// FIXME: we could also enable listening on our WiFi
|
|
// socket here to significantly reduce the attack surface.
|
|
#ifdef ENABLE_SDREMOTE_BLUETOOTH
|
|
BluetoothServer::ensureDiscoverable();
|
|
#endif
|
|
}
|
|
|
|
void RemoteServer::restoreDiscoverable()
|
|
{
|
|
#ifdef ENABLE_SDREMOTE_BLUETOOTH
|
|
BluetoothServer::restoreDiscoverable();
|
|
#endif
|
|
}
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|