diff --git a/outboxinterface/addressattribute.h b/outboxinterface/addressattribute.h index 8074f91d4..c9090d12f 100644 --- a/outboxinterface/addressattribute.h +++ b/outboxinterface/addressattribute.h @@ -1,79 +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_ADDRESSATTRIBUTE_H #define OUTBOXINTERFACE_ADDRESSATTRIBUTE_H #include #include #include -#include +#include namespace MailTransport { class Transport; } namespace OutboxInterface { /** * Attribute storing the From, To, CC, BCC addresses of a message. */ class OUTBOXINTERFACE_EXPORT AddressAttribute : public Akonadi::Attribute { public: explicit AddressAttribute( const QString &from = QString(), const QStringList &to = QStringList(), const QStringList &cc = QStringList(), const QStringList &bcc = QStringList() ); virtual ~AddressAttribute(); virtual AddressAttribute* clone() const; virtual QByteArray type() const; virtual QByteArray serialized() const; virtual void deserialize( const QByteArray &data ); QString from() const; void setFrom( const QString &from ); QStringList to() const; void setTo( const QStringList &to ); QStringList cc() const; void setCc( const QStringList &cc ); QStringList bcc() const; void setBcc( const QStringList &bcc ); private: QString mFrom; QStringList mTo; QStringList mCc; QStringList mBcc; }; } #endif diff --git a/outboxinterface/dispatchmodeattribute.h b/outboxinterface/dispatchmodeattribute.h index b1e0bab36..b0f72cd76 100644 --- a/outboxinterface/dispatchmodeattribute.h +++ b/outboxinterface/dispatchmodeattribute.h @@ -1,72 +1,72 @@ /* 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 +#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 ); QDateTime dueDate() const; void setDueDate( const QDateTime &date ); private: DispatchMode mMode; QDateTime mDueDate; }; } #endif diff --git a/outboxinterface/errorattribute.h b/outboxinterface/errorattribute.h index 4c14e62f7..6070a9e16 100644 --- a/outboxinterface/errorattribute.h +++ b/outboxinterface/errorattribute.h @@ -1,63 +1,63 @@ /* 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_ERRORATTRIBUTE_H #define OUTBOXINTERFACE_ERRORATTRIBUTE_H #include #include -#include +#include namespace OutboxInterface { /** * Attribute storing the status of a message in the outbox. */ class OUTBOXINTERFACE_EXPORT ErrorAttribute : public Akonadi::Attribute { public: ErrorAttribute( const QString &msg = QString() ); virtual ~ErrorAttribute(); virtual ErrorAttribute* clone() const; virtual QByteArray type() const; virtual QByteArray serialized() const; virtual void deserialize( const QByteArray &data ); /** Textual explanation of error. */ QString message() const; void setMessage( const QString &msg ); private: QString mMessage; }; } #endif diff --git a/outboxinterface/localfolders.cpp b/outboxinterface/localfolders.cpp index d925f593a..37d640107 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 "transportattribute.h" #include #include #include #include #include #include #include #include #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(); } 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 5aff476b5..9b50a7eb6 100644 --- a/outboxinterface/messagequeuejob.cpp +++ b/outboxinterface/messagequeuejob.cpp @@ -1,317 +1,317 @@ /* 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 +#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; started = false; } MessageQueueJob *const q; Message::Ptr message; int transport; DispatchModeAttribute::DispatchMode mode; QDateTime dueDate; bool useDefaultSentMail; Collection::Id sentMail; 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() ) { 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 ) { q->setError( UserDefinedError ); q->setErrorText( i18n( "Message has invalid sent-mail folder." ) ); q->emitResult(); return false; } return true; // all ok } void MessageQueueJob::Private::doStart() { if( !validate() ) { // The error has been set; the result has been emitted. return; } Q_ASSERT( !started ); started = true; // 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 ); 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 ) ) { } MessageQueueJob::~MessageQueueJob() { 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; } QDateTime MessageQueueJob::sendDueDate() const { if( d->mode != DispatchModeAttribute::AfterDueDate ) { kWarning() << "called when mode is not AfterDueDate"; } return d->dueDate; } Collection::Id MessageQueueJob::sentMailCollection() const { return d->sentMail; } 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; } void MessageQueueJob::setDueDate( const QDateTime &date ) { d->dueDate = date; } void MessageQueueJob::setSentMailCollection( Collection::Id id ) { d->useDefaultSentMail = false; d->sentMail = id; } 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 355c9b8ba..d9546a491 100644 --- a/outboxinterface/messagequeuejob.h +++ b/outboxinterface/messagequeuejob.h @@ -1,118 +1,118 @@ /* 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 #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; 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 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/sentcollectionattribute.h b/outboxinterface/sentcollectionattribute.h index 8b614a6b8..e95cff8c1 100644 --- a/outboxinterface/sentcollectionattribute.h +++ b/outboxinterface/sentcollectionattribute.h @@ -1,60 +1,60 @@ /* 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 #include -#include -#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 { public: explicit SentCollectionAttribute( Akonadi::Collection::Id id = -1 ); virtual ~SentCollectionAttribute(); virtual SentCollectionAttribute* 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 ); private: Akonadi::Collection::Id mId; }; } #endif diff --git a/outboxinterface/tests/foldersrequester.cpp b/outboxinterface/tests/foldersrequester.cpp index 32ab1d2f0..39f67ad19 100644 --- a/outboxinterface/tests/foldersrequester.cpp +++ b/outboxinterface/tests/foldersrequester.cpp @@ -1,69 +1,69 @@ /* 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 "foldersrequester.h" #include #include #include -#include -#include +#include +#include #include using namespace Akonadi; using namespace OutboxInterface; Requester::Requester() { Control::start(); connect( LocalFolders::self(), SIGNAL( foldersReady() ), this, SLOT( checkFolders() ) ); LocalFolders::self()->fetch(); } void Requester::checkFolders() { Collection outbox = LocalFolders::self()->outbox(); Collection sentMail = LocalFolders::self()->sentMail(); kDebug() << "Got outbox" << outbox.id() << "sent-mail" << sentMail.id(); if( !outbox.isValid() || !sentMail.isValid() ) { KApplication::exit( 1 ); } else { KApplication::exit( 2 ); } } int main( int argc, char **argv ) { KCmdLineArgs::init( argc, argv, "foldersrequester", 0, ki18n( "foldersrequester" ), "0", ki18n( "An app that requests LocalFolders" ) ); KApplication app; new Requester(); return app.exec(); } #include "foldersrequester.moc" diff --git a/outboxinterface/tests/messagequeuejobtest.cpp b/outboxinterface/tests/messagequeuejobtest.cpp index de1ba99c5..169f5a993 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 #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(); QVERIFY( sA ); QCOMPARE( sA->sentCollection(), LocalFolders::self()->sentMail().id() ); // 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 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 ); 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" diff --git a/outboxinterface/tests/queuer.cpp b/outboxinterface/tests/queuer.cpp index c17641884..5e01f643e 100644 --- a/outboxinterface/tests/queuer.cpp +++ b/outboxinterface/tests/queuer.cpp @@ -1,132 +1,132 @@ /* Copyright (c) 2006 - 2007 Volker Krause 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 "queuer.h" #include #include #include #include #include -#include +#include #include #include #include using namespace KMime; using namespace MailTransport; using namespace OutboxInterface; MessageQueuer::MessageQueuer() { if( !Akonadi::Control::start() ) { kFatal() << "Could not start Akonadi server."; } mComboBox = new TransportComboBox( this ); mComboBox->setEditable( true ); mSenderEdit = new KLineEdit( this ); mSenderEdit->setClickMessage( "Sender" ); mToEdit = new KLineEdit( this ); mToEdit->setText( "idanoka@gmail.com" ); mToEdit->setClickMessage( "To" ); mCcEdit = new KLineEdit( this ); mCcEdit->setClickMessage( "Cc" ); mBccEdit = new KLineEdit( this ); mBccEdit->setClickMessage( "Bcc" ); mMailEdit = new KTextEdit( this ); mMailEdit->setText( "test from queuer!" ); mMailEdit->setAcceptRichText( false ); mMailEdit->setLineWrapMode( QTextEdit::NoWrap ); QPushButton *b = new QPushButton( "&Send", this ); connect( b, SIGNAL(clicked(bool)), SLOT(sendBtnClicked()) ); } void MessageQueuer::sendBtnClicked() { Message::Ptr msg = Message::Ptr( new Message ); // No headers; need a '\n' to separate headers from body. // TODO: use real headers msg->setContent( QByteArray("\n") + mMailEdit->document()->toPlainText().toLatin1() ); kDebug() << "msg:" << msg->encodedContent( true ); MessageQueueJob *job = new MessageQueueJob(); job->setMessage( msg ); job->setTransportId( mComboBox->currentTransportId() ); // default dispatch mode // default sent-mail collection job->setFrom( mSenderEdit->text() ); job->setTo( mToEdit->text().isEmpty() ? QStringList() : mToEdit->text().split( ',' ) ); job->setCc( mCcEdit->text().isEmpty() ? QStringList() : mCcEdit->text().split( ',' ) ); job->setBcc( mBccEdit->text().isEmpty() ? QStringList() : mBccEdit->text().split( ',' ) ); connect( job, SIGNAL(result(KJob*)), SLOT(jobResult(KJob*)) ); connect( job, SIGNAL(percent(KJob*,unsigned long)), SLOT(jobPercent(KJob*,unsigned long)) ); connect( job, SIGNAL(infoMessage(KJob*,QString,QString)), SLOT(jobInfoMessage(KJob*,QString,QString)) ); kDebug() << "MessageQueueJob started."; job->start(); } int main( int argc, char **argv ) { KCmdLineArgs::init( argc, argv, "messagequeuer", 0, ki18n( "messagequeuer" ), "0", ki18n( "MessageQueuerJob Demo" ) ); KApplication app; MessageQueuer *t = new MessageQueuer(); t->show(); app.exec(); delete t; } void MessageQueuer::jobResult( KJob *job ) { if( job->error() ) { kDebug() << "job error:" << job->errorText(); } else { kDebug() << "job success."; } } void MessageQueuer::jobPercent( KJob *job, unsigned long percent ) { Q_UNUSED( job ); kDebug() << percent << "%"; } void MessageQueuer::jobInfoMessage( KJob *job, const QString &info, const QString &info2 ) { Q_UNUSED( job ); kDebug() << info; kDebug() << info2; } #include "queuer.moc" diff --git a/outboxinterface/tests/racetest.cpp b/outboxinterface/tests/racetest.cpp index 95a952eb4..a40d1d390 100644 --- a/outboxinterface/tests/racetest.cpp +++ b/outboxinterface/tests/racetest.cpp @@ -1,166 +1,166 @@ /* 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 "racetest.h" #include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include #include #define TIMEOUT_SECONDS 20 #define MAXCOUNT 10 // NOTE: REQUESTER_EXE is defined by cmake. using namespace Akonadi; using namespace OutboxInterface; void RaceTest::initTestCase() { QVERIFY( Control::start() ); QTest::qWait( 1000 ); // give the MDA time to start so that we can kill it in peace } void RaceTest::testMultipleProcesses_data() { QTest::addColumn( "count" ); // how many processes to create QTest::addColumn( "delay" ); // number of ms to wait before starting next process QTest::newRow( "1-nodelay" ) << 1 << 0; QTest::newRow( "2-nodelay" ) << 2 << 0; QTest::newRow( "5-nodelay" ) << 5 << 0; QTest::newRow( "10-nodelay" ) << 10 << 0; QTest::newRow( "2-shortdelay" ) << 2 << 100; QTest::newRow( "5-shortdelay" ) << 5 << 100; QTest::newRow( "10-shortdelay" ) << 10 << 100; QTest::newRow( "2-longdelay" ) << 2 << 1000; QTest::newRow( "5-longdelay" ) << 5 << 1000; QTest::newRow( "5-verylongdelay" ) << 5 << 4000; Q_ASSERT( 10 <= MAXCOUNT ); } void RaceTest::testMultipleProcesses() { QFETCH( int, count ); QFETCH( int, delay ); killZombies(); // Remove all maildir instances (at most 1 really) and MDAs (which use LocalFolders). // (This is to ensure that one of *our* instances is the main instance.) AgentType::List types; types.append( AgentManager::self()->type( "akonadi_maildir_resource" ) ); types.append( AgentManager::self()->type( "akonadi_maildispatcher_agent" ) ); AgentInstance::List instances = AgentManager::self()->instances(); foreach( const AgentInstance &instance, instances ) { if( types.contains( instance.type() ) ) { kDebug() << "Removing instance of type" << instance.type().identifier(); AgentManager::self()->removeInstance( instance ); QTest::kWaitForSignal( AgentManager::self(), SIGNAL( instanceRemoved( const Akonadi::AgentInstance& ) ) ); } } instances = AgentManager::self()->instances(); foreach( const AgentInstance &instance, instances ) { QVERIFY( !types.contains( instance.type() ) ); } QSignalSpy *errorSpy[ MAXCOUNT ]; QSignalSpy *finishedSpy[ MAXCOUNT ]; for( int i = 0; i < count; i++ ) { kDebug() << "Starting process" << i + 1 << "of" << count; KProcess *proc = new KProcess; procs.append( proc ); proc->setProgram( REQUESTER_EXE ); errorSpy[i] = new QSignalSpy( proc, SIGNAL( error( QProcess::ProcessError ) ) ); finishedSpy[i] = new QSignalSpy( proc, SIGNAL( finished( int, QProcess::ExitStatus ) ) ); proc->start(); QTest::qWait( delay ); } kDebug() << "Launched" << count << "processes."; int seconds = 0; int error, finished; while( true ) { seconds++; QTest::qWait( 1000 ); error = 0; finished = 0; for( int i = 0; i < count; i++ ) { if( errorSpy[i]->count() > 0 ) error++; if( finishedSpy[i]->count() > 0 ) finished++; } kDebug() << seconds << "seconds elapsed." << error << "processes error'd," << finished << "processes finished."; if( error + finished >= count ) break; #if 0 if( seconds >= TIMEOUT_SECONDS ) { kDebug() << "Timeout, gdb master!"; QTest::qWait( 1000*1000 ); } #endif QVERIFY2( seconds < TIMEOUT_SECONDS, "Timeout" ); } QCOMPARE( error, 0 ); QCOMPARE( finished, count ); for( int i = 0; i < count; i++ ) { kDebug() << "Checking exit status of process" << i + 1 << "of" << count; QCOMPARE( finishedSpy[i]->count(), 1 ); QList args = finishedSpy[i]->takeFirst(); QCOMPARE( args[0].toInt(), 2 ); } while( !procs.isEmpty() ) { KProcess *proc = procs.takeFirst(); QCOMPARE( proc->exitStatus(), QProcess::NormalExit ); QCOMPARE( proc->exitCode(), 2 ); delete proc; } QVERIFY( procs.isEmpty() ); } void RaceTest::killZombies() { while( !procs.isEmpty() ) { // These processes probably hung, and will never recover, so we need to kill them. // (This happens if the last test failed.) kDebug() << "Killing zombies from the past."; KProcess *proc = procs.takeFirst(); proc->kill(); proc->deleteLater(); } } QTEST_AKONADIMAIN( RaceTest, NoGUI ) #include "racetest.moc" diff --git a/outboxinterface/transportattribute.h b/outboxinterface/transportattribute.h index fcd8be7c5..bc2f65718 100644 --- a/outboxinterface/transportattribute.h +++ b/outboxinterface/transportattribute.h @@ -1,66 +1,66 @@ /* 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_TRANSPORTATTRIBUTE_H #define OUTBOXINTERFACE_TRANSPORTATTRIBUTE_H #include -#include +#include namespace MailTransport { class Transport; } namespace OutboxInterface { /** * Attribute determining which transport to use for sending a message. * @see mailtransport */ class OUTBOXINTERFACE_EXPORT TransportAttribute : public Akonadi::Attribute { public: TransportAttribute( int id = -1 ); virtual ~TransportAttribute(); virtual TransportAttribute* clone() const; virtual QByteArray type() const; virtual QByteArray serialized() const; virtual void deserialize( const QByteArray &data ); int transportId() const; MailTransport::Transport* transport() const; void setTransportId( int id ); private: int mId; }; } #endif