You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdepim/knode/knnetaccess.cpp

546 lines
15 KiB

/*
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"