diff -r 2b40d63a9c3d -r 90517678cc4f qtmobility/src/messaging/modestengine_maemo.cpp --- 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 -#include +#include "qmessagefolder_p.h" +#include "qmessagestore_p.h" +#include "qmessageservice_maemo_p.h" +#include "qmessagecontentcontainer_maemo_p.h" #include -#include +#include +#include +#include + #include - #include +#include +#include +#include +#include + +// 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(); + + qRegisterMetaType(); + qRegisterMetaType(); + qDBusRegisterMetaType(); + + qRegisterMetaType(); + + + 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(msg.arguments()[11].toUInt()); + modestMessage.priority = static_cast(msg.arguments()[12].toUInt()); + + QVariant variant = msg.arguments()[13]; + QDBusArgument argument = variant.value(); + 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/ + // => 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(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(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(pf->_comparatorValue)); + if (cmp == QMessageDataComparator::Equal) { + QMessage::StandardFolder standardFolder = static_cast(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(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(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(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(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 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(); + QList > 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 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(j.value().toUInt()); + } else if (j.key() == "priority") { + modestMessage.priority = static_cast(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 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 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 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(); + QList 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(); + QList 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 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 filters(m_filters); + QMap::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: + // ://@:... + 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: + // ://@:... + 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: && + 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 i(m_dirs); + while (i.hasNext()) { + inotify_rm_watch(m_inotifyFileDescriptor, i.next().key()); + } + m_dirs.clear(); + + QMapIterator 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 buffer(bufferSize); + bufferSize = read(m_inotifyFileDescriptor, buffer.data(), bufferSize); + const char* at = buffer.data(); + const char* const end = at + bufferSize; + + QMap eventsForWatchedFile; + QMap eventsForFileInWatchedDirectory; + while (at < end) { + const inotify_event *event = reinterpret_cast(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::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::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