diff --git a/outboxinterface/CMakeLists.txt b/outboxinterface/CMakeLists.txt index dbcd88c04..c59425f01 100644 --- a/outboxinterface/CMakeLists.txt +++ b/outboxinterface/CMakeLists.txt @@ -1,50 +1,50 @@ #include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${KDE4_INCLUDES} ${KDEPIMLIBS_INCLUDE_DIRS}) # TODO: 5324 is mailtransport. we need one of our own! add_definitions( -DKDE_DEFAULT_DEBUG_AREA=5324 ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${KDE4_ENABLE_EXCEPTIONS}" ) add_subdirectory( tests ) set(outboxinterface_lib_srcs dispatcherinterface.cpp localfolders.cpp messagequeuejob.cpp addressattribute.cpp dispatchmodeattribute.cpp errorattribute.cpp - sentcollectionattribute.cpp + sentbehaviourattribute.cpp transportattribute.cpp resourcetester/resourcesynchronizationjob.cpp ) qt4_add_dbus_interface( outboxinterface_lib_srcs interfaces/org.kde.Akonadi.MailDispatcher.xml mdainterface ) install( FILES interfaces/org.kde.Akonadi.MailDispatcher.xml DESTINATION ${DBUS_INTERFACES_INSTALL_DIR} ) kde4_add_kcfg_files(outboxinterface_lib_srcs settings.kcfgc) install(FILES outboxinterface.kcfg DESTINATION ${KCFG_INSTALL_DIR}) kde4_add_library(outboxinterface SHARED ${outboxinterface_lib_srcs}) target_link_libraries(outboxinterface ${KDE4_KIO_LIBS} akonadi-kde kmime mailtransport ) set_target_properties(outboxinterface PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION} ) install(TARGETS outboxinterface ${INSTALL_TARGETS_DEFAULT_ARGS}) install( FILES outboxinterface_export.h dispatcherinterface.h localfolders.h messagequeuejob.h addressattribute.h dispatchmodeattribute.h errorattribute.h - sentcollectionattribute.h + sentbehaviourattribute.h transportattribute.h DESTINATION ${INCLUDE_INSTALL_DIR}/outboxinterface COMPONENT Devel) diff --git a/outboxinterface/TODO b/outboxinterface/TODO index 455aac75a..a2816b050 100644 --- a/outboxinterface/TODO +++ b/outboxinterface/TODO @@ -1,9 +1,7 @@ * figure out a better name (than outboxinterface), and decide where to merge (in mailtransport?) * better name for MessageQueuer? * Figure out a better way to configure the resource via D-Bus. -* Provide an option (attribute?) for messages to be deleted from outbox once - they have been sent (as opposed to moving to sent-mail). * Krazy wants me to use private d-pointers in the attributes -- probably a good idea since this is a library?... diff --git a/outboxinterface/dispatcherinterface.cpp b/outboxinterface/dispatcherinterface.cpp index aa1451c0b..2794675b2 100644 --- a/outboxinterface/dispatcherinterface.cpp +++ b/outboxinterface/dispatcherinterface.cpp @@ -1,230 +1,224 @@ /* Copyright (c) 2009 Constantin Berzan 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 "dispatcherinterface.h" -#include "localfolders.h" -#include "addressattribute.h" -#include "dispatchmodeattribute.h" -#include "sentcollectionattribute.h" -#include "transportattribute.h" - #include "mdainterface.h" #include #include #include #include using namespace Akonadi; using namespace OutboxInterface; /** * Private class that helps to provide binary compatibility between releases. * @internal */ class OutboxInterface::DispatcherInterfacePrivate { public: DispatcherInterfacePrivate(); ~DispatcherInterfacePrivate(); DispatcherInterface *instance; bool connected; org::kde::Akonadi::MailDispatcher *iface; AgentInstance agent; // slots void connectToAgent(); void dbusServiceOwnerChanged( const QString &name, const QString &oldOwner, const QString &newOwner ); void agentInstanceRemoved( const AgentInstance &a ); void agentInstanceChanged( const AgentInstance &a ); }; K_GLOBAL_STATIC( DispatcherInterfacePrivate, sInstance ) DispatcherInterfacePrivate::DispatcherInterfacePrivate() : instance( new DispatcherInterface( this ) ) , iface( 0 ) { QDBusConnection bus = QDBusConnection::sessionBus(); QObject::connect( bus.interface(), SIGNAL(serviceOwnerChanged(QString,QString,QString)), instance, SLOT(dbusServiceOwnerChanged(QString,QString,QString)) ); // AgentInstance objects are not updated automatically, so we need to watch // for AgentManager's signals: QObject::connect( AgentManager::self(), SIGNAL(instanceOnline(Akonadi::AgentInstance,bool)), instance, SLOT(agentInstanceChanged(Akonadi::AgentInstance)) ); QObject::connect( AgentManager::self(), SIGNAL(instanceProgressChanged(Akonadi::AgentInstance)), instance, SLOT(agentInstanceChanged(Akonadi::AgentInstance)) ); QObject::connect( AgentManager::self(), SIGNAL(instanceStatusChanged(Akonadi::AgentInstance)), instance, SLOT(agentInstanceChanged(Akonadi::AgentInstance)) ); QObject::connect( AgentManager::self(), SIGNAL(instanceRemoved(Akonadi::AgentInstance)), instance, SLOT(agentInstanceRemoved(Akonadi::AgentInstance)) ); connected = false; connectToAgent(); } DispatcherInterfacePrivate::~DispatcherInterfacePrivate() { delete instance; } void DispatcherInterfacePrivate::connectToAgent() { if( connected ) { kDebug() << "Already connected to MDA."; return; } delete iface; iface = new org::kde::Akonadi::MailDispatcher( QLatin1String( "org.freedesktop.Akonadi.Agent.akonadi_maildispatcher_agent" ), QLatin1String( "/" ), QDBusConnection::sessionBus(), instance ); if( !iface->isValid() ) { kDebug() << "Couldn't get D-Bus interface of MDA. Retrying in 1s."; QTimer::singleShot( 1000, instance, SLOT(connectToAgent()) ); return; } agent = AgentManager::self()->instance( QLatin1String( "akonadi_maildispatcher_agent" ) ); if( !agent.isValid() ) { kDebug() << "Could not get agent instance of MDA. Retrying in 1s."; QTimer::singleShot( 1000, instance, SLOT(connectToAgent()) ); return; } kDebug() << "Connected to the MDA."; connected = true; } void DispatcherInterfacePrivate::dbusServiceOwnerChanged( const QString &name, const QString &oldOwner, const QString &newOwner ) { Q_UNUSED( oldOwner ); if( name == QLatin1String( "org.freedesktop.Akonadi.Agent.akonad_maildispatcher_agent" ) ) { if( newOwner.isEmpty() ) { kDebug() << "MDA disappeared from D-Bus."; connected = false; QTimer::singleShot( 0, instance, SLOT(connectToAgent()) ); } } } void DispatcherInterfacePrivate::agentInstanceRemoved( const AgentInstance &a ) { if( agent == a ) { kDebug() << "MDA agent disappeared."; connected = false; QTimer::singleShot( 0, instance, SLOT(connectToAgent()) ); } } void DispatcherInterfacePrivate::agentInstanceChanged( const AgentInstance &a ) { if( agent == a ) { kDebug() << "Updating instance."; agent = a; // This is not as weird as it looks :) operator== checks the id only, but // operator= copies everything (like status, progress etc.) } } DispatcherInterface::DispatcherInterface( DispatcherInterfacePrivate *dd ) : QObject() , d( dd ) { } DispatcherInterface *DispatcherInterface::self() { return sInstance->instance; } bool DispatcherInterface::isReady() const { return d->connected; } bool DispatcherInterface::dispatcherOnline() const { if( !d->connected ) { kWarning() << "Not connected to the MDA."; return false; } return d->agent.isOnline(); } AgentInstance::Status DispatcherInterface::dispatcherStatus() const { if( !d->connected ) { kWarning() << "Not connected to the MDA."; return AgentInstance::Broken; } return d->agent.status(); } int DispatcherInterface::dispatcherProgress() const { if( !d->connected ) { kWarning() << "Not connected to the MDA."; return -1; } return d->agent.progress(); } void DispatcherInterface::abortDispatching() { if( !d->connected ) { kWarning() << "Not connected to the MDA."; return; } d->iface->abort(); } void DispatcherInterface::dispatchManually() { if( !d->connected ) { kWarning() << "Not connected to the MDA."; return; } kDebug() << "implement me"; //TODO } void DispatcherInterface::retryDispatching() { if( !d->connected ) { kWarning() << "Not connected to the MDA."; return; } kDebug() << "implement me"; //TODO } #include "dispatcherinterface.moc" diff --git a/outboxinterface/dispatcherinterface.h b/outboxinterface/dispatcherinterface.h index 93bf49753..6012be3f0 100644 --- a/outboxinterface/dispatcherinterface.h +++ b/outboxinterface/dispatcherinterface.h @@ -1,97 +1,95 @@ /* Copyright (c) 2009 Constantin Berzan 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. */ #ifndef OUTBOXINTERFACE_DISPATCHERINTERFACE_H #define OUTBOXINTERFACE_DISPATCHERINTERFACE_H #include #include #include namespace OutboxInterface { class DispatcherInterfacePrivate; /** An interface for apps to interact with the MDA. It connects to the MDA via D-Bus and provides info about queued messages and progress, and methods such as abort or retry sending. - TODO should this class provide a progress widget? - TODO dispatchManually and retryDispatching functions should be offered on a per-item basis as well, I imagine when the user will have a messageView of the outbox. Then do we need global ones here (i.e. for all items in the outbox)? */ class OUTBOXINTERFACE_EXPORT DispatcherInterface : public QObject { Q_OBJECT public: /** Returns the DispatcherInterface instance. */ static DispatcherInterface *self(); bool isReady() const; bool dispatcherOnline() const; Akonadi::AgentInstance::Status dispatcherStatus() const; int dispatcherProgress() const; /** Aborts sending the current message, and marks all messages in the queue as DispatchMode::Never. */ void abortDispatching(); /** Looks for messages in the outbox with DispatchMode::Never and marks them DispatchMode::Immediately for sending. */ void dispatchManually(); /** Looks for messages in the outbox with ErrorAttribute, and clears them and queues them again for sending. */ void retryDispatching(); private: friend class DispatcherInterfacePrivate; DispatcherInterfacePrivate *const d; // singleton class; the only instance resides in sInstance->instance DispatcherInterface( DispatcherInterfacePrivate *dd ); Q_PRIVATE_SLOT( d, void connectToAgent() ) Q_PRIVATE_SLOT( d, void dbusServiceOwnerChanged( const QString&, const QString&, const QString& ) ) Q_PRIVATE_SLOT( d, void agentInstanceRemoved( const Akonadi::AgentInstance& ) ) Q_PRIVATE_SLOT( d, void agentInstanceChanged( const Akonadi::AgentInstance& ) ) }; } #endif diff --git a/outboxinterface/dispatchmodeattribute.cpp b/outboxinterface/dispatchmodeattribute.cpp index 8dbac3242..300f96ab2 100644 --- a/outboxinterface/dispatchmodeattribute.cpp +++ b/outboxinterface/dispatchmodeattribute.cpp @@ -1,98 +1,97 @@ /* Copyright 2009 Constantin Berzan 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 "dispatchmodeattribute.h" #include using namespace Akonadi; using namespace OutboxInterface; DispatchModeAttribute::DispatchModeAttribute( DispatchMode mode, const QDateTime &date ) : mMode(mode) , mDueDate(date) { } DispatchModeAttribute::~DispatchModeAttribute() { } DispatchModeAttribute* DispatchModeAttribute::clone() const { return new DispatchModeAttribute( mMode, mDueDate ); } QByteArray DispatchModeAttribute::type() const { static const QByteArray sType( "DispatchModeAttribute" ); return sType; } QByteArray DispatchModeAttribute::serialized() const { switch ( mMode ) { case Immediately: return "immediately"; case AfterDueDate: return "after" + mDueDate.toString(Qt::ISODate).toLatin1(); case Never: return "never"; } Q_ASSERT(false); - return ""; // suppress control-reaches-end-of-non-void-function warning + return QByteArray(); // suppress control-reaches-end-of-non-void-function warning } void DispatchModeAttribute::deserialize( const QByteArray &data ) { mDueDate = QDateTime(); - if ( data == "immediately" ) + if ( data == "immediately" ) { mMode = Immediately; - else if ( data == "never" ) + } else if ( data == "never" ) { mMode = Never; - else if ( data.startsWith( QByteArray( "after" ) ) ) - { + } else if ( data.startsWith( QByteArray( "after" ) ) ) { mMode = AfterDueDate; mDueDate = QDateTime::fromString( data.mid(5), Qt::ISODate ); // NOTE: 5 is the strlen of "after". Not very maintenance-friendly. - } - else + } else { kWarning() << "Failed to deserialize data [" << data << "]"; + } } DispatchModeAttribute::DispatchMode DispatchModeAttribute::dispatchMode() const { return mMode; } void DispatchModeAttribute::setDispatchMode( DispatchMode mode ) { mMode = mode; } QDateTime DispatchModeAttribute::dueDate() const { return mDueDate; } void DispatchModeAttribute::setDueDate( const QDateTime &date ) { mDueDate = date; } diff --git a/outboxinterface/dispatchmodeattribute.h b/outboxinterface/dispatchmodeattribute.h index b0f72cd76..3b143a8e3 100644 --- a/outboxinterface/dispatchmodeattribute.h +++ b/outboxinterface/dispatchmodeattribute.h @@ -1,72 +1,77 @@ /* Copyright 2009 Constantin Berzan 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. */ #ifndef OUTBOXINTERFACE_DISPATCHMODEATTRIBUTE_H #define OUTBOXINTERFACE_DISPATCHMODEATTRIBUTE_H #include #include #include namespace OutboxInterface { /** * Attribute determining how and when a message from the outbox is dispatched. * * TODO: name: SendPolicy? SendingPolicy? */ class OUTBOXINTERFACE_EXPORT DispatchModeAttribute : public Akonadi::Attribute { public: enum DispatchMode { Immediately, AfterDueDate, Never }; explicit DispatchModeAttribute( DispatchMode mode = Immediately, const QDateTime &date = QDateTime() ); virtual ~DispatchModeAttribute(); virtual DispatchModeAttribute* clone() const; virtual QByteArray type() const; virtual QByteArray serialized() const; virtual void deserialize( const QByteArray &data ); DispatchMode dispatchMode() const; void setDispatchMode( DispatchMode mode ); + + /** + The due date for sending the message. + Only valid if dispatchMode() is AfterDueDate. + */ QDateTime dueDate() const; void setDueDate( const QDateTime &date ); private: DispatchMode mMode; QDateTime mDueDate; }; } #endif diff --git a/outboxinterface/localfolders.cpp b/outboxinterface/localfolders.cpp index 468181969..ab4449724 100644 --- a/outboxinterface/localfolders.cpp +++ b/outboxinterface/localfolders.cpp @@ -1,445 +1,445 @@ /* Copyright (c) 2009 Constantin Berzan 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 "localfolders.h" #include "settings.h" #include "addressattribute.h" #include "dispatchmodeattribute.h" #include "errorattribute.h" -#include "sentcollectionattribute.h" +#include "sentbehaviourattribute.h" #include "transportattribute.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // svn:external from playground/pim/akonaditest #define DBUS_SERVICE_NAME QLatin1String( "org.kde.pim.LocalFolders" ) using namespace Akonadi; using namespace OutboxInterface; /** * Private class that helps to provide binary compatibility between releases. * @internal */ class OutboxInterface::LocalFoldersPrivate { public: LocalFoldersPrivate(); ~LocalFoldersPrivate(); LocalFolders *instance; bool ready; bool preparing; bool scheduled; bool isMainInstance; Collection outbox; Collection sentMail; Collection rootMaildir; KJob *outboxJob; KJob *sentMailJob; Monitor *monitor; /** If this is the main instance, attempts to create the resource and collections if necessary, then fetches them. If this is not the main instance, waits for them to be created by the main instance, and then fetches them. Will emit foldersReady() when done */ void prepare(); /** Schedules a prepare() in 1 second. Called when this is not the main instance and we need to wait, or when something disappeared and needs to be recreated. */ void schedulePrepare(); // slot /** Creates the maildir resource, if it is not found. */ void createResourceIfNeeded(); /** Creates the outbox and sent-mail collections, if they are not present. */ void createCollectionsIfNeeded(); /** Creates a Monitor to watch the resource and connects to its signals. This is used to watch for evil users deleting the resource / outbox / etc. */ void connectMonitor(); /** Fetches the collections of the maildir resource. There is one root collection, which contains the outbox and sent-mail collections. */ void fetchCollections(); void resourceCreateResult( KJob *job ); void resourceSyncResult( KJob *job ); void collectionCreateResult( KJob *job ); void collectionFetchResult( KJob *job ); }; K_GLOBAL_STATIC( LocalFoldersPrivate, sInstance ) LocalFolders::LocalFolders( LocalFoldersPrivate *dd ) : QObject() , d( dd ) { // register attributes AttributeFactory::registerAttribute(); AttributeFactory::registerAttribute(); AttributeFactory::registerAttribute(); - AttributeFactory::registerAttribute(); + AttributeFactory::registerAttribute(); AttributeFactory::registerAttribute(); } LocalFolders *LocalFolders::self() { return sInstance->instance; } void LocalFolders::fetch() { d->prepare(); } bool LocalFolders::isReady() const { return d->ready; } Collection LocalFolders::outbox() const { Q_ASSERT( d->ready ); return d->outbox; } Collection LocalFolders::sentMail() const { Q_ASSERT( d->ready ); return d->sentMail; } LocalFoldersPrivate::LocalFoldersPrivate() : instance( new LocalFolders(this) ) { isMainInstance = QDBusConnection::sessionBus().registerService( DBUS_SERVICE_NAME ); ready = false; // prepare() expects these preparing = false; outboxJob = 0; sentMailJob = 0; monitor = 0; prepare(); } LocalFoldersPrivate::~LocalFoldersPrivate() { delete instance; } void LocalFoldersPrivate::prepare() { if( ready ) { //kDebug() << "Already ready. Emitting foldersReady()."; emit instance->foldersReady(); return; } if( preparing ) { kDebug() << "Already preparing."; return; } kDebug() << "Preparing. isMainInstance" << isMainInstance; preparing = true; scheduled = false; Q_ASSERT( outboxJob == 0 ); Q_ASSERT( sentMailJob == 0 ); Q_ASSERT( monitor == 0); rootMaildir = Collection( -1 ); outbox = Collection( -1 ); sentMail = Collection( -1 ); createResourceIfNeeded(); } void LocalFoldersPrivate::schedulePrepare() { if( scheduled ) { kDebug() << "Prepare already scheduled."; return; } kDebug() << "Scheduling prepare."; if( monitor ) { monitor->disconnect( instance ); monitor->deleteLater(); monitor = 0; } ready = false; preparing = false; scheduled = true; QTimer::singleShot( 1000, instance, SLOT( prepare() ) ); } void LocalFoldersPrivate::createResourceIfNeeded() { Q_ASSERT( preparing ); // Another instance might have created the resource and updated the config. Settings::self()->readConfig(); kDebug() << "Resource from config:" << Settings::resourceId(); // check that the maildir resource exists AgentInstance resource = AgentManager::self()->instance( Settings::resourceId() ); if( !resource.isValid() ) { // Try to grab main instance status (if previous main instance quit). if( !isMainInstance ) { isMainInstance = QDBusConnection::sessionBus().registerService( DBUS_SERVICE_NAME ); if( isMainInstance ) { kDebug() << "I have become the main instance."; } } // Create resource if main instance. if( !isMainInstance ) { kDebug() << "Waiting for the main instance to create the resource."; schedulePrepare(); } else { kDebug() << "Creating maildir resource."; AgentType type = AgentManager::self()->type( "akonadi_maildir_resource" ); AgentInstanceCreateJob *job = new AgentInstanceCreateJob( type ); QObject::connect( job, SIGNAL( result( KJob * ) ), instance, SLOT( resourceCreateResult( KJob * ) ) ); // this is not an Akonadi::Job, so we must start it ourselves job->start(); } } else { connectMonitor(); } } void LocalFoldersPrivate::createCollectionsIfNeeded() { Q_ASSERT( preparing ); // but I may not be the main instance Q_ASSERT( rootMaildir.isValid() ); if( !outbox.isValid() ) { kDebug() << "Creating outbox collection."; Collection col; col.setParent( rootMaildir ); col.setName( "outbox" ); col.setContentMimeTypes( QStringList( "message/rfc822" ) ); Q_ASSERT( outboxJob == 0 ); outboxJob = new CollectionCreateJob( col ); QObject::connect( outboxJob, SIGNAL( result( KJob * ) ), instance, SLOT( collectionCreateResult( KJob * ) ) ); } if( !sentMail.isValid() ) { kDebug() << "Creating sent-mail collection."; Collection col; col.setParent( rootMaildir ); col.setName( "sent-mail" ); col.setContentMimeTypes( QStringList( "message/rfc822" ) ); Q_ASSERT( sentMailJob == 0 ); sentMailJob = new CollectionCreateJob( col ); QObject::connect( sentMailJob, SIGNAL( result( KJob * ) ), instance, SLOT( collectionCreateResult( KJob * ) ) ); } if( outboxJob == 0 && sentMailJob == 0 ) { // Everything is ready (created and fetched). kDebug() << "Local folders ready. resourceId" << Settings::resourceId() << "outbox id" << outbox.id() << "sentMail id" << sentMail.id(); Q_ASSERT( !ready ); ready = true; preparing = false; Settings::self()->writeConfig(); emit instance->foldersReady(); } } void LocalFoldersPrivate::connectMonitor() { Q_ASSERT( preparing ); // but I may not be the main instance Q_ASSERT( monitor == 0 ); monitor = new Monitor( instance ); monitor->setResourceMonitored( Settings::resourceId().toAscii() ); QObject::connect( monitor, SIGNAL( collectionRemoved( Akonadi::Collection ) ), instance, SLOT( schedulePrepare() ) ); kDebug() << "Connected monitor."; fetchCollections(); } void LocalFoldersPrivate::fetchCollections() { Q_ASSERT( preparing ); // but I may not be the main instance kDebug() << "Fetching collections in maildir resource."; CollectionFetchJob *job = new CollectionFetchJob( Collection::root(), CollectionFetchJob::Recursive ); job->setResource( Settings::resourceId() ); // limit search QObject::connect( job, SIGNAL( result( KJob * ) ), instance, SLOT( collectionFetchResult( KJob * ) ) ); } void LocalFoldersPrivate::resourceCreateResult( KJob *job ) { Q_ASSERT( isMainInstance ); Q_ASSERT( preparing ); if( job->error() ) { kFatal() << "AgentInstanceCreateJob failed to make a maildir resource for us."; } AgentInstanceCreateJob *createJob = static_cast( job ); AgentInstance agent = createJob->instance(); Settings::setResourceId( agent.identifier() ); kDebug() << "Created maildir resource with id" << Settings::resourceId(); // configure the resource agent.setName( i18n( "Local Mail Folders" ) ); QDBusInterface conf( "org.freedesktop.Akonadi.Resource." + Settings::resourceId(), "/Settings", "org.kde.Akonadi.Maildir.Settings" ); QDBusReply reply = conf.call( "setPath", KGlobal::dirs()->localxdgdatadir() + "mail" ); if( !reply.isValid() ) { kFatal() << "Failed to set the root maildir."; } agent.reconfigure(); // sync the resource ResourceSynchronizationJob *sjob = new ResourceSynchronizationJob( agent ); QObject::connect( sjob, SIGNAL( result( KJob* ) ), instance, SLOT( resourceSyncResult( KJob* ) ) ); sjob->start(); // non-Akonadi } void LocalFoldersPrivate::resourceSyncResult( KJob *job ) { Q_ASSERT( isMainInstance ); Q_ASSERT( preparing ); if( job->error() ) { kFatal() << "ResourceSynchronizationJob failed."; } connectMonitor(); } void LocalFoldersPrivate::collectionCreateResult( KJob *job ) { Q_ASSERT( isMainInstance ); if( job->error() ) { kFatal() << "CollectionCreateJob failed to make a collection for us."; } CollectionCreateJob *createJob = static_cast( job ); if( job == outboxJob ) { outboxJob = 0; outbox = createJob->collection(); kDebug() << "Created outbox collection with id" << outbox.id(); } else if( job == sentMailJob ) { sentMailJob = 0; sentMail = createJob->collection(); kDebug() << "Created sent-mail collection with id" << sentMail.id(); } else { kFatal() << "Got a result for a job I don't know about."; } if( outboxJob == 0 && sentMailJob == 0 ) { // Done creating. Refetch everything. fetchCollections(); } } void LocalFoldersPrivate::collectionFetchResult( KJob *job ) { Q_ASSERT( preparing ); // but I may not be the main instance CollectionFetchJob *fetchJob = static_cast( job ); Collection::List cols = fetchJob->collections(); kDebug() << "CollectionFetchJob fetched" << cols.count() << "collections."; outbox = Collection( -1 ); sentMail = Collection( -1 ); Q_FOREACH( const Collection &col, cols ) { if( col.parent() == Collection::root().id() ) { rootMaildir = col; kDebug() << "Fetched root maildir collection."; } else if( col.name() == "outbox" ) { Q_ASSERT( outbox.id() == -1 ); outbox = col; kDebug() << "Fetched outbox collection."; } else if( col.name() == "sent-mail" ) { Q_ASSERT( sentMail.id() == -1 ); sentMail = col; kDebug() << "Fetched sent-mail collection."; } else { kWarning() << "Extraneous collection" << col.name() << "with id" << col.id() << "found."; } } if( !rootMaildir.isValid() ) { kFatal() << "Failed to fetch root maildir collection."; } createCollectionsIfNeeded(); } #include "localfolders.moc" diff --git a/outboxinterface/messagequeuejob.cpp b/outboxinterface/messagequeuejob.cpp index 157f888e3..5fa194e1c 100644 --- a/outboxinterface/messagequeuejob.cpp +++ b/outboxinterface/messagequeuejob.cpp @@ -1,321 +1,320 @@ /* Copyright (c) 2009 Constantin Berzan 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 "messagequeuejob.h" #include "localfolders.h" #include "addressattribute.h" -#include "dispatchmodeattribute.h" -#include "sentcollectionattribute.h" #include "transportattribute.h" #include #include #include #include #include #include #include #include using namespace Akonadi; using namespace KMime; using namespace MailTransport; using namespace OutboxInterface; /** * Private class that helps to provide binary compatibility between releases. * @internal */ class OutboxInterface::MessageQueueJob::Private { public: Private( MessageQueueJob *qq ) : q( qq ) { transport = -1; - mode = DispatchModeAttribute::Immediately; - useDefaultSentMail = true; - sentMail = -1; + dispatchMode = DispatchModeAttribute::Immediately; + sentBehaviour = SentBehaviourAttribute::MoveToDefaultSentCollection; + moveToCollection = -1; started = false; } MessageQueueJob *const q; Message::Ptr message; int transport; - DispatchModeAttribute::DispatchMode mode; + DispatchModeAttribute::DispatchMode dispatchMode; QDateTime dueDate; - bool useDefaultSentMail; - Collection::Id sentMail; + SentBehaviourAttribute::SentBehaviour sentBehaviour; + Collection::Id moveToCollection; QString from; QStringList to; QStringList cc; QStringList bcc; bool started; void readAddressesFromMime(); /** Checks that this message has everything it needs and is ready to be sent. */ bool validate(); // slot void doStart(); }; void MessageQueueJob::Private::readAddressesFromMime() { kDebug() << "implement me"; // big TODO } bool MessageQueueJob::Private::validate() { if( !message ) { q->setError( UserDefinedError ); q->setErrorText( i18n( "Empty message." ) ); q->emitResult(); return false; // NOTE: the MDA also asserts that msg->encodedContent(true) is non-empty. } if( to.count() + cc.count() + bcc.count() == 0 ) { q->setError( UserDefinedError ); q->setErrorText( i18n( "Message has no recipients." ) ); q->emitResult(); return false; } - if( mode == DispatchModeAttribute::AfterDueDate && !dueDate.isValid() ) { + if( dispatchMode == DispatchModeAttribute::AfterDueDate && !dueDate.isValid() ) { q->setError( UserDefinedError ); q->setErrorText( i18n( "Message has invalid due date." ) ); q->emitResult(); return false; } if( TransportManager::self()->transportById( transport, false ) == 0 ) { q->setError( UserDefinedError ); q->setErrorText( i18n( "Message has invalid transport." ) ); q->emitResult(); return false; } - if( useDefaultSentMail ) { - Q_ASSERT( LocalFolders::self()->isReady() ); - sentMail = LocalFolders::self()->sentMail().id(); - // Can't do this in the constructor because LocalFolders is not ready. - - // TODO: add support for DefaultSentMailCollection and DeleteAfterSending - // in SentCollectionAttribute - } - - if( sentMail < 0 ) { + if( sentBehaviour == SentBehaviourAttribute::MoveToCollection && moveToCollection < 0 ) { q->setError( UserDefinedError ); q->setErrorText( i18n( "Message has invalid sent-mail folder." ) ); q->emitResult(); return false; + } else if( sentBehaviour == SentBehaviourAttribute::MoveToDefaultSentCollection ) { + Q_ASSERT( LocalFolders::self()->isReady() ); + Q_ASSERT( LocalFolders::self()->sentMail().isValid() ); } return true; // all ok } void MessageQueueJob::Private::doStart() { LocalFolders::self()->disconnect( q ); //kDebug() << q << "starting"; Q_ASSERT( !started ); started = true; if( !validate() ) { // The error has been set; the result has been emitted. return; } // create item Item item; item.setMimeType( "message/rfc822" ); item.setPayload( message ); //kDebug() << "message:" << message->encodedContent( true ); // set attributes AddressAttribute *addrA = new AddressAttribute( from, to, cc, bcc ); - DispatchModeAttribute *dmA = new DispatchModeAttribute( mode ); - SentCollectionAttribute *sA = new SentCollectionAttribute( sentMail ); + DispatchModeAttribute *dmA = new DispatchModeAttribute( dispatchMode, dueDate ); + SentBehaviourAttribute *sA = new SentBehaviourAttribute( sentBehaviour, moveToCollection ); TransportAttribute *tA = new TransportAttribute( transport ); item.addAttribute( addrA ); item.addAttribute( dmA ); item.addAttribute( sA ); item.addAttribute( tA ); // set flags item.setFlag( "queued" ); // put item in Akonadi storage Q_ASSERT( LocalFolders::self()->isReady() ); Collection col = LocalFolders::self()->outbox(); ItemCreateJob *job = new ItemCreateJob( item, col ); // job autostarts q->addSubjob( job ); } MessageQueueJob::MessageQueueJob( QObject *parent ) : KCompositeJob( parent ) , d( new Private( this ) ) { //kDebug() << this << "created"; } MessageQueueJob::~MessageQueueJob() { //kDebug() << this << "destroyed"; delete d; } Message::Ptr MessageQueueJob::message() const { return d->message; } int MessageQueueJob::transportId() const { return d->transport; } DispatchModeAttribute::DispatchMode MessageQueueJob::dispatchMode() const { - return d->mode; + return d->dispatchMode; } QDateTime MessageQueueJob::sendDueDate() const { - if( d->mode != DispatchModeAttribute::AfterDueDate ) { - kWarning() << "called when mode is not AfterDueDate"; + if( d->dispatchMode != DispatchModeAttribute::AfterDueDate ) { + kWarning() << "Called when dispatchMode is not AfterDueDate."; } return d->dueDate; } -Collection::Id MessageQueueJob::sentMailCollection() const +Collection::Id MessageQueueJob::moveToCollection() const { - return d->sentMail; + if( d->sentBehaviour != SentBehaviourAttribute::MoveToCollection ) { + kWarning() << "Called when sentBehaviour is not MoveToCollection."; + } + return d->moveToCollection; } QString MessageQueueJob::from() const { return d->from; } QStringList MessageQueueJob::to() const { return d->to; } QStringList MessageQueueJob::cc() const { return d->cc; } QStringList MessageQueueJob::bcc() const { return d->bcc; } void MessageQueueJob::setMessage( Message::Ptr message ) { d->message = message; } void MessageQueueJob::setTransportId( int id ) { d->transport = id; } void MessageQueueJob::setDispatchMode( DispatchModeAttribute::DispatchMode mode ) { - d->mode = mode; + d->dispatchMode = mode; } void MessageQueueJob::setDueDate( const QDateTime &date ) { d->dueDate = date; } -void MessageQueueJob::setSentMailCollection( Collection::Id id ) +void MessageQueueJob::setSentBehaviour( SentBehaviourAttribute::SentBehaviour beh ) +{ + d->sentBehaviour = beh; +} + +void MessageQueueJob::setMoveToCollection( Collection::Id cid ) { - d->useDefaultSentMail = false; - d->sentMail = id; + d->moveToCollection = cid; } void MessageQueueJob::setFrom( const QString &from ) { d->from = from; } void MessageQueueJob::setTo( const QStringList &to ) { d->to = to; } void MessageQueueJob::setCc( const QStringList &cc ) { d->cc = cc; } void MessageQueueJob::setBcc( const QStringList &bcc ) { d->bcc = bcc; } void MessageQueueJob::readAddressesFromMime() { d->readAddressesFromMime(); } void MessageQueueJob::start() { LocalFolders *folders = LocalFolders::self(); connect( folders, SIGNAL( foldersReady() ), this, SLOT( doStart() ) ); folders->fetch(); // will emit foldersReady() } void MessageQueueJob::slotResult( KJob *job ) { // error handling KCompositeJob::slotResult( job ); if( !error() ) { //kDebug() << "item created ok. emitting result."; emitResult(); } } #include "messagequeuejob.moc" diff --git a/outboxinterface/messagequeuejob.h b/outboxinterface/messagequeuejob.h index d9546a491..48bb80bfb 100644 --- a/outboxinterface/messagequeuejob.h +++ b/outboxinterface/messagequeuejob.h @@ -1,118 +1,121 @@ /* Copyright (c) 2009 Constantin Berzan 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. */ #ifndef OUTBOXINTERFACE_MESSAGEQUEUEJOB_H #define OUTBOXINTERFACE_MESSAGEQUEUEJOB_H #include #include "dispatchmodeattribute.h" +#include "sentbehaviourattribute.h" #include #include #include #include #include #include #include namespace OutboxInterface { /** This is the preferred interface for sending email from applications. Example: @code TODO */ class OUTBOXINTERFACE_EXPORT MessageQueueJob : public KCompositeJob { Q_OBJECT public: explicit MessageQueueJob( QObject *parent = 0 ); virtual ~MessageQueueJob(); //TODO there is a lot of duplication between these and the attributes. // Any better way to handle this? //TODO document all of these KMime::Message::Ptr message() const; int transportId() const; DispatchModeAttribute::DispatchMode dispatchMode() const; QDateTime sendDueDate() const; - Akonadi::Collection::Id sentMailCollection() const; + SentBehaviourAttribute::SentBehaviour sentBehaviour() const; + Akonadi::Collection::Id moveToCollection() const; QString from() const; QStringList to() const; QStringList cc() const; QStringList bcc() const; void setMessage( KMime::Message::Ptr message ); void setTransportId( int id ); void setDispatchMode( DispatchModeAttribute::DispatchMode mode ); void setDueDate( const QDateTime &date ); - void setSentMailCollection( Akonadi::Collection::Id id ); + void setSentBehaviour( SentBehaviourAttribute::SentBehaviour beh ); + void setMoveToCollection( Akonadi::Collection::Id cid ); void setFrom( const QString &from ); void setTo( const QStringList &to ); void setCc( const QStringList &cc ); void setBcc( const QStringList &bcc ); /** Reads From, To, Cc, Bcc from the MIME message. */ void readAddressesFromMime(); /** Creates the item and places it in the outbox. It is now queued for sending by the mail dispatcher agent. (reimplemented from KJob) */ virtual void start(); protected Q_SLOTS: /** Called when the subjob finishes. (reimplemented from KCompositeJob) */ virtual void slotResult( KJob * ); private: class Private; //friend class Private; Private *const d; Q_PRIVATE_SLOT( d, void doStart() ) }; } #endif diff --git a/outboxinterface/sentbehaviourattribute.cpp b/outboxinterface/sentbehaviourattribute.cpp new file mode 100644 index 000000000..60d43b290 --- /dev/null +++ b/outboxinterface/sentbehaviourattribute.cpp @@ -0,0 +1,98 @@ +/* + Copyright 2009 Constantin Berzan + + 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 "sentbehaviourattribute.h" + +#include + +#include + +using namespace Akonadi; +using namespace OutboxInterface; + + +SentBehaviourAttribute::SentBehaviourAttribute( SentBehaviour beh, Collection::Id moveToCollection ) + : mBehaviour( beh ) + , mMoveToCollection( moveToCollection ) +{ +} + +SentBehaviourAttribute::~SentBehaviourAttribute() +{ +} + +SentBehaviourAttribute* SentBehaviourAttribute::clone() const +{ + return new SentBehaviourAttribute( mBehaviour, mMoveToCollection ); +} + +QByteArray SentBehaviourAttribute::type() const +{ + static const QByteArray sType( "SentBehaviourAttribute" ); + return sType; +} + +QByteArray SentBehaviourAttribute::serialized() const +{ + switch( mBehaviour ) { + case Delete: return "delete"; + case MoveToCollection: return "moveTo" + QByteArray::number( mMoveToCollection ); + case MoveToDefaultSentCollection: return "moveToDefault"; + } + + Q_ASSERT( false ); + return QByteArray(); +} + +void SentBehaviourAttribute::deserialize( const QByteArray &data ) +{ + mMoveToCollection = -1; + if ( data == "delete" ) { + mBehaviour = Delete; + } else if ( data == "moveToDefault" ) { + mBehaviour = MoveToDefaultSentCollection; + } else if ( data.startsWith( QByteArray( "moveTo" ) ) ) { + mBehaviour = MoveToCollection; + mMoveToCollection = data.mid(6).toLongLong(); + // NOTE: 6 is the strlen of "moveTo". + } else { + Q_ASSERT( false ); + } +} + +SentBehaviourAttribute::SentBehaviour SentBehaviourAttribute::sentBehaviour() const +{ + return mBehaviour; +} + +void SentBehaviourAttribute::setSentBehaviour( SentBehaviour beh ) +{ + mBehaviour = beh; +} + +Collection::Id SentBehaviourAttribute::moveToCollection() const +{ + return mMoveToCollection; +} + +void SentBehaviourAttribute::setMoveToCollection( Collection::Id moveToCollection ) +{ + mMoveToCollection = moveToCollection; +} + diff --git a/outboxinterface/sentcollectionattribute.h b/outboxinterface/sentbehaviourattribute.h similarity index 56% rename from outboxinterface/sentcollectionattribute.h rename to outboxinterface/sentbehaviourattribute.h index e95cff8c1..0b7f92f13 100644 --- a/outboxinterface/sentcollectionattribute.h +++ b/outboxinterface/sentbehaviourattribute.h @@ -1,60 +1,79 @@ /* Copyright 2009 Constantin Berzan 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. */ -#ifndef OUTBOXINTERFACE_SENTCOLLECTIONATTRIBUTE_H -#define OUTBOXINTERFACE_SENTCOLLECTIONATTRIBUTE_H +#ifndef OUTBOXINTERFACE_SENTBEHAVIOURATTRIBUTE_H +#define OUTBOXINTERFACE_SENTBEHAVIOURATTRIBUTE_H #include #include #include namespace OutboxInterface { /** * Attribute storing the id of the sent-mail collection for a message. The * dispatcher agent will move the item to that collection after it is sent. */ -class OUTBOXINTERFACE_EXPORT SentCollectionAttribute : public Akonadi::Attribute +class OUTBOXINTERFACE_EXPORT SentBehaviourAttribute : public Akonadi::Attribute { public: - explicit SentCollectionAttribute( Akonadi::Collection::Id id = -1 ); - virtual ~SentCollectionAttribute(); - - virtual SentCollectionAttribute* clone() const; + /** + What to do with the item in the outbox after it has been sent successfully. + */ + enum SentBehaviour + { + Delete, + MoveToCollection, + MoveToDefaultSentCollection + }; + + explicit SentBehaviourAttribute( SentBehaviour beh = MoveToDefaultSentCollection, + Akonadi::Collection::Id moveToCollection = -1 ); + virtual ~SentBehaviourAttribute(); + + virtual SentBehaviourAttribute* clone() const; virtual QByteArray type() const; virtual QByteArray serialized() const; virtual void deserialize( const QByteArray &data ); - Akonadi::Collection::Id sentCollection() const; - void setSentCollection( Akonadi::Collection::Id id ); + SentBehaviour sentBehaviour() const; + void setSentBehaviour( SentBehaviour beh ); + + /** + The collection to move the item to after it is sent. + Only valid if sentBehaviour() is MoveToCollection. + */ + Akonadi::Collection::Id moveToCollection() const; + void setMoveToCollection( Akonadi::Collection::Id moveToCollection ); private: - Akonadi::Collection::Id mId; + SentBehaviour mBehaviour; + Akonadi::Collection::Id mMoveToCollection; }; } #endif diff --git a/outboxinterface/sentcollectionattribute.cpp b/outboxinterface/sentcollectionattribute.cpp deleted file mode 100644 index 4dde0cea0..000000000 --- a/outboxinterface/sentcollectionattribute.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - Copyright 2009 Constantin Berzan - - 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 "sentcollectionattribute.h" - -#include - -#include - -using namespace Akonadi; -using namespace OutboxInterface; - - -SentCollectionAttribute::SentCollectionAttribute( Collection::Id id ) - : mId( id ) -{ -} - -SentCollectionAttribute::~SentCollectionAttribute() -{ -} - -SentCollectionAttribute* SentCollectionAttribute::clone() const -{ - return new SentCollectionAttribute( mId ); -} - -QByteArray SentCollectionAttribute::type() const -{ - static const QByteArray sType( "SentCollectionAttribute" ); - return sType; -} - -QByteArray SentCollectionAttribute::serialized() const -{ - return QByteArray::number( mId ); -} - -void SentCollectionAttribute::deserialize( const QByteArray &data ) -{ - // NOTE: We secretly know Akonadi::Collection::Id is qlonglong. - // Is there a way to make this type-agnostic? - bool ok; - mId = data.toLongLong( &ok ); - if( !ok ) { - kWarning() << "Could not deserialize" << data; - mId = -1; - } -} - -Collection::Id SentCollectionAttribute::sentCollection() const -{ - return mId; -} - -void SentCollectionAttribute::setSentCollection( Collection::Id id ) -{ - mId = id; -} - diff --git a/outboxinterface/tests/TODO b/outboxinterface/tests/TODO index 4d2821aeb..03dc5a0f3 100644 --- a/outboxinterface/tests/TODO +++ b/outboxinterface/tests/TODO @@ -1,8 +1,12 @@ LocalFolders: * LocalFolders should be able to recover from any of the following situations: - maildir resource and collection were removed - maildir resource was removed, but collections are still there - one or both collections have been removed MessageQueueJob: +- see source + +Attributes: +- add test for serialization and common mistakes such as forgetting to setDueDate diff --git a/outboxinterface/tests/messagequeuejobtest.cpp b/outboxinterface/tests/messagequeuejobtest.cpp index 169f5a993..c2d50cd18 100644 --- a/outboxinterface/tests/messagequeuejobtest.cpp +++ b/outboxinterface/tests/messagequeuejobtest.cpp @@ -1,198 +1,198 @@ /* Copyright 2009 Constantin Berzan 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 "messagequeuejobtest.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #define SPAM_ADDRESS ( QStringList() << "idanoka@gmail.com" ) using namespace Akonadi; using namespace KMime; using namespace MailTransport; using namespace OutboxInterface; void MessageQueueJobTest::initTestCase() { Control::start(); // HACK: Otherwise the MDA is not switched offline soon enough apparently... QTest::qWait( 1000 ); // Switch MDA offline to avoid spam. AgentInstance mda = AgentManager::self()->instance( "akonadi_maildispatcher_agent" ); QVERIFY( mda.isValid() ); mda.setIsOnline( false ); // check that outbox is empty LocalFolders::self()->fetch(); QTest::kWaitForSignal( LocalFolders::self(), SIGNAL( foldersReady() ) ); verifyOutboxContents( 0 ); } void MessageQueueJobTest::testAddressesFromMime() { // TODO } void MessageQueueJobTest::testValidMessages() { // check transport int tid = TransportManager::self()->defaultTransportId(); QVERIFY2( tid >= 0, "I need a default transport, but there is none." ); // send a valid message using the default transport MessageQueueJob *qjob = new MessageQueueJob; qjob->setTransportId( tid ); Message::Ptr msg = Message::Ptr( new Message ); msg->setContent( "\nThis is message #1 from the MessageQueueJobTest unit test.\n" ); qjob->setMessage( msg ); qjob->setTo( SPAM_ADDRESS ); verifyOutboxContents( 0 ); AKVERIFYEXEC( qjob ); // fetch the message and verify it QTest::qWait( 1000 ); verifyOutboxContents( 1 ); ItemFetchJob *fjob = new ItemFetchJob( LocalFolders::self()->outbox() ); fjob->fetchScope().fetchFullPayload(); fjob->fetchScope().fetchAllAttributes(); AKVERIFYEXEC( fjob ); QCOMPARE( fjob->items().count(), 1 ); Item item = fjob->items().first(); QVERIFY( !item.remoteId().isEmpty() ); // stored by the resource QVERIFY( item.hasPayload() ); AddressAttribute *addrA = item.attribute(); QVERIFY( addrA ); QVERIFY( addrA->from().isEmpty() ); QCOMPARE( addrA->to().count(), 1 ); QCOMPARE( addrA->to(), SPAM_ADDRESS ); QCOMPARE( addrA->cc().count(), 0 ); QCOMPARE( addrA->bcc().count(), 0 ); DispatchModeAttribute *dA = item.attribute(); QVERIFY( dA ); QCOMPARE( dA->dispatchMode(), DispatchModeAttribute::Immediately ); // default mode - SentCollectionAttribute *sA = item.attribute(); + SentBehaviourAttribute *sA = item.attribute(); QVERIFY( sA ); - QCOMPARE( sA->sentCollection(), LocalFolders::self()->sentMail().id() ); // default sent collection + QCOMPARE( sA->sentBehaviour(), SentBehaviourAttribute::MoveToDefaultSentCollection ); // default sent collection TransportAttribute *tA = item.attribute(); QVERIFY( tA ); QCOMPARE( tA->transportId(), tid ); ErrorAttribute *eA = item.attribute(); QVERIFY( !eA ); // no error QCOMPARE( item.flags().count(), 1 ); QVERIFY( item.flags().contains( "queued" ) ); // delete message, for further tests ItemDeleteJob *djob = new ItemDeleteJob( item ); AKVERIFYEXEC( djob ); verifyOutboxContents( 0 ); // TODO test with no To: but only BCC: // TODO test due-date sending // TODO test sending with custom sent-mail collections } void MessageQueueJobTest::testInvalidMessages() { MessageQueueJob *job = 0; Message::Ptr msg; // without message job = new MessageQueueJob; job->setTransportId( TransportManager::self()->defaultTransportId() ); job->setTo( SPAM_ADDRESS ); QVERIFY( !job->exec() ); // without recipients job = new MessageQueueJob; msg = Message::Ptr( new Message ); msg->setContent( "\nThis is a message sent from the MessageQueueJobTest unittest. This shouldn't have been sent.\n" ); job->setMessage( msg ); job->setTransportId( TransportManager::self()->defaultTransportId() ); QVERIFY( !job->exec() ); // without transport job = new MessageQueueJob; msg = Message::Ptr( new Message ); msg->setContent( "\nThis is a message sent from the MessageQueueJobTest unittest. This shouldn't have been sent.\n" ); job->setMessage( msg ); job->setTo( SPAM_ADDRESS ); QVERIFY( !job->exec() ); // with AfterDueDate and no due date job = new MessageQueueJob; msg = Message::Ptr( new Message ); msg->setContent( "\nThis is a message sent from the MessageQueueJobTest unittest. This shouldn't have been sent.\n" ); job->setMessage( msg ); job->setTo( SPAM_ADDRESS ); job->setDispatchMode( DispatchModeAttribute::AfterDueDate ); QVERIFY( !job->exec() ); - // with invalid sent-mail folder + // with MoveToCollection and no sent-mail folder job = new MessageQueueJob; msg = Message::Ptr( new Message ); msg->setContent( "\nThis is a message sent from the MessageQueueJobTest unittest. This shouldn't have been sent.\n" ); job->setMessage( msg ); job->setTo( SPAM_ADDRESS ); - job->setSentMailCollection( -1 ); + job->setSentBehaviour( SentBehaviourAttribute::MoveToCollection ); QVERIFY( !job->exec() ); } void MessageQueueJobTest::verifyOutboxContents( qlonglong count ) { QVERIFY( LocalFolders::self()->isReady() ); Collection outbox = LocalFolders::self()->outbox(); QVERIFY( outbox.isValid() ); CollectionStatisticsJob *job = new CollectionStatisticsJob( outbox ); AKVERIFYEXEC( job ); QCOMPARE( job->statistics().count(), count ); } QTEST_AKONADIMAIN( MessageQueueJobTest, NoGUI ) #include "messagequeuejobtest.moc"