00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "servertest.h"
00024 #include "socket.h"
00025
00026 #include <mailtransport/transportbase.h>
00027 #include <mailtransport/mailtransport_defs.h>
00028
00029
00030 #include <QHostInfo>
00031 #include <QProgressBar>
00032 #include <QTimer>
00033
00034
00035 #include <klocale.h>
00036 #include <kdebug.h>
00037
00038 using namespace MailTransport;
00039
00040 namespace MailTransport
00041 {
00042
00043 class ServerTestPrivate
00044 {
00045 public:
00046 ServerTestPrivate( ServerTest *test );
00047
00048 ServerTest *const q;
00049 QString server;
00050 QString fakeHostname;
00051 QString testProtocol;
00052
00053 MailTransport::Socket *normalSocket;
00054 MailTransport::Socket *secureSocket;
00055
00056 QSet< int > connectionResults;
00057 QHash< int, QList<int> > authenticationResults;
00058 QSet< ServerTest::Capability > capabilityResults;
00059 QHash< int, uint > customPorts;
00060 QTimer *normalSocketTimer;
00061 QTimer *secureSocketTimer;
00062 QTimer *progressTimer;
00063
00064 QProgressBar *testProgress;
00065
00066 bool secureSocketFinished;
00067 bool normalSocketFinished;
00068 bool tlsFinished;
00069 bool popSupportsTLS;
00070 int normalStage;
00071 int secureStage;
00072 int encryptionMode;
00073
00074 void finalResult();
00075 void handleSMTPIMAPResponse( int type, const QString &text );
00076 void sendInitialCapabilityQuery( MailTransport::Socket *socket );
00077 bool handlePopConversation( MailTransport::Socket *socket, int type, int stage,
00078 const QString &response, bool *shouldStartTLS );
00079 QList< int > parseAuthenticationList( const QStringList &authentications );
00080
00081
00082 void slotNormalPossible();
00083 void slotNormalNotPossible();
00084 void slotSslPossible();
00085 void slotSslNotPossible();
00086 void slotTlsDone();
00087 void slotReadNormal( const QString &text );
00088 void slotReadSecure( const QString &text );
00089 void slotUpdateProgress();
00090 };
00091
00092 }
00093
00094 ServerTestPrivate::ServerTestPrivate( ServerTest *test )
00095 : q( test ), testProgress( 0 ), secureSocketFinished( false ),
00096 normalSocketFinished( false ), tlsFinished( false )
00097 {
00098 }
00099
00100 void ServerTestPrivate::finalResult()
00101 {
00102 if ( !secureSocketFinished || !normalSocketFinished || !tlsFinished ) {
00103 return;
00104 }
00105
00106 kDebug() << "Modes:" << connectionResults;
00107 kDebug() << "Capabilities:" << capabilityResults;
00108 kDebug() << "Normal:" << q->normalProtocols();
00109 kDebug() << "SSL:" << q->secureProtocols();
00110 kDebug() << "TLS:" << q->tlsProtocols();
00111
00112 if ( testProgress ) {
00113 testProgress->hide();
00114 }
00115 progressTimer->stop();
00116 secureSocketFinished = false;
00117 normalSocketFinished = false;
00118 tlsFinished = false ;
00119
00120 emit q->finished( connectionResults.toList() );
00121 }
00122
00123 QList< int > ServerTestPrivate::parseAuthenticationList( const QStringList &authentications )
00124 {
00125 QList< int > result;
00126 for ( QStringList::ConstIterator it = authentications.begin();
00127 it != authentications.end(); ++it ) {
00128 QString current = (*it).toUpper();
00129 if ( current == QLatin1String( "LOGIN" ) ) {
00130 result << Transport::EnumAuthenticationType::LOGIN;
00131 } else if ( current == QLatin1String( "PLAIN" ) ) {
00132 result << Transport::EnumAuthenticationType::PLAIN;
00133 } else if ( current == QLatin1String( "CRAM-MD5" ) ) {
00134 result << Transport::EnumAuthenticationType::CRAM_MD5;
00135 } else if ( current == QLatin1String( "DIGEST-MD5" ) ) {
00136 result << Transport::EnumAuthenticationType::DIGEST_MD5;
00137 } else if ( current == QLatin1String( "NTLM" ) ) {
00138 result << Transport::EnumAuthenticationType::NTLM;
00139 } else if ( current == QLatin1String( "GSSAPI" ) ) {
00140 result << Transport::EnumAuthenticationType::GSSAPI;
00141 } else if ( current == QLatin1String( "ANONYMOUS" ) ) {
00142 result << Transport::EnumAuthenticationType::ANONYMOUS;
00143 }
00144
00145 }
00146 kDebug() << authentications << result;
00147
00148
00149
00150
00151 if ( result.contains( Transport::EnumAuthenticationType::PLAIN ) ) {
00152 result.removeAll( Transport::EnumAuthenticationType::LOGIN );
00153 }
00154
00155 return result;
00156 }
00157
00158 void ServerTestPrivate::handleSMTPIMAPResponse( int type, const QString &text )
00159 {
00160 if ( !text.contains( QLatin1String( "AUTH" ), Qt::CaseInsensitive ) ) {
00161 kDebug() << "No authentication possible";
00162 return;
00163 }
00164
00165 QStringList protocols;
00166 protocols << QLatin1String( "LOGIN" ) << QLatin1String( "PLAIN" )
00167 << QLatin1String( "CRAM-MD5" ) << QLatin1String( "DIGEST-MD5" )
00168 << QLatin1String( "NTLM" ) << QLatin1String( "GSSAPI" )
00169 << QLatin1String( "ANONYMOUS" );
00170
00171 QStringList results;
00172 for ( int i = 0; i < protocols.count(); ++i ) {
00173 if ( text.contains( protocols.at( i ), Qt::CaseInsensitive ) ) {
00174 results.append( protocols.at( i ) );
00175 }
00176 }
00177
00178 authenticationResults[type] = parseAuthenticationList( results );
00179 kDebug() << "For type" << type << ", we have:" << authenticationResults[type];
00180 }
00181
00182 void ServerTestPrivate::slotNormalPossible()
00183 {
00184 normalSocketTimer->stop();
00185 connectionResults << Transport::EnumEncryption::None;
00186 }
00187
00188 void ServerTestPrivate::sendInitialCapabilityQuery( MailTransport::Socket *socket )
00189 {
00190 kDebug();
00191 if ( testProtocol == IMAP_PROTOCOL ) {
00192 socket->write( QLatin1String( "1 CAPABILITY" ) );
00193 }
00194
00195 else if ( testProtocol == SMTP_PROTOCOL ) {
00196
00197
00198
00199
00200
00201 QString hostname;
00202 if ( !fakeHostname.isNull() ) {
00203 hostname = fakeHostname;
00204 } else {
00205 hostname = QHostInfo::localHostName();
00206 if( hostname.isEmpty() ) {
00207 hostname = QLatin1String( "localhost.invalid" );
00208 } else if ( !hostname.contains( QChar::fromAscii( '.' ) ) ) {
00209 hostname += QLatin1String( ".localnet" );
00210 }
00211 }
00212 kDebug() << "Hostname for EHLO is" << hostname;
00213
00214 socket->write( QLatin1String( "EHLO " ) + hostname );
00215 }
00216 }
00217
00218 void ServerTestPrivate::slotTlsDone()
00219 {
00220 kDebug();
00221
00222
00223
00224 slotReadNormal( QString() );
00225 }
00226
00227 bool ServerTestPrivate::handlePopConversation( MailTransport::Socket *socket, int type, int stage,
00228 const QString &response, bool *shouldStartTLS )
00229 {
00230 Q_ASSERT( shouldStartTLS != 0 );
00231
00232
00233 if ( stage == 0 ) {
00234
00235
00236 QString responseWithoutCRLF = response;
00237 responseWithoutCRLF.chop( 2 );
00238 QRegExp re( QLatin1String( "<[A-Za-z0-9\\.\\-_]+@[A-Za-z0-9\\.\\-_]+>$" ),
00239 Qt::CaseInsensitive );
00240 if ( responseWithoutCRLF.indexOf( re ) != -1 ) {
00241 authenticationResults[type] << Transport::EnumAuthenticationType::APOP;
00242 }
00243
00244
00245 authenticationResults[type] << Transport::EnumAuthenticationType::CLEAR;
00246
00247
00248
00249 if ( type == Transport::EnumEncryption::TLS &&
00250 authenticationResults[Transport::EnumEncryption::None].
00251 contains( Transport::EnumAuthenticationType::APOP ) ) {
00252 authenticationResults[Transport::EnumEncryption::TLS]
00253 << Transport::EnumAuthenticationType::APOP;
00254 }
00255
00256 socket->write( QLatin1String( "CAPA" ) );
00257 return true;
00258 }
00259
00260
00261 else if( stage == 1 ) {
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271 if ( response.contains( QLatin1String( "TOP" ) ) ) {
00272 capabilityResults += ServerTest::Top;
00273 }
00274 if ( response.contains( QLatin1String( "PIPELINING" ) ) ) {
00275 capabilityResults += ServerTest::Pipelining;
00276 }
00277 if ( response.contains( QLatin1String( "UIDL" ) ) ) {
00278 capabilityResults += ServerTest::UIDL;
00279 }
00280 if ( response.contains( QLatin1String( "STLS" ) ) ) {
00281 connectionResults << Transport::EnumEncryption::TLS;
00282 popSupportsTLS = true;
00283 }
00284 socket->write( QLatin1String( "AUTH" ) );
00285 return true;
00286 }
00287
00288
00289 else if( stage == 2 ) {
00290
00291
00292
00293
00294
00295
00296 QString formattedReply = response;
00297
00298
00299 formattedReply.chop( 3 );
00300
00301
00302 formattedReply = formattedReply.right( formattedReply.size() -
00303 formattedReply.indexOf( QLatin1Char( '\n' ) ) - 1 );
00304 formattedReply =
00305 formattedReply.replace( QLatin1Char( ' ' ), QLatin1Char( '-' ) ).
00306 replace( QLatin1String( "\r\n" ), QLatin1String( " " ) );
00307
00308 authenticationResults[type] +=
00309 parseAuthenticationList( formattedReply.split( QLatin1Char( ' ' ) ) );
00310 }
00311
00312 *shouldStartTLS = popSupportsTLS;
00313 return false;
00314 }
00315
00316
00317
00318
00319
00320 void ServerTestPrivate::slotReadNormal( const QString &text )
00321 {
00322 Q_ASSERT( encryptionMode != Transport::EnumEncryption::SSL );
00323 static const int tlsHandshakeStage = 42;
00324
00325 kDebug() << "Stage" << normalStage + 1 << ", Mode" << encryptionMode;
00326
00327
00328
00329
00330
00331 if ( normalStage == tlsHandshakeStage ) {
00332 Q_ASSERT( encryptionMode == Transport::EnumEncryption::TLS );
00333 normalStage = -1;
00334 normalSocket->startTLS();
00335 return;
00336 }
00337
00338 bool shouldStartTLS = false;
00339 normalStage++;
00340
00341
00342
00343 if ( testProtocol == POP_PROTOCOL ) {
00344 if ( handlePopConversation( normalSocket, encryptionMode, normalStage, text,
00345 &shouldStartTLS ) ) {
00346 return;
00347 }
00348 } else {
00349
00350
00351 if ( normalStage == 0 ) {
00352 sendInitialCapabilityQuery( normalSocket );
00353 return;
00354 }
00355
00356 if ( text.contains( QLatin1String( "STARTTLS" ), Qt::CaseInsensitive ) ) {
00357 connectionResults << Transport::EnumEncryption::TLS;
00358 shouldStartTLS = true;
00359 }
00360 handleSMTPIMAPResponse( encryptionMode, text );
00361 }
00362
00363
00364
00365 normalSocketFinished = true;
00366
00367
00368
00369 if ( shouldStartTLS && encryptionMode == Transport::EnumEncryption::None ) {
00370 kDebug() << "Trying TLS...";
00371 connectionResults << Transport::EnumEncryption::TLS;
00372 if ( testProtocol == POP_PROTOCOL ) {
00373 normalSocket->write( QLatin1String( "STLS" ) );
00374 } else if ( testProtocol == IMAP_PROTOCOL ) {
00375 normalSocket->write( QLatin1String( "2 STARTTLS" ) );
00376 } else {
00377 normalSocket->write( QLatin1String( "STARTTLS" ) );
00378 }
00379 encryptionMode = Transport::EnumEncryption::TLS;
00380 normalStage = tlsHandshakeStage;
00381 return;
00382 }
00383
00384
00385
00386 tlsFinished = true;
00387 finalResult();
00388 }
00389
00390 void ServerTestPrivate::slotReadSecure( const QString &text )
00391 {
00392 secureStage++;
00393 if ( testProtocol == POP_PROTOCOL ) {
00394 bool dummy;
00395 if ( handlePopConversation( secureSocket, Transport::EnumEncryption::SSL,
00396 secureStage, text, &dummy ) ) {
00397 return;
00398 }
00399 } else {
00400 if ( secureStage == 0 ) {
00401 sendInitialCapabilityQuery( secureSocket );
00402 return;
00403 }
00404 handleSMTPIMAPResponse( Transport::EnumEncryption::SSL, text );
00405 }
00406 secureSocketFinished = true;
00407 finalResult();
00408 }
00409
00410 void ServerTestPrivate::slotNormalNotPossible()
00411 {
00412 normalSocketFinished = true;
00413 tlsFinished = true;
00414 finalResult();
00415 }
00416
00417 void ServerTestPrivate::slotSslPossible()
00418 {
00419 secureSocketTimer->stop();
00420 connectionResults << Transport::EnumEncryption::SSL;
00421 }
00422
00423 void ServerTestPrivate::slotSslNotPossible()
00424 {
00425 secureSocketFinished = true;
00426 finalResult();
00427 }
00428
00429 void ServerTestPrivate::slotUpdateProgress()
00430 {
00431 if ( testProgress ) {
00432 testProgress->setValue( testProgress->value() + 1 );
00433 }
00434 }
00435
00436
00437
00438 ServerTest::ServerTest( QWidget *parent )
00439 : QWidget( parent ), d( new ServerTestPrivate( this ) )
00440 {
00441 d->normalSocketTimer = new QTimer( this );
00442 d->normalSocketTimer->setSingleShot( true );
00443 connect( d->normalSocketTimer, SIGNAL( timeout() ), SLOT( slotNormalNotPossible() ) );
00444
00445 d->secureSocketTimer = new QTimer( this );
00446 d->secureSocketTimer->setSingleShot( true );
00447 connect( d->secureSocketTimer, SIGNAL( timeout() ), SLOT( slotSslNotPossible() ) );
00448
00449 d->progressTimer = new QTimer( this );
00450 connect( d->progressTimer, SIGNAL( timeout() ), SLOT( slotUpdateProgress() ) );
00451 }
00452
00453 ServerTest::~ServerTest()
00454 {
00455 delete d;
00456 }
00457
00458 void ServerTest::start()
00459 {
00460 kDebug() << d;
00461
00462 d->connectionResults.clear();
00463 d->authenticationResults.clear();
00464 d->capabilityResults.clear();
00465 d->popSupportsTLS = false;
00466 d->normalStage = -1;
00467 d->secureStage = -1;
00468 d->encryptionMode = Transport::EnumEncryption::None;
00469
00470 if ( d->testProgress ) {
00471 d->testProgress->setMaximum( 20 );
00472 d->testProgress->setValue( 0 );
00473 d->testProgress->setTextVisible( true );
00474 d->testProgress->show();
00475 d->progressTimer->start( 1000 );
00476 }
00477
00478 d->normalSocket = new MailTransport::Socket( this );
00479 d->secureSocket = new MailTransport::Socket( this );
00480 d->normalSocket->setObjectName( QLatin1String( "normal" ) );
00481 d->normalSocket->setServer( d->server );
00482 d->normalSocket->setProtocol( d->testProtocol );
00483 if ( d->testProtocol == IMAP_PROTOCOL ) {
00484 d->normalSocket->setPort( IMAP_PORT );
00485 d->secureSocket->setPort( IMAPS_PORT );
00486 } else if ( d->testProtocol == SMTP_PROTOCOL ) {
00487 d->normalSocket->setPort( SMTP_PORT );
00488 d->secureSocket->setPort( SMTPS_PORT );
00489 } else if ( d->testProtocol == POP_PROTOCOL ) {
00490 d->normalSocket->setPort( POP_PORT );
00491 d->secureSocket->setPort( POPS_PORT );
00492 }
00493
00494 if ( d->customPorts.contains( Transport::EnumEncryption::None ) ) {
00495 d->normalSocket->setPort( d->customPorts.value( Transport::EnumEncryption::None ) );
00496 }
00497 if ( d->customPorts.contains( Transport::EnumEncryption::SSL ) ) {
00498 d->secureSocket->setPort( d->customPorts.value( Transport::EnumEncryption::SSL ) );
00499 }
00500
00501 connect( d->normalSocket, SIGNAL(connected()), SLOT(slotNormalPossible()) );
00502 connect( d->normalSocket, SIGNAL(failed()), SLOT(slotNormalNotPossible()) );
00503 connect( d->normalSocket, SIGNAL(data(const QString&)),
00504 SLOT(slotReadNormal(const QString&)) );
00505 connect( d->normalSocket, SIGNAL(tlsDone()), SLOT(slotTlsDone()));
00506 d->normalSocket->reconnect();
00507 d->normalSocketTimer->start( 10000 );
00508
00509 d->secureSocket->setObjectName( QLatin1String( "secure" ) );
00510 d->secureSocket->setServer( d->server );
00511 d->secureSocket->setProtocol( d->testProtocol + QLatin1Char( 's' ) );
00512 d->secureSocket->setSecure( true );
00513 connect( d->secureSocket, SIGNAL(connected()), SLOT(slotSslPossible()) );
00514 connect( d->secureSocket, SIGNAL(failed()), SLOT(slotSslNotPossible()) );
00515 connect( d->secureSocket, SIGNAL(data(const QString&) ),
00516 SLOT(slotReadSecure(const QString&)) );
00517 d->secureSocket->reconnect();
00518 d->secureSocketTimer->start( 10000 );
00519 }
00520
00521 void ServerTest::setFakeHostname( const QString &fakeHostname )
00522 {
00523 d->fakeHostname = fakeHostname;
00524 }
00525
00526 QString ServerTest::fakeHostname()
00527 {
00528 return d->fakeHostname;
00529 }
00530
00531 void ServerTest::setServer( const QString &server )
00532 {
00533 d->server = server;
00534 }
00535
00536 void ServerTest::setPort( Transport::EnumEncryption::type encryptionMode, uint port )
00537 {
00538 Q_ASSERT( encryptionMode == Transport::EnumEncryption::None ||
00539 encryptionMode == Transport::EnumEncryption::SSL );
00540 d->customPorts.insert( encryptionMode, port );
00541 }
00542
00543 void ServerTest::setProgressBar( QProgressBar *pb )
00544 {
00545 d->testProgress = pb;
00546 }
00547
00548 void ServerTest::setProtocol( const QString &protocol )
00549 {
00550 d->testProtocol = protocol;
00551 }
00552
00553 QString ServerTest::protocol()
00554 {
00555 return d->testProtocol;
00556 }
00557
00558 QString ServerTest::server()
00559 {
00560 return d->server;
00561 }
00562
00563 int ServerTest::port( Transport::EnumEncryption::type encryptionMode )
00564 {
00565 Q_ASSERT( encryptionMode == Transport::EnumEncryption::None ||
00566 encryptionMode == Transport::EnumEncryption::SSL );
00567 if ( d->customPorts.contains( encryptionMode ) ) {
00568 return d->customPorts.value( static_cast<int>( encryptionMode ) );
00569 } else {
00570 return -1;
00571 }
00572 }
00573
00574 QProgressBar *ServerTest::progressBar()
00575 {
00576 return d->testProgress;
00577 }
00578
00579 QList< int > ServerTest::normalProtocols()
00580 {
00581 return d->authenticationResults[TransportBase::EnumEncryption::None];
00582 }
00583
00584 QList< int > ServerTest::tlsProtocols()
00585 {
00586 return d->authenticationResults[TransportBase::EnumEncryption::TLS];
00587 }
00588
00589 QList< int > ServerTest::secureProtocols()
00590 {
00591 return d->authenticationResults[Transport::EnumEncryption::SSL];
00592 }
00593
00594 QList< ServerTest::Capability > ServerTest::capabilities() const
00595 {
00596 return d->capabilityResults.toList();
00597 }
00598
00599 #include "servertest.moc"