diff options
Diffstat (limited to 'kioslave/pop3/pop3.cc')
-rw-r--r-- | kioslave/pop3/pop3.cc | 1263 |
1 files changed, 0 insertions, 1263 deletions
diff --git a/kioslave/pop3/pop3.cc b/kioslave/pop3/pop3.cc deleted file mode 100644 index e950f79c1..000000000 --- a/kioslave/pop3/pop3.cc +++ /dev/null @@ -1,1263 +0,0 @@ -/* - * Copyright (c) 1999-2001 Alex Zepeda - * Copyright (c) 2001-2002 Michael Haeckel <[email protected]> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/time.h> -#ifdef HAVE_SYS_SELECT_H -#include <sys/select.h> -#endif - -#include <errno.h> -#include <stdio.h> - -#ifdef HAVE_LIBSASL2 -extern "C" { -#include <sasl/sasl.h> -} -#endif - -#include <tqcstring.h> -#include <tqglobal.h> -#include <tqregexp.h> - -#include <kdebug.h> -#include <kinstance.h> -#include <klocale.h> -#include <kmdcodec.h> -#include <kprotocolmanager.h> -#include <ksock.h> - -#include <kio/connection.h> -#include <kio/slaveinterface.h> -#include <kio/passdlg.h> -#include "pop3.h" - -#define GREETING_BUF_LEN 1024 -#define MAX_RESPONSE_LEN 512 -#define MAX_COMMANDS 10 - -#define POP3_DEBUG kdDebug(7105) - -extern "C" { - int KDE_EXPORT kdemain(int argc, char **argv); -} - -using namespace TDEIO; - -#ifdef HAVE_LIBSASL2 -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 } -}; -#endif - -int kdemain(int argc, char **argv) -{ - - if (argc != 4) { - POP3_DEBUG << "Usage: kio_pop3 protocol domain-socket1 domain-socket2" - << endl; - return -1; - } - -#ifdef HAVE_LIBSASL2 - if ( sasl_client_init( NULL ) != SASL_OK ) { - fprintf(stderr, "SASL library initialization failed!\n"); - return -1; - } -#endif - - TDEInstance instance("kio_pop3"); - POP3Protocol *slave; - - // Are we looking to use SSL? - if (strcasecmp(argv[1], "pop3s") == 0) { - slave = new POP3Protocol(argv[2], argv[3], true); - } else { - slave = new POP3Protocol(argv[2], argv[3], false); - } - - slave->dispatchLoop(); - delete slave; - -#ifdef HAVE_LIBSASL2 - sasl_done(); -#endif - - return 0; -} - -POP3Protocol::POP3Protocol(const TQCString & pool, const TQCString & app, - bool isSSL) -: TCPSlaveBase((isSSL ? 995 : 110), (isSSL ? "pop3s" : "pop3"), pool, app, - isSSL) -{ - POP3_DEBUG << "POP3Protocol::POP3Protocol()" << endl; - m_bIsSSL = isSSL; - m_cmd = CMD_NONE; - m_iOldPort = 0; - m_tTimeout.tv_sec = 10; - m_tTimeout.tv_usec = 0; - supports_apop = false; - m_try_apop = true; - m_try_sasl = true; - opened = false; - readBufferLen = 0; -} - -POP3Protocol::~POP3Protocol() -{ - POP3_DEBUG << "POP3Protocol::~POP3Protocol()" << endl; - closeConnection(); -} - -void POP3Protocol::setHost(const TQString & _host, int _port, - const TQString & _user, const TQString & _pass) -{ - m_sServer = _host; - m_iPort = _port; - m_sUser = _user; - m_sPass = _pass; -} - -ssize_t POP3Protocol::myRead(void *data, ssize_t len) -{ - if (readBufferLen) { - ssize_t copyLen = (len < readBufferLen) ? len : readBufferLen; - memcpy(data, readBuffer, copyLen); - readBufferLen -= copyLen; - if (readBufferLen) - memmove(readBuffer, &readBuffer[copyLen], readBufferLen); - return copyLen; - } - waitForResponse(600); - return read(data, len); -} - -ssize_t POP3Protocol::myReadLine(char *data, ssize_t len) -{ - ssize_t copyLen = 0, readLen = 0; - while (true) { - while (copyLen < readBufferLen && readBuffer[copyLen] != '\n') - copyLen++; - if (copyLen < readBufferLen || copyLen == len) { - copyLen++; - memcpy(data, readBuffer, copyLen); - data[copyLen] = '\0'; - readBufferLen -= copyLen; - if (readBufferLen) - memmove(readBuffer, &readBuffer[copyLen], readBufferLen); - return copyLen; - } - waitForResponse(600); - readLen = read(&readBuffer[readBufferLen], len - readBufferLen); - readBufferLen += readLen; - if (readLen <= 0) { - data[0] = '\0'; - return 0; - } - } -} - -POP3Protocol::Resp POP3Protocol::getResponse(char *r_buf, unsigned int r_len, - const char *cmd) -{ - char *buf = 0; - unsigned int recv_len = 0; - // fd_set FDs; - - // Give the buffer the appropriate size - r_len = r_len ? r_len : MAX_RESPONSE_LEN; - - buf = new char[r_len]; - - // Clear out the buffer - memset(buf, 0, r_len); - myReadLine(buf, r_len - 1); - - // This is really a funky crash waiting to happen if something isn't - // null terminated. - recv_len = strlen(buf); - - /* - * From rfc1939: - * - * Responses in the POP3 consist of a status indicator and a keyword - * possibly followed by additional information. All responses are - * terminated by a CRLF pair. Responses may be up to 512 characters - * long, including the terminating CRLF. There are currently two status - * indicators: positive ("+OK") and negative ("-ERR"). Servers MUST - * send the "+OK" and "-ERR" in upper case. - */ - - if (strncmp(buf, "+OK", 3) == 0) { - if (r_buf && r_len) { - memcpy(r_buf, (buf[3] == ' ' ? buf + 4 : buf + 3), - QMIN(r_len, (buf[3] == ' ' ? recv_len - 4 : recv_len - 3))); - } - - delete[]buf; - - return Ok; - } else if (strncmp(buf, "-ERR", 4) == 0) { - if (r_buf && r_len) { - memcpy(r_buf, (buf[4] == ' ' ? buf + 5 : buf + 4), - QMIN(r_len, (buf[4] == ' ' ? recv_len - 5 : recv_len - 4))); - } - - TQString command = TQString::fromLatin1(cmd); - TQString serverMsg = TQString::fromLatin1(buf).mid(5).stripWhiteSpace(); - - if (command.left(4) == "PASS") { - command = i18n("PASS <your password>"); - } - - m_sError = i18n("The server said: \"%1\"").arg(serverMsg); - - delete[]buf; - - return Err; - } else if (strncmp(buf, "+ ", 2) == 0) { - if (r_buf && r_len) { - memcpy(r_buf, buf + 2, QMIN(r_len, recv_len - 4)); - r_buf[QMIN(r_len - 1, recv_len - 4)] = '\0'; - } - - delete[]buf; - - return Cont; - } else { - POP3_DEBUG << "Invalid POP3 response received!" << endl; - - if (r_buf && r_len) { - memcpy(r_buf, buf, QMIN(r_len, recv_len)); - } - - if (!buf || !*buf) { - m_sError = i18n("The server terminated the connection."); - } else { - m_sError = i18n("Invalid response from server:\n\"%1\"").arg(buf); - } - - delete[]buf; - - return Invalid; - } -} - -bool POP3Protocol::sendCommand(const char *cmd) -{ - /* - * From rfc1939: - * - * Commands in the POP3 consist of a case-insensitive keyword, possibly - * followed by one or more arguments. All commands are terminated by a - * CRLF pair. Keywords and arguments consist of printable ASCII - * characters. Keywords and arguments are each separated by a single - * SPACE character. Keywords are three or four characters long. Each - * argument may be up to 40 characters long. - */ - - if (!isConnectionValid()) return false; - - char *cmdrn = new char[strlen(cmd) + 3]; - sprintf(cmdrn, "%s\r\n", (cmd) ? cmd : ""); - - if (write(cmdrn, strlen(cmdrn)) != static_cast < ssize_t > - (strlen(cmdrn))) { - m_sError = i18n("Could not send to server.\n"); - delete[]cmdrn; - return false; - } - - delete[]cmdrn; - return true; -} - -POP3Protocol::Resp POP3Protocol::command(const char *cmd, char *recv_buf, - unsigned int len) -{ - sendCommand(cmd); - return getResponse(recv_buf, len, cmd); -} - -void POP3Protocol::openConnection() -{ - m_try_apop = !hasMetaData("auth") || metaData("auth") == "APOP"; - m_try_sasl = !hasMetaData("auth") || metaData("auth") == "SASL"; - - if (!pop3_open()) { - POP3_DEBUG << "pop3_open failed" << endl; - } else { - connected(); - } -} - -void POP3Protocol::closeConnection() -{ - // If the file pointer exists, we can assume the socket is valid, - // and to make sure that the server doesn't magically undo any of - // our deletions and so-on, we should send a QUIT and wait for a - // response. We don't care if it's positive or negative. Also - // flush out any semblance of a persistant connection, i.e.: the - // old username and password are now invalid. - if (!opened) { - return; - } - - command("QUIT"); - closeDescriptor(); - readBufferLen = 0; - m_sOldUser = m_sOldPass = m_sOldServer = ""; - opened = false; -} - -int POP3Protocol::loginAPOP( char *challenge, TDEIO::AuthInfo &ai ) -{ - char buf[512]; - - TQString apop_string = TQString::fromLatin1("APOP "); - if (m_sUser.isEmpty() || m_sPass.isEmpty()) { - // Prompt for usernames - if (!openPassDlg(ai)) { - error(ERR_ABORTED, i18n("No authentication details supplied.")); - closeConnection(); - return -1; - } else { - m_sUser = ai.username; - m_sPass = ai.password; - } - } - m_sOldUser = m_sUser; - m_sOldPass = m_sPass; - - apop_string.append(m_sUser); - - memset(buf, 0, sizeof(buf)); - - KMD5 ctx; - - POP3_DEBUG << "APOP challenge: " << challenge << endl; - - // Generate digest - ctx.update(challenge, strlen(challenge)); - ctx.update(m_sPass.latin1() ); - - // Genenerate APOP command - apop_string.append(" "); - apop_string.append(ctx.hexDigest()); - - if (command(apop_string.local8Bit(), buf, sizeof(buf)) == Ok) { - return 0; - } - - POP3_DEBUG << "Couldn't login via APOP. Falling back to USER/PASS" << - endl; - closeConnection(); - if (metaData("auth") == "APOP") { - error(ERR_COULD_NOT_LOGIN, - i18n - ("Login via APOP failed. The server %1 may not support APOP, although it claims to support it, or the password may be wrong.\n\n%2"). - arg(m_sServer). - arg(m_sError)); - return -1; - } - return 1; -} - -bool POP3Protocol::saslInteract( void *in, AuthInfo &ai ) -{ -#ifdef HAVE_LIBSASL2 - POP3_DEBUG << "sasl_interact" << endl; - sasl_interact_t *interact = ( sasl_interact_t * ) in; - - //some mechanisms do not require username && pass, so don'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 ) { - POP3_DEBUG << "SASL_INTERACT id: " << interact->id << endl; - switch( interact->id ) { - case SASL_CB_USER: - case SASL_CB_AUTHNAME: - POP3_DEBUG << "SASL_CB_[USER|AUTHNAME]: " << m_sUser << endl; - interact->result = strdup( m_sUser.utf8() ); - interact->len = strlen( (const char *) interact->result ); - break; - case SASL_CB_PASS: - POP3_DEBUG << "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; -#else - return false; -#endif -} - -#define SASLERROR closeConnection(); \ -error(ERR_COULD_NOT_AUTHENTICATE, i18n("An error occured during authentication: %1").arg \ -( TQString::fromUtf8( sasl_errdetail( conn ) ))); \ - -int POP3Protocol::loginSASL( TDEIO::AuthInfo &ai ) -{ -#ifdef HAVE_LIBSASL2 - char buf[512]; - TQString sasl_buffer = TQString::fromLatin1("AUTH"); - - int result; - sasl_conn_t *conn = NULL; - sasl_interact_t *client_interact = NULL; - const char *out = NULL; - uint outlen; - const char *mechusing = NULL; - Resp resp; - - result = sasl_client_new( "pop", - m_sServer.latin1(), - 0, 0, callbacks, 0, &conn ); - - if ( result != SASL_OK ) { - POP3_DEBUG << "sasl_client_new failed with: " << result << endl; - SASLERROR - return false; - } - - // We need to check what methods the server supports... - // This is based on RFC 1734's wisdom - if ( hasMetaData("sasl") || command(sasl_buffer.local8Bit()) == Ok ) { - - TQStringList sasl_list; - if (hasMetaData("sasl")) { - sasl_list.append(metaData("sasl").latin1()); - } else - while (true /* !AtEOF() */ ) { - memset(buf, 0, sizeof(buf)); - myReadLine(buf, sizeof(buf) - 1); - - // HACK: This assumes fread stops at the first \n and not \r - if (strcmp(buf, ".\r\n") == 0) { - break; // End of data - } - // sanders, changed -2 to -1 below - buf[strlen(buf) - 2] = '\0'; - - sasl_list.append(buf); - } - - do { - result = sasl_client_start(conn, sasl_list.join(" ").latin1(), - &client_interact, &out, &outlen, &mechusing); - - if (result == SASL_INTERACT) - if ( !saslInteract( client_interact, ai ) ) { - closeConnection(); - sasl_dispose( &conn ); - return -1; - }; - } while ( result == SASL_INTERACT ); - if ( result != SASL_CONTINUE && result != SASL_OK ) { - POP3_DEBUG << "sasl_client_start failed with: " << result << endl; - SASLERROR - sasl_dispose( &conn ); - return -1; - } - - POP3_DEBUG << "Preferred authentication method is " << mechusing << "." << endl; - - TQByteArray challenge, tmp; - - TQString firstCommand = "AUTH " + TQString::fromLatin1( mechusing ); - challenge.setRawData( out, outlen ); - KCodecs::base64Encode( challenge, tmp ); - challenge.resetRawData( out, outlen ); - if ( !tmp.isEmpty() ) { - firstCommand += " "; - firstCommand += TQString::fromLatin1( tmp.data(), tmp.size() ); - } - - challenge.resize( 2049 ); - resp = command( firstCommand.latin1(), challenge.data(), 2049 ); - while( resp == Cont ) { - challenge.resize(challenge.find(0)); -// POP3_DEBUG << "S: " << TQCString(challenge.data(),challenge.size()+1) << endl; - KCodecs::base64Decode( challenge, tmp ); - do { - result = sasl_client_step(conn, tmp.isEmpty() ? 0 : tmp.data(), - tmp.size(), - &client_interact, - &out, &outlen); - - if (result == SASL_INTERACT) - if ( !saslInteract( client_interact, ai ) ) { - closeConnection(); - sasl_dispose( &conn ); - return -1; - }; - } while ( result == SASL_INTERACT ); - if ( result != SASL_CONTINUE && result != SASL_OK ) { - POP3_DEBUG << "sasl_client_step failed with: " << result << endl; - SASLERROR - sasl_dispose( &conn ); - return -1; - } - - challenge.setRawData( out, outlen ); - KCodecs::base64Encode( challenge, tmp ); - challenge.resetRawData( out, outlen ); -// POP3_DEBUG << "C: " << TQCString(tmp.data(),tmp.size()+1) << endl; - tmp.resize(tmp.size()+1); - tmp[tmp.size()-1] = '\0'; - challenge.resize(2049); - resp = command( tmp.data(), challenge.data(), 2049 ); - } - - sasl_dispose( &conn ); - if ( resp == Ok ) { - POP3_DEBUG << "SASL authenticated" << endl; - m_sOldUser = m_sUser; - m_sOldPass = m_sPass; - return 0; - } - - if (metaData("auth") == "SASL") { - closeConnection(); - error(ERR_COULD_NOT_LOGIN, - i18n - ("Login via SASL (%1) failed. The server may not support %2, or the password may be wrong.\n\n%3"). - arg(mechusing).arg(mechusing).arg(m_sError)); - return -1; - } - } - - if (metaData("auth") == "SASL") { - closeConnection(); - error(ERR_COULD_NOT_LOGIN, - i18n("Your POP3 server does not support SASL.\n" - "Choose a different authentication method.")); - return -1; - } - return 1; -#else - if (metaData("auth") == "SASL") { - closeConnection(); - error(ERR_COULD_NOT_LOGIN, i18n("SASL authentication is not compiled into kio_pop3.")); - return -1; - } - return 1; //if SASL not explicitly required, try another method (USER/PASS) -#endif -} - -bool POP3Protocol::loginPASS( TDEIO::AuthInfo &ai ) -{ - char buf[512]; - - if (m_sUser.isEmpty() || m_sPass.isEmpty()) { - // Prompt for usernames - if (!openPassDlg(ai)) { - error(ERR_ABORTED, i18n("No authentication details supplied.")); - closeConnection(); - return false; - } else { - m_sUser = ai.username; - m_sPass = ai.password; - } - } - m_sOldUser = m_sUser; - m_sOldPass = m_sPass; - - TQString one_string = TQString::fromLatin1("USER "); - one_string.append( m_sUser ); - - if ( command(one_string.local8Bit(), buf, sizeof(buf)) != Ok ) { - POP3_DEBUG << "Couldn't login. Bad username Sorry" << endl; - - m_sError = - i18n("Could not login to %1.\n\n").arg(m_sServer) + m_sError; - error(ERR_COULD_NOT_LOGIN, m_sError); - closeConnection(); - - return false; - } - - one_string = TQString::fromLatin1("PASS "); - one_string.append(m_sPass); - - if ( command(one_string.local8Bit(), buf, sizeof(buf)) != Ok ) { - POP3_DEBUG << "Couldn't login. Bad password Sorry." << endl; - m_sError = - i18n - ("Could not login to %1. The password may be wrong.\n\n%2"). - arg(m_sServer).arg(m_sError); - error(ERR_COULD_NOT_LOGIN, m_sError); - closeConnection(); - return false; - } - POP3_DEBUG << "USER/PASS login succeeded" << endl; - return true; -} - -bool POP3Protocol::pop3_open() -{ - POP3_DEBUG << "pop3_open()" << endl; - char *greeting_buf; - if ((m_iOldPort == port(m_iPort)) && (m_sOldServer == m_sServer) && - (m_sOldUser == m_sUser) && (m_sOldPass == m_sPass)) { - POP3_DEBUG << "Reusing old connection" << endl; - return true; - } - do { - closeConnection(); - - if (!connectToHost(m_sServer.ascii(), m_iPort)) { - // error(ERR_COULD_NOT_CONNECT, m_sServer); - // ConnectToHost has already send an error message. - return false; - } - opened = true; - - greeting_buf = new char[GREETING_BUF_LEN]; - memset(greeting_buf, 0, GREETING_BUF_LEN); - - // If the server doesn't respond with a greeting - if (getResponse(greeting_buf, GREETING_BUF_LEN, "") != Ok) { - m_sError = - i18n("Could not login to %1.\n\n").arg(m_sServer) + - ((!greeting_buf - || !*greeting_buf) ? - i18n("The server terminated the connection immediately.") : - i18n("Server does not respond properly:\n%1\n"). - arg(greeting_buf)); - error(ERR_COULD_NOT_LOGIN, m_sError); - delete[]greeting_buf; - closeConnection(); - return false; // we've got major problems, and possibly the - // wrong port - } - TQCString greeting(greeting_buf); - delete[]greeting_buf; - - if (greeting.length() > 0) { - greeting.truncate(greeting.length() - 2); - } - - // Does the server support APOP? - TQString apop_cmd; - TQRegExp re("<[A-Za-z0-9\\.\\-_]+@[A-Za-z0-9\\.\\-_]+>$", false); - - POP3_DEBUG << "greeting: " << greeting << endl; - int apop_pos = greeting.find(re); - supports_apop = (bool) (apop_pos != -1); - - if (metaData("nologin") == "on") - return true; - - if (metaData("auth") == "APOP" && !supports_apop) { - error(ERR_COULD_NOT_LOGIN, - i18n("Your POP3 server does not support APOP.\n" - "Choose a different authentication method.")); - closeConnection(); - return false; - } - - m_iOldPort = m_iPort; - m_sOldServer = m_sServer; - - // Try to go into TLS mode - if ((metaData("tls") == "on" || (canUseTLS() && - metaData("tls") != "off")) - && command("STLS") == Ok ) { - int tlsrc = startTLS(); - if (tlsrc == 1) { - POP3_DEBUG << "TLS mode has been enabled." << endl; - } else { - if (tlsrc != -3) { - POP3_DEBUG << "TLS mode setup has failed. Aborting." << endl; - error(ERR_COULD_NOT_CONNECT, - i18n("Your POP3 server claims to " - "support TLS but negotiation " - "was unsuccessful. You can " - "disable TLS in KDE using the " - "crypto settings module.")); - } - closeConnection(); - return false; - } - } else if (metaData("tls") == "on") { - error(ERR_COULD_NOT_CONNECT, - i18n("Your POP3 server does not support TLS. Disable " - "TLS, if you want to connect without encryption.")); - closeConnection(); - return false; - } - - TDEIO::AuthInfo authInfo; - authInfo.username = m_sUser; - authInfo.password = m_sPass; - authInfo.prompt = i18n("Username and password for your POP3 account:"); - - if ( supports_apop && m_try_apop ) { - POP3_DEBUG << "Trying APOP" << endl; - int retval = loginAPOP( greeting.data() + apop_pos, authInfo ); - switch ( retval ) { - case 0: return true; - case -1: return false; - default: - m_try_apop = false; - } - } else if ( m_try_sasl ) { - POP3_DEBUG << "Trying SASL" << endl; - int retval = loginSASL( authInfo ); - switch ( retval ) { - case 0: return true; - case -1: return false; - default: - m_try_sasl = false; - } - } else { - // Fall back to conventional USER/PASS scheme - POP3_DEBUG << "Trying USER/PASS" << endl; - return loginPASS( authInfo ); - } - } while ( true ); -} - -size_t POP3Protocol::realGetSize(unsigned int msg_num) -{ - char *buf; - TQCString cmd; - size_t ret = 0; - - buf = new char[MAX_RESPONSE_LEN]; - memset(buf, 0, MAX_RESPONSE_LEN); - cmd.sprintf("LIST %u", msg_num); - if ( command(cmd.data(), buf, MAX_RESPONSE_LEN) != Ok ) { - delete[]buf; - return 0; - } else { - cmd = buf; - cmd.remove(0, cmd.find(" ")); - ret = cmd.toLong(); - } - delete[]buf; - return ret; -} - -void POP3Protocol::special(const TQByteArray & aData) -{ - TQString result; - char buf[MAX_PACKET_LEN]; - TQDataStream stream(aData, IO_ReadOnly); - int tmp; - stream >> tmp; - - if (tmp != 'c') - return; - - for (int i = 0; i < 2; i++) { - TQCString cmd = (i) ? "AUTH" : "CAPA"; - if ( command(cmd) != Ok ) - continue; - while (true) { - myReadLine(buf, MAX_PACKET_LEN - 1); - if (qstrcmp(buf, ".\r\n") == 0) - break; - result += " " + TQString(buf).left(strlen(buf) - 2) - .replace(" ", "-"); - } - } - if (supports_apop) - result += " APOP"; - result = result.mid(1); - infoMessage(result); - finished(); -} - -void POP3Protocol::get(const KURL & url) -{ -// List of supported commands -// -// URI Command Result -// pop3://user:pass@domain/index LIST List message sizes -// pop3://user:pass@domain/uidl UIDL List message UIDs -// pop3://user:pass@domain/remove/#1 DELE #1 Mark a message for deletion -// pop3://user:pass@domain/download/#1 RETR #1 Get message header and body -// pop3://user:pass@domain/list/#1 LIST #1 Get size of a message -// pop3://user:pass@domain/uid/#1 UIDL #1 Get UID of a message -// pop3://user:pass@domain/commit QUIT Delete marked messages -// pop3://user:pass@domain/headers/#1 TOP #1 Get header of message -// -// Notes: -// Sizes are in bytes. -// No support for the STAT command has been implemented. -// commit closes the connection to the server after issuing the QUIT command. - - bool ok = true; - char buf[MAX_PACKET_LEN]; - char destbuf[MAX_PACKET_LEN]; - TQByteArray array; - TQString cmd, path = url.path(); - int maxCommands = (metaData("pipelining") == "on") ? MAX_COMMANDS : 1; - - if (path.at(0) == '/') - path.remove(0, 1); - if (path.isEmpty()) { - POP3_DEBUG << "We should be a dir!!" << endl; - error(ERR_IS_DIRECTORY, url.url()); - m_cmd = CMD_NONE; - return; - } - - if (((path.find('/') == -1) && (path != "index") && (path != "uidl") - && (path != "commit"))) { - error(ERR_MALFORMED_URL, url.url()); - m_cmd = CMD_NONE; - return; - } - - cmd = path.left(path.find('/')); - path.remove(0, path.find('/') + 1); - - if (!pop3_open()) { - POP3_DEBUG << "pop3_open failed" << endl; - error(ERR_COULD_NOT_CONNECT, m_sServer); - return; - } - - if ((cmd == "index") || (cmd == "uidl")) { - unsigned long size = 0; - bool result; - - if (cmd == "index") { - result = ( command("LIST") == Ok ); - } else { - result = ( command("UIDL") == Ok ); - } - - /* - LIST - +OK Mailbox scan listing follows - 1 2979 - 2 1348 - . - */ - if (result) { - while (true /* !AtEOF() */ ) { - memset(buf, 0, sizeof(buf)); - myReadLine(buf, sizeof(buf) - 1); - - // HACK: This assumes fread stops at the first \n and not \r - if (strcmp(buf, ".\r\n") == 0) { - break; // End of data - } - // sanders, changed -2 to -1 below - int bufStrLen = strlen(buf); - buf[bufStrLen - 2] = '\0'; - size += bufStrLen; - array.setRawData(buf, bufStrLen); - data(array); - array.resetRawData(buf, bufStrLen); - totalSize(size); - } - } - POP3_DEBUG << "Finishing up list" << endl; - data(TQByteArray()); - finished(); - } else if (cmd == "remove") { - TQStringList waitingCommands = TQStringList::split(',', path); - int activeCommands = 0; - TQStringList::Iterator it = waitingCommands.begin(); - while (it != waitingCommands.end() || activeCommands > 0) { - while (activeCommands < maxCommands && it != waitingCommands.end()) { - sendCommand(("DELE " + *it).latin1()); - activeCommands++; - it++; - } - getResponse(buf, sizeof(buf) - 1, ""); - activeCommands--; - } - finished(); - m_cmd = CMD_NONE; - } else if (cmd == "download" || cmd == "headers") { - TQStringList waitingCommands = TQStringList::split(',', path); - bool noProgress = (metaData("progress") == "off" - || waitingCommands.count() > 1); - int p_size = 0; - unsigned int msg_len = 0; - TQString list_cmd("LIST "); - list_cmd += path; - memset(buf, 0, sizeof(buf)); - if ( !noProgress ) { - if ( command(list_cmd.ascii(), buf, sizeof(buf) - 1) == Ok ) { - list_cmd = buf; - // We need a space, otherwise we got an invalid reply - if (!list_cmd.find(" ")) { - POP3_DEBUG << "List command needs a space? " << list_cmd << endl; - closeConnection(); - error(ERR_INTERNAL, i18n("Unexpected response from POP3 server.")); - return; - } - list_cmd.remove(0, list_cmd.find(" ") + 1); - msg_len = list_cmd.toUInt(&ok); - if (!ok) { - POP3_DEBUG << "LIST command needs to return a number? :" << - list_cmd << ":" << endl; - closeConnection(); - error(ERR_INTERNAL, i18n("Unexpected response from POP3 server.")); - return; - } - } else { - closeConnection(); - error(ERR_COULD_NOT_READ, m_sError); - return; - } - } - - int activeCommands = 0; - TQStringList::Iterator it = waitingCommands.begin(); - while (it != waitingCommands.end() || activeCommands > 0) { - while (activeCommands < maxCommands && it != waitingCommands.end()) { - sendCommand(((cmd == - "headers") ? "TOP " + *it + " 0" : "RETR " + - *it).latin1()); - activeCommands++; - it++; - } - if ( getResponse(buf, sizeof(buf) - 1, "") == Ok ) { - activeCommands--; - mimeType("message/rfc822"); - totalSize(msg_len); - memset(buf, 0, sizeof(buf)); - char ending = '\n'; - bool endOfMail = false; - bool eat = false; - while (true /* !AtEOF() */ ) { - ssize_t readlen = myRead(buf, sizeof(buf) - 1); - if (readlen <= 0) { - if (isConnectionValid()) - error(ERR_SERVER_TIMEOUT, m_sServer); - else - error(ERR_CONNECTION_BROKEN, m_sServer); - closeConnection(); - return; - } - if (ending == '.' && readlen > 1 && buf[0] == '\r' - && buf[1] == '\n') { - readBufferLen = readlen - 2; - memcpy(readBuffer, &buf[2], readBufferLen); - break; - } - bool newline = (ending == '\n'); - - if (buf[readlen - 1] == '\n') - ending = '\n'; - else if (buf[readlen - 1] == '.' - && ((readlen > 1) ? buf[readlen - 2] == '\n' : ending == - '\n')) - ending = '.'; - else - ending = ' '; - - char *buf1 = buf, *buf2 = destbuf; - // ".." at start of a line means only "." - // "." means end of data - for (ssize_t i = 0; i < readlen; i++) { - if (*buf1 == '\r' && eat) { - endOfMail = true; - if (i == readlen - 1 /* && !AtEOF() */ ) - myRead(buf, 1); - else if (i < readlen - 2) { - readBufferLen = readlen - i - 2; - memcpy(readBuffer, &buf[i + 2], readBufferLen); - } - break; - } else if (*buf1 == '\n') { - newline = true; - eat = false; - } else if (*buf1 == '.' && newline) { - newline = false; - eat = true; - } else { - newline = false; - eat = false; - } - if (!eat) { - *buf2 = *buf1; - buf2++; - } - buf1++; - } - - if (buf2 > destbuf) { - array.setRawData(destbuf, buf2 - destbuf); - data(array); - array.resetRawData(destbuf, buf2 - destbuf); - } - - if (endOfMail) - break; - - if (!noProgress) { - p_size += readlen; - processedSize(p_size); - } - } - infoMessage("message complete"); - } else { - POP3_DEBUG << "Couldn't login. Bad RETR Sorry" << endl; - closeConnection(); - error(ERR_COULD_NOT_READ, m_sError); - return; - } - } - POP3_DEBUG << "Finishing up" << endl; - data(TQByteArray()); - finished(); - } else if ((cmd == "uid") || (cmd == "list")) { - TQString qbuf; - (void) path.toInt(&ok); - - if (!ok) { - return; // We fscking need a number! - } - - if (cmd == "uid") { - path.prepend("UIDL "); - } else { - path.prepend("LIST "); - } - - memset(buf, 0, sizeof(buf)); - if ( command(path.ascii(), buf, sizeof(buf) - 1) == Ok ) { - const int len = strlen(buf); - mimeType("text/plain"); - totalSize(len); - array.setRawData(buf, len); - data(array); - array.resetRawData(buf, len); - processedSize(len); - POP3_DEBUG << buf << endl; - POP3_DEBUG << "Finishing up uid" << endl; - data(TQByteArray()); - finished(); - } else { - closeConnection(); - error(ERR_INTERNAL, i18n("Unexpected response from POP3 server.")); - return; - } - } else if (cmd == "commit") { - POP3_DEBUG << "Issued QUIT" << endl; - closeConnection(); - finished(); - m_cmd = CMD_NONE; - return; - } -} - -void POP3Protocol::listDir(const KURL &) -{ - bool isINT; - int num_messages = 0; - char buf[MAX_RESPONSE_LEN]; - TQCString q_buf; - - // Try and open a connection - if (!pop3_open()) { - POP3_DEBUG << "pop3_open failed" << endl; - error(ERR_COULD_NOT_CONNECT, m_sServer); - return; - } - // Check how many messages we have. STAT is by law required to - // at least return +OK num_messages total_size - memset(buf, 0, MAX_RESPONSE_LEN); - if ( command("STAT", buf, MAX_RESPONSE_LEN) != Ok ) { - error(ERR_INTERNAL, "??"); - return; - } - POP3_DEBUG << "The stat buf is :" << buf << ":" << endl; - q_buf = buf; - if (q_buf.find(" ") == -1) { - error(ERR_INTERNAL, - "Invalid POP3 response, we should have at least one space!"); - closeConnection(); - return; - } - q_buf.remove(q_buf.find(" "), q_buf.length()); - - num_messages = q_buf.toUInt(&isINT); - if (!isINT) { - error(ERR_INTERNAL, "Invalid POP3 STAT response!"); - closeConnection(); - return; - } - UDSEntry entry; - UDSAtom atom; - TQString fname; - for (int i = 0; i < num_messages; i++) { - fname = "Message %1"; - - atom.m_uds = UDS_NAME; - atom.m_long = 0; - atom.m_str = fname.arg(i + 1); - entry.append(atom); - - atom.m_uds = UDS_MIME_TYPE; - atom.m_long = 0; - atom.m_str = "text/plain"; - entry.append(atom); - POP3_DEBUG << "Mimetype is " << atom.m_str.ascii() << endl; - - atom.m_uds = UDS_URL; - KURL uds_url; - if (m_bIsSSL) { - uds_url.setProtocol("pop3s"); - } else { - uds_url.setProtocol("pop3"); - } - - uds_url.setUser(m_sUser); - uds_url.setPass(m_sPass); - uds_url.setHost(m_sServer); - uds_url.setPath(TQString::fromLatin1("/download/%1").arg(i + 1)); - atom.m_str = uds_url.url(); - atom.m_long = 0; - entry.append(atom); - - atom.m_uds = UDS_FILE_TYPE; - atom.m_str = ""; - atom.m_long = S_IFREG; - entry.append(atom); - - atom.m_uds = UDS_SIZE; - atom.m_str = ""; - atom.m_long = realGetSize(i + 1); - entry.append(atom); - - atom.m_uds = TDEIO::UDS_ACCESS; - atom.m_long = S_IRUSR | S_IXUSR | S_IWUSR; - entry.append (atom); - - listEntry(entry, false); - entry.clear(); - } - listEntry(entry, true); // ready - - finished(); -} - -void POP3Protocol::stat(const KURL & url) -{ - TQString _path = url.path(); - - if (_path.at(0) == '/') - _path.remove(0, 1); - - UDSEntry entry; - UDSAtom atom; - - atom.m_uds = UDS_NAME; - atom.m_str = _path; - entry.append(atom); - - atom.m_uds = UDS_FILE_TYPE; - atom.m_str = ""; - atom.m_long = S_IFREG; - entry.append(atom); - - atom.m_uds = UDS_MIME_TYPE; - atom.m_str = "message/rfc822"; - entry.append(atom); - - // TODO: maybe get the size of the message? - statEntry(entry); - - finished(); -} - -void POP3Protocol::del(const KURL & url, bool /*isfile */ ) -{ - TQString invalidURI = TQString::null; - bool isInt; - - if (!pop3_open()) { - POP3_DEBUG << "pop3_open failed" << endl; - error(ERR_COULD_NOT_CONNECT, m_sServer); - return; - } - - TQString _path = url.path(); - if (_path.at(0) == '/') { - _path.remove(0, 1); - } - - _path.toUInt(&isInt); - if (!isInt) { - invalidURI = _path; - } else { - _path.prepend("DELE "); - if ( command(_path.ascii()) != Ok ) { - invalidURI = _path; - } - } - - POP3_DEBUG << "POP3Protocol::del " << _path << endl; - finished(); -} |