00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00027 #ifdef HAVE_CONFIG_H
00028 # include <config.h>
00029 #endif
00030
00031 extern "C" {
00032 #include <sasl/sasl.h>
00033 }
00034 #include "sieve.h"
00035
00036 #include <kdebug.h>
00037 #include <kinstance.h>
00038 #include <klocale.h>
00039 #include <kurl.h>
00040 #include <kmdcodec.h>
00041 #include <kglobal.h>
00042
00043 #include <qcstring.h>
00044 #include <qregexp.h>
00045
00046 #include <cstdlib>
00047 using std::exit;
00048 #include <sys/stat.h>
00049
00050 #include <kdepimmacros.h>
00051
00052 static const int debugArea = 7122;
00053
00054 static inline
00055 #ifdef NDEBUG
00056 kndbgstream ksDebug() { return kdDebug( debugArea ); }
00057 kndbgstream ksDebug( bool cond ) { return kdDebug( cond, debugArea ); }
00058 #else
00059 kdbgstream ksDebug() { return kdDebug( debugArea ); }
00060 kdbgstream ksDebug( bool cond ) { return kdDebug( cond, debugArea ); }
00061 #endif
00062
00063 #define SIEVE_DEFAULT_PORT 2000
00064
00065 static sasl_callback_t callbacks[] = {
00066 { SASL_CB_ECHOPROMPT, NULL, NULL },
00067 { SASL_CB_NOECHOPROMPT, NULL, NULL },
00068 { SASL_CB_GETREALM, NULL, NULL },
00069 { SASL_CB_USER, NULL, NULL },
00070 { SASL_CB_AUTHNAME, NULL, NULL },
00071 { SASL_CB_PASS, NULL, NULL },
00072 { SASL_CB_CANON_USER, NULL, NULL },
00073 { SASL_CB_LIST_END, NULL, NULL }
00074 };
00075
00076 static const unsigned int SIEVE_DEFAULT_RECIEVE_BUFFER = 512;
00077
00078 using namespace KIO;
00079 extern "C"
00080 {
00081 KDE_EXPORT int kdemain(int argc, char **argv)
00082 {
00083 KInstance instance("kio_sieve" );
00084
00085 ksDebug() << "*** Starting kio_sieve " << endl;
00086
00087 if (argc != 4) {
00088 ksDebug() << "Usage: kio_sieve protocol domain-socket1 domain-socket2" << endl;
00089 exit(-1);
00090 }
00091
00092 if ( sasl_client_init( NULL ) != SASL_OK ) {
00093 fprintf(stderr, "SASL library initialization failed!\n");
00094 ::exit (-1);
00095 }
00096
00097 kio_sieveProtocol slave(argv[2], argv[3]);
00098 slave.dispatchLoop();
00099
00100 sasl_done();
00101
00102 ksDebug() << "*** kio_sieve Done" << endl;
00103 return 0;
00104 }
00105 }
00106
00107
00108 kio_sieveResponse::kio_sieveResponse()
00109 {
00110 clear();
00111 }
00112
00113
00114 const uint& kio_sieveResponse::getType() const
00115 {
00116 return rType;
00117 }
00118
00119
00120 const uint kio_sieveResponse::getQuantity() const
00121 {
00122 return quantity;
00123 }
00124
00125
00126 const QCString& kio_sieveResponse::getAction() const
00127 {
00128 return key;
00129 }
00130
00131
00132 const QCString& kio_sieveResponse::getKey() const
00133 {
00134 return key;
00135 }
00136
00137
00138 const QCString& kio_sieveResponse::getVal() const
00139 {
00140 return val;
00141 }
00142
00143
00144 const QCString& kio_sieveResponse::getExtra() const
00145 {
00146 return extra;
00147 }
00148
00149
00150 void kio_sieveResponse::setQuantity(const uint& newQty)
00151 {
00152 rType = QUANTITY;
00153 quantity = newQty;
00154 }
00155
00156
00157 void kio_sieveResponse::setAction(const QCString& newAction)
00158 {
00159 rType = ACTION;
00160 key = newAction.copy();
00161 }
00162
00163
00164 void kio_sieveResponse::setKey(const QCString& newKey)
00165 {
00166 rType = KEY_VAL_PAIR;
00167 key = newKey.copy();
00168 }
00169
00170
00171 void kio_sieveResponse::setVal(const QCString& newVal)
00172 {
00173 val = newVal.copy();
00174 }
00175
00176
00177 void kio_sieveResponse::setExtra(const QCString& newExtra)
00178 {
00179 extra = newExtra.copy();
00180 }
00181
00182
00183 void kio_sieveResponse::clear()
00184 {
00185 rType = NONE;
00186 extra = key = val = QCString("");
00187 quantity = 0;
00188 }
00189
00190
00191 kio_sieveProtocol::kio_sieveProtocol(const QCString &pool_socket, const QCString &app_socket)
00192 : TCPSlaveBase( SIEVE_DEFAULT_PORT, "sieve", pool_socket, app_socket, false)
00193 , m_connMode(NORMAL)
00194 , m_supportsTLS(false)
00195 , m_shouldBeConnected(false)
00196 {
00197 }
00198
00199
00200 kio_sieveProtocol::~kio_sieveProtocol()
00201 {
00202 if ( isConnectionValid() )
00203 disconnect();
00204 }
00205
00206
00207 void kio_sieveProtocol::setHost (const QString &host, int port, const QString &user, const QString &pass)
00208 {
00209 if ( isConnectionValid() &&
00210 ( m_sServer != host ||
00211 m_iPort != port ||
00212 m_sUser != user ||
00213 m_sPass != pass ) ) {
00214 disconnect();
00215 }
00216 m_sServer = host;
00217 m_iPort = port ? port : m_iDefaultPort;
00218 m_sUser = user;
00219 m_sPass = pass;
00220 m_supportsTLS = false;
00221 }
00222
00223
00224 void kio_sieveProtocol::openConnection()
00225 {
00226 m_connMode = CONNECTION_ORIENTED;
00227 connect();
00228 }
00229
00230 bool kio_sieveProtocol::parseCapabilities(bool requestCapabilities)
00231 {
00232 ksDebug() << k_funcinfo << endl;
00233
00234
00235 bool ret = false;
00236
00237 if (requestCapabilities) {
00238 sendData("CAPABILITY");
00239 }
00240
00241 while (receiveData()) {
00242 ksDebug() << "Looping receive" << endl;
00243
00244 if (r.getType() == kio_sieveResponse::ACTION) {
00245 if ( r.getAction().contains("ok", false) != -1 ) {
00246 ksDebug() << "Sieve server ready & awaiting authentication." << endl;
00247 break;
00248 } else
00249 ksDebug() << "Unknown action " << r.getAction() << "." << endl;
00250
00251 } else if (r.getKey() == "IMPLEMENTATION") {
00252 if (r.getVal().contains("sieve", false) != -1) {
00253 ksDebug() << "Connected to Sieve server: " << r.getVal() << endl;
00254 ret = true;
00255 setMetaData("implementation", r.getVal());
00256 m_implementation = r.getVal();
00257 }
00258
00259 } else if (r.getKey() == "SASL") {
00260
00261 m_sasl_caps = QStringList::split(' ', r.getVal());
00262 ksDebug() << "Server SASL authentication methods: " << m_sasl_caps.join(", ") << endl;
00263 setMetaData("saslMethods", r.getVal());
00264
00265 } else if (r.getKey() == "SIEVE") {
00266
00267 ksDebug() << "Server script capabilities: " << QStringList::split(' ', r.getVal()).join(", ") << endl;
00268 setMetaData("sieveExtensions", r.getVal());
00269
00270 } else if (r.getKey() == "STARTTLS") {
00271
00272 ksDebug() << "Server supports TLS" << endl;
00273 m_supportsTLS = true;
00274 setMetaData("tlsSupported", "true");
00275
00276 } else {
00277 ksDebug() << "Unrecognised key " << r.getKey() << endl;
00278 }
00279 }
00280
00281 if (!m_supportsTLS) {
00282 setMetaData("tlsSupported", "false");
00283 }
00284
00285 return ret;
00286 }
00287
00288
00289
00294 void kio_sieveProtocol::changeCheck( const KURL &url )
00295 {
00296 QString auth;
00297
00298 if (!metaData("sasl").isEmpty())
00299 auth = metaData("sasl").upper();
00300 else {
00301 QString query = url.query();
00302 if ( query.startsWith("?") ) query.remove( 0, 1 );
00303 QStringList q = QStringList::split( ",", query );
00304 QStringList::iterator it;
00305
00306 for ( it = q.begin(); it != q.end(); ++it ) {
00307 if ( ( (*it).section('=',0,0) ).lower() == "x-mech" ) {
00308 auth = ( (*it).section('=',1) ).upper();
00309 break;
00310 }
00311 }
00312 }
00313 ksDebug() << "auth: " << auth << " m_sAuth: " << m_sAuth << endl;
00314 if ( m_sAuth != auth ) {
00315 m_sAuth = auth;
00316 if ( isConnectionValid() )
00317 disconnect();
00318 }
00319 }
00320
00321
00326 bool kio_sieveProtocol::connect(bool useTLSIfAvailable)
00327 {
00328 ksDebug() << k_funcinfo << endl;
00329
00330 if (isConnectionValid()) return true;
00331
00332 infoMessage(i18n("Connecting to %1...").arg( m_sServer));
00333
00334 if (m_connMode == CONNECTION_ORIENTED && m_shouldBeConnected) {
00335 error(ERR_CONNECTION_BROKEN, i18n("The connection to the server was lost."));
00336 return false;
00337 }
00338
00339 setBlockConnection(true);
00340
00341 if (!connectToHost(m_sServer, m_iPort, true)) {
00342 return false;
00343 }
00344
00345 if (!parseCapabilities()) {
00346 closeDescriptor();
00347 error(ERR_UNSUPPORTED_PROTOCOL, i18n("Server identification failed."));
00348 return false;
00349 }
00350
00351
00352
00353 if (useTLSIfAvailable && m_supportsTLS && canUseTLS()) {
00354 sendData("STARTTLS");
00355 if (operationSuccessful()) {
00356 ksDebug() << "TLS has been accepted. Starting TLS..." << endl
00357 << "WARNING this is untested and may fail." << endl;
00358 int retval = startTLS();
00359 if (retval == 1) {
00360 ksDebug() << "TLS enabled successfully." << endl;
00361
00362 parseCapabilities( requestCapabilitiesAfterStartTLS() );
00363 } else {
00364 ksDebug() << "TLS initiation failed, code " << retval << endl;
00365 disconnect(true);
00366 return connect(false);
00367
00368 }
00369 } else
00370 ksDebug() << "Server incapable of TLS. Transmitted documents will be unencrypted." << endl;
00371 } else
00372 ksDebug() << "We are incapable of TLS. Transmitted documents will be unencrypted." << endl;
00373
00374 infoMessage(i18n("Authenticating user..."));
00375 if (!authenticate()) {
00376 disconnect();
00377 error(ERR_COULD_NOT_AUTHENTICATE, i18n("Authentication failed."));
00378 return false;
00379 }
00380
00381 m_shouldBeConnected = true;
00382 return true;
00383 }
00384
00385
00386 void kio_sieveProtocol::closeConnection()
00387 {
00388 m_connMode = CONNECTION_ORIENTED;
00389 disconnect();
00390 }
00391
00392
00393 void kio_sieveProtocol::disconnect(bool forcibly)
00394 {
00395 if (!forcibly) {
00396 sendData("LOGOUT");
00397
00398
00399
00400
00401
00402
00403 }
00404
00405 closeDescriptor();
00406 m_shouldBeConnected = false;
00407 }
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418 void kio_sieveProtocol::special(const QByteArray &data)
00419 {
00420 int tmp;
00421 QDataStream stream(data, IO_ReadOnly);
00422 KURL url;
00423
00424 stream >> tmp;
00425
00426 switch (tmp) {
00427 case 1:
00428 stream >> url;
00429 if (!activate(url))
00430 return;
00431 break;
00432 case 2:
00433 if (!deactivate())
00434 return;
00435 break;
00436 case 3:
00437 parseCapabilities(true);
00438 break;
00439 }
00440
00441 infoMessage(i18n("Done."));
00442
00443 finished();
00444 }
00445
00446
00447 bool kio_sieveProtocol::activate(const KURL& url)
00448 {
00449 changeCheck( url );
00450 if (!connect())
00451 return false;
00452
00453 infoMessage(i18n("Activating script..."));
00454
00455 QString filename = url.fileName(false);
00456
00457 if (filename.isEmpty()) {
00458 error(ERR_DOES_NOT_EXIST, url.prettyURL());
00459 return false;
00460 }
00461
00462 if (!sendData("SETACTIVE \"" + filename.utf8() + "\""))
00463 return false;
00464
00465 if (operationSuccessful()) {
00466 ksDebug() << "Script activation complete." << endl;
00467 return true;
00468 } else {
00469 error(ERR_INTERNAL_SERVER, i18n("There was an error activating the script."));
00470 return false;
00471 }
00472 }
00473
00474
00475 bool kio_sieveProtocol::deactivate()
00476 {
00477 if (!connect())
00478 return false;
00479
00480 if (!sendData("SETACTIVE \"\""))
00481 return false;
00482
00483 if (operationSuccessful()) {
00484 ksDebug() << "Script deactivation complete." << endl;
00485 return true;
00486 } else {
00487 error(ERR_INTERNAL_SERVER, i18n("There was an error deactivating the script."));
00488 return false;
00489 }
00490 }
00491
00492 static void append_lf2crlf( QByteArray & out, const QByteArray & in ) {
00493 if ( in.isEmpty() )
00494 return;
00495 const unsigned int oldOutSize = out.size();
00496 out.resize( oldOutSize + 2 * in.size() );
00497 const char * s = in.begin();
00498 const char * const end = in.end();
00499 char * d = out.begin() + oldOutSize;
00500 char last = '\0';
00501 while ( s < end ) {
00502 if ( *s == '\n' && last != '\r' )
00503 *d++ = '\r';
00504 *d++ = last = *s++;
00505 }
00506 out.resize( d - out.begin() );
00507 }
00508
00509 void kio_sieveProtocol::put(const KURL& url, int , bool , bool )
00510 {
00511 changeCheck( url );
00512 if (!connect())
00513 return;
00514
00515 infoMessage(i18n("Sending data..."));
00516
00517 QString filename = url.fileName(false);
00518
00519 if (filename.isEmpty()) {
00520 error(ERR_MALFORMED_URL, url.prettyURL());
00521 return;
00522 }
00523
00524 QByteArray data;
00525 for (;;) {
00526 dataReq();
00527 QByteArray buffer;
00528 const int newSize = readData(buffer);
00529 append_lf2crlf( data, buffer );
00530 if ( newSize < 0 ) {
00531
00532 error(ERR_COULD_NOT_READ, i18n("KIO data supply error."));
00533 return;
00534 }
00535 if ( newSize == 0 )
00536 break;
00537 }
00538
00539
00540 int bufLen = (int)data.size();
00541 totalSize(bufLen);
00542
00543
00544
00545
00546
00547
00548
00549
00550
00551
00552 #ifndef HAVE_BROKEN_TIMSIEVED
00553
00554 if (!sendData("HAVESPACE \"" + filename.utf8() + "\" "
00555 + QCString().setNum( bufLen )))
00556 return;
00557
00558 if (!operationSuccessful()) {
00559 error(ERR_DISK_FULL, i18n("Quota exceeded"));
00560 return;
00561 }
00562 #endif
00563
00564 if (!sendData("PUTSCRIPT \"" + filename.utf8() + "\" {"
00565 + QCString().setNum( bufLen ) + "+}"))
00566 return;
00567
00568
00569
00570
00571
00572
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585
00586
00587
00588
00589
00590
00591 if (write(data, bufLen) != bufLen) {
00592 error(ERR_COULD_NOT_WRITE, i18n("Network error."));
00593 disconnect(true);
00594 return;
00595 }
00596
00597
00598 if (!sendData(""))
00599 return;
00600
00601 processedSize(bufLen);
00602
00603 infoMessage(i18n("Verifying upload completion..."));
00604
00605 if (operationSuccessful())
00606 ksDebug() << "Script upload complete." << endl;
00607
00608 else {
00609
00610
00611
00612
00613 if (r.getAction().length() > 3) {
00614
00615 QCString extra = r.getAction().right(r.getAction().length() - 3);
00616
00617
00618 receiveData(false, &extra);
00619
00620 if (r.getType() == kio_sieveResponse::QUANTITY) {
00621
00622 uint len = r.getQuantity();
00623
00624 QCString errmsg(len + 1);
00625
00626 read(errmsg.data(), len);
00627
00628 error(ERR_INTERNAL_SERVER,
00629 i18n("The script did not upload successfully.\n"
00630 "This is probably due to errors in the script.\n"
00631 "The server responded:\n%1").arg(errmsg));
00632
00633
00634 receiveData();
00635 } else if (r.getType() == kio_sieveResponse::KEY_VAL_PAIR) {
00636 error(ERR_INTERNAL_SERVER,
00637 i18n("The script did not upload successfully.\n"
00638 "This is probably due to errors in the script.\n"
00639 "The server responded:\n%1").arg(r.getKey()));
00640 } else
00641 error(ERR_INTERNAL_SERVER,
00642 i18n("The script did not upload successfully.\n"
00643 "The script may contain errors."));
00644 } else
00645 error(ERR_INTERNAL_SERVER,
00646 i18n("The script did not upload successfully.\n"
00647 "The script may contain errors."));
00648 }
00649
00650
00651
00652
00653 infoMessage(i18n("Done."));
00654
00655 finished();
00656 }
00657
00658 static void inplace_crlf2lf( QByteArray & in ) {
00659 if ( in.isEmpty() )
00660 return;
00661 QByteArray & out = in;
00662 const char * s = in.begin();
00663 const char * const end = in.end();
00664 char * d = out.begin();
00665 char last = '\0';
00666 while ( s < end ) {
00667 if ( *s == '\n' && last == '\r' )
00668 --d;
00669 *d++ = last = *s++;
00670 }
00671 out.resize( d - out.begin() );
00672 }
00673
00674
00675 void kio_sieveProtocol::get(const KURL& url)
00676 {
00677 changeCheck( url );
00678 if (!connect())
00679 return;
00680
00681 infoMessage(i18n("Retrieving data..."));
00682
00683 QString filename = url.fileName(false);
00684
00685 if (filename.isEmpty()) {
00686 error(ERR_MALFORMED_URL, url.prettyURL());
00687 return;
00688 }
00689
00690
00691
00692 if (!sendData("GETSCRIPT \"" + filename.utf8() + "\""))
00693 return;
00694
00695 if (receiveData() && r.getType() == kio_sieveResponse::QUANTITY) {
00696
00697 ssize_t total_len = r.getQuantity();
00698 totalSize( total_len );
00699
00700 int recv_len = 0;
00701 do {
00702
00703 if ( !waitForResponse( 600 ) ) {
00704 error( KIO::ERR_SERVER_TIMEOUT, m_sServer );
00705 disconnect( true );
00706 return;
00707 }
00708
00709
00710
00711
00712 QByteArray dat( kMin( total_len - recv_len, ssize_t(64 * 1024 )) );
00713 ssize_t this_recv_len = read( dat.data(), dat.size() );
00714
00715 if ( this_recv_len < 1 && !isConnectionValid() ) {
00716 error( KIO::ERR_CONNECTION_BROKEN, m_sServer );
00717 disconnect( true );
00718 return;
00719 }
00720
00721 dat.resize( this_recv_len );
00722 inplace_crlf2lf( dat );
00723
00724 data( dat );
00725
00726 recv_len += this_recv_len;
00727 processedSize( recv_len );
00728 } while ( recv_len < total_len );
00729
00730 infoMessage(i18n("Finishing up...") );
00731 data(QByteArray());
00732
00733 if (operationSuccessful())
00734 ksDebug() << "Script retrieval complete." << endl;
00735 else
00736 ksDebug() << "Script retrieval failed." << endl;
00737 } else {
00738 error(ERR_UNSUPPORTED_PROTOCOL, i18n("A protocol error occurred "
00739 "while trying to negotiate script downloading."));
00740 return;
00741 }
00742
00743 infoMessage(i18n("Done."));
00744 finished();
00745 }
00746
00747 void kio_sieveProtocol::del(const KURL &url, bool isfile)
00748 {
00749 if (!isfile) {
00750 error(ERR_INTERNAL, i18n("Folders are not supported."));
00751 return;
00752 }
00753
00754 changeCheck( url );
00755 if (!connect())
00756 return;
00757
00758 infoMessage(i18n("Deleting file..."));
00759
00760 QString filename = url.fileName(false);
00761
00762 if (filename.isEmpty()) {
00763 error(ERR_MALFORMED_URL, url.prettyURL());
00764 return;
00765 }
00766
00767 if (!sendData("DELETESCRIPT \"" + filename.utf8() + "\""))
00768 return;
00769
00770 if (operationSuccessful())
00771 ksDebug() << "Script deletion successful." << endl;
00772 else {
00773 error(ERR_INTERNAL_SERVER, i18n("The server would not delete the file."));
00774 return;
00775 }
00776
00777 infoMessage(i18n("Done."));
00778
00779 finished();
00780 }
00781
00782 void kio_sieveProtocol::chmod(const KURL& url, int permissions)
00783 {
00784 switch ( permissions ) {
00785 case 0700:
00786 activate(url);
00787 break;
00788 case 0600:
00789 deactivate();
00790 break;
00791 default:
00792 error(ERR_CANNOT_CHMOD, i18n("Cannot chmod to anything but 0700 (active) or 0600 (inactive script)."));
00793 return;
00794 }
00795
00796 finished();
00797 }
00798
00799 #if defined(_AIX) && defined(stat)
00800 #undef stat
00801 #endif
00802
00803 void kio_sieveProtocol::stat(const KURL& url)
00804 {
00805 changeCheck( url );
00806 if (!connect())
00807 return;
00808
00809 UDSEntry entry;
00810
00811 QString filename = url.fileName(false);
00812
00813 if (filename.isEmpty()) {
00814 UDSAtom atom;
00815 atom.m_uds = KIO::UDS_NAME;
00816 atom.m_str = "/";
00817 entry.append(atom);
00818
00819 atom.m_uds = KIO::UDS_FILE_TYPE;
00820 atom.m_long = S_IFDIR;
00821 entry.append(atom);
00822
00823 atom.m_uds = KIO::UDS_ACCESS;
00824 atom.m_long = 0700;
00825 entry.append(atom);
00826
00827 statEntry(entry);
00828
00829 } else {
00830 if (!sendData("LISTSCRIPTS"))
00831 return;
00832
00833 while(receiveData()) {
00834 if (r.getType() == kio_sieveResponse::ACTION) {
00835 if (r.getAction().contains("OK", false) == 1)
00836
00837 break;
00838
00839 } else
00840 if (filename == QString::fromUtf8(r.getKey())) {
00841 entry.clear();
00842
00843 UDSAtom atom;
00844 atom.m_uds = KIO::UDS_NAME;
00845 atom.m_str = QString::fromUtf8(r.getKey());
00846 entry.append(atom);
00847
00848 atom.m_uds = KIO::UDS_FILE_TYPE;
00849 atom.m_long = S_IFREG;
00850 entry.append(atom);
00851
00852 atom.m_uds = KIO::UDS_ACCESS;
00853 if ( r.getExtra() == "ACTIVE" )
00854 atom.m_long = 0700;
00855 else
00856 atom.m_long = 0600;
00857 entry.append(atom);
00858
00859 atom.m_uds = KIO::UDS_MIME_TYPE;
00860 atom.m_str = "application/sieve";
00861 entry.append(atom);
00862
00863
00864
00865 statEntry(entry);
00866
00867
00868 }
00869 }
00870 }
00871
00872 finished();
00873 }
00874
00875 void kio_sieveProtocol::listDir(const KURL& url)
00876 {
00877 changeCheck( url );
00878 if (!connect())
00879 return;
00880
00881 if (!sendData("LISTSCRIPTS"))
00882 return;
00883
00884 UDSEntry entry;
00885
00886 while(receiveData()) {
00887 if (r.getType() == kio_sieveResponse::ACTION) {
00888 if (r.getAction().contains("OK", false) == 1)
00889
00890 break;
00891
00892 } else {
00893 entry.clear();
00894
00895 UDSAtom atom;
00896 atom.m_uds = KIO::UDS_NAME;
00897 atom.m_str = QString::fromUtf8(r.getKey());
00898 entry.append(atom);
00899
00900 atom.m_uds = KIO::UDS_FILE_TYPE;
00901 atom.m_long = S_IFREG;
00902 entry.append(atom);
00903
00904 atom.m_uds = KIO::UDS_ACCESS;
00905 if ( r.getExtra() == "ACTIVE" )
00906 atom.m_long = 0700;
00907 else
00908 atom.m_long = 0600;
00909 entry.append(atom);
00910
00911 atom.m_uds = KIO::UDS_MIME_TYPE;
00912 atom.m_str = "application/sieve";
00913 entry.append(atom);
00914
00915
00916
00917 ksDebug() << "Listing script " << r.getKey() << endl;
00918 listEntry(entry , false);
00919 }
00920 }
00921
00922 listEntry(entry, true);
00923
00924 finished();
00925 }
00926
00927
00928 bool kio_sieveProtocol::saslInteract( void *in, AuthInfo &ai )
00929 {
00930 ksDebug() << "sasl_interact" << endl;
00931 sasl_interact_t *interact = ( sasl_interact_t * ) in;
00932
00933
00934
00935 for ( ; interact->id != SASL_CB_LIST_END; interact++ ) {
00936 if ( interact->id == SASL_CB_AUTHNAME ||
00937 interact->id == SASL_CB_PASS ) {
00938
00939 if (m_sUser.isEmpty() || m_sPass.isEmpty()) {
00940 if (!openPassDlg(ai)) {
00941 error(ERR_ABORTED, i18n("No authentication details supplied."));
00942 return false;
00943 }
00944 m_sUser = ai.username;
00945 m_sPass = ai.password;
00946 }
00947 break;
00948 }
00949 }
00950
00951 interact = ( sasl_interact_t * ) in;
00952 while( interact->id != SASL_CB_LIST_END ) {
00953 ksDebug() << "SASL_INTERACT id: " << interact->id << endl;
00954 switch( interact->id ) {
00955 case SASL_CB_USER:
00956 case SASL_CB_AUTHNAME:
00957 ksDebug() << "SASL_CB_[AUTHNAME|USER]: '" << m_sUser << "'" << endl;
00958 interact->result = strdup( m_sUser.utf8() );
00959 interact->len = strlen( (const char *) interact->result );
00960 break;
00961 case SASL_CB_PASS:
00962 ksDebug() << "SASL_CB_PASS: [hidden] " << endl;
00963 interact->result = strdup( m_sPass.utf8() );
00964 interact->len = strlen( (const char *) interact->result );
00965 break;
00966 default:
00967 interact->result = NULL; interact->len = 0;
00968 break;
00969 }
00970 interact++;
00971 }
00972 return true;
00973 }
00974
00975 #define SASLERROR error(ERR_COULD_NOT_AUTHENTICATE, i18n("An error occurred during authentication: %1").arg( \
00976 QString::fromUtf8( sasl_errdetail( conn ) )));
00977
00978 bool kio_sieveProtocol::authenticate()
00979 {
00980 int result;
00981 sasl_conn_t *conn = NULL;
00982 sasl_interact_t *client_interact = NULL;
00983 const char *out = NULL;
00984 uint outlen;
00985 const char *mechusing = NULL;
00986 QByteArray challenge, tmp;
00987
00988
00989
00990
00991
00992 AuthInfo ai;
00993 ai.url.setProtocol("sieve");
00994 ai.url.setHost(m_sServer);
00995 ai.url.setPort(m_iPort);
00996 ai.username = m_sUser;
00997 ai.password = m_sPass;
00998 ai.keepPassword = true;
00999 ai.caption = i18n("Sieve Authentication Details");
01000 ai.comment = i18n("Please enter your authentication details for your sieve account "
01001 "(usually the same as your email password):");
01002
01003 result = sasl_client_new( "sieve",
01004 m_sServer.latin1(),
01005 0, 0, callbacks, 0, &conn );
01006
01007 if ( result != SASL_OK ) {
01008 ksDebug() << "sasl_client_new failed with: " << result << endl;
01009 SASLERROR
01010 return false;
01011 }
01012
01013 QStringList strList;
01014
01015
01016 if ( !m_sAuth.isEmpty() )
01017 strList.append( m_sAuth );
01018 else
01019 strList = m_sasl_caps;
01020
01021 do {
01022 result = sasl_client_start(conn, strList.join(" ").latin1(), &client_interact,
01023 &out, &outlen, &mechusing);
01024
01025 if (result == SASL_INTERACT)
01026 if ( !saslInteract( client_interact, ai ) ) {
01027 sasl_dispose( &conn );
01028 return false;
01029 };
01030 } while ( result == SASL_INTERACT );
01031
01032 if ( result != SASL_CONTINUE && result != SASL_OK ) {
01033 ksDebug() << "sasl_client_start failed with: " << result << endl;
01034 SASLERROR
01035 sasl_dispose( &conn );
01036 return false;
01037 }
01038
01039 ksDebug() << "Preferred authentication method is " << mechusing << "." << endl;
01040
01041 QString firstCommand = "AUTHENTICATE \"" + QString::fromLatin1( mechusing ) + "\"";
01042 tmp.setRawData( out, outlen );
01043 KCodecs::base64Encode( tmp, challenge );
01044 tmp.resetRawData( out, outlen );
01045 if ( !challenge.isEmpty() ) {
01046 firstCommand += " \"";
01047 firstCommand += QString::fromLatin1( challenge.data(), challenge.size() );
01048 firstCommand += "\"";
01049 }
01050
01051 if (!sendData( firstCommand.latin1() ))
01052 return false;
01053
01054 QCString command;
01055
01056 do {
01057 receiveData();
01058
01059 if (operationResult() != OTHER)
01060 break;
01061
01062 ksDebug() << "Challenge len " << r.getQuantity() << endl;
01063
01064 if (r.getType() != kio_sieveResponse::QUANTITY) {
01065 sasl_dispose( &conn );
01066 error(ERR_SLAVE_DEFINED,
01067 i18n("A protocol error occurred during authentication.\n"
01068 "Choose a different authentication method to %1.").arg(mechusing));
01069 return false;
01070 }
01071
01072 uint qty = r.getQuantity();
01073
01074 receiveData();
01075
01076 if (r.getType() != kio_sieveResponse::ACTION && r.getAction().length() != qty) {
01077 sasl_dispose( &conn );
01078 error(ERR_UNSUPPORTED_PROTOCOL,
01079 i18n("A protocol error occurred during authentication.\n"
01080 "Choose a different authentication method to %1.").arg(mechusing));
01081 return false;
01082 }
01083
01084 tmp.setRawData( r.getAction().data(), qty );
01085 KCodecs::base64Decode( tmp, challenge );
01086 tmp.resetRawData( r.getAction().data(), qty );
01087
01088
01089
01090 do {
01091 result = sasl_client_step(conn, challenge.isEmpty() ? 0 : challenge.data(),
01092 challenge.size(),
01093 &client_interact,
01094 &out, &outlen);
01095
01096 if (result == SASL_INTERACT)
01097 if ( !saslInteract( client_interact, ai ) ) {
01098 sasl_dispose( &conn );
01099 return false;
01100 };
01101 } while ( result == SASL_INTERACT );
01102
01103 ksDebug() << "sasl_client_step: " << result << endl;
01104 if ( result != SASL_CONTINUE && result != SASL_OK ) {
01105 ksDebug() << "sasl_client_step failed with: " << result << endl;
01106 SASLERROR
01107 sasl_dispose( &conn );
01108 return false;
01109 }
01110
01111 tmp.setRawData( out, outlen );
01112 KCodecs::base64Encode( tmp, challenge );
01113 tmp.resetRawData( out, outlen );
01114 sendData("\"" + QCString( challenge.data(), challenge.size()+1 ) + "\"");
01115
01116
01117 } while ( true );
01118
01119 ksDebug() << "Challenges finished." << endl;
01120 sasl_dispose( &conn );
01121
01122 if (operationResult() == OK) {
01123
01124 return true;
01125 } else {
01126
01127 error(ERR_COULD_NOT_AUTHENTICATE, i18n("Authentication failed.\nMost likely the password is wrong.\nThe server responded:\n%1").arg( r.getAction() ) );
01128 return false;
01129 }
01130 }
01131
01132
01133 void kio_sieveProtocol::mimetype(const KURL & url)
01134 {
01135 ksDebug() << "Requesting mimetype for " << url.prettyURL() << endl;
01136
01137 if (url.fileName(false).isEmpty())
01138 mimeType( "inode/directory" );
01139 else
01140 mimeType( "application/sieve" );
01141
01142 finished();
01143 }
01144
01145
01146
01147 bool kio_sieveProtocol::sendData(const QCString &data)
01148 {
01149 QCString write_buf = data + "\r\n";
01150
01151
01152
01153
01154 ssize_t write_buf_len = write_buf.length();
01155 if (write(write_buf.data(), write_buf_len) != write_buf_len) {
01156 error(ERR_COULD_NOT_WRITE, i18n("Network error."));
01157 disconnect(true);
01158 return false;
01159 }
01160
01161 return true;
01162 }
01163
01164
01165 bool kio_sieveProtocol::receiveData(bool waitForData, QCString *reparse)
01166 {
01167 QCString interpret;
01168 int start, end;
01169
01170 if (!reparse) {
01171 if (!waitForData)
01172
01173 if (atEnd()) return false;
01174
01175
01176 char buffer[SIEVE_DEFAULT_RECIEVE_BUFFER];
01177 readLine(buffer, SIEVE_DEFAULT_RECIEVE_BUFFER - 1);
01178 buffer[SIEVE_DEFAULT_RECIEVE_BUFFER-1] = '\0';
01179
01180
01181 interpret = QCString(buffer).left(qstrlen(buffer) - 2);
01182
01183 } else {
01184 interpret = reparse->copy();
01185 }
01186
01187 r.clear();
01188
01189
01190
01191 switch(interpret[0]) {
01192 case '{':
01193 {
01194
01195 start = 0;
01196 end = interpret.find("+}", start + 1);
01197
01198 if ( end == -1 )
01199 end = interpret.find('}', start + 1);
01200
01201 bool ok = false;
01202 r.setQuantity(interpret.mid(start + 1, end - start - 1).toUInt( &ok ));
01203 if (!ok) {
01204 disconnect();
01205 error(ERR_INTERNAL_SERVER, i18n("A protocol error occurred."));
01206 return false;
01207 }
01208
01209 return true;
01210 }
01211 case '"':
01212
01213 break;
01214 default:
01215
01216 r.setAction(interpret);
01217 return true;
01218 }
01219
01220 start = 0;
01221
01222 end = interpret.find(34, start + 1);
01223 if (end == -1) {
01224 ksDebug() << "Possible insufficient buffer size." << endl;
01225 r.setKey(interpret.right(interpret.length() - start));
01226 return true;
01227 }
01228
01229 r.setKey(interpret.mid(start + 1, end - start - 1));
01230
01231 start = interpret.find(34, end + 1);
01232 if (start == -1) {
01233 if ((int)interpret.length() > end)
01234
01235 r.setExtra(interpret.right(interpret.length() - end - 2));
01236
01237 return true;
01238 }
01239
01240 end = interpret.find(34, start + 1);
01241 if (end == -1) {
01242 ksDebug() << "Possible insufficient buffer size." << endl;
01243 r.setVal(interpret.right(interpret.length() - start));
01244 return true;
01245 }
01246
01247 r.setVal(interpret.mid(start + 1, end - start - 1));
01248 return true;
01249 }
01250
01251 bool kio_sieveProtocol::operationSuccessful()
01252 {
01253 while (receiveData(false)) {
01254 if (r.getType() == kio_sieveResponse::ACTION) {
01255 QCString response = r.getAction().left(2);
01256 if (response == "OK") {
01257 return true;
01258 } else if (response == "NO") {
01259 return false;
01260 }
01261 }
01262 }
01263 return false;
01264 }
01265
01266 int kio_sieveProtocol::operationResult()
01267 {
01268 if (r.getType() == kio_sieveResponse::ACTION) {
01269 QCString response = r.getAction().left(2);
01270 if (response == "OK") {
01271 return OK;
01272 } else if (response == "NO") {
01273 return NO;
01274 } else if (response == "BY") {
01275 return BYE;
01276 }
01277 }
01278
01279 return OTHER;
01280 }
01281
01282 bool kio_sieveProtocol::requestCapabilitiesAfterStartTLS() const
01283 {
01284
01285
01286
01287 QRegExp regExp( "Cyrus\\stimsieved\\sv(\\d+)\\.(\\d+)\\.(\\d+)([-\\w]*)", false );
01288 if ( regExp.search( m_implementation ) >= 0 ) {
01289 const int major = regExp.cap( 1 ).toInt();
01290 const int minor = regExp.cap( 2 ).toInt();
01291 const int patch = regExp.cap( 3 ).toInt();
01292 const QString vendor = regExp.cap( 4 );
01293 if ( major < 2 || (major == 2 && (minor < 3 || (minor == 3 && patch < 11))) || (vendor == "-kolab-nocaps") ) {
01294 ksDebug() << k_funcinfo << "Enabling compat mode for Cyrus < 2.3.11 or Cyrus marked as \"kolab-nocaps\"" << endl;
01295 return true;
01296 }
01297 }
01298 return false;
01299 }