/* Copyright (c) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company Author: Kevin Ottens This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "changecollectiontask.h" #include #include #include #include #include #include "collectionannotationsattribute.h" #include "imapaclattribute.h" #include "imapquotaattribute.h" #include ChangeCollectionTask::ChangeCollectionTask( ResourceStateInterface::Ptr resource, QObject *parent ) : ResourceTask( DeferIfNoSession, resource, parent ), m_pendingJobs(0) { } ChangeCollectionTask::~ChangeCollectionTask() { } void ChangeCollectionTask::doStart( KIMAP::Session *session ) { if ( collection().remoteId().isEmpty() ) { emitError( i18n( "Cannot modify IMAP folder '%1', it does not exist on the server.", collection().name() ) ); changeProcessed(); return; } m_collection = collection(); m_pendingJobs = 0; if ( parts().contains( "AccessRights" ) ) { Akonadi::ImapAclAttribute *aclAttribute = collection().attribute(); if ( aclAttribute==0 ) { emitWarning( i18n( "ACLs for '%1' need to be retrieved from the IMAP server first. Skipping ACL change", collection().name() ) ); } else { KIMAP::Acl::Rights imapRights = aclAttribute->rights()[userName().toUtf8()]; Akonadi::Collection::Rights newRights = collection().rights(); if ( newRights & Akonadi::Collection::CanChangeItem ) { imapRights|= KIMAP::Acl::Write; } else { imapRights&= ~KIMAP::Acl::Write; } if ( newRights & Akonadi::Collection::CanCreateItem ) { imapRights|= KIMAP::Acl::Insert; } else { imapRights&= ~KIMAP::Acl::Insert; } if ( newRights & Akonadi::Collection::CanDeleteItem ) { imapRights|= KIMAP::Acl::DeleteMessage; } else { imapRights&= ~KIMAP::Acl::DeleteMessage; } if ( newRights & ( Akonadi::Collection::CanChangeCollection | Akonadi::Collection::CanCreateCollection ) ) { imapRights|= KIMAP::Acl::CreateMailbox; imapRights|= KIMAP::Acl::Create; } else { imapRights&= ~KIMAP::Acl::CreateMailbox; imapRights&= ~KIMAP::Acl::Create; } if ( newRights & Akonadi::Collection::CanDeleteCollection ) { imapRights|= KIMAP::Acl::DeleteMailbox; } else { imapRights&= ~KIMAP::Acl::DeleteMailbox; } if ( ( newRights & Akonadi::Collection::CanDeleteItem ) && ( newRights & Akonadi::Collection::CanDeleteCollection ) ) { imapRights|= KIMAP::Acl::Delete; } else { imapRights&= ~KIMAP::Acl::Delete; } kDebug( 5327 ) << "imapRights:" << imapRights << "newRights:" << newRights; KIMAP::SetAclJob *job = new KIMAP::SetAclJob( session ); job->setMailBox( mailBoxForCollection( collection() ) ); job->setRights( KIMAP::SetAclJob::Change, imapRights ); job->setIdentifier( userName().toUtf8() ); connect( job, SIGNAL(result(KJob*)), this, SLOT(onSetAclDone(KJob*)) ); job->start(); m_pendingJobs++; } } if ( parts().contains( "collectionannotations" ) && serverSupportsAnnotations() ) { Akonadi::CollectionAnnotationsAttribute *annotationsAttribute = collection().attribute(); if ( annotationsAttribute ) { // No annotations it seems... server is lieing to us? QMap annotations = annotationsAttribute->annotations(); kDebug( 5327 ) << "All annotations: " << annotations; foreach ( const QByteArray &entry, annotations.keys() ) { //krazy:exclude=foreach KIMAP::SetMetaDataJob *job = new KIMAP::SetMetaDataJob( session ); if ( serverCapabilities().contains( QLatin1String("METADATA") ) ) { job->setServerCapability( KIMAP::MetaDataJobBase::Metadata ); } else { job->setServerCapability( KIMAP::MetaDataJobBase::Annotatemore ); } job->setMailBox( mailBoxForCollection( collection() ) ); if ( !entry.startsWith( "/shared" ) && !entry.startsWith( "/private" ) ) { //Support for legacy annotations that don't include the prefix job->addMetaData( QByteArray("/shared") + entry, annotations[entry] ); } else { job->addMetaData( entry, annotations[entry] ); } kDebug( 5327 ) << "Job got entry:" << entry << "value:" << annotations[entry]; connect( job, SIGNAL(result(KJob*)), this, SLOT(onSetMetaDataDone(KJob*)) ); job->start(); m_pendingJobs++; } } } if ( parts().contains( "imapacl" ) ) { Akonadi::ImapAclAttribute *aclAttribute = collection().attribute(); if ( aclAttribute ) { const QMap rights = aclAttribute->rights(); const QMap oldRights = aclAttribute->oldRights(); const QList oldIds = oldRights.keys(); const QList ids = rights.keys(); // remove all ACL entries that have been deleted foreach ( const QByteArray &oldId, oldIds ) { if ( !ids.contains( oldId ) ) { KIMAP::SetAclJob *job = new KIMAP::SetAclJob( session ); job->setMailBox( mailBoxForCollection( collection() ) ); job->setIdentifier( oldId ); job->setRights( KIMAP::SetAclJob::Remove, oldRights[oldId] ); connect( job, SIGNAL(result(KJob*)), this, SLOT(onSetAclDone(KJob*)) ); job->start(); m_pendingJobs++; } } foreach ( const QByteArray &id, ids ) { KIMAP::SetAclJob *job = new KIMAP::SetAclJob( session ); job->setMailBox( mailBoxForCollection( collection() ) ); job->setIdentifier( id ); job->setRights( KIMAP::SetAclJob::Change, rights[id] ); connect( job, SIGNAL(result(KJob*)), this, SLOT(onSetAclDone(KJob*)) ); job->start(); m_pendingJobs++; } } } // Check if we need to rename the mailbox // This one goes last on purpose, we don't want the previous jobs // we triggered to act on the wrong mailbox name if ( parts().contains( "NAME" ) ) { const QChar separator = separatorCharacter(); m_collection.setName( m_collection.name().replace( separator, QString() ) ); m_collection.setRemoteId( separator + m_collection.name() ); const QString oldMailBox = mailBoxForCollection( collection() ); const QString newMailBox = mailBoxForCollection( m_collection ); if ( oldMailBox != newMailBox ) { KIMAP::RenameJob *renameJob = new KIMAP::RenameJob( session ); renameJob->setSourceMailBox( oldMailBox ); renameJob->setDestinationMailBox( newMailBox ); connect( renameJob, SIGNAL(result(KJob*)), this, SLOT(onRenameDone(KJob*)) ); renameJob->start(); m_pendingJobs++; } } // we scheduled no change on the server side, probably we got only // unsupported part, so just declare the task done if ( m_pendingJobs == 0 ) { changeCommitted( collection() ); } } void ChangeCollectionTask::onRenameDone( KJob *job ) { if ( job->error() ) { const QString prevRid = collection().remoteId(); Q_ASSERT( !prevRid.isEmpty() ); emitWarning( i18n( "Failed to rename the folder, restoring folder list." ) ); m_collection.setName( prevRid.mid( 1 ) ); m_collection.setRemoteId( prevRid ); endTaskIfNeeded(); } else { KIMAP::RenameJob *renameJob = static_cast( job ); KIMAP::SubscribeJob *subscribeJob = new KIMAP::SubscribeJob( renameJob->session() ); subscribeJob->setMailBox( renameJob->destinationMailBox() ); connect( subscribeJob, SIGNAL(result(KJob*)), this, SLOT(onSubscribeDone(KJob*)) ); subscribeJob->start(); } } void ChangeCollectionTask::onSubscribeDone( KJob *job ) { if ( job->error() && isSubscriptionEnabled() ) { emitWarning( i18n( "Failed to subscribe to the renamed folder '%1' on the IMAP server. " "It will disappear on next sync. Use the subscription dialog to overcome that", m_collection.name() ) ); } endTaskIfNeeded(); } void ChangeCollectionTask::onSetAclDone( KJob *job ) { if ( job->error() ) { emitWarning( i18n( "Failed to write some ACLs for '%1' on the IMAP server. %2", collection().name(), job->errorText() ) ); } endTaskIfNeeded(); } void ChangeCollectionTask::onSetMetaDataDone( KJob *job ) { if ( job->error() ) { emitWarning( i18n( "Failed to write some annotations for '%1' on the IMAP server. %2", collection().name(), job->errorText() ) ); } endTaskIfNeeded(); } void ChangeCollectionTask::endTaskIfNeeded() { if ( --m_pendingJobs == 0 ) { // the others have ended, we're done, the next one can go changeCommitted( m_collection ); } }