/*************************************************************************** ConfigElem.cpp - description ------------------- begin : Tue May 9 2000 copyright : (C) 2000-2001 by Eggert Ehmke email : eggert.ehmke@berlin.de ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "configelem.h" int const ConfigElem::continueShowHeaders( 0 ); int const ConfigElem::cancelShowHeaders( 1 ); ConfigElem::ConfigElem( ) : TQObject() { //initialize account init(); //set default values m_url.setProtocol( "pop3" ); m_url.setPort( 110 ); m_bActive = false; appConfig = NULL; m_strAccount = ""; } ConfigElem::ConfigElem( ConfigList* config ) : TQObject() { //initialize account init(); m_url.setProtocol( "pop3" ); m_url.setPort (110); m_bActive = false; appConfig = config; } ConfigElem::ConfigElem( ConfigElem* pElem ) : TQObject() { //initialize account init(); //set active by default m_bActive = pElem->isActive(); //copy some interesting stuff from the sample //the url object contains all necessary information about the server m_strAccount = pElem->getAccountName(); m_url = pElem->getURL(); appConfig = pElem->appConfig; } ConfigElem::ConfigElem( ConfigList* config, const TQString& account ) : TQObject() { //initialize account init(); //set account name m_strAccount = account; //deactivate it by default m_bActive = false; //set the pointer to the general app configuration appConfig = config; } void ConfigElem::init( ) { //initialize timeout timer pop3Timer = new TQTimer( this ); connect( pop3Timer, SIGNAL( timeout() ), this, SLOT( slotTimeout() ) ); //state is idle state = AccountIdle; //create new empty mail list m_pshowrecord = new ShowRecord(); //the account has no appropriate account list view item yet m_pViewItem = NULL; //set default values PasswordStorage = DEFAULT_ACCOUNT_PASSWORD_STORAGE; filterApplied = false; deletionPerformedByFilters = false; refreshPerformedByFilters = false; downloadActionsInvoked = false; //initialize counters moveCounter = 0; nmbDeletedMailsLastRefresh = 0; nmbDeletedMailsLastStart = 0; nmbMovedMailsLastRefresh = 0; nmbMovedMailsLastStart = 0; nmbIgnoredMails = 0; } ConfigElem::~ConfigElem() { // do not delete m_pshowrecord here } void ConfigElem::saveOptions( TQDomDocument& doc, TQDomElement& parent ) { //get application config TDEConfig* config = TDEApplication::kApplication()->config(); //save the active state config->setGroup( getAccountName() ); config->writeEntry( CONFIG_ENTRY_ACCOUNT_ACTIVE, m_bActive ); config->sync(); //save the stored mails inside this account parent.setAttribute( ATTRIBUTE_ACCOUNT_NAME, m_strAccount ); m_pshowrecord->saveOptions( doc, parent ); } void ConfigElem::readStoredMails( TQDomElement& parent ) { //get mails m_pshowrecord->readStoredMails( parent ); } int ConfigElem::count() { return m_pshowrecord->count(); } bool ConfigElem::isActive( ) const { return m_bActive; } void ConfigElem::setActive( bool active ) { m_bActive = active; } TQString ConfigElem::getAccountName( ) const { return m_strAccount; } void ConfigElem::setAccountName( TQString name ) { if( name != NULL ) m_strAccount = name; } TQString ConfigElem::getPassword( ) const { return m_url.pass(); } void ConfigElem::setPassword( const TQString& password ) { m_url.setPass( password ); } KURL ConfigElem::getURL( ) const { return m_url; } bool ConfigElem::hasPassword( ) const { return m_url.hasPass(); } void ConfigElem::setListViewItem( TQListViewItem* item ) { m_pViewItem = item; } TQListViewItem * ConfigElem::getListViewItem( ) { return m_pViewItem; } bool ConfigElem::isSelected( ) const { if( m_pViewItem == NULL ) return false; else return m_pViewItem->isSelected(); } void ConfigElem::clearMailList( ) { if( m_pshowrecord == NULL ) //there is no mail list yet, create a one m_pshowrecord = new ShowRecord; else //clear the existing mail list m_pshowrecord->clear(); } void ConfigElem::setHost( const TQString& host ) { m_url.setHost( host ); } void ConfigElem::setProtocol( const TQString& protocol ) { m_url.setProtocol( protocol ); } void ConfigElem::setPort( unsigned short int port ) { m_url.setPort( port ); } void ConfigElem::setUser( const TQString & user ) { m_url.setUser( user ); } TQString ConfigElem::getUser( ) const { return m_url.user(); } TQString ConfigElem::getHost( ) const { return m_url.host(); } void ConfigElem::deleteSelectedMails( ) { //return if this account has no selected mails or //the account is not idle or the account is not active if( !m_pshowrecord->hasSelectedMails() || state != AccountIdle || !isActive() ) { emit sigDeleteReady( m_strAccount ); return; } //check whether we have a password for this account //if not, ask for it //return when no password is available if( !assertPassword() ) { emit sigDeleteReady( m_strAccount ); return; } //get the numbers of all selected mails MailsToDelete = m_pshowrecord->getSelectedMails(); if( MailsToDelete.empty() ) { kdError() << "ConfigElem::deleteSelectedMails (Account " << m_strAccount << "): The account has selected mails to delete but ShowRecord::getSelectedMails has returned an empty list." << endl; emit sigDeleteReady( m_strAccount ); return; } //set account state state = AccountDeleting; //start the deleting of all mails in MailsToDelete deleteNextMail(); } bool ConfigElem::assertPassword( bool force ) { //is a password stored? if ( !hasPassword() || force ) { //no password found, we will ask the user! //set normal cursor while( TQApplication::overrideCursor() ) TQApplication::restoreOverrideCursor(); TQCString password; //for the password dialog to store the password int result = KPasswordDialog::getPassword( password, i18n( "Please type in the password for %1" ).arg( getAccountName() ) ); //set waiting cursor TQApplication::setOverrideCursor( TQt::waitCursor ); //let's look, what the user has done :o) if( result == KPasswordDialog::Accepted ) { //the user has clicked OK in the password dialog //store the password setPassword( password ); //save password in file or TDEWallet TDEConfig* config = TDEApplication::kApplication()->config(); config->setGroup( getAccountName() ); if( PasswordStorage == CONFIG_VALUE_ACCOUNT_PASSWORD_SAVE_FILE ) config->writeEntry( CONFIG_ENTRY_ACCOUNT_PASSWORD, crypt( m_url ) ); else config->writeEntry( CONFIG_ENTRY_ACCOUNT_PASSWORD, TQString::null ); if( PasswordStorage == CONFIG_VALUE_ACCOUNT_PASSWORD_SAVE_KWALLET ) TDEWalletAccess::savePassword( getAccountName(), m_url.pass() ); config->sync(); //emit configuration changed signal emit ( sigConfigChanged() ); //tell we have a password return true; } else //the user has clicked Cancel in the password dialog; we don't have a password return false; } else //we have already a password for this account return true; } void ConfigElem::deleteNextMail( ) { //if the list of mails to delete is empty, finalize the deletion and return if( MailsToDelete.empty() ) { if( deletionPerformedByFilters ) { applyFiltersDeleted(); } else { commitDeletion(); } return; } //start job startKIOJob( TQString( "/remove/%1" ).arg( *MailsToDelete.begin() ) ); connect( pop3Job, SIGNAL( result( TDEIO::Job* ) ), this, SLOT( slotMailDeleted( TDEIO::Job* ) ) ); } void ConfigElem::slotMailDeleted( TDEIO::Job* job ) { //stop timeout timer pop3Timer->stop(); //check for errors //if an error is occured, the deletion will be canceled //or will ask for a new password if( job->error() == TDEIO::ERR_COULD_NOT_LOGIN ) { //login failed, ask for a new password job->showErrorDialog(); bool res = assertPassword( true ); if( res == false ) { //we have not got a new password; cancel delete if( deletionPerformedByFilters ) { applyFiltersDeleted(); } else { slotFinalizeDeletion( NULL ); } return; } //if we have got a new password, it jumps to the end of the if-statement } else if( job->error() != 0 ) { //unknown error, show message and cancel delete job->showErrorDialog(); if( deletionPerformedByFilters ) { applyFiltersDeleted(); } else { slotFinalizeDeletion( NULL ); } return; } else { //operation was successful //remove the deleted mail from the internal mail list m_pshowrecord->removeMail( *MailsToDelete.begin() ); //remove the first item of the list of mails to delete MailsToDelete.remove( MailsToDelete.begin() ); //if the list of mails to delete is empty, finalize the deletion and return if( MailsToDelete.empty() ) { if( deletionPerformedByFilters ) { applyFiltersDeleted(); } else { commitDeletion(); } return; } } //delete next mail in list deleteNextMail(); } void ConfigElem::slotFinalizeDeletion( TDEIO::Job* ) { //stop timeout time pop3Timer->stop(); //set account state to idle state = AccountIdle; //emit signal to report the deletion is ready emit sigDeleteReady( m_strAccount ); } void ConfigElem::startKIOJob( const TQString & path ) { TDEIO::MetaData options; //options for the pop3 job //set options options.insert( "progress", "off" ); options.insert( "pipelining", "off" ); if( useTLS ) options.insert( "tls", "on" ); else options.insert( "tls", "off" ); //Where is secure login? //I have decided against a configurable secure login because the used POP3 tdeioslave //always tries to login with APOP, if the server has sent a timestap (inside of the greeting string) for this authentification type. //It just follows the auth-metadata, if the server doesn't support APOP (no timestamp inside of the greeting string). //But I think, there is no server, which support a SASL authentification without also provide APOP. //Ulrich Weigelt //set the given command and parameters m_url.setPath( path ); //print debug message kdDebug() << "ConfigElem::startKIOJob: start KIO job on URL " << m_url.url() << endl; //start the job and get handle to it pop3Job = TDEIO::get( m_url, false, false ); //put options to the job pop3Job->addMetaData( options ); //start timeout timer pop3Timer->start( getTimeoutTime() * 1000, true ); } Types::AccountState_Type ConfigElem::getState( ) { return state; } void ConfigElem::commitDeletion( ) { //start job to commit startKIOJob( TQString( "/commit" ) ); connect( pop3Job, SIGNAL( result( TDEIO::Job* ) ), this, SLOT( slotFinalizeDeletion( TDEIO::Job* ) ) ); } unsigned int ConfigElem::getTimeoutTime( ) { //return default time, if the configuration is not accessable if( appConfig == NULL ) return DEFAULT_TIMEOUT_TIME; //get time from configuration unsigned int time = appConfig->getTimeoutTime(); //take minimum time, if get time is less if( time < MINIMUM_TIMEOUT_TIME ) time = MINIMUM_TIMEOUT_TIME; return time; } void ConfigElem::slotTimeout( ) { //kill a running job if( pop3Job != NULL ) pop3Job->kill( true ); //show error message (during refresh if desired only) kdError() << "Timeout error!" << endl; if( state != AccountRefreshing || appConfig->showConnectionErrors() ) KMessageBox::error( NULL, TQString( i18n( "Time out on %1. The operation could not be finished on time" ) ).arg( m_strAccount ), i18n( "Time Out" ) ); //call the appropriate finalize methode switch( state ) { case AccountIdle : break; case AccountDeleting : slotFinalizeDeletion( NULL ); break; case AccountDownloading : slotFinalizeShowMail( NULL ); break; case AccountRefreshing : cancelRefresh(); break; default : break; } } TQStringList ConfigElem::getSelectedSubjects( ) const { return m_pshowrecord->getSelectedSubjects(); } bool ConfigElem::hasSelectedMails( ) { return m_pshowrecord->hasSelectedMails(); } void ConfigElem::showSelectedMails( ) { //return if this account has no selected mails or //the account is not idle or the account is not active if( !m_pshowrecord->hasSelectedMails() || state != AccountIdle || !isActive() ) { emit sigShowBodiesReady( m_strAccount ); return; } //check whether we have a password for this account //if not, ask for it //return when no password is available if( !assertPassword() ) { emit sigShowBodiesReady( m_strAccount ); return; } //get the numbers of all selected mails MailsToShow = m_pshowrecord->getSelectedMails(); if( MailsToShow.empty() ) { kdError() << "ConfigElem::showSelectedMails (Account " << m_strAccount << "): The account has selected mails to show but ShowRecord::getSelectedMails has returned an empty list." << endl; emit sigShowBodiesReady( m_strAccount ); return; } //set account state state = AccountDownloading; //start the deleting of all mails in MailsToDelete showNextMail(); } void ConfigElem::showNextMail( ) { //if the list of mails to show is empty, finalize it and return if( MailsToShow.empty() ) { slotFinalizeShowMail( NULL ); return; } //clear the class variable mailbody, which contains the downloaded mail body mailbody.resize( 0 ); //start job startKIOJob( TQString( "/download/%1" ).arg( *MailsToShow.begin() ) ); connect( pop3Job, SIGNAL( data( TDEIO::Job*, const TQByteArray & ) ), SLOT( slotDataMailBody( TDEIO::Job*, const TQByteArray & ) ) ); connect( pop3Job, SIGNAL( result( TDEIO::Job* ) ), this, SLOT( slotBodyDownloaded( TDEIO::Job* ) ) ); } void ConfigElem::slotBodyDownloaded( TDEIO::Job * job ) { //stop timeout timer pop3Timer->stop(); //check for errors //if an error has occured, the download will be canceled //or will ask for a new password if( job->error() == TDEIO::ERR_COULD_NOT_LOGIN ) { //login failed, ask for a new password job->showErrorDialog(); bool res = assertPassword( true ); if( res == false ) { //we have not got a new password; cancel delete slotFinalizeShowMail( NULL ); return; } //if we have got a new password, jump to the end of the if-statement } else if( job->error() != 0 ) { job->showErrorDialog(); slotFinalizeShowMail( NULL ); return; } else { //succesful download //show mail int currentMail = *MailsToShow.begin(); TQString tsender = m_pshowrecord->getSenderOf( currentMail ); TQString tdate = m_pshowrecord->getDateOf( currentMail ); TQString tsize = m_pshowrecord->getSizeOf( currentMail ); TQString tsubject = m_pshowrecord->getSubjectOf( currentMail ); TQString tmailbody( m_pshowrecord->decodeMailBody( mailbody, currentMail, appConfig->allowHTML() ) ); //emit signal to notify the opening of a window emit sigMessageWindowOpened(); //create and open the window ShowMailDialog dlg( kapp->mainWidget(), m_strAccount, appConfig->allowHTML(), tsender, tdate, tsize, tsubject, tmailbody ); int ret = dlg.exec(); //emit signal to notify the closing of a window emit sigMessageWindowClosed(); //cancel the download if desired if( ret == KDialogBase::Rejected ) { MailsToShow.clear(); commitDownloading(); return; } //remove the first item of the list of mails to show MailsToShow.remove( MailsToShow.begin() ); //if the list of mails is empty, finalize the showing and return if( MailsToShow.empty() ) { commitDownloading(); return; } } //show next mail in list showNextMail(); } void ConfigElem::slotFinalizeShowMail( TDEIO::Job* ) { //stop timeout time pop3Timer->stop(); //set account state to idle state = AccountIdle; //emit signal to report the download is ready emit sigShowBodiesReady( m_strAccount ); } void ConfigElem::slotDataMailBody( TDEIO::Job *, const TQByteArray & datas ) { if( !datas.isEmpty() ) { //we get the next part of the mail //append it uint lastSize = mailbody.size(); mailbody.resize( lastSize + datas.size() ); for( uint i = 0; i < datas.size(); i++ ) mailbody[ lastSize + i ] = datas[ i ]; } } void ConfigElem::commitDownloading( ) { //start job to commit startKIOJob( TQString( "/commit" ) ); connect( pop3Job, SIGNAL( result( TDEIO::Job* ) ), this, SLOT( slotFinalizeShowMail( TDEIO::Job* ) ) ); } void ConfigElem::refreshMailList( FilterLog* log ) { //store pointer to log if( log != NULL ) FLog = log; //return, if account is not active if( !isActive() ) { emit sigRefreshReady( m_strAccount ); return; } //check whether we have a password for this account //if not, ask for it //return when no password is available if( !assertPassword() ) { emit sigRefreshReady( m_strAccount ); return; } //create a new ShowRecord instance //When the refresh has finished successfully, this will //replace the old mail list tempMailList = new ShowRecord(); //set account state state = AccountRefreshing; //init counter if( !refreshPerformedByFilters ) { nmbDeletedMailsLastRefresh = 0; nmbMovedMailsLastRefresh = 0; nmbIgnoredMails = 0; } //the first step is to get the UIDs getUIDs(); } void ConfigElem::getUIDs( ) { //clears the TQString list, which contains all received UIDs receivedUIDs.clear(); //start job startKIOJob( TQString( "/uidl" ) ); connect( pop3Job, SIGNAL( data( TDEIO::Job*, const TQByteArray & ) ), SLOT( slotReceiveUID( TDEIO::Job*, const TQByteArray & ) ) ); connect( pop3Job, SIGNAL( result( TDEIO::Job* ) ), this, SLOT( slotUIDsReceived( TDEIO::Job* ) ) ); } void ConfigElem::slotReceiveUID( TDEIO::Job*, const TQByteArray& data ) { //return, when data is empty if( data.isEmpty() ) return; //cast the data to TQString TQString uid( data ); //insert the uid at the end of the UID list receivedUIDs.append( uid ); } void ConfigElem::slotUIDsReceived( TDEIO::Job * job ) { int number; //an extracted mail number TQString uid; //an extracted uid bool corruptData = false; //set to TRUE, if a data is corrupt bool isNew = false; //state of the received mail //stop timeout timer pop3Timer->stop(); //check for errors //if an error has occured, the refresh will be canceled //or will ask for a new password if( job->error() == TDEIO::ERR_COULD_NOT_LOGIN ) { //login failed, ask for a new password job->showErrorDialog(); bool res = assertPassword( true ); if( res == true ) { //we have got a new password, try again delete tempMailList; refreshMailList(); } else //we have not got a new password; cancel refresh cancelRefresh(); return; } else if( job->error() != 0 ) { //show error message if desired if( appConfig->showConnectionErrors() ) job->showErrorDialog(); cancelRefresh(); return; } //analyze UIDs if( !receivedUIDs.isEmpty() ) { //iterate over all UIDs in the list for ( TQStringList::Iterator it = receivedUIDs.begin(); it != receivedUIDs.end(); ++it ) { TQString line = *it; //every line has the format "number UID", e.g.: 1 bf10d38018de7c1d628d65288d722f6a //get the position of the separating space int positionOfSpace = line.find( " " ); //if no space was found, the line is corrupt if( positionOfSpace == -1 ) { kdError() << "ConfigElem::slotUIDsReceived: get a corrupt UID from " << dynamic_cast(job)->url().host() << ". No space. : " << line << endl; corruptData = true; } else { //extract mail number and uid bool isNumber; number = line.left( positionOfSpace ).toInt( &isNumber ); //check number if( !isNumber ) { //the first part is not a number kdError() << "ConfigElem::slotUIDsReceived: get a corrupt UID from " << dynamic_cast(job)->url().host() << ". No number found at begin. : " << line << endl; corruptData = true; } else { //number is ok; extract uid uid = line.mid( positionOfSpace + 1 ); //determine about new mail or not if( !m_pshowrecord->hasMail( uid ) ) { //the old list doesn't contain a mail with this uid //the mail is new isNew = true; } else if( ( appConfig->keepNew() || refreshPerformedByFilters ) && m_pshowrecord->isNew( uid ) ) { //the mail is already in the old list //but we will leave the state of formerly new mails, because the user wants it or this refresh is performed by filters isNew = true; } else isNew = false; //append mail to the list tempMailList->appendNewMail( number, uid, isNew ); } } } //if the data are ok, start the second step: get sizes //otherwise cancel the refresh if( !corruptData ) getSizes(); else cancelRefresh(); } else { //we haven't received any UIDs. The account has no mails. //finalize the refresh swapMailLists(); } } void ConfigElem::cancelRefresh() { //print error message kdError() << m_strAccount << ": " << "Refresh canceled" << endl; //delete the new mail list delete tempMailList; //delete old mail list and create a new empty one delete m_pshowrecord; m_pshowrecord = new ShowRecord(); //emit signal emit sigRefreshReady( m_strAccount ); //set account state to idle state = AccountIdle; //we don't need an error message, because the KIO job has shown one } void ConfigElem::slotFinalizeRefresh( TDEIO::Job* ) { //stop timeout time pop3Timer->stop(); //unset the flag refreshPerformedByFilters = false; //emit signal emit sigRefreshReady( m_strAccount ); //set account state to idle state = AccountIdle; } void ConfigElem::commitRefresh( ) { //start job to commit startKIOJob( TQString( "/commit" ) ); connect( pop3Job, SIGNAL( result( TDEIO::Job* ) ), this, SLOT( slotFinalizeRefresh( TDEIO::Job* ) ) ); } void ConfigElem::getSizes( ) { //clears the TQString list, which contains all received UIDs receivedSizes.clear(); //start job startKIOJob( TQString( "/index" ) ); connect( pop3Job, SIGNAL( data( TDEIO::Job*, const TQByteArray & ) ), SLOT( slotReceiveSize( TDEIO::Job*, const TQByteArray & ) ) ); connect( pop3Job, SIGNAL( result( TDEIO::Job* ) ), this, SLOT( slotSizesReceived( TDEIO::Job* ) ) ); } void ConfigElem::slotSizesReceived( TDEIO::Job * job ) { int number; //an extracted mail number long size; //an extracted size bool corruptData = false; //set to TRUE, if a data is corrupt //stop timeout timer pop3Timer->stop(); //check for errors //if an error has occured, the refresh will be canceled if( job->error() != 0 ) { //show error message if desired if( appConfig->showConnectionErrors() ) job->showErrorDialog(); cancelRefresh(); return; } //analyze UIDs if( !receivedSizes.isEmpty() ) { //iterate over all sizes in the list for ( TQStringList::Iterator it = receivedSizes.begin(); it != receivedSizes.end(); ++it ) { TQString line = *it; //every line has the format "number size", e.g.: 1 1234 //get the position of the separating space int positionOfSpace = line.find( " " ); //if no space was found, the line is corrupt if( positionOfSpace == -1 ) { kdError() << "ConfigElem::slotSizesReceived: get a corrupt size from " << dynamic_cast(job)->url().host() << ". No space. : " << line << endl; corruptData = true; } else { //extract mail number and size bool isNumber; number = line.left( positionOfSpace ).toInt( &isNumber ); //check number if( !isNumber ) { //the first part is not a number kdError() << "ConfigElem::slotSizesReceived: get a corrupt size from " << dynamic_cast(job)->url().host() << ". No number found at begin. : " << line << endl; corruptData = true; } else { //number is ok; extract size size = line.mid( positionOfSpace + 1 ).toLong( &isNumber ); //check size if( !isNumber ) { //the second part of the string is not a number kdError() << "ConfigElem::slotSizesReceived: get a corrupt size from " << dynamic_cast(job)->url().host() << ". No size found at end. : " << line << endl; corruptData = true; } else { //size is ok //set it tempMailList->setSize( number, size ); } } } } //if the data are ok, start the third step: get headers //otherwise cancel the refresh if( !corruptData ) getHeaders(); else cancelRefresh(); } } void ConfigElem::slotReceiveSize( TDEIO::Job *, const TQByteArray & data ) { //return, when data is empty if( data.isEmpty() ) return; //cast the data to TQString TQString size( data ); //insert the uid at the end of the sizes list receivedSizes.append( size ); } void ConfigElem::getHeaders( ) { //get the numbers of all new mails newMails = tempMailList->getNewMails(); if( newMails.empty() ) { //no new mails available; copy the known headers from the old mail list copyHeaders(); return; } //get the headers getNextHeader(); } void ConfigElem::getNextHeader( ) { //if the list of mails empty, copy the known headers from the old mail list if( newMails.empty() ) { copyHeaders(); return; } //clear temporary header store receivedHeader.resize( 0 ); //start job startKIOJob( TQString( "/headers/%1" ).arg( *newMails.begin() ) ); connect( pop3Job, SIGNAL( data( TDEIO::Job*, const TQByteArray & ) ), this, SLOT( slotReceiveHeader( TDEIO::Job*, const TQByteArray & ) ) ); connect( pop3Job, SIGNAL( result( TDEIO::Job* ) ), this, SLOT( slotHeaderDownloaded( TDEIO::Job* ) ) ); } void ConfigElem::slotHeaderDownloaded( TDEIO::Job * job ) { //stop timeout timer pop3Timer->stop(); //check for errors //if an error is occured, the download will be canceled if( job->error() != 0 ) { //show error message if desired if( appConfig->showConnectionErrors() ) job->showErrorDialog(); cancelRefresh(); return; } //store header tempMailList->setHeader( *newMails.begin(), TQString( receivedHeader ) ); //remove the first item of the list of new mails newMails.remove( newMails.begin() ); //if the list of new mails is empty, copy the headers of old mails to the new list if( newMails.empty() ) { copyHeaders(); return; } //get next header getNextHeader(); } void ConfigElem::copyHeaders( ) { //get the UIDs of the old mails in the temporary mail list TQStringList UIDs = tempMailList->getUIDsOfOldMails(); //iterate over all members of the list, //get the header from the old list and store it in the new one TQStringList::iterator it; for ( it = UIDs.begin(); it != UIDs.end(); ++it ) { TQString header = m_pshowrecord->getHeaderOf( *it ); tempMailList->setHeader( *it, header ); } //now we have the a complete new mail list swapMailLists(); } void ConfigElem::slotReceiveHeader( TDEIO::Job *, const TQByteArray & data ) { if( !data.isEmpty() ) { //we get the next part of the mail //append it uint lastSize = receivedHeader.size(); receivedHeader.resize( lastSize + data.size() ); for( uint i = 0; i < data.size(); i++ ) receivedHeader[ lastSize + i ] = data[ i ]; } } int ConfigElem::getNumberNewMails( ) { return m_pshowrecord->getNumberNewMails(); } int ConfigElem::getNumberMails( ) { return m_pshowrecord->getNumberMails(); } long ConfigElem::getTotalSize( ) { return m_pshowrecord->getTotalSize(); } void ConfigElem::fillMailListView( KshowmailView* view ) { m_pshowrecord->fillMailListView( view, m_strAccount ); } void ConfigElem::refreshAccountListItem( ) { if( m_pViewItem != NULL ) { if( isActive() ) { m_pViewItem->setText( 4, TQString( "%1" ).arg( getNumberMails(), 3 ) ); m_pViewItem->setText( 5, TQString( "%1" ).arg( getTotalSize(), 8 ) ); } else { m_pViewItem->setText( 4, TQString( "???" ) ); m_pViewItem->setText( 5, TQString( "???" ) ); } } } void ConfigElem::killPOP3Job( ) { //just try to kill, if it is not idle if( state != AccountIdle ) { //kill a running job if( pop3Job != NULL ) pop3Job->kill( true ); //stop timeout timer pop3Timer->stop(); //call the appropriate finalize method switch( state ) { case AccountDeleting : slotFinalizeDeletion( NULL ); break; case AccountDownloading : slotFinalizeShowMail( NULL ); break; case AccountRefreshing : cancelRefresh(); break; default : break; } } } int ConfigElem::showSelectedHeaders( ) { //return, if no mails are selected if( !hasSelectedMails() ) return ConfigElem::continueShowHeaders; //order the mail list to show the headers of the selected mails int ret = m_pshowrecord->showSelectedHeaders( m_strAccount ); return ret == ShowRecord::continueShowHeaders ? ConfigElem::continueShowHeaders : ConfigElem::cancelShowHeaders; } void ConfigElem::printSetup( ) const { kdDebug() << "Setup of " << m_strAccount << ":" << endl; kdDebug() << "Host: " << m_url.host() << endl; kdDebug() << "Protocol: " << m_url.protocol() << endl; kdDebug() << "Port: " << m_url.port() << endl; kdDebug() << "User: " << m_url.user() << endl; kdDebug() << "Password: " << m_url.pass() << endl; switch( PasswordStorage ) { case CONFIG_VALUE_ACCOUNT_PASSWORD_DONT_SAVE : kdDebug() << "Password Storage: don't save" << endl; break; case CONFIG_VALUE_ACCOUNT_PASSWORD_SAVE_FILE : kdDebug() << "Password Storage: save in file" << endl; break; case CONFIG_VALUE_ACCOUNT_PASSWORD_SAVE_KWALLET : kdDebug() << "Password Storage: use TDEWallet" << endl; break; default : kdDebug() << "Password Storage: invalid value" << endl; } kdDebug() << "active: " << m_bActive << endl << endl; } void ConfigElem::setPasswordStorage( int storage ) { if( storage == CONFIG_VALUE_ACCOUNT_PASSWORD_DONT_SAVE || storage == CONFIG_VALUE_ACCOUNT_PASSWORD_SAVE_FILE || storage == CONFIG_VALUE_ACCOUNT_PASSWORD_SAVE_KWALLET ) PasswordStorage = storage; else PasswordStorage = DEFAULT_ACCOUNT_PASSWORD_STORAGE; } int ConfigElem::getPasswordStorage( ) const { return PasswordStorage; } TQString ConfigElem::getProtocol( bool upperCase ) const { if( upperCase ) return m_url.protocol().upper(); else return m_url.protocol(); } unsigned short int ConfigElem::getPort( ) const { return m_url.port(); } void ConfigElem::setTLS( bool tls ) { useTLS = tls; } bool ConfigElem::getTLS( ) const { return useTLS; } void ConfigElem::reloadFilterSettings( ) { headerFilter.load(); } void ConfigElem::applyFilters( ) { //are we executed by the MOVE routines? if( !downloadActionsInvoked ) { //this is the first call (at the current refresh cycle) of this methode //we get the lists of mails to delete an move and call the MOVE routines if necessary //OK, the filters were applied filterApplied = true; //order the mail list to apply the header filters //it returns lists of mail numbers which shall be deleted or moved //the marking will be done by the mail list itself //the mail list removes all mails which shall be ignored itself MailsToDelete.clear(); m_pshowrecord->applyHeaderFilter( &headerFilter, getAccountName(), MailsToDelete, MailsToDownload, nmbIgnoredMails, FLog ); nmbDeletedMailsLastRefresh += MailsToDelete.count(); nmbDeletedMailsLastStart += MailsToDelete.count(); //This part will be executed, if mails shall be downloaded if( !MailsToDownload.empty() ) { downloadActionsInvoked = true; doDownloadActions(); //we quit this methode at this point, because after the bodies are downloaded and written this methode will recalled. //At this time the else branch of this IF-statement will be executed and the methode continues return; } } else { //this is the second call (at the current refresh cycle) of this methode. //it is called by the Move routines. //the downloading of the mailbodies and writing it to the mailboxes has ended. //A second call was just exceuted, if there was mails to move downloadActionsInvoked = false; //after an move error there are maybe some mails leftover in MailsToMove MailsToDownload.clear(); } //we have get the list of mails to delete and the all mails to move are written to its mailboxes //now we delete this mails (the moved mails too) if( !MailsToDelete.empty() ) { //there are mails to delete //we delete they //after the delete cycle has done its job, it will call applyFiltersDeleted() deletionPerformedByFilters = true; //this is set to indicate the deletion is performed by filters and not by user //the deletion methodes need it to decide on branch targets deleteNextMail(); } else { //if we need not to start a second refresh cycle (no mails was deleted or moved) //we just commit the refresh and let the filter applied flag to false for the next regular refresh commitRefresh(); filterApplied = false; } } void ConfigElem::swapMailLists( ) { //delete old mail list delete m_pshowrecord; //assign the new list if( tempMailList != NULL ) m_pshowrecord = tempMailList; else m_pshowrecord = new ShowRecord(); //if the filters were not applied yet, we do it now //applyFilters() will either start a second refresh cycle if it did some deletions //or call commitRefresh() to commit the refresh cycle. //if the filters were already applied we commit the refresh. if( filterApplied | !headerFilter.isActive() ) { commitRefresh(); filterApplied = false; return; } else { applyFilters(); return; } } void ConfigElem::applyFiltersDeleted( ) { //unset the flag deletionPerformedByFilters = false; //start the second refresh cycle refreshPerformedByFilters = true; //this sends a commit and restart the refresh commitBeforeRefresh(); return; //refreshMailList(); } bool ConfigElem::writeToMailBox( const TQString & mail, const TQString & box ) { TQDir mailDir( box ); //check whether the given path is a maildir if( !isMailDir( mailDir ) ) { //show an error message KMessageBox::error( NULL, i18n( TQString( "%1 is not a mailbox." ).arg( box ) ) ); return false; } //create unique file name according http://cr.yp.to/proto/maildir.html TQString partTime = TQString::number( time( NULL ) ); //left part, output of time() char hname[256]; //right part, the hostname TQString partHostname; if( gethostname( hname, 255 ) == 0 ) partHostname = TQString( hname ); else { //the hostname is not readable //show an error message and exit KMessageBox::error( NULL, i18n( TQString( "Can't read the hostname of your computer. But KShowmail need it to write a mail into the mailbox." ) ) ); return false; } TQString partPID = TQString::number( getpid() ); //middle part, the PID TQString partCounter = TQString::number( moveCounter++ ); TQString uniqueName( partTime + "." + partPID + partCounter + "." + partHostname ); //build absolute path mailDir.cd( "tmp" ); TQString absFile = mailDir.filePath( uniqueName ); //and writing! TQFile file( absFile ); if( file.open( IO_WriteOnly ) ) { TQTextStream stream( &file ); stream << mail << endl; file.close(); } else { KMessageBox::detailedError( NULL, i18n( TQString( "Could not file a mail to %1." ) ).arg( box ), i18n( file.errorString() ) ); return false; } //now we move it to the "new" subdirectory mailDir.cdUp(); mailDir.cd( "new" ); TQString absNewFile = mailDir.filePath( uniqueName ); if( rename( absFile.ascii(), absNewFile.ascii() ) == -1 ) { KMessageBox::error( NULL, i18n( TQString( "Could not move a mail from %1 to %2." ) ).arg( absFile ).arg( absNewFile ) ); return false; } //the writing was successful return true; } void ConfigElem::doDownloadActions() { //get first mail getNextMailForDownloadActions(); } bool ConfigElem::isMailDir( const TQDir & path ) { //get a list of all subdirectories in this directory const TQStringList entries = path.entryList( TQDir::Dirs | TQDir::Readable | TQDir::Writable | TQDir::Hidden, TQDir::Name | TQDir::IgnoreCase | TQDir::LocaleAware ); //a maildir folder must contains the folders "cur", "new" and "tmp" bool curFound = false; bool newFound = false; bool tmpFound = false; //iterate over all directories and look for the three necessary dirs TQStringList::const_iterator it = entries.begin(); while( it != entries.end() && !( curFound && newFound && tmpFound ) ) { if( *it == "tmp" ) tmpFound = true; else if( *it == "cur" ) curFound = true; else if( *it == "new" ) newFound = true; ++it; } return curFound && newFound && tmpFound; } void ConfigElem::getNextMailForDownloadActions() { //if the list of mails to move is empty return to applyFilters if( MailsToDownload.empty() ) { applyFilters(); return; } //clear the class variable mailbody, which contains the downloaded mail body mailbody.resize( 0 ); //start job startKIOJob( TQString( "/download/%1" ).arg( MailsToDownload.begin().key() ) ); connect( pop3Job, SIGNAL( data( TDEIO::Job*, const TQByteArray & ) ), SLOT( slotDataMailBody( TDEIO::Job*, const TQByteArray & ) ) ); connect( pop3Job, SIGNAL( result( TDEIO::Job* ) ), this, SLOT( slotMailDownloadedForAction( TDEIO::Job* ) ) ); } void ConfigElem::slotMailDownloadedForAction(TDEIO::Job * job) { //stop timeout timer pop3Timer->stop(); //check for errors //if an error has occured, the download will be canceled //or will ask for a new password if( job->error() == TDEIO::ERR_COULD_NOT_LOGIN ) { //login failed, ask for a new password job->showErrorDialog(); bool res = assertPassword( true ); if( res == false ) { //we have not got a new password; cancel delete applyFilters(); return; } //if we have got a new password, jump to the end of the if-statement } else if( job->error() != 0 ) { job->showErrorDialog(); applyFilters(); return; } else { //succesful download //do action MailToDownloadMap_Type::Iterator firstMail = MailsToDownload.begin(); int currentMailNumber = firstMail.key(); //get mail number TQString currentMailBox( firstMail.data().mailbox ); //get mailbox TQString mail( mailbody ); //convert mailtext FilterAction_Type action = firstMail.data().action; //get action bool resultMove = false; //TRUE - mail is written into the mailbox bool resultSpam = false; //TRUE - mail is Spam bool deleteIt = false; //TRUE - mail shall be deleted bool resultAction = false; //True - the action was succesful performed switch( action ) { case FActMove : resultMove = writeToMailBox( mail, currentMailBox ); //log entry is made by ShowRecordElem::applyHeaderFilter if( resultMove == true ) { nmbMovedMailsLastRefresh++; nmbMovedMailsLastStart++; resultAction = true; deleteIt = true; } else { resultAction = false; deleteIt = false; } break; case FActSpamcheck : resultSpam = isSpam( mailbody ); //it is spam? if( resultSpam == true ) //yes, it is spam! Arrgghh! Torture it!!! { switch( appConfig->getSpamAction() ) { case FActMove : resultMove = writeToMailBox( mail, appConfig->getSpamMailbox() ); if( resultMove == true ) { nmbMovedMailsLastRefresh++; nmbMovedMailsLastStart++; if( FLog != NULL ) m_pshowrecord->writeToMoveLog( FLog, currentMailNumber, getAccountName(), appConfig->getSpamMailbox() ); resultAction = true; deleteIt = true; } else { resultAction = false; deleteIt = false; } break; case FActMark : m_pshowrecord->setMarkAtNextViewRefresh( currentMailNumber ); resultAction = true; deleteIt = false; break; case FActDelete : if( FLog != NULL ) m_pshowrecord->writeToDeleteLog( FLog, currentMailNumber, getAccountName() ); nmbDeletedMailsLastRefresh++; nmbDeletedMailsLastStart++; resultAction = true; deleteIt = true; break; default : kdError() << "invalid action for spam mail" << endl; resultAction = false; deleteIt = false; break; } } else //mail is not spam { resultAction = true; deleteIt = false; } break; default : deleteIt = false; resultAction = false; } if( resultAction == true ) { //Action was successful //remove this mail from the list MailsToDownload.remove( firstMail ); //maybe add this mail to list of mails to delete if( deleteIt ) MailsToDelete.append( currentMailNumber ); } else { //Action was not successful //returns to applyFilters() to continue the filtering applyFilters(); return; } //if the list of mails is empty, return to applyFilters() to continue the filtering if( MailsToDownload.empty() ) { applyFilters(); return; } } //show next mail in list getNextMailForDownloadActions(); } bool ConfigElem::isSpam( TQByteArray mail ) const { //check for a running spamassassin if( !isSpamAssassinRunning() ) { KMessageBox::information( NULL, i18n( "You want to check your mails for spam, but SpamAssassin is not running.\nKShowmail skips the spam check." ), i18n( "SpamAssassin is not running" ), "ConfigElemNoSpamAssassinRunning" ); return false; } //append an \0 at the end of the string int size = mail.size(); if( mail[ size - 1 ] != '\0' ) { mail.resize( size + 1 ); mail[ size ] = '\0'; } //calls spmac and get an file pointer to stdin of it FILE *write_fp; write_fp = popen( "spamc -E", "w" ); //forward the mail to SpamAssassin if( write_fp != NULL ) { fwrite( mail.data(), sizeof( char), mail.size(), write_fp ); //check exit code of spamc and return result int excode = pclose( write_fp ); if( excode == 0 ) return false; else return true; } else { kdError() << "Could not call the command spamc of SpamAssassin." << endl; return false; } return false; } bool ConfigElem::isSpamAssassinRunning( ) const { FILE *read_fp; char buffer[ BUFSIZ + 1 ]; int chars_read; bool found = false; memset( buffer, '\0', sizeof( buffer ) ); read_fp = popen( "ps -eo comm", "r" ); if( read_fp != NULL ) { chars_read = fread( buffer, sizeof( char ), BUFSIZ, read_fp ); while( chars_read > 0 ) { buffer[ chars_read - 1 ] = '\0'; TQString output( buffer ); found = output.contains( NAME_SPAMASSASSIN_DAEMON ) > 0; chars_read = fread( buffer, sizeof( char ), BUFSIZ, read_fp ); } pclose( read_fp ); } return found; } int ConfigElem::numberDeletedMailsLastRefresh( ) { return nmbDeletedMailsLastRefresh; } int ConfigElem::numberDeletedMailsStart( ) { return nmbDeletedMailsLastStart; } int ConfigElem::numberMovedMailsLastRefresh( ) { return nmbMovedMailsLastRefresh; } int ConfigElem::numberMovedMailsStart( ) { return nmbMovedMailsLastStart; } int ConfigElem::numberIgnoredMails( ) { return nmbIgnoredMails; } TQStringList ConfigElem::getSelectedSenders( ) const { return m_pshowrecord->getSelectedSenders(); } void ConfigElem::commitBeforeRefresh() { //start job to commit startKIOJob( TQString( "/commit" ) ); connect( pop3Job, SIGNAL( result( TDEIO::Job* ) ), this, SLOT( slotCommitBeforeRefreshDone( TDEIO::Job* ) ) ); } void ConfigElem::slotCommitBeforeRefreshDone(TDEIO::Job *) { //after a commit was send, we start a new refresh cyle refreshMailList(); }