• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepim API Reference
  • KDE Home
  • Contact Us
 

kleopatra

  • sources
  • kde-4.12
  • kdepim
  • kleopatra
  • commands
importcertificatescommand.cpp
Go to the documentation of this file.
1 /* -*- mode: c++; c-basic-offset:4 -*-
2  commands/importcertificatescommand.cpp
3 
4  This file is part of Kleopatra, the KDE keymanager
5  Copyright (c) 2007, 2008 Klarälvdalens Datakonsult AB
6 
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.
11 
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.
16 
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
20 
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.
31 */
32 
33 #include <config-kleopatra.h>
34 
35 #include "importcertificatescommand.h"
36 #include "importcertificatescommand_p.h"
37 
38 #include <models/keylistsortfilterproxymodel.h>
39 #include <models/predicates.h>
40 
41 #include <utils/formatting.h>
42 
43 #include <kleo/stl_util.h>
44 #include <kleo/cryptobackendfactory.h>
45 #include <kleo/importjob.h>
46 #include <kleo/importfromkeyserverjob.h>
47 
48 #include <gpgme++/global.h>
49 #include <gpgme++/importresult.h>
50 
51 #include <KLocale>
52 #include <KMessageBox>
53 #include <KConfigGroup>
54 #include <KDebug>
55 
56 #include <QByteArray>
57 #include <QFile>
58 #include <QFileDialog>
59 #include <QString>
60 #include <QWidget>
61 #include <QFileInfo>
62 #include <QTreeView>
63 #include <QTextDocument> // for Qt::escape
64 
65 #include <boost/bind.hpp>
66 #include <boost/mem_fn.hpp>
67 
68 #include <memory>
69 #include <algorithm>
70 #include <cassert>
71 #include <map>
72 #include <set>
73 
74 using namespace GpgME;
75 using namespace Kleo;
76 using namespace boost;
77 
78 namespace {
79 
80  make_comparator_str( ByImportFingerprint, .fingerprint() );
81 
82  class ImportResultProxyModel : public AbstractKeyListSortFilterProxyModel {
83  Q_OBJECT
84  public:
85  ImportResultProxyModel( const std::vector<ImportResult> & results, const QStringList & ids, QObject * parent=0 )
86  : AbstractKeyListSortFilterProxyModel( parent )
87  {
88  updateFindCache( results, ids );
89  }
90 
91  ~ImportResultProxyModel() {}
92 
93  /* reimp */ ImportResultProxyModel * clone() const {
94  // compiler-generated copy ctor is fine!
95  return new ImportResultProxyModel( *this );
96  }
97 
98  void setImportResults( const std::vector<ImportResult> & results, const QStringList & ids ) {
99  updateFindCache( results, ids );
100  invalidateFilter();
101  }
102 
103  protected:
104  /* reimp */ QVariant data( const QModelIndex & index, int role ) const {
105  if ( !index.isValid() || role != Qt::ToolTipRole )
106  return AbstractKeyListSortFilterProxyModel::data( index, role );
107  const QString fpr = index.data( FingerprintRole ).toString();
108  // find information:
109  const std::vector<Import>::const_iterator it
110  = qBinaryFind( m_importsByFingerprint.begin(), m_importsByFingerprint.end(),
111  fpr.toLatin1().constData(),
112  ByImportFingerprint<std::less>() );
113  if ( it == m_importsByFingerprint.end() )
114  return AbstractKeyListSortFilterProxyModel::data( index, role );
115  else
116  return Formatting::importMetaData( *it, kdtools::copy<QStringList>( m_idsByFingerprint[it->fingerprint()] ) );
117  }
118  /* reimp */ bool filterAcceptsRow( int source_row, const QModelIndex & source_parent ) const {
119  //
120  // 0. Keep parents of matching children:
121  //
122  const QModelIndex index = sourceModel()->index( source_row, 0, source_parent );
123  assert( index.isValid() );
124  for ( int i = 0, end = sourceModel()->rowCount( index ) ; i != end ; ++i )
125  if ( filterAcceptsRow( i, index ) )
126  return true;
127  //
128  // 1. Check that this is an imported key:
129  //
130  const QString fpr = index.data( FingerprintRole ).toString();
131 
132  return std::binary_search( m_importsByFingerprint.begin(), m_importsByFingerprint.end(),
133  fpr.toLatin1().constData(),
134  ByImportFingerprint<std::less>() );
135  }
136 
137  private:
138  void updateFindCache( const std::vector<ImportResult> & results, const QStringList & ids ) {
139  assert( results.size() == static_cast<unsigned>( ids.size() ) );
140  m_importsByFingerprint.clear();
141  m_idsByFingerprint.clear();
142  m_results = results;
143  for ( unsigned int i = 0, end = results.size() ; i != end ; ++i ) {
144  const std::vector<Import> imports = results[i].imports();
145  m_importsByFingerprint.insert( m_importsByFingerprint.end(), imports.begin(), imports.end() );
146  const QString & id = ids[i];
147  for ( std::vector<Import>::const_iterator it = imports.begin(), end = imports.end() ; it != end ; ++it ) {
148  m_idsByFingerprint[it->fingerprint()].insert( id );
149  }
150  }
151  std::sort( m_importsByFingerprint.begin(), m_importsByFingerprint.end(),
152  ByImportFingerprint<std::less>() );
153  }
154 
155  private:
156  mutable std::vector<Import> m_importsByFingerprint;
157  mutable std::map< const char*, std::set<QString>, ByImportFingerprint<std::less> > m_idsByFingerprint;
158  std::vector<ImportResult> m_results;
159  };
160 
161 }
162 
163 ImportCertificatesCommand::Private::Private( ImportCertificatesCommand * qq, KeyListController * c )
164  : Command::Private( qq, c ),
165  waitForMoreJobs( false ),
166  nonWorkingProtocols(),
167  idsByJob(),
168  jobs(),
169  results(),
170  ids()
171 {
172 
173 }
174 
175 ImportCertificatesCommand::Private::~Private() {}
176 
177 #define d d_func()
178 #define q q_func()
179 
180 
181 ImportCertificatesCommand::ImportCertificatesCommand( KeyListController * p )
182  : Command( new Private( this, p ) )
183 {
184 
185 }
186 
187 ImportCertificatesCommand::ImportCertificatesCommand( QAbstractItemView * v, KeyListController * p )
188  : Command( v, new Private( this, p ) )
189 {
190 
191 }
192 
193 ImportCertificatesCommand::~ImportCertificatesCommand() {}
194 
195 static QString format_ids( const QStringList & ids ) {
196  return kdtools::transform_if<QStringList>( ids, Qt::escape, !boost::bind( &QString::isEmpty, _1 ) ).join( QLatin1String("<br>") );
197 }
198 
199 static QString make_tooltip( const QStringList & ids ) {
200  if ( ids.empty() )
201  return QString();
202  if ( ids.size() == 1 )
203  if ( ids.front().isEmpty() )
204  return QString();
205  else
206  return i18nc( "@info:tooltip",
207  "Imported Certificates from %1",
208  Qt::escape( ids.front() ) );
209  else
210  return i18nc( "@info:tooltip",
211  "Imported certificates from these sources:<br/>%1",
212  format_ids( ids ) );
213 }
214 
215 void ImportCertificatesCommand::Private::setImportResultProxyModel( const std::vector<ImportResult> & results, const QStringList & ids ) {
216  if ( kdtools::none_of( results, mem_fn( &ImportResult::numConsidered ) ) )
217  return;
218  q->addTemporaryView( i18nc("@title:tab", "Imported Certificates"),
219  new ImportResultProxyModel( results, ids ),
220  make_tooltip( ids ) );
221  if ( QTreeView * const tv = qobject_cast<QTreeView*>( parentWidgetOrView() ) )
222  tv->expandAll();
223 }
224 
225 int sum( const std::vector<ImportResult> & res, int (ImportResult::*fun)() const ) {
226  return kdtools::accumulate_transform( res.begin(), res.end(), mem_fn( fun ), 0 );
227 }
228 
229 static QString make_report( const std::vector<ImportResult> & res, const QString & id=QString() ) {
230 
231  const KLocalizedString normalLine = ki18n("<tr><td align=\"right\">%1</td><td>%2</td></tr>");
232  const KLocalizedString boldLine = ki18n("<tr><td align=\"right\"><b>%1</b></td><td>%2</td></tr>");
233  const KLocalizedString headerLine = ki18n("<tr><th colspan=\"2\" align=\"center\">%1</th></tr>");
234 
235 #define SUM( x ) sum( res, &ImportResult::x )
236 
237  QStringList lines;
238  if ( !id.isEmpty() )
239  lines.push_back( headerLine.subs( id ).toString() );
240  lines.push_back( normalLine.subs( i18n("Total number processed:") )
241  .subs( SUM(numConsidered) ).toString() );
242  lines.push_back( normalLine.subs( i18n("Imported:") )
243  .subs( SUM(numImported) ).toString() );
244  if ( const int n = SUM(newSignatures) )
245  lines.push_back( normalLine.subs( i18n("New signatures:") )
246  .subs( n ).toString() );
247  if ( const int n = SUM(newUserIDs) )
248  lines.push_back( normalLine.subs( i18n("New user IDs:") )
249  .subs( n ).toString() );
250  if ( const int n = SUM(numKeysWithoutUserID) )
251  lines.push_back( normalLine.subs( i18n("Certificates without user IDs:") )
252  .subs( n ).toString() );
253  if ( const int n = SUM(newSubkeys) )
254  lines.push_back( normalLine.subs( i18n("New subkeys:") )
255  .subs( n ).toString() );
256  if ( const int n = SUM(newRevocations) )
257  lines.push_back( boldLine.subs( i18n("Newly revoked:") )
258  .subs( n ).toString() );
259  if ( const int n = SUM(notImported) )
260  lines.push_back( boldLine.subs( i18n("Not imported:") )
261  .subs( n ).toString() );
262  if ( const int n = SUM(numUnchanged) )
263  lines.push_back( normalLine.subs( i18n("Unchanged:") )
264  .subs( n ).toString() );
265  if ( const int n = SUM(numSecretKeysConsidered) )
266  lines.push_back( normalLine.subs( i18n("Secret keys processed:") )
267  .subs( n ).toString() );
268  if ( const int n = SUM(numSecretKeysImported) )
269  lines.push_back( normalLine.subs( i18n("Secret keys imported:") )
270  .subs( n ).toString() );
271  if ( const int n = SUM(numSecretKeysConsidered) - SUM(numSecretKeysImported) - SUM(numSecretKeysUnchanged) )
272  if ( n > 0 )
273  lines.push_back( boldLine.subs( i18n("Secret keys <em>not</em> imported:") )
274  .subs( n ).toString() );
275  if ( const int n = SUM(numSecretKeysUnchanged) )
276  lines.push_back( normalLine.subs( i18n("Secret keys unchanged:") )
277  .subs( n ).toString() );
278 
279 #undef sum
280 
281  return lines.join( QString() );
282 }
283 
284 static QString make_message_report( const std::vector<ImportResult> & res, const QStringList & ids ) {
285 
286  assert( res.size() == static_cast<unsigned>( ids.size() ) );
287 
288  if ( res.empty() )
289  return i18n( "No imports (should not happen, please report a bug)." );
290 
291  if ( res.size() == 1 )
292  return ids.front().isEmpty()
293  ? i18n( "<qt><p>Detailed results of certificate import:</p>"
294  "<table width=\"100%\">%1</table></qt>", make_report( res ) )
295  : i18n( "<qt><p>Detailed results of importing %1:</p>"
296  "<table width=\"100%\">%2</table></qt>", ids.front(), make_report( res ) );
297 
298  return i18n( "<qt><p>Detailed results of certificate import:</p>"
299  "<table width=\"100%\">%1</table></qt>", make_report( res, i18n("Totals") ) );
300 }
301 
302 void ImportCertificatesCommand::Private::showDetails( QWidget * parent, const std::vector<ImportResult> & res, const QStringList & ids ) {
303  if ( parent ) {
304  setImportResultProxyModel( res, ids );
305  KMessageBox::information( parent, make_message_report( res, ids ), i18n( "Certificate Import Result" ) );
306  } else {
307  showDetails( res, ids );
308  }
309 }
310 
311 void ImportCertificatesCommand::Private::showDetails( const std::vector<ImportResult> & res, const QStringList & ids ) {
312  setImportResultProxyModel( res, ids );
313  information( make_message_report( res, ids ), i18n( "Certificate Import Result" ) );
314 }
315 
316 static QString make_error_message( const Error & err, const QString & id ) {
317  assert( err );
318  assert( !err.isCanceled() );
319  return id.isEmpty()
320  ? i18n( "<qt><p>An error occurred while trying "
321  "to import the certificate:</p>"
322  "<p><b>%1</b></p></qt>",
323  QString::fromLocal8Bit( err.asString() ) )
324  : i18n( "<qt><p>An error occurred while trying "
325  "to import the certificate %1:</p>"
326  "<p><b>%2</b></p></qt>",
327  id, QString::fromLocal8Bit( err.asString() ) );
328 }
329 
330 void ImportCertificatesCommand::Private::showError( QWidget * parent, const Error & err, const QString & id ) {
331  if ( parent )
332  KMessageBox::error( parent, make_error_message( err, id ), i18n( "Certificate Import Failed" ) );
333  else
334  showError( err, id );
335 }
336 
337 void ImportCertificatesCommand::Private::showError( const Error & err, const QString & id ) {
338  error( make_error_message( err, id ), i18n( "Certificate Import Failed" ) );
339 }
340 
341 void ImportCertificatesCommand::Private::setWaitForMoreJobs( bool wait ) {
342  if ( wait == waitForMoreJobs )
343  return;
344  waitForMoreJobs = wait;
345  tryToFinish();
346 }
347 
348 void ImportCertificatesCommand::Private::importResult( const ImportResult & result ) {
349 
350  jobs.erase( std::remove( jobs.begin(), jobs.end(), q->sender() ), jobs.end() );
351 
352  importResult( result, idsByJob[q->sender()] );
353 }
354 
355 void ImportCertificatesCommand::Private::importResult( const ImportResult & result, const QString & id ) {
356 
357  results.push_back( result );
358  ids.push_back( id );
359 
360  tryToFinish();
361 }
362 
363 void ImportCertificatesCommand::Private::tryToFinish() {
364 
365  if ( waitForMoreJobs || !jobs.empty() )
366  return;
367 
368  if ( kdtools::any( results, boost::bind( &Error::code, boost::bind( &ImportResult::error, _1 ) ) ) ) {
369  setImportResultProxyModel( results, ids );
370  if ( kdtools::all( results, boost::bind( &Error::isCanceled, boost::bind( &ImportResult::error, _1 ) ) ) )
371  emit q->canceled();
372  else
373  for ( unsigned int i = 0, end = results.size() ; i != end ; ++i )
374  if ( const Error err = results[i].error() )
375  showError( err, ids[i] );
376  }
377  else
378  showDetails( results, ids );
379  finished();
380 }
381 
382 static std::auto_ptr<ImportJob> get_import_job( GpgME::Protocol protocol ) {
383  assert( protocol != UnknownProtocol );
384  if ( const Kleo::CryptoBackend::Protocol * const backend = CryptoBackendFactory::instance()->protocol( protocol ) )
385  return std::auto_ptr<ImportJob>( backend->importJob() );
386  else
387  return std::auto_ptr<ImportJob>();
388 }
389 
390 void ImportCertificatesCommand::Private::startImport( GpgME::Protocol protocol, const QByteArray & data, const QString & id ) {
391  assert( protocol != UnknownProtocol );
392 
393  if ( kdtools::contains( nonWorkingProtocols, protocol ) )
394  return;
395 
396  std::auto_ptr<ImportJob> job = get_import_job( protocol );
397  if ( !job.get() ) {
398  nonWorkingProtocols.push_back( protocol );
399  error( i18n( "The type of this certificate (%1) is not supported by this Kleopatra installation.",
400  Formatting::displayName( protocol ) ),
401  i18n( "Certificate Import Failed" ) );
402  importResult( ImportResult(), id );
403  return;
404  }
405 
406  connect( job.get(), SIGNAL(result(GpgME::ImportResult)),
407  q, SLOT(importResult(GpgME::ImportResult)) );
408  connect( job.get(), SIGNAL(progress(QString,int,int)),
409  q, SIGNAL(progress(QString,int,int)) );
410  const GpgME::Error err = job->start( data );
411  if ( err.code() ) {
412  importResult( ImportResult( err ), id );
413  } else {
414  jobs.push_back( job.release() );
415  idsByJob[jobs.back()] = id;
416  }
417 }
418 
419 static std::auto_ptr<ImportFromKeyserverJob> get_import_from_keyserver_job( GpgME::Protocol protocol ) {
420  assert( protocol != UnknownProtocol );
421  if ( const Kleo::CryptoBackend::Protocol * const backend = CryptoBackendFactory::instance()->protocol( protocol ) )
422  return std::auto_ptr<ImportFromKeyserverJob>( backend->importFromKeyserverJob() );
423  else
424  return std::auto_ptr<ImportFromKeyserverJob>();
425 }
426 
427 void ImportCertificatesCommand::Private::startImport( GpgME::Protocol protocol, const std::vector<Key> & keys, const QString & id ) {
428  assert( protocol != UnknownProtocol );
429 
430  if ( kdtools::contains( nonWorkingProtocols, protocol ) )
431  return;
432 
433  std::auto_ptr<ImportFromKeyserverJob> job = get_import_from_keyserver_job( protocol );
434  if ( !job.get() ) {
435  nonWorkingProtocols.push_back( protocol );
436  error( i18n( "The type of this certificate (%1) is not supported by this Kleopatra installation.",
437  Formatting::displayName( protocol ) ),
438  i18n( "Certificate Import Failed" ) );
439  importResult( ImportResult(), id );
440  return;
441  }
442 
443  connect( job.get(), SIGNAL(result(GpgME::ImportResult)),
444  q, SLOT(importResult(GpgME::ImportResult)) );
445  connect( job.get(), SIGNAL(progress(QString,int,int)),
446  q, SIGNAL(progress(QString,int,int)) );
447  const GpgME::Error err = job->start( keys );
448  if ( err.code() ) {
449  importResult( ImportResult( err ), id );
450  } else {
451  jobs.push_back( job.release() );
452  idsByJob[jobs.back()] = id;
453  }
454 }
455 
456 
457 void ImportCertificatesCommand::doCancel() {
458  kdtools::for_each( d->jobs, mem_fn( &Job::slotCancel ) );
459 }
460 
461 #undef d
462 #undef q
463 
464 #include "moc_importcertificatescommand.cpp"
465 #include "importcertificatescommand.moc"
466 
Kleo::ImportCertificatesCommand::Private::showError
void showError(QWidget *parent, const GpgME::Error &error, const QString &id=QString())
sum
int sum(const std::vector< ImportResult > &res, int(ImportResult::*fun)() const )
Definition: importcertificatescommand.cpp:225
Kleo::Command::finished
void finished()
Kleo::ImportCertificatesCommand::~ImportCertificatesCommand
~ImportCertificatesCommand()
Definition: importcertificatescommand.cpp:193
Kleo::ImportCertificatesCommand::Private
Definition: importcertificatescommand_p.h:55
Kleo::ImportCertificatesCommand::Private::showDetails
void showDetails(QWidget *parent, const std::vector< GpgME::ImportResult > &results, const QStringList &ids)
Kleo::ImportCertificatesCommand::ImportCertificatesCommand
ImportCertificatesCommand(KeyListController *parent)
Definition: importcertificatescommand.cpp:181
Kleo::KeyListController
Definition: keylistcontroller.h:55
Kleo::ImportCertificatesCommand::Private::startImport
void startImport(GpgME::Protocol proto, const QByteArray &data, const QString &id=QString())
Definition: importcertificatescommand.cpp:390
Kleo::Formatting::displayName
QString displayName(GpgME::Protocol prot)
QWidget
Kleo::Command::addTemporaryView
void addTemporaryView(const QString &title, AbstractKeyListSortFilterProxyModel *proxy=0, const QString &tabToolTip=QString())
Definition: commands/command.cpp:192
formatting.h
make_comparator_str
#define make_comparator_str(Name, expr)
Definition: predicates.h:100
importcertificatescommand_p.h
Kleo::ImportCertificatesCommand
Definition: importcertificatescommand.h:42
importcertificatescommand.h
q
#define q
Definition: importcertificatescommand.cpp:178
Kleo::Command::d
kdtools::pimpl_ptr< Private > d
Definition: commands/command.h:129
Kleo::Command::Private::q
Command *const q
Definition: commands/command_p.h:55
Kleo::Command::Private::parentWidgetOrView
QWidget * parentWidgetOrView() const
Definition: commands/command_p.h:61
make_error_message
static QString make_error_message(const Error &err, const QString &id)
Definition: importcertificatescommand.cpp:316
Kleo::ImportCertificatesCommand::Private::setImportResultProxyModel
void setImportResultProxyModel(const std::vector< GpgME::ImportResult > &results, const QStringList &ids)
Definition: importcertificatescommand.cpp:215
keylistsortfilterproxymodel.h
Kleo::AbstractKeyListSortFilterProxyModel
Definition: keylistsortfilterproxymodel.h:51
SUM
#define SUM(x)
make_message_report
static QString make_message_report(const std::vector< ImportResult > &res, const QStringList &ids)
Definition: importcertificatescommand.cpp:284
format_ids
static QString format_ids(const QStringList &ids)
Definition: importcertificatescommand.cpp:195
get_import_from_keyserver_job
static std::auto_ptr< ImportFromKeyserverJob > get_import_from_keyserver_job(GpgME::Protocol protocol)
Definition: importcertificatescommand.cpp:419
Kleo::ImportCertificatesCommand::Private::~Private
~Private()
Definition: importcertificatescommand.cpp:175
Kleo::Command::progress
void progress(const QString &message, int current, int total)
get_import_job
static std::auto_ptr< ImportJob > get_import_job(GpgME::Protocol protocol)
Definition: importcertificatescommand.cpp:382
Kleo::Formatting::importMetaData
QString importMetaData(const GpgME::Import &import)
predicates.h
make_tooltip
static QString make_tooltip(const QStringList &ids)
Definition: importcertificatescommand.cpp:199
make_report
static QString make_report(const std::vector< ImportResult > &res, const QString &id=QString())
Definition: importcertificatescommand.cpp:229
Kleo::ImportCertificatesCommand::doCancel
void doCancel()
Definition: importcertificatescommand.cpp:457
Kleo::ImportCertificatesCommand::Private::importResult
void importResult(const GpgME::ImportResult &)
Kleo::Command
Definition: commands/command.h:58
Kleo::ImportCertificatesCommand::Private::setWaitForMoreJobs
void setWaitForMoreJobs(bool waiting)
Definition: importcertificatescommand.cpp:341
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:56:41 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

kleopatra

Skip menu "kleopatra"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members

kdepim API Reference

Skip menu "kdepim API Reference"
  • akonadi_next
  • akregator
  • blogilo
  • calendarsupport
  • console
  •   kabcclient
  •   konsolekalendar
  • kaddressbook
  • kalarm
  •   lib
  • kdgantt2
  • kjots
  • kleopatra
  • kmail
  • knode
  • knotes
  • kontact
  • korgac
  • korganizer
  • ktimetracker
  • libkdepim
  • libkleo
  • libkpgp
  • mailcommon
  • messagelist
  • messageviewer

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal