qtmobility/src/messaging/qmessageservice_win.cpp
changeset 1 2b40d63a9c3d
child 4 90517678cc4f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qtmobility/src/messaging/qmessageservice_win.cpp	Fri Apr 16 15:51:22 2010 +0300
@@ -0,0 +1,1037 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmessageservice.h"
+#include "winhelpers_p.h"
+#include "qmessagemanager.h"
+#include "qmessageid_p.h"
+#include "qmessagefolderid_p.h"
+#include "qmessageaccountid_p.h"
+#include "qmessage_p.h"
+#include "qmessagestore_p.h"
+#include "qmessagecontentcontainer_p.h"
+#include "qmessagecontentcontainerid_p.h"
+#include <QDebug>
+#include <QThread>
+#include <QTimer>
+#include <mapix.h>
+#include <objbase.h>
+#include <mapiutil.h>
+#include <qmobilityglobal.h>
+#ifdef _WIN32_WCE
+#include <cemapi.h>
+#endif
+
+using namespace QtMobility::WinHelpers;
+
+QTM_BEGIN_NAMESPACE
+
+static const unsigned long SmsCharLimit = 160;
+
+class QMessageServicePrivate : public QObject
+{
+    Q_OBJECT
+
+    Q_DECLARE_PUBLIC(QMessageService)
+
+public:
+    QMessageServicePrivate(QMessageService* parent);
+    ~QMessageServicePrivate();
+
+    bool send(const QMessage& message, bool showComposer = false);
+    bool show(const QMessageId& id);
+#ifdef _WIN32_WCE
+    bool isPartiallyDownloaded(const QMessageId& id, bool considerAttachments = false);
+    bool markForDownload(const QMessageId& id, bool includeAttachments = false);
+    bool synchronize(const QMessageAccountId& id);
+    bool registerUpdates(const QMessageId& id);
+    void unregisterUpdates();
+    bool retrieveBody(const QMessage& partialMessage);
+#endif
+
+    void setFinished(bool successful);
+
+public slots:
+    void completed();
+    void reportMatchingIds();
+    void reportMessagesCounted();
+
+#ifdef _WIN32_WCE
+    void messageUpdated(const QMessageId& id);
+#endif
+
+signals:
+    void stateChanged(QMessageService::State);
+    void messagesFound(const QMessageIdList&);
+    void messagesCounted(int);
+    void progressChanged(uint, uint);
+
+public:
+    QMessageService* q_ptr;
+    QMessageManager _manager;
+    bool _active;
+    QMessageManager::Error _error;
+    QMessageIdList _candidateIds;
+    int _count;
+    QMessageService::State _state;
+    QMessageId m_bodyDownloadTarget;
+    QMessageManager::NotificationFilterId m_bodyDownloadFilterId;
+    bool m_registeredUpdates;
+    QThread *m_queryThread;
+    QList<QThread*> m_obsoleteThreads;
+};
+
+namespace {
+
+class QueryThread : public QThread
+{
+    Q_OBJECT
+
+    QMessageServicePrivate *_parent;
+    QMessageFilter _filter;
+    QString _body;
+    QMessageDataComparator::MatchFlags _matchFlags;
+    QMessageSortOrder _ordering;
+    uint _limit;
+    uint _offset;
+
+    // Ensure that the main thread has instantiated the store before we access it from another thread:
+    QMessageManager _manager;
+
+public:
+    QueryThread(QMessageServicePrivate *parent, const QMessageFilter &filter, const QString &body, QMessageDataComparator::MatchFlags matchFlags, const QMessageSortOrder &sortOrder, uint limit, uint offset);
+    void run();
+
+signals:
+    void completed();
+};
+
+QueryThread::QueryThread(QMessageServicePrivate *parent, const QMessageFilter &filter, const QString &body, QMessageDataComparator::MatchFlags matchFlags, const QMessageSortOrder &sortOrder, uint limit, uint offset)
+: QThread(),
+    _parent(parent),
+    _filter(filter),
+    _body(body),
+    _matchFlags(matchFlags),
+    _ordering(sortOrder),
+    _limit(limit),
+    _offset(offset)
+{
+}
+
+void QueryThread::run()
+{
+    // Ensure that this thread has initialized MAPI
+    WinHelpers::MapiInitializationToken token(WinHelpers::initializeMapi());
+
+    _parent->_candidateIds = _manager.queryMessages(_filter, _body, _matchFlags, _ordering, _limit, _offset);
+    _parent->_error = _manager.error();
+    emit completed();
+}
+
+}
+
+void QMessageServicePrivate::completed()
+{
+    _active = false;
+    _state = QMessageService::FinishedState;
+    emit stateChanged(_state);
+}
+
+void QMessageServicePrivate::reportMatchingIds()
+{
+    if (_error == QMessageManager::NoError) {
+        emit messagesFound(_candidateIds);
+    }
+
+    completed();
+}
+
+void QMessageServicePrivate::reportMessagesCounted()
+{
+    if (_error == QMessageManager::NoError) {
+        emit messagesCounted(_candidateIds.count());
+    }
+    completed();
+}
+
+#ifdef _WIN32_WCE
+void QMessageServicePrivate::messageUpdated(const QMessageId& id)
+{
+    if (id == m_bodyDownloadTarget) {
+        bool isBodyDownloaded = !isPartiallyDownloaded(id);
+        if (isBodyDownloaded) {
+            unregisterUpdates();
+
+            completed();
+        }
+    }
+}
+
+#endif
+
+QMessageServicePrivate::QMessageServicePrivate(QMessageService* parent)
+    :q_ptr(parent),
+     _active(false),
+     _state(QMessageService::InactiveState),
+     m_registeredUpdates(false),
+     m_queryThread(0)
+{
+}
+
+QMessageServicePrivate::~QMessageServicePrivate()
+{
+    qDeleteAll(m_obsoleteThreads);
+    delete m_queryThread;
+    _manager.unregisterNotificationFilter(m_bodyDownloadFilterId);
+}
+
+static Lptstr createMCFRecipients(QMessageAddress::Type filterAddressType, const QMessageAddressList& addressList)
+{
+    QStringList temp;
+
+    foreach(const QMessageAddress& a, addressList)
+    {
+        if(a.type() == filterAddressType)
+            temp.append(a.recipient());
+    }
+
+    return temp.isEmpty() ? Lptstr(0) : LptstrFromQString(temp.join(";"));
+}
+
+bool QMessageServicePrivate::send(const QMessage& message, bool showComposer)
+{
+    //check message type
+
+    if(message.type() == QMessage::AnyType || message.type() == QMessage::NoType)
+    {
+        qWarning() << "Unsupported message type for sending/composition";
+        _error = QMessageManager::ConstraintFailure;
+        return false;
+    }
+
+    //check account
+
+    QMessageAccountId accountId = message.parentAccountId();
+
+    if(!accountId.isValid())
+    {
+        accountId = QMessageAccount::defaultAccount(message.type());
+        if(!accountId.isValid())
+        {
+            qWarning() << "Invalid account for sending/composition";
+            _error = QMessageManager::InvalidId;
+            return false;
+        }
+    }
+
+    //check account/message type compatibility
+
+    QMessageAccount account(accountId);
+    if(!(account.messageTypes() & message.type()))
+    {
+        qWarning() << "Message type unsupported by account";
+        _error = QMessageManager::ConstraintFailure;
+        return false;
+    }
+
+#ifdef _WIN32_WCE
+    if(showComposer)
+    {
+        MAILCOMPOSEFIELDS mcf;
+        memset(&mcf,0,sizeof(mcf));
+
+        Lptstr accountName = LptstrFromQString(QMessageAccount(accountId).name());
+        Lptstr to;
+        Lptstr cc;
+        Lptstr bcc;
+        Lptstr subject;
+        Lptstr attachments;
+        Lptstr bodyText;
+
+        //account
+
+        mcf.pszAccount = accountName;
+        mcf.dwFlags = MCF_ACCOUNT_IS_NAME;
+
+        if(message.type() == QMessage::Email)
+        {
+            //recipients
+
+            to = createMCFRecipients(QMessageAddress::Email, message.to());
+            cc = createMCFRecipients(QMessageAddress::Email, message.cc());
+            bcc = createMCFRecipients(QMessageAddress::Email, message.bcc());
+            mcf.pszTo = to;
+            mcf.pszCc = cc;
+            mcf.pszBcc = bcc;
+
+            //subject
+
+            subject = LptstrFromQString(message.subject());
+            mcf.pszSubject = subject;
+
+            //body
+
+            QMessageContentContainerId bodyId = message.bodyId();
+            if(bodyId.isValid())
+            {
+                const QMessageContentContainer& bodyContainer = message.find(bodyId);
+                bodyText = LptstrFromQString(bodyContainer.textContent());
+                mcf.pszBody = bodyText;
+            }
+
+            //attachments
+
+            if(message.status() & QMessage::HasAttachments)
+            {
+                QStringList attachmentList;
+
+                foreach(const QMessageContentContainerId& id, message.attachmentIds())
+                {
+                    const QMessageContentContainer& attachmentContainer = message.find(id);
+                    attachmentList.append(QMessageContentContainerPrivate::attachmentFilename(attachmentContainer));
+                }
+
+                mcf.cAttachments = attachmentList.count();
+                QChar nullChar(0);
+                attachments = LptstrFromQString(attachmentList.join(nullChar));
+                mcf.pszAttachments = attachments;
+            }
+        }
+        else if(message.type() == QMessage::Sms)
+        {
+            //recipients
+
+            to = createMCFRecipients(QMessageAddress::Phone, message.to());
+            mcf.pszTo = to;
+
+            //body
+
+            QMessageContentContainerId bodyId = message.bodyId();
+            if(bodyId.isValid())
+            {
+                const QMessageContentContainer& bodyContainer = message.find(bodyId);
+                QString textContent = bodyContainer.textContent();
+                if(textContent.length() > SmsCharLimit)
+                {
+                    textContent.truncate(SmsCharLimit);
+                    qWarning() << "SMS messages may not exceed " << SmsCharLimit << " characters. Body trucated.";
+                }
+                bodyText = LptstrFromQString(textContent);
+                mcf.pszBody = bodyText;
+            }
+        }
+
+       mcf.cbSize = sizeof(mcf);
+
+       if(FAILED(MailComposeMessage(&mcf)))
+       {
+           _error = QMessageManager::FrameworkFault;
+           qWarning() << "MailComposeMessage failed";
+           return false;
+       }
+    }
+    else
+    {
+#endif
+
+    //check recipients
+    QMessageAddressList recipients = message.to() + message.bcc() + message.cc();
+    if(recipients.isEmpty() && !showComposer)
+    {
+        qWarning() << "Message must have recipients for sending";
+        _error = QMessageManager::ConstraintFailure;
+        return false;
+    }
+
+    MapiSessionPtr mapiSession(MapiSession::createSession(&_error));
+    if (_error != QMessageManager::NoError)
+    {
+        qWarning() << "Could not create MAPI session for sending";
+        return false;
+    }
+
+    QMessage outgoing(message);
+
+    //ensure the message is marked read otherwise MapiForm displays the message as incomming
+    outgoing.setStatus(QMessage::Read,true);
+
+    //set default account if unset
+    if(!outgoing.parentAccountId().isValid())
+        outgoing.setParentAccountId(accountId);
+
+    MapiStorePtr mapiStore = mapiSession->findStore(&_error, outgoing.parentAccountId(),false);
+
+    if(mapiStore.isNull() || _error != QMessageManager::NoError)
+    {
+        qWarning() << "Unable to retrieve MAPI store for account";
+        return false;
+    }
+
+    //try first to create message in outbox for store, failing that attempt draft
+
+    MapiFolderPtr mapiFolder = mapiStore->findFolder(&_error,QMessage::OutboxFolder);
+
+    if( mapiFolder.isNull() || _error != QMessageManager::NoError ) {
+        qWarning() << "Unable to retrieve outbox MAPI folder for sending, attempting drafts...";
+        mapiFolder = mapiStore->findFolder(&_error,QMessage::DraftsFolder);
+        if(mapiFolder.isNull() || _error != QMessageManager::NoError) {
+            qWarning() << "Unable to retrieve drafts MAPI folder for sending";
+            return false;
+        }
+    }
+
+    IMessage* mapiMessage = mapiFolder->createMessage(&_error, outgoing, mapiSession, DontSavePropertyChanges);
+
+    if(!mapiMessage || _error != QMessageManager::NoError)
+    {
+        qWarning() << "Unable to create MAPI message from source";
+        mapiRelease(mapiMessage);
+        return false;
+    }
+
+#ifndef _WIN32_WCE
+    if(showComposer)
+    {
+        MapiForm* mapiForm = new MapiForm(mapiStore->store(),mapiSession->session(),mapiFolder->folder(),mapiMessage);
+        bool result = mapiForm->show();
+        mapiRelease(mapiForm);
+
+        if(!result)
+        {
+            qWarning() << "MapiForm::Show failed";
+            _error = QMessageManager::FrameworkFault;
+            return false;
+        }
+    }
+    else
+#endif
+    {
+        if(FAILED(mapiMessage->SubmitMessage(0)))
+        {
+            qWarning() << "MAPI SubmitMessage failed.";
+            _error = QMessageManager::FrameworkFault;
+            mapiRelease(mapiMessage);
+            return false;
+        }
+    }
+
+#ifdef _WIN32_WCE
+    }
+#endif
+
+    return true;
+}
+
+bool QMessageServicePrivate::show(const QMessageId& messageId)
+{
+    if(!messageId.isValid())
+    {
+        _error = QMessageManager::InvalidId;
+        qWarning() << "Invalid QMessageId";
+        return false;
+    }
+
+#ifdef _WIN32_WCE
+    MapiEntryId entryId = QMessageIdPrivate::entryId(messageId);
+    LPENTRYID entryIdPtr(reinterpret_cast<LPENTRYID>(const_cast<char*>(entryId.data())));
+    if(FAILED(MailDisplayMessage(entryIdPtr,entryId.length())))
+    {
+        qWarning() << "MailDisplayMessage failed";
+        _error = QMessageManager::FrameworkFault;
+        return false;
+    }
+    return true;
+
+#else
+
+    MapiSessionPtr mapiSession(MapiSession::createSession(&_error));
+    if (_error != QMessageManager::NoError)
+    {
+        qWarning() << "Could not create MAPI seesion";
+        return false;
+    }
+
+    MapiEntryId entryId = QMessageIdPrivate::entryId(messageId);
+    MapiRecordKey folderRecordKey = QMessageIdPrivate::folderRecordKey(messageId);
+    MapiRecordKey storeRecordKey = QMessageIdPrivate::storeRecordKey(messageId);
+
+    MapiStorePtr mapiStore = mapiSession->findStore(&_error,QMessageAccountIdPrivate::from(storeRecordKey));
+
+    if(mapiStore.isNull() || _error != QMessageManager::NoError)
+    {
+        qWarning() << "Unable to retrieve MAPI store for account";
+        return false;
+    }
+
+    MapiFolderPtr mapiFolder = mapiStore->openFolderWithKey(&_error,folderRecordKey);
+
+    if( mapiFolder.isNull() || _error != QMessageManager::NoError ) {
+        qWarning() << "Unable to retrieve MAPI folder for message";
+        return false;
+    }
+
+    IMessage* mapiMessage = mapiFolder->openMessage(&_error,entryId);
+
+    if(!mapiMessage || _error != QMessageManager::NoError)
+    {
+        qWarning() << "Unable to retrieve MAPI message";
+        mapiRelease(mapiMessage);
+        return false;
+    }
+
+    MapiForm* mapiForm = new MapiForm(mapiStore->store(),mapiSession->session(),mapiFolder->folder(),mapiMessage);
+    bool result = mapiForm->show();
+    mapiRelease(mapiForm);
+
+    if(!result)
+    {
+        qWarning() << "MapiForm::show failed.";
+        _error = QMessageManager::FrameworkFault;
+        return false;
+    }
+
+    mapiRelease(mapiMessage);
+
+    return result;
+
+#endif
+}
+
+#ifdef _WIN32_WCE
+
+bool QMessageServicePrivate::isPartiallyDownloaded(const QMessageId& id, bool considerAttachments)
+{
+    if(!id.isValid())
+    {
+        _error = QMessageManager::InvalidId;
+        qWarning() << "Invalid QMessageId";
+        return false;
+    }
+
+    MapiSessionPtr mapiSession(MapiSession::createSession(&_error));
+
+    if (_error != QMessageManager::NoError)
+    {
+        qWarning() << "Could not create MAPI session";
+        return false;
+    }
+
+    MapiStorePtr mapiStore = mapiSession->openStore(&_error,QMessageIdPrivate::storeRecordKey(id));
+
+    if(mapiStore.isNull() || _error != QMessageManager::NoError) {
+        qWarning() << "Unable to retrieve MAPI store for account";
+        return false;
+    }
+
+    IMessage* message = mapiStore->openMessage(&_error,QMessageIdPrivate::entryId(id));
+
+    ULONG status = 0;
+    if(!getMapiProperty(message,PR_MSG_STATUS,&status)) {
+        qWarning() << "Unable to get MAPI message status flags";
+        _error = QMessageManager::FrameworkFault;
+        return false;
+    }
+    else
+    {
+        mapiRelease(message);
+        bool bodyNotDownloaded = (status & MSGSTATUS_HEADERONLY) || (status & MSGSTATUS_PARTIAL);
+        bool attachmentsNotDownloaded = (status & MSGSTATUS_PENDING_ATTACHMENTS);
+        return considerAttachments ? bodyNotDownloaded && attachmentsNotDownloaded : bodyNotDownloaded;
+    }
+}
+
+bool QMessageServicePrivate::markForDownload(const QMessageId& id, bool includeAttachments)
+{
+    if(!id.isValid())
+    {
+        _error = QMessageManager::InvalidId;
+        qWarning() << "Invalid QMessageId";
+        return false;
+    }
+
+    MapiSessionPtr mapiSession(MapiSession::createSession(&_error));
+
+    if (_error != QMessageManager::NoError)
+    {
+        qWarning() << "Could not create MAPI session";
+        return false;
+    }
+
+    MapiStorePtr mapiStore = mapiSession->openStore(&_error,QMessageIdPrivate::storeRecordKey(id));
+
+    if(mapiStore.isNull() || _error != QMessageManager::NoError)
+    {
+        qWarning() << "Unable to retrieve MAPI store for message account";
+        return false;
+    }
+
+    IMessage* message = mapiStore->openMessage(&_error,QMessageIdPrivate::entryId(id));
+
+    ULONG status = 0;
+
+    if(!getMapiProperty(message,PR_MSG_STATUS,&status))
+    {
+        qWarning() << "Unable to get MAPI message status flags";
+        _error = QMessageManager::FrameworkFault;
+        return false;
+    }
+    else
+    {
+        //mark the message to download on the next sync
+
+        status |= MSGSTATUS_REMOTE_DOWNLOAD;
+        if(includeAttachments)
+            status |= MSGSTATUS_REMOTE_DOWNLOAD_ATTACH;
+
+        if(!setMapiProperty(message, PR_MSG_STATUS, status))
+        {
+            qWarning() << "Could not mark the MAPI message for download!";
+            _error = QMessageManager::FrameworkFault;
+            return false;
+        }
+
+        mapiRelease(message);
+
+        //TODO investigate possiblity of interacting with mapi transport directly
+        /*
+        QString transportName = mapiStore->transportName();
+        if(transportName.isEmpty())
+        {
+            qWarning() << "Could not get transport name for mapi store";
+            return false;
+        }
+        */
+    }
+    return true;
+}
+
+bool QMessageServicePrivate::synchronize(const QMessageAccountId& id)
+{
+    if(!id.isValid())
+    {
+        qWarning() << "Cannot synchronize invalid QMessageAccountId";
+        _error = QMessageManager::InvalidId;
+        return false;
+    }
+
+    QMessageAccount account(id);
+    if(FAILED(MailSyncMessages(LptstrFromQString(account.name()),MCF_ACCOUNT_IS_NAME | MCF_RUN_IN_BACKGROUND)))
+    {
+        qWarning() << "MailSyncMessages failed for account: " << account.name();
+        _error = QMessageManager::FrameworkFault;
+        return false;
+    }
+    return true;
+}
+
+bool QMessageServicePrivate::registerUpdates(const QMessageId& id)
+{
+    if(!id.isValid())
+    {
+        qWarning() << "Cannot register for update notifications on invalid QMessageId";
+        _error = QMessageManager::InvalidId;
+        return false;
+    }
+
+    if(!m_registeredUpdates)
+    {
+        connect(&_manager, SIGNAL(messageUpdated(const QMessageId&, const QMessageManager::NotificationFilterIdSet&)),this,SLOT(messageUpdated(const QMessageId&)));
+        m_bodyDownloadFilterId = _manager.registerNotificationFilter(QMessageFilter::byId(id));
+        m_bodyDownloadTarget = id;
+        m_registeredUpdates = true;
+    }
+    return m_registeredUpdates;
+}
+
+void QMessageServicePrivate::unregisterUpdates()
+{
+    disconnect(&_manager, SIGNAL(messageUpdated(const QMessageId&, const QMessageManager::NotificationFilterIdSet&)),this,SLOT(messageUpdated(const QMessageId&)));
+    _manager.unregisterNotificationFilter(m_bodyDownloadFilterId);
+    m_bodyDownloadFilterId = 0;
+    m_registeredUpdates = false;
+}
+
+bool QMessageServicePrivate::retrieveBody(const QMessage& partialMessage)
+{
+    if(partialMessage.type() != QMessage::Email)
+    {
+        qWarning() << "Cannot retrieve body for non-email message type";
+        _error = QMessageManager::ConstraintFailure;
+        return false;
+    }
+
+    if(isPartiallyDownloaded(partialMessage.id()))
+    {
+        //only valid for Email
+        if(markForDownload(partialMessage.id(),true))
+            if(registerUpdates(partialMessage.id()))
+                if(!synchronize(partialMessage.parentAccountId()))
+                    unregisterUpdates();
+    }
+    else QTimer::singleShot(0,this,SLOT(completed()));
+
+    return (_error == QMessageManager::NoError);
+}
+
+#endif
+
+void QMessageServicePrivate::setFinished(bool successful)
+{
+    if (!successful && (_error == QMessageManager::NoError)) {
+        _error = QMessageManager::RequestIncomplete;
+    }
+
+    completed();
+}
+
+QMessageService::QMessageService(QObject *parent)
+    : QObject(parent),
+    d_ptr(new QMessageServicePrivate(this))
+{
+    connect(d_ptr, SIGNAL(stateChanged(QMessageService::State)),
+        this, SIGNAL(stateChanged(QMessageService::State)));
+    connect(d_ptr, SIGNAL(messagesFound(const QMessageIdList&)),
+        this, SIGNAL(messagesFound(const QMessageIdList&)));
+    connect(d_ptr, SIGNAL(messagesCounted(int)),
+        this, SIGNAL(messagesCounted(int)));
+    connect(d_ptr, SIGNAL(progressChanged(uint, uint)),
+        this, SIGNAL(progressChanged(uint, uint)));
+}
+
+QMessageService::~QMessageService()
+{
+    delete d_ptr;
+    d_ptr = 0;
+}
+
+bool QMessageService::queryMessages(const QMessageFilter &filter, const QMessageSortOrder &sortOrder, uint limit, uint offset)
+{
+    return queryMessages(filter, QString(), QMessageDataComparator::MatchFlags(), sortOrder, limit, offset);
+}
+
+bool QMessageService::queryMessages(const QMessageFilter &filter, const QString &body, QMessageDataComparator::MatchFlags matchFlags, const QMessageSortOrder &sortOrder, uint limit, uint offset)
+{
+    if (d_ptr->_active) {
+        qWarning() << "Service is currently busy";
+        return false;
+    }
+
+
+    d_ptr->_active = true;
+    d_ptr->_error = QMessageManager::NoError;
+    d_ptr->_state = QMessageService::ActiveState;
+    emit stateChanged(d_ptr->_state);
+
+#if 0
+    d_ptr->_candidateIds = d_ptr->_manager.queryMessages(filter, body, matchFlags, sortOrder, limit, offset);
+    d_ptr->_error = d_ptr->_manager.error();
+    QTimer::singleShot(0,d_ptr,SLOT(reportMatchingIds()));
+#else
+    // Perform the query in another thread to keep the UI thread free
+    QueryThread *query = new QueryThread(d_ptr, filter, body, matchFlags, sortOrder, limit, offset);
+    connect(query, SIGNAL(completed()), d_ptr, SLOT(reportMatchingIds()), Qt::QueuedConnection);
+    query->start();
+
+    if (d_ptr->m_queryThread) {
+        // Don't delete the previous thread object immediately
+        if (!d_ptr->m_obsoleteThreads.isEmpty()) {
+            qDeleteAll(d_ptr->m_obsoleteThreads);
+            d_ptr->m_obsoleteThreads.clear();
+        }
+        d_ptr->m_obsoleteThreads.append(d_ptr->m_queryThread);
+    }
+    d_ptr->m_queryThread = query;
+#endif
+    return true;
+}
+
+bool QMessageService::countMessages(const QMessageFilter &filter)
+{
+    if (d_ptr->_active) {
+        qWarning() << "Service is currently busy";
+        return false;
+    }
+    d_ptr->_active = true;
+    d_ptr->_error = QMessageManager::NoError;
+    d_ptr->_state = QMessageService::ActiveState;
+    emit stateChanged(d_ptr->_state);
+
+    // Perform the query in another thread to keep the UI thread free
+    QueryThread *query = new QueryThread(d_ptr, filter, QString(), QMessageDataComparator::MatchFlags(), QMessageSortOrder(), 0, 0);
+    connect(query, SIGNAL(completed()), d_ptr, SLOT(reportMessagesCounted()), Qt::QueuedConnection);
+    query->start();
+
+    if (d_ptr->m_queryThread) {
+        // Don't delete the previous thread object immediately
+        if (!d_ptr->m_obsoleteThreads.isEmpty()) {
+            qDeleteAll(d_ptr->m_obsoleteThreads);
+        }
+        d_ptr->m_obsoleteThreads.append(d_ptr->m_queryThread);
+    }
+    d_ptr->m_queryThread = query;
+
+    return true;
+}
+
+bool QMessageService::send(QMessage &message)
+{
+    if(d_ptr->_active) {
+        qWarning() << "Service is currently busy";
+        return false;
+    }
+
+    d_ptr->_active = true;
+    d_ptr->_state = QMessageService::ActiveState;
+    d_ptr->_error = QMessageManager::NoError;
+    emit stateChanged(d_ptr->_state);
+
+    bool result = d_ptr->send(message);
+    d_ptr->setFinished(result);
+
+    return result;
+}
+
+bool QMessageService::compose(const QMessage &message)
+{
+    if(d_ptr->_active) {
+        qWarning() << "Service is currently busy";
+        return false;
+    }
+
+    d_ptr->_active = true;
+    d_ptr->_state = QMessageService::ActiveState;
+    d_ptr->_error = QMessageManager::NoError;
+    emit stateChanged(d_ptr->_state);
+
+    bool result = d_ptr->send(message,true);
+    d_ptr->setFinished(result);
+
+    return result;
+}
+
+bool QMessageService::retrieveHeader(const QMessageId& id)
+{
+    Q_UNUSED(id);
+
+    if(d_ptr->_active) {
+        qWarning() << "Service is currently busy";
+        return false;
+    }
+
+    d_ptr->_error = QMessageManager::NoError;
+    d_ptr->setFinished(true);
+
+    return true;
+}
+
+bool QMessageService::retrieveBody(const QMessageId& id)
+{
+
+    if(d_ptr->_active) {
+        qWarning() << "Service is currently busy";
+        return false;
+    }
+
+#ifdef _WIN32_WCE
+
+    d_ptr->_active = true;
+    d_ptr->_state = ActiveState;
+    d_ptr->_error = QMessageManager::NoError;
+    emit stateChanged(d_ptr->_state);
+
+    if(!id.isValid())
+    {
+        qWarning() << "Invalid QMessageId";
+        d_ptr->_error = QMessageManager::InvalidId;
+    }
+
+    QMessage message;
+
+    if(d_ptr->_error == QMessageManager::NoError)
+    {
+        message = QMessage(id);
+        d_ptr->_error = QMessageManager().error();
+    }
+
+
+    if(d_ptr->_error == QMessageManager::NoError)
+        d_ptr->retrieveBody(message);
+
+    //emit failure immediately
+    if (d_ptr->_error != QMessageManager::NoError) {
+        d_ptr->setFinished(false);
+        return false;
+    }
+
+    return true;
+
+#else
+    Q_UNUSED(id);
+
+    d_ptr->_error = QMessageManager::NotYetImplemented;
+    d_ptr->setFinished(false);
+    return false;
+#endif
+}
+
+bool QMessageService::retrieve(const QMessageId& messageId, const QMessageContentContainerId& id)
+{
+
+    if(d_ptr->_active) {
+        qWarning() << "Service is currently busy";
+        return false;
+    }
+
+#ifdef _WIN32_WCE
+
+    d_ptr->_active = true;
+    d_ptr->_state = ActiveState;
+    d_ptr->_error = QMessageManager::NoError;
+    emit stateChanged(d_ptr->_state);
+
+    if(!messageId.isValid())
+    {
+        qWarning() << "Invalid QMessageId";
+        d_ptr->_error = QMessageManager::InvalidId;
+    }
+
+    QMessage message;
+
+    if(d_ptr->_error == QMessageManager::NoError)
+    {
+        message = QMessage(messageId);
+        d_ptr->_error = d_ptr->_manager.error();
+    }
+
+    if(d_ptr->_error == QMessageManager::NoError)
+    {
+        bool isBodyContainer = message.bodyId() == id;
+        if(isBodyContainer)
+            d_ptr->retrieveBody(message);
+        //TODO downloading attachment programatically possible?
+    }
+
+    //emit failure immediately
+    if (d_ptr->_error != QMessageManager::NoError) {
+        d_ptr->setFinished(false);
+        return false;
+    }
+
+    return true;
+
+#else
+    Q_UNUSED(messageId)
+    Q_UNUSED(id)
+
+    d_ptr->_error = QMessageManager::NotYetImplemented;
+    d_ptr->setFinished(false);
+#endif
+
+    return false;
+}
+
+bool QMessageService::show(const QMessageId& id)
+{
+    if(d_ptr->_active) {
+        qWarning() << "Service is currently busy";
+        return false;
+    }
+
+    d_ptr->_active = true;
+    d_ptr->_state = ActiveState;
+    d_ptr->_error = QMessageManager::NoError;
+    emit stateChanged(d_ptr->_state);
+
+    bool result = d_ptr->show(id);
+    d_ptr->setFinished(result);
+
+    return result;
+}
+
+bool QMessageService::exportUpdates(const QMessageAccountId &id)
+{
+    Q_UNUSED(id);
+
+    if(d_ptr->_active) {
+        qWarning() << "Service is currently busy";
+        return false;
+    }
+
+    d_ptr->_error = QMessageManager::NotYetImplemented;
+    d_ptr->setFinished(false);
+
+    return false;
+}
+
+QMessageService::State QMessageService::state() const
+{
+    return d_ptr->_state;
+}
+
+void QMessageService::cancel()
+{
+#ifdef _WIN32_WCE
+    if(d_ptr->_active)
+    {
+        bool awaitingBodyRetrieval(d_ptr->m_bodyDownloadFilterId != 0);
+
+        if(awaitingBodyRetrieval)
+        {
+            d_ptr->unregisterUpdates();
+            d_ptr->_error = QMessageManager::NoError;
+            d_ptr->_state = QMessageService::InactiveState;
+            d_ptr->_active = false;
+            emit stateChanged(d_ptr->_state);
+        }
+    }
+#else
+    //NOOP
+#endif
+}
+
+QMessageManager::Error QMessageService::error() const
+{
+    return d_ptr->_error;
+}
+
+#include <qmessageservice_win.moc>
+
+QTM_END_NAMESPACE