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