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 {
00375 normalSocket->write( QLatin1String( "STARTTLS" ) );
00376 }
00377 encryptionMode = Transport::EnumEncryption::TLS;
00378 normalStage = tlsHandshakeStage;
00379 return;
00380 }
00381
00382
00383
00384 tlsFinished = true;
00385 finalResult();
00386 }
00387
00388 void ServerTestPrivate::slotReadSecure( const QString &text )
00389 {
00390 secureStage++;
00391 if ( testProtocol == POP_PROTOCOL ) {
00392 bool dummy;
00393 if ( handlePopConversation( secureSocket, Transport::EnumEncryption::SSL,
00394 secureStage, text, &dummy ) ) {
00395 return;
00396 }
00397 } else {
00398 if ( secureStage == 0 ) {
00399 sendInitialCapabilityQuery( secureSocket );
00400 return;
00401 }
00402 handleSMTPIMAPResponse( Transport::EnumEncryption::SSL, text );
00403 }
00404 secureSocketFinished = true;
00405 finalResult();
00406 }
00407
00408 void ServerTestPrivate::slotNormalNotPossible()
00409 {
00410 normalSocketFinished = true;
00411 tlsFinished = true;
00412 finalResult();
00413 }
00414
00415 void ServerTestPrivate::slotSslPossible()
00416 {
00417 secureSocketTimer->stop();
00418 connectionResults << Transport::EnumEncryption::SSL;
00419 }
00420
00421 void ServerTestPrivate::slotSslNotPossible()
00422 {
00423 secureSocketFinished = true;
00424 finalResult();
00425 }
00426
00427 void ServerTestPrivate::slotUpdateProgress()
00428 {
00429 if ( testProgress ) {
00430 testProgress->setValue( testProgress->value() + 1 );
00431 }
00432 }
00433
00434
00435
00436 ServerTest::ServerTest( QWidget *parent )
00437 : QWidget( parent ), d( new ServerTestPrivate( this ) )
00438 {
00439 d->normalSocketTimer = new QTimer( this );
00440 d->normalSocketTimer->setSingleShot( true );
00441 connect( d->normalSocketTimer, SIGNAL( timeout() ), SLOT( slotNormalNotPossible() ) );
00442
00443 d->secureSocketTimer = new QTimer( this );
00444 d->secureSocketTimer->setSingleShot( true );
00445 connect( d->secureSocketTimer, SIGNAL( timeout() ), SLOT( slotSslNotPossible() ) );
00446
00447 d->progressTimer = new QTimer( this );
00448 connect( d->progressTimer, SIGNAL( timeout() ), SLOT( slotUpdateProgress() ) );
00449 }
00450
00451 ServerTest::~ServerTest()
00452 {
00453 delete d;
00454 }
00455
00456 void ServerTest::start()
00457 {
00458 kDebug() << d;
00459
00460 d->connectionResults.clear();
00461 d->authenticationResults.clear();
00462 d->capabilityResults.clear();
00463 d->popSupportsTLS = false;
00464 d->normalStage = -1;
00465 d->secureStage = -1;
00466 d->encryptionMode = Transport::EnumEncryption::None;
00467
00468 if ( d->testProgress ) {
00469 d->testProgress->setMaximum( 20 );
00470 d->testProgress->setValue( 0 );
00471 d->testProgress->setTextVisible( true );
00472 d->testProgress->show();
00473 d->progressTimer->start( 1000 );
00474 }
00475
00476 d->normalSocket = new MailTransport::Socket( this );
00477 d->secureSocket = new MailTransport::Socket( this );
00478 d->normalSocket->setObjectName( QLatin1String( "normal" ) );
00479 d->normalSocket->setServer( d->server );
00480 d->normalSocket->setProtocol( d->testProtocol );
00481 if ( d->testProtocol == IMAP_PROTOCOL ) {
00482 d->normalSocket->setPort( IMAP_PORT );
00483 d->secureSocket->setPort( IMAPS_PORT );
00484 } else if ( d->testProtocol == SMTP_PROTOCOL ) {
00485 d->normalSocket->setPort( SMTP_PORT );
00486 d->secureSocket->setPort( SMTPS_PORT );
00487 } else if ( d->testProtocol == POP_PROTOCOL ) {
00488 d->normalSocket->setPort( POP_PORT );
00489 d->secureSocket->setPort( POPS_PORT );
00490 }
00491
00492 if ( d->customPorts.contains( Transport::EnumEncryption::None ) ) {
00493 d->normalSocket->setPort( d->customPorts.value( Transport::EnumEncryption::None ) );
00494 }
00495 if ( d->customPorts.contains( Transport::EnumEncryption::SSL ) ) {
00496 d->secureSocket->setPort( d->customPorts.value( Transport::EnumEncryption::SSL ) );
00497 }
00498
00499 connect( d->normalSocket, SIGNAL(connected()), SLOT(slotNormalPossible()) );
00500 connect( d->normalSocket, SIGNAL(failed()), SLOT(slotNormalNotPossible()) );
00501 connect( d->normalSocket, SIGNAL(data(const QString&)),
00502 SLOT(slotReadNormal(const QString&)) );
00503 connect( d->normalSocket, SIGNAL(tlsDone()), SLOT(slotTlsDone()));
00504 d->normalSocket->reconnect();
00505 d->normalSocketTimer->start( 10000 );
00506
00507 d->secureSocket->setObjectName( QLatin1String( "secure" ) );
00508 d->secureSocket->setServer( d->server );
00509 d->secureSocket->setProtocol( d->testProtocol + QLatin1Char( 's' ) );
00510 d->secureSocket->setSecure( true );
00511 connect( d->secureSocket, SIGNAL(connected()), SLOT(slotSslPossible()) );
00512 connect( d->secureSocket, SIGNAL(failed()), SLOT(slotSslNotPossible()) );
00513 connect( d->secureSocket, SIGNAL(data(const QString&) ),
00514 SLOT(slotReadSecure(const QString&)) );
00515 d->secureSocket->reconnect();
00516 d->secureSocketTimer->start( 10000 );
00517 }
00518
00519 void ServerTest::setFakeHostname( const QString &fakeHostname )
00520 {
00521 d->fakeHostname = fakeHostname;
00522 }
00523
00524 QString ServerTest::fakeHostname()
00525 {
00526 return d->fakeHostname;
00527 }
00528
00529 void ServerTest::setServer( const QString &server )
00530 {
00531 d->server = server;
00532 }
00533
00534 void ServerTest::setPort( Transport::EnumEncryption::type encryptionMode, uint port )
00535 {
00536 Q_ASSERT( encryptionMode == Transport::EnumEncryption::None ||
00537 encryptionMode == Transport::EnumEncryption::SSL );
00538 d->customPorts.insert( encryptionMode, port );
00539 }
00540
00541 void ServerTest::setProgressBar( QProgressBar *pb )
00542 {
00543 d->testProgress = pb;
00544 }
00545
00546 void ServerTest::setProtocol( const QString &protocol )
00547 {
00548 d->testProtocol = protocol;
00549 }
00550
00551 QString ServerTest::protocol()
00552 {
00553 return d->testProtocol;
00554 }
00555
00556 QString ServerTest::server()
00557 {
00558 return d->server;
00559 }
00560
00561 int ServerTest::port( Transport::EnumEncryption::type encryptionMode )
00562 {
00563 Q_ASSERT( encryptionMode == Transport::EnumEncryption::None ||
00564 encryptionMode == Transport::EnumEncryption::SSL );
00565 if ( d->customPorts.contains( encryptionMode ) ) {
00566 return d->customPorts.value( static_cast<int>( encryptionMode ) );
00567 } else {
00568 return -1;
00569 }
00570 }
00571
00572 QProgressBar *ServerTest::progressBar()
00573 {
00574 return d->testProgress;
00575 }
00576
00577 QList< int > ServerTest::normalProtocols()
00578 {
00579 return d->authenticationResults[TransportBase::EnumEncryption::None];
00580 }
00581
00582 QList< int > ServerTest::tlsProtocols()
00583 {
00584 return d->authenticationResults[TransportBase::EnumEncryption::TLS];
00585 }
00586
00587 QList< int > ServerTest::secureProtocols()
00588 {
00589 return d->authenticationResults[Transport::EnumEncryption::SSL];
00590 }
00591
00592 QList< ServerTest::Capability > ServerTest::capabilities() const
00593 {
00594 return d->capabilityResults.toList();
00595 }
00596
00597 #include "servertest.moc"