00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "collectionfetchjob.h"
00021
00022 #include "imapparser_p.h"
00023 #include "job_p.h"
00024 #include "protocol_p.h"
00025 #include "protocolhelper_p.h"
00026 #include "entity_p.h"
00027 #include "collectionfetchscope.h"
00028 #include "collectionutils_p.h"
00029
00030 #include <kdebug.h>
00031 #include <KLocale>
00032
00033 #include <QtCore/QHash>
00034 #include <QtCore/QStringList>
00035 #include <QtCore/QTimer>
00036
00037 using namespace Akonadi;
00038
00039 class Akonadi::CollectionFetchJobPrivate : public JobPrivate
00040 {
00041 public:
00042 CollectionFetchJobPrivate( CollectionFetchJob *parent )
00043 : JobPrivate( parent )
00044 {
00045 }
00046
00047 Q_DECLARE_PUBLIC( CollectionFetchJob )
00048
00049 CollectionFetchJob::Type mType;
00050 Collection mBase;
00051 Collection::List mBaseList;
00052 Collection::List mCollections;
00053 CollectionFetchScope mScope;
00054 Collection::List mPendingCollections;
00055 QTimer *mEmitTimer;
00056
00057 void timeout()
00058 {
00059 Q_Q( CollectionFetchJob );
00060
00061 mEmitTimer->stop();
00062 if ( !mPendingCollections.isEmpty() ) {
00063 if ( !q->error() )
00064 emit q->collectionsReceived( mPendingCollections );
00065 mPendingCollections.clear();
00066 }
00067 }
00068 };
00069
00070 CollectionFetchJob::CollectionFetchJob( const Collection &collection, Type type, QObject *parent )
00071 : Job( new CollectionFetchJobPrivate( this ), parent )
00072 {
00073 Q_D( CollectionFetchJob );
00074
00075 d->mBase = collection;
00076 d->mType = type;
00077
00078 d->mEmitTimer = new QTimer( this );
00079 d->mEmitTimer->setSingleShot( true );
00080 d->mEmitTimer->setInterval( 100 );
00081 connect( d->mEmitTimer, SIGNAL( timeout() ), this, SLOT( timeout() ) );
00082 connect( this, SIGNAL( result( KJob* ) ), this, SLOT( timeout() ) );
00083 }
00084
00085 CollectionFetchJob::CollectionFetchJob( const Collection::List & cols, QObject * parent )
00086 : Job( new CollectionFetchJobPrivate( this ), parent )
00087 {
00088 Q_D( CollectionFetchJob );
00089
00090 Q_ASSERT( !cols.isEmpty() );
00091 if ( cols.size() == 1 ) {
00092 d->mBase = cols.first();
00093 d->mType = CollectionFetchJob::Base;
00094 } else {
00095 d->mBaseList = cols;
00096 }
00097
00098 d->mEmitTimer = new QTimer( this );
00099 d->mEmitTimer->setSingleShot( true );
00100 d->mEmitTimer->setInterval( 100 );
00101 connect( d->mEmitTimer, SIGNAL( timeout() ), this, SLOT( timeout() ) );
00102 connect( this, SIGNAL( result( KJob* ) ), this, SLOT( timeout() ) );
00103 }
00104
00105 CollectionFetchJob::~CollectionFetchJob()
00106 {
00107 }
00108
00109 Akonadi::Collection::List CollectionFetchJob::collections() const
00110 {
00111 Q_D( const CollectionFetchJob );
00112
00113 return d->mCollections;
00114 }
00115
00116 void CollectionFetchJob::doStart()
00117 {
00118 Q_D( CollectionFetchJob );
00119
00120 if ( !d->mBaseList.isEmpty() ) {
00121 foreach ( const Collection &col, d->mBaseList ) {
00122 CollectionFetchJob *subJob = new CollectionFetchJob( col, CollectionFetchJob::Base, this );
00123 subJob->setFetchScope( fetchScope() );
00124 }
00125 return;
00126 }
00127
00128 if ( !d->mBase.isValid() && d->mBase.remoteId().isEmpty() ) {
00129 setError( Unknown );
00130 setErrorText( i18n( "Invalid collection given." ) );
00131 emitResult();
00132 return;
00133 }
00134
00135 QByteArray command = d->newTag();
00136 if ( !d->mBase.isValid() ) {
00137 if ( CollectionUtils::hasValidHierarchicalRID( d->mBase ) )
00138 command += " HRID";
00139 else
00140 command += " " AKONADI_CMD_RID;
00141 }
00142 if ( d->mScope.includeUnsubscribed() )
00143 command += " LIST ";
00144 else
00145 command += " LSUB ";
00146
00147 if ( d->mBase.isValid() )
00148 command += QByteArray::number( d->mBase.id() );
00149 else if ( CollectionUtils::hasValidHierarchicalRID( d->mBase ) )
00150 command += '(' + ProtocolHelper::hierarchicalRidToByteArray( d->mBase ) + ')';
00151 else
00152 command += ImapParser::quote( d->mBase.remoteId().toUtf8() );
00153
00154 command += ' ';
00155 switch ( d->mType ) {
00156 case Base:
00157 command += "0 (";
00158 break;
00159 case FirstLevel:
00160 command += "1 (";
00161 break;
00162 case Recursive:
00163 command += "INF (";
00164 break;
00165 default:
00166 Q_ASSERT( false );
00167 }
00168
00169 QList<QByteArray> filter;
00170 if ( !d->mScope.resource().isEmpty() ) {
00171 filter.append( "RESOURCE" );
00172
00173 filter.append( d->mScope.resource().toUtf8() );
00174 }
00175
00176 if ( !d->mScope.contentMimeTypes().isEmpty() ) {
00177 filter.append( "MIMETYPE" );
00178 QList<QByteArray> mts;
00179 foreach ( const QString &mt, d->mScope.contentMimeTypes() )
00180
00181 mts.append( mt.toUtf8() );
00182 filter.append( '(' + ImapParser::join( mts, " " ) + ')' );
00183 }
00184
00185 QList<QByteArray> options;
00186 if ( d->mScope.includeStatistics() ) {
00187 options.append( "STATISTICS" );
00188 options.append( "true" );
00189 }
00190 if ( d->mScope.ancestorRetrieval() != CollectionFetchScope::None ) {
00191 options.append( "ANCESTORS" );
00192 switch ( d->mScope.ancestorRetrieval() ) {
00193 case CollectionFetchScope::None:
00194 options.append( "0" );
00195 break;
00196 case CollectionFetchScope::Parent:
00197 options.append( "1" );
00198 break;
00199 case CollectionFetchScope::All:
00200 options.append( "INF" );
00201 break;
00202 default:
00203 Q_ASSERT( false );
00204 }
00205 }
00206
00207 command += ImapParser::join( filter, " " ) + ") (" + ImapParser::join( options, " " ) + ")\n";
00208 d->writeData( command );
00209 }
00210
00211 void CollectionFetchJob::doHandleResponse( const QByteArray & tag, const QByteArray & data )
00212 {
00213 Q_D( CollectionFetchJob );
00214
00215 if ( tag == "*" ) {
00216 Collection collection;
00217 ProtocolHelper::parseCollection( data, collection );
00218 if ( !collection.isValid() )
00219 return;
00220
00221 collection.d_ptr->resetChangeLog();
00222 d->mCollections.append( collection );
00223 d->mPendingCollections.append( collection );
00224 if ( !d->mEmitTimer->isActive() )
00225 d->mEmitTimer->start();
00226 return;
00227 }
00228 kDebug() << "Unhandled server response" << tag << data;
00229 }
00230
00231 void CollectionFetchJob::setResource(const QString & resource)
00232 {
00233 Q_D( CollectionFetchJob );
00234
00235 d->mScope.setResource( resource );
00236 }
00237
00238 void CollectionFetchJob::slotResult(KJob * job)
00239 {
00240 Q_D( CollectionFetchJob );
00241
00242 CollectionFetchJob *list = dynamic_cast<CollectionFetchJob*>( job );
00243 Q_ASSERT( job );
00244 d->mCollections += list->collections();
00245 Job::slotResult( job );
00246 if ( !job->error() && !hasSubjobs() )
00247 emitResult();
00248 }
00249
00250 void CollectionFetchJob::includeUnsubscribed(bool include)
00251 {
00252 Q_D( CollectionFetchJob );
00253
00254 d->mScope.setIncludeUnsubscribed( include );
00255 }
00256
00257 void CollectionFetchJob::includeStatistics(bool include)
00258 {
00259 Q_D( CollectionFetchJob );
00260
00261 d->mScope.setIncludeStatistics( include );
00262 }
00263
00264 void CollectionFetchJob::setFetchScope( const CollectionFetchScope &scope )
00265 {
00266 Q_D( CollectionFetchJob );
00267 d->mScope = scope;
00268 }
00269
00270 CollectionFetchScope& CollectionFetchJob::fetchScope()
00271 {
00272 Q_D( CollectionFetchJob );
00273 return d->mScope;
00274 }
00275
00276 #include "collectionfetchjob.moc"