diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f05ea0ed..1d0647ee5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,166 +1,167 @@ project(kdepimlibs) # where to look first for cmake modules. This line must be the first one or cmake will use the system's FindFoo.cmake set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules") ############### Build Options ############### option(KDEPIM_ONLY_KLEO "Only build the libraries needed by Kleopatra." FALSE) ############### The kdepimlibs version (used e.g. in KdepimLibsConfig.cmake) ############### set(KDEPIMLIBS_VERSION_MAJOR 4) set(KDEPIMLIBS_VERSION_MINOR 2) set(KDEPIMLIBS_VERSION_PATCH 88) set(KDEPIMLIBS_VERSION ${KDEPIMLIBS_VERSION_MAJOR}.${KDEPIMLIBS_VERSION_MINOR}.${KDEPIMLIBS_VERSION_PATCH}) ############### search packages used by KDE ############### find_package(KDE4 4.2.85 REQUIRED) # see SVN rev. 952875/954493 include(KDE4Defaults) include(MacroLibrary) ############### Needed commands before building anything ############### add_definitions (${QT_DEFINITIONS} ${KDE4_DEFINITIONS}) include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${KDE4_INCLUDES}) ############### Find the stuff we need ############### set(Boost_MINIMUM_VERSION "1.33.1") find_package(Boost) macro_log_feature(Boost_FOUND "boost" "Boost C++ Libraries" "http://www.boost.org" TRUE "" "Required by several critical KDEPIM apps.") #FindGpgme.cmake already handles the log message but we must ensure it is required. find_package(Gpgme REQUIRED) # configure macros if (GPGME_FOUND) include (gpgme++/ConfigureChecks.cmake) endif (GPGME_FOUND) if (NOT KDEPIM_ONLY_KLEO) find_package(Akonadi 1.1.85) macro_log_feature(Akonadi_FOUND "Akonadi" "Akonadi server (from kdesupport)" "http://pim.kde.org/akonadi" TRUE "" "Akonadi is required to build KdepimLibs.") find_package(Sasl2) macro_log_feature(SASL2_FOUND "cyrus-sasl" "Cyrus SASL API" "http://asg.web.cmu.edu/sasl/sasl-library.html" TRUE "" "Required to support authentication of logins in the IMAP and Sieve kioslaves.") include (ConfigureChecks.cmake) endif (NOT KDEPIM_ONLY_KLEO) ############### Now, we add the KDEPIMLibs components ############### # These targets will always be built add_subdirectory(cmake) add_subdirectory(gpgme++) add_subdirectory(qgpgme) if (NOT KDEPIM_ONLY_KLEO) add_subdirectory(akonadi) add_subdirectory(kabc) add_subdirectory(kblog) add_subdirectory(kcal) add_subdirectory(kholidays) add_subdirectory(kimap) add_subdirectory(kioslave) add_subdirectory(kldap) add_subdirectory(kmime) add_subdirectory(kpimidentities) add_subdirectory(kpimutils) + add_subdirectory(kpimtextedit) add_subdirectory(kresources) add_subdirectory(ktnef) add_subdirectory(kxmlrpcclient) add_subdirectory(mailtransport) add_subdirectory(microblog) - add_subdirectory(kpimtextedit) + add_subdirectory(outboxinterface) add_subdirectory(syndication) # Build the CamelCase headers add_subdirectory(includes) endif (NOT KDEPIM_ONLY_KLEO) # doc must be a subdir of kdepimlibs macro_optional_add_subdirectory(doc) # All done, let's display what we found... macro_display_feature_log() # now create the KdepimLibsConfig.cmake file, which will be loaded by # kdelibs/cmake/modules/FindKdepimLibs.cmake and which has to contain all information # about the installed kdepimlibs anybody would like to have. Alex # we need the absolute directories where stuff will be installed too # but since the variables which contain the destinations can be relative # or absolute paths, we need this macro to make them all absoulte, Alex macro(MAKE_INSTALL_PATH_ABSOLUTE out in) if (IS_ABSOLUTE "${in}") # IS_ABSOLUTE is new since cmake 2.4.8 set(${out} "${in}") else (IS_ABSOLUTE "${in}") set(${out} "\${KDEPIMLIBS_INSTALL_DIR}/${in}") endif (IS_ABSOLUTE "${in}") endmacro(MAKE_INSTALL_PATH_ABSOLUTE out in) # all the following variables are put into KdepimLibsConfig.cmake, so # they are usable by projects using kdepimlibs. Alex make_install_path_absolute(KDEPIMLIBS_DATA_DIR ${DATA_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_DBUS_INTERFACES_DIR ${DBUS_INTERFACES_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_DBUS_SERVICES_DIR ${DBUS_SERVICES_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_INCLUDE_DIR ${INCLUDE_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_LIB_DIR ${LIB_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_BIN_DIR ${BIN_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_LIBEXEC_DIR ${LIBEXEC_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_SBIN_DIR ${SBIN_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_HTML_DIR ${HTML_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_CONFIG_DIR ${CONFIG_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_ICON_DIR ${ICON_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_KCFG_DIR ${KCFG_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_LOCALE_DIR ${LOCALE_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_MIME_DIR ${MIME_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_SOUND_DIR ${SOUND_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_TEMPLATES_DIR ${TEMPLATES_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_WALLPAPER_DIR ${WALLPAPER_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_KCONF_UPDATE_DIR ${KCONF_UPDATE_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_AUTOSTART_DIR ${AUTOSTART_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_XDG_APPS_DIR ${XDG_APPS_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_XDG_DIRECTORY_DIR ${XDG_DIRECTORY_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_SYSCONF_DIR ${SYSCONF_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_MAN_DIR ${MAN_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_INFO_DIR ${INFO_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_SERVICES_DIR ${SERVICES_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_SERVICETYPES_DIR ${SERVICETYPES_INSTALL_DIR}) # Used in configure_file() and install(EXPORT) set(KDEPIMLIBS_TARGET_PREFIX KDEPIMLibs__) # this file is installed and contains all necessary information about the installed kdepimlibs, it also loads the file with the exported targets configure_file(KdepimLibsConfig.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/KdepimLibsConfig.cmake" @ONLY) # this file will be installed too and will be used by cmake when searching for the Config.cmake file to check the version of kdepimlibs, Alex macro_write_basic_cmake_version_file(${CMAKE_CURRENT_BINARY_DIR}/KdepimLibsConfigVersion.cmake ${KDEPIMLIBS_VERSION_MAJOR} ${KDEPIMLIBS_VERSION_MINOR} ${KDEPIMLIBS_VERSION_PATCH}) set(_KdepimLibsConfig_INSTALL_DIR ${LIB_INSTALL_DIR}/KdepimLibs/cmake) # places where find_package() looks for FooConfig.cmake files: # CMake >= 2.6.0 looks in lib/Foo*/cmake/, CMake >= 2.6.3 also looks in # lib/cmake/Foo*/, which packagers prefer. So they can set the KDE4_USE_COMMON_CMAKE_PACKAGE_CONFIG_DIR # option to have kdepimlibs install its Config file there. Alex if(KDE4_USE_COMMON_CMAKE_PACKAGE_CONFIG_DIR) set(_KdepimLibsConfig_INSTALL_DIR ${LIB_INSTALL_DIR}/cmake/KdepimLibs) endif(KDE4_USE_COMMON_CMAKE_PACKAGE_CONFIG_DIR) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/KdepimLibsConfigVersion.cmake ${CMAKE_CURRENT_BINARY_DIR}/KdepimLibsConfig.cmake DESTINATION ${_KdepimLibsConfig_INSTALL_DIR} ) # Install the file with the exported targets, use ${KDEPIMLIBS_TARGET_PREFIX} as prefix for the names of these targets, Alex install(EXPORT kdepimlibsLibraryTargets NAMESPACE ${KDEPIMLIBS_TARGET_PREFIX} DESTINATION ${_KdepimLibsConfig_INSTALL_DIR} FILE KDEPimLibsLibraryTargetsWithPrefix.cmake ) # Install a KDEPimLibsDependencies.cmake so people using kdepimlibs 4.2 with kdelibs < 4.2 get a useful error message, Alex file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/KDEPimLibsDependencies.cmake "\n message(FATAL_ERROR \"For using this version of kdepimlibs (${KDEPIMLIBS_VERSION}) you need a newer version of kdelibs, please update.\")\n") install(FILES ${CMAKE_CURRENT_BINARY_DIR}/KDEPimLibsDependencies.cmake DESTINATION ${DATA_INSTALL_DIR}/cmake/modules) diff --git a/outboxinterface/CMakeLists.txt b/outboxinterface/CMakeLists.txt new file mode 100644 index 000000000..cc339d241 --- /dev/null +++ b/outboxinterface/CMakeLists.txt @@ -0,0 +1,45 @@ +include_directories (${CMAKE_SOURCE_DIR} ${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 + localfolders.cpp + messagequeuejob.cpp + + addressattribute.cpp + dispatchmodeattribute.cpp + errorattribute.cpp + sentcollectionattribute.cpp + transportattribute.cpp + + resourcetester/resourcesynchronizationjob.cpp +) + +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_AKONADI_LIBS} ${KDE4_KIO_LIBS} ${KDE4_MAILTRANSPORT_LIBS} ${KDE4_KMIME_LIBS} ) +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 + localfolders.h + messagequeuejob.h + + addressattribute.h + dispatchmodeattribute.h + errorattribute.h + sentcollectionattribute.h + transportattribute.h + + DESTINATION ${INCLUDE_INSTALL_DIR}/outboxinterface COMPONENT Devel) + diff --git a/outboxinterface/TODO b/outboxinterface/TODO new file mode 100644 index 000000000..455aac75a --- /dev/null +++ b/outboxinterface/TODO @@ -0,0 +1,9 @@ +* 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/addressattribute.cpp b/outboxinterface/addressattribute.cpp new file mode 100644 index 000000000..8b6b2f461 --- /dev/null +++ b/outboxinterface/addressattribute.cpp @@ -0,0 +1,113 @@ +/* + 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 "addressattribute.h" + +#include + +#include + +using namespace Akonadi; +using namespace OutboxInterface; + + +AddressAttribute::AddressAttribute( const QString &from, const QStringList &to, + const QStringList &cc, const QStringList &bcc ) + : mFrom( from ), mTo( to ), mCc( cc ), mBcc( bcc ) +{ +} + +AddressAttribute::~AddressAttribute() +{ +} + +AddressAttribute* AddressAttribute::clone() const +{ + return new AddressAttribute( mFrom, mTo, mCc, mBcc ); +} + +QByteArray AddressAttribute::type() const +{ + static const QByteArray sType( "AddressAttribute" ); + return sType; +} + +QByteArray AddressAttribute::serialized() const +{ + QByteArray serializedData; + QDataStream serializer( &serializedData, QIODevice::WriteOnly ); + serializer.setVersion( QDataStream::Qt_4_5 ); + serializer << mFrom; + serializer << mTo; + serializer << mCc; + serializer << mBcc; + return serializedData; +} + +void AddressAttribute::deserialize( const QByteArray &data ) +{ + QDataStream deserializer( data ); + // TODO: is this enough to make sure new versions won't trick us? + deserializer.setVersion( QDataStream::Qt_4_5 ); + deserializer >> mFrom; + deserializer >> mTo; + deserializer >> mCc; + deserializer >> mBcc; +} + +QString AddressAttribute::from() const +{ + return mFrom; +} + +void AddressAttribute::setFrom( const QString &from ) +{ + mFrom = from; +} + +QStringList AddressAttribute::to() const +{ + return mTo; +} + +void AddressAttribute::setTo( const QStringList &to ) +{ + mTo = to; +} + +QStringList AddressAttribute::cc() const +{ + return mCc; +} + +void AddressAttribute::setCc( const QStringList &cc ) +{ + mCc = cc; +} + +QStringList AddressAttribute::bcc() const +{ + return mBcc; +} + +void AddressAttribute::setBcc( const QStringList &bcc ) +{ + mBcc = bcc; +} + diff --git a/outboxinterface/addressattribute.h b/outboxinterface/addressattribute.h new file mode 100644 index 000000000..8074f91d4 --- /dev/null +++ b/outboxinterface/addressattribute.h @@ -0,0 +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 + + +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.cpp b/outboxinterface/dispatchmodeattribute.cpp new file mode 100644 index 000000000..8dbac3242 --- /dev/null +++ b/outboxinterface/dispatchmodeattribute.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 "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 +} + +void DispatchModeAttribute::deserialize( const QByteArray &data ) +{ + mDueDate = QDateTime(); + if ( data == "immediately" ) + mMode = Immediately; + else if ( data == "never" ) + mMode = Never; + 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 + 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 new file mode 100644 index 000000000..b1e0bab36 --- /dev/null +++ b/outboxinterface/dispatchmodeattribute.h @@ -0,0 +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 + + +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.cpp b/outboxinterface/errorattribute.cpp new file mode 100644 index 000000000..3e79b9c92 --- /dev/null +++ b/outboxinterface/errorattribute.cpp @@ -0,0 +1,69 @@ +/* + 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 "errorattribute.h" + +#include + +#include + +using namespace Akonadi; +using namespace OutboxInterface; + + +ErrorAttribute::ErrorAttribute( const QString &msg ) + : mMessage( msg ) +{ +} + +ErrorAttribute::~ErrorAttribute() +{ +} + +ErrorAttribute* ErrorAttribute::clone() const +{ + return new ErrorAttribute( mMessage ); +} + +QByteArray ErrorAttribute::type() const +{ + static const QByteArray sType( "ErrorAttribute" ); + return sType; +} + +QByteArray ErrorAttribute::serialized() const +{ + return mMessage.toLocal8Bit(); +} + +void ErrorAttribute::deserialize( const QByteArray &data ) +{ + mMessage = QString::fromLocal8Bit( data ); +} + +QString ErrorAttribute::message() const +{ + return mMessage; +} + +void ErrorAttribute::setMessage( const QString &msg ) +{ + mMessage = msg; +} + diff --git a/outboxinterface/errorattribute.h b/outboxinterface/errorattribute.h new file mode 100644 index 000000000..4c14e62f7 --- /dev/null +++ b/outboxinterface/errorattribute.h @@ -0,0 +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 + + +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 new file mode 100644 index 000000000..d925f593a --- /dev/null +++ b/outboxinterface/localfolders.cpp @@ -0,0 +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 // 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/localfolders.h b/outboxinterface/localfolders.h new file mode 100644 index 000000000..80b5eb4ec --- /dev/null +++ b/outboxinterface/localfolders.h @@ -0,0 +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_LOCALFOLDERS_H +#define OUTBOXINTERFACE_LOCALFOLDERS_H + +#include + +#include + +class KJob; + +namespace Akonadi { + class Collection; +} + + +namespace OutboxInterface { + + +class LocalFoldersPrivate; + + +/** + Creates and monitors the Outbox and Sent-Mail collections for the mail + dispatcher agent. + + The first time it is used, or when you call fetch(), this class checks for + the following: + * a maildir resource with name 'Local Mail Folders' + * 'outbox' and 'sent-mail' collections under that resource + If they do not exist, this class creates them, and then emits foldersReady(). + + Do not store the collections returned by outbox() and sentMail(). They can + become invalid if e.g. the user deletes them and LocalFolders creates them + again, so you should always use LocalFolders::outbox() instead of storing + its return value. + +*/ +class OUTBOXINTERFACE_EXPORT LocalFolders : public QObject +{ + Q_OBJECT + + public: + /** + Returns the LocalFolders instance. + Does a fetch() when first called. + */ + static LocalFolders *self(); + + /** + Begins creating / fetching the resource and collections. + Emits foldersReady() when done. + */ + void fetch(); + + /** + Returns whether the outbox and sent-mail collections have been + fetched and are ready to be used via outbox() and sentMail(). + */ + bool isReady() const; + + public Q_SLOTS: + /** + Returns the outbox collection. + */ + Akonadi::Collection outbox() const; + + /** + Returns the sent-mail collection. + */ + Akonadi::Collection sentMail() const; + + Q_SIGNALS: + /** + Emitted when the outbox and sent-mail collections have been fetched and + are ready to be used via outbox() and sentMail(). + */ + void foldersReady(); + + private: + friend class LocalFoldersPrivate; + + // singleton class; the only instance resides in sInstance->instance + LocalFolders( LocalFoldersPrivate *dd ); + + LocalFoldersPrivate *const d; + + Q_PRIVATE_SLOT( d, void prepare() ) + Q_PRIVATE_SLOT( d, void schedulePrepare() ) + Q_PRIVATE_SLOT( d, void resourceCreateResult( KJob * ) ) + Q_PRIVATE_SLOT( d, void resourceSyncResult( KJob * ) ) + Q_PRIVATE_SLOT( d, void collectionCreateResult( KJob * ) ) + Q_PRIVATE_SLOT( d, void collectionFetchResult( KJob * ) ) + +}; + + +} + + +#endif diff --git a/outboxinterface/messagequeuejob.cpp b/outboxinterface/messagequeuejob.cpp new file mode 100644 index 000000000..5aff476b5 --- /dev/null +++ b/outboxinterface/messagequeuejob.cpp @@ -0,0 +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 + + +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 new file mode 100644 index 000000000..355c9b8ba --- /dev/null +++ b/outboxinterface/messagequeuejob.h @@ -0,0 +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 + + +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/outboxinterface.kcfg b/outboxinterface/outboxinterface.kcfg new file mode 100644 index 000000000..281118bc4 --- /dev/null +++ b/outboxinterface/outboxinterface.kcfg @@ -0,0 +1,22 @@ + + + + + + + Id of the maildir resource containing the outbox and sent-mail collections. + "" + + + + -1 + + + + -1 + + + diff --git a/outboxinterface/outboxinterface_export.h b/outboxinterface/outboxinterface_export.h new file mode 100644 index 000000000..2bc35dcbc --- /dev/null +++ b/outboxinterface/outboxinterface_export.h @@ -0,0 +1,39 @@ +/* + 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. +*/ + + +// stolen from kdepimlibs/mailtransport/mailtransport_export.h + + +#ifndef OUTBOXINTERFACE_OUTBOXINTERFACE_EXPORT_H +#define OUTBOXINTERFACE_OUTBOXINTERFACE_EXPORT_H + +#include + +#ifndef OUTBOXINTERFACE_EXPORT +# if defined(MAKE_OUTBOXINTERFACE_LIB) + /* We are building this library */ +# define OUTBOXINTERFACE_EXPORT KDE_EXPORT +# else + /* We are using this library */ +# define OUTBOXINTERFACE_EXPORT KDE_IMPORT +# endif +#endif + +#endif diff --git a/outboxinterface/sentcollectionattribute.cpp b/outboxinterface/sentcollectionattribute.cpp new file mode 100644 index 000000000..4dde0cea0 --- /dev/null +++ b/outboxinterface/sentcollectionattribute.cpp @@ -0,0 +1,76 @@ +/* + 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/sentcollectionattribute.h b/outboxinterface/sentcollectionattribute.h new file mode 100644 index 000000000..8b614a6b8 --- /dev/null +++ b/outboxinterface/sentcollectionattribute.h @@ -0,0 +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 + + +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/settings.kcfgc b/outboxinterface/settings.kcfgc new file mode 100644 index 000000000..e7a2caddd --- /dev/null +++ b/outboxinterface/settings.kcfgc @@ -0,0 +1,7 @@ +File=outboxinterface.kcfg +ClassName=Settings +NameSpace=OutboxInterface +Singleton=true +ItemAccessors=true +Mutators=true +SetUserTextx=true diff --git a/outboxinterface/tests/CMakeLists.txt b/outboxinterface/tests/CMakeLists.txt new file mode 100644 index 000000000..fdb021207 --- /dev/null +++ b/outboxinterface/tests/CMakeLists.txt @@ -0,0 +1,56 @@ +set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) + + +set(queuer_srcs queuer.cpp) +kde4_add_executable(queuer TEST ${queuer_srcs}) +target_link_libraries(queuer ${KDE4_KDEUI_LIBS} ${KDE4_MAILTRANSPORT_LIBS} ${KDE4_KMIME_LIBS} outboxinterface) + + +set( requester_srcs foldersrequester.cpp ) +kde4_add_executable( requester TEST ${requester_srcs} ) +target_link_libraries( requester ${KDE4_KDEUI_LIBS} outboxinterface ) +# for racetest +add_definitions( -DREQUESTER_EXE=\\"${CMAKE_CURRENT_BINARY_DIR}/requester\\" ) + + + +# Stolen from kdepimlibs/akonadi/tests +macro(add_akonadi_isolated_test _source) + get_filename_component(_targetName ${_source} NAME_WE) + set(_srcList ${_source} ) + + kde4_add_executable(${_targetName} TEST ${_srcList}) + target_link_libraries(${_targetName} + ${QT_QTTEST_LIBRARY} + ${QT_QTGUI_LIBRARY} + ${KDE4_AKONADI_LIBS} + ${KDE4_KDECORE_LIBS} + ${KDE4_MAILTRANSPORT_LIBS} + ${KDE4_KMIME_LIBS} + ${QT_QTCORE_LIBRARY} + ${QT_QTDBUS_LIBRARY} + outboxinterface + ) + + # based on kde4_add_unit_test + if (WIN32) + get_target_property( _loc ${_targetName} LOCATION ) + set(_executable ${_loc}.bat) + else (WIN32) + set(_executable ${EXECUTABLE_OUTPUT_PATH}/${_targetName}) + endif (WIN32) + if (UNIX) + set(_executable ${_executable}.shell) + endif (UNIX) + + find_program(_testrunner akonaditest) + + add_test( outboxinterface-${_targetName} ${_testrunner} -c ${CMAKE_CURRENT_SOURCE_DIR}/unittestenv/config.xml ${_executable} ) +endmacro(add_akonadi_isolated_test) + + + + +add_akonadi_isolated_test( messagequeuejobtest.cpp ) +add_akonadi_isolated_test( racetest.cpp ) + diff --git a/outboxinterface/tests/TODO b/outboxinterface/tests/TODO new file mode 100644 index 000000000..4d2821aeb --- /dev/null +++ b/outboxinterface/tests/TODO @@ -0,0 +1,8 @@ +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: + diff --git a/outboxinterface/tests/foldersrequester.cpp b/outboxinterface/tests/foldersrequester.cpp new file mode 100644 index 000000000..32ab1d2f0 --- /dev/null +++ b/outboxinterface/tests/foldersrequester.cpp @@ -0,0 +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 + +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/foldersrequester.h b/outboxinterface/tests/foldersrequester.h new file mode 100644 index 000000000..cee1c7bc2 --- /dev/null +++ b/outboxinterface/tests/foldersrequester.h @@ -0,0 +1,46 @@ +/* + 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 FOLDERSREQUESTER_H +#define FOLDERSREQUESTER_H + +#include + +/** + This class requests the LocalFolders, then exits the app with a status of 2 + if they were delivered OK, or 1 if they were not. + + NOTE: The non-standard exit status 2 in case of success is to make feel more + comfortable than checking for zero (I actually had a bug causing it to always + return zero). +*/ +class Requester : public QObject +{ + Q_OBJECT + + public: + Requester(); + + private slots: + void checkFolders(); + +}; + + +#endif diff --git a/outboxinterface/tests/messagequeuejobtest.cpp b/outboxinterface/tests/messagequeuejobtest.cpp new file mode 100644 index 000000000..de1ba99c5 --- /dev/null +++ b/outboxinterface/tests/messagequeuejobtest.cpp @@ -0,0 +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 + +#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/messagequeuejobtest.h b/outboxinterface/tests/messagequeuejobtest.h new file mode 100644 index 000000000..23ca4cf35 --- /dev/null +++ b/outboxinterface/tests/messagequeuejobtest.h @@ -0,0 +1,47 @@ +/* + 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 MESSAGEQUEUEJOBTEST_H +#define MESSAGEQUEUEJOBTEST_H + +#include + + +/** + This tests the ability to queue messages (MessageQueueJob class). + Note that the actual sending of messages is the MDA's job, and is not tested + here. + */ +class MessageQueueJobTest : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase(); + void testAddressesFromMime(); + void testValidMessages(); + void testInvalidMessages(); + + private: + void verifyOutboxContents( qlonglong count ); + +}; + + +#endif diff --git a/outboxinterface/tests/queuer.cpp b/outboxinterface/tests/queuer.cpp new file mode 100644 index 000000000..c17641884 --- /dev/null +++ b/outboxinterface/tests/queuer.cpp @@ -0,0 +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 + + +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/queuer.h b/outboxinterface/tests/queuer.h new file mode 100644 index 000000000..840592412 --- /dev/null +++ b/outboxinterface/tests/queuer.h @@ -0,0 +1,56 @@ +/* + 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. +*/ + +#ifndef MESSAGEQUEUER_H +#define MESSAGEQUEUER_H + +#include +#include + +class KJob; +class KLineEdit; +class KTextEdit; + + +/** + This is stolen from kdepimlibs/mailtransport/tests/transportmgr.{h,cpp} +*/ +class MessageQueuer : public KVBox +{ + Q_OBJECT + + public: + MessageQueuer(); + + private slots: + void sendBtnClicked(); + void jobResult( KJob *job ); + void jobPercent( KJob *job, unsigned long percent ); + void jobInfoMessage( KJob *job, const QString &info, const QString &info2 ); + + private: + MailTransport::TransportComboBox *mComboBox; + KLineEdit *mSenderEdit, *mToEdit, *mCcEdit, *mBccEdit; + KTextEdit *mMailEdit; + +}; + + +#endif diff --git a/outboxinterface/tests/racetest.cpp b/outboxinterface/tests/racetest.cpp new file mode 100644 index 000000000..95a952eb4 --- /dev/null +++ b/outboxinterface/tests/racetest.cpp @@ -0,0 +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 + +#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/tests/racetest.h b/outboxinterface/tests/racetest.h new file mode 100644 index 000000000..b075af4b0 --- /dev/null +++ b/outboxinterface/tests/racetest.h @@ -0,0 +1,51 @@ +/* + 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 RACETEST_H +#define RACETEST_H + +#include +#include + +class KProcess; + + +/** + This tests the ability of LocalFolders to exist peacefully in multiple processes. + The main instance (normally the first one created) is supposed to create the + resource and collections, while the other instances are supposed to wait and + then just fetch the collections. + */ +class RaceTest : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase(); + void testMultipleProcesses_data(); + void testMultipleProcesses(); + void killZombies(); + + private: + QList procs; + +}; + + +#endif diff --git a/outboxinterface/tests/unittestenv/config.xml b/outboxinterface/tests/unittestenv/config.xml new file mode 100644 index 000000000..47d7cb0b8 --- /dev/null +++ b/outboxinterface/tests/unittestenv/config.xml @@ -0,0 +1,5 @@ + + kdehome + xdgconfig + xdglocal + diff --git a/outboxinterface/tests/unittestenv/kdehome/share/config/akonadi-firstrunrc b/outboxinterface/tests/unittestenv/kdehome/share/config/akonadi-firstrunrc new file mode 100644 index 000000000..1cac492a3 --- /dev/null +++ b/outboxinterface/tests/unittestenv/kdehome/share/config/akonadi-firstrunrc @@ -0,0 +1,3 @@ +[ProcessedDefaults] +defaultaddressbook=done +defaultcalendar=done diff --git a/outboxinterface/tests/unittestenv/kdehome/share/config/kdebugrc b/outboxinterface/tests/unittestenv/kdehome/share/config/kdebugrc new file mode 100644 index 000000000..32317f745 --- /dev/null +++ b/outboxinterface/tests/unittestenv/kdehome/share/config/kdebugrc @@ -0,0 +1,110 @@ +[0] +AbortFatal=true +ErrorFilename[$e]=kdebug.dbg +ErrorOutput=2 +FatalFilename[$e]=kdebug.dbg +FatalOutput=2 +InfoFilename[$e]=kdebug.dbg +InfoOutput=2 +WarnFilename[$e]=kdebug.dbg +WarnOutput=2 + +[5250] +InfoOutput=2 + +[5251] +InfoOutput=2 + +[5252] +InfoOutput=2 + +[5253] +InfoOutput=2 + +[5254] +AbortFatal=true +ErrorFilename[$e]=kdebug.dbg +ErrorOutput=2 +FatalFilename[$e]=kdebug.dbg +FatalOutput=2 +InfoFilename[$e]=kdebug.dbg +InfoOutput=2 +WarnFilename[$e]=kdebug.dbg +WarnOutput=2 + +[5255] +InfoOutput=2 + +[5256] +InfoOutput=2 + +[5257] +InfoOutput=2 + +[5258] +InfoOutput=2 + +[5259] +InfoOutput=2 + +[5260] +InfoOutput=2 + +[5261] +InfoOutput=2 + +[5262] +InfoOutput=2 + +[5263] +InfoOutput=2 + +[5264] +InfoOutput=2 + +[5265] +AbortFatal=true +ErrorFilename[$e]=kdebug.dbg +ErrorOutput=2 +FatalFilename[$e]=kdebug.dbg +FatalOutput=2 +InfoFilename[$e]=kdebug.dbg +InfoOutput=2 +WarnFilename[$e]=kdebug.dbg +WarnOutput=2 + +[5266] +AbortFatal=true +ErrorFilename[$e]=kdebug.dbg +ErrorOutput=2 +FatalFilename[$e]=kdebug.dbg +FatalOutput=2 +InfoFilename[$e]=kdebug.dbg +InfoOutput=2 +WarnFilename[$e]=kdebug.dbg +WarnOutput=2 + +[5295] +AbortFatal=true +ErrorFilename[$e]=kdebug.dbg +ErrorOutput=2 +FatalFilename[$e]=kdebug.dbg +FatalOutput=2 +InfoFilename[$e]=kdebug.dbg +InfoOutput=2 +WarnFilename[$e]=kdebug.dbg +WarnOutput=2 + +[5324] +AbortFatal=true +ErrorFilename[$e]=kdebug.dbg +ErrorOutput=2 +FatalFilename[$e]=kdebug.dbg +FatalOutput=2 +InfoFilename[$e]=kdebug.dbg +InfoOutput=2 +WarnFilename[$e]=kdebug.dbg +WarnOutput=2 + +[7129] +InfoOutput=2 diff --git a/outboxinterface/tests/unittestenv/kdehome/share/config/kwalletrc b/outboxinterface/tests/unittestenv/kdehome/share/config/kwalletrc new file mode 100644 index 000000000..8ba29ca12 --- /dev/null +++ b/outboxinterface/tests/unittestenv/kdehome/share/config/kwalletrc @@ -0,0 +1,2 @@ +[Wallet] +Enabled=false diff --git a/outboxinterface/tests/unittestenv/kdehome/share/config/mailtransports b/outboxinterface/tests/unittestenv/kdehome/share/config/mailtransports new file mode 100644 index 000000000..bc9c63f07 --- /dev/null +++ b/outboxinterface/tests/unittestenv/kdehome/share/config/mailtransports @@ -0,0 +1,17 @@ +[$Version] +update_info=mailtransports.upd:initial-kmail-migration,mailtransports.upd:initial-knode-migration + +[General] +default-transport=549190884 + +[Transport 549190884] +auth=true +encryption=SSL +host=smtp.gmail.com +id=549190884 +name=idanoka2-stored +password=ᄒᄡᄚᄆᄒᄏᄊᆱᄎᆲᆱ +port=465 +storepass=true +user=idanoka2@gmail.com + diff --git a/outboxinterface/tests/unittestenv/kdehome/share/config/qttestrc b/outboxinterface/tests/unittestenv/kdehome/share/config/qttestrc new file mode 100644 index 000000000..2e2f28ea1 --- /dev/null +++ b/outboxinterface/tests/unittestenv/kdehome/share/config/qttestrc @@ -0,0 +1,2 @@ +[Notification Messages] +WalletMigrate=false diff --git a/outboxinterface/tests/unittestenv/xdgconfig/akonadi/akonadiserverrc b/outboxinterface/tests/unittestenv/xdgconfig/akonadi/akonadiserverrc new file mode 100644 index 000000000..7f738ce21 --- /dev/null +++ b/outboxinterface/tests/unittestenv/xdgconfig/akonadi/akonadiserverrc @@ -0,0 +1,4 @@ +[%General] + +[Search] +Manager=Dummy diff --git a/outboxinterface/transportattribute.cpp b/outboxinterface/transportattribute.cpp new file mode 100644 index 000000000..78ded7435 --- /dev/null +++ b/outboxinterface/transportattribute.cpp @@ -0,0 +1,75 @@ +/* + 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 "transportattribute.h" + +#include + +#include "mailtransport/transportmanager.h" + +using namespace Akonadi; +using namespace MailTransport; +using namespace OutboxInterface; + + +TransportAttribute::TransportAttribute( int id ) + : mId( id ) +{ +} + +TransportAttribute::~TransportAttribute() +{ +} + +TransportAttribute* TransportAttribute::clone() const +{ + return new TransportAttribute( mId ); +} + +QByteArray TransportAttribute::type() const +{ + static const QByteArray sType( "TransportAttribute" ); + return sType; +} + +QByteArray TransportAttribute::serialized() const +{ + return QByteArray::number( mId ); +} + +void TransportAttribute::deserialize( const QByteArray &data ) +{ + mId = data.toInt(); +} + +int TransportAttribute::transportId() const +{ + return mId; +} + +Transport* TransportAttribute::transport() const +{ + return TransportManager::self()->transportById( mId, false ); +} + +void TransportAttribute::setTransportId( int id ) +{ + mId = id; +} + diff --git a/outboxinterface/transportattribute.h b/outboxinterface/transportattribute.h new file mode 100644 index 000000000..fcd8be7c5 --- /dev/null +++ b/outboxinterface/transportattribute.h @@ -0,0 +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 + + +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