--- a/qtmobility/src/messaging/modestengine_maemo.cpp Fri Apr 16 15:51:22 2010 +0300
+++ b/qtmobility/src/messaging/modestengine_maemo.cpp Mon May 03 13:18:40 2010 +0300
@@ -40,19 +40,162 @@
****************************************************************************/
#include "modestengine_maemo_p.h"
#include "maemohelpers_p.h"
+#include "qmessage_p.h"
#include "qmessageaccount.h"
#include "qmessageaccount_p.h"
#include "qmessageaccountfilter.h"
#include "qmessageaccountfilter_p.h"
-#include "qmessageservice.h"
-#include <libmodest-dbus-client/libmodest-dbus-client.h>
-#include <libmodest-dbus-client/libmodest-dbus-api.h>
+#include "qmessagefolder_p.h"
+#include "qmessagestore_p.h"
+#include "qmessageservice_maemo_p.h"
+#include "qmessagecontentcontainer_maemo_p.h"
#include <QUrl>
-#include <QtDBus/QtDBus>
+#include <QtDBus>
+#include <QFileSystemWatcher>
+#include <QTimer>
+
#include <dbus/dbus.h>
-
#include <QDebug>
+#include </usr/include/libmodest-dbus-client-1.0/libmodest-dbus-client/libmodest-dbus-api.h>
+#include </usr/include/sys/inotify.h>
+#include </usr/include/sys/ioctl.h>
+#include </usr/include/sys/fcntl.h>
+
+// Marshall the ModestStringMap data into a D-Bus argument
+QDBusArgument &operator<<(QDBusArgument &argument,
+ const QtMobility::ModestStringMap &map)
+{
+ QtMobility::ModestStringMap::const_iterator iter;
+
+ argument.beginMap (QVariant::String, QVariant::String);
+ for (iter = map.constBegin(); iter != map.constEnd(); iter++) {
+ argument.beginMapEntry();
+ argument << iter.key() << iter.value();
+ argument.endMapEntry();
+ }
+ argument.endMap();
+
+ return argument;
+}
+
+// Retrieve the ModestStringMap data from the D-Bus argument
+const QDBusArgument &operator>>(const QDBusArgument &argument,
+ QtMobility::ModestStringMap &map)
+{
+ map.clear();
+
+ argument.beginMap();
+ while (!argument.atEnd()) {
+ QString key, value;
+ argument.beginMapEntry();
+ argument >> key >> value;
+ argument.endMapEntry();
+ map[key] = value;
+ }
+ argument.endMap();
+
+ return argument;
+}
+
+QDBusArgument &operator<<(QDBusArgument &argument,
+ const QtMobility::ModestUnreadMessageDBusStruct &unreadMessage)
+{
+ argument.beginStructure();
+ argument << unreadMessage.timeStamp;
+ argument << unreadMessage.subject;
+ argument.endStructure();
+ return argument;
+}
+
+const QDBusArgument &operator>>(const QDBusArgument &argument,
+ QtMobility::ModestUnreadMessageDBusStruct &unreadMessage)
+{
+ argument.beginStructure();
+ argument >> unreadMessage.timeStamp;
+ argument >> unreadMessage.subject;
+ argument.endStructure();
+ return argument;
+}
+
+QDBusArgument &operator<<(QDBusArgument &argument,
+ const QtMobility::ModestAccountsUnreadMessagesDBusStruct &unreadMessages)
+{
+ argument.beginStructure();
+ argument << unreadMessages.accountId;
+ argument << unreadMessages.accountName;
+ argument << unreadMessages.accountProtocol;
+ argument << unreadMessages.unreadCount;
+ argument << unreadMessages.unreadMessages;
+ argument.endStructure();
+ return argument;
+}
+
+const QDBusArgument &operator>>(const QDBusArgument &argument,
+ QtMobility::ModestAccountsUnreadMessagesDBusStruct &unreadMessages)
+{
+ argument.beginStructure();
+ argument >> unreadMessages.accountId;
+ argument >> unreadMessages.accountName;
+ argument >> unreadMessages.accountProtocol;
+ argument >> unreadMessages.unreadCount;
+ argument >> unreadMessages.unreadMessages;
+ argument.endStructure();
+ return argument;
+}
+
+QDBusArgument &operator<<(QDBusArgument &argument, const QtMobility::ModestMessage &message)
+{
+ argument.beginStructure();
+ argument << message.id;
+ argument << message.subject;
+ argument << message.folder;
+ argument << message.sender;
+ argument << message.size;
+ argument << message.hasAttachment;
+ argument << message.isUnread;
+ argument << message.timeStamp;
+ argument.endStructure();
+ return argument;
+}
+
+const QDBusArgument &operator>>(const QDBusArgument &argument, QtMobility::ModestMessage &message)
+{
+ argument.beginStructure();
+ argument >> message.id;
+ argument >> message.subject;
+ argument >> message.folder;
+ argument >> message.sender;
+ argument >> message.size;
+ argument >> message.hasAttachment;
+ argument >> message.isUnread;
+ argument >> message.timeStamp;
+ argument.endStructure();
+ return argument;
+}
+
+QDBusArgument &operator<<(QDBusArgument &argument, const QtMobility::MessagingModestMimePart &mimePart)
+{
+ argument.beginStructure();
+ argument << mimePart.mimeType;
+ argument << mimePart.isAttachment;
+ argument << mimePart.fileName;
+ argument << mimePart.contentId;
+ argument.endStructure();
+ return argument;
+}
+
+const QDBusArgument &operator>>(const QDBusArgument &argument, QtMobility::MessagingModestMimePart &mimePart)
+{
+ argument.beginStructure();
+ argument >> mimePart.mimeType;
+ argument >> mimePart.isAttachment;
+ argument >> mimePart.fileName;
+ argument >> mimePart.contentId;
+ argument.endStructure();
+ return argument;
+}
+
QTM_BEGIN_NAMESPACE
/* configuration key definitions for modest */
@@ -65,10 +208,32 @@
#define MODESTENGINE_ACCOUNT_EMAIL "email"
#define MODESTENGINE_ACCOUNT_STORE_ACCOUNT "store_account"
#define MODESTENGINE_ACCOUNT_TRANSPORT_ACCOUNT "transport_account"
+#define MODESTENGINE_ACCOUNT_PROTOCOL "proto"
+#define MODESTENGINE_ACCOUNT_USERNAME "username"
+#define MODESTENGINE_ACCOUNT_HOSTNAME "hostname"
+#define MODESTENGINE_ACCOUNT_PORT "port"
+
+// The modest engine has a new plugin, we need service names for it
+#define MODESTENGINE_QTM_PLUGIN_PATH "/com/nokia/Qtm/Modest/Plugin"
+#define MODESTENGINE_QTM_PLUGIN_NAME "com.nokia.Qtm.Modest.Plugin"
+
+typedef enum {
+ MODEST_DBUS_SEARCH_SUBJECT = (1 << 0),
+ MODEST_DBUS_SEARCH_SENDER = (1 << 1),
+ MODEST_DBUS_SEARCH_RECIPIENT = (1 << 2),
+ MODEST_DBUS_SEARCH_SIZE = (1 << 3),
+ MODEST_DBUS_SEARCH_BODY = (1 << 6)
+} ModestDBusSearchFlags;
+
+// Specific priority settings to translate to modest priorities
+#define MODESTENGINE_HIGH_PRIORITY 2
+#define MODESTENGINE_NORMAL_PRIORITY 0
+#define MODESTENGINE_LOW_PRIORITY 1
Q_GLOBAL_STATIC(ModestEngine,modestEngine);
ModestEngine::ModestEngine()
+ : m_queryIds(0)
{
g_type_init();
m_gconfclient = gconf_client_get_default();
@@ -77,6 +242,62 @@
} else {
updateEmailAccounts();
}
+
+ // Setup DBus Interface for Modest
+ m_ModestDBusInterface = new QDBusInterface(MODEST_DBUS_SERVICE,
+ MODEST_DBUS_OBJECT,
+ MODEST_DBUS_IFACE,
+ QDBusConnection::sessionBus(),
+ this);
+
+ // Get notifications of Incoming Messages
+ m_ModestDBusInterface->connection().connect(MODEST_DBUS_SERVICE,
+ MODEST_DBUS_OBJECT,
+ MODEST_DBUS_IFACE,
+ MODEST_DBUS_SIGNAL_FOLDER_UPDATED,
+ this, SLOT(folderUpdatedSlot(QDBusMessage)));
+
+ // Get notifications of message Read/Unread state changes
+ m_ModestDBusInterface->connection().connect(MODEST_DBUS_SERVICE,
+ MODEST_DBUS_OBJECT,
+ MODEST_DBUS_IFACE,
+ MODEST_DBUS_SIGNAL_MSG_READ_CHANGED,
+ this, SLOT(messageReadChangedSlot(QDBusMessage)));
+
+ // Setup Qt Mobility Modest Plugin based DBus Interface for Modest
+ m_QtmPluginDBusInterface = new QDBusInterface(MODESTENGINE_QTM_PLUGIN_NAME,
+ MODESTENGINE_QTM_PLUGIN_PATH,
+ MODESTENGINE_QTM_PLUGIN_NAME,
+ QDBusConnection::sessionBus(),
+ this);
+
+ qDBusRegisterMetaType< ModestStringMap >();
+ qDBusRegisterMetaType< ModestStringMapList >();
+
+ qRegisterMetaType<INotifyWatcher::FileNotification>();
+
+ qRegisterMetaType<ModestUnreadMessageDBusStruct>();
+ qRegisterMetaType<ModestAccountsUnreadMessagesDBusStruct>();
+ qDBusRegisterMetaType<ModestMessage>();
+
+ qRegisterMetaType<MessagingModestMimePart>();
+
+
+ connect(&m_MailFoldersWatcher, SIGNAL(fileChanged(int, QString, uint)),
+ this, SLOT(fileChangedSlot(int, QString, uint)));
+
+ watchAllKnownEmailFolders();
+
+ // Get latest messages from each account
+ // => This ensures that notifications of all incoming messages will be sent
+ int messagesPerAccount = 1;
+ QDBusPendingCall pendingCall = m_ModestDBusInterface->asyncCall(MODEST_DBUS_METHOD_GET_UNREAD_MESSAGES,
+ messagesPerAccount);
+ QDBusPendingCallWatcher* pendingCallWatcher = new QDBusPendingCallWatcher(pendingCall);
+ pendingCallWatcher->setProperty("setOnlyDates", true);
+ connect(pendingCallWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
+ this, SLOT(pendingGetUnreadMessagesFinishedSlot(QDBusPendingCallWatcher*)));
+
}
ModestEngine::~ModestEngine()
@@ -90,6 +311,51 @@
return modestEngine();
}
+MessagingModestMessage ModestEngine::messageFromModest(const QString& accountId, const QString &folderId, const QString& messageId) const
+{
+ MessagingModestMessage modestMessage;
+
+ QDBusPendingCall pendingCall = m_QtmPluginDBusInterface->asyncCall("GetMessage",
+ accountId,
+ folderId,
+ messageId);
+ QDBusPendingCallWatcher pendingCallWatcher(pendingCall);
+ pendingCallWatcher.waitForFinished();
+
+ QDBusMessage msg = pendingCallWatcher.reply();
+
+ if (msg.type() == QDBusMessage::ReplyMessage) {
+ modestMessage.id = messageId;
+ modestMessage.accountId = accountId;
+ modestMessage.folderId = folderId;
+ modestMessage.url = msg.arguments()[0].toString();
+ modestMessage.mimeType = msg.arguments()[1].toString();
+ modestMessage.from = msg.arguments()[2].toString();
+ modestMessage.to = msg.arguments()[3].toString();
+ modestMessage.cc = msg.arguments()[4].toString();
+ modestMessage.bcc = msg.arguments()[5].toString();
+ modestMessage.replyTo = msg.arguments()[6].toString();
+ modestMessage.subject = msg.arguments()[7].toString();
+ modestMessage.dateReceived = msg.arguments()[8].toLongLong();
+ modestMessage.dateSent = msg.arguments()[9].toLongLong();
+ modestMessage.size = msg.arguments()[10].toLongLong();
+ modestMessage.flags = static_cast<MessagingModestMessageFlags>(msg.arguments()[11].toUInt());
+ modestMessage.priority = static_cast<MessagingModestMessagePriority>(msg.arguments()[12].toUInt());
+
+ QVariant variant = msg.arguments()[13];
+ QDBusArgument argument = variant.value<QDBusArgument>();
+ argument >> modestMessage.mimeParts;
+ } else {
+ modestMessage.dateReceived = 0;
+ modestMessage.dateSent = 0;
+ modestMessage.size = 0;
+ modestMessage.flags = MessagingModestMessageNotDefined;
+ modestMessage.priority = MessagingModestMessagePriorityDefined;
+ }
+
+ return modestMessage;
+}
+
void ModestEngine::updateEmailAccounts() const
{
iDefaultEmailAccountId = QMessageAccountId();
@@ -105,12 +371,14 @@
#endif
g_error_free(error);
} else {
- gchar *default_account = gconf_client_get_string(m_gconfclient, MODESTENGINE_DEFAULT_ACCOUNT, &error);
+ gchar *default_account_id = gconf_client_get_string(m_gconfclient, MODESTENGINE_DEFAULT_ACCOUNT, &error);
if (error) {
qWarning("qtmessaging: failed to get '%s': %s", MODESTENGINE_DEFAULT_ACCOUNT, error->message);
g_error_free(error);
}
+ const size_t prefix_len = strlen(MODESTENGINE_ACCOUNT_NAMESPACE) + 1;
+
GSList *iter = accounts;
while (iter) {
if (!(iter->data)) {
@@ -118,61 +386,83 @@
continue;
}
- const gchar* account_name_key = (const gchar*)iter->data;
+ const gchar* account_key = (const gchar*)iter->data;
+ // account_key = /apps/modest/server_accounts/<account id>
+ // => take account id from account_key & unescape account id
+ gchar* unescaped_account_id = gconf_unescape_key(account_key+prefix_len, strlen(account_key)-prefix_len);
gboolean account_ok = FALSE;
// Check if account is enabled
- if (account_name_key) {
- gchar* key = g_strconcat(account_name_key, "/", MODESTENGINE_ACCOUNT_ENABLED, NULL);
+ if (account_key) {
+ gchar* key = g_strconcat(account_key, "/", MODESTENGINE_ACCOUNT_ENABLED, NULL);
account_ok = gconf_client_get_bool(m_gconfclient, key, NULL);
g_free(key);
}
+
// Check if account store is defined
if (account_ok) {
- gchar* key = g_strconcat(account_name_key, "/", MODESTENGINE_ACCOUNT_STORE_ACCOUNT, NULL);
+ gchar* key = g_strconcat(account_key, "/", MODESTENGINE_ACCOUNT_STORE_ACCOUNT, NULL);
gchar* server_account_name = gconf_client_get_string(m_gconfclient, key, NULL);
if (server_account_name) {
- gchar* key = g_strconcat(MODESTENGINE_SERVER_ACCOUNT_NAMESPACE, "/", server_account_name, NULL);
- if (!gconf_client_dir_exists(m_gconfclient, key, NULL)) {
+ gchar* escaped_server_account_name = gconf_escape_key(server_account_name, strlen(server_account_name));
+ g_free(server_account_name);
+ gchar* store_account_key = g_strconcat(MODESTENGINE_SERVER_ACCOUNT_NAMESPACE, "/", escaped_server_account_name, NULL);
+ if (!gconf_client_dir_exists(m_gconfclient, store_account_key, NULL)) {
account_ok = FALSE;
}
- g_free(server_account_name);
+ g_free(store_account_key);
+ g_free(escaped_server_account_name);
}
g_free(key);
}
+
// Check if account transport is defined
if (account_ok) {
- gchar* key = g_strconcat(account_name_key, "/", MODESTENGINE_ACCOUNT_TRANSPORT_ACCOUNT, NULL);
+ gchar* key = g_strconcat(account_key, "/", MODESTENGINE_ACCOUNT_TRANSPORT_ACCOUNT, NULL);
gchar* server_account_name = gconf_client_get_string(m_gconfclient, key, NULL);
if (server_account_name) {
- gchar* key = g_strconcat(MODESTENGINE_SERVER_ACCOUNT_NAMESPACE, "/", server_account_name, NULL);
- if (!gconf_client_dir_exists(m_gconfclient, key, NULL)) {
+ gchar* escaped_server_account_name = gconf_escape_key(server_account_name, strlen(server_account_name));
+ g_free(server_account_name);
+ gchar* transport_account_key = g_strconcat(MODESTENGINE_SERVER_ACCOUNT_NAMESPACE, "/", escaped_server_account_name, NULL);
+ if (!gconf_client_dir_exists(m_gconfclient, transport_account_key, NULL)) {
account_ok = FALSE;
}
- g_free(server_account_name);
+ g_free(transport_account_key);
+ g_free(escaped_server_account_name);
}
g_free(key);
}
if (account_ok) {
- QString accountId = QString::fromUtf8(account_name_key);
- gchar* name_key = g_strconcat(account_name_key, "/", MODESTENGINE_ACCOUNT_DISPLAY_NAME, NULL);
- QString accountName = QString::fromUtf8(gconf_client_get_string(m_gconfclient, name_key, NULL));
+ gchar* escaped_account_id = gconf_escape_key(unescaped_account_id, strlen(unescaped_account_id));
+ QString accountId = "MO_"+QString::fromUtf8(escaped_account_id);
+ g_free(escaped_account_id);
+
+ gchar* name_key = g_strconcat(account_key, "/", MODESTENGINE_ACCOUNT_DISPLAY_NAME, NULL);
+ gchar* account_name = gconf_client_get_string(m_gconfclient, name_key, NULL);
+ QString accountName = QString::fromUtf8(account_name);
+ g_free(account_name);
g_free(name_key);
- gchar* email_key = g_strconcat(account_name_key, "/", MODESTENGINE_ACCOUNT_EMAIL, NULL);
- QString accountAddress = QString::fromUtf8(gconf_client_get_string(m_gconfclient, email_key, NULL));
+
+ gchar* email_key = g_strconcat(account_key, "/", MODESTENGINE_ACCOUNT_EMAIL, NULL);
+ gchar* email = gconf_client_get_string(m_gconfclient, email_key, NULL);
+ QString accountAddress = QString::fromUtf8(email);
+ g_free(email);
g_free(email_key);
+
QMessageAccount account = QMessageAccountPrivate::from(QMessageAccountId(accountId),
accountName,
QMessageAddress(QMessageAddress::Email, accountAddress),
QMessage::Email);
iAccounts.insert(accountId, account);
- if (strncmp(account_name_key, default_account, strlen(default_account))) {
+ // Check if newly added account is default account
+ if (!strncmp(default_account_id, unescaped_account_id, strlen(default_account_id))) {
iDefaultEmailAccountId = accountId;
}
}
+ g_free(unescaped_account_id);
g_free(iter->data);
iter->data = NULL;
iter = g_slist_next(iter);
@@ -180,13 +470,17 @@
// strings were freed in while loop
// => it's enough to just free accounts list
g_slist_free(accounts);
- g_free(default_account);
+ g_free(default_account_id);
}
}
QMessageAccountIdList ModestEngine::queryAccounts(const QMessageAccountFilter &filter, const QMessageAccountSortOrder &sortOrder,
uint limit, uint offset, bool &isFiltered, bool &isSorted) const
{
+ Q_UNUSED(sortOrder)
+ Q_UNUSED(limit)
+ Q_UNUSED(offset)
+
QMessageAccountIdList accountIds;
updateEmailAccounts();
@@ -214,15 +508,410 @@
return iAccounts[id.toString()];
}
-QMessageAccountId ModestEngine::defaultAccount(QMessage::Type type) const
+QMessageAccountId ModestEngine::defaultAccount() const
{
updateEmailAccounts();
return iDefaultEmailAccountId;
}
+QFileInfoList ModestEngine::localFolders() const
+{
+ QDir dir(localRootFolder());
+ dir.setFilter(QDir::AllDirs | QDir::NoDotAndDotDot);
+ QFileInfoList fileInfoList = dir.entryInfoList();
+ appendLocalSubFolders(fileInfoList, 0);
+ return fileInfoList;
+}
+
+void ModestEngine::appendLocalSubFolders(QFileInfoList& fileInfoList, int startIndex) const
+{
+ int endIndex = fileInfoList.count();
+ for (int i=startIndex; i < endIndex; i++) {
+ QDir dir(fileInfoList[i].absoluteFilePath());
+ if (dir.exists()) {
+ dir.setFilter(QDir::AllDirs | QDir::NoDotAndDotDot);
+ QFileInfoList dirs = dir.entryInfoList();
+ for (int j = 0; j < dirs.count(); j++) {
+ QString fileName = dirs[j].fileName();
+ if (fileName != "cur" && fileName != "new" && fileName != "tmp") {
+ fileInfoList.append(dirs[j]);
+ }
+ }
+ }
+ }
+ if (fileInfoList.count() > endIndex) {
+ appendLocalSubFolders(fileInfoList, endIndex);
+ }
+}
+
+void ModestEngine::appendIMAPSubFolders(QFileInfoList& fileInfoList, int startIndex) const
+{
+ int endIndex = fileInfoList.count();
+ for (int i=startIndex; i < endIndex; i++) {
+ QDir dir(fileInfoList[i].absoluteFilePath()+QString("/subfolders"));
+ if (dir.exists()) {
+ dir.setFilter(QDir::AllDirs | QDir::NoDotAndDotDot);
+ fileInfoList.append(dir.entryInfoList());
+ }
+ }
+ if (fileInfoList.count() > endIndex) {
+ appendIMAPSubFolders(fileInfoList, endIndex);
+ }
+}
+
+QString ModestEngine::localRootFolder() const
+{
+ return QDir::home().absolutePath()+QString("/.modest/local_folders");
+}
+
+QString ModestEngine::accountRootFolder(QMessageAccountId& accountId) const
+{
+ QString modestAccountId = escapeString(modestAccountIdFromAccountId(accountId));
+
+ QString userName;
+ QString hostName;
+ QString port;
+ QString protocol;
+
+ gchar* store_account_key = g_strconcat(MODESTENGINE_ACCOUNT_NAMESPACE, "/", modestAccountId.toUtf8().data(), "/", MODESTENGINE_ACCOUNT_STORE_ACCOUNT, NULL);
+ gchar* store_account_name = gconf_client_get_string(m_gconfclient, store_account_key, NULL);
+ g_free(store_account_key);
+ gchar* escaped_store_account_name = gconf_escape_key(store_account_name, strlen(store_account_name));
+ g_free(store_account_name);
+
+ gchar* username_key = g_strconcat(MODESTENGINE_SERVER_ACCOUNT_NAMESPACE, "/", escaped_store_account_name, "/", MODESTENGINE_ACCOUNT_USERNAME, NULL);
+ gchar* account_username = gconf_client_get_string(m_gconfclient, username_key, NULL);
+ userName = QString::fromUtf8(account_username);
+ g_free(account_username);
+ g_free(username_key);
+
+ gchar* hostname_key = g_strconcat(MODESTENGINE_SERVER_ACCOUNT_NAMESPACE, "/", escaped_store_account_name, "/", MODESTENGINE_ACCOUNT_HOSTNAME, NULL);
+ gchar* account_hostname = gconf_client_get_string(m_gconfclient, hostname_key, NULL);
+ hostName = QString::fromUtf8(account_hostname);
+ g_free(account_hostname);
+ g_free(hostname_key);
+
+ gchar* port_key = g_strconcat(MODESTENGINE_SERVER_ACCOUNT_NAMESPACE, "/", escaped_store_account_name, "/", MODESTENGINE_ACCOUNT_PORT, NULL);
+ gint account_port = gconf_client_get_int(m_gconfclient, port_key, NULL);
+ port = QString::number(account_port);
+ g_free(port_key);
+
+ gchar* protocol_key = g_strconcat(MODESTENGINE_SERVER_ACCOUNT_NAMESPACE, "/", escaped_store_account_name, "/", MODESTENGINE_ACCOUNT_PROTOCOL, NULL);
+ gchar* account_protocol = gconf_client_get_string(m_gconfclient, protocol_key, NULL);
+ protocol = QString::fromUtf8(account_protocol);
+ g_free(account_protocol);
+ g_free(protocol_key);
+
+ g_free(escaped_store_account_name);
+
+ if (protocol == "pop") {
+ return QDir::home().absolutePath()+"/.modest/cache/mail/"+protocol+"/"+userName+"__"+hostName+"_"+port;
+ } else if (protocol == "imap") {
+ return QDir::home().absolutePath()+"/.modest/cache/mail/"+protocol+"/"+userName+"__"+hostName+"_"+port+"/folders";
+ }
+ return QString();
+}
+
+QFileInfoList ModestEngine::accountFolders(QMessageAccountId& accountId) const
+{
+ QFileInfoList fileInfoList;
+
+ EmailProtocol protocol = accountEmailProtocol(accountId);
+
+ if (protocol == ModestEngine::EmailProtocolPop3) {
+ QFileInfo fileInfo = QFileInfo(accountRootFolder(accountId)+"/cache");
+ fileInfoList.append(fileInfo);
+ } else if (protocol == ModestEngine::EmailProtocolIMAP) {
+ QDir dir(accountRootFolder(accountId));
+ dir.setFilter(QDir::AllDirs | QDir::NoDotAndDotDot);
+ fileInfoList = dir.entryInfoList();
+ appendIMAPSubFolders(fileInfoList, 0);
+ }
+
+ return fileInfoList;
+}
+
+ModestEngine::EmailProtocol ModestEngine::accountEmailProtocol(QMessageAccountId& accountId) const
+{
+ EmailProtocol protocol = EmailProtocolUnknown;
+
+ QString modestAccountId = escapeString(modestAccountIdFromAccountId(accountId));
+
+ gchar* store_account_key = g_strconcat(MODESTENGINE_ACCOUNT_NAMESPACE, "/", modestAccountId.toUtf8().data(), "/", MODESTENGINE_ACCOUNT_STORE_ACCOUNT, NULL);
+ gchar* store_account_name = gconf_client_get_string(m_gconfclient, store_account_key, NULL);
+ g_free(store_account_key);
+ gchar* escaped_store_account_name = gconf_escape_key(store_account_name, strlen(store_account_name));
+ g_free(store_account_name);
+
+ gchar* protocol_key = g_strconcat(MODESTENGINE_SERVER_ACCOUNT_NAMESPACE, "/", escaped_store_account_name, "/", MODESTENGINE_ACCOUNT_PROTOCOL, NULL);
+ gchar* account_protocol = gconf_client_get_string(m_gconfclient, protocol_key, NULL);
+ if (QString("pop") == account_protocol) {
+ protocol = EmailProtocolPop3;
+ } else if (QString("imap") == account_protocol) {
+ protocol = EmailProtocolIMAP;
+ }
+ g_free(account_protocol);
+ g_free(protocol_key);
+
+ g_free(escaped_store_account_name);
+
+ return protocol;
+}
+
+QString ModestEngine::accountEmailProtocolAsString(const QMessageAccountId& accountId) const
+{
+ QString protocol;
+
+ QString modestAccountId = escapeString(modestAccountIdFromAccountId(accountId));
+
+ gchar* store_account_key = g_strconcat(MODESTENGINE_ACCOUNT_NAMESPACE, "/", modestAccountId.toUtf8().data(), "/", MODESTENGINE_ACCOUNT_STORE_ACCOUNT, NULL);
+ gchar* store_account_name = gconf_client_get_string(m_gconfclient, store_account_key, NULL);
+ g_free(store_account_key);
+ gchar* escaped_store_account_name = gconf_escape_key(store_account_name, strlen(store_account_name));
+ g_free(store_account_name);
+
+ gchar* protocol_key = g_strconcat(MODESTENGINE_SERVER_ACCOUNT_NAMESPACE, "/", escaped_store_account_name, "/", MODESTENGINE_ACCOUNT_PROTOCOL, NULL);
+ gchar* account_protocol = gconf_client_get_string(m_gconfclient, protocol_key, NULL);
+ protocol = QString::fromUtf8(account_protocol);
+ g_free(account_protocol);
+ g_free(protocol_key);
+
+ g_free(escaped_store_account_name);
+
+ return protocol;
+}
+
+QString ModestEngine::accountUsername(QMessageAccountId& accountId) const
+{
+ QString username;
+
+ QString modestAccountId = escapeString(modestAccountIdFromAccountId(accountId));
+
+ gchar* store_account_key = g_strconcat(MODESTENGINE_ACCOUNT_NAMESPACE, "/", modestAccountId.toUtf8().data(), "/", MODESTENGINE_ACCOUNT_STORE_ACCOUNT, NULL);
+ gchar* store_account_name = gconf_client_get_string(m_gconfclient, store_account_key, NULL);
+ g_free(store_account_key);
+ gchar* escaped_store_account_name = gconf_escape_key(store_account_name, strlen(store_account_name));
+ g_free(store_account_name);
+
+ gchar* username_key = g_strconcat(MODESTENGINE_SERVER_ACCOUNT_NAMESPACE, "/", escaped_store_account_name, "/", MODESTENGINE_ACCOUNT_USERNAME, NULL);
+ gchar* account_username = gconf_client_get_string(m_gconfclient, username_key, NULL);
+ username = QString::fromUtf8(account_username);
+ g_free(account_username);
+ g_free(username_key);
+
+ g_free(escaped_store_account_name);
+
+ return username;
+}
+
+QString ModestEngine::accountHostname(QMessageAccountId& accountId) const
+{
+ QString host;
+
+ QString modestAccountId = escapeString(modestAccountIdFromAccountId(accountId));
+
+ gchar* store_account_key = g_strconcat(MODESTENGINE_ACCOUNT_NAMESPACE, "/", modestAccountId.toUtf8().data(), "/", MODESTENGINE_ACCOUNT_STORE_ACCOUNT, NULL);
+ gchar* store_account_name = gconf_client_get_string(m_gconfclient, store_account_key, NULL);
+ g_free(store_account_key);
+ gchar* escaped_store_account_name = gconf_escape_key(store_account_name, strlen(store_account_name));
+ g_free(store_account_name);
+
+ gchar* host_key = g_strconcat(MODESTENGINE_SERVER_ACCOUNT_NAMESPACE, "/", escaped_store_account_name, "/", MODESTENGINE_ACCOUNT_HOSTNAME, NULL);
+ gchar* account_host = gconf_client_get_string(m_gconfclient, host_key, NULL);
+ host = QString::fromUtf8(account_host);
+ g_free(account_host);
+ g_free(host_key);
+
+ g_free(escaped_store_account_name);
+
+ return host;
+}
+
+QMessageFolderIdList ModestEngine::queryFolders(const QMessageFolderFilter &filter, const QMessageFolderSortOrder &sortOrder,
+ uint limit, uint offset, bool &isFiltered, bool &isSorted) const
+{
+ Q_UNUSED(sortOrder)
+ Q_UNUSED(limit)
+ Q_UNUSED(offset)
+
+ QMessageFolderIdList folderIds;
+
+ updateEmailAccounts();
+
+ //QDBusMessage msg = m_ModestDBusInterface->call(MODEST_DBUS_METHOD_GET_FOLDERS);
+ QFileInfoList localFolders = this->localFolders();
+ QString localRootFolder = this->localRootFolder();
+
+ foreach (QMessageAccount value, iAccounts) {
+ QMessageAccountId accountId = value.id();
+ QString rootFolder = accountRootFolder(accountId);
+ QFileInfoList folders = this->accountFolders(accountId);
+
+ for (int i=0; i < folders.count(); i++) {
+ QString filePath = folders[i].absoluteFilePath();
+ QString id = accountId.toString()+"&"+accountEmailProtocolAsString(accountId)+"&"+filePath.right(filePath.size()-rootFolder.size()-1);
+ id = id.remove("/subfolders");
+ folderIds.append(QMessageFolderId(id));
+ }
+
+ // Each account sees local folders as account folders
+ for (int i=0; i < localFolders.count(); i++) {
+ QString filePath = localFolders[i].absoluteFilePath();
+ QString id = accountId.toString()+"&"+"maildir"+"&"+filePath.right(filePath.size()-localRootFolder.size()-1);
+ folderIds.append(QMessageFolderId(id));
+ }
+ }
+
+ MessagingHelper::filterFolders(folderIds, filter);
+ isFiltered = true;
+ isSorted = false;
+ return folderIds;
+}
+
+int ModestEngine::countFolders(const QMessageFolderFilter &filter) const
+{
+ bool isFiltered, isSorted;
+ return queryFolders(filter, QMessageFolderSortOrder(), 0, 0, isFiltered, isSorted).count();
+}
+
+QMessageFolder ModestEngine::folder(const QMessageFolderId &id) const
+{
+ QString idString = id.toString();
+ int endOfAccountId = idString.indexOf('&');
+ int endOfProtocolString = idString.lastIndexOf('&');
+ QString accountId = idString.left(endOfAccountId);
+ QString protocolString = idString.mid(endOfAccountId+1, endOfProtocolString-endOfAccountId-1);
+ QString folder = idString.right(idString.length()-idString.lastIndexOf('&')-1);
+ QMessageFolderId parentId;
+ QString name;
+ if (folder.lastIndexOf('/') == -1) {
+ // Folder does not have subfolders
+ name = folder;
+ if ((protocolString == "pop") && (name == "cache")) {
+ name = "Inbox";
+ }
+ } else {
+ // Folder has subfolders
+ name = folder.right(folder.length()-folder.lastIndexOf('/')-1);
+ parentId = idString.left(idString.lastIndexOf('/'));
+ }
+ return QMessageFolderPrivate::from(id, QMessageAccountId(accountId), parentId, name, folder);
+}
+
+void ModestEngine::watchAllKnownEmailFolders()
+{
+ QFileInfoList localFolders = this->localFolders();
+
+ // Changes in local Email folders can be monitored using directory
+ // monitoring. <=> All messages are stored as individual files.
+ for (int i=0; i < localFolders.count(); i++) {
+ m_MailFoldersWatcher.addDirectory(localFolders[i].absoluteFilePath()+"/cur", IN_MOVED_TO | IN_DELETE);
+ }
+
+ // Monitor changes also in root folder to see if new folders are added
+ m_MailFoldersWatcher.addDirectory(localRootFolder(), IN_CREATE);
+
+ // Changes in remote Email folders must be monitored using file monitoring.
+ // That's because message headers are stored into summary.mmap file (and summary.mmap
+ // file maybe the only place that contains message information).
+ // => summary.mmap files will be monitored
+ foreach (QMessageAccount value, iAccounts) {
+ QMessageAccountId accountId = value.id();
+ QString rootFolder = accountRootFolder(accountId);
+ EmailProtocol protocol = accountEmailProtocol(accountId);
+ QFileInfoList folders = this->accountFolders(accountId);
+
+ for (int i=0; i < folders.count(); i++) {
+ if (protocol == ModestEngine::EmailProtocolPop3) {
+ QString folder = folders[i].absoluteFilePath();
+ m_MailFoldersWatcher.addDirectory(folder, IN_CREATE);
+ } else if (protocol == ModestEngine::EmailProtocolIMAP) {
+ m_MailFoldersWatcher.addDirectory(folders[i].absoluteFilePath(), IN_MOVED_TO | IN_DELETE);
+ }
+ }
+
+ }
+}
+
+void ModestEngine::fileChangedSlot(int watchDescriptor, QString filePath, uint events)
+{
+ Q_UNUSED(watchDescriptor)
+
+ if (events & IN_CREATE) {
+ if (QFileInfo(filePath).isDir()) {
+ // New directory was added
+ // => Start watching new folder
+ QString newDirPath = QString(filePath.toUtf8());
+ m_MailFoldersWatcher.addDirectory(newDirPath + "/cur");
+ }
+ }
+
+ int filenameBegin = filePath.lastIndexOf('/')+1;
+ QString fileName = filePath.mid(filenameBegin,filePath.lastIndexOf('.')-filenameBegin);
+ if (fileName != "summary") {
+ if (events & (IN_MOVED_TO | IN_CREATE)) {
+ if (events != (IN_MOVED_TO | IN_MOVED_FROM)) {
+ notification(messageIdFromModestMessageFilePath(filePath), ModestEngine::Added);
+ }
+ } else if (events & IN_DELETE) {
+ notification(messageIdFromModestMessageFilePath(filePath), ModestEngine::Removed);
+ }
+ }
+}
+
bool ModestEngine::sendEmail(QMessage &message)
{
- return composeEmail(message);
+ ModestStringMap senderInfo;
+ ModestStringMap recipients;
+ ModestStringMap messageData;
+ ModestStringMapList attachments;
+ ModestStringMapList images;
+ uint priority = 0;
+ ModestStringMap headers;
+
+ senderInfo = getModestSenderInfo(message);
+
+ if (senderInfo.isEmpty()) {
+ return false;
+ }
+
+ recipients = getModestRecipients(message);
+
+ if (recipients.isEmpty()) {
+ return false;
+ }
+
+ messageData = getModestMessageData(message);
+ attachments = getModestAttachments(message);
+ images = getModestImages(message);
+ priority = getModestPriority(message);
+ headers = getModestHeaders(message);
+
+ qDebug() << "Sending D-BUS message";
+
+ QDBusPendingCall call = m_QtmPluginDBusInterface->asyncCall (
+ "SendEmail",
+ QVariant::fromValue (senderInfo),
+ QVariant::fromValue (recipients),
+ QVariant::fromValue (messageData),
+ QVariant::fromValue (attachments),
+ QVariant::fromValue (images),
+ priority,
+ QVariant::fromValue (headers));
+
+ qDebug() << "Message sent";
+
+ if (call.isError()) {
+ qWarning() << "Call failed! " << call.error();
+ return false;
+ }
+
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher (call, this);
+
+ connect (watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
+ this, SLOT(sendEmailCallEnded(QDBusPendingCallWatcher*)));
+
+ return true;
}
bool ModestEngine::composeEmail(const QMessage &message)
@@ -232,10 +921,10 @@
if (!list.empty()){
for (int i = 0; i < list.size(); ++i) {
if (i == 0) {
- mailString += list.at(i).recipient();
+ mailString += list.at(i).addressee();
} else {
mailString += QString("%2C%20");
- mailString += list.at(i).recipient();
+ mailString += list.at(i).addressee();
}
}
}
@@ -274,20 +963,2170 @@
return true;
}
-bool ModestEngine::queryMessages(QMessageService& messageService, const QMessageFilter &filter, const QMessageSortOrder &sortOrder, uint limit, uint offset) const
+bool ModestEngine::showMessage(const QMessageId &id)
{
+ QMessage msg = message(id, false);
+ QMessagePrivate* privateMessage = QMessagePrivate::implementation(msg);
+ if (privateMessage->_url.isEmpty()) {
+ return false;
+ } else {
+ m_ModestDBusInterface->call(MODEST_DBUS_METHOD_OPEN_MESSAGE,
+ privateMessage->_url);
+ }
+ return true;
+}
+
+bool ModestEngine::exportUpdates(const QMessageAccountId &id)
+{
+ Q_UNUSED(id) // Modest does not offer Account specific updates
+
+ m_ModestDBusInterface->call(MODEST_DBUS_METHOD_SEND_RECEIVE);
return true;
}
-bool ModestEngine::queryMessages(QMessageService& messageService, const QMessageFilter &filter, const QString &body, QMessageDataComparator::MatchFlags matchFlags, const QMessageSortOrder &sortOrder, uint limit, uint offset) const
+QMessage ModestEngine::message(const QMessageId &id, bool useCache) const
+{
+ if (useCache) {
+ QMessage message = m_messageCache.value(id.toString());
+ if (message.type() != QMessage::NoType) {
+ return message;
+ }
+ }
+
+ QString modestAccountId = modestAccountIdFromMessageId(id);
+ QString modestFolderId = modestFolderIdFromMessageId(id);
+ QString modestMessageId = modestMessageIdFromMessageId(id);
+
+ MessagingModestMessage modestMessage = messageFromModest(modestAccountId,
+ modestFolderId,
+ modestMessageId);
+
+ if (modestMessage.flags & MessagingModestMessageDeleted) {
+ return QMessage();
+ }
+
+ if (modestMessage.accountId.isEmpty()) {
+ return QMessage();
+ }
+
+ return messageFromModestMessage(modestMessage, accountIdFromMessageId(id));
+}
+
+QMessage::StandardFolder ModestEngine::standardFolderFromModestFolderId(const QString& modestFolderId) const
+{
+ if (!QString::compare(modestFolderId, "INBOX", Qt::CaseInsensitive)) {
+ return QMessage::InboxFolder;
+ } else if (!QString::compare(modestFolderId, "drafts", Qt::CaseInsensitive)) {
+ return QMessage::DraftsFolder;
+ } else if (!QString::compare(modestFolderId, "sent", Qt::CaseInsensitive)) {
+ return QMessage::SentFolder;
+ }
+
+ return QMessage::DraftsFolder;
+}
+
+QString ModestEngine::modestFolderIdFromStandardFolder(QMessage::StandardFolder standardFolder) const
+{
+ switch (standardFolder) {
+ case QMessage::InboxFolder: return "INBOX";
+ case QMessage::DraftsFolder: return "drafts";
+ case QMessage::SentFolder: return "sent";
+ case QMessage::OutboxFolder: return "outbox";
+ case QMessage::TrashFolder: return "thrash";
+ }
+ return "drafts";
+}
+
+QMessage ModestEngine::messageFromModestMessage(const MessagingModestMessage& modestMessage,
+ QMessageAccountId accountId) const
{
+ QMessage message;
+ QMessagePrivate* privateMessage = QMessagePrivate::implementation(message);
+ QMessageContentContainerPrivate* container = QMessagePrivate::containerImplementation(message);
+
+ // Type
+ message.setType(QMessage::Email);
+
+ // Parent Account Id
+ QMessageAccountId parentAccountId;
+ if (accountId.isValid()) {
+ parentAccountId = accountId;
+ } else {
+ parentAccountId = realAccountId(modestMessage);
+ }
+ message.setParentAccountId(parentAccountId);
+
+ // Parent Folder Id
+ QMessageFolderId folderId;
+ if (modestMessage.accountId == "local_folders") {
+ folderId = folderIdFromModestFolderId(parentAccountId, true, modestMessage.folderId);
+ } else {
+ // Since Message is not in local folder, message status can be set to Incoming
+ privateMessage->_status = privateMessage->_status | QMessage::Incoming;
+ folderId = folderIdFromModestFolderId(parentAccountId, false, modestMessage.folderId);
+ }
+ privateMessage->_parentFolderId = folderId;
+
+ // Message Id
+ QMessageId messageId = QMessageId(folderId.toString()+"/"+modestMessage.id);
+ privateMessage->_id = messageId;
+
+ // Dates
+ message.setDate(QDateTime::fromTime_t(modestMessage.dateSent));
+ message.setReceivedDate(QDateTime::fromTime_t(modestMessage.dateReceived));
+
+ // Priority
+ switch (modestMessage.priority) {
+ case QtMobility::MessagingModestMessageHighPriority:
+ message.setPriority(QMessage::HighPriority);
+ break;
+ case QtMobility::MessagingModestMessageNormalPriority:
+ message.setPriority(QMessage::NormalPriority);
+ break;
+ case QtMobility::MessagingModestMessageLowPriority:
+ message.setPriority(QMessage::LowPriority);
+ break;
+ case QtMobility::MessagingModestMessageSuspendedPriority:
+ message.setPriority(QMessage::NormalPriority);
+ break;
+ }
+
+ // Standard Folder
+ QMessagePrivate::setStandardFolder(message,
+ standardFolderFromModestFolderId(modestMessage.folderId));
+
+ // Body & Attachments handling
+ for (int i=0; i < modestMessage.mimeParts.count(); i++) {
+ if (!modestMessage.mimeParts[i].isAttachment) {
+ // Body
+ QByteArray fullMimeType = modestMessage.mimeParts[i].mimeType.toUtf8();
+ QString contentId = modestMessage.mimeParts[i].contentId;
+ QByteArray fileName = modestMessage.mimeParts[i].fileName.toAscii();
+
+ QByteArray mainType("text");
+ QByteArray subType("plain");
+ QByteArray charset;
+
+ int index = fullMimeType.indexOf("/");
+ if (index != -1) {
+ mainType = fullMimeType.left(index).trimmed();
+
+ subType = fullMimeType.mid(index + 1).trimmed();
+ index = subType.indexOf(";");
+ if (index != -1) {
+ QString remainder = subType.mid(index + 1);
+ subType = subType.left(index).trimmed();
+
+ QRegExp charsetPattern("charset=(\\S+)");
+ index = charsetPattern.indexIn(remainder);
+ if (index != -1) {
+ charset = charsetPattern.cap(1).toLatin1();
+ }
+ }
+ }
+
+ if (charset.isEmpty()) {
+ charset = "UTF-8";
+ }
+
+ QMessageContentContainerId existingBodyId(message.bodyId());
+ if (existingBodyId.isValid()) {
+ if (existingBodyId == container->bodyContentId()) {
+ // The body content is in the message itself
+ container->_containingMessageId = messageId.toString();
+ container->_attachmentId = contentId;
+ container->_name = fileName;
+ container->_type = mainType;
+ container->_subType = subType;
+ container->_charset = charset;
+ container->_size = 0;
+ container->_available = true;
+ } else {
+ // The body content is in the first attachment
+ QMessageContentContainerPrivate *attachmentContainer(QMessageContentContainerPrivate::implementation(*container->attachment(existingBodyId)));
+ attachmentContainer->_containingMessageId = messageId.toString();
+ attachmentContainer->_attachmentId = contentId;
+ attachmentContainer->_name = fileName;
+ attachmentContainer->_type = mainType;
+ attachmentContainer->_subType = subType;
+ attachmentContainer->_charset = charset;
+ attachmentContainer->_size = 0;
+ attachmentContainer->_available = true;
+ }
+ } else {
+ if (container->_attachments.isEmpty()) {
+ // Put the content directly into the message
+ container->_containingMessageId = messageId.toString();
+ container->_attachmentId = contentId;
+ container->_name = fileName;
+ container->_type = mainType;
+ container->_subType = subType;
+ container->_charset = charset;
+ container->_size = 0;
+ container->_available = true;
+ privateMessage->_bodyId = container->bodyContentId();
+ } else {
+ // Add the body as the first attachment
+ QMessageContentContainer newBody;
+ QMessageContentContainerPrivate *attachmentContainer = QMessageContentContainerPrivate::implementation(newBody);
+ attachmentContainer->_containingMessageId = messageId.toString();
+ attachmentContainer->_attachmentId = contentId;
+ attachmentContainer->_name = fileName;
+ attachmentContainer->_type = mainType;
+ attachmentContainer->_subType = subType;
+ attachmentContainer->_charset = charset;
+ attachmentContainer->_size = 0;
+ attachmentContainer->_available = true;
+ privateMessage->_bodyId = container->prependContent(newBody);
+ }
+ }
+ } else {
+ // Attachment
+ QString fullMimeType = modestMessage.mimeParts[i].mimeType;
+ QString contentId = modestMessage.mimeParts[i].contentId;
+ int slashIndex = fullMimeType.indexOf('/');
+ QByteArray mimeType = fullMimeType.left(slashIndex).toAscii();
+ QByteArray mimeSubType = fullMimeType.mid(slashIndex+1).toAscii();
+ // TODO: Attachment size
+ QByteArray fileName = modestMessage.mimeParts[i].fileName.toAscii();
+ fileName = fileName.mid(fileName.lastIndexOf('/')+1);
+ QString msgId = messageId.toString();
+ QMessageContentContainer attachment =
+ QMessageContentContainerPrivate::from(msgId,
+ contentId,
+ fileName,
+ mimeType,
+ mimeSubType,
+ 0);
+ appendAttachmentToMessage(message, attachment);
+ }
+ }
+
+ // From
+ if (modestMessage.from.size() > 0) {
+ message.setFrom(QMessageAddress(QMessageAddress::Email, modestMessage.from));
+ QMessagePrivate::setSenderName(message, modestMessage.from);
+ }
+
+ // To
+ if (modestMessage.to.size() > 0) {
+ QMessageAddressList toAddresses;
+ foreach (const QString &element, modestMessage.to.split(",", QString::SkipEmptyParts)) {
+ QMessageAddress addr;
+ addr.setType(QMessageAddress::Email);
+ addr.setAddressee(element.trimmed());
+ toAddresses.append(addr);
+ }
+ message.setTo(toAddresses);
+ }
+
+ // Cc
+ if (modestMessage.cc.size() > 0) {
+ QMessageAddressList ccAddresses;
+ foreach (const QString &element, modestMessage.cc.split(",", QString::SkipEmptyParts)) {
+ QMessageAddress addr;
+ addr.setType(QMessageAddress::Email);
+ addr.setAddressee(element.trimmed());
+ ccAddresses.append(addr);
+ }
+ message.setCc(ccAddresses);
+ }
+
+ // Bcc
+ if (modestMessage.bcc.size() > 0) {
+ QMessageAddressList bccAddresses;
+ foreach (const QString &element, modestMessage.bcc.split(",", QString::SkipEmptyParts)) {
+ QMessageAddress addr;
+ addr.setType(QMessageAddress::Email);
+ addr.setAddressee(element.trimmed());
+ bccAddresses.append(addr);
+ }
+ message.setBcc(bccAddresses);
+ }
+
+ // Subject
+ message.setSubject(modestMessage.subject);
+
+ // Size
+ privateMessage->_size = modestMessage.size;
+
+ // Read Status
+ if (modestMessage.flags & MessagingModestMessageSeen) {
+ privateMessage->_status = privateMessage->_status | QMessage::Read;
+ }
+
+ // Message MIME type
+ QString fullMimeType = modestMessage.mimeType;
+ int slashIndex = fullMimeType.indexOf('/');
+ QByteArray mimeType = fullMimeType.left(slashIndex).toAscii();
+ QByteArray mimeSubType = fullMimeType.mid(slashIndex+1).toAscii();
+ container->_type = mimeType.data();
+ container->_subType = mimeSubType.data();
+
+ // Modest specific url
+ privateMessage->_url = modestMessage.url;
+
+ // Modified flag
+ privateMessage->_modified = false;
+
+ return message;
+}
+
+void ModestEngine::appendAttachmentToMessage(QMessage& message, QMessageContentContainer& attachment) const
+{
+ QMessagePrivate* privateMessage = QMessagePrivate::implementation(message);
+ QMessageContentContainerPrivate* container = QMessagePrivate::containerImplementation(message);
+
+ if (container->_attachments.isEmpty()) {
+ QMessageContentContainerId existingBodyId(message.bodyId());
+ if (existingBodyId == QMessageContentContainerPrivate::bodyContentId()) {
+ // The body content is in the message itself - move it to become the first attachment
+ QMessageContentContainer newBody(message);
+ QMessageContentContainerPrivate::implementation(newBody)->setDerivedMessage(0);
+
+ container->setContentType("multipart", "mixed", "");
+ privateMessage->_bodyId = container->prependContent(newBody);
+ } else {
+ // This message is now multipart
+ container->setContentType("multipart", "mixed", "");
+ }
+
+ container->_available = true;
+ }
+
+ container->appendContent(attachment);
+
+ bool haveAttachments = !container->_attachments.isEmpty();
+ message.setStatus(QMessage::HasAttachments,haveAttachments);
+
+ privateMessage->_modified = true;
+}
+
+bool ModestEngine::addMessage(QMessage &message)
+{
+ QString modestFolder;
+ ModestStringMap senderInfo;
+ ModestStringMap recipients;
+ ModestStringMap messageData;
+ ModestStringMapList attachments;
+ ModestStringMapList images;
+ uint priority = 0;
+ ModestStringMap headers;
+
+ qDebug() << __PRETTY_FUNCTION__;
+
+ senderInfo = getModestSenderInfo (message);
+ recipients = getModestRecipients (message);
+ messageData = getModestMessageData (message);
+ attachments = getModestAttachments (message);
+ images = getModestImages (message);
+ priority = getModestPriority (message);
+ headers = getModestHeaders (message);
+
+ QString accountName;
+ if (message.parentFolderId().isValid()) {
+ modestFolder = modestFolderIdFromFolderId (message.parentFolderId());
+ accountName = modestAccountIdFromFolderId(message.parentFolderId());
+ } else {
+ modestFolder = modestFolderIdFromStandardFolder(message.standardFolder());
+ if (message.standardFolder() == QMessage::DraftsFolder) {
+ accountName = "local_folders";
+ } else {
+ accountName = modestAccountIdFromAccountId(message.parentAccountId());
+ }
+ }
+ senderInfo["account-name"] = accountName;
+
+ QDBusPendingCall pendingCall = m_QtmPluginDBusInterface->asyncCall (
+ "AddMessage",
+ QVariant::fromValue (modestFolder),
+ QVariant::fromValue (senderInfo),
+ QVariant::fromValue (recipients),
+ QVariant::fromValue (messageData),
+ QVariant::fromValue (attachments),
+ QVariant::fromValue (images),
+ priority,
+ QVariant::fromValue (headers));
+
+ if (pendingCall.isError()) {
+ qWarning() << "DBus call failed! " << pendingCall.error();
+ return false;
+ }
+
+ QDBusPendingCallWatcher pendingCallWatcher(pendingCall);
+ pendingCallWatcher.waitForFinished();
+ QDBusMessage msg = pendingCallWatcher.reply();
+ if (msg.type() == QDBusMessage::ErrorMessage) {
+ qWarning() << "Failed to add message via modest: " << msg.errorMessage();
+ return false;
+ }
+
+ QMessagePrivate* privateMessage = QMessagePrivate::implementation(message);
+ QString messageId;
+ if (message.parentFolderId().isValid()) {
+ messageId = message.parentFolderId().toString()+"/"+msg.arguments()[0].toString();
+ } else if (accountName == "local_folders") {
+ messageId = message.parentAccountId().toString()+
+ "&maildir&"+modestFolder+"/"+msg.arguments()[0].toString();
+ } else {
+ messageId = message.parentAccountId().toString()+"&"+
+ accountEmailProtocolAsString(message.parentAccountId())+
+ "&"+modestFolder+"/"+msg.arguments()[0].toString();
+ }
+ privateMessage->_id = QMessageId(messageId);
+
return true;
}
+bool ModestEngine::updateMessage(QMessage &message)
+{
+ Q_UNUSED(message) // TODO:
+
+ return false;
+}
+
+bool ModestEngine::removeMessage(const QMessageId &id, QMessageManager::RemovalOption option)
+{
+ Q_UNUSED(option) // TODO:
+
+ QMessage msg = message(id, false);
+ QMessagePrivate* privateMessage = QMessagePrivate::implementation(msg);
+ if (privateMessage->_url.isEmpty()) {
+ return false;
+ } else {
+ m_ModestDBusInterface->call(MODEST_DBUS_METHOD_DELETE_MESSAGE,
+ privateMessage->_url);
+ // Make sure that there will instant notification about removed message
+ notification(id, ModestEngine::Removed);
+ }
+ return true;
+}
+
+bool ModestEngine::filterMessage(const QMessage& message, QMessageFilterPrivate::SortedMessageFilterList filterList, int start) const
+{
+ if (filterList.count() > start) {
+ for (int j=start; j < filterList.count(); j++) {
+ QMessageFilterPrivate* pf = QMessageFilterPrivate::implementation(filterList[j]);
+ if (!pf->filter(message)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+QMessageIdList ModestEngine::queryMessagesSync(const QMessageFilter &filter, const QMessageSortOrder &sortOrder,
+ uint limit, uint offset, bool &isFiltered, bool &isSorted) const
+{
+ QMessageIdList ids;
+
+ QMessageServicePrivate* privateService = QMessageServicePrivate::implementation(m_service);
+ if (privateService->queryMessages(m_service, filter, sortOrder, limit, offset,
+ QMessageServicePrivate::EnginesToCallModest)) {
+ QObject::connect(&m_service, SIGNAL(stateChanged(QMessageService::State)),
+ this, SLOT(stateChanged(QMessageService::State)));
+
+ m_eventLoop.exec();
+
+ isSorted = m_isSorted;
+ isFiltered = m_isFiltered;
+ ids = m_ids;
+ m_ids.clear();
+ }
+
+ return ids;
+}
+
+QMessageIdList ModestEngine::queryMessagesSync(const QMessageFilter &filter, const QString &body,
+ QMessageDataComparator::MatchFlags matchFlags,
+ const QMessageSortOrder &sortOrder, uint limit, uint offset,
+ bool &isFiltered, bool &isSorted) const
+{
+ QMessageIdList ids;
+
+ QMessageServicePrivate* privateService = QMessageServicePrivate::implementation(m_service);
+ if (privateService->queryMessages(m_service, filter, body, matchFlags,
+ sortOrder, limit, offset,
+ QMessageServicePrivate::EnginesToCallModest)) {
+ QObject::connect(&m_service, SIGNAL(stateChanged(QMessageService::State)),
+ this, SLOT(stateChanged(QMessageService::State)));
+
+ m_eventLoop.exec();
+
+ isSorted = m_isSorted;
+ isFiltered = m_isFiltered;
+ ids = m_ids;
+ m_ids.clear();
+ }
+
+ return ids;
+}
+
+int ModestEngine::countMessagesSync(const QMessageFilter &filter) const
+{
+ int count;
+
+ QMessageServicePrivate* privateService = QMessageServicePrivate::implementation(m_service);
+ if (privateService->countMessages(m_service, filter, QMessageServicePrivate::EnginesToCallModest)) {
+ QObject::connect(&m_service, SIGNAL(stateChanged(QMessageService::State)),
+ this, SLOT(stateChanged(QMessageService::State)));
+
+ m_eventLoop.exec();
+
+ count = m_count;
+ }
+
+ return count;
+}
+
+void ModestEngine::stateChanged(QMessageService::State newState)
+{
+ if (newState == QMessageService::FinishedState) {
+ QMessageServicePrivate* privateService = QMessageServicePrivate::implementation(m_service);
+
+ m_ids = privateService->_ids;
+ m_isSorted = privateService->_sorted;
+ m_isFiltered = privateService->_filtered;
+ m_count = privateService->_count;
+
+ m_eventLoop.quit();
+ }
+}
+
+bool ModestEngine::queryMessages(QMessageService& messageService, const QMessageFilter &filter, const QMessageSortOrder &sortOrder, uint limit, uint offset) const
+{
+ return queryMessages(messageService, filter, QString(), 0, sortOrder, limit, offset);
+}
+
bool ModestEngine::countMessages(QMessageService& messageService, const QMessageFilter &filter)
{
+ m_pendingMessageQueries.append(MessageQueryInfo());
+
+ MessageQueryInfo &queryInfo = m_pendingMessageQueries[m_pendingMessageQueries.count()-1];
+
+ queryInfo.filter = filter;
+ queryInfo.limit = 0;
+ queryInfo.offset = 0;
+ queryInfo.privateService = QMessageServicePrivate::implementation(messageService);
+ queryInfo.currentFilterListIndex = 0;
+ queryInfo.handledFiltersCount = 0;
+ queryInfo.isQuery = false;
+ queryInfo.returnWithSingleShot = false;
+
+ if (!startQueryingAndFilteringMessages(m_pendingMessageQueries[m_pendingMessageQueries.count()-1])) {
+ QMessageServicePrivate::implementation(messageService)->setFinished(false);
+ m_pendingMessageQueries.removeAt(m_pendingMessageQueries.count()-1);
+ return false;
+ }
+
+ return true;
+}
+
+bool ModestEngine::queryMessages(QMessageService& messageService, const QMessageFilter &filter, const QString &body,
+ QMessageDataComparator::MatchFlags matchFlags, const QMessageSortOrder &sortOrder,
+ uint limit, uint offset) const
+{
+ m_pendingMessageQueries.append(MessageQueryInfo());
+
+ MessageQueryInfo &queryInfo = m_pendingMessageQueries[m_pendingMessageQueries.count()-1];
+
+ queryInfo.body = body;
+ queryInfo.matchFlags = matchFlags;
+ queryInfo.filter = filter;
+ queryInfo.sortOrder = sortOrder;
+ queryInfo.limit = limit;
+ queryInfo.offset = offset;
+ queryInfo.privateService = QMessageServicePrivate::implementation(messageService);
+ queryInfo.currentFilterListIndex = 0;
+ queryInfo.handledFiltersCount = 0;
+ queryInfo.isQuery = true;
+ queryInfo.returnWithSingleShot = false;
+
+ if (!startQueryingAndFilteringMessages(m_pendingMessageQueries[m_pendingMessageQueries.count()-1])) {
+ QMessageServicePrivate::implementation(messageService)->setFinished(false);
+ m_pendingMessageQueries.removeAt(m_pendingMessageQueries.count()-1);
+ return false;
+ }
+
+ return true;
+}
+
+bool ModestEngine::startQueryingAndFilteringMessages(MessageQueryInfo &msgQueryInfo) const
+{
+ QMessageFilterPrivate* pf = QMessageFilterPrivate::implementation(msgQueryInfo.filter);
+ if (pf->_filterList.count() == 0) {
+ if ((pf->_field == QMessageFilterPrivate::None) &&
+ (pf->_filterList.count() == 0) &&
+ (pf->_notFilter)) {
+ // There is only one filter: empty ~QMessageFilter()
+ // => return empty QMessageIdList
+ msgQueryInfo.ids.clear();
+ msgQueryInfo.returnWithSingleShot = true;
+ QTimer::singleShot(0, (ModestEngine*)this, SLOT(returnQueryResultsSlot()));
+ return true;
+ } else {
+ // One single filter to be handled
+ QMessageFilter newFilter;
+ QMessageFilterPrivate* pfNew = QMessageFilterPrivate::implementation(newFilter);
+ pfNew->_filterList.append(QMessageFilterPrivate::SortedMessageFilterList());
+ pfNew->_filterList[0] << msgQueryInfo.filter;
+ msgQueryInfo.filter = newFilter;
+ }
+ }
+
+ return queryAndFilterMessages(msgQueryInfo);
+}
+
+void ModestEngine::returnQueryResultsSlot()
+{
+ for (int i=m_pendingMessageQueries.count()-1; i >= 0; i--) {
+ if (m_pendingMessageQueries[i].returnWithSingleShot) {
+ if (m_pendingMessageQueries[i].isQuery) {
+ m_pendingMessageQueries[i].privateService->messagesFound(m_pendingMessageQueries[i].ids, true, true);
+ } else {
+ m_pendingMessageQueries[i].privateService->messagesCounted(m_pendingMessageQueries[i].ids.count());
+ }
+ m_pendingMessageQueries.removeAt(i);
+ }
+ }
+}
+
+bool ModestEngine::queryAndFilterMessages(MessageQueryInfo &msgQueryInfo) const
+{
+ QStringList accountIds;
+ QStringList folderUris;
+ QDateTime startTimeStamp;
+ QDateTime endTimeStamp;
+ QDateTime startReceptionTimeStamp;
+ QDateTime endReceptionTimeStamp;
+
+ QMessageFilterPrivate* pf = QMessageFilterPrivate::implementation(msgQueryInfo.filter);
+
+ // Filters have been sorted into matrix of filters:
+ // - there is AND operation between every filter in one row
+ // - there is OR operation between every row
+ // => Every row can be handled individually
+ // => Final result set can be created by combining result sets
+ // from individual row based queries
+ QMessageFilterPrivate::SortedMessageFilterList filters = pf->_filterList[msgQueryInfo.currentFilterListIndex];
+
+ msgQueryInfo.realAccountId = QString();
+
+ int handledFiltersCount = 0;
+ pf = QMessageFilterPrivate::implementation(filters[handledFiltersCount]);
+ if ((filters.count() == 1) &&
+ (pf->_field == QMessageFilterPrivate::None) &&
+ (pf->_filterList.count() == 0)) {
+ if (pf->_notFilter) {
+ // There is only one filter: empty ~QMessageFilter()
+ // => this query results empty QMessageIdList
+ // => Skip to next query round
+ int index = -1;
+ for (int i=0; i < m_pendingMessageQueries.count(); i++) {
+ if (m_pendingMessageQueries[i].queryId == msgQueryInfo.queryId) {
+ index = i;
+ break;
+ }
+ }
+ if (index >= 0) {
+ handleQueryFinished(index);
+ }
+ return true;
+ } else {
+ // There is only one filter: empty QMessageFilter()
+ // => return all messages
+ handledFiltersCount++;
+ }
+ }
+
+ bool handled = true;
+ while (handledFiltersCount < filters.count() && handled) {
+ handled = false;
+ pf = QMessageFilterPrivate::implementation(filters[handledFiltersCount]);
+ switch (pf->_field) {
+ case QMessageFilterPrivate::Id:
+ break;
+ case QMessageFilterPrivate::ParentAccountId:
+ {
+ if (pf->_comparatorType == QMessageFilterPrivate::Equality) {
+ if (accountIds.count()) {
+ // Only one account/one query can be used
+ break;
+ }
+ QMessageDataComparator::EqualityComparator cmp(static_cast<QMessageDataComparator::EqualityComparator>(pf->_comparatorValue));
+ if (cmp == QMessageDataComparator::Equal) {
+ if (pf->_value.toString().length() > 0) {
+ accountIds.append(modestAccountIdFromAccountId(pf->_value.toString()));
+ msgQueryInfo.realAccountId = pf->_value.toString();
+ handled = true;
+ }
+ }
+ }
+ break;
+ }
+ case QMessageFilterPrivate::ParentFolderId:
+ {
+ if (pf->_comparatorType == QMessageFilterPrivate::Equality) {
+ QMessageDataComparator::EqualityComparator cmp(static_cast<QMessageDataComparator::EqualityComparator>(pf->_comparatorValue));
+ if (cmp == QMessageDataComparator::Equal) {
+ if (pf->_value.toString().length() > 0) {
+ folderUris.append(modestFolderIdFromFolderId(pf->_value.toString()));
+ if (accountIds.count() == 0) {
+ accountIds.append(modestAccountIdFromFolderId(pf->_value.toString()));
+
+ // Note: Even though local folders belong to "local_folders" account
+ // inside Modest, local folders can belong to any "real"
+ // POP3 or IMAP account in Qt Mobility Messaging side
+ // <=> Qt Mobility Messaging does not have "Local Folders"
+ // account
+ // If folder is local folder, "local_folders" accountId will be
+ // added to accountIds list to enable correct filtering inside
+ // Modest Plugin
+ accountIds.append("local_folders");
+
+ // realAccountId will contain id of Qt Mobility Messaging account
+ // (AccountId can be found from the beginning of folderId string)
+ // <=> realAccountId will not ever be "local_folders"
+ msgQueryInfo.realAccountId = accountIdFromFolderId(pf->_value.toString()).toString();
+ }
+ handled = true;
+ }
+ }
+ }
+ break;
+ }
+ case QMessageFilterPrivate::AncestorFolderIds:
+ break;
+ case QMessageFilterPrivate::Type:
+ break;
+ case QMessageFilterPrivate::StandardFolder:
+ if (pf->_comparatorType == QMessageFilterPrivate::Equality) {
+ QMessageDataComparator::EqualityComparator cmp(static_cast<QMessageDataComparator::EqualityComparator>(pf->_comparatorValue));
+ if (cmp == QMessageDataComparator::Equal) {
+ QMessage::StandardFolder standardFolder = static_cast<QMessage::StandardFolder>(pf->_value.toInt());
+ if (standardFolder == QMessage::SentFolder) {
+ folderUris.append("sent");
+ if (accountIds.count() == 0) {
+ accountIds.append("local_folders");
+ }
+ }
+ handled = true;
+ }
+ }
+ break;
+ case QMessageFilterPrivate::ParentAccountIdFilter:
+ break;
+ case QMessageFilterPrivate::ParentFolderIdFilter:
+ break;
+ case QMessageFilterPrivate::TimeStamp:
+ {
+ if (pf->_comparatorType == QMessageFilterPrivate::Equality) {
+ QMessageDataComparator::EqualityComparator cmp(static_cast<QMessageDataComparator::EqualityComparator>(pf->_comparatorValue));
+ if (cmp == QMessageDataComparator::Equal) {
+ endTimeStamp = pf->_value.toDateTime();
+ startTimeStamp = pf->_value.toDateTime();
+ handled = true;
+ }
+ }
+ if (pf->_comparatorType == QMessageFilterPrivate::Relation) {
+ QMessageDataComparator::RelationComparator cmp(static_cast<QMessageDataComparator::RelationComparator>(pf->_comparatorValue));
+ if (cmp == QMessageDataComparator::LessThan) {
+ endTimeStamp = pf->_value.toDateTime();
+ handled = true;
+ } else if (cmp == QMessageDataComparator::LessThanEqual) {
+ endTimeStamp = pf->_value.toDateTime();
+ handled = true;
+ } else if (cmp == QMessageDataComparator::GreaterThan) {
+ startTimeStamp = pf->_value.toDateTime();
+ handled = true;
+ } if (cmp == QMessageDataComparator::GreaterThanEqual) {
+ startTimeStamp = pf->_value.toDateTime();
+ handled = true;
+ }
+ }
+ break;
+ }
+ case QMessageFilterPrivate::ReceptionTimeStamp:
+ {
+ if (pf->_comparatorType == QMessageFilterPrivate::Equality) {
+ QMessageDataComparator::EqualityComparator cmp(static_cast<QMessageDataComparator::EqualityComparator>(pf->_comparatorValue));
+ if (cmp == QMessageDataComparator::Equal) {
+ endReceptionTimeStamp = pf->_value.toDateTime();
+ startReceptionTimeStamp = pf->_value.toDateTime();
+ handled = true;
+ }
+ }
+ if (pf->_comparatorType == QMessageFilterPrivate::Relation) {
+ QMessageDataComparator::RelationComparator cmp(static_cast<QMessageDataComparator::RelationComparator>(pf->_comparatorValue));
+ if (cmp == QMessageDataComparator::LessThan) {
+ endReceptionTimeStamp = pf->_value.toDateTime();
+ handled = true;
+ } else if (cmp == QMessageDataComparator::LessThanEqual) {
+ endReceptionTimeStamp = pf->_value.toDateTime();
+ handled = true;
+ } else if (cmp == QMessageDataComparator::GreaterThan) {
+ startReceptionTimeStamp = pf->_value.toDateTime();
+ handled = true;
+ } if (cmp == QMessageDataComparator::GreaterThanEqual) {
+ startReceptionTimeStamp = pf->_value.toDateTime();
+ handled = true;
+ }
+ }
+ break;
+ }
+ case QMessageFilterPrivate::Sender:
+ break;
+ case QMessageFilterPrivate::Recipients:
+ break;
+ case QMessageFilterPrivate::Subject:
+ break;
+ case QMessageFilterPrivate::Status:
+ break;
+ case QMessageFilterPrivate::Priority:
+ break;
+ case QMessageFilterPrivate::Size:
+ break;
+ case QMessageFilterPrivate::None:
+ break;
+ }
+ handledFiltersCount++;
+ }
+
+ msgQueryInfo.handledFiltersCount = 0; // Do filtering also for filters which has been handled above
+
+ return searchMessages(msgQueryInfo, accountIds, folderUris, msgQueryInfo.body, startTimeStamp,
+ endTimeStamp, startReceptionTimeStamp, endReceptionTimeStamp);
+}
+
+bool ModestEngine::searchMessages(MessageQueryInfo &msgQueryInfo, const QStringList& accountIds,
+ const QStringList& folderUris, const QString& body,
+ const QDateTime& startTimeStamp, const QDateTime& endTimeStamp,
+ const QDateTime& startReceptionTimeStamp, const QDateTime& endReceptionTimeStamp) const
+{
+ Q_UNUSED(body) //TODO: Body search
+
+ qulonglong sDate = 0;
+ if (startTimeStamp.isValid() && startReceptionTimeStamp.isValid()) {
+ if (startTimeStamp < startReceptionTimeStamp) {
+ sDate = startTimeStamp.toTime_t();
+ } else {
+ sDate = startReceptionTimeStamp.toTime_t();
+ }
+ } else {
+ if (startTimeStamp.isValid()) {
+ sDate = startTimeStamp.toTime_t();
+ } else if (startReceptionTimeStamp.isValid()) {
+ sDate = startReceptionTimeStamp.toTime_t();
+ }
+ }
+
+
+ qulonglong eDate = 0;
+ if (endTimeStamp.isValid() && endReceptionTimeStamp.isValid()) {
+ if (endTimeStamp > endReceptionTimeStamp) {
+ eDate = endTimeStamp.toTime_t();
+ } else {
+ eDate = endReceptionTimeStamp.toTime_t();
+ }
+ } else {
+ if (endTimeStamp.isValid()) {
+ eDate = endTimeStamp.toTime_t();
+ } else if (endReceptionTimeStamp.isValid()) {
+ eDate = endReceptionTimeStamp.toTime_t();
+ }
+ }
+
+ if (m_pendingMessageQueries.count() == 1) {
+ // This is the first query in pending queries queue
+ // => connect to signals
+ m_QtmPluginDBusInterface->connection().connect(MODESTENGINE_QTM_PLUGIN_NAME,
+ MODESTENGINE_QTM_PLUGIN_PATH,
+ MODESTENGINE_QTM_PLUGIN_NAME,
+ "HeadersReceived",
+ (ModestEngine*)this,
+ SLOT(searchMessagesHeadersReceivedSlot(QDBusMessage)));
+
+ m_QtmPluginDBusInterface->connection().connect(MODESTENGINE_QTM_PLUGIN_NAME,
+ MODESTENGINE_QTM_PLUGIN_PATH,
+ MODESTENGINE_QTM_PLUGIN_NAME,
+ "HeadersFetched",
+ (ModestEngine*)this,
+ SLOT(searchMessagesHeadersFetchedSlot(QDBusMessage)));
+ }
+
+ QDBusMessage reply = m_QtmPluginDBusInterface->call("GetHeaders",
+ accountIds,
+ folderUris,
+ sDate,
+ eDate,
+ false);
+ if (reply.type() != QDBusMessage::ErrorMessage) {
+ m_messageCache.clear();
+ msgQueryInfo.queryId = reply.arguments().takeFirst().toInt();
+ } else {
+ // Request failed
+ int index = -1;
+ for (int i=0; i < m_pendingMessageQueries.count(); i++) {
+ if (m_pendingMessageQueries[i].queryId == msgQueryInfo.queryId) {
+ index = i;
+ break;
+ }
+ }
+ if (index > -1) {
+ m_pendingMessageQueries.removeAt(index);
+ }
+ msgQueryInfo.privateService->setFinished(false);
+
+ if (m_pendingMessageQueries.count() == 0) {
+ // This was last query in pending queries queue
+ // => Disconnect from "GetHeaders" request related DBus signals
+ // Note: Disconnecting signals is done to optimize signal handling
+ // <=> Disconnecting prevents unnecessary handling of signals
+ // which have been sent from other applications using
+ // Qt Mobility Messaging API
+ m_QtmPluginDBusInterface->connection().disconnect(MODESTENGINE_QTM_PLUGIN_NAME,
+ MODESTENGINE_QTM_PLUGIN_PATH,
+ MODESTENGINE_QTM_PLUGIN_NAME,
+ "HeadersReceived",
+ (ModestEngine*)this,
+ SLOT(searchMessagesHeadersReceivedSlot(QDBusMessage)));
+
+ m_QtmPluginDBusInterface->connection().disconnect(MODESTENGINE_QTM_PLUGIN_NAME,
+ MODESTENGINE_QTM_PLUGIN_PATH,
+ MODESTENGINE_QTM_PLUGIN_NAME,
+ "HeadersFetched",
+ (ModestEngine*)this,
+ SLOT(searchMessagesHeadersFetchedSlot(QDBusMessage)));
+ }
+ return false;
+ }
+
return true;
}
+void ModestEngine::searchMessagesHeadersReceivedSlot(QDBusMessage msg)
+{
+ QList<QVariant> arguments = msg.arguments();
+ int queryId = arguments.takeFirst().toInt();
+
+ int index = -1;
+ for (int i=0; i < m_pendingMessageQueries.count(); i++) {
+ if (m_pendingMessageQueries[i].queryId == queryId) {
+ index = i;
+ break;
+ }
+ }
+ if (index == -1) {
+ // Received DBus Message is not reply for the DBus query
+ // that was sent from this process/instance of modest engine
+ // => Continue waiting
+ return;
+ }
+
+ MessageQueryInfo &queryInfo = m_pendingMessageQueries[index];
+
+ QString reportedAccountId = arguments.takeFirst().toString();
+ QString accountId;
+ if (!queryInfo.realAccountId.isEmpty()) {
+ // Search was done to Modest "local_folders" account
+ // => Correct Qt Mobility Messaging AccountId
+ // can be taken realAccountId field
+ // => Transform Messaging AccountId to Modest accountId
+ // by removing "MO_" from the beginning of accountId string
+ accountId = queryInfo.realAccountId;
+ accountId.remove(0,3);
+ } else {
+ accountId = reportedAccountId.remove("_store");
+ }
+
+ QString folderId = arguments.takeFirst().toString();
+ QVariant variant = arguments.takeFirst();
+ QDBusArgument argument = variant.value<QDBusArgument>();
+ QList<QMap<QString, QVariant> > messages;
+ argument >> messages;
+
+ QMessageFilterPrivate::SortedMessageFilterList filters;
+ int firstUnhandledFilterIndex = 0;
+
+ QMessageFilterPrivate* pf = QMessageFilterPrivate::implementation(queryInfo.filter);
+ if (pf->_filterList.count() == 0) {
+ filters.append(queryInfo.filter);
+ } else {
+ if (queryInfo.handledFiltersCount < pf->_filterList[queryInfo.currentFilterListIndex].count()) {
+ filters = pf->_filterList[queryInfo.currentFilterListIndex];
+ }
+ }
+ firstUnhandledFilterIndex = queryInfo.handledFiltersCount;
+
+ for (int i=0; i < messages.count(); i++) {
+ MessagingModestMessage modestMessage;
+ modestMessage.accountId = accountId;
+ modestMessage.folderId = folderId;
+ modestMessage.dateReceived = 0;
+ modestMessage.dateSent = 0;
+ modestMessage.size = 0;
+ modestMessage.flags = MessagingModestMessageNotDefined;
+ modestMessage.priority = MessagingModestMessagePriorityDefined;
+ QMapIterator<QString, QVariant> j(messages[i]);
+ while (j.hasNext()) {
+ j.next();
+ if (j.key() == "url") {
+ modestMessage.url = j.value().toString();
+ } else if (j.key() == "message-uid") {
+ modestMessage.id = j.value().toString();
+ } else if (j.key() == "from") {
+ modestMessage.from = j.value().toString();
+ } else if (j.key() == "to") {
+ modestMessage.to = j.value().toString();
+ } else if (j.key() == "cc") {
+ modestMessage.cc = j.value().toString();
+ } else if (j.key() == "bcc") {
+ modestMessage.bcc = j.value().toString();
+ } else if (j.key() == "replyto") {
+ modestMessage.replyTo = j.value().toString();
+ } else if (j.key() == "subject") {
+ modestMessage.subject = j.value().toString();
+ } else if (j.key() == "date-received") {
+ modestMessage.dateReceived = j.value().toLongLong();
+ } else if (j.key() == "date-sent") {
+ modestMessage.dateSent = j.value().toLongLong();
+ } else if (j.key() == "size") {
+ modestMessage.size = j.value().toLongLong();
+ } else if (j.key() == "flags") {
+ modestMessage.flags = static_cast<MessagingModestMessageFlags>(j.value().toUInt());
+ } else if (j.key() == "priority") {
+ modestMessage.priority = static_cast<MessagingModestMessagePriority>(j.value().toUInt());
+ }
+ }
+
+ QMessage message = messageFromModestMessage(modestMessage);
+ if (reportedAccountId == "local_folders") {
+ QMessagePrivate* privateMessage = QMessagePrivate::implementation(message);
+ QString id = privateMessage->_id.toString();
+ QString newProtocol = "maildir";
+ replaceProtocol(id, newProtocol);
+ privateMessage->_id = QMessageId(id);
+ id = privateMessage->_parentFolderId.toString();
+ replaceProtocol(id, newProtocol);
+ privateMessage->_parentFolderId = QMessageFolderId(id);
+ }
+ if (filterMessage(message, filters, 0)) {
+ if (!queryInfo.ids.contains(message.id())) {
+ if (m_messageCache.size() < maxCacheSize) {
+ m_messageCache.insert(message.id().toString(), message);
+ }
+ queryInfo.ids.append(message.id());
+ }
+ }
+ }
+}
+
+void ModestEngine::searchMessagesHeadersFetchedSlot(QDBusMessage msg)
+{
+ QList<QVariant> arguments = msg.arguments();
+ int queryId = arguments.takeFirst().toInt();
+
+ int index = -1;
+ for (int i=0; i < m_pendingMessageQueries.count(); i++) {
+ if (m_pendingMessageQueries[i].queryId == queryId) {
+ index = i;
+ break;
+ }
+ }
+ if (index == -1) {
+ // Received DBus Message is not reply for the DBus query
+ // that was sent from this process/instance of modest engine
+ // => Continue waiting
+ return;
+ }
+
+ handleQueryFinished(index);
+}
+
+void ModestEngine::handleQueryFinished(int index) const
+{
+ MessageQueryInfo &queryInfo = m_pendingMessageQueries[index];
+
+ queryInfo.currentFilterListIndex++;
+ QMessageFilterPrivate* pf = QMessageFilterPrivate::implementation(queryInfo.filter);
+ if (queryInfo.currentFilterListIndex < pf->_filterList.count()) {
+ if (queryAndFilterMessages(queryInfo)) {
+ // Continue searching
+ return;
+ }
+ }
+
+ MessagingHelper::orderMessages(queryInfo.ids, queryInfo.sortOrder);
+ MessagingHelper::applyOffsetAndLimitToMessageIdList(queryInfo.ids, queryInfo.limit, queryInfo.offset);
+
+ // Search finished
+ if (queryInfo.isQuery) {
+ queryInfo.privateService->messagesFound(queryInfo.ids, true, true);
+ } else {
+ queryInfo.privateService->messagesCounted(queryInfo.ids.count());
+ }
+ m_pendingMessageQueries.removeAt(index);
+
+ if (m_pendingMessageQueries.count() == 0) {
+ // This was last query in pending queries queue
+ // => Disconnect from "GetHeaders" request related DBus signals
+ // Note: Disconnecting signals is done to optimize signal handling
+ // <=> Disconnecting prevents unnecessary handling of signals
+ // which have been sent from other applications using
+ // Qt Mobility Messaging API
+ m_QtmPluginDBusInterface->connection().disconnect(MODESTENGINE_QTM_PLUGIN_NAME,
+ MODESTENGINE_QTM_PLUGIN_PATH,
+ MODESTENGINE_QTM_PLUGIN_NAME,
+ "HeadersReceived",
+ (ModestEngine*)this,
+ SLOT(searchMessagesHeadersReceivedSlot(QDBusMessage)));
+
+ m_QtmPluginDBusInterface->connection().disconnect(MODESTENGINE_QTM_PLUGIN_NAME,
+ MODESTENGINE_QTM_PLUGIN_PATH,
+ MODESTENGINE_QTM_PLUGIN_NAME,
+ "HeadersFetched",
+ (ModestEngine*)this,
+ SLOT(searchMessagesHeadersFetchedSlot(QDBusMessage)));
+ }
+}
+
+void
+ModestEngine::sendEmailCallEnded(QDBusPendingCallWatcher *watcher)
+{
+ if (watcher->isError ()) {
+ // TODO: Emit a failure
+ qWarning() << "Failed to send email via modest: " << watcher->error();
+ } else {
+ // TODO: Emit a success (or put to outbox)
+ qDebug() << "Message should be outboxed now...";
+ }
+}
+
+void
+ModestEngine::addMessageCallEnded(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QString> reply = *watcher;
+ if (reply.isError ()) {
+ // TODO: Emit a failure
+ qWarning() << "Failed to add message via modest: " << reply.error();
+ } else {
+ QString id = reply.argumentAt<0>();
+ // TODO: Emit a success ... with message's id?
+ qDebug() << "Message with id" << id << "should be added now";
+ }
+}
+
+void ModestEngine::folderUpdatedSlot(QDBusMessage msg)
+{
+ QList<QVariant> arguments = msg.arguments();
+ QString modestAccountId = arguments.takeFirst().toString();
+ QString modestFolderId = arguments.takeFirst().toString();
+ QMessageFolderId updatedFolderId;
+
+ if (modestAccountId == "local_folders") {
+ updatedFolderId = QMessageFolderId("MO_LOCAL&maildir&"+modestFolderId);
+ } else {
+ QMessageAccountId accountId = QMessageAccountId("MO_"+escapeString(modestAccountId));
+ QString protocol = accountEmailProtocolAsString(accountId);
+ if ((protocol == "pop") && (modestFolderId == "INBOX")) {
+ modestFolderId = "cache";
+ }
+ updatedFolderId = QMessageFolderId(accountId.toString()+"&"+protocol+"&"+modestFolderId);
+ }
+
+ int messagesPerAccount = 5;
+ QDBusPendingCall pendingCall = m_ModestDBusInterface->asyncCall(MODEST_DBUS_METHOD_GET_UNREAD_MESSAGES,
+ messagesPerAccount);
+ QDBusPendingCallWatcher* pendingCallWatcher = new QDBusPendingCallWatcher(pendingCall);
+ pendingCallWatcher->setProperty("folderId", updatedFolderId.toString());
+ connect(pendingCallWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
+ this, SLOT(pendingGetUnreadMessagesFinishedSlot(QDBusPendingCallWatcher*)));
+}
+
+void ModestEngine::pendingGetUnreadMessagesFinishedSlot(QDBusPendingCallWatcher* pendingCallWatcher)
+{
+ QDBusMessage msg = pendingCallWatcher->reply();
+ QVariant variant = msg.arguments().takeFirst();
+ QDBusArgument argument = variant.value<QDBusArgument>();
+ QList<ModestAccountsUnreadMessagesDBusStruct> accountsWithUnreadMessages;
+ argument >> accountsWithUnreadMessages;
+
+ bool setOnlyDates = false;
+ if (pendingCallWatcher->property("setOnlyDates").isValid()) {
+ setOnlyDates = true;
+ }
+ QMessageFolderId folderId;
+ if (pendingCallWatcher->property("folderId").isValid()) {
+ folderId = QMessageFolderId(pendingCallWatcher->property("folderId").toString());
+ }
+
+ for (int i=0; i < accountsWithUnreadMessages.count(); i++) {
+ QDateTime newLatestTimeStamp;
+ QDateTime latestTimeStamp = accountsLatestTimestamp.take(accountsWithUnreadMessages[i].accountId);
+ for (int j=0; j < accountsWithUnreadMessages[i].unreadMessages.count(); j++) {
+ ModestUnreadMessageDBusStruct unreadMessage = accountsWithUnreadMessages[i].unreadMessages[j];
+ QDateTime time = QDateTime::fromTime_t(unreadMessage.timeStamp);
+ if (time > newLatestTimeStamp) {
+ newLatestTimeStamp = time;
+ }
+ if (!setOnlyDates) {
+ if (time > latestTimeStamp) {
+ searchNewMessages(unreadMessage.subject, QString(), time, time, MODEST_DBUS_SEARCH_SUBJECT, 0);
+ }
+ }
+ }
+ if (newLatestTimeStamp.isValid()) {
+ accountsLatestTimestamp.remove(accountsWithUnreadMessages[i].accountId);
+ accountsLatestTimestamp.insert(accountsWithUnreadMessages[i].accountId, newLatestTimeStamp);
+ } else {
+ accountsLatestTimestamp.insert(accountsWithUnreadMessages[i].accountId, QDateTime::currentDateTime());
+ }
+ }
+}
+
+void ModestEngine::searchNewMessages(const QString& searchString, const QString& folderToSearch,
+ const QDateTime& startDate, const QDateTime& endDate,
+ int searchflags, uint minimumMessageSize) const
+{
+ qlonglong sDate = 0;
+ if (startDate.isValid()) {
+ sDate = startDate.toTime_t();
+ }
+ qlonglong eDate = 0;
+ if (endDate.isValid()) {
+ eDate = endDate.toTime_t();
+ }
+
+ QDBusInterface interface(MODEST_DBUS_SERVICE, MODEST_DBUS_OBJECT, MODEST_DBUS_IFACE);
+ QDBusPendingCall pendingCall = interface.asyncCall(MODEST_DBUS_METHOD_SEARCH,
+ searchString,
+ folderToSearch,
+ sDate,
+ eDate,
+ searchflags,
+ minimumMessageSize);
+ QDBusPendingCallWatcher* pendingCallWatcher = new QDBusPendingCallWatcher(pendingCall);
+ connect(pendingCallWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
+ this, SLOT(pendingSearchFinishedSlot(QDBusPendingCallWatcher*)));
+}
+
+void ModestEngine::pendingSearchFinishedSlot(QDBusPendingCallWatcher* pendingCallWatcher)
+{
+ QDBusMessage msg = pendingCallWatcher->reply();
+ QVariant variant = msg.arguments().takeFirst();
+ QDBusArgument argument = variant.value<QDBusArgument>();
+ QList<ModestMessage> messages;
+ argument >> messages;
+
+ for (int i=0; i < messages.count(); i++) {
+ notification(messageIdFromModestMessageId(messages[i].id), ModestEngine::Added);
+ }
+}
+
+void ModestEngine::messageReadChangedSlot(QDBusMessage msg)
+{
+ QString changedMessageId = msg.arguments().takeFirst().toString();
+ notification(messageIdFromModestMessageId(changedMessageId), ModestEngine::Updated);
+}
+
+QMessageManager::NotificationFilterId ModestEngine::registerNotificationFilter(QMessageStorePrivate& messageStore,
+ const QMessageFilter &filter,
+ QMessageManager::NotificationFilterId id)
+{
+ m_messageStore = &messageStore;
+
+ int filterId = id;
+ if (filterId == 0) {
+ filterId = ++m_filterId;
+ }
+ m_filters.insert(filterId, filter);
+ return filterId;
+}
+
+void ModestEngine::unregisterNotificationFilter(QMessageManager::NotificationFilterId notificationFilterId)
+{
+ m_filters.remove(notificationFilterId);
+}
+
+QByteArray ModestEngine::getMimePart (const QMessageId &id, const QString &attachmentId)
+{
+ QByteArray result;
+
+ QString modestAccountId = modestAccountIdFromMessageId(id);
+ QString modestFolderId = modestFolderIdFromMessageId(id);
+ QString modestMessageId = modestMessageIdFromMessageId(id);
+
+ QString filePath, mimeType;
+ int mimeSize = -1;
+ bool expunge = false, isAttachment = false;
+
+ QDBusPendingReply<QString, QString, int, bool, bool> reply =
+ m_QtmPluginDBusInterface->asyncCall(
+ "GetMimePart",
+ QVariant::fromValue(modestAccountId),
+ QVariant::fromValue(modestFolderId),
+ QVariant::fromValue(modestMessageId),
+ QVariant::fromValue(attachmentId));
+
+ reply.waitForFinished();
+
+ if (reply.isError()) {
+ qWarning () << reply.error();
+ return result;
+ }
+
+ filePath = reply.argumentAt<0>();
+ mimeType = reply.argumentAt<1>();
+ mimeSize = reply.argumentAt<2>();
+ isAttachment = reply.argumentAt<3>();
+ expunge = reply.argumentAt<4>();
+
+ if (filePath.isEmpty()) {
+ qWarning() << "Received empty file path!";
+ return result;
+ }
+
+ QFile file(filePath);
+
+ if (file.open(QIODevice::ReadWrite) == false) {
+ qWarning() << "Failed to open file" << filePath << ": "
+ << file.error();
+ return result;
+ }
+
+ result = file.readAll();
+
+ if (expunge) {
+ file.remove();
+ } else {
+ file.close();
+ }
+
+ return result;
+}
+
+void ModestEngine::notification(const QMessageId& messageId, NotificationType notificationType) const
+{
+ QMessageId realMessageId = messageId;
+
+ if (notificationType == ModestEngine::Removed) {
+ // Make sure that there will not be many Removed notifications
+ // in a row for a same message
+ QString modestMessageId = modestMessageIdFromMessageId(messageId);
+ if (!m_latestRemoveNotifications.contains(modestMessageId)) {
+ if (m_latestRemoveNotifications.count() > 10) {
+ // Remove oldest notification from the beginning of the list
+ m_latestRemoveNotifications.removeFirst();
+ }
+ // Append new notification
+ m_latestRemoveNotifications.append(modestMessageId);
+ } else {
+ return;
+ }
+ }
+
+ QMessageManager::NotificationFilterIdSet matchingFilters;
+
+ // Copy the filter map to protect against modification during traversal
+ QMap<int, QMessageFilter> filters(m_filters);
+ QMap<int, QMessageFilter>::const_iterator it = filters.begin(), end = filters.end();
+ QMessage message;
+ bool messageRetrieved = false;
+ for ( ; it != end; ++it) {
+ const QMessageFilter &filter(it.value());
+
+ if (!messageRetrieved) {
+ QString modestAccountId = modestAccountIdFromMessageId(messageId);
+ QString modestFolderId = modestFolderIdFromMessageId(messageId);
+ QString modestMessageId = modestMessageIdFromMessageId(messageId);
+
+ MessagingModestMessage modestMessage = messageFromModest(modestAccountId,
+ modestFolderId,
+ modestMessageId);
+
+ if (modestMessage.accountId.isEmpty()) {
+ return;
+ }
+
+ if (modestMessage.flags & MessagingModestMessageDeleted) {
+ notificationType = ModestEngine::Removed;
+ }
+ if (modestMessage.size == 0) {
+ notificationType = ModestEngine::Removed;
+ }
+
+ message = messageFromModestMessage(modestMessage);
+ realMessageId =message.id();
+ messageRetrieved = true;
+ }
+
+ if (filter.isEmpty()) {
+ // Empty filter matches to all messages
+ matchingFilters.insert(it.key());
+ } else {
+ if (message.type() == QMessage::NoType) {
+ continue;
+ }
+ QMessageFilterPrivate* privateMessageFilter = QMessageFilterPrivate::implementation(filter);
+ if (privateMessageFilter->filter(message)) {
+ matchingFilters.insert(it.key());
+ }
+ }
+ }
+
+ if (matchingFilters.count() > 0) {
+ if (notificationType == ModestEngine::Added) {
+ m_messageStore->messageNotification(QMessageStorePrivate::Added, realMessageId, matchingFilters);
+ } else if (notificationType == ModestEngine::Updated) {
+ m_messageStore->messageNotification(QMessageStorePrivate::Updated, realMessageId, matchingFilters);
+ } else if (notificationType == ModestEngine::Removed) {
+ m_messageStore->messageNotification(QMessageStorePrivate::Removed, realMessageId, matchingFilters);
+ }
+ }
+}
+
+QMessageAccountId ModestEngine::accountIdFromModestMessageId(const QString& modestMessageId) const
+{
+ // Modest messageId format is following:
+ // <email protocol>://<username>@<hostname>:<port>...
+ QMessageAccountId accountId;
+
+ int usernameBegin = modestMessageId.indexOf("//")+2;
+ int usernameEnd = modestMessageId.indexOf("@");
+ QString username = QUrl::fromPercentEncoding(modestMessageId.mid(usernameBegin, usernameEnd-usernameBegin).toUtf8());
+ int hostnameBegin = usernameEnd+1;
+ int hostnameEnd = modestMessageId.indexOf(':',hostnameBegin);
+ QString hostname = QUrl::fromPercentEncoding(modestMessageId.mid(hostnameBegin, hostnameEnd-hostnameBegin).toUtf8());
+
+ foreach (QMessageAccount value, iAccounts) {
+ QMessageAccountId accId = value.id();
+ if ((accountUsername(accId) == username) &&
+ (accountHostname(accId) == hostname)) {
+ accountId = accId;
+ }
+ }
+
+ if (!accountId.isValid()) {
+ if (modestMessageId.left(modestMessageId.indexOf(':')) == "maildir") {
+ accountId = QMessageAccountId("MO_LOCAL");
+ }
+ }
+
+ return accountId;
+}
+
+QMessageFolderId ModestEngine::folderIdFromModestMessageId(const QString& modestMessageId,
+ const QMessageAccountId accountId) const
+{
+ // Modest messageId format is following:
+ // <email protocol>://<username>@<hostname>:<port>...
+ QMessageFolderId folderId;
+ QString folderIdAsString;
+
+ if (!accountId.isValid()) {
+ folderIdAsString = accountIdFromModestMessageId(modestMessageId).toString();
+ } else {
+ folderIdAsString = accountId.toString();
+ }
+
+ int protocolEnd = modestMessageId.indexOf(':');
+ QString protocol = modestMessageId.left(protocolEnd);
+ folderIdAsString += "&" + protocol;
+ if (protocol == "pop") {
+ folderIdAsString += "&cache";
+ } else if (protocol == "imap") {
+ int pathBegin = modestMessageId.indexOf('/',modestMessageId.lastIndexOf(':'))+1;
+ int pathEnd = modestMessageId.lastIndexOf('/');
+ folderIdAsString += "&" + modestMessageId.mid(pathBegin, pathEnd-pathBegin);
+ } else if (protocol == "maildir") {
+ int pathBegin = modestMessageId.indexOf('#')+1;
+ int pathEnd = modestMessageId.lastIndexOf('/');
+ folderIdAsString += "&" + modestMessageId.mid(pathBegin, pathEnd-pathBegin);
+ }
+ folderId = QMessageFolderId(QUrl::fromPercentEncoding(folderIdAsString.toUtf8()));
+
+ return folderId;
+}
+
+QString ModestEngine::modestAccountIdFromAccountId(const QMessageAccountId& accountId) const
+{
+ // Just remove "MO_" prefix from the beginning of id string
+ return accountId.toString().remove(0,3);
+}
+
+QString ModestEngine::modestFolderIdFromFolderId(const QMessageFolderId& folderId) const
+{
+ QString modestFolderId;
+
+ QString folderIdString = folderId.toString();
+ int protocolBegin = folderIdString.indexOf('&');
+ int protocolEnd = folderIdString.lastIndexOf('&');
+
+ modestFolderId = folderIdString.mid(protocolEnd+1);
+ QString protocol = folderIdString.mid(protocolBegin+1,protocolEnd-protocolBegin-1);
+ if ((protocol == "pop") && (modestFolderId == "cache")) {
+ modestFolderId = "INBOX";
+ }
+
+ return modestFolderId;
+}
+
+QString ModestEngine::modestFolderUriFromFolderId(const QMessageFolderId& folderId) const
+{
+ Q_UNUSED(folderId) //TODO:
+ return QString();
+}
+
+QString ModestEngine::modestAccountIdFromMessageId(const QMessageId& messageId,
+ bool checkProtocol) const
+{
+ QString id = messageId.toString();
+ int protocolBegin = id.indexOf('&');
+ int protocolEnd = id.lastIndexOf('&');
+ QString protocol = id.mid(protocolBegin+1,protocolEnd-protocolBegin-1);
+ if (checkProtocol && protocol == "maildir") {
+ return "local_folders";
+ }
+
+ return unescapeString(id.left(protocolBegin).remove(0,3));
+}
+
+QMessageAccountId ModestEngine::accountIdFromFolderId(const QMessageFolderId& folderId) const
+{
+ QString id = folderId.toString();
+ int protocolBegin = id.indexOf('&');
+ return QMessageAccountId(id.left(protocolBegin));
+}
+
+QMessageAccountId ModestEngine::accountIdFromMessageId(const QMessageId& messageId) const
+{
+ QString id = messageId.toString();
+ int protocolBegin = id.indexOf('&');
+ return QMessageAccountId(id.left(protocolBegin));
+}
+
+QString ModestEngine::modestAccountIdFromFolderId(const QMessageFolderId& folderId,
+ bool checkProtocol) const
+{
+ QString id = folderId.toString();
+ int protocolBegin = id.indexOf('&');
+ int protocolEnd = id.lastIndexOf('&');
+ QString protocol = id.mid(protocolBegin+1,protocolEnd-protocolBegin-1);
+ if (checkProtocol && protocol == "maildir") {
+ return "local_folders";
+ }
+
+ return unescapeString(id.left(protocolBegin).remove(0,3));
+}
+
+QString ModestEngine::modestFolderIdFromMessageId(const QMessageId& messageId) const
+{
+ QString id = messageId.toString();
+
+ int protocolBegin = id.indexOf('&');
+ int protocolEnd = id.lastIndexOf('&');
+ int folderEnd = id.lastIndexOf('/');
+
+ QString protocol = id.mid(protocolBegin+1,protocolEnd-protocolBegin-1);
+ id = id.mid(protocolEnd+1,folderEnd-protocolEnd-1);
+ if ((protocol == "pop") && (id == "cache")) {
+ id = "INBOX";
+ }
+
+ return id;
+}
+
+void ModestEngine::replaceProtocol(QString& id, const QString& newProtocol) const
+{
+ int protocolBegin = id.indexOf('&');
+ int protocolEnd = id.lastIndexOf('&');
+ id.remove(protocolBegin+1,protocolEnd-protocolBegin-1);
+ id.insert(protocolBegin+1, newProtocol);
+}
+
+QMessageAccountId ModestEngine::realAccountId(const MessagingModestMessage& modestMessage) const
+{
+ QMessageAccountId accountId;
+
+ if (modestMessage.accountId == "local_folders") {
+ QString accountIdString;
+ // Message is in local foldar, but message can be linked
+ // to actual account using 'From', 'To', 'Cc' or 'Bcc' fields
+ foreach (QMessageAccount value, iAccounts) {
+ QMessageAccountPrivate* privAccount = QMessageAccountPrivate::implementation(value);
+ if (modestMessage.from.contains(privAccount->_address.addressee())) {
+ accountIdString = value.id().toString();
+ break;
+ } else if (modestMessage.to.contains(privAccount->_address.addressee())) {
+ accountIdString = value.id().toString();
+ break;
+ } else if (modestMessage.cc.contains(privAccount->_address.addressee())) {
+ accountIdString = value.id().toString();
+ break;
+ } else if (modestMessage.bcc.contains(privAccount->_address.addressee())) {
+ accountIdString = value.id().toString();
+ break;
+ }
+ }
+ if (!accountIdString.isEmpty()) {
+ accountId = QMessageAccountId(accountIdString);
+ }
+ } else {
+ accountId = accountIdFromModestAccountId(modestMessage.accountId);
+ }
+
+ return accountId;
+}
+
+QString ModestEngine::modestMessageIdFromMessageId(const QMessageId& messageId) const
+{
+ QString id = messageId.toString();
+ return id.mid(id.lastIndexOf('/')+1);
+}
+
+QMessageAccountId ModestEngine::accountIdFromModestAccountId(const QString& accountId) const
+{
+ // Just add "MO_" prefix to the beginning of id string & escape created Id
+ return QMessageAccountId(escapeString("MO_"+accountId));
+}
+
+QMessageFolderId ModestEngine::folderIdFromModestFolderId(const QMessageAccountId& accountId,
+ bool isLocalFolder,
+ const QString& modestFolderId) const
+{
+ // Format: <accountId>&<email protocol>&<Modest folderId>
+ QMessageFolderId folderId;
+
+ if (isLocalFolder) {
+ folderId = QMessageFolderId(accountId.toString()+"&maildir&"+modestFolderId);
+ } else {
+ QString protocol = accountEmailProtocolAsString(accountId);
+ if ((protocol == "pop") && (modestFolderId == "INBOX")) {
+ folderId = QMessageFolderId(accountId.toString()+"&"+protocol+"&cache");
+ } else {
+ folderId = QMessageFolderId(accountId.toString()+"&"+protocol+"&"+modestFolderId);
+ }
+ }
+
+ return folderId;
+}
+
+QMessageId ModestEngine::messageIdFromModestMessageId(const QString& messageId) const
+{
+ QString messageIdString = folderIdFromModestMessageId(messageId).toString();
+ int idPartBegin = messageId.lastIndexOf('/');
+ messageIdString += messageId.mid(idPartBegin, idPartBegin-messageId.length());
+ return QMessageId(messageIdString);
+}
+
+QMessageId ModestEngine::messageIdFromModestMessageFilePath(const QString& messageFilePath) const
+{
+ QString messageIdString;
+
+ QString filePath = messageFilePath;
+ QString localRootFolder = this->localRootFolder();
+ if (filePath.startsWith(localRootFolder)) {
+ messageIdString = "MO_LOCAL&maildir&";
+ filePath.remove(0,localRootFolder.length()+1);
+ filePath.remove("/cur");
+ messageIdString += filePath.left(filePath.lastIndexOf('!'));
+ } else {
+ foreach (QMessageAccount value, iAccounts) {
+ QMessageAccountId accountId = value.id();
+ QString rootFolder = accountRootFolder(accountId);
+ if (filePath.startsWith(rootFolder)) {
+ QString protocol = accountEmailProtocolAsString(accountId);
+ messageIdString = accountId.toString()+"&"+protocol+"&";
+ filePath.remove(0,rootFolder.length()+1);
+ filePath.remove("/subfolders");
+ messageIdString += filePath.left(filePath.lastIndexOf('.'));
+ if (protocol == "pop") {
+ QDir dir(messageFilePath);
+ dir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot);
+ QFileInfoList dirs = dir.entryInfoList();
+ if (dirs.count() > 0) {
+ QString fileName = dirs[0].fileName();
+ // Remove folder that contains actual message
+ messageIdString = messageIdString.left(messageIdString.lastIndexOf('/')+1);
+ // Add message name
+ messageIdString = messageIdString+fileName;
+ }
+ }
+ }
+ }
+ }
+
+ return QMessageId(messageIdString);
+}
+
+QString ModestEngine::unescapeString(const QString& string)
+{
+ QString unescapedString;
+
+ QByteArray str = string.toUtf8();
+ gchar* unescaped_string = gconf_unescape_key(str.data(), str.length());
+ unescapedString = QString::fromUtf8(unescaped_string);
+ g_free(unescaped_string);
+
+ return unescapedString;
+}
+
+QString ModestEngine::escapeString(const QString& string)
+{
+ QString escapedString;
+
+ QByteArray str = unescapeString(string).toUtf8();
+ gchar* escaped_string = gconf_escape_key(str.data(), str.length());
+ escapedString = QString::fromUtf8(escaped_string);
+ g_free(escaped_string);
+
+ return escapedString;
+}
+
+INotifyWatcher::INotifyWatcher()
+{
+ // Initialize inotify instance
+ // => returned file descriptor is associated with
+ // a new inotify event queue
+ // O_CLOEXEC flag makes sure that file descriptor
+ // is closed if execution is transfered
+ // from this process to a new program
+ // (Check more info from 'execve' documentation)
+#ifdef IN_CLOEXEC
+ m_inotifyFileDescriptor = inotify_init1(IN_CLOEXEC);
+#else
+ m_inotifyFileDescriptor = inotify_init();
+ if (m_inotifyFileDescriptor >= 0) {
+ ::fcntl(m_inotifyFileDescriptor, F_SETFD, FD_CLOEXEC);
+ }
+#endif
+ if (m_inotifyFileDescriptor >= 0) {
+ // Change thread affinity for this object to this
+ // thread.
+ // => Event processing (for this objects events) will
+ // be done in this thread
+ moveToThread(this);
+ }
+}
+
+INotifyWatcher::~INotifyWatcher()
+{
+ // Tell the thread's event loop to exit
+ // => thread returns from the call to exec()
+ exit();
+
+ // Wait until this thread has finished execution
+ // <=> waits until thread returns from run()
+ wait();
+
+ clear();
+
+ // Close file descriptor that's referring to inotify instance
+ // => The underlying inotify object and its resources are freed
+ ::close(m_inotifyFileDescriptor);
+}
+
+void INotifyWatcher::clear()
+{
+ // Remove all watches from inotify instance watch list
+ QMapIterator<int, QString> i(m_dirs);
+ while (i.hasNext()) {
+ inotify_rm_watch(m_inotifyFileDescriptor, i.next().key());
+ }
+ m_dirs.clear();
+
+ QMapIterator<int, QString> j(m_files);
+ while (j.hasNext()) {
+ inotify_rm_watch(m_inotifyFileDescriptor, j.next().key());
+ }
+ m_files.clear();
+}
+
+void INotifyWatcher::run()
+{
+ // Start listening inotify
+ QSocketNotifier socketNotifier(m_inotifyFileDescriptor, QSocketNotifier::Read, this);
+ connect(&socketNotifier, SIGNAL(activated(int)), SLOT(notifySlot()));
+
+ // Enter the thread event loop
+ (void) exec();
+}
+
+int INotifyWatcher::addFile(const QString& path, uint eventsToWatch)
+{
+ int watchDescriptor = 0;
+ QMutexLocker locker(&m_mutex);
+
+ if (m_inotifyFileDescriptor >= 0) {
+ int watchDescriptor = 0;
+ if (eventsToWatch == 0) {
+ watchDescriptor = inotify_add_watch(m_inotifyFileDescriptor,
+ QFile::encodeName(path),
+ 0 | IN_ATTRIB
+ | IN_MODIFY
+ | IN_MOVE
+ | IN_MOVE_SELF
+ | IN_DELETE_SELF);
+ } else {
+ watchDescriptor = inotify_add_watch(m_inotifyFileDescriptor,
+ QFile::encodeName(path),
+ eventsToWatch);
+ }
+ if (watchDescriptor > 0) {
+ m_files.insert(watchDescriptor, path);
+ } else {
+ watchDescriptor = 0;
+ }
+ }
+
+ // Start thread (if thread is not already running)
+ start();
+
+ return watchDescriptor;
+}
+
+int INotifyWatcher::addDirectory(const QString& path, uint eventsToWatch)
+{
+ int watchDescriptor = 0;
+ QMutexLocker locker(&m_mutex);
+
+ if (m_inotifyFileDescriptor >= 0) {
+ int watchDescriptor = 0;
+ if (eventsToWatch == 0) {
+ watchDescriptor = inotify_add_watch(m_inotifyFileDescriptor,
+ QFile::encodeName(path),
+ 0 | IN_ATTRIB
+ | IN_MOVE
+ | IN_CREATE
+ | IN_DELETE
+ | IN_DELETE_SELF);
+ } else {
+ watchDescriptor = inotify_add_watch(m_inotifyFileDescriptor,
+ QFile::encodeName(path),
+ eventsToWatch);
+ }
+ if (watchDescriptor > 0) {
+ m_dirs.insert(watchDescriptor, path);
+ } else {
+ watchDescriptor = 0;
+ }
+ }
+
+ // Start thread (if thread is not already running)
+ start();
+
+ return watchDescriptor;
+}
+
+QStringList INotifyWatcher::directories() const
+{
+ return m_dirs.values();
+}
+
+QStringList INotifyWatcher::files() const
+{
+ return m_dirs.values();
+}
+
+void INotifyWatcher::notifySlot()
+{
+ QMutexLocker locker(&m_mutex);
+
+ int bufferSize = 0;
+ ioctl(m_inotifyFileDescriptor, FIONREAD, (char*) &bufferSize);
+ QVarLengthArray<char, 4096> buffer(bufferSize);
+ bufferSize = read(m_inotifyFileDescriptor, buffer.data(), bufferSize);
+ const char* at = buffer.data();
+ const char* const end = at + bufferSize;
+
+ QMap<int, INotifyEvent> eventsForWatchedFile;
+ QMap<QString, INotifyEvent> eventsForFileInWatchedDirectory;
+ while (at < end) {
+ const inotify_event *event = reinterpret_cast<const inotify_event *>(at);
+ if (m_files.contains(event->wd)) {
+ // File event handling
+ if (eventsForWatchedFile.contains(event->wd)) {
+ // There is already unhandled event for this file in queue
+ // => Mask is ORed to existing event
+ eventsForWatchedFile[event->wd].mask |= event->mask;
+ } else {
+ // There is no event for this file in queue
+ // => New event is created
+ INotifyEvent inotifyEvent;
+ inotifyEvent.watchDescriptor = event->wd;
+ inotifyEvent.mask = event->mask;
+ inotifyEvent.fileName = QString::fromAscii(event->name, event->len);
+ eventsForWatchedFile.insert(event->wd, inotifyEvent);
+ }
+ } else {
+ // Directory event handling
+ QString changeForFileInDirectory = QString::fromAscii(event->name, event->len);
+ // Remove unnecessary postfix (starting with '!') from the file name
+ changeForFileInDirectory = changeForFileInDirectory.left(changeForFileInDirectory.lastIndexOf('!'));
+ if (!changeForFileInDirectory.isEmpty()) {
+ QString eventId = QString::number(event->wd)+changeForFileInDirectory;
+ if (eventsForFileInWatchedDirectory.contains(eventId)) {
+ // There is already unhandled event for this file in queue
+ // => Mask is ORed to existing event
+ eventsForFileInWatchedDirectory[eventId].mask |= event->mask;
+ } else {
+ // There is no event for this file in queue
+ // => New event is created
+ INotifyEvent inotifyEvent;
+ inotifyEvent.watchDescriptor = event->wd;
+ inotifyEvent.mask = event->mask;
+ inotifyEvent.fileName = QString::fromAscii(event->name, event->len);
+ eventsForFileInWatchedDirectory.insert(eventId, inotifyEvent);
+ }
+ }
+ }
+ at += sizeof(inotify_event) + event->len;
+ }
+
+ QMap<int, INotifyEvent>::const_iterator it = eventsForWatchedFile.constBegin();
+ while (it != eventsForWatchedFile.constEnd()) {
+ INotifyEvent event = *it;
+ QString file = m_files.value(event.watchDescriptor);
+ if (!file.isEmpty()) {
+ emit fileChanged(event.watchDescriptor, file, event.mask);
+ }
+ ++it;
+ }
+
+ QMap<QString, INotifyEvent>::const_iterator jt = eventsForFileInWatchedDirectory.constBegin();
+ while (jt != eventsForFileInWatchedDirectory.constEnd()) {
+ INotifyEvent event = *jt;
+ QString file = m_dirs.value(event.watchDescriptor)+"/"+event.fileName;
+ emit fileChanged(event.watchDescriptor, file, event.mask);
+ ++jt;
+ }
+}
+
+ModestStringMap ModestEngine::getModestSenderInfo(QMessage &message)
+{
+ QMessageAddress address;
+ ModestStringMap senderInfo;
+ QMessageAccountId accountId;
+ QString value;
+
+ accountId = message.parentAccountId();
+ if (accountId.isValid() == false) {
+ qWarning () << "Account ID is invalid";
+ return ModestStringMap();
+ }
+
+ senderInfo["account-name"] = unescapeString(modestAccountIdFromAccountId(accountId));
+
+ QMessageAccount messageAccount = account(accountId);
+ QMessageAccountPrivate* privAccount = QMessageAccountPrivate::implementation(messageAccount);
+ address = privAccount->_address;
+ value = address.addressee();
+
+ if (value.isEmpty() == false && value.isNull() == false) {
+ senderInfo["from"] = value;
+ }
+
+ return senderInfo;
+}
+
+ModestStringMap ModestEngine::getModestRecipients(QMessage &message)
+{
+ QMessageAddressList addresses;
+ QMessageAddress address;
+ ModestStringMap recipients;
+ QString value;
+
+ addresses = message.to();
+ value.clear();
+ for (int i = 0; i < addresses.length(); i++) {
+ address = addresses[i];
+
+ if (value.isEmpty()) {
+ value = address.addressee();
+ } else {
+ value.append (",");
+ value.append (address.addressee());
+ }
+ }
+
+ if (value.isEmpty() == false && value.isNull() == false) {
+ recipients["to"] = value;
+ }
+
+ addresses = message.cc();
+ value.clear();
+ for (int i = 0; i < addresses.length(); i++) {
+ address = addresses[i];
+
+ if (value.isEmpty()) {
+ value = address.addressee();
+ } else {
+ value.append (",");
+ value.append (address.addressee());
+ }
+ }
+
+ if (value.isEmpty() == false && value.isNull() == false) {
+ recipients["cc"] = value;
+ }
+
+ addresses = message.bcc();
+ value.clear();
+ for (int i = 0; i < addresses.length(); i++) {
+ address = addresses[i];
+
+ if (value.isEmpty()) {
+ value = address.addressee();
+ } else {
+ value.append (",");
+ value.append (address.addressee());
+ }
+ }
+
+ if (value.isEmpty() == false && value.isNull() == false) {
+ recipients["bcc"] = value;
+ }
+
+ return recipients;
+}
+
+ModestStringMap ModestEngine::getModestMessageData(QMessage &message)
+{
+ QMessageContentContainerId bodyId;
+ QMessageContentContainer body;
+ ModestStringMap messageData;
+ QString value;
+
+ value = message.subject();
+
+ if (value.isEmpty() == false && value.isNull() == false) {
+ messageData["subject"] = value;
+ }
+
+ bodyId = message.bodyId();
+ if (bodyId.isValid()) {
+ body = message.find (bodyId);
+ } else {
+ body = message;
+ }
+
+ value = body.contentType();
+
+ if (value == "text") {
+ QString key, data;
+ bool hasContent = false;
+
+ value = body.contentSubType();
+
+ if ((hasContent = body.isContentAvailable()) == true) {
+ data = body.textContent();
+ }
+
+ if (value == "plain") {
+ key = "plain-body";
+ } else if (value == "html") {
+ key = "html-body";
+ }
+
+ if (key.isEmpty() == false && key.isNull() == false && hasContent) {
+ messageData[key] = data;
+ }
+ }
+
+ return messageData;
+}
+
+ModestStringMapList ModestEngine::getModestAttachments(QMessage &message)
+{
+ QMessageContentContainerIdList attachmentIds;
+ ModestStringMapList attachments;
+ QMessage::StatusFlags messageStatus;
+ QString value;
+
+ messageStatus = message.status();
+
+ if (messageStatus & QMessage::HasAttachments) {
+ attachmentIds = message.attachmentIds();
+
+ foreach (QMessageContentContainerId identifier, attachmentIds) {
+ ModestStringMap attachmentData;
+ QMessageContentContainer attachmentCont;
+
+ if (identifier.isValid() == false) continue;
+
+ attachmentCont = message.find (identifier);
+
+ if (attachmentCont.isContentAvailable () == false) continue;
+
+ attachmentData.clear();
+
+ value = attachmentCont.contentType();
+
+ if (value.isEmpty() == false) {
+ value.append("/");
+ value.append (attachmentCont.contentSubType());
+ attachmentData["mime-type"] = value;
+
+ qDebug() << "mime-type: " << value;
+ }
+
+ value = QMessageContentContainerPrivate::attachmentFilename (
+ attachmentCont);
+
+ if (value.isEmpty() == false) {
+ attachmentData["filename"] = value;
+ qDebug() << "filename: " << value;
+ }
+
+ qDebug() << "Charset: " << attachmentCont.contentCharset();
+ qDebug() << "Headers: " << attachmentCont.headerFields();
+
+ if (attachmentData.isEmpty() == false) {
+ attachmentData["content-id"] = identifier.toString();
+ attachments.append (attachmentData);
+ }
+ }
+ }
+
+ return attachments;
+}
+
+ModestStringMapList ModestEngine::getModestImages(QMessage &message)
+{
+ Q_UNUSED(message);
+ // Don't know if this even makes sense. Modest expects inlined images
+ // to be in a separate list, but that doesn't make much sense?
+ return ModestStringMapList();
+}
+
+uint ModestEngine::getModestPriority(QMessage &message)
+{
+ uint priority = 0;
+
+ switch (message.priority()) {
+ case QMessage::HighPriority:
+ priority = MODESTENGINE_HIGH_PRIORITY;
+ break;
+
+ default:
+ case QMessage::NormalPriority:
+ priority = MODESTENGINE_NORMAL_PRIORITY;
+ break;
+
+ case QMessage::LowPriority:
+ priority = MODESTENGINE_LOW_PRIORITY;
+ break;
+ }
+
+ return priority;
+}
+
+ModestStringMap ModestEngine::getModestHeaders(QMessage &message)
+{
+ Q_UNUSED(message);
+ return ModestStringMap(); // stub
+}
+
+#include "moc_modestengine_maemo_p.cpp"
QTM_END_NAMESPACE