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

akonadi

  • sources
  • kde-4.12
  • kdepimlibs
  • akonadi
selftestdialog.cpp
1 /*
2  Copyright (c) 2008 Volker Krause <vkrause@kde.org>
3 
4  This library is free software; you can redistribute it and/or modify it
5  under the terms of the GNU Library General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or (at your
7  option) any later version.
8 
9  This library is distributed in the hope that it will be useful, but WITHOUT
10  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12  License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB. If not, write to the
16  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  02110-1301, USA.
18 */
19 
20 #include "selftestdialog_p.h"
21 #include "agentmanager.h"
22 #include "dbusconnectionpool.h"
23 #include "session_p.h"
24 #include "servermanager.h"
25 #include "servermanager_p.h"
26 
27 #include <akonadi/private/xdgbasedirs_p.h>
28 
29 #include <KDebug>
30 #include <KIcon>
31 #include <KFileDialog>
32 #include <KLocalizedString>
33 #include <KMessageBox>
34 #include <KRun>
35 #include <KStandardDirs>
36 #include <KUser>
37 
38 #include <QtCore/QFileInfo>
39 #include <QtCore/QProcess>
40 #include <QtCore/QSettings>
41 #include <QtCore/QTextStream>
42 #include <QtDBus/QtDBus>
43 #include <QApplication>
44 #include <QClipboard>
45 #include <QStandardItemModel>
46 #include <QtSql/QSqlDatabase>
47 #include <QtSql/QSqlError>
48 
49 // @cond PRIVATE
50 
51 #define AKONADI_SEARCH_SERVICE QLatin1String( "org.kde.nepomuk.services.nepomukqueryservice" )
52 
53 using namespace Akonadi;
54 
55 static QString makeLink( const QString &file )
56 {
57  return QString::fromLatin1( "<a href=\"%1\">%2</a>" ).arg( file, file );
58 }
59 
60 enum SelfTestRole {
61  ResultTypeRole = Qt::UserRole,
62  FileIncludeRole,
63  ListDirectoryRole,
64  EnvVarRole,
65  SummaryRole,
66  DetailsRole
67 };
68 
69 SelfTestDialog::SelfTestDialog(QWidget * parent) :
70  KDialog( parent )
71 {
72  setCaption( i18n( "Akonadi Server Self-Test" ) );
73  setButtons( Close | User1 | User2 );
74  setButtonText( User1, i18n( "Save Report..." ) );
75  setButtonIcon( User1, KIcon( QString::fromLatin1( "document-save" ) ) );
76  setButtonText( User2, i18n( "Copy Report to Clipboard" ) );
77  setButtonIcon( User2, KIcon( QString::fromLatin1( "edit-copy" ) ) );
78  showButtonSeparator( true );
79  ui.setupUi( mainWidget() );
80 
81  mTestModel = new QStandardItemModel( this );
82  ui.testView->setModel( mTestModel );
83  connect( ui.testView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
84  SLOT(selectionChanged(QModelIndex)) );
85  connect( ui.detailsLabel, SIGNAL(linkActivated(QString)), SLOT(linkActivated(QString)) );
86 
87  connect( this, SIGNAL(user1Clicked()), SLOT(saveReport()) );
88  connect( this, SIGNAL(user2Clicked()), SLOT(copyReport()) );
89 
90  connect( ServerManager::self(), SIGNAL(stateChanged(Akonadi::ServerManager::State)), SLOT(runTests()) );
91  runTests();
92 }
93 
94 void SelfTestDialog::hideIntroduction()
95 {
96  ui.introductionLabel->hide();
97 }
98 
99 QStandardItem* SelfTestDialog::report( ResultType type, const KLocalizedString & summary, const KLocalizedString & details)
100 {
101  QStandardItem *item = new QStandardItem( summary.toString() );
102  switch ( type ) {
103  case Skip:
104  item->setIcon( KIcon( QString::fromLatin1( "dialog-ok" ) ) );
105  break;
106  case Success:
107  item->setIcon( KIcon( QString::fromLatin1( "dialog-ok-apply" ) ) );
108  break;
109  case Warning:
110  item->setIcon( KIcon( QString::fromLatin1( "dialog-warning" ) ) );
111  break;
112  case Error:
113  default:
114  item->setIcon( KIcon( QString::fromLatin1( "dialog-error" ) ) );
115  }
116  item->setEditable( false );
117  item->setWhatsThis( details.toString() );
118  item->setData( type, ResultTypeRole );
119  item->setData( summary.toString( 0 ), SummaryRole );
120  item->setData( details.toString( 0 ), DetailsRole );
121  mTestModel->appendRow( item );
122  return item;
123 }
124 
125 void SelfTestDialog::selectionChanged(const QModelIndex &index )
126 {
127  if ( index.isValid() ) {
128  ui.detailsLabel->setText( index.data( Qt::WhatsThisRole ).toString() );
129  ui.detailsGroup->setEnabled( true );
130  } else {
131  ui.detailsLabel->setText( QString() );
132  ui.detailsGroup->setEnabled( false );
133  }
134 }
135 
136 void SelfTestDialog::runTests()
137 {
138  mTestModel->clear();
139 
140  const QString driver = serverSetting( QLatin1String( "General" ), "Driver", QLatin1String( "QMYSQL" ) ).toString();
141  testSQLDriver();
142  if (driver == QLatin1String( "QPSQL" )) {
143  testPSQLServer();
144  }
145  else {
146 #ifndef Q_OS_WIN
147  testRootUser();
148 #endif
149  testMySQLServer();
150  testMySQLServerLog();
151  testMySQLServerConfig();
152  }
153  testAkonadiCtl();
154  testServerStatus();
155  testSearchStatus();
156  testProtocolVersion();
157  testResources();
158  testServerLog();
159  testControlLog();
160 }
161 
162 QVariant SelfTestDialog::serverSetting(const QString & group, const char *key, const QVariant &def ) const
163 {
164  const QString serverConfigFile = XdgBaseDirs::akonadiServerConfigFile( XdgBaseDirs::ReadWrite );
165  QSettings settings( serverConfigFile, QSettings::IniFormat );
166  settings.beginGroup( group );
167  return settings.value( QString::fromLatin1(key), def );
168 }
169 
170 bool SelfTestDialog::useStandaloneMysqlServer() const
171 {
172  const QString driver = serverSetting( QLatin1String( "General" ), "Driver", QLatin1String( "QMYSQL" ) ).toString();
173  if ( driver != QLatin1String( "QMYSQL" ) )
174  return false;
175  const bool startServer = serverSetting( driver, "StartServer", true ).toBool();
176  if ( !startServer )
177  return false;
178  return true;
179 }
180 
181 bool SelfTestDialog::runProcess(const QString & app, const QStringList & args, QString & result) const
182 {
183  QProcess proc;
184  proc.start( app, args );
185  const bool rv = proc.waitForFinished();
186  result.clear();
187  result += QString::fromLocal8Bit( proc.readAllStandardError() );
188  result += QString::fromLocal8Bit( proc.readAllStandardOutput() );
189  return rv;
190 }
191 
192 void SelfTestDialog::testSQLDriver()
193 {
194  const QString driver = serverSetting( QLatin1String( "General" ), "Driver", QLatin1String( "QMYSQL" ) ).toString();
195  const QStringList availableDrivers = QSqlDatabase::drivers();
196  const KLocalizedString detailsOk = ki18n( "The QtSQL driver '%1' is required by your current Akonadi server configuration and was found on your system." )
197  .subs( driver );
198  const KLocalizedString detailsFail = ki18n( "The QtSQL driver '%1' is required by your current Akonadi server configuration.\n"
199  "The following drivers are installed: %2.\n"
200  "Make sure the required driver is installed." )
201  .subs( driver )
202  .subs( availableDrivers.join( QLatin1String( ", " ) ) );
203  QStandardItem *item = 0;
204  if ( availableDrivers.contains( driver ) )
205  item = report( Success, ki18n( "Database driver found." ), detailsOk );
206  else
207  item = report( Error, ki18n( "Database driver not found." ), detailsFail );
208  item->setData( XdgBaseDirs::akonadiServerConfigFile( XdgBaseDirs::ReadWrite ), FileIncludeRole );
209 }
210 
211 void SelfTestDialog::testMySQLServer()
212 {
213  if ( !useStandaloneMysqlServer() ) {
214  report( Skip, ki18n( "MySQL server executable not tested." ),
215  ki18n( "The current configuration does not require an internal MySQL server." ) );
216  return;
217  }
218 
219  const QString driver = serverSetting( QLatin1String( "General" ), "Driver", QLatin1String( "QMYSQL" ) ).toString();
220  const QString serverPath = serverSetting( driver, "ServerPath", QLatin1String( "" ) ).toString(); // ### default?
221 
222  const KLocalizedString details = ki18n( "You have currently configured Akonadi to use the MySQL server '%1'.\n"
223  "Make sure you have the MySQL server installed, set the correct path and ensure you have the "
224  "necessary read and execution rights on the server executable. The server executable is typically "
225  "called 'mysqld'; its location varies depending on the distribution." ).subs( serverPath );
226 
227  QFileInfo info( serverPath );
228  if ( !info.exists() )
229  report( Error, ki18n( "MySQL server not found." ), details );
230  else if ( !info.isReadable() )
231  report( Error, ki18n( "MySQL server not readable." ), details );
232  else if ( !info.isExecutable() )
233  report( Error, ki18n( "MySQL server not executable." ), details );
234  else if ( !serverPath.contains( QLatin1String( "mysqld" ) ) )
235  report( Warning, ki18n( "MySQL found with unexpected name." ), details );
236  else
237  report( Success, ki18n( "MySQL server found." ), details );
238 
239  // be extra sure and get the server version while we are at it
240  QString result;
241  if ( runProcess( serverPath, QStringList() << QLatin1String( "--version" ), result ) ) {
242  const KLocalizedString details = ki18n( "MySQL server found: %1" ).subs( result );
243  report( Success, ki18n( "MySQL server is executable." ), details );
244  } else {
245  const KLocalizedString details = ki18n( "Executing the MySQL server '%1' failed with the following error message: '%2'" )
246  .subs( serverPath ).subs( result );
247  report( Error, ki18n( "Executing the MySQL server failed." ), details );
248  }
249 }
250 
251 void SelfTestDialog::testMySQLServerLog()
252 {
253  if ( !useStandaloneMysqlServer() ) {
254  report( Skip, ki18n( "MySQL server error log not tested." ),
255  ki18n( "The current configuration does not require an internal MySQL server." ) );
256  return;
257  }
258 
259  const QString logFileName = XdgBaseDirs::saveDir( "data", QLatin1String( "akonadi/db_data" ) )
260  + QDir::separator() + QString::fromLatin1( "mysql.err" );
261  const QFileInfo logFileInfo( logFileName );
262  if ( !logFileInfo.exists() || logFileInfo.size() == 0 ) {
263  report( Success, ki18n( "No current MySQL error log found." ),
264  ki18n( "The MySQL server did not report any errors during this startup. The log can be found in '%1'." ).subs( logFileName ) );
265  return;
266  }
267  QFile logFile( logFileName );
268  if ( !logFile.open( QFile::ReadOnly | QFile::Text ) ) {
269  report( Error, ki18n( "MySQL error log not readable." ),
270  ki18n( "A MySQL server error log file was found but is not readable: %1" ).subs( makeLink( logFileName ) ) );
271  return;
272  }
273  bool warningsFound = false;
274  QStandardItem *item = 0;
275  while ( !logFile.atEnd() ) {
276  const QString line = QString::fromUtf8( logFile.readLine() );
277  if ( line.contains( QLatin1String( "error" ), Qt::CaseInsensitive ) ) {
278  item = report( Error, ki18n( "MySQL server log contains errors." ),
279  ki18n( "The MySQL server error log file '%1' contains errors." ).subs( makeLink( logFileName ) ) );
280  item->setData( logFileName, FileIncludeRole );
281  return;
282  }
283  if ( !warningsFound && line.contains( QLatin1String( "warn" ), Qt::CaseInsensitive ) ) {
284  warningsFound = true;
285  }
286  }
287  if ( warningsFound ) {
288  item = report( Warning, ki18n( "MySQL server log contains warnings." ),
289  ki18n( "The MySQL server log file '%1' contains warnings." ).subs( makeLink( logFileName ) ) );
290  } else {
291  item = report( Success, ki18n( "MySQL server log contains no errors." ),
292  ki18n( "The MySQL server log file '%1' does not contain any errors or warnings." )
293  .subs( makeLink( logFileName ) ) );
294  }
295  item->setData( logFileName, FileIncludeRole );
296 
297  logFile.close();
298 }
299 
300 void SelfTestDialog::testMySQLServerConfig()
301 {
302  if ( !useStandaloneMysqlServer() ) {
303  report( Skip, ki18n( "MySQL server configuration not tested." ),
304  ki18n( "The current configuration does not require an internal MySQL server." ) );
305  return;
306  }
307 
308  QStandardItem *item = 0;
309  const QString globalConfig = XdgBaseDirs::findResourceFile( "config", QLatin1String( "akonadi/mysql-global.conf" ) );
310  const QFileInfo globalConfigInfo( globalConfig );
311  if ( !globalConfig.isEmpty() && globalConfigInfo.exists() && globalConfigInfo.isReadable() ) {
312  item = report( Success, ki18n( "MySQL server default configuration found." ),
313  ki18n( "The default configuration for the MySQL server was found and is readable at %1." )
314  .subs( makeLink( globalConfig ) ) );
315  item->setData( globalConfig, FileIncludeRole );
316  } else {
317  report( Error, ki18n( "MySQL server default configuration not found." ),
318  ki18n( "The default configuration for the MySQL server was not found or was not readable. "
319  "Check your Akonadi installation is complete and you have all required access rights." ) );
320  }
321 
322  const QString localConfig = XdgBaseDirs::findResourceFile( "config", QLatin1String( "akonadi/mysql-local.conf" ) );
323  const QFileInfo localConfigInfo( localConfig );
324  if ( localConfig.isEmpty() || !localConfigInfo.exists() ) {
325  report( Skip, ki18n( "MySQL server custom configuration not available." ),
326  ki18n( "The custom configuration for the MySQL server was not found but is optional." ) );
327  } else if ( localConfigInfo.exists() && localConfigInfo.isReadable() ) {
328  item = report( Success, ki18n( "MySQL server custom configuration found." ),
329  ki18n( "The custom configuration for the MySQL server was found and is readable at %1" )
330  .subs( makeLink( localConfig ) ) );
331  item->setData( localConfig, FileIncludeRole );
332  } else {
333  report( Error, ki18n( "MySQL server custom configuration not readable." ),
334  ki18n( "The custom configuration for the MySQL server was found at %1 but is not readable. "
335  "Check your access rights." ).subs( makeLink( localConfig ) ) );
336  }
337 
338  const QString actualConfig = XdgBaseDirs::saveDir( "data", QLatin1String( "akonadi" ) ) + QLatin1String( "/mysql.conf" );
339  const QFileInfo actualConfigInfo( actualConfig );
340  if ( actualConfig.isEmpty() || !actualConfigInfo.exists() || !actualConfigInfo.isReadable() ) {
341  report( Error, ki18n( "MySQL server configuration not found or not readable." ),
342  ki18n( "The MySQL server configuration was not found or is not readable." ) );
343  } else {
344  item = report( Success, ki18n( "MySQL server configuration is usable." ),
345  ki18n( "The MySQL server configuration was found at %1 and is readable." ).subs( makeLink( actualConfig ) ) );
346  item->setData( actualConfig, FileIncludeRole );
347  }
348 }
349 
350 void SelfTestDialog::testPSQLServer()
351 {
352  const QString dbname = serverSetting( QLatin1String( "QPSQL" ), "Name", QLatin1String( "akonadi" )).toString();
353  const QString hostname = serverSetting( QLatin1String( "QPSQL" ), "Host", QLatin1String( "localhost" )).toString();
354  const QString username = serverSetting( QLatin1String( "QPSQL" ), "User", QString() ).toString();
355  const QString password = serverSetting( QLatin1String( "QPSQL" ), "Password", QString() ).toString();
356  const int port = serverSetting( QLatin1String( "QPSQL" ), "Port", 5432).toInt();
357 
358  QSqlDatabase db = QSqlDatabase::addDatabase( QLatin1String( "QPSQL" ) );
359  db.setHostName( hostname );
360  db.setDatabaseName( dbname );
361 
362  if ( !username.isEmpty() )
363  db.setUserName( username );
364 
365  if ( !password.isEmpty() )
366  db.setPassword( password );
367 
368  db.setPort( port );
369 
370  if ( !db.open() ) {
371  const KLocalizedString details = ki18n( db.lastError().text().toLatin1() );
372  report( Error, ki18n( "Cannot connect to PostgreSQL server." ), details);
373  }
374  else {
375  report( Success, ki18n( "PostgreSQL server found." ),
376  ki18n( "The PostgreSQL server was found and connection is working." ));
377  }
378  db.close();
379 }
380 
381 void SelfTestDialog::testAkonadiCtl()
382 {
383  const QString path = KStandardDirs::findExe( QLatin1String( "akonadictl" ) );
384  if ( path.isEmpty() ) {
385  report( Error, ki18n( "akonadictl not found" ),
386  ki18n( "The program 'akonadictl' needs to be accessible in $PATH. "
387  "Make sure you have the Akonadi server installed." ) );
388  return;
389  }
390  QString result;
391  if ( runProcess( path, QStringList() << QLatin1String( "--version" ), result ) ) {
392  report( Success, ki18n( "akonadictl found and usable" ),
393  ki18n( "The program '%1' to control the Akonadi server was found "
394  "and could be executed successfully.\nResult:\n%2" ).subs( path ).subs( result ) );
395  } else {
396  report( Error, ki18n( "akonadictl found but not usable" ),
397  ki18n( "The program '%1' to control the Akonadi server was found "
398  "but could not be executed successfully.\nResult:\n%2\n"
399  "Make sure the Akonadi server is installed correctly." ).subs( path ).subs( result ) );
400  }
401 }
402 
403 void SelfTestDialog::testServerStatus()
404 {
405  if ( DBusConnectionPool::threadConnection().interface()->isServiceRegistered( ServerManager::serviceName(ServerManager::Control) ) ) {
406  report( Success, ki18n( "Akonadi control process registered at D-Bus." ),
407  ki18n( "The Akonadi control process is registered at D-Bus which typically indicates it is operational." ) );
408  } else {
409  report( Error, ki18n( "Akonadi control process not registered at D-Bus." ),
410  ki18n( "The Akonadi control process is not registered at D-Bus which typically means it was not started "
411  "or encountered a fatal error during startup." ) );
412  }
413 
414  if ( DBusConnectionPool::threadConnection().interface()->isServiceRegistered( ServerManager::serviceName(ServerManager::Server) ) ) {
415  report( Success, ki18n( "Akonadi server process registered at D-Bus." ),
416  ki18n( "The Akonadi server process is registered at D-Bus which typically indicates it is operational." ) );
417  } else {
418  report( Error, ki18n( "Akonadi server process not registered at D-Bus." ),
419  ki18n( "The Akonadi server process is not registered at D-Bus which typically means it was not started "
420  "or encountered a fatal error during startup." ) );
421  }
422 }
423 
424 void SelfTestDialog::testSearchStatus()
425 {
426  bool searchAvailable = false;
427  if ( DBusConnectionPool::threadConnection().interface()->isServiceRegistered( AKONADI_SEARCH_SERVICE ) ) {
428  searchAvailable = true;
429  report( Success, ki18n( "Nepomuk search service registered at D-Bus." ),
430  ki18n( "The Nepomuk search service is registered at D-Bus which typically indicates it is operational." ) );
431  } else {
432  report( Error, ki18n( "Nepomuk search service not registered at D-Bus." ),
433  ki18n( "The Nepomuk search service is not registered at D-Bus which typically means it was not started "
434  "or encountered a fatal error during startup." ) );
435  }
436 
437  if ( searchAvailable ) {
438  // check which backend is used
439  QDBusInterface interface( QLatin1String( "org.kde.NepomukStorage" ), QLatin1String( "/nepomukstorage" ) );
440  const QDBusReply<QString> reply = interface.call( QLatin1String( "usedSopranoBackend" ) );
441  if ( reply.isValid() ) {
442  const QString name = reply.value();
443 
444  // put blacklisted backends here
445  if ( name.contains( QLatin1String( "redland" ) ) ) {
446  report( Error, ki18n( "Nepomuk search service uses inappropriate backend." ),
447  ki18n( "The Nepomuk search service uses the '%1' backend, which is not "
448  "recommended for use with Akonadi." ).subs( name ) );
449  } else {
450  report( Success, ki18n( "Nepomuk search service uses an appropriate backend. " ),
451  ki18n( "The Nepomuk search service uses one of the recommended backends." ) );
452  }
453  }
454  }
455 }
456 
457 void SelfTestDialog::testProtocolVersion()
458 {
459  if ( Internal::serverProtocolVersion() < 0 ) {
460  report( Skip, ki18n( "Protocol version check not possible." ),
461  ki18n( "Without a connection to the server it is not possible to check if the protocol version meets the requirements." ) );
462  return;
463  }
464  if ( Internal::serverProtocolVersion() < SessionPrivate::minimumProtocolVersion() ) {
465  report( Error, ki18n( "Server protocol version is too old." ),
466  ki18n( "The server protocol version is %1, but at least version %2 is required. "
467  "Install a newer version of the Akonadi server." )
468  .subs( Internal::serverProtocolVersion() )
469  .subs( SessionPrivate::minimumProtocolVersion() ) );
470  } else {
471  report( Success, ki18n( "Server protocol version is recent enough." ),
472  ki18n( "The server Protocol version is %1, which equal or newer than the required version %2." )
473  .subs( Internal::serverProtocolVersion() )
474  .subs( SessionPrivate::minimumProtocolVersion() ) );
475  }
476 }
477 
478 void SelfTestDialog::testResources()
479 {
480  AgentType::List agentTypes = AgentManager::self()->types();
481  bool resourceFound = false;
482  foreach ( const AgentType &type, agentTypes ) {
483  if ( type.capabilities().contains( QLatin1String( "Resource" ) ) ) {
484  resourceFound = true;
485  break;
486  }
487  }
488 
489  const QStringList pathList = XdgBaseDirs::findAllResourceDirs( "data", QLatin1String( "akonadi/agents" ) );
490  QStandardItem *item = 0;
491  if ( resourceFound ) {
492  item = report( Success, ki18n( "Resource agents found." ), ki18n( "At least one resource agent has been found." ) );
493  } else {
494  item = report( Error, ki18n( "No resource agents found." ),
495  ki18n( "No resource agents have been found, Akonadi is not usable without at least one. "
496  "This usually means that no resource agents are installed or that there is a setup problem. "
497  "The following paths have been searched: '%1'. "
498  "The XDG_DATA_DIRS environment variable is set to '%2'; make sure this includes all paths "
499  "where Akonadi agents are installed." )
500  .subs( pathList.join( QLatin1String( " " ) ) )
501  .subs( QString::fromLocal8Bit( qgetenv( "XDG_DATA_DIRS" ) ) ) );
502  }
503  item->setData( pathList, ListDirectoryRole );
504  item->setData( QByteArray( "XDG_DATA_DIRS" ), EnvVarRole );
505 }
506 
507 void Akonadi::SelfTestDialog::testServerLog()
508 {
509  QString serverLog = XdgBaseDirs::saveDir( "data", QLatin1String( "akonadi" ) )
510  + QDir::separator() + QString::fromLatin1( "akonadiserver.error" );
511  QFileInfo info( serverLog );
512  if ( !info.exists() || info.size() <= 0 ) {
513  report( Success, ki18n( "No current Akonadi server error log found." ),
514  ki18n( "The Akonadi server did not report any errors during its current startup." ) );
515  } else {
516  QStandardItem *item = report( Error, ki18n( "Current Akonadi server error log found." ),
517  ki18n( "The Akonadi server reported errors during its current startup. The log can be found in %1." ).subs( makeLink( serverLog ) ) );
518  item->setData( serverLog, FileIncludeRole );
519  }
520 
521  serverLog += QLatin1String( ".old" );
522  info.setFile( serverLog );
523  if ( !info.exists() || info.size() <= 0 ) {
524  report( Success, ki18n( "No previous Akonadi server error log found." ),
525  ki18n( "The Akonadi server did not report any errors during its previous startup." ) );
526  } else {
527  QStandardItem *item = report( Error, ki18n( "Previous Akonadi server error log found." ),
528  ki18n( "The Akonadi server reported errors during its previous startup. The log can be found in %1." ).subs( makeLink( serverLog ) ) );
529  item->setData( serverLog, FileIncludeRole );
530  }
531 }
532 
533 void SelfTestDialog::testControlLog()
534 {
535  QString controlLog = XdgBaseDirs::saveDir( "data", QLatin1String( "akonadi" ) )
536  + QDir::separator() + QString::fromLatin1( "akonadi_control.error" );
537  QFileInfo info( controlLog );
538  if ( !info.exists() || info.size() <= 0 ) {
539  report( Success, ki18n( "No current Akonadi control error log found." ),
540  ki18n( "The Akonadi control process did not report any errors during its current startup." ) );
541  } else {
542  QStandardItem *item = report( Error, ki18n( "Current Akonadi control error log found." ),
543  ki18n( "The Akonadi control process reported errors during its current startup. The log can be found in %1." ).subs( makeLink( controlLog ) ) );
544  item->setData( controlLog, FileIncludeRole );
545  }
546 
547  controlLog += QLatin1String( ".old" );
548  info.setFile( controlLog );
549  if ( !info.exists() || info.size() <= 0 ) {
550  report( Success, ki18n( "No previous Akonadi control error log found." ),
551  ki18n( "The Akonadi control process did not report any errors during its previous startup." ) );
552  } else {
553  QStandardItem *item = report( Error, ki18n( "Previous Akonadi control error log found." ),
554  ki18n( "The Akonadi control process reported errors during its previous startup. The log can be found in %1." ).subs( makeLink( controlLog ) ) );
555  item->setData( controlLog, FileIncludeRole );
556  }
557 }
558 
559 void SelfTestDialog::testRootUser()
560 {
561  KUser user;
562  if ( user.isSuperUser() ) {
563  report( Error, ki18n( "Akonadi was started as root" ), ki18n( "Running Internet-facing applications as root/administrator exposes you to many security risks. MySQL, used by this Akonadi installation, will not allow itself to run as root, to protect you from these risks." ) );
564  } else {
565  report( Success, ki18n( "Akonadi is not running as root" ), ki18n( "Akonadi is not running as a root/administrator user, which is the recommended setup for a secure system." ) );
566  }
567 }
568 
569 QString SelfTestDialog::createReport()
570 {
571  QString result;
572  QTextStream s( &result );
573  s << "Akonadi Server Self-Test Report" << endl;
574  s << "===============================" << endl;
575 
576  for ( int i = 0; i < mTestModel->rowCount(); ++i ) {
577  QStandardItem *item = mTestModel->item( i );
578  s << endl;
579  s << "Test " << (i + 1) << ": ";
580  switch ( item->data( ResultTypeRole ).toInt() ) {
581  case Skip:
582  s << "SKIP"; break;
583  case Success:
584  s << "SUCCESS"; break;
585  case Warning:
586  s << "WARNING"; break;
587  case Error:
588  default:
589  s << "ERROR"; break;
590  }
591  s << endl << "--------" << endl;
592  s << endl;
593  s << item->data( SummaryRole ).toString() << endl;
594  s << "Details: " << item->data( DetailsRole ).toString() << endl;
595  if ( item->data( FileIncludeRole ).isValid() ) {
596  s << endl;
597  const QString fileName = item->data( FileIncludeRole ).toString();
598  QFile f( fileName );
599  if ( f.open( QFile::ReadOnly ) ) {
600  s << "File content of '" << fileName << "':" << endl;
601  s << f.readAll() << endl;
602  } else {
603  s << "File '" << fileName << "' could not be opened" << endl;
604  }
605  }
606  if ( item->data( ListDirectoryRole ).isValid() ) {
607  s << endl;
608  const QStringList pathList = item->data( ListDirectoryRole ).toStringList();
609  if ( pathList.isEmpty() )
610  s << "Directory list is empty." << endl;
611  foreach ( const QString &path, pathList ) {
612  s << "Directory listing of '" << path << "':" << endl;
613  QDir dir( path );
614  dir.setFilter( QDir::AllEntries | QDir::NoDotAndDotDot );
615  foreach ( const QString &entry, dir.entryList() )
616  s << entry << endl;
617  }
618  }
619  if ( item->data( EnvVarRole ).isValid() ) {
620  s << endl;
621  const QByteArray envVarName = item->data( EnvVarRole ).toByteArray();
622  const QByteArray envVarValue = qgetenv( envVarName );
623  s << "Environment variable " << envVarName << " is set to '" << envVarValue << "'" << endl;
624  }
625  }
626 
627  s << endl;
628  s.flush();
629  return result;
630 }
631 
632 void SelfTestDialog::saveReport()
633 {
634  const QString defaultFileName = QLatin1String( "akonadi-selftest-report-" )
635  + QDate::currentDate().toString( QLatin1String( "yyyyMMdd" ) )
636  + QLatin1String( ".txt" );
637  const QString fileName = KFileDialog::getSaveFileName( QUrl(defaultFileName), QString(), this,
638  i18n( "Save Test Report" ) );
639  if ( fileName.isEmpty() )
640  return;
641 
642  QFile file( fileName );
643  if ( !file.open( QFile::ReadWrite ) ) {
644  KMessageBox::error( this, i18n( "Could not open file '%1'", fileName ) );
645  return;
646  }
647 
648  file.write( createReport().toUtf8() );
649  file.close();
650 }
651 
652 void SelfTestDialog::copyReport()
653 {
654 #ifndef QT_NO_CLIPBOARD
655  QApplication::clipboard()->setText( createReport() );
656 #endif
657 }
658 
659 void SelfTestDialog::linkActivated(const QString & link)
660 {
661  KRun::runUrl( KUrl::fromPath( link ), QLatin1String( "text/plain" ), this );
662 }
663 
664 // @endcond
665 
666 #include "moc_selftestdialog_p.cpp"
Akonadi::ServerManager::self
static ServerManager * self()
Returns the singleton instance of this class, for connecting to its signals.
Definition: servermanager.cpp:152
Akonadi::SelfTestDialog::hideIntroduction
void hideIntroduction()
Hides the label with the introduction message.
Akonadi::ServerManager::serviceName
static QString serviceName(ServiceType serviceType)
Returns the namespaced D-Bus service name for serviceType.
Definition: servermanager.cpp:298
Akonadi::AgentType
A representation of an agent type.
Definition: agenttype.h:58
Akonadi::AgentType::List
QList< AgentType > List
Describes a list of agent types.
Definition: agenttype.h:67
Akonadi::AgentType::capabilities
QStringList capabilities() const
Returns the list of supported capabilities of the agent type.
Definition: agenttype.cpp:76
Akonadi::SelfTestDialog::SelfTestDialog
SelfTestDialog(QWidget *parent=0)
Creates a new self test dialog.
Akonadi::AgentManager::types
AgentType::List types() const
Returns the list of all available agent types.
Definition: agentmanager.cpp:389
Akonadi::AgentManager::self
static AgentManager * self()
Returns the global instance of the agent manager.
Definition: agentmanager.cpp:380
Akonadi::ServerManager::State
State
Enum for the various states the server can be in.
Definition: servermanager.h:50
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 23:00:27 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

akonadi

Skip menu "akonadi"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Modules
  • Related Pages

kdepimlibs API Reference

Skip menu "kdepimlibs API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kldap
  • kmbox
  • kmime
  • kpimidentities
  • kpimtextedit
  • kresources
  • ktnef
  • kxmlrpcclient
  • microblog

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