選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
kshowmail/kshowmail/configelem.cpp

1759 行
47 KiB

/***************************************************************************
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<TDEIO::SimpleJob*>(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<TDEIO::SimpleJob*>(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<TDEIO::SimpleJob*>(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<TDEIO::SimpleJob*>(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<TDEIO::SimpleJob*>(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();
}