diff options
author | Darrell Anderson <[email protected]> | 2014-02-06 17:54:49 -0600 |
---|---|---|
committer | Darrell Anderson <[email protected]> | 2014-02-06 17:54:49 -0600 |
commit | ca705978d11b2e0bdcaf8186b0addff48d851483 (patch) | |
tree | da2d37c0fc29b1746cc24594e948bc7c9f1241eb /tdeioslaves/sieve/sieve.cpp | |
parent | eaa1b143397fadcb31d69944ed69d6cf215be77d (diff) | |
download | tdepim-ca705978d11b2e0bdcaf8186b0addff48d851483.tar.gz tdepim-ca705978d11b2e0bdcaf8186b0addff48d851483.zip |
Reorganize tdeioslave help handbooks, fix related protocol files and issues, update and add handbooks.
Diffstat (limited to 'tdeioslaves/sieve/sieve.cpp')
-rw-r--r-- | tdeioslaves/sieve/sieve.cpp | 1343 |
1 files changed, 0 insertions, 1343 deletions
diff --git a/tdeioslaves/sieve/sieve.cpp b/tdeioslaves/sieve/sieve.cpp deleted file mode 100644 index b36336400..000000000 --- a/tdeioslaves/sieve/sieve.cpp +++ /dev/null @@ -1,1343 +0,0 @@ -/*************************************************************************** - sieve.cpp - description - ------------------- - begin : Thu Dec 20 18:47:08 EST 2001 - copyright : (C) 2001 by Hamish Rodda - email : [email protected] - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - ***************************************************************************/ - -/** - * Portions adapted from the SMTP ioslave. - * Copyright (c) 2000, 2001 Alex Zepeda <[email protected]> - * Copyright (c) 2001 Michael Häckel <[email protected]> - * All rights reserved. - * - * Policy: the function where the error occurs calls error(). A result of - * false, where it signifies an error, thus doesn't need to call error() itself. - */ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -extern "C" { -#include <sasl/sasl.h> -} -#include "sieve.h" - -#include <kdebug.h> -#include <kinstance.h> -#include <tdelocale.h> -#include <kurl.h> -#include <kmdcodec.h> -#include <tdeglobal.h> -#include <tdemessagebox.h> - -#include <tqcstring.h> -#include <tqregexp.h> - -#include <cstdlib> -using std::exit; -#include <sys/stat.h> -#include <cassert> - -#include <tdepimmacros.h> - -static const int debugArea = 7122; - -static inline -#ifdef NDEBUG - kndbgstream ksDebug() { return kdDebug( debugArea ); } - kndbgstream ksDebug( bool cond ) { return kdDebug( cond, debugArea ); } -#else - kdbgstream ksDebug() { return kdDebug( debugArea ); } - kdbgstream ksDebug( bool cond ) { return kdDebug( cond, debugArea ); } -#endif - -#define SIEVE_DEFAULT_PORT 2000 - -static sasl_callback_t callbacks[] = { - { SASL_CB_ECHOPROMPT, NULL, NULL }, - { SASL_CB_NOECHOPROMPT, NULL, NULL }, - { SASL_CB_GETREALM, NULL, NULL }, - { SASL_CB_USER, NULL, NULL }, - { SASL_CB_AUTHNAME, NULL, NULL }, - { SASL_CB_PASS, NULL, NULL }, - { SASL_CB_CANON_USER, NULL, NULL }, - { SASL_CB_LIST_END, NULL, NULL } -}; - -static const unsigned int SIEVE_DEFAULT_RECIEVE_BUFFER = 512; - -using namespace TDEIO; -extern "C" -{ - KDE_EXPORT int kdemain(int argc, char **argv) - { - TDEInstance instance("tdeio_sieve" ); - - ksDebug() << "*** Starting tdeio_sieve " << endl; - - if (argc != 4) { - ksDebug() << "Usage: tdeio_sieve protocol domain-socket1 domain-socket2" << endl; - exit(-1); - } - - if ( sasl_client_init( NULL ) != SASL_OK ) { - fprintf(stderr, "SASL library initialization failed!\n"); - ::exit (-1); - } - - tdeio_sieveProtocol slave(argv[2], argv[3]); - slave.dispatchLoop(); - - sasl_done(); - - ksDebug() << "*** tdeio_sieve Done" << endl; - return 0; - } -} - -/* ---------------------------------------------------------------------------------- */ -tdeio_sieveResponse::tdeio_sieveResponse() -{ - clear(); -} - -/* ---------------------------------------------------------------------------------- */ -const uint& tdeio_sieveResponse::getType() const -{ - return rType; -} - -/* ---------------------------------------------------------------------------------- */ -const uint tdeio_sieveResponse::getQuantity() const -{ - return quantity; -} - -/* ---------------------------------------------------------------------------------- */ -const TQCString& tdeio_sieveResponse::getAction() const -{ - return key; -} - -/* ---------------------------------------------------------------------------------- */ -const TQCString& tdeio_sieveResponse::getKey() const -{ - return key; -} - -/* ---------------------------------------------------------------------------------- */ -const TQCString& tdeio_sieveResponse::getVal() const -{ - return val; -} - -/* ---------------------------------------------------------------------------------- */ -const TQCString& tdeio_sieveResponse::getExtra() const -{ - return extra; -} - -/* ---------------------------------------------------------------------------------- */ -void tdeio_sieveResponse::setQuantity(const uint& newTQty) -{ - rType = QUANTITY; - quantity = newTQty; -} - -/* ---------------------------------------------------------------------------------- */ -void tdeio_sieveResponse::setAction(const TQCString& newAction) -{ - rType = ACTION; - key = newAction.copy(); -} - -/* ---------------------------------------------------------------------------------- */ -void tdeio_sieveResponse::setKey(const TQCString& newKey) -{ - rType = KEY_VAL_PAIR; - key = newKey.copy(); -} - -/* ---------------------------------------------------------------------------------- */ -void tdeio_sieveResponse::setVal(const TQCString& newVal) -{ - val = newVal.copy(); -} - -/* ---------------------------------------------------------------------------------- */ -void tdeio_sieveResponse::setExtra(const TQCString& newExtra) -{ - extra = newExtra.copy(); -} - -/* ---------------------------------------------------------------------------------- */ -void tdeio_sieveResponse::clear() -{ - rType = NONE; - extra = key = val = TQCString(""); - quantity = 0; -} - -/* ---------------------------------------------------------------------------------- */ -tdeio_sieveProtocol::tdeio_sieveProtocol(const TQCString &pool_socket, const TQCString &app_socket) - : TCPSlaveBase( SIEVE_DEFAULT_PORT, "sieve", pool_socket, app_socket, false) - , m_connMode(NORMAL) - , m_supportsTLS(false) - , m_shouldBeConnected(false) - , m_allowUnencrypted(false) -{ -} - -/* ---------------------------------------------------------------------------------- */ -tdeio_sieveProtocol::~tdeio_sieveProtocol() -{ - if ( isConnectionValid() ) - disconnect(); -} - -/* ---------------------------------------------------------------------------------- */ -void tdeio_sieveProtocol::setHost (const TQString &host, int port, const TQString &user, const TQString &pass) -{ - if ( isConnectionValid() && - ( m_sServer != host || - m_iPort != port || - m_sUser != user || - m_sPass != pass ) ) { - disconnect(); - } - m_sServer = host; - m_iPort = port ? port : m_iDefaultPort; - m_sUser = user; - m_sPass = pass; - m_supportsTLS = false; -} - -/* ---------------------------------------------------------------------------------- */ -void tdeio_sieveProtocol::openConnection() -{ - m_connMode = CONNECTION_ORIENTED; - connect(); -} - -bool tdeio_sieveProtocol::parseCapabilities(bool requestCapabilities/* = false*/) -{ - ksDebug() << k_funcinfo << endl; - - // Setup... - bool ret = false; - - if (requestCapabilities) { - sendData("CAPABILITY"); - } - - while (receiveData()) { - ksDebug() << "Looping receive" << endl; - - if (r.getType() == tdeio_sieveResponse::ACTION) { - if ( r.getAction().contains("ok", false) != -1 ) { - ksDebug() << "Sieve server ready & awaiting authentication." << endl; - break; - } else - ksDebug() << "Unknown action " << r.getAction() << "." << endl; - - } else if (r.getKey() == "IMPLEMENTATION") { - if (r.getVal().contains("sieve", false) != -1) { - ksDebug() << "Connected to Sieve server: " << r.getVal() << endl; - ret = true; - setMetaData("implementation", r.getVal()); - m_implementation = r.getVal(); - } - - } else if (r.getKey() == "SASL") { - // Save list of available SASL methods - m_sasl_caps = TQStringList::split(' ', r.getVal()); - ksDebug() << "Server SASL authentication methods: " << m_sasl_caps.join(", ") << endl; - setMetaData("saslMethods", r.getVal()); - - } else if (r.getKey() == "SIEVE") { - // Save script capabilities; report back as meta data: - ksDebug() << "Server script capabilities: " << TQStringList::split(' ', r.getVal()).join(", ") << endl; - setMetaData("sieveExtensions", r.getVal()); - - } else if (r.getKey() == "STARTTLS") { - // The server supports TLS - ksDebug() << "Server supports TLS" << endl; - m_supportsTLS = true; - setMetaData("tlsSupported", "true"); - - } else { - ksDebug() << "Unrecognised key." << endl; - } - } - - if (!m_supportsTLS) { - setMetaData("tlsSupported", "false"); - } - - return ret; -} - - -/* ---------------------------------------------------------------------------------- */ -/** - * Checks if connection parameters have changed. - * If it it, close the current connection - */ -void tdeio_sieveProtocol::changeCheck( const KURL &url ) -{ - TQString auth; - - if (!metaData("sasl").isEmpty()) - auth = metaData("sasl").upper(); - else { - TQString query = url.query(); - if ( query.startsWith("?") ) query.remove( 0, 1 ); - TQStringList q = TQStringList::split( ",", query ); - TQStringList::iterator it; - - for ( it = q.begin(); it != q.end(); ++it ) { - if ( TQString( (*it).section('=',0,0) ).lower() == "x-mech" ) { - auth = TQString( (*it).section('=',1) ).upper(); - break; - } - } - } - ksDebug() << "auth: " << auth << " m_sAuth: " << m_sAuth << endl; - if ( m_sAuth != auth ) { - m_sAuth = auth; - if ( isConnectionValid() ) - disconnect(); - } - - // For TLS, only disconnect if we are unencrypted and are - // no longer allowed (otherwise, it's still fine): - const bool allowUnencryptedNow = url.queryItem("x-allow-unencrypted") == "true" ; - if ( m_allowUnencrypted && !allowUnencryptedNow ) - if ( isConnectionValid() ) - disconnect(); - m_allowUnencrypted = allowUnencryptedNow; -} - -/* ---------------------------------------------------------------------------------- */ -/** - * Connects to the server. - * returns false and calls error() if an error occurred. - */ -bool tdeio_sieveProtocol::connect(bool useTLSIfAvailable) -{ - ksDebug() << k_funcinfo << endl; - - if (isConnectionValid()) return true; - - infoMessage(i18n("Connecting to %1...").arg( m_sServer)); - - if (m_connMode == CONNECTION_ORIENTED && m_shouldBeConnected) { - error(ERR_CONNECTION_BROKEN, i18n("The connection to the server was lost.")); - return false; - } - - setBlockConnection(true); - - if (!connectToHost(m_sServer, m_iPort, true)) { - return false; - } - - if (!parseCapabilities()) { - closeDescriptor(); - error(ERR_UNSUPPORTED_PROTOCOL, i18n("Server identification failed.")); - return false; - } - - // Attempt to start TLS - if ( !m_allowUnencrypted && !canUseTLS() ) { - error( ERR_SLAVE_DEFINED, i18n("Can not use TLS. Please enable TLS in the TDE cryptography setting.") ); - disconnect(); - return false; - } - - if ( !m_allowUnencrypted && useTLSIfAvailable && canUseTLS() && !m_supportsTLS && - messageBox( WarningContinueCancel, - i18n("TLS encryption was requested, but your Sieve server does not advertise TLS in its capabilities.\n" - "You can choose to try to initiate TLS negotiations nonetheless, or cancel the operation."), - i18n("Server Does Not Advertise TLS"), i18n("&Start TLS nonetheless"), i18n("&Cancel") ) != KMessageBox::Continue ) - { - error( ERR_USER_CANCELED, i18n("TLS encryption requested, but not supported by server.") ); - disconnect(); - return false; - } - - // FIXME find a test server and test that this works - if (useTLSIfAvailable && canUseTLS()) { - sendData("STARTTLS"); - if (operationSuccessful()) { - ksDebug() << "TLS has been accepted. Starting TLS..." << endl - << "WARNING this is untested and may fail." << endl; - int retval = startTLS(); - if (retval == 1) { - ksDebug() << "TLS enabled successfully." << endl; - // reparse capabilities: - parseCapabilities( requestCapabilitiesAfterStartTLS() ); - } else { - ksDebug() << "TLS initiation failed, code " << retval << endl; - if ( m_allowUnencrypted ) { - disconnect(true); - return connect(false); - } - if ( retval != -3 ) - messageBox( Information, - i18n("Your Sieve server claims to support TLS, " - "but negotiation was unsuccessful."), - i18n("Connection Failed") ); - disconnect(true); - return false; - } - } else if ( !m_allowUnencrypted ) { - ksDebug() << "Server incapable of TLS." << endl; - disconnect(); - error( ERR_SLAVE_DEFINED, i18n("The server does not seem to support TLS. " - "Disable TLS if you want to connect without encryption.") ); - return false; - } else - ksDebug() << "Server incapable of TLS. Transmitted documents will be unencrypted." << endl; - } else - ksDebug() << "We are incapable of TLS. Transmitted documents will be unencrypted." << endl; - - assert( m_allowUnencrypted || usingTLS() ); - - infoMessage(i18n("Authenticating user...")); - if (!authenticate()) { - disconnect(); - error(ERR_COULD_NOT_AUTHENTICATE, i18n("Authentication failed.")); - return false; - } - - m_shouldBeConnected = true; - return true; -} - -/* ---------------------------------------------------------------------------------- */ -void tdeio_sieveProtocol::closeConnection() -{ - m_connMode = CONNECTION_ORIENTED; - disconnect(); -} - -/* ---------------------------------------------------------------------------------- */ -void tdeio_sieveProtocol::disconnect(bool forcibly) -{ - if (!forcibly) { - sendData("LOGOUT"); - - // This crashes under certain conditions as described in - // http://intevation.de/roundup/kolab/issue2442 - // Fixing TDEIO::TCPSlaveBase::atEnd() for !fd would also work but 3.x is on life support. - //if (!operationSuccessful()) - // ksDebug() << "Server did not logout cleanly." << endl; - } - - closeDescriptor(); - m_shouldBeConnected = false; -} - -/* ---------------------------------------------------------------------------------- */ -/*void tdeio_sieveProtocol::slave_status() -{ - slaveStatus(isConnectionValid() ? m_sServer : "", isConnectionValid()); - - finished(); -}*/ - -/* ---------------------------------------------------------------------------------- */ -void tdeio_sieveProtocol::special(const TQByteArray &data) -{ - int tmp; - TQDataStream stream(data, IO_ReadOnly); - KURL url; - - stream >> tmp; - - switch (tmp) { - case 1: - stream >> url; - if (!activate(url)) - return; - break; - case 2: - if (!deactivate()) - return; - break; - case 3: - parseCapabilities(true); - break; - } - - infoMessage(i18n("Done.")); - - finished(); -} - -/* ---------------------------------------------------------------------------------- */ -bool tdeio_sieveProtocol::activate(const KURL& url) -{ - changeCheck( url ); - if (!connect()) - return false; - - infoMessage(i18n("Activating script...")); - - TQString filename = url.fileName(false); - - if (filename.isEmpty()) { - error(ERR_DOES_NOT_EXIST, url.prettyURL()); - return false; - } - - if (!sendData("SETACTIVE \"" + filename.utf8() + "\"")) - return false; - - if (operationSuccessful()) { - ksDebug() << "Script activation complete." << endl; - return true; - } else { - error(ERR_INTERNAL_SERVER, i18n("There was an error activating the script.")); - return false; - } -} - -/* ---------------------------------------------------------------------------------- */ -bool tdeio_sieveProtocol::deactivate() -{ - if (!connect()) - return false; - - if (!sendData("SETACTIVE \"\"")) - return false; - - if (operationSuccessful()) { - ksDebug() << "Script deactivation complete." << endl; - return true; - } else { - error(ERR_INTERNAL_SERVER, i18n("There was an error deactivating the script.")); - return false; - } -} - -static void append_lf2crlf( TQByteArray & out, const TQByteArray & in ) { - if ( in.isEmpty() ) - return; - const unsigned int oldOutSize = out.size(); - out.resize( oldOutSize + 2 * in.size() ); - const char * s = in.begin(); - const char * const end = in.end(); - char * d = out.begin() + oldOutSize; - char last = '\0'; - while ( s < end ) { - if ( *s == '\n' && last != '\r' ) - *d++ = '\r'; - *d++ = last = *s++; - } - out.resize( d - out.begin() ); -} - -void tdeio_sieveProtocol::put(const KURL& url, int /*permissions*/, bool /*overwrite*/, bool /*resume*/) -{ - changeCheck( url ); - if (!connect()) - return; - - infoMessage(i18n("Sending data...")); - - TQString filename = url.fileName(false); - - if (filename.isEmpty()) { - error(ERR_MALFORMED_URL, url.prettyURL()); - return; - } - - TQByteArray data; - for (;;) { - dataReq(); - TQByteArray buffer; - const int newSize = readData(buffer); - append_lf2crlf( data, buffer ); - if ( newSize < 0 ) { - // read error: network in unknown state so disconnect - error(ERR_COULD_NOT_READ, i18n("TDEIO data supply error.")); - return; - } - if ( newSize == 0 ) - break; - } - - // script size - int bufLen = (int)data.size(); - totalSize(bufLen); - - // timsieved 1.1.0: - // C: HAVESPACE "rejected" 74 - // S: NO "Number expected" - // C: HAVESPACE 74 - // S: NO "Missing script name" - // S: HAVESPACE "rejected" "74" - // C: NO "Number expected" - // => broken, we can't use it :-( - // (will be fixed in Cyrus 2.1.10) -#ifndef HAVE_BROKEN_TIMSIEVED - // first, check quota (it's a SHOULD in draft std) - if (!sendData("HAVESPACE \"" + filename.utf8() + "\" " - + TQCString().setNum( bufLen ))) - return; - - if (!operationSuccessful()) { - error(ERR_DISK_FULL, i18n("Quota exceeded")); - return; - } -#endif - - if (!sendData("PUTSCRIPT \"" + filename.utf8() + "\" {" - + TQCString().setNum( bufLen ) + "+}")) - return; - - // atEnd() lies so the code below doesn't work. - /*if (!atEnd()) { - // We are not expecting any data here, so if the server has responded - // with anything but OK we treat it as an error. - char * buf = new char[2]; - while (!atEnd()) { - ksDebug() << "Reading..." << endl; - read(buf, 1); - ksDebug() << "Trailing [" << buf[0] << "]" << endl; - } - ksDebug() << "End of data." << endl; - delete[] buf; - - if (!operationSuccessful()) { - error(ERR_UNSUPPORTED_PROTOCOL, i18n("A protocol error occurred " - "while trying to negotiate script uploading.\n" - "The server responded:\n%1") - .arg(r.getAction().right(r.getAction().length() - 3))); - return; - } - }*/ - - // upload data to the server - if (write(data, bufLen) != bufLen) { - error(ERR_COULD_NOT_WRITE, i18n("Network error.")); - disconnect(true); - return; - } - - // finishing CR/LF - if (!sendData("")) - return; - - processedSize(bufLen); - - infoMessage(i18n("Verifying upload completion...")); - - if (operationSuccessful()) - ksDebug() << "Script upload complete." << endl; - - else { - /* The managesieve server parses received scripts and rejects - * scripts which are not syntactically correct. Here we expect - * to receive a message detailing the error (only the first - * error is reported. */ - if (r.getAction().length() > 3) { - // make a copy of the extra info - TQCString extra = r.getAction().right(r.getAction().length() - 3); - - // send the extra message off for re-processing - receiveData(false, &extra); - - if (r.getType() == tdeio_sieveResponse::QUANTITY) { - // length of the error message - uint len = r.getQuantity(); - - TQCString errmsg(len + 1); - - read(errmsg.data(), len); - - error(ERR_INTERNAL_SERVER, - i18n("The script did not upload successfully.\n" - "This is probably due to errors in the script.\n" - "The server responded:\n%1").arg(TQString(errmsg))); - - // clear the rest of the incoming data - receiveData(); - } else if (r.getType() == tdeio_sieveResponse::KEY_VAL_PAIR) { - error(ERR_INTERNAL_SERVER, - i18n("The script did not upload successfully.\n" - "This is probably due to errors in the script.\n" - "The server responded:\n%1").arg(TQString(r.getKey()))); - } else - error(ERR_INTERNAL_SERVER, - i18n("The script did not upload successfully.\n" - "The script may contain errors.")); - } else - error(ERR_INTERNAL_SERVER, - i18n("The script did not upload successfully.\n" - "The script may contain errors.")); - } - - //if ( permissions != -1 ) - // chmod( url, permissions ); - - infoMessage(i18n("Done.")); - - finished(); -} - -static void inplace_crlf2lf( TQByteArray & in ) { - if ( in.isEmpty() ) - return; - TQByteArray & out = in; // inplace - const char * s = in.begin(); - const char * const end = in.end(); - char * d = out.begin(); - char last = '\0'; - while ( s < end ) { - if ( *s == '\n' && last == '\r' ) - --d; - *d++ = last = *s++; - } - out.resize( d - out.begin() ); -} - -/* ---------------------------------------------------------------------------------- */ -void tdeio_sieveProtocol::get(const KURL& url) -{ - changeCheck( url ); - if (!connect()) - return; - - infoMessage(i18n("Retrieving data...")); - - TQString filename = url.fileName(false); - - if (filename.isEmpty()) { - error(ERR_MALFORMED_URL, url.prettyURL()); - return; - } - - //SlaveBase::mimetype( TQString("text/plain") ); // "application/sieve"); - - if (!sendData("GETSCRIPT \"" + filename.utf8() + "\"")) - return; - - if (receiveData() && r.getType() == tdeio_sieveResponse::QUANTITY) { - // determine script size - ssize_t total_len = r.getQuantity(); - totalSize( total_len ); - - int recv_len = 0; - do { - // wait for data... - if ( !waitForResponse( 600 ) ) { - error( TDEIO::ERR_SERVER_TIMEOUT, m_sServer ); - disconnect( true ); - return; - } - - // ...read data... - // Only read as much as we need, otherwise we slurp in the OK that - // operationSuccessful() is expecting below. - TQByteArray dat( kMin( total_len - recv_len, ssize_t(64 * 1024 )) ); - ssize_t this_recv_len = read( dat.data(), dat.size() ); - - if ( this_recv_len < 1 && !isConnectionValid() ) { - error( TDEIO::ERR_CONNECTION_BROKEN, m_sServer ); - disconnect( true ); - return; - } - - dat.resize( this_recv_len ); - inplace_crlf2lf( dat ); - // send data to slaveinterface - data( dat ); - - recv_len += this_recv_len; - processedSize( recv_len ); - } while ( recv_len < total_len ); - - infoMessage(i18n("Finishing up...") ); - data(TQByteArray()); - - if (operationSuccessful()) - ksDebug() << "Script retrieval complete." << endl; - else - ksDebug() << "Script retrieval failed." << endl; - } else { - error(ERR_UNSUPPORTED_PROTOCOL, i18n("A protocol error occurred " - "while trying to negotiate script downloading.")); - return; - } - - infoMessage(i18n("Done.")); - finished(); -} - -void tdeio_sieveProtocol::del(const KURL &url, bool isfile) -{ - if (!isfile) { - error(ERR_INTERNAL, i18n("Folders are not supported.")); - return; - } - - changeCheck( url ); - if (!connect()) - return; - - infoMessage(i18n("Deleting file...")); - - TQString filename = url.fileName(false); - - if (filename.isEmpty()) { - error(ERR_MALFORMED_URL, url.prettyURL()); - return; - } - - if (!sendData("DELETESCRIPT \"" + filename.utf8() + "\"")) - return; - - if (operationSuccessful()) - ksDebug() << "Script deletion successful." << endl; - else { - error(ERR_INTERNAL_SERVER, i18n("The server would not delete the file.")); - return; - } - - infoMessage(i18n("Done.")); - - finished(); -} - -void tdeio_sieveProtocol::chmod(const KURL& url, int permissions) -{ - switch ( permissions ) { - case 0700: // activate - activate(url); - break; - case 0600: // deactivate - deactivate(); - break; - default: // unsupported - error(ERR_CANNOT_CHMOD, i18n("Cannot chmod to anything but 0700 (active) or 0600 (inactive script).")); - return; - } - - finished(); -} - -#if defined(_AIX) && defined(stat) -#undef stat -#endif - -void tdeio_sieveProtocol::stat(const KURL& url) -{ - changeCheck( url ); - if (!connect()) - return; - - UDSEntry entry; - - TQString filename = url.fileName(false); - - if (filename.isEmpty()) { - UDSAtom atom; - atom.m_uds = TDEIO::UDS_NAME; - atom.m_str = "/"; - entry.append(atom); - - atom.m_uds = TDEIO::UDS_FILE_TYPE; - atom.m_long = S_IFDIR; - entry.append(atom); - - atom.m_uds = TDEIO::UDS_ACCESS; - atom.m_long = 0700; - entry.append(atom); - - statEntry(entry); - - } else { - if (!sendData("LISTSCRIPTS")) - return; - - while(receiveData()) { - if (r.getType() == tdeio_sieveResponse::ACTION) { - if (r.getAction().contains("OK", false) == 1) - // Script list completed - break; - - } else - if (filename == TQString::fromUtf8(r.getKey())) { - entry.clear(); - - UDSAtom atom; - atom.m_uds = TDEIO::UDS_NAME; - atom.m_str = TQString::fromUtf8(r.getKey()); - entry.append(atom); - - atom.m_uds = TDEIO::UDS_FILE_TYPE; - atom.m_long = S_IFREG; - entry.append(atom); - - atom.m_uds = TDEIO::UDS_ACCESS; - if ( r.getExtra() == "ACTIVE" ) - atom.m_long = 0700; // mark exec'able - else - atom.m_long = 0600; - entry.append(atom); - - atom.m_uds = TDEIO::UDS_MIME_TYPE; - atom.m_str = "application/sieve"; - entry.append(atom); - - //setMetaData("active", (r.getExtra() == "ACTIVE") ? "yes" : "no"); - - statEntry(entry); - // cannot break here because we need to clear - // the rest of the incoming data. - } - } - } - - finished(); -} - -void tdeio_sieveProtocol::listDir(const KURL& url) -{ - changeCheck( url ); - if (!connect()) - return; - - if (!sendData("LISTSCRIPTS")) - return; - - UDSEntry entry; - - while(receiveData()) { - if (r.getType() == tdeio_sieveResponse::ACTION) { - if (r.getAction().contains("OK", false) == 1) - // Script list completed. - break; - - } else { - entry.clear(); - - UDSAtom atom; - atom.m_uds = TDEIO::UDS_NAME; - atom.m_str = TQString::fromUtf8(r.getKey()); - entry.append(atom); - - atom.m_uds = TDEIO::UDS_FILE_TYPE; - atom.m_long = S_IFREG; - entry.append(atom); - - atom.m_uds = TDEIO::UDS_ACCESS; - if ( r.getExtra() == "ACTIVE" ) - atom.m_long = 0700; // mark exec'able - else - atom.m_long = 0600; - entry.append(atom); - - atom.m_uds = TDEIO::UDS_MIME_TYPE; - atom.m_str = "application/sieve"; - entry.append(atom); - - //asetMetaData("active", (r.getExtra() == "ACTIVE") ? "true" : "false"); - - ksDebug() << "Listing script " << r.getKey() << endl; - listEntry(entry , false); - } - } - - listEntry(entry, true); - - finished(); -} - -/* ---------------------------------------------------------------------------------- */ -bool tdeio_sieveProtocol::saslInteract( void *in, AuthInfo &ai ) -{ - ksDebug() << "sasl_interact" << endl; - sasl_interact_t *interact = ( sasl_interact_t * ) in; - - //some mechanisms do not require username && pass, so it doesn't need a popup - //window for getting this info - for ( ; interact->id != SASL_CB_LIST_END; interact++ ) { - if ( interact->id == SASL_CB_AUTHNAME || - interact->id == SASL_CB_PASS ) { - - if (m_sUser.isEmpty() || m_sPass.isEmpty()) { - if (!openPassDlg(ai)) { - error(ERR_ABORTED, i18n("No authentication details supplied.")); - return false; - } - m_sUser = ai.username; - m_sPass = ai.password; - } - break; - } - } - - interact = ( sasl_interact_t * ) in; - while( interact->id != SASL_CB_LIST_END ) { - ksDebug() << "SASL_INTERACT id: " << interact->id << endl; - switch( interact->id ) { - case SASL_CB_USER: - case SASL_CB_AUTHNAME: - ksDebug() << "SASL_CB_[AUTHNAME|USER]: '" << m_sUser << "'" << endl; - interact->result = strdup( m_sUser.utf8() ); - interact->len = strlen( (const char *) interact->result ); - break; - case SASL_CB_PASS: - ksDebug() << "SASL_CB_PASS: [hidden] " << endl; - interact->result = strdup( m_sPass.utf8() ); - interact->len = strlen( (const char *) interact->result ); - break; - default: - interact->result = NULL; interact->len = 0; - break; - } - interact++; - } - return true; -} - -#define SASLERROR error(ERR_COULD_NOT_AUTHENTICATE, i18n("An error occurred during authentication: %1").arg( \ - TQString::fromUtf8( sasl_errdetail( conn ) ))); - -bool tdeio_sieveProtocol::authenticate() -{ - int result; - sasl_conn_t *conn = NULL; - sasl_interact_t *client_interact = NULL; - const char *out = NULL; - uint outlen; - const char *mechusing = NULL; - TQByteArray challenge, tmp; - - /* Retrieve authentication details from user. - * Note: should this require realm as well as user & pass details - * before it automatically skips the prompt? - * Note2: encoding issues with PLAIN login? */ - AuthInfo ai; - ai.url.setProtocol("sieve"); - ai.url.setHost(m_sServer); - ai.url.setPort(m_iPort); - ai.username = m_sUser; - ai.password = m_sPass; - ai.keepPassword = true; - ai.caption = i18n("Sieve Authentication Details"); - ai.comment = i18n("Please enter your authentication details for your sieve account " - "(usually the same as your email password):"); - - result = sasl_client_new( "sieve", - m_sServer.latin1(), - 0, 0, callbacks, 0, &conn ); - - if ( result != SASL_OK ) { - ksDebug() << "sasl_client_new failed with: " << result << endl; - SASLERROR - return false; - } - - TQStringList strList; -// strList.append("NTLM"); - - if ( !m_sAuth.isEmpty() ) - strList.append( m_sAuth ); - else - strList = m_sasl_caps; - - do { - result = sasl_client_start(conn, strList.join(" ").latin1(), &client_interact, - &out, &outlen, &mechusing); - - if (result == SASL_INTERACT) - if ( !saslInteract( client_interact, ai ) ) { - sasl_dispose( &conn ); - return false; - }; - } while ( result == SASL_INTERACT ); - - if ( result != SASL_CONTINUE && result != SASL_OK ) { - ksDebug() << "sasl_client_start failed with: " << result << endl; - SASLERROR - sasl_dispose( &conn ); - return false; - } - - ksDebug() << "Preferred authentication method is " << mechusing << "." << endl; - - TQString firstCommand = "AUTHENTICATE \"" + TQString::fromLatin1( mechusing ) + "\""; - tmp.setRawData( out, outlen ); - KCodecs::base64Encode( tmp, challenge ); - tmp.resetRawData( out, outlen ); - if ( !challenge.isEmpty() ) { - firstCommand += " \""; - firstCommand += TQString::fromLatin1( challenge.data(), challenge.size() ); - firstCommand += "\""; - } - - if (!sendData( firstCommand.latin1() )) - return false; - - TQCString command; - - do { - receiveData(); - - if (operationResult() != OTHER) - break; - - ksDebug() << "Challenge len " << r.getQuantity() << endl; - - if (r.getType() != tdeio_sieveResponse::QUANTITY) { - sasl_dispose( &conn ); - error(ERR_SLAVE_DEFINED, - i18n("A protocol error occurred during authentication.\n" - "Choose a different authentication method to %1.").arg(mechusing)); - return false; - } - - uint qty = r.getQuantity(); - - receiveData(); - - if (r.getType() != tdeio_sieveResponse::ACTION && r.getAction().length() != qty) { - sasl_dispose( &conn ); - error(ERR_UNSUPPORTED_PROTOCOL, - i18n("A protocol error occurred during authentication.\n" - "Choose a different authentication method to %1.").arg(mechusing)); - return false; - } - - tmp.setRawData( r.getAction().data(), qty ); - KCodecs::base64Decode( tmp, challenge ); - tmp.resetRawData( r.getAction().data(), qty ); -// ksDebug() << "S: [" << r.getAction() << "]." << endl; -// ksDebug() << "S-1: [" << TQCString(challenge.data(), challenge.size()+1) << "]." << endl; - - do { - result = sasl_client_step(conn, challenge.isEmpty() ? 0 : challenge.data(), - challenge.size(), - &client_interact, - &out, &outlen); - - if (result == SASL_INTERACT) - if ( !saslInteract( client_interact, ai ) ) { - sasl_dispose( &conn ); - return false; - }; - } while ( result == SASL_INTERACT ); - - ksDebug() << "sasl_client_step: " << result << endl; - if ( result != SASL_CONTINUE && result != SASL_OK ) { - ksDebug() << "sasl_client_step failed with: " << result << endl; - SASLERROR - sasl_dispose( &conn ); - return false; - } - - tmp.setRawData( out, outlen ); - KCodecs::base64Encode( tmp, challenge ); - tmp.resetRawData( out, outlen ); - sendData("\"" + TQCString( challenge.data(), challenge.size()+1 ) + "\""); -// ksDebug() << "C: [" << TQCString(challenge.data(), challenge.size()+1) << "]." << endl; -// ksDebug() << "C-1: [" << out << "]." << endl; - } while ( true ); - - ksDebug() << "Challenges finished." << endl; - sasl_dispose( &conn ); - - if (operationResult() == OK) { - // Authentication succeeded. - return true; - } else { - // Authentication failed. - error(ERR_COULD_NOT_AUTHENTICATE, i18n("Authentication failed.\nMost likely the password is wrong.\nThe server responded:\n%1").arg( TQString(r.getAction()) ) ); - return false; - } -} - -/* --------------------------------------------------------------------------- */ -void tdeio_sieveProtocol::mimetype(const KURL & url) -{ - ksDebug() << "Requesting mimetype for " << url.prettyURL() << endl; - - if (url.fileName(false).isEmpty()) - mimeType( "inode/directory" ); - else - mimeType( "application/sieve" ); - - finished(); -} - - -/* --------------------------------------------------------------------------- */ -bool tdeio_sieveProtocol::sendData(const TQCString &data) -{ - TQCString write_buf = data + "\r\n"; - - //ksDebug() << "C: " << data << endl; - - // Write the command - ssize_t write_buf_len = write_buf.length(); - if (write(write_buf.data(), write_buf_len) != write_buf_len) { - error(ERR_COULD_NOT_WRITE, i18n("Network error.")); - disconnect(true); - return false; - } - - return true; -} - -/* --------------------------------------------------------------------------- */ -bool tdeio_sieveProtocol::receiveData(bool waitForData, TQCString *reparse) -{ - TQCString interpret; - int start, end; - - if (!reparse) { - if (!waitForData) - // is there data waiting? - if (atEnd()) return false; - - // read data from the server - char buffer[SIEVE_DEFAULT_RECIEVE_BUFFER]; - readLine(buffer, SIEVE_DEFAULT_RECIEVE_BUFFER - 1); - buffer[SIEVE_DEFAULT_RECIEVE_BUFFER-1] = '\0'; - - // strip LF/CR - interpret = TQCString(buffer).left(tqstrlen(buffer) - 2); - - } else { - interpret = reparse->copy(); - } - - r.clear(); - - //ksDebug() << "S: " << interpret << endl; - - switch(interpret[0]) { - case '{': - { - // expecting {quantity} - start = 0; - end = interpret.find("+}", start + 1); - // some older versions of Cyrus enclose the literal size just in { } instead of { +} - if ( end == -1 ) - end = interpret.find('}', start + 1); - - bool ok = false; - r.setQuantity(interpret.mid(start + 1, end - start - 1).toUInt( &ok )); - if (!ok) { - disconnect(); - error(ERR_INTERNAL_SERVER, i18n("A protocol error occurred.")); - return false; - } - - return true; - } - case '"': - // expecting "key" "value" pairs - break; - default: - // expecting single string - r.setAction(interpret); - return true; - } - - start = 0; - - end = interpret.find(34, start + 1); - if (end == -1) { - ksDebug() << "Possible insufficient buffer size." << endl; - r.setKey(interpret.right(interpret.length() - start)); - return true; - } - - r.setKey(interpret.mid(start + 1, end - start - 1)); - - start = interpret.find(34, end + 1); - if (start == -1) { - if ((int)interpret.length() > end) - // skip " and space - r.setExtra(interpret.right(interpret.length() - end - 2)); - - return true; - } - - end = interpret.find(34, start + 1); - if (end == -1) { - ksDebug() << "Possible insufficient buffer size." << endl; - r.setVal(interpret.right(interpret.length() - start)); - return true; - } - - r.setVal(interpret.mid(start + 1, end - start - 1)); - return true; -} - -bool tdeio_sieveProtocol::operationSuccessful() -{ - while (receiveData(false)) { - if (r.getType() == tdeio_sieveResponse::ACTION) { - TQCString response = r.getAction().left(2); - if (response == "OK") { - return true; - } else if (response == "NO") { - return false; - } - } - } - return false; -} - -int tdeio_sieveProtocol::operationResult() -{ - if (r.getType() == tdeio_sieveResponse::ACTION) { - TQCString response = r.getAction().left(2); - if (response == "OK") { - return OK; - } else if (response == "NO") { - return NO; - } else if (response == "BY"/*E*/) { - return BYE; - } - } - - return OTHER; -} - -bool tdeio_sieveProtocol::requestCapabilitiesAfterStartTLS() const -{ - // Cyrus didn't send CAPABILITIES after STARTTLS until 2.3.11, which is - // not standard conform, but we need to support that anyway. - // m_implementation looks like this 'Cyrus timsieved v2.2.12' for Cyrus btw. - TQRegExp regExp( "Cyrus\\stimsieved\\sv(\\d+)\\.(\\d+)\\.(\\d+)([-\\w]*)", false ); - if ( regExp.search( m_implementation ) >= 0 ) { - const int major = regExp.cap( 1 ).toInt(); - const int minor = regExp.cap( 2 ).toInt(); - const int patch = regExp.cap( 3 ).toInt(); - const TQString vendor = regExp.cap( 4 ); - if ( major < 2 || (major == 2 && (minor < 3 || (minor == 3 && patch < 11))) || (vendor == "-kolab-nocaps") ) { - ksDebug() << k_funcinfo << "Enabling compat mode for Cyrus < 2.3.11 or Cyrus marked as \"kolab-nocaps\"" << endl; - return true; - } - } - return false; -} |