00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "job.h"
00023 #include "job_p.h"
00024 #include <QTime>
00025 #include "imapparser_p.h"
00026 #include "session.h"
00027 #include "session_p.h"
00028
00029 #include <kdebug.h>
00030 #include <klocale.h>
00031
00032 #include <QtCore/QEventLoop>
00033 #include <QtCore/QTimer>
00034 #include <QtCore/QTextStream>
00035 #include <QtNetwork/QHostAddress>
00036 #include <QtNetwork/QTcpSocket>
00037 #include <QtDBus/QDBusInterface>
00038 #include <QtDBus/QDBusConnectionInterface>
00039
00040 using namespace Akonadi;
00041
00042 static QDBusAbstractInterface *s_jobtracker = 0;
00043
00044
00045 void JobPrivate::handleResponse( const QByteArray & tag, const QByteArray & data )
00046 {
00047 Q_Q( Job );
00048
00049 if ( mCurrentSubJob ) {
00050 mCurrentSubJob->d_ptr->handleResponse( tag, data );
00051 return;
00052 }
00053
00054 if ( tag == mTag ) {
00055 if ( data.startsWith( "NO " ) || data.startsWith( "BAD " ) ) {
00056 QString msg = QString::fromUtf8( data );
00057
00058 msg.remove( 0, msg.startsWith( QLatin1String( "NO " ) ) ? 3 : 4 );
00059
00060 if ( msg.endsWith( QLatin1String( "\r\n" ) ) )
00061 msg.chop( 2 );
00062
00063 q->setError( Job::Unknown );
00064 q->setErrorText( msg );
00065 q->emitResult();
00066 return;
00067 } else if ( data.startsWith( "OK" ) ) {
00068
00069
00070
00071
00072
00073
00074 QTimer::singleShot( 0, q, SLOT( delayedEmitResult() ) );
00075 return;
00076 }
00077 }
00078
00079 q->doHandleResponse( tag, data );
00080 }
00081
00082 void JobPrivate::init( QObject *parent )
00083 {
00084 Q_Q( Job );
00085
00086 mParentJob = dynamic_cast<Job*>( parent );
00087 mSession = dynamic_cast<Session*>( parent );
00088
00089 if ( !mSession ) {
00090 if ( !mParentJob )
00091 mSession = Session::defaultSession();
00092 else
00093 mSession = mParentJob->d_ptr->mSession;
00094 }
00095
00096 if ( !mParentJob )
00097 mSession->d->addJob( q );
00098 else
00099 mParentJob->addSubjob( q );
00100
00101
00102 if ( !s_jobtracker ) {
00103
00104
00105 static QTime s_lastTime;
00106 if ( s_lastTime.isNull() )
00107 s_lastTime.start();
00108 if ( s_lastTime.elapsed() > 3000 ) {
00109 if ( QDBusConnection::sessionBus().interface()->isServiceRegistered(QLatin1String( "org.kde.akonadiconsole" ) ) ) {
00110 s_jobtracker = new QDBusInterface( QLatin1String( "org.kde.akonadiconsole" ),
00111 QLatin1String( "/jobtracker" ),
00112 QLatin1String( "org.freedesktop.Akonadi.JobTracker" ),
00113 QDBusConnection::sessionBus(), 0 );
00114 } else {
00115 s_lastTime.restart();
00116 }
00117 }
00118
00119
00120 }
00121 QMetaObject::invokeMethod( q, "signalCreationToJobTracker", Qt::QueuedConnection );
00122 }
00123
00124 void JobPrivate::signalCreationToJobTracker()
00125 {
00126 Q_Q( Job );
00127 if ( s_jobtracker ) {
00128
00129
00130
00131 QList<QVariant> argumentList;
00132 argumentList << QLatin1String( mSession->sessionId() )
00133 << QString::number(reinterpret_cast<unsigned long>( q ), 16)
00134 << ( mParentJob ? QString::number( reinterpret_cast<unsigned long>( mParentJob ), 16) : QString() )
00135 << QString::fromLatin1( q->metaObject()->className() );
00136 s_jobtracker->callWithArgumentList(QDBus::NoBlock, QLatin1String( "jobCreated" ), argumentList);
00137 }
00138 }
00139
00140 void JobPrivate::delayedEmitResult()
00141 {
00142 Q_Q( Job );
00143 q->emitResult();
00144 }
00145
00146 void JobPrivate::startQueued()
00147 {
00148 Q_Q( Job );
00149 mStarted = true;
00150
00151 emit q->aboutToStart( q );
00152 q->doStart();
00153 QTimer::singleShot( 0, q, SLOT( startNext() ) );
00154
00155
00156 if ( s_jobtracker ) {
00157 QList<QVariant> argumentList;
00158 argumentList << QString::number(reinterpret_cast<unsigned long>( q ), 16);
00159 s_jobtracker->callWithArgumentList(QDBus::NoBlock, QLatin1String( "jobStarted" ), argumentList);
00160 }
00161 }
00162
00163 void JobPrivate::lostConnection()
00164 {
00165 Q_Q( Job );
00166
00167 if ( mCurrentSubJob ) {
00168 mCurrentSubJob->d_ptr->lostConnection();
00169 } else {
00170 q->setError( Job::ConnectionFailed );
00171 q->kill( KJob::EmitResult );
00172 }
00173 }
00174
00175 void JobPrivate::slotSubJobAboutToStart( Job * job )
00176 {
00177 Q_ASSERT( mCurrentSubJob == 0 );
00178 mCurrentSubJob = job;
00179 }
00180
00181 void JobPrivate::startNext()
00182 {
00183 Q_Q( Job );
00184
00185 if ( mStarted && !mCurrentSubJob && q->hasSubjobs() ) {
00186 Job *job = dynamic_cast<Akonadi::Job*>( q->subjobs().first() );
00187 Q_ASSERT( job );
00188 job->d_ptr->startQueued();
00189 }
00190 }
00191
00192 QByteArray JobPrivate::newTag( )
00193 {
00194 if ( mParentJob )
00195 mTag = mParentJob->d_ptr->newTag();
00196 else
00197 mTag = QByteArray::number( mSession->d->nextTag() );
00198 return mTag;
00199 }
00200
00201 QByteArray JobPrivate::tag() const
00202 {
00203 return mTag;
00204 }
00205
00206 void JobPrivate::writeData( const QByteArray & data )
00207 {
00208 Q_ASSERT_X( !mWriteFinished, "Job::writeData()", "Calling writeData() after emitting writeFinished()" );
00209 mSession->d->writeData( data );
00210 }
00211
00212
00213
00214 Job::Job( QObject *parent )
00215 : KCompositeJob( parent ),
00216 d_ptr( new JobPrivate( this ) )
00217 {
00218 d_ptr->init( parent );
00219 }
00220
00221 Job::Job( JobPrivate *dd, QObject *parent )
00222 : KCompositeJob( parent ),
00223 d_ptr( dd )
00224 {
00225 d_ptr->init( parent );
00226 }
00227
00228 Job::~Job()
00229 {
00230 delete d_ptr;
00231
00232
00233 if ( s_jobtracker ) {
00234 QList<QVariant> argumentList;
00235 argumentList << QString::number(reinterpret_cast<unsigned long>( this ), 16)
00236 << errorString();
00237 s_jobtracker->callWithArgumentList(QDBus::NoBlock, QLatin1String( "jobEnded" ), argumentList);
00238 }
00239 }
00240
00241 void Job::start()
00242 {
00243 }
00244
00245 bool Job::doKill()
00246 {
00247 Q_D( Job );
00248 d->mStarted = false;
00249 return true;
00250 }
00251
00252 QString Job::errorString() const
00253 {
00254 QString str;
00255 switch ( error() ) {
00256 case NoError:
00257 break;
00258 case ConnectionFailed:
00259 str = i18n( "Cannot connect to the Akonadi service." );
00260 break;
00261 case ProtocolVersionMismatch:
00262 str = i18n( "The protocol version of the Akonadi server is incompatible. Make sure you have a compatible version installed." );
00263 break;
00264 case UserCanceled:
00265 str = i18n( "User canceled operation." );
00266 break;
00267 case Unknown:
00268 default:
00269 str = i18n( "Unknown error." );
00270 break;
00271 }
00272 if ( !errorText().isEmpty() ) {
00273 str += QString::fromLatin1( " (%1)" ).arg( errorText() );
00274 }
00275 return str;
00276 }
00277
00278 bool Job::addSubjob( KJob * job )
00279 {
00280 bool rv = KCompositeJob::addSubjob( job );
00281 if ( rv ) {
00282 connect( job, SIGNAL( aboutToStart( Akonadi::Job* ) ), SLOT( slotSubJobAboutToStart( Akonadi::Job* ) ) );
00283 QTimer::singleShot( 0, this, SLOT( startNext() ) );
00284 }
00285 return rv;
00286 }
00287
00288 bool Job::removeSubjob(KJob * job)
00289 {
00290 bool rv = KCompositeJob::removeSubjob( job );
00291 if ( job == d_ptr->mCurrentSubJob ) {
00292 d_ptr->mCurrentSubJob = 0;
00293 QTimer::singleShot( 0, this, SLOT( startNext() ) );
00294 }
00295 return rv;
00296 }
00297
00298 void Job::doHandleResponse(const QByteArray & tag, const QByteArray & data)
00299 {
00300 kDebug() << "Unhandled response: " << tag << data;
00301 }
00302
00303 void Job::slotResult(KJob * job)
00304 {
00305 if ( d_ptr->mCurrentSubJob == job ) {
00306
00307 d_ptr->mCurrentSubJob = 0;
00308 KCompositeJob::slotResult( job );
00309 if ( !job->error() )
00310 QTimer::singleShot( 0, this, SLOT( startNext() ) );
00311 } else {
00312
00313
00314
00315 KCompositeJob::removeSubjob( job );
00316 }
00317 }
00318
00319 void Job::emitWriteFinished()
00320 {
00321 d_ptr->mWriteFinished = true;
00322 emit writeFinished( this );
00323 }
00324
00325 #include "job.moc"