diff options
Diffstat (limited to 'knode/knnetaccess.cpp')
-rw-r--r-- | knode/knnetaccess.cpp | 545 |
1 files changed, 545 insertions, 0 deletions
diff --git a/knode/knnetaccess.cpp b/knode/knnetaccess.cpp new file mode 100644 index 000000000..65b43e7a5 --- /dev/null +++ b/knode/knnetaccess.cpp @@ -0,0 +1,545 @@ +/* + KNode, the KDE newsreader + Copyright (c) 1999-2005 the KNode authors. + See file AUTHORS for details + + 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. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US +*/ + +#include <unistd.h> +#include <fcntl.h> + +#include <qsocketnotifier.h> + +#include <klocale.h> +#include <kmessagebox.h> +#include <kdebug.h> +#include <kio/job.h> +#include <kio/passdlg.h> +#include <ksocks.h> +#include <kapplication.h> + +#include "knaccountmanager.h" +#include "knarticle.h" +#include "knmainwidget.h" +#include "knjobdata.h" +#include "knnntpclient.h" +#include "knglobals.h" +#include "knnetaccess.h" +#include "knwidgets.h" + +using KPIM::ProgressManager; + + +KNNetAccess::KNNetAccess(QObject *parent, const char *name ) + : QObject(parent,name), currentNntpJob(0), currentSmtpJob(0) +{ + if ( pipe(nntpInPipe) == -1 || pipe(nntpOutPipe) == -1 ) { + KMessageBox::error(knGlobals.topWidget, i18n("Internal error:\nFailed to open pipes for internal communication.")); + kapp->exit(1); + } + if ( fcntl( nntpInPipe[0], F_SETFL, O_NONBLOCK ) == -1 || + fcntl( nntpOutPipe[0], F_SETFL, O_NONBLOCK ) == -1 ) { + KMessageBox::error(knGlobals.topWidget, i18n("Internal error:\nFailed to open pipes for internal communication.")); + kapp->exit(1); + } + + nntpNotifier=new QSocketNotifier(nntpInPipe[0], QSocketNotifier::Read); + connect(nntpNotifier, SIGNAL(activated(int)), this, SLOT(slotThreadSignal(int))); + + // initialize the KSocks stuff in the main thread, otherwise we get + // strange effects on FreeBSD + (void) KSocks::self(); + + nntpClient=new KNNntpClient(nntpOutPipe[0],nntpInPipe[1],nntp_Mutex); + nntpClient->start(); + + connect( knGlobals.accountManager(), SIGNAL(passwordsChanged()), SLOT(slotPasswordsChanged()) ); +} + + + +KNNetAccess::~KNNetAccess() +{ + disconnect(nntpNotifier, SIGNAL(activated(int)), this, SLOT(slotThreadSignal(int))); + + nntpClient->terminateClient(); + triggerAsyncThread(nntpOutPipe[1]); + nntpClient->wait(); + + delete nntpClient; + delete nntpNotifier; + + if ( ::close(nntpInPipe[0]) == -1 || + ::close(nntpInPipe[1]) == -1 || + ::close(nntpOutPipe[0]) == -1 || + ::close(nntpOutPipe[1]) == -1 ) + kdDebug(5003) << "Can't close pipes" << endl; +} + + + +void KNNetAccess::addJob(KNJobData *job) +{ + // kdDebug(5003) << "KNNetAccess::addJob() : job queued" << endl; + if(job->account()==0) { + job->setErrorString(i18n("Internal Error: No account set for this job.")); + job->notifyConsumer(); + return; + } + + job->createProgressItem(); + connect( job->progressItem(), SIGNAL(progressItemCanceled(KPIM::ProgressItem*)), SLOT(slotCancelJob(KPIM::ProgressItem*)) ); + emit netActive( true ); + + // put jobs which are waiting for the wallet into an extra queue + if ( !job->account()->readyForLogin() ) { + mWalletQueue.append( job ); + knGlobals.accountManager()->loadPasswordsAsync(); + job->setStatus( i18n( "Waiting for KWallet..." ) ); + return; + } + + if (job->type()==KNJobData::JTmail) { + smtpJobQueue.append(job); + if (!currentSmtpJob) // no active job, start the new one + startJobSmtp(); + } else { + + /* + TODO: the following code doesn't really belong here, it should + be moved to KNGroupManager, or elsewere... + */ + + // avoid duplicate fetchNewHeader jobs... + bool duplicate = false; + if ( job->type() == KNJobData::JTfetchNewHeaders || job->type() == KNJobData::JTsilentFetchNewHeaders ) { + QValueList<KNJobData*>::ConstIterator it; + for ( it = nntpJobQueue.begin(); it != nntpJobQueue.end(); ++it ) { + if ( ( (*it)->type() == KNJobData::JTfetchNewHeaders || (*it)->type() == KNJobData::JTsilentFetchNewHeaders ) + && (*it)->data() == job->data() ) // job works on the same group... + duplicate = true; + } + } + + if (!duplicate) { + // give a lower priority to fetchNewHeaders and postArticle jobs + if ( job->type() == KNJobData::JTfetchNewHeaders + || job->type() == KNJobData::JTsilentFetchNewHeaders + || job->type() == KNJobData::JTpostArticle ) { + nntpJobQueue.append( job ); + } else { + nntpJobQueue.prepend( job ); + } + + if (!currentNntpJob) // no active job, start the new one + startJobNntp(); + } + } + updateStatus(); +} + + +void KNNetAccess::cancelCurrentNntpJob( int type ) +{ + if ((currentNntpJob && !currentNntpJob->canceled()) && ((type==0)||(currentNntpJob->type()==type))) { // stop active job + currentNntpJob->cancel(); + triggerAsyncThread(nntpOutPipe[1]); + } +} + + +void KNNetAccess::stopJobsNntp( int type ) +{ + cancelCurrentNntpJob( type ); + KNJobData *tmp = 0; + QValueList<KNJobData*>::Iterator it; + for ( it = nntpJobQueue.begin(); it != nntpJobQueue.end();) { + tmp = *it; + if ( type == 0 || tmp->type() == type ) { + it = nntpJobQueue.remove( it ); + tmp->cancel(); + tmp->notifyConsumer(); + } else + ++it; + } + for ( it = mWalletQueue.begin(); it != mWalletQueue.end();) { + tmp = *it; + if ( type == 0 || tmp->type() == type ) { + it = mWalletQueue.remove( it ); + tmp->cancel(); + tmp->notifyConsumer(); + } else + ++it; + } + updateStatus(); +} + + + +// type==0 => all jobs +void KNNetAccess::cancelCurrentSmtpJob( int type ) +{ + if ((currentSmtpJob && !currentSmtpJob->canceled()) && ((type==0)||(currentSmtpJob->type()==type))) { // stop active job + currentSmtpJob->cancel(); + threadDoneSmtp(); + } +} + + +void KNNetAccess::stopJobsSmtp( int type ) +{ + cancelCurrentSmtpJob( type ); + KNJobData *tmp = 0; + QValueList<KNJobData*>::Iterator it; + for ( it = smtpJobQueue.begin(); it != smtpJobQueue.end();) { + tmp = *it; + if ( type == 0 || tmp->type() == type ) { + it = smtpJobQueue.remove( it ); + tmp->cancel(); + tmp->notifyConsumer(); + } else + ++it; + } + updateStatus(); +} + + + +// passes a signal through the ipc-pipe to the net-thread +void KNNetAccess::triggerAsyncThread(int pipeFd) +{ + int signal=0; + + // kdDebug(5003) << "KNNetAccess::triggerAsyncThread() : sending signal to net thread" << endl; + write(pipeFd, &signal, sizeof(int)); +} + + + +void KNNetAccess::startJobNntp() +{ + if ( nntpJobQueue.isEmpty() ) + return; + + currentNntpJob = nntpJobQueue.first(); + nntpJobQueue.remove( nntpJobQueue.begin() ); + currentNntpJob->prepareForExecution(); + if (currentNntpJob->success()) { + nntpClient->insertJob(currentNntpJob); + triggerAsyncThread(nntpOutPipe[1]); + kdDebug(5003) << "KNNetAccess::startJobNntp(): job started" << endl; + } else { + threadDoneNntp(); + } +} + + + +void KNNetAccess::startJobSmtp() +{ + if ( smtpJobQueue.isEmpty() ) + return; + + currentSmtpJob = smtpJobQueue.first(); + smtpJobQueue.remove( smtpJobQueue.begin() ); + currentSmtpJob->prepareForExecution(); + if (currentSmtpJob->success()) { + KNLocalArticle *art = static_cast<KNLocalArticle*>( currentSmtpJob->data() ); + // create url query part + QString query("headers=0&from="); + query += KURL::encode_string( art->from()->email() ); + QStrList emails; + art->to()->emails( &emails ); + for ( char *e = emails.first(); e; e = emails.next() ) { + query += "&to=" + KURL::encode_string( e ); + } + // create url + KURL destination; + KNServerInfo *account = currentSmtpJob->account(); + if ( account->encryption() == KNServerInfo::SSL ) + destination.setProtocol( "smtps" ); + else + destination.setProtocol( "smtp" ); + destination.setHost( account->server() ); + destination.setPort( account->port() ); + destination.setQuery( query ); + if ( account->needsLogon() ) { + destination.setUser( account->user() ); + destination.setPass( account->pass() ); + } + KIO::Job* job = KIO::storedPut( art->encodedContent(true), destination, -1, false, false, false ); + connect( job, SIGNAL( result(KIO::Job*) ), + SLOT( slotJobResult(KIO::Job*) ) ); + if ( account->encryption() == KNServerInfo::TLS ) + job->addMetaData( "tls", "on" ); + else + job->addMetaData( "tls", "off" ); + currentSmtpJob->setJob( job ); + + kdDebug(5003) << "KNNetAccess::startJobSmtp(): job started" << endl; + } else { + threadDoneSmtp(); + } +} + + + +void KNNetAccess::threadDoneNntp() +{ + KNJobData *tmp; + if (!currentNntpJob) { + kdWarning(5003) << "KNNetAccess::threadDoneNntp(): no current job?? aborting" << endl; + return; + } + + kdDebug(5003) << "KNNetAccess::threadDoneNntp(): job done" << endl; + + tmp = currentNntpJob; + + if (!tmp->success() && tmp->authError()) { + kdDebug(5003) << "KNNetAccess::threadDoneNntp(): authentication error" << endl; + KNServerInfo *info = tmp->account(); + if (info) { + QString user = info->user(); + QString pass = info->pass(); + bool keep=false; + if (KDialog::Accepted == KIO::PasswordDialog::getNameAndPassword(user, pass, &keep, + i18n("You need to supply a username and a\npassword to access this server"), false, + kapp->makeStdCaption(i18n("Authentication Failed")),info->server(),i18n("Server:"))) { + info->setNeedsLogon(true); + info->setUser(user); + info->setPass(pass); + tmp->setAuthError(false); + tmp->setErrorString(QString::null); + + kdDebug(5003) << "KNNetAccess::threadDoneNntp(): trying again with authentication data" << endl; + + // restart job... + triggerAsyncThread(nntpOutPipe[1]); + return; + } + } + } + + nntpClient->removeJob(); + currentNntpJob = 0L; + + currMsg = QString::null; + knGlobals.setStatusMsg(); + tmp->setComplete(); + + tmp->notifyConsumer(); + + if (!nntpJobQueue.isEmpty()) + startJobNntp(); + + updateStatus(); +} + + + +void KNNetAccess::threadDoneSmtp() +{ + KNJobData *tmp; + if (!currentSmtpJob) { + kdWarning(5003) << "KNNetAccess::threadDoneSmtp(): no current job?? aborting" << endl; + return; + } + + kdDebug(5003) << "KNNetAccess::threadDoneSmtp(): job done" << endl; + + tmp = currentSmtpJob; + currentSmtpJob = 0L; + if (!currentNntpJob) { + currMsg = QString::null; + knGlobals.setStatusMsg(); + } + tmp->setComplete(); + + tmp->notifyConsumer(); + + if (!smtpJobQueue.isEmpty()) + startJobSmtp(); + + updateStatus(); +} + + +void KNNetAccess::cancelAllJobs() +{ + stopJobsNntp(0); + stopJobsSmtp(0); +} + + + +void KNNetAccess::slotThreadSignal(int i) +{ + int signal; + QString tmp; + + //kdDebug(5003) << "KNNetAccess::slotThreadSignal() : signal received from net thread" << endl; + if(read(i, &signal, sizeof(int))==-1) { + kdDebug(5003) << "KNNetAccess::slotThreadSignal() : cannot read from pipe" << endl; + return; + } + + if (i == nntpInPipe[0]) { // signal from nntp thread + switch(signal) { + case KNProtocolClient::TSworkDone: + threadDoneNntp(); + break; + case KNProtocolClient::TSconnect: + currMsg = i18n(" Connecting to server..."); + knGlobals.setStatusMsg(currMsg); + currentNntpJob->setStatus(currMsg); + break; + case KNProtocolClient::TSloadGrouplist: + currMsg = i18n(" Loading group list from disk..."); + knGlobals.setStatusMsg(currMsg); + currentNntpJob->setStatus(currMsg); + break; + case KNProtocolClient::TSwriteGrouplist: + currMsg = i18n(" Writing group list to disk..."); + knGlobals.setStatusMsg(currMsg); + currentNntpJob->setStatus(currMsg); + break; + case KNProtocolClient::TSdownloadGrouplist: + currMsg = i18n(" Downloading group list..."); + knGlobals.setStatusMsg(currMsg); + currentNntpJob->setStatus(currMsg); + break; + case KNProtocolClient::TSdownloadNewGroups: + currMsg = i18n(" Looking for new groups..."); + knGlobals.setStatusMsg(currMsg); + currentNntpJob->setStatus(currMsg); + break; + case KNProtocolClient::TSdownloadDesc: + currMsg = i18n(" Downloading group descriptions..."); + knGlobals.setStatusMsg(currMsg); + currentNntpJob->setStatus(currMsg); + break; + case KNProtocolClient::TSdownloadNew: + currMsg = i18n(" Downloading new headers..."); + knGlobals.setStatusMsg(currMsg); + currentNntpJob->setStatus(currMsg); + break; + case KNProtocolClient::TSsortNew: + currMsg = i18n(" Sorting..."); + knGlobals.setStatusMsg(currMsg); + currentNntpJob->setStatus(currMsg); + break; + case KNProtocolClient::TSdownloadArticle: + currMsg = i18n(" Downloading article..."); + knGlobals.setStatusMsg(currMsg); + currentNntpJob->setStatus(currMsg); + break; + case KNProtocolClient::TSsendArticle: + currMsg = i18n(" Sending article..."); + knGlobals.setStatusMsg(currMsg); + currentNntpJob->setStatus(currMsg); + break; + case KNProtocolClient::TSjobStarted: + currentNntpJob->setProgress(0); + break; + case KNProtocolClient::TSprogressUpdate: + currentNntpJob->setProgress(nntpClient->getProgressValue()/10); + break; + }; + } +} + + +void KNNetAccess::slotJobResult( KIO::Job *job ) +{ + if ( job == currentSmtpJob->job() ) { + if ( job->error() ) + currentSmtpJob->setErrorString( job->errorString() ); + threadDoneSmtp(); + return; + } + if ( job == currentNntpJob->job() ) { + // TODO + return; + } + kdError(5003) << k_funcinfo << "unknown job" << endl; +} + + +void KNNetAccess::slotPasswordsChanged() +{ + QValueList<KNJobData*>::ConstIterator it; + for ( it = mWalletQueue.begin(); it != mWalletQueue.end(); ++it ) { + (*it)->setStatus( i18n("Waiting...") ); + if ( (*it)->type() == KNJobData::JTmail ) + smtpJobQueue.append( (*it) ); + else + nntpJobQueue.append( (*it) ); + } + mWalletQueue.clear(); + if ( !currentNntpJob ) + startJobNntp(); + if ( !currentSmtpJob ) + startJobSmtp(); +} + + +void KNNetAccess::slotCancelJob( KPIM::ProgressItem *item ) +{ + KNJobData *tmp = 0; + QValueList<KNJobData*>::Iterator it; + for ( it = nntpJobQueue.begin(); it != nntpJobQueue.end();) { + tmp = *it; + if ( tmp->progressItem() == item ) { + it = nntpJobQueue.remove( it ); + tmp->cancel(); + tmp->notifyConsumer(); + } else + ++it; + } + for ( it = smtpJobQueue.begin(); it != smtpJobQueue.end();) { + tmp = *it; + if ( tmp->progressItem() == item ) { + it = smtpJobQueue.remove( it ); + tmp->cancel(); + tmp->notifyConsumer(); + } else + ++it; + } + for ( it = mWalletQueue.begin(); it != mWalletQueue.end();) { + tmp = *it; + if ( tmp->progressItem() == item ) { + it = mWalletQueue.remove( it ); + tmp->cancel(); + tmp->notifyConsumer(); + } else + ++it; + } + + if ( currentNntpJob && currentNntpJob->progressItem() == item ) + cancelCurrentNntpJob(); + if ( currentSmtpJob && currentSmtpJob->progressItem() == item ) + cancelCurrentSmtpJob(); + + updateStatus(); +} + +void KNNetAccess::updateStatus( ) +{ + if ( nntpJobQueue.isEmpty() && smtpJobQueue.isEmpty() && !currentNntpJob + && !currentSmtpJob && mWalletQueue.isEmpty() ) + emit netActive( false ); + else + emit netActive( true ); +} + +//-------------------------------- + +#include "knnetaccess.moc" |