/* 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 #include #include #include #include #include #include #include #include #include #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(TQObject *parent, const char *name ) : TQObject(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 TQSocketNotifier(nntpInPipe[0], TQSocketNotifier::Read); connect(nntpNotifier, TQT_SIGNAL(activated(int)), this, TQT_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(), TQT_SIGNAL(passwordsChanged()), TQT_SLOT(slotPasswordsChanged()) ); } KNNetAccess::~KNNetAccess() { disconnect(nntpNotifier, TQT_SIGNAL(activated(int)), this, TQT_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(), TQT_SIGNAL(progressItemCanceled(KPIM::ProgressItem*)), TQT_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 ) { TQValueList::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; TQValueList::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; TQValueList::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( currentSmtpJob->data() ); // create url query part TQString query("headers=0&from="); query += KURL::encode_string( art->from()->email() ); TQStrList 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, TQT_SIGNAL( result(KIO::Job*) ), TQT_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) { TQString user = info->user(); TQString 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(TQString()); kdDebug(5003) << "KNNetAccess::threadDoneNntp(): trying again with authentication data" << endl; // restart job... triggerAsyncThread(nntpOutPipe[1]); return; } } } nntpClient->removeJob(); currentNntpJob = 0L; currMsg = TQString(); 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 = TQString(); 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; TQString 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() { TQValueList::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; TQValueList::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"