french -> French
[kdepim.git] / kleopatra / uiserver / uiserver.cpp
blobe2bf618f7993c6aef62019a0076d31aedc323a21
1 /* -*- mode: c++; c-basic-offset:4 -*-
2 uiserver/uiserver.cpp
4 This file is part of Kleopatra, the KDE keymanager
5 Copyright (c) 2007 Klarälvdalens Datakonsult AB
7 Kleopatra is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 Kleopatra is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 In addition, as a special exception, the copyright holders give
22 permission to link the code of this program with any edition of
23 the Qt library by Trolltech AS, Norway (or with modified versions
24 of Qt that use the same license as Qt), and distribute linked
25 combinations including the two. You must obey the GNU General
26 Public License in all respects for all of the code used other than
27 Qt. If you modify this file, you may extend this exception to
28 your version of the file, but you are not obligated to do so. If
29 you do not wish to do so, delete this exception statement from
30 your version.
33 #include <config-kleopatra.h>
35 #include "uiserver.h"
36 #include "uiserver_p.h"
38 #include "sessiondata.h"
40 #include <utils/detail_p.h>
41 #include <utils/gnupg-helper.h>
43 #include <kleo/stl_util.h>
44 #include <kleo/exception.h>
46 #include <KDebug>
47 #include <KLocalizedString>
49 #include <QTcpSocket>
50 #include <QDir>
51 #include <QEventLoop>
52 #include <QTimer>
53 #include <QFile>
55 #include <boost/range/empty.hpp>
56 #include <boost/bind.hpp>
58 #include <algorithm>
59 #include <cassert>
60 #include <cerrno>
62 using namespace Kleo;
63 using namespace boost;
65 // static
66 void UiServer::setLogStream( FILE * stream ) {
67 assuan_set_assuan_log_stream( stream );
70 UiServer::Private::Private( UiServer * qq )
71 : QTcpServer(),
72 q( qq ),
73 file(),
74 factories(),
75 connections(),
76 suggestedSocketName(),
77 actualSocketName(),
78 cryptoCommandsEnabled( false )
80 #ifndef HAVE_ASSUAN2
81 assuan_set_assuan_err_source( GPG_ERR_SOURCE_DEFAULT );
82 #else
83 assuan_set_gpg_err_source( GPG_ERR_SOURCE_DEFAULT );
84 assuan_sock_init();
85 #endif
88 bool UiServer::Private::isStaleAssuanSocket( const QString& fileName )
90 assuan_context_t ctx = 0;
91 #ifndef HAVE_ASSUAN2
92 const bool error = assuan_socket_connect_ext( &ctx, QFile::encodeName( fileName ).constData(), -1, 0 );
93 #else
94 const bool error = assuan_new( &ctx ) || assuan_socket_connect( ctx, QFile::encodeName( fileName ).constData(), ASSUAN_INVALID_PID, 0 );
95 #endif
96 if ( !error )
97 #ifndef HAVE_ASSUAN2
98 assuan_disconnect( ctx );
99 #else
100 assuan_release( ctx );
101 #endif
102 return error;
105 UiServer::UiServer( const QString & socket, QObject * p )
106 : QObject( p ), d( new Private( this ) )
108 d->suggestedSocketName = d->makeFileName( socket );
111 UiServer::~UiServer() {
112 if ( QFile::exists( d->actualSocketName ) )
113 QFile::remove( d->actualSocketName );
116 bool UiServer::registerCommandFactory( const shared_ptr<AssuanCommandFactory> & cf ) {
117 if ( cf && empty( std::equal_range( d->factories.begin(), d->factories.end(), cf, _detail::ByName<std::less>() ) ) ) {
118 d->factories.push_back( cf );
119 std::inplace_merge( d->factories.begin(), d->factories.end() - 1, d->factories.end(), _detail::ByName<std::less>() );
120 return true;
121 } else {
122 if ( !cf ) {
123 kWarning() << "NULL factory";
124 } else {
125 kWarning() << ( void* )cf.get() << " factory already registered";
128 return false;
132 void UiServer::start() {
133 d->makeListeningSocket();
136 void UiServer::stop() {
138 d->close();
140 if ( d->file.exists() )
141 d->file.remove();
143 if ( isStopped() ) {
144 SessionDataHandler::instance()->clear();
145 emit stopped();
150 void UiServer::enableCryptoCommands( bool on ) {
151 if ( on == d->cryptoCommandsEnabled )
152 return;
153 d->cryptoCommandsEnabled = on;
154 kdtools::for_each( d->connections,
155 boost::bind( &AssuanServerConnection::enableCryptoCommands, _1, on ) );
158 QString UiServer::socketName() const {
159 return d->actualSocketName;
162 bool UiServer::waitForStopped( unsigned int ms ) {
163 if ( isStopped() )
164 return true;
165 QEventLoop loop;
166 QTimer timer;
167 timer.setInterval( ms );
168 timer.setSingleShot( true );
169 connect( &timer, SIGNAL(timeout()), &loop, SLOT(quit()) );
170 connect( this, SIGNAL(stopped()), &loop, SLOT(quit()) );
171 loop.exec();
172 return !timer.isActive();
175 bool UiServer::isStopped() const {
176 return d->connections.empty() && !d->isListening() ;
179 bool UiServer::isStopping() const {
180 return !d->connections.empty() && !d->isListening() ;
183 void UiServer::Private::slotConnectionClosed( Kleo::AssuanServerConnection * conn ) {
184 kDebug() << "UiServer: connection " << ( void* )conn << " closed";
185 connections.erase( std::remove_if( connections.begin(), connections.end(),
186 boost::bind( &boost::shared_ptr<AssuanServerConnection>::get, _1 ) == conn ),
187 connections.end() );
188 if ( q->isStopped() ) {
189 SessionDataHandler::instance()->clear();
190 emit q->stopped();
195 void UiServer::Private::incomingConnection( int fd ) {
196 try {
197 kDebug() << "UiServer: client connect on fd " << fd;
198 #if defined(HAVE_ASSUAN_SOCK_GET_NONCE) || defined(HAVE_ASSUAN2)
199 if ( assuan_sock_check_nonce( (assuan_fd_t)fd, &nonce ) ) {
200 kDebug() << "UiServer: nonce check failed";
201 assuan_sock_close( (assuan_fd_t)fd );
202 return;
204 #endif
205 const shared_ptr<AssuanServerConnection> c( new AssuanServerConnection( (assuan_fd_t)fd, factories ) );
206 connect( c.get(), SIGNAL(closed(Kleo::AssuanServerConnection*)),
207 this, SLOT(slotConnectionClosed(Kleo::AssuanServerConnection*)) );
208 connect( c.get(), SIGNAL(startKeyManagerRequested()),
209 q, SIGNAL(startKeyManagerRequested()), Qt::QueuedConnection );
210 connect( c.get(), SIGNAL(startConfigDialogRequested()),
211 q, SIGNAL(startConfigDialogRequested()), Qt::QueuedConnection );
212 c->enableCryptoCommands( cryptoCommandsEnabled );
213 connections.push_back( c );
214 kDebug() << "UiServer: client connection " << ( void *)c.get() << " established successfully";
215 } catch ( const Exception & e ) {
216 kDebug() << "UiServer: client connection failed: " << e.what();
217 QTcpSocket s;
218 s.setSocketDescriptor( fd );
219 QTextStream( &s ) << "ERR " << e.error_code() << " " << e.what() << "\r\n";
220 s.waitForBytesWritten();
221 s.close();
222 } catch ( ... ) {
223 kDebug() << "UiServer: client connection failed: unknown exception caught";
224 // this should never happen...
225 QTcpSocket s;
226 s.setSocketDescriptor( fd );
227 QTextStream( &s ) << "ERR 63 unknown exception caught\r\n";
228 s.waitForBytesWritten();
229 s.close();
233 QString UiServer::Private::makeFileName( const QString & socket ) const {
234 if ( !socket.isEmpty() )
235 return socket;
236 const QString gnupgHome = gnupgHomeDirectory();
237 if ( gnupgHome.isEmpty() )
238 throw_<std::runtime_error>( i18n( "Could not determine the GnuPG home directory. Consider setting the GNUPGHOME environment variable." ) );
239 ensureDirectoryExists( gnupgHome );
240 const QDir dir( gnupgHome );
241 assert( dir.exists() );
242 return dir.absoluteFilePath( QLatin1String("S.uiserver") );
245 void UiServer::Private::ensureDirectoryExists( const QString& path ) const {
246 const QFileInfo info( path );
247 if ( info.exists() && !info.isDir() )
248 throw_<std::runtime_error>( i18n( "Cannot determine the GnuPG home directory: %1 exists but is not a directory.", path ) );
249 if ( info.exists() )
250 return;
251 const QDir dummy; //there is no static QDir::mkpath()...
252 errno = 0;
253 if ( !dummy.mkpath( path ) )
254 throw_<std::runtime_error>( i18n( "Could not create GnuPG home directory %1: %2", path, systemErrorString() ) );
257 void UiServer::Private::makeListeningSocket() {
259 // First, create a file (we do this only for the name, gmpfh)
260 const QString fileName = suggestedSocketName;
262 if ( QFile::exists( fileName ) ) {
263 if ( isStaleAssuanSocket( fileName ) ) {
264 QFile::remove( fileName );
265 } else {
266 throw_<std::runtime_error>( i18n( "Detected another running gnupg UI server listening at %1.", fileName ) );
270 doMakeListeningSocket( QFile::encodeName( fileName ) );
272 actualSocketName = suggestedSocketName;
275 #include "moc_uiserver_p.cpp"