src/messaging/winhelpers.cpp
changeset 0 876b1a06bc25
equal deleted inserted replaced
-1:000000000000 0:876b1a06bc25
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt Mobility Components.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #ifndef _UNICODE
       
    43 #define _UNICODE
       
    44 #endif
       
    45 
       
    46 #ifndef MDB_ONLINE
       
    47 #define MDB_ONLINE ((ULONG) 0x00000100)
       
    48 #endif
       
    49 
       
    50 #ifndef DELETE_HARD_DELETE
       
    51 #define DELETE_HARD_DELETE ((ULONG) 0x00000010)
       
    52 #endif
       
    53 
       
    54 #ifndef STORE_HTML_OK
       
    55 #define STORE_HTML_OK ((ULONG)0x00010000)
       
    56 #endif
       
    57 
       
    58 #ifndef PR_IS_NEWSGROUP
       
    59 #define PR_IS_NEWSGROUP PROP_TAG( PT_BOOLEAN, 0x6697 )
       
    60 #endif
       
    61 
       
    62 #ifndef PR_IS_NEWSGROUP_ANCHOR
       
    63 #define PR_IS_NEWSGROUP_ANCHOR PROP_TAG( PT_BOOLEAN, 0x6696 )
       
    64 #endif
       
    65 
       
    66 #ifndef PR_EXTENDED_FOLDER_FLAGS
       
    67 #define PR_EXTENDED_FOLDER_FLAGS PROP_TAG( PT_BINARY, 0x36DA )
       
    68 #endif
       
    69 
       
    70 #include "winhelpers_p.h"
       
    71 #include "qmessageid_p.h"
       
    72 #include "qmessagefolderid_p.h"
       
    73 #include "qmessageaccountid_p.h"
       
    74 #include "qmessage_p.h"
       
    75 #include "qmessagecontentcontainer_p.h"
       
    76 #include "qmessagefilter_p.h"
       
    77 #include "qmessagesortorder_p.h"
       
    78 #include "qmessagefolder_p.h"
       
    79 #include "qmessagefolderfilter_p.h"
       
    80 #include "qmessagefoldersortorder_p.h"
       
    81 #include "qmessageaccount_p.h"
       
    82 #include "qmessageaccountfilter_p.h"
       
    83 #include "qmessageaccountsortorder_p.h"
       
    84 #include "qmessagestore_p.h"
       
    85 #include "messagingutil_p.h"
       
    86 
       
    87 #include <QCoreApplication>
       
    88 #include <QDebug>
       
    89 #include <QFile>
       
    90 #include <QTextCodec>
       
    91 #include <QThreadStorage>
       
    92 #include <QTimer>
       
    93 #include <QMutexLocker>
       
    94 
       
    95 #include <shlwapi.h>
       
    96 #include <shlguid.h>
       
    97 #include <tchar.h>
       
    98 
       
    99 #ifdef _WIN32_WCE
       
   100 #include "win32wce/qmailmessage.h"
       
   101 #include <cemapi.h>
       
   102 #endif
       
   103 
       
   104 //unexported before Windows 7, include manually
       
   105 #ifndef IID_PPV_ARGS
       
   106 extern "C++"
       
   107 {
       
   108     template<typename T> void** IID_PPV_ARGS_Helper(T** pp)
       
   109     {
       
   110         // make sure everyone derives from IUnknown
       
   111         static_cast<IUnknown*>(*pp);
       
   112 
       
   113         return reinterpret_cast<void**>(pp);
       
   114     }
       
   115 }
       
   116 #define IID_PPV_ARGS(ppType) __uuidof(**(ppType)), IID_PPV_ARGS_Helper(ppType)
       
   117 #endif //IID_PPV_ARGSA
       
   118 
       
   119 QTM_BEGIN_NAMESPACE
       
   120 
       
   121 namespace WinHelpers
       
   122 {
       
   123 	
       
   124 
       
   125     bool setMapiProperty(IMAPIProp *object, ULONG tag, const QString &value)
       
   126     {
       
   127         SPropValue prop = { 0 };
       
   128         prop.ulPropTag = tag;
       
   129         prop.Value.LPSZ = reinterpret_cast<LPWSTR>(const_cast<quint16*>(value.utf16()));
       
   130         return HR_SUCCEEDED(HrSetOneProp(object, &prop));
       
   131     }
       
   132 
       
   133     bool setMapiProperty(IMAPIProp *object, ULONG tag, LONG value)
       
   134     {
       
   135         SPropValue prop = { 0 };
       
   136         prop.ulPropTag = tag;
       
   137         prop.Value.l = value;
       
   138         return HR_SUCCEEDED(HrSetOneProp(object, &prop));
       
   139     }
       
   140 
       
   141     bool setMapiProperty(IMAPIProp *object, ULONG tag, ULONG value)
       
   142     {
       
   143         SPropValue prop = { 0 };
       
   144         prop.ulPropTag = tag;
       
   145         prop.Value.ul = value;
       
   146         return HR_SUCCEEDED(HrSetOneProp(object, &prop));
       
   147     }
       
   148 
       
   149     bool setMapiProperty(IMAPIProp *object, ULONG tag, bool value)
       
   150     {
       
   151         SPropValue prop = { 0 };
       
   152         prop.ulPropTag = tag;
       
   153         prop.Value.b = value;
       
   154         return HR_SUCCEEDED(HrSetOneProp(object, &prop));
       
   155     }
       
   156 
       
   157     bool setMapiProperty(IMAPIProp *object, ULONG tag, FILETIME value)
       
   158     {
       
   159         SPropValue prop = { 0 };
       
   160         prop.ulPropTag = tag;
       
   161         prop.Value.ft = value;
       
   162         return HR_SUCCEEDED(HrSetOneProp(object, &prop));
       
   163     }
       
   164 
       
   165     bool setMapiProperty(IMAPIProp *object, ULONG tag, MapiEntryId value)
       
   166     {
       
   167         SBinary s;
       
   168         s.cb = value.count();
       
   169         s.lpb = reinterpret_cast<LPBYTE>(value.data());
       
   170         SPropValue prop = { 0 };
       
   171         prop.ulPropTag = tag;
       
   172         prop.Value.bin = s;
       
   173         return HR_SUCCEEDED(HrSetOneProp(object, &prop));
       
   174     }
       
   175 
       
   176     bool getMapiProperty(IMAPIProp *object, ULONG tag, ULONG *value)
       
   177     {
       
   178         bool result(false);
       
   179 
       
   180         SPropValue *prop;
       
   181         HRESULT rv = HrGetOneProp(object, tag, &prop);
       
   182         if (HR_SUCCEEDED(rv)) {
       
   183             if (prop->ulPropTag == tag) {
       
   184                 *value = prop->Value.ul;
       
   185                 result = true;
       
   186             }
       
   187 
       
   188             MAPIFreeBuffer(prop);
       
   189         }
       
   190 
       
   191         return result;
       
   192     }
       
   193 
       
   194     bool getMapiProperty(IMAPIProp *object, ULONG tag, LONG *value)
       
   195     {
       
   196         bool result(false);
       
   197 
       
   198         SPropValue *prop;
       
   199         HRESULT rv = HrGetOneProp(object, tag, &prop);
       
   200         if (HR_SUCCEEDED(rv)) {
       
   201             if (prop->ulPropTag == tag) {
       
   202                 *value = prop->Value.l;
       
   203                 result = true;
       
   204             }
       
   205 
       
   206             MAPIFreeBuffer(prop);
       
   207         }
       
   208 
       
   209         return result;
       
   210     }
       
   211 
       
   212     bool getMapiProperty(IMAPIProp *object, ULONG tag, QByteArray *value)
       
   213     {
       
   214         bool result(false);
       
   215 
       
   216         SPropValue *prop;
       
   217         HRESULT rv = HrGetOneProp(object, tag, &prop);
       
   218         if (HR_SUCCEEDED(rv)) {
       
   219             if (prop->ulPropTag == tag) {
       
   220                 *value = QByteArray(reinterpret_cast<const char*>(prop->Value.bin.lpb), prop->Value.bin.cb);
       
   221                 result = true;
       
   222             }
       
   223 
       
   224             MAPIFreeBuffer(prop);
       
   225         }
       
   226 
       
   227         return result;
       
   228     }
       
   229 
       
   230     bool getMapiProperty(IMAPIProp *object, ULONG tag, QString *value)
       
   231     {
       
   232         bool result(false);
       
   233 
       
   234         SPropValue *prop;
       
   235         HRESULT rv = HrGetOneProp(object, tag, &prop);
       
   236         if (HR_SUCCEEDED(rv)) {
       
   237             if (prop->ulPropTag == tag) {
       
   238                 *value = QStringFromLpctstr(prop->Value.lpszW);
       
   239                 result = true;
       
   240             }
       
   241             MAPIFreeBuffer(prop);
       
   242         }
       
   243 
       
   244         return result;
       
   245     }
       
   246 }
       
   247 
       
   248 using namespace WinHelpers;
       
   249 
       
   250 namespace {
       
   251 
       
   252     typedef QWeakPointer<WinHelpers::MapiInitializer> InitRecord;
       
   253     typedef InitRecord *InitRecordPtr;
       
   254 
       
   255     QThreadStorage<InitRecordPtr> initializer;
       
   256 
       
   257     GUID GuidPublicStrings = { 0x00020329, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 };
       
   258 
       
   259     FILETIME toFileTime(const QDateTime &dt)
       
   260     {
       
   261         FILETIME ft = {0};
       
   262 
       
   263         QDate date(dt.date());
       
   264         QTime time(dt.time());
       
   265 
       
   266         SYSTEMTIME st = {0};
       
   267         st.wYear = date.year();
       
   268         st.wMonth = date.month();
       
   269         st.wDay = date.day();
       
   270         st.wHour = time.hour();
       
   271         st.wMinute = time.minute();
       
   272         st.wSecond = time.second();
       
   273         st.wMilliseconds = time.msec();
       
   274 
       
   275         SystemTimeToFileTime(&st, &ft);
       
   276         return ft;
       
   277     }
       
   278 
       
   279     QDateTime fromFileTime(const FILETIME &ft)
       
   280     {
       
   281         SYSTEMTIME st = {0};
       
   282         FileTimeToSystemTime(&ft, &st);
       
   283         QString dateStr(QString("yyyy%1M%2d%3h%4m%5s%6z%7").arg(st.wYear).arg(st.wMonth).arg(st.wDay).arg(st.wHour).arg(st.wMinute).arg(st.wSecond).arg(st.wMilliseconds));
       
   284         QDateTime dt(QDateTime::fromString(dateStr, "'yyyy'yyyy'M'M'd'd'h'h'm'm's's'z'z"));
       
   285         dt.setTimeSpec(Qt::UTC);
       
   286         return dt;
       
   287     }
       
   288 
       
   289     struct AccountFilterPredicate
       
   290     {
       
   291         const QMessageAccountFilter &_filter;
       
   292 
       
   293         AccountFilterPredicate(const QMessageAccountFilter &filter) : _filter(filter) {}
       
   294 
       
   295         bool operator()(const MapiStorePtr &store) const
       
   296         {
       
   297             return QMessageAccountFilterPrivate::matchesStore(_filter, store);
       
   298         }
       
   299     };
       
   300 
       
   301     //used in preference to HrQueryAllRows
       
   302     //as per: http://blogs.msdn.com/stephen_griffin/archive/2009/03/23/try-not-to-query-all-rows.aspx
       
   303 
       
   304     class QueryAllRows
       
   305     {
       
   306         static const int BatchSize = 20;
       
   307         public:
       
   308         QueryAllRows(LPMAPITABLE ptable,
       
   309                 LPSPropTagArray ptaga,
       
   310                 LPSRestriction pres,
       
   311                 LPSSortOrderSet psos,
       
   312                 bool setPosition = true);
       
   313         ~QueryAllRows();
       
   314 
       
   315         LONG rowCount();
       
   316         bool query();
       
   317         LPSRowSet rows() const;
       
   318         QMessageManager::Error error() const;
       
   319 
       
   320         private:
       
   321         LPMAPITABLE m_table;
       
   322         LPSPropTagArray m_tagArray;
       
   323         LPSRestriction m_restriction;
       
   324         LPSSortOrderSet m_sortOrderSet;
       
   325         LPSRowSet m_rows;
       
   326         QMessageManager::Error m_error;
       
   327     };
       
   328 
       
   329     QueryAllRows::QueryAllRows(LPMAPITABLE ptable,
       
   330                                LPSPropTagArray ptaga,
       
   331                                LPSRestriction pres,
       
   332                                LPSSortOrderSet psos,
       
   333                                bool setPosition)
       
   334     :
       
   335         m_table(ptable),
       
   336         m_tagArray(ptaga),
       
   337         m_restriction(pres),
       
   338         m_sortOrderSet(psos),
       
   339         m_rows(0),
       
   340         m_error(QMessageManager::NoError)
       
   341     {
       
   342         bool initFailed = false;
       
   343 
       
   344 #ifndef _WIN32_WCE
       
   345         unsigned long flags = TBL_BATCH;
       
   346 #else
       
   347         unsigned long flags = 0;
       
   348 #endif
       
   349 
       
   350         if(m_tagArray)
       
   351             initFailed |= FAILED(m_table->SetColumns(m_tagArray, flags));
       
   352 
       
   353         if(m_restriction)
       
   354             initFailed |= FAILED(m_table->Restrict(m_restriction, flags));
       
   355 
       
   356         if(m_sortOrderSet)
       
   357             initFailed |= FAILED(m_table->SortTable(m_sortOrderSet, flags));
       
   358 
       
   359         if(setPosition) {
       
   360             if(initFailed |= FAILED(m_table->SeekRow(BOOKMARK_BEGINNING,0, NULL)))
       
   361                 qWarning() << "SeekRow function failed. Ensure it's not being called on hierarchy tables or message stores tables";
       
   362         }
       
   363 
       
   364         if(initFailed)
       
   365             m_error = QMessageManager::ContentInaccessible;
       
   366     }
       
   367 
       
   368     QueryAllRows::~QueryAllRows()
       
   369     {
       
   370         FreeProws(m_rows);
       
   371         m_rows = 0;
       
   372     }
       
   373 
       
   374     LONG QueryAllRows::rowCount()
       
   375     {
       
   376         if (m_error != QMessageManager::NoError)
       
   377             return -1;
       
   378 
       
   379         ULONG count(0);
       
   380         HRESULT rv = m_table->GetRowCount(0, &count);
       
   381         if (HR_FAILED(rv)) {
       
   382             m_error = QMessageManager::ContentInaccessible;
       
   383             return -1;
       
   384         }
       
   385 
       
   386         return count;
       
   387     }
       
   388 
       
   389     bool QueryAllRows::query()
       
   390     {
       
   391         if (m_error != QMessageManager::NoError)
       
   392             return false;
       
   393 
       
   394         FreeProws(m_rows);
       
   395         m_rows = 0;
       
   396         m_error = QMessageManager::NoError;
       
   397 
       
   398         HRESULT rv = m_table->QueryRows( QueryAllRows::BatchSize, NULL, &m_rows);
       
   399         if (HR_FAILED(rv)) {
       
   400             m_error = QMessageManager::ContentInaccessible;
       
   401             return false;
       
   402         }
       
   403 
       
   404         return (m_rows && m_rows->cRows);
       
   405     }
       
   406 
       
   407     LPSRowSet QueryAllRows::rows() const
       
   408     {
       
   409         return m_rows;
       
   410     }
       
   411 
       
   412     QMessageManager::Error QueryAllRows::error() const
       
   413     {
       
   414         return m_error;
       
   415     }
       
   416 
       
   417     ADRLIST *createAddressList(int count, int propertyCount)
       
   418     {
       
   419         ADRLIST *list(0);
       
   420 
       
   421         uint size = CbNewADRLIST(count);
       
   422         MAPIAllocateBuffer(size, reinterpret_cast<LPVOID*>(&list));
       
   423         if (list) {
       
   424             memset(list, 0, size);
       
   425             list->cEntries = count;
       
   426 
       
   427             for (int i = 0; i < count; ++i) {
       
   428                 list->aEntries[i].cValues = propertyCount;
       
   429                 MAPIAllocateBuffer(propertyCount * sizeof(SPropValue), reinterpret_cast<LPVOID*>(&list->aEntries[i].rgPropVals));
       
   430             }
       
   431         }
       
   432 
       
   433         return list;
       
   434     }
       
   435 
       
   436     void fillAddressEntry(ADRENTRY &entry, const QMessageAddress &addr, LONG type, QList<LPTSTR> &addresses)
       
   437     {
       
   438         entry.rgPropVals[0].ulPropTag = PR_RECIPIENT_TYPE;
       
   439         entry.rgPropVals[0].Value.l = type;
       
   440 
       
   441 #ifdef _WIN32_WCE
       
   442         QString addressStr = addr.addressee();
       
   443 #else
       
   444         QString addressStr("[%1:%2]");
       
   445         addressStr = addressStr.arg(addr.type() == QMessageAddress::Phone ? "SMS" : "SMTP");
       
   446         addressStr = addressStr.arg(addr.addressee());
       
   447 #endif
       
   448 
       
   449         // TODO: Escape illegal characters, as per: http://msdn.microsoft.com/en-us/library/cc842281.aspx
       
   450 
       
   451         uint len = addressStr.length();
       
   452         LPTSTR address = new TCHAR[len + 1];
       
   453         memcpy(address, addressStr.utf16(), len * sizeof(TCHAR));
       
   454         address[len] = 0;
       
   455 
       
   456 #ifdef _WIN32_WCE
       
   457 
       
   458         entry.rgPropVals[1].ulPropTag = PR_EMAIL_ADDRESS;
       
   459         entry.rgPropVals[1].Value.LPSZ = address;
       
   460 
       
   461         if(addr.type() == QMessageAddress::Email)
       
   462         {
       
   463             //Set the address type(SMTP is the only type currently supported)
       
   464             entry.rgPropVals[2].ulPropTag = PR_ADDRTYPE;
       
   465             entry.rgPropVals[2].Value.LPSZ = L"SMTP";
       
   466         }
       
   467         else if(addr.type() == QMessageAddress::Phone)
       
   468         {
       
   469             //Set the address type(SMTP is the only type currently supported)
       
   470             entry.rgPropVals[2].ulPropTag = PR_ADDRTYPE;
       
   471             entry.rgPropVals[2].Value.LPSZ = L"SMS";
       
   472         }
       
   473         else
       
   474             qWarning() << "Unrecognized address type";
       
   475 
       
   476 #else
       
   477         entry.rgPropVals[1].ulPropTag = PR_DISPLAY_NAME;
       
   478         entry.rgPropVals[1].Value.LPSZ = address;
       
   479 #endif
       
   480 
       
   481         addresses.append(address);
       
   482     }
       
   483 
       
   484     bool resolveAddressList(ADRLIST *list, IMAPISession *session)
       
   485     {
       
   486         bool result(false);
       
   487 
       
   488         if (session) {
       
   489             IAddrBook *book(0);
       
   490             HRESULT rv = session->OpenAddressBook(0, 0, AB_NO_DIALOG, &book);
       
   491             if (HR_SUCCEEDED(rv)) {
       
   492                 rv = book->ResolveName(0, MAPI_UNICODE, 0, list);
       
   493                 if (HR_SUCCEEDED(rv)) {
       
   494                     result = true;
       
   495                 } else {
       
   496                     qWarning() << "Unable to resolve addresses.";
       
   497                 }
       
   498 
       
   499                 book->Release();
       
   500             } else {
       
   501                 qWarning() << "Unable to open address book.";
       
   502             }
       
   503         }
       
   504 
       
   505         return result;
       
   506     }
       
   507 
       
   508     void destroyAddressList(ADRLIST *list, QList<LPTSTR> &addresses)
       
   509     {
       
   510         foreach (LPTSTR address, addresses) {
       
   511             delete [] address;
       
   512         }
       
   513 
       
   514         addresses.clear();
       
   515 
       
   516         for (uint i = 0; i < list->cEntries; ++i) {
       
   517             MAPIFreeBuffer(list->aEntries[i].rgPropVals);
       
   518         }
       
   519 
       
   520         MAPIFreeBuffer(list);
       
   521     }
       
   522 
       
   523     ULONG addAttachment(IMessage* message, const QMessageContentContainer& attachmentContainer)
       
   524     {
       
   525         IAttach *attachment(0);
       
   526         ULONG attachmentNumber(0);
       
   527 
       
   528         if (HR_SUCCEEDED(message->CreateAttach(NULL, 0, &attachmentNumber, &attachment))) {
       
   529             QString fileName(attachmentContainer.suggestedFileName());
       
   530             QString extension;
       
   531             int index = fileName.lastIndexOf(".");
       
   532             if (index != -1) {
       
   533                 extension = fileName.mid(index);
       
   534             }
       
   535 
       
   536             WinHelpers::Lptstr suggestedFileNameLptstr(WinHelpers::LptstrFromQString(fileName));
       
   537             WinHelpers::Lptstr extensionLptstr(WinHelpers::LptstrFromQString(extension));
       
   538 
       
   539             const int nProperties = 5;
       
   540             SPropValue prop[nProperties] = { 0 };
       
   541 
       
   542             prop[0].ulPropTag = PR_ATTACH_METHOD;
       
   543             prop[0].Value.ul = ATTACH_BY_VALUE;
       
   544             prop[1].ulPropTag = PR_RENDERING_POSITION;
       
   545             prop[1].Value.l = -1;
       
   546             prop[2].ulPropTag = PR_ATTACH_LONG_FILENAME;
       
   547             prop[2].Value.LPSZ = suggestedFileNameLptstr;
       
   548             prop[3].ulPropTag = PR_ATTACH_FILENAME;
       
   549             prop[3].Value.LPSZ = suggestedFileNameLptstr;
       
   550             prop[4].ulPropTag = PR_ATTACH_EXTENSION;
       
   551             prop[4].Value.LPSZ = extensionLptstr;
       
   552 
       
   553             if (HR_SUCCEEDED(attachment->SetProps(nProperties, prop, NULL))) {
       
   554                 IStream *os(0);
       
   555                 if (HR_SUCCEEDED(attachment->OpenProperty(PR_ATTACH_DATA_BIN, &IID_IStream, 0, MAPI_MODIFY | MAPI_CREATE, (LPUNKNOWN*)&os))) {
       
   556                     const int BUF_SIZE=4096;
       
   557                     char pData[BUF_SIZE];
       
   558                     ULONG ulSize=0,ulRead,ulWritten, ulTotalWritten=0;
       
   559 
       
   560                     QDataStream attachmentStream(attachmentContainer.content());
       
   561 
       
   562                     ulRead=attachmentStream.readRawData(static_cast<char*>(pData), BUF_SIZE);
       
   563                     while (ulRead) {
       
   564                         os->Write(pData,ulRead, &ulWritten);
       
   565                         ulTotalWritten += ulWritten;
       
   566 
       
   567                         ulSize += ulRead;
       
   568                         ulRead = attachmentStream.readRawData(static_cast<char*>(pData), BUF_SIZE);
       
   569                     }
       
   570 
       
   571                     ULARGE_INTEGER uli = { 0 };
       
   572                     uli.LowPart = ulTotalWritten;
       
   573                     os->SetSize(uli);
       
   574 
       
   575                     os->Commit(STGC_DEFAULT);
       
   576 
       
   577                     mapiRelease(os);
       
   578 
       
   579                     prop[0].ulPropTag=PR_ATTACH_SIZE;
       
   580                     prop[0].Value.ul=ulSize;
       
   581                     attachment->SetProps(1, prop, NULL);
       
   582 #ifndef _WIN32_WCE //unsupported
       
   583                     attachment->SaveChanges(KEEP_OPEN_READONLY);
       
   584 #endif
       
   585                 } else {
       
   586                     qWarning() << "Could not open MAPI attachment data stream";
       
   587                 }
       
   588             } else {
       
   589                 qWarning() << "Could not set MAPI attachment properties";
       
   590             }
       
   591 
       
   592             mapiRelease(attachment);
       
   593         } else {
       
   594             qWarning() << "Could not create MAPI attachment";
       
   595         }
       
   596 
       
   597         return attachmentNumber;
       
   598     }
       
   599 
       
   600     template<typename T> QString getLastError(T& mapiType, HRESULT hr)
       
   601     {
       
   602         LPMAPIERROR err;
       
   603 
       
   604         HRESULT thisResult = mapiType.GetLastError(hr,MAPI_UNICODE,&err);
       
   605 
       
   606         if(thisResult != S_OK || !err)
       
   607         {
       
   608             qWarning() << "Could not get last MAPI error string";
       
   609             return QString();
       
   610         }
       
   611 
       
   612         QString mapiErrorMsg = QStringFromLpctstr(err->lpszError);
       
   613         QString mapiComponent = QStringFromLpctstr(err->lpszComponent);
       
   614 
       
   615         QString result = QString("MAPI Error: %1 ; MAPI Component: %2").arg(mapiErrorMsg).arg(mapiComponent);
       
   616 
       
   617         MAPIFreeBuffer(err);
       
   618 
       
   619         return result;
       
   620     }
       
   621 
       
   622     typedef QPair<QString, QString> StringPair;
       
   623 
       
   624     QList<StringPair> decomposeHeaders(const QString &headers)
       
   625     {
       
   626         QList<StringPair> result;
       
   627 
       
   628         if (!headers.isEmpty()) {
       
   629             int lastIndex = 0;
       
   630             int index = -2;
       
   631 
       
   632             do {
       
   633                 index += 2;
       
   634                 lastIndex = index;
       
   635 
       
   636                 // Find CRLF not followed by whitespace
       
   637                 QRegExp lineSeparator("\r\n(?!\\s)");
       
   638                 index = headers.indexOf(lineSeparator, lastIndex);
       
   639 
       
   640                 QString line = headers.mid(lastIndex, (index == -1 ? -1 : (index - lastIndex)));
       
   641 
       
   642                 // Split the current line
       
   643                 QRegExp headerIdentifier("\\s*(\\w+)\\s*:");
       
   644                 if (line.indexOf(headerIdentifier) == 0) {
       
   645                     result.append(qMakePair(headerIdentifier.cap(1), line.mid(headerIdentifier.cap(0).length()).trimmed()));
       
   646                 } else {
       
   647                     // Assume the whole line is an identifier
       
   648                     result.append(qMakePair(line, QString()));
       
   649                 }
       
   650             } while (index != -1);
       
   651         }
       
   652 
       
   653         return result;
       
   654     }
       
   655 
       
   656     QMessageAddress createAddress(const QString &name, const QString &address)
       
   657     {
       
   658         QMessageAddress result;
       
   659 
       
   660         if (!name.isEmpty() || !address.isEmpty()) {
       
   661             QString from;
       
   662             if (!name.isEmpty() && !address.isEmpty() && (name != address)) {
       
   663                 from = name + " <" + address + ">";
       
   664             } else {
       
   665                 from = (!name.isEmpty() ? name : address);
       
   666             }
       
   667 
       
   668             result = QMessageAddress(QMessageAddress::Email, from);
       
   669         }
       
   670 
       
   671         return result;
       
   672     }
       
   673 
       
   674     ULONG streamSize(QMessageManager::Error* error, IStream* is)
       
   675     {
       
   676         ULONG size = 0;
       
   677         STATSTG stg = { 0 };
       
   678         HRESULT rv = is->Stat(&stg, STATFLAG_NONAME);
       
   679         if (HR_SUCCEEDED(rv)) {
       
   680             size = stg.cbSize.LowPart;
       
   681         } else {
       
   682             *error = QMessageManager::ContentInaccessible;
       
   683         }
       
   684         return size;
       
   685     }
       
   686 
       
   687     QByteArray readStream(QMessageManager::Error *error, IStream *is)
       
   688     {
       
   689         QByteArray result;
       
   690 
       
   691         STATSTG stg = { 0 };
       
   692         HRESULT rv = is->Stat(&stg, STATFLAG_NONAME);
       
   693         if (HR_SUCCEEDED(rv)) {
       
   694             ULONG sz = stg.cbSize.LowPart;
       
   695             result.resize(sz);
       
   696             ULONG bytes = 0;
       
   697             rv = is->Read(result.data(), sz, &bytes);
       
   698             if (HR_FAILED(rv)) {
       
   699                 *error = QMessageManager::ContentInaccessible;
       
   700             }
       
   701         } else {
       
   702             *error = QMessageManager::ContentInaccessible;
       
   703         }
       
   704 
       
   705         return result;
       
   706     }
       
   707 
       
   708     bool writeStream(QMessageManager::Error *error, IStream *os, const void *address, ULONG bytes, bool truncate)
       
   709     {
       
   710         ULONG existingSize = truncate ? streamSize(error, os) : 0;
       
   711         if (*error == QMessageManager::NoError) {
       
   712             HRESULT rv = S_OK;
       
   713             if (existingSize > bytes) {
       
   714                 ULARGE_INTEGER uli = { 0 };
       
   715                 uli.LowPart = bytes;
       
   716                 rv = os->SetSize(uli);
       
   717             }
       
   718 
       
   719             if (HR_SUCCEEDED(rv)) {
       
   720                 ULONG written(0);
       
   721                 rv = os->Write(address, bytes, &written);
       
   722                 if (HR_SUCCEEDED(rv)) {
       
   723                     if (written < bytes) {
       
   724                         qWarning() << "Only wrote partial data to output stream.";
       
   725                     } else {
       
   726                         rv = os->Commit(STGC_DEFAULT);
       
   727                         if (HR_SUCCEEDED(rv)) {
       
   728                             return true;
       
   729                         } else {
       
   730                             qWarning() << "Unable to commit write to output stream.";
       
   731                         }
       
   732                     }
       
   733                 } else {
       
   734                     qWarning() << "Unable to write data to output stream.";
       
   735                 }
       
   736             } else {
       
   737                 qWarning() << "Unable to resize output stream.";
       
   738             }
       
   739         }
       
   740 
       
   741         *error = QMessageManager::FrameworkFault;
       
   742         return false;
       
   743     }
       
   744 
       
   745     QString decodeContent(const QByteArray &data, const QByteArray &charset, int length = -1)
       
   746     {
       
   747         QString result;
       
   748 
       
   749         QTextCodec *codec = QTextCodec::codecForName(charset);
       
   750         if (codec) {
       
   751             if (length == -1) {
       
   752                 // Convert the entirety
       
   753                 result = codec->toUnicode(data);
       
   754             } else {
       
   755                 // Convert only the first length bytes
       
   756                 QTextCodec::ConverterState state;
       
   757                 result = codec->toUnicode(data, length, &state);
       
   758             }
       
   759         } else {
       
   760             qWarning() << "No codec for charset:" << charset;
       
   761         }
       
   762 
       
   763         return result;
       
   764     }
       
   765 
       
   766 #ifndef _WIN32_WCE
       
   767     QByteArray extractPlainText(const QByteArray &rtf)
       
   768     {
       
   769         // Attempt to extract the HTML from the RTF
       
   770         // as per CMapiEx, http://www.codeproject.com/KB/IP/CMapiEx.aspx
       
   771         QByteArray text;
       
   772 
       
   773         const QByteArray startTag("\\fs20");
       
   774         int index = rtf.indexOf(startTag);
       
   775         if (index != -1) {
       
   776             const QByteArray par("\\par");
       
   777             const QByteArray tab("\\tab");
       
   778             const QByteArray li("\\li");
       
   779             const QByteArray fi("\\fi-");
       
   780             const QByteArray pntext("\\pntext");
       
   781 
       
   782             const char zero = '\0';
       
   783             const char space = ' ';
       
   784             const char openingBrace = '{';
       
   785             const char closingBrace = '}';
       
   786             const char ignore[] = { openingBrace, closingBrace, '\r', '\n' };
       
   787 
       
   788             QByteArray::const_iterator rit = rtf.constBegin() + index, rend = rtf.constEnd();
       
   789             while ((rit != rend) && (*rit != zero)) {
       
   790                 if (*rit == ignore[0] || *rit == ignore[1] || *rit == ignore[2] || *rit == ignore[3]) {
       
   791                     ++rit;
       
   792                 } else {
       
   793                     bool skipSection(false);
       
   794                     bool skipDigits(false);
       
   795                     bool skipSpace(false);
       
   796 
       
   797                     const QByteArray remainder(QByteArray::fromRawData(rit, (rend - rit)));
       
   798 
       
   799                     if (remainder.startsWith(par)) {
       
   800                         rit += par.length();
       
   801                         text += "\r\n";
       
   802                         skipSpace = true;
       
   803                     } else if (remainder.startsWith(tab)) {
       
   804                         rit += tab.length();
       
   805                         text += "\t";
       
   806                         skipSpace = true;
       
   807                     } else if (remainder.startsWith(li)) {
       
   808                         rit += li.length();
       
   809                         skipDigits = true;
       
   810                         skipSpace = true;
       
   811                     } else if (remainder.startsWith(fi)) {
       
   812                         rit += fi.length();
       
   813                         skipDigits = true;
       
   814                         skipSpace = true;
       
   815                     } else if (remainder.startsWith(QByteArray("\\'"))) {
       
   816                         rit += 2;
       
   817                         QByteArray encodedChar(QByteArray::fromRawData(rit, 2));
       
   818                         rit += 2;
       
   819                         text += char(encodedChar.toUInt(0, 16));
       
   820                     } else if (remainder.startsWith(pntext)) {
       
   821                         rit += pntext.length();
       
   822                         skipSection = true;
       
   823                     } else if (remainder.startsWith(QByteArray("\\{"))) {
       
   824                         rit += 2;
       
   825                         text += "{";
       
   826                     } else if (remainder.startsWith(QByteArray("\\}"))) {
       
   827                         rit += 2;
       
   828                         text += "}";
       
   829                     } else {
       
   830                         text += *rit;
       
   831                         ++rit;
       
   832                     }
       
   833 
       
   834                     if (skipSection) {
       
   835                         while ((rit != rend) && (*rit != closingBrace)) {
       
   836                             ++rit;
       
   837                         }
       
   838                     }
       
   839                     if (skipDigits) {
       
   840                         while ((rit != rend) && isdigit(*rit)) {
       
   841                             ++rit;
       
   842                         }
       
   843                     }
       
   844                     if (skipSpace) {
       
   845                         if ((rit != rend) && (*rit == space)) {
       
   846                             ++rit;
       
   847                         }
       
   848                     }
       
   849                 }
       
   850             }
       
   851         }
       
   852 
       
   853         return text;
       
   854     }
       
   855 
       
   856     int digitValue(char n)
       
   857     {
       
   858         if (n >= '0' && n <= '9') {
       
   859             return (n - '0');
       
   860         }
       
   861 
       
   862         return 0;
       
   863     }
       
   864 
       
   865     QByteArray extractHtml(const QByteArray &rtf)
       
   866     {
       
   867         // Attempt to extract the HTML from the RTF
       
   868         // as per CMapiEx, http://www.codeproject.com/KB/IP/CMapiEx.aspx
       
   869         QByteArray html;
       
   870 
       
   871         const QByteArray htmltag("\\*\\htmltag");
       
   872         int index = rtf.indexOf("<html", Qt::CaseInsensitive);
       
   873         if (index == -1) {
       
   874             index = rtf.indexOf(htmltag, Qt::CaseInsensitive);
       
   875         }
       
   876         if (index != -1) {
       
   877             const QByteArray mhtmltag("\\*\\mhtmltag");
       
   878             const QByteArray par("\\par");
       
   879             const QByteArray tab("\\tab");
       
   880             const QByteArray li("\\li");
       
   881             const QByteArray fi("\\fi-");
       
   882             const QByteArray pntext("\\pntext");
       
   883             const QByteArray htmlrtf("\\htmlrtf");
       
   884 
       
   885             const char zero = '\0';
       
   886             const char space = ' ';
       
   887             const char openingBrace = '{';
       
   888             const char closingBrace = '}';
       
   889             const char ignore[] = { openingBrace, closingBrace, '\r', '\n' };
       
   890 
       
   891             int tagIgnored = -1;
       
   892 
       
   893             QByteArray::const_iterator rit = rtf.constBegin() + index, rend = rtf.constEnd();
       
   894             while ((rit != rend) && (*rit != zero)) {
       
   895                 if (*rit == ignore[0] || *rit == ignore[1] || *rit == ignore[2] || *rit == ignore[3]) {
       
   896                     ++rit;
       
   897                 } else {
       
   898                     bool skipSection(false);
       
   899                     bool skipDigits(false);
       
   900                     bool skipSpace(false);
       
   901 
       
   902                     const QByteArray remainder(QByteArray::fromRawData(rit, (rend - rit)));
       
   903 
       
   904                     if (remainder.startsWith(htmltag)) {
       
   905                         rit += htmltag.length();
       
   906 
       
   907                         int tagNumber = 0;
       
   908                         while (isdigit(*rit)) {
       
   909                             tagNumber = (tagNumber * 10) + digitValue(*rit);
       
   910                             ++rit;
       
   911                         }
       
   912                         skipSpace = true;
       
   913 
       
   914                         if (tagNumber == tagIgnored) {
       
   915                             skipSection = true;
       
   916                             tagIgnored = -1;
       
   917                         }
       
   918                     } else if (remainder.startsWith(mhtmltag)) {
       
   919                         rit += mhtmltag.length();
       
   920 
       
   921                         int tagNumber = 0;
       
   922                         while (isdigit(*rit)) {
       
   923                             tagNumber = (tagNumber * 10) + digitValue(*rit);
       
   924                             ++rit;
       
   925                         }
       
   926                         skipSpace = true;
       
   927 
       
   928                         tagIgnored = tagNumber;
       
   929                     } else if (remainder.startsWith(par)) {
       
   930                         rit += par.length();
       
   931                         html += char('\r');
       
   932                         html += char('\n');
       
   933                         skipSpace = true;
       
   934                     } else if (remainder.startsWith(tab)) {
       
   935                         rit += tab.length();
       
   936                         html += char('\t');
       
   937                         skipSpace = true;
       
   938                     } else if (remainder.startsWith(li)) {
       
   939                         rit += li.length();
       
   940                         skipDigits = true;
       
   941                         skipSpace = true;
       
   942                     } else if (remainder.startsWith(fi)) {
       
   943                         rit += fi.length();
       
   944                         skipDigits = true;
       
   945                         skipSpace = true;
       
   946                     } else if (remainder.startsWith(QByteArray("\\'"))) {
       
   947                         rit += 2;
       
   948 
       
   949                         QByteArray encodedChar(QByteArray::fromRawData(rit, 2));
       
   950                         rit += 2;
       
   951                         html += char(encodedChar.toUInt(0, 16));
       
   952                     } else if (remainder.startsWith(pntext)) {
       
   953                         rit += pntext.length();
       
   954                         skipSection = true;
       
   955                     } else if (remainder.startsWith(htmlrtf)) {
       
   956                         rit += htmlrtf.length();
       
   957 
       
   958                         // Find the terminating tag
       
   959                         const QByteArray terminator("\\htmlrtf0");
       
   960                         int index = remainder.indexOf(terminator, htmlrtf.length());
       
   961                         if (index == -1) {
       
   962                             rit = rend;
       
   963                         } else {
       
   964                             rit += (index + terminator.length() - htmlrtf.length());
       
   965                             skipSpace = true;
       
   966                         }
       
   967                     } else if (remainder.startsWith(QByteArray("\\{"))) {
       
   968                         rit += 2;
       
   969                         html += openingBrace;
       
   970                     } else if (remainder.startsWith(QByteArray("\\}"))) {
       
   971                         rit += 2;
       
   972                         html += closingBrace;
       
   973                     } else {
       
   974                         html += *rit;
       
   975                         ++rit;
       
   976                     }
       
   977 
       
   978                     if (skipSection) {
       
   979                         while ((rit != rend) && (*rit != closingBrace)) {
       
   980                             ++rit;
       
   981                         }
       
   982                     }
       
   983                     if (skipDigits) {
       
   984                         while ((rit != rend) && isdigit(*rit)) {
       
   985                             ++rit;
       
   986                         }
       
   987                     }
       
   988                     if (skipSpace) {
       
   989                         if ((rit != rend) && (*rit == space)) {
       
   990                             ++rit;
       
   991                         }
       
   992                     }
       
   993                 }
       
   994             }
       
   995         }
       
   996 
       
   997         return html;
       
   998     }
       
   999 #endif
       
  1000 
       
  1001     void storeMessageProperties(QMessageManager::Error *error, const QMessage &source, IMessage *message)
       
  1002     {
       
  1003         if (!setMapiProperty(message, PR_SUBJECT, source.subject())) {
       
  1004             qWarning() << "Unable to set subject in message.";
       
  1005             *error = QMessageManager::FrameworkFault;
       
  1006         } else {
       
  1007             QString emailAddress = source.from().addressee();
       
  1008             if (!setMapiProperty(message, PR_SENDER_EMAIL_ADDRESS, emailAddress)) {
       
  1009                 qWarning() << "Unable to set sender address in message.";
       
  1010                 *error = QMessageManager::FrameworkFault;
       
  1011             } else {
       
  1012 #ifdef _WIN32_WCE
       
  1013                 unsigned long msgType  = 0;
       
  1014                 if(source.type() == QMessage::Email)
       
  1015                     msgType = MSGSTATUS_RECTYPE_SMTP;
       
  1016                 else if(source.type() == QMessage::Sms)
       
  1017                     msgType = MSGSTATUS_RECTYPE_SMS;
       
  1018                 else
       
  1019                     qWarning() << "Unrecognized message type";
       
  1020 
       
  1021                 if(msgType && !setMapiProperty(message, PR_MSG_STATUS, static_cast<long>(msgType)))
       
  1022                 {
       
  1023                     qWarning() << "Unable to set type of message.";
       
  1024                     *error = QMessageManager::FrameworkFault;
       
  1025                 }
       
  1026                 else
       
  1027                 {
       
  1028 #endif
       
  1029                     if (!setMapiProperty(message, PR_CLIENT_SUBMIT_TIME, toFileTime(source.date()))) {
       
  1030                         qWarning() << "Unable to set submit time in message.";
       
  1031                         *error = QMessageManager::FrameworkFault;
       
  1032                     } else {
       
  1033                         QDateTime receivedDate(source.receivedDate());
       
  1034                         if (receivedDate.isValid()) {
       
  1035                             if (!setMapiProperty(message, PR_MESSAGE_DELIVERY_TIME, toFileTime(receivedDate))) {
       
  1036                                 qWarning() << "Unable to set delivery time in message.";
       
  1037                                 *error = QMessageManager::FrameworkFault;
       
  1038                             }
       
  1039                         }
       
  1040 
       
  1041                         if (*error == QMessageManager::NoError) {
       
  1042                             // Mark this message as read/unread
       
  1043 #ifndef _WIN32_WCE
       
  1044                             LONG flags = (source.status() & QMessage::Read ? 0 : CLEAR_READ_FLAG);
       
  1045                             HRESULT rv = message->SetReadFlag(flags);
       
  1046                             if (HR_FAILED(rv)) {
       
  1047                                 qWarning() << "Unable to set flags in message.";
       
  1048                                 *error = QMessageManager::FrameworkFault;
       
  1049                             }
       
  1050 #else
       
  1051                             LONG flags = (source.status() & QMessage::Read ? MSGFLAG_READ : 0);
       
  1052                             if (!setMapiProperty(message, PR_MESSAGE_FLAGS, flags)) {
       
  1053                                 qWarning() << "Unable to set flags in message.";
       
  1054                                 *error = QMessageManager::FrameworkFault;
       
  1055                             }
       
  1056 #endif
       
  1057                         }
       
  1058 
       
  1059                         if (*error == QMessageManager::NoError) {
       
  1060                             LONG priority = (source.priority() == QMessage::HighPriority ? PRIO_URGENT : (source.priority() == QMessage::NormalPriority ? PRIO_NORMAL : PRIO_NONURGENT));
       
  1061                             if (!setMapiProperty(message, PR_PRIORITY, priority)) {
       
  1062                                 qWarning() << "Unable to set priority in message.";
       
  1063                                 *error = QMessageManager::FrameworkFault;
       
  1064                             }
       
  1065                         }
       
  1066 
       
  1067                         if (*error == QMessageManager::NoError) {
       
  1068                             QStringList headers;
       
  1069                             foreach (const QByteArray &name, source.headerFields()) {
       
  1070                                 foreach (const QString &value, source.headerFieldValues(name)) {
       
  1071                                     // TODO: Do we need soft line-breaks?
       
  1072                                     headers.append(QString("%1: %2").arg(QString(name)).arg(value));
       
  1073                                 }
       
  1074                             }
       
  1075                             if (!headers.isEmpty()) {
       
  1076                                 QString transportHeaders = headers.join("\r\n").append("\r\n\r\n");
       
  1077                                 if (!setMapiProperty(message, PR_TRANSPORT_MESSAGE_HEADERS, transportHeaders)) {
       
  1078                                     qWarning() << "Unable to set transport headers in message.";
       
  1079                                     *error = QMessageManager::FrameworkFault;
       
  1080                                 }
       
  1081                             }
       
  1082                         }
       
  1083                     }
       
  1084                 }
       
  1085 #ifdef _WIN32_WCE
       
  1086             }
       
  1087 #endif
       
  1088         }
       
  1089     }
       
  1090 
       
  1091     void replaceMessageRecipients(QMessageManager::Error *error, const QMessage &source, IMessage *message, IMAPISession *session)
       
  1092     {
       
  1093 #ifdef _WIN32_WCE
       
  1094         Q_UNUSED(session)
       
  1095 
       
  1096         // For CE, the only available option is to replace the existing list
       
  1097         HRESULT rv;
       
  1098 #else
       
  1099         // Find any existing recipients and remove them
       
  1100         IMAPITable *recipientsTable(0);
       
  1101         HRESULT rv = message->GetRecipientTable(MAPI_UNICODE, &recipientsTable);
       
  1102         if (HR_SUCCEEDED(rv)) {
       
  1103             ULONG count(0);
       
  1104             rv = recipientsTable->GetRowCount(0, &count);
       
  1105             if (HR_SUCCEEDED(rv)) {
       
  1106                 if (count > 0) {
       
  1107                     ADRLIST *list = createAddressList(count, 1);
       
  1108                     if (list) {
       
  1109                         int index = 0;
       
  1110                         SizedSPropTagArray(1, columns) = {1, {PR_ROWID}};
       
  1111 
       
  1112                         QueryAllRows qar(recipientsTable, reinterpret_cast<LPSPropTagArray>(&columns), 0, 0);
       
  1113                         while (qar.query()) {
       
  1114                             for (uint n = 0; n < qar.rows()->cRows; ++n) {
       
  1115                                 ULONG rowId = qar.rows()->aRow[n].lpProps[0].Value.l;
       
  1116                                 if (rowId) {
       
  1117                                     // Add this row's ID to the removal list
       
  1118                                     ADRENTRY &entry(list->aEntries[index]);
       
  1119                                     entry.rgPropVals[0].ulPropTag = PR_ROWID;
       
  1120                                     entry.rgPropVals[0].Value.l = rowId;
       
  1121                                     ++index;
       
  1122                                 }
       
  1123                             }
       
  1124                         }
       
  1125 
       
  1126                         if (qar.error() != QMessageManager::NoError) {
       
  1127                             *error = qar.error();
       
  1128                         } else {
       
  1129                             rv = message->ModifyRecipients(MODRECIP_REMOVE, list);
       
  1130                             if (HR_FAILED(rv)) {
       
  1131                                 qWarning() << "Unable to clear address list for message.";
       
  1132                                 *error = QMessageManager::FrameworkFault;
       
  1133                             }
       
  1134                         }
       
  1135 
       
  1136                         destroyAddressList(list, QList<LPTSTR>());
       
  1137                     } else {
       
  1138                         qWarning() << "Unable to allocate address list for message.";
       
  1139                         *error = QMessageManager::FrameworkFault;
       
  1140                     }
       
  1141                 }
       
  1142             } else {
       
  1143                 qWarning() << "Unable to get row count for recipients table.";
       
  1144                 *error = QMessageManager::ContentInaccessible;
       
  1145             }
       
  1146 
       
  1147             mapiRelease(recipientsTable);
       
  1148         } else {
       
  1149             if (rv != MAPI_E_NO_RECIPIENTS) {
       
  1150                 *error = QMessageManager::FrameworkFault;
       
  1151                 qWarning() << "Unable to get recipients table from message.";
       
  1152             }
       
  1153         }
       
  1154 #endif
       
  1155 
       
  1156         if (*error == QMessageManager::NoError) {
       
  1157             // Add the current message recipients
       
  1158             uint recipientCount(source.to().count() + source.cc().count() + source.bcc().count());
       
  1159             if (recipientCount) {
       
  1160 #ifdef _WIN32_WCE
       
  1161                 unsigned int propertyCount = 3;
       
  1162 #else
       
  1163                 unsigned int propertyCount = 2;
       
  1164 #endif
       
  1165 
       
  1166                 ADRLIST *list = createAddressList(recipientCount, propertyCount);
       
  1167                 if (list) {
       
  1168                     int index = 0;
       
  1169                     QList<LPTSTR> addresses;
       
  1170 
       
  1171                     foreach (const QMessageAddress &addr, source.to()) {
       
  1172                         ADRENTRY &entry(list->aEntries[index]);
       
  1173                         fillAddressEntry(entry, addr, MAPI_TO, addresses);
       
  1174                         ++index;
       
  1175                     }
       
  1176 
       
  1177                     foreach (const QMessageAddress &addr, source.cc()) {
       
  1178                         ADRENTRY &entry(list->aEntries[index]);
       
  1179                         fillAddressEntry(entry, addr, MAPI_CC, addresses);
       
  1180                         ++index;
       
  1181                     }
       
  1182 
       
  1183                     foreach (const QMessageAddress &addr, source.bcc()) {
       
  1184                         ADRENTRY &entry(list->aEntries[index]);
       
  1185                         fillAddressEntry(entry, addr, MAPI_BCC, addresses);
       
  1186                         ++index;
       
  1187                     }
       
  1188 
       
  1189 #ifndef _WIN32_WCE
       
  1190                     if (resolveAddressList(list, session)) {
       
  1191 #endif
       
  1192                         rv = message->ModifyRecipients(MODRECIP_ADD, list);
       
  1193                         if (HR_FAILED(rv)) {
       
  1194                             qWarning() << "Unable to store address list for message.";
       
  1195                             *error = QMessageManager::FrameworkFault;
       
  1196                         }
       
  1197 #ifndef _WIN32_WCE
       
  1198                     } else {
       
  1199                         qWarning() << "Unable to resolve address list for message.";
       
  1200                         *error = QMessageManager::FrameworkFault;
       
  1201                     }
       
  1202 #endif
       
  1203 
       
  1204                     destroyAddressList(list, addresses);
       
  1205                 } else {
       
  1206                     qWarning() << "Unable to allocate address list for message.";
       
  1207                     *error = QMessageManager::FrameworkFault;
       
  1208                 }
       
  1209             }
       
  1210         }
       
  1211     }
       
  1212 
       
  1213     void replaceMessageBody(QMessageManager::Error *error, const QMessage &source, IMessage *message, const MapiStorePtr &store)
       
  1214     {
       
  1215         Q_UNUSED(store);
       
  1216         // Remove any preexisting body elements
       
  1217 #ifndef _WIN32_WCE
       
  1218         SizedSPropTagArray(2, props) = {2, {PR_BODY, PR_RTF_COMPRESSED}};
       
  1219 #elif(_WIN32_WCE > 0x501)
       
  1220         SizedSPropTagArray(2, props) = {2, {PR_BODY_HTML_A, PR_BODY_W}};
       
  1221 #else
       
  1222         SizedSPropTagArray(2, props) = {2, {PR_BODY, PR_BODY_W}};
       
  1223 #endif
       
  1224         HRESULT rv = message->DeleteProps(reinterpret_cast<LPSPropTagArray>(&props), 0);
       
  1225 #ifdef _WIN32_WCE
       
  1226         if (rv == E_FAIL) {
       
  1227             // On CE, deleting nonexisting properties fails... assume that they're not present
       
  1228             rv = S_OK;
       
  1229         }
       
  1230 #endif
       
  1231         if (HR_SUCCEEDED(rv)) {
       
  1232             // Insert the current body data
       
  1233             QMessageContentContainerId bodyId(source.bodyId());
       
  1234             if (bodyId.isValid()) {
       
  1235                 QMessageContentContainer bodyContent(source.find(bodyId));
       
  1236                 QByteArray subType(bodyContent.contentSubType().toLower());
       
  1237 
       
  1238 #ifdef _WIN32_WCE
       
  1239                 if (subType == "rtf") {
       
  1240                     // CE does not support RTF; just store it as plain text
       
  1241                     subType = "plain";
       
  1242                 } else if (subType == "html") {
       
  1243                     // If the store doesn't support HTML then fallback to text
       
  1244                     if (!store->supports(STORE_HTML_OK)) {
       
  1245                         subType = "plain";
       
  1246                         qWarning() << "Store does not support HTML.";
       
  1247                     }
       
  1248                 }
       
  1249 
       
  1250                 if (subType == "html") {
       
  1251                     IStream *os(0);
       
  1252 #if(_WIN32_WCE > 0x501)
       
  1253                     HRESULT rv = message->OpenProperty(PR_BODY_HTML_A, 0, STGM_WRITE, MAPI_MODIFY | MAPI_CREATE,(LPUNKNOWN*)&os);
       
  1254 #else
       
  1255                     // TODO: If we store the HTML in the body property, how do we know that it is HTML on extraction?
       
  1256                     HRESULT rv = message->OpenProperty(PR_BODY, 0, STGM_WRITE, MAPI_MODIFY | MAPI_CREATE,(LPUNKNOWN*)&os);
       
  1257 #endif
       
  1258                     if (HR_SUCCEEDED(rv)) {
       
  1259                         QByteArray body(bodyContent.textContent().toLatin1());
       
  1260                         writeStream(error, os, body.data(), body.count(), true);
       
  1261 
       
  1262                         mapiRelease(os);
       
  1263                     } else {
       
  1264                         qWarning() << "Unable to write HTML to body";
       
  1265                         *error = QMessageManager::FrameworkFault;
       
  1266                     }
       
  1267 #else
       
  1268                 if ((subType == "rtf") || (subType == "html")) {
       
  1269                     QByteArray body(bodyContent.textContent().toLatin1());
       
  1270                     LONG textFormat(EDITOR_FORMAT_RTF);
       
  1271                     if (subType == "html") {
       
  1272                         // Encode the HTML within RTF
       
  1273                         TCHAR codePage[6] = _T("1252");
       
  1274                         GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTANSICODEPAGE, codePage, sizeof(codePage));
       
  1275 
       
  1276                         QByteArray format("{\\rtf1\\ansi\\ansicpg" + 
       
  1277                                           QString::fromUtf16(reinterpret_cast<const quint16*>(codePage)).toLatin1() +
       
  1278                       "\\fromhtml1 {\\*\\htmltag1 ");
       
  1279                       body = format + body + "}}";
       
  1280                         textFormat = EDITOR_FORMAT_HTML;
       
  1281                     }
       
  1282 
       
  1283                     // Mark this message as formatted
       
  1284                     if (!setMapiProperty(message, PR_MSG_EDITOR_FORMAT, textFormat)) {
       
  1285                         qWarning() << "Unable to set message editor format in message.";
       
  1286                         *error = QMessageManager::FrameworkFault;
       
  1287                     }
       
  1288 
       
  1289                     IStream *os(0);
       
  1290                     rv = message->OpenProperty(PR_RTF_COMPRESSED, &IID_IStream, STGM_CREATE | STGM_WRITE, MAPI_CREATE | MAPI_MODIFY, reinterpret_cast<LPUNKNOWN*>(&os));
       
  1291                     if (HR_SUCCEEDED(rv)) {
       
  1292                         IStream *compressor(0);
       
  1293                         rv = WrapCompressedRTFStream(os, MAPI_MODIFY, &compressor);
       
  1294                         if (HR_SUCCEEDED(rv)) {
       
  1295                             // The wrapper stream does not support Stat()
       
  1296                             writeStream(error, compressor, body.data(), body.length(), false);
       
  1297 
       
  1298                             compressor->Release();
       
  1299                         } else {
       
  1300                             qWarning() << "Unable to open RTF compressor stream for write.";
       
  1301                             *error = QMessageManager::FrameworkFault;
       
  1302                         }
       
  1303 
       
  1304                         os->Release();
       
  1305                     } else {
       
  1306                         qWarning() << "Unable to open compressed RTF stream for write.";
       
  1307                         *error = QMessageManager::FrameworkFault;
       
  1308                     }
       
  1309 #endif
       
  1310                 } else {
       
  1311                     QString body(bodyContent.textContent());
       
  1312 
       
  1313 #ifdef _WIN32_WCE
       
  1314                     IStream *os(0);
       
  1315                     HRESULT rv = message->OpenProperty(PR_BODY_W, 0, STGM_WRITE, MAPI_MODIFY | MAPI_CREATE,(LPUNKNOWN*)&os);
       
  1316                     if (HR_SUCCEEDED(rv)) {
       
  1317                         writeStream(error, os, body.utf16(), body.count() * sizeof(WCHAR), true);
       
  1318 
       
  1319                         mapiRelease(os);
       
  1320                     } else {
       
  1321                         qWarning() << "Unable to write text to body";
       
  1322                         *error = QMessageManager::FrameworkFault;
       
  1323                     }
       
  1324 #else
       
  1325                     // Mark this message as plain text
       
  1326                     LONG textFormat(EDITOR_FORMAT_PLAINTEXT);
       
  1327                     if (!setMapiProperty(message, PR_MSG_EDITOR_FORMAT, textFormat)) {
       
  1328                         qWarning() << "Unable to set message editor format in message.";
       
  1329                         *error = QMessageManager::FrameworkFault;
       
  1330                     }
       
  1331 
       
  1332                     if (!setMapiProperty(message, PR_BODY, body)) {
       
  1333                         qWarning() << "Unable to set body in message.";
       
  1334                         *error = QMessageManager::FrameworkFault;
       
  1335                     }
       
  1336 #endif
       
  1337                 }
       
  1338             }
       
  1339         } else {
       
  1340             qWarning() << "Unable to delete existing body properties from message.";
       
  1341             *error = QMessageManager::FrameworkFault;
       
  1342         }
       
  1343     }
       
  1344 
       
  1345     void addMessageAttachments(QMessageManager::Error *error, const QMessage &source, IMessage *message, WinHelpers::SavePropertyOption saveOption = WinHelpers::SavePropertyChanges )
       
  1346     {
       
  1347         Q_UNUSED(saveOption);
       
  1348 
       
  1349         QList<LONG> attachmentNumbers;
       
  1350 
       
  1351         if (MapiSession::messageImpl(source)->_hasAttachments) {
       
  1352             // Find any existing attachments
       
  1353             IMAPITable *attachmentsTable(0);
       
  1354             HRESULT rv = message->GetAttachmentTable(MAPI_UNICODE, &attachmentsTable);
       
  1355             if (HR_SUCCEEDED(rv)) {
       
  1356                 SizedSPropTagArray(1, columns) = {1, {PR_ATTACH_NUM}};
       
  1357 
       
  1358                 QueryAllRows qar(attachmentsTable, reinterpret_cast<LPSPropTagArray>(&columns), 0, 0, false);
       
  1359                 while (qar.query()) {
       
  1360                     for (uint n = 0; n < qar.rows()->cRows; ++n) {
       
  1361                         attachmentNumbers.append(qar.rows()->aRow[n].lpProps[0].Value.l);
       
  1362                     }
       
  1363                 }
       
  1364 
       
  1365                 *error = qar.error();
       
  1366                 if (*error != QMessageManager::NoError) {
       
  1367                     qWarning() << "Unable to get attachments numbers from table.";
       
  1368                 }
       
  1369                 mapiRelease(attachmentsTable);
       
  1370             } else {
       
  1371                 qWarning() << "Unable to get attachments table from message.";
       
  1372                 *error = QMessageManager::FrameworkFault;
       
  1373             }
       
  1374         }
       
  1375 
       
  1376         if (*error == QMessageManager::NoError) {
       
  1377             // Add any current attachments not present
       
  1378             foreach (const QMessageContentContainerId &attachmentId, source.attachmentIds()) {
       
  1379                 QMessageContentContainer attachment(source.find(attachmentId));
       
  1380                 bool isAttachment = (!attachment.suggestedFileName().isEmpty() && attachment.isContentAvailable());
       
  1381                 if (isAttachment) {
       
  1382                     LONG number(MapiSession::containerImpl(attachment)->_attachmentNumber);
       
  1383                     if (!number || !attachmentNumbers.contains(number)) {
       
  1384                         addAttachment(message, attachment);
       
  1385                     }
       
  1386                 }
       
  1387             }
       
  1388         }
       
  1389     }
       
  1390 
       
  1391     struct FolderHeapNode
       
  1392     {
       
  1393         FolderHeapNode(const MapiFolderPtr &folder, const QMessageFilter &filter);
       
  1394 
       
  1395         QMessageFilter _filter;
       
  1396         MapiFolderPtr _folder;
       
  1397         LPMAPITABLE _table;
       
  1398         QMessage _front;
       
  1399     };
       
  1400 
       
  1401     FolderHeapNode::FolderHeapNode(const MapiFolderPtr &folder, const QMessageFilter &filter)
       
  1402         : _filter(filter),
       
  1403           _folder(folder)
       
  1404     {
       
  1405     }
       
  1406 
       
  1407     typedef QSharedPointer<FolderHeapNode> FolderHeapNodePtr;
       
  1408 
       
  1409     // FolderHeap is a binary heap used to merge sort messages from different folders and stores
       
  1410     class FolderHeap {
       
  1411     public:
       
  1412         FolderHeap(QMessageManager::Error *error, const QList<FolderHeapNodePtr> &protoHeap, const QMessageSortOrder &sortOrder);
       
  1413         ~FolderHeap();
       
  1414 
       
  1415         QMessage takeFront(QMessageManager::Error *error);
       
  1416         bool isEmpty() const { return _heap.count() == 0; }
       
  1417 
       
  1418     private:
       
  1419         void sink(int i); // Also known as sift-down
       
  1420 
       
  1421         QMessageFilter _filter;
       
  1422         QMessageSortOrder _ordering;
       
  1423         QList<FolderHeapNodePtr> _heap;
       
  1424     };
       
  1425 
       
  1426     FolderHeap::FolderHeap(QMessageManager::Error *error, const QList<FolderHeapNodePtr> &protoHeap, const QMessageSortOrder &sortOrder)
       
  1427     {
       
  1428         _ordering = sortOrder;
       
  1429 
       
  1430         foreach (const FolderHeapNodePtr &node, protoHeap) {
       
  1431             if (!node->_folder) {
       
  1432                 qWarning() << "Unable to access folder:" << node->_folder->name();
       
  1433                 continue;
       
  1434             }
       
  1435 
       
  1436             QMessageIdList messageIdList;
       
  1437             node->_table = node->_folder->queryBegin(error, node->_filter, sortOrder);
       
  1438             if (*error == QMessageManager::NoError) {
       
  1439                 if (node->_table) {
       
  1440                     messageIdList = node->_folder->queryNext(error, node->_table, node->_filter);
       
  1441                     if (messageIdList.isEmpty() || (*error != QMessageManager::NoError)) {
       
  1442                         node->_folder->queryEnd(node->_table);
       
  1443                     }
       
  1444                 }
       
  1445 
       
  1446                 if (!messageIdList.isEmpty()) {
       
  1447                     node->_front = node->_folder->message(error, messageIdList.front());
       
  1448 
       
  1449                     if (*error == QMessageManager::NoError) {
       
  1450                         _heap.append(node);
       
  1451                     }
       
  1452                 }
       
  1453             }
       
  1454         }
       
  1455 
       
  1456         if (!_ordering.isEmpty()) {
       
  1457             for (int i = _heap.count()/2 - 1; i >= 0; --i)
       
  1458                 sink(i);
       
  1459         }
       
  1460     }
       
  1461 
       
  1462     FolderHeap::~FolderHeap()
       
  1463     {
       
  1464     }
       
  1465 
       
  1466     QMessage FolderHeap::takeFront(QMessageManager::Error *error)
       
  1467     {
       
  1468         QMessage result(_heap[0]->_front);
       
  1469 
       
  1470         FolderHeapNodePtr node(_heap[0]);
       
  1471 
       
  1472         QMessageIdList messageIdList;
       
  1473         if (node->_table) {
       
  1474             messageIdList = node->_folder->queryNext(error, node->_table, node->_filter);
       
  1475             if (messageIdList.isEmpty() || (*error != QMessageManager::NoError)) {
       
  1476                 node->_folder->queryEnd(node->_table);
       
  1477             }
       
  1478         }
       
  1479         if (*error != QMessageManager::NoError)
       
  1480             return result;
       
  1481 
       
  1482         if (messageIdList.isEmpty()) {
       
  1483             if (_heap.count() > 1) {
       
  1484                 _heap[0] = _heap.takeLast();
       
  1485             } else {
       
  1486                 _heap.pop_back();
       
  1487             }
       
  1488         } else {
       
  1489             node->_front = node->_folder->message(error, messageIdList.front());
       
  1490             if (*error != QMessageManager::NoError)
       
  1491                 return result;
       
  1492         }
       
  1493 
       
  1494         if (!_ordering.isEmpty()) {
       
  1495             // Reposition this folder in the heap based on the new front message
       
  1496             sink(0);
       
  1497         }
       
  1498         return result;
       
  1499     }
       
  1500 
       
  1501     void FolderHeap::sink(int i)
       
  1502     {
       
  1503         while (true) {
       
  1504             const int leftChild(2*i + 1);
       
  1505             if (leftChild >= _heap.count())
       
  1506                 return;
       
  1507 
       
  1508             const int rightChild(leftChild + 1);
       
  1509             int minChild(leftChild);
       
  1510             if ((rightChild < _heap.count()) && (QMessageSortOrderPrivate::lessThan(_ordering, _heap[rightChild]->_front, _heap[leftChild]->_front)))
       
  1511                 minChild = rightChild;
       
  1512 
       
  1513             // Reverse positions only if the child sorts before the parent
       
  1514             if (QMessageSortOrderPrivate::lessThan(_ordering, _heap[minChild]->_front, _heap[i]->_front)) {
       
  1515                 FolderHeapNodePtr temp(_heap[minChild]);
       
  1516                 _heap[minChild] = _heap[i];
       
  1517                 _heap[i] = temp;
       
  1518                 i = minChild;
       
  1519             } else {
       
  1520                 return;
       
  1521             }
       
  1522         }
       
  1523     }
       
  1524 
       
  1525     bool bodyMatches(const QMessage &message, const QString &body, QMessageDataComparator::MatchFlags matchFlags)
       
  1526     {
       
  1527         if (body.isEmpty())
       
  1528             return true;
       
  1529 
       
  1530         QMessageContentContainer bodyContainer(message.find(message.bodyId()));
       
  1531         if (matchFlags & QMessageDataComparator::MatchCaseSensitive) {
       
  1532             return bodyContainer.textContent().contains(body, Qt::CaseSensitive);
       
  1533         }
       
  1534         return bodyContainer.textContent().contains(body, Qt::CaseInsensitive);
       
  1535     }
       
  1536 
       
  1537     QMessageIdList filterMessages(QMessageManager::Error *error, QList<FolderHeapNodePtr> &folderNodes, const QMessageSortOrder &sortOrder, uint limit, uint offset, const QString &body = QString(), QMessageDataComparator::MatchFlags matchFlags = 0)
       
  1538     {
       
  1539         QMessageIdList result;
       
  1540         QHash<QMessageId, bool> avoidDuplicates; // For complex filters it's necessary to check for duplicates
       
  1541 
       
  1542         FolderHeap folderHeap(error, folderNodes, sortOrder);
       
  1543         if (*error != QMessageManager::NoError)
       
  1544             return result;
       
  1545 
       
  1546         int count = 0 - offset;
       
  1547         while (!folderHeap.isEmpty()) {
       
  1548             // Ideally would not retrieve unwanted messages using IMAPITable::SeekRow
       
  1549             // But this is not feasible on Windows Mobile http://msdn.microsoft.com/en-us/library/bb446068.aspx:
       
  1550             // 'If there are large numbers of rows in the table, the SeekRow oepration can be slow. Do not set lRowCount ot a numer greater than 50'
       
  1551             if (limit && (count == limit))
       
  1552                 break;
       
  1553 
       
  1554             QMessage front(folderHeap.takeFront(error));
       
  1555             if (*error != QMessageManager::NoError)
       
  1556                 return result;
       
  1557 
       
  1558             if (!avoidDuplicates.contains(front.id()) && bodyMatches(front, body, matchFlags)) {
       
  1559                 avoidDuplicates.insert(front.id(), true);
       
  1560                 if (count >= 0) {
       
  1561                     result.append(front.id());
       
  1562                 }
       
  1563 
       
  1564                 ++count;
       
  1565             }
       
  1566         }
       
  1567 
       
  1568         if (offset) {
       
  1569             return result.mid(offset, (limit ? limit : -1));
       
  1570         } else {
       
  1571             return result;
       
  1572         }
       
  1573     }
       
  1574 
       
  1575 }
       
  1576 
       
  1577     QEvent::Type MapiSession::NotifyEvent::eventType()
       
  1578     {
       
  1579         static int result = QEvent::registerEventType();
       
  1580         return static_cast<QEvent::Type>(result);
       
  1581     }
       
  1582 
       
  1583     MapiSession::NotifyEvent::NotifyEvent(MapiStore *store, const QMessageId &id, MapiSession::NotifyType type)
       
  1584         : QEvent(eventType()),
       
  1585           _store(store),
       
  1586           _id(id),
       
  1587           _notifyType(type)
       
  1588     {
       
  1589     }
       
  1590 
       
  1591     QEvent::Type MapiSession::NotifyEvent::type()
       
  1592     {
       
  1593         return eventType();
       
  1594     }
       
  1595 
       
  1596 namespace WinHelpers {
       
  1597 
       
  1598     // Note: UNICODE is always defined
       
  1599     QString QStringFromLpctstr(LPCTSTR lpszValue)
       
  1600     {
       
  1601         bool isBadStringPointer = false;
       
  1602 #ifndef _WIN32_WCE
       
  1603         isBadStringPointer = ::IsBadStringPtr(lpszValue, (UINT_PTR)-1); // Don't crash when MAPI returns a bad string (and it does).
       
  1604 #endif
       
  1605 
       
  1606         if (!lpszValue || isBadStringPointer)
       
  1607             return QString();
       
  1608 
       
  1609         return QString::fromUtf16(reinterpret_cast<const quint16*>(lpszValue));
       
  1610     }
       
  1611 
       
  1612     Lptstr LptstrFromQString(const QString &src)
       
  1613     {
       
  1614         uint length(src.length());
       
  1615         Lptstr dst(length+1);
       
  1616 
       
  1617         const quint16 *data = src.utf16();
       
  1618         const quint16 *it = data, *end = data + length;
       
  1619         TCHAR *oit = dst;
       
  1620         for ( ; it != end; ++it, ++oit) {
       
  1621             *oit = static_cast<TCHAR>(*it);
       
  1622         }
       
  1623         *oit = TCHAR('\0');
       
  1624         return dst;
       
  1625     }
       
  1626 
       
  1627     ULONG createNamedProperty(IMAPIProp *object, const QString &name)
       
  1628     {
       
  1629         ULONG result = 0;
       
  1630 
       
  1631         if (!name.isEmpty()) {
       
  1632             Lptstr nameBuffer = LptstrFromQString(name);
       
  1633 
       
  1634             MAPINAMEID propName = { 0 };
       
  1635             propName.lpguid = &GuidPublicStrings;
       
  1636             propName.ulKind = MNID_STRING;
       
  1637             propName.Kind.lpwstrName = nameBuffer;
       
  1638 
       
  1639             LPMAPINAMEID propNames = &propName;
       
  1640 
       
  1641             SPropTagArray *props;
       
  1642             HRESULT rv = object->GetIDsFromNames(1, &propNames, MAPI_CREATE, &props);
       
  1643             if (HR_SUCCEEDED(rv)) {
       
  1644                 result = props->aulPropTag[0] | PT_UNICODE;
       
  1645 
       
  1646                 MAPIFreeBuffer(props);
       
  1647             } else {
       
  1648                 qWarning() << "createNamedProperty: GetIDsFromNames failed";
       
  1649             }
       
  1650         }
       
  1651 
       
  1652         return result;
       
  1653     }
       
  1654 
       
  1655     ULONG getNamedPropertyTag(IMAPIProp *object, const QString &name)
       
  1656     {
       
  1657         ULONG result = 0;
       
  1658 
       
  1659         if (!name.isEmpty()) {
       
  1660             LPTSTR nameBuffer = LptstrFromQString(name);
       
  1661 
       
  1662             MAPINAMEID propName = { 0 };
       
  1663             propName.lpguid = &GuidPublicStrings;
       
  1664             propName.ulKind = MNID_STRING;
       
  1665             propName.Kind.lpwstrName = nameBuffer;
       
  1666 
       
  1667             LPMAPINAMEID propNames = &propName;
       
  1668 
       
  1669             SPropTagArray *props;
       
  1670             HRESULT rv = object->GetIDsFromNames(1, &propNames, 0, &props);
       
  1671             if (HR_SUCCEEDED(rv)) {
       
  1672                 if (props->aulPropTag[0] != PT_ERROR) {
       
  1673                     result = props->aulPropTag[0] | PT_UNICODE;
       
  1674                 }
       
  1675 
       
  1676                 MAPIFreeBuffer(props);
       
  1677             } else {
       
  1678                 qWarning() << "getNamedPropertyTag: GetIDsFromNames failed";
       
  1679             }
       
  1680         }
       
  1681 
       
  1682         return result;
       
  1683     }
       
  1684 
       
  1685     bool setNamedProperty(IMAPIProp *object, ULONG tag, const QString &value)
       
  1686     {
       
  1687         if (object && tag && !value.isEmpty()) {
       
  1688             SPropValue prop = { 0 };
       
  1689             prop.ulPropTag = tag;
       
  1690             prop.Value.LPSZ = reinterpret_cast<LPTSTR>(const_cast<quint16*>(value.utf16()));
       
  1691 
       
  1692             HRESULT rv = object->SetProps(1, &prop, 0);
       
  1693             if (HR_SUCCEEDED(rv)) {
       
  1694                 return true;
       
  1695             } else {
       
  1696                 qWarning() << "setNamedProperty: SetProps failed";
       
  1697             }
       
  1698         }
       
  1699 
       
  1700         return false;
       
  1701     }
       
  1702 
       
  1703     QString getNamedProperty(IMAPIProp *object, ULONG tag)
       
  1704     {
       
  1705         QString result;
       
  1706 
       
  1707         if (object && tag) {
       
  1708             SPropValue *prop(0);
       
  1709             HRESULT rv = HrGetOneProp(object, tag, &prop);
       
  1710             if (HR_SUCCEEDED(rv)) {
       
  1711                 result = QStringFromLpctstr(prop->Value.LPSZ);
       
  1712 
       
  1713                 MAPIFreeBuffer(prop);
       
  1714             } else if (rv != MAPI_E_NOT_FOUND) {
       
  1715                 qWarning() << "getNamedProperty: HrGetOneProp failed";
       
  1716             }
       
  1717         }
       
  1718 
       
  1719         return result;
       
  1720     }
       
  1721 
       
  1722     QByteArray contentTypeFromExtension(const QString &extension)
       
  1723     {
       
  1724         QByteArray result("application/octet-stream");
       
  1725 
       
  1726         if (!extension.isEmpty()) {
       
  1727             // Create the extension string to search for
       
  1728             QString dotExtension(extension);
       
  1729             if (!dotExtension.startsWith('.')) {
       
  1730                 dotExtension.prepend('.');
       
  1731             }
       
  1732 
       
  1733             Lptstr ext = LptstrFromQString(dotExtension);
       
  1734 
       
  1735 #if !defined(_WIN32_WCE) && (_MSC_VER>=1500)
       
  1736             IQueryAssociations *associations(0);
       
  1737             HRESULT rv = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARGS(&associations));
       
  1738             if (HR_SUCCEEDED(rv)) {
       
  1739                 rv = associations->Init(0, ext, 0, 0);
       
  1740                 if (HR_SUCCEEDED(rv)) {
       
  1741                     // Find the length of the content-type string
       
  1742                     DWORD length = 0;
       
  1743                     rv = associations->GetString(0, ASSOCSTR_CONTENTTYPE, 0, 0, &length);
       
  1744                     if ((rv == S_FALSE) && length) {
       
  1745                         // Retrieve the string
       
  1746                         wchar_t *buffer = new wchar_t[length + 1];
       
  1747                         buffer[length] = '\0';
       
  1748                         rv = associations->GetString(0, ASSOCSTR_CONTENTTYPE, 0, buffer, &length);
       
  1749                         if (rv == S_OK) {
       
  1750                             QString output(QString::fromUtf16(reinterpret_cast<quint16*>(buffer)));
       
  1751                             result = output.toLatin1();
       
  1752                         }
       
  1753 
       
  1754                         delete [] buffer;
       
  1755                     }
       
  1756                 }
       
  1757                 mapiRelease(associations);
       
  1758             }
       
  1759 #elif  !defined(_WIN32_WCE) && (_MSC_VER<1500)
       
  1760             //TODO Windows 2005 and 2003 don't have IID_PPV_ARGS.
       
  1761             //find alternative
       
  1762 #else
       
  1763             // Find any registry entry for this extension
       
  1764             HKEY key = { 0 };
       
  1765             LONG rv = RegOpenKeyEx(HKEY_CLASSES_ROOT, ext, 0, 0, &key);
       
  1766             if (rv == ERROR_SUCCESS) {
       
  1767                 WCHAR value[512] = { 0 };
       
  1768                 DWORD valueBytes = sizeof(value);
       
  1769                 rv = RegQueryValueEx(key, L"Content Type", 0, 0, reinterpret_cast<LPBYTE>(&value), &valueBytes);
       
  1770                 if (rv == ERROR_SUCCESS) {
       
  1771                     if (valueBytes > 1) {
       
  1772                         result = QStringFromLpctstr(value).toLatin1();
       
  1773                     }
       
  1774                 } else {
       
  1775                     qWarning() << "Unable to query key for extension:" << extension;
       
  1776                 }
       
  1777 
       
  1778                 RegCloseKey(key);
       
  1779             }
       
  1780 #endif
       
  1781         }
       
  1782 
       
  1783         return result;
       
  1784     }
       
  1785 
       
  1786     MapiInitializer::MapiInitializer()
       
  1787         : _initialized(false)
       
  1788     {
       
  1789 #ifndef QT_NO_THREAD
       
  1790         // Note MAPIINIT is ignored on Windows Mobile but used on Outlook 2007 see msdn ms862621 vs cc842343
       
  1791         MAPIINIT_0 MAPIINIT = { 0, MAPI_MULTITHREAD_NOTIFICATIONS };
       
  1792         LPVOID arg(&MAPIINIT);
       
  1793 #else
       
  1794         LPVOID arg(0);
       
  1795 #endif
       
  1796         HRESULT rv = MAPIInitialize(arg);
       
  1797         if (HR_SUCCEEDED(rv)) {
       
  1798             _initialized = true;
       
  1799         } else {
       
  1800             _initialized = false;
       
  1801             qWarning() << "Unable to initialize MAPI - rv:" << hex << (ULONG)rv;
       
  1802         }
       
  1803     }
       
  1804 
       
  1805     MapiInitializer::~MapiInitializer()
       
  1806     {
       
  1807         if (_initialized) {
       
  1808             MAPIUninitialize();
       
  1809             _initialized = false;
       
  1810         }
       
  1811     }
       
  1812 
       
  1813     MapiInitializationToken initializeMapi()
       
  1814     {
       
  1815         MapiInitializationToken result;
       
  1816 
       
  1817         if (!initializer.hasLocalData()) {
       
  1818             initializer.setLocalData(new InitRecord);
       
  1819         }
       
  1820 
       
  1821         InitRecordPtr &threadInitializer(initializer.localData());
       
  1822         if (!(*threadInitializer).isNull()) {
       
  1823             result = (*threadInitializer).toStrongRef();
       
  1824         } else {
       
  1825             result = MapiInitializationToken(new MapiInitializer());
       
  1826             (*threadInitializer) = result;
       
  1827         }
       
  1828 
       
  1829         return result;
       
  1830     }
       
  1831 
       
  1832     class StoreSortHelper {
       
  1833     public:
       
  1834         StoreSortHelper(const QMessageAccountSortOrder *sortOrder, MapiStorePtr storePtr)
       
  1835             :_ordering(sortOrder),
       
  1836              _storePtr(storePtr)
       
  1837         {}
       
  1838 
       
  1839         MapiStorePtr store()
       
  1840         {
       
  1841             return _storePtr;
       
  1842         }
       
  1843 
       
  1844         StoreSortHelper& operator=(const StoreSortHelper &other) {
       
  1845             if (&other == this)
       
  1846                 return *this;
       
  1847             _ordering = other._ordering;
       
  1848             _storePtr = other._storePtr;
       
  1849             return *this;
       
  1850         }
       
  1851 
       
  1852         bool operator<(const StoreSortHelper &other) const
       
  1853         {
       
  1854             bool result (_storePtr->name() < other._storePtr->name());
       
  1855             if (QMessageAccountSortOrderPrivate::order(*_ordering) == Qt::DescendingOrder)
       
  1856                 result = !result;
       
  1857             return result;
       
  1858         }
       
  1859     private:
       
  1860         const QMessageAccountSortOrder *_ordering;
       
  1861         MapiStorePtr _storePtr;
       
  1862     };
       
  1863 
       
  1864     class FolderSortHelper {
       
  1865     public:
       
  1866         FolderSortHelper(const QMessageFolderSortOrder *sortOrder, MapiFolderPtr folderPtr, const QMessageFolder &folder)
       
  1867             :_ordering(sortOrder),
       
  1868              _folderPtr(folderPtr),
       
  1869              _folder(folder)
       
  1870         {}
       
  1871 
       
  1872         MapiFolderPtr mapiFolderPtr()
       
  1873         {
       
  1874             return _folderPtr;
       
  1875         }
       
  1876 
       
  1877         QMessageFolder folder()
       
  1878         {
       
  1879             return _folder;
       
  1880         }
       
  1881 
       
  1882         FolderSortHelper& operator=(const FolderSortHelper &other) {
       
  1883             if (&other == this)
       
  1884                 return *this;
       
  1885             _ordering = other._ordering;
       
  1886             _folderPtr = other._folderPtr;
       
  1887             _folder = other._folder;
       
  1888             return *this;
       
  1889         }
       
  1890 
       
  1891         bool operator<(const FolderSortHelper &other) const
       
  1892         {
       
  1893             return QMessageFolderSortOrderPrivate::lessthan(*_ordering, _folder, other._folder);
       
  1894         }
       
  1895     private:
       
  1896         const QMessageFolderSortOrder *_ordering;
       
  1897         MapiFolderPtr _folderPtr;
       
  1898         QMessageFolder _folder;
       
  1899     };
       
  1900 }
       
  1901 
       
  1902 using namespace WinHelpers;
       
  1903 
       
  1904 MapiFolder::MapiFolder()
       
  1905     :_valid(false),
       
  1906      _folder(0),
       
  1907      _hasSubFolders(false),
       
  1908      _messageCount(0),
       
  1909      _init(false)
       
  1910 {
       
  1911 }
       
  1912 
       
  1913 MapiFolderPtr MapiFolder::createFolder(QMessageManager::Error *error, const MapiStorePtr &store, IMAPIFolder *folder, const MapiRecordKey &recordKey, const QString &name, const MapiEntryId &entryId, bool hasSubFolders, uint messageCount)
       
  1914 {
       
  1915     Q_UNUSED(error);
       
  1916     MapiFolderPtr ptr = MapiFolderPtr(new MapiFolder(store, folder, recordKey, name, entryId, hasSubFolders, messageCount));
       
  1917     if (!ptr.isNull()) {
       
  1918         ptr->_self = ptr;
       
  1919     }
       
  1920     return ptr;
       
  1921 }
       
  1922 
       
  1923 MapiFolder::MapiFolder(const MapiStorePtr &store, IMAPIFolder *folder, const MapiRecordKey &recordKey, const QString &name, const MapiEntryId &entryId, bool hasSubFolders, uint messageCount)
       
  1924     :_store(store),
       
  1925      _valid(folder != 0),
       
  1926      _folder(folder),
       
  1927      _key(recordKey),
       
  1928      _name(name),
       
  1929      _entryId(entryId),
       
  1930      _hasSubFolders(hasSubFolders),
       
  1931      _messageCount(messageCount),
       
  1932      _init(false)
       
  1933 {
       
  1934 }
       
  1935 
       
  1936 MapiFolder::~MapiFolder()
       
  1937 {
       
  1938     mapiRelease(_folder);
       
  1939     _valid = false;
       
  1940     _init = false;
       
  1941 }
       
  1942 
       
  1943 void MapiFolder::findSubFolders(QMessageManager::Error *error)
       
  1944 {
       
  1945     if (!_folder) {
       
  1946         *error = QMessageManager::FrameworkFault;
       
  1947         qWarning() << "No folder to search for subfolders";
       
  1948     } else if (_hasSubFolders) {
       
  1949         IMAPITable *subFolders(0);
       
  1950         HRESULT rv = _folder->GetHierarchyTable(0, &subFolders);
       
  1951         if (HR_SUCCEEDED(rv)) {
       
  1952             SizedSPropTagArray(5, columns) = {5, {PR_ENTRYID, PR_IS_NEWSGROUP, PR_IS_NEWSGROUP_ANCHOR, PR_EXTENDED_FOLDER_FLAGS, PR_FOLDER_TYPE}};
       
  1953             rv = subFolders->SetColumns(reinterpret_cast<LPSPropTagArray>(&columns), 0);
       
  1954             if (HR_SUCCEEDED(rv)) {
       
  1955                 // Extract the Entry IDs for the subfolders
       
  1956                 while (true) {
       
  1957                     LPSRowSet rows(0);
       
  1958                     if (subFolders->QueryRows(1, 0, &rows) == S_OK) {
       
  1959                         if (rows->cRows == 1) {
       
  1960                             SRow *row(&rows->aRow[0]);
       
  1961 
       
  1962                             MapiEntryId entryId(row->lpProps[0].Value.bin.lpb, row->lpProps[0].Value.bin.cb);
       
  1963                             bool isNewsGroup = (row->lpProps[1].ulPropTag == PR_IS_NEWSGROUP && row->lpProps[1].Value.b);
       
  1964                             bool isNewsGroupAnchor = (row->lpProps[2].ulPropTag == PR_IS_NEWSGROUP_ANCHOR && row->lpProps[2].Value.b);
       
  1965 
       
  1966                             bool special(isNewsGroup || isNewsGroupAnchor);
       
  1967 #ifndef _WIN32_WCE
       
  1968                             // Skip slow folders, necessary evil
       
  1969                             if (row->lpProps[3].ulPropTag == PR_EXTENDED_FOLDER_FLAGS) {
       
  1970                                 QByteArray extendedFlags(reinterpret_cast<const char*>(row->lpProps[3].Value.bin.lpb), row->lpProps[3].Value.bin.cb);
       
  1971                                 if (extendedFlags[2] & 8) { // Synchronization issues, skip like Outlook
       
  1972                                     special = true;
       
  1973                                 }
       
  1974                             } else if (row->lpProps[4].ulPropTag == PR_FOLDER_TYPE) {
       
  1975                                 if (row->lpProps[4].Value.ul != FOLDER_GENERIC) {
       
  1976                                     special = true;
       
  1977                                 }
       
  1978                             } else {
       
  1979                                 special = true;
       
  1980                             }
       
  1981 #endif
       
  1982                             FreeProws(rows);
       
  1983 
       
  1984                             if (special) {
       
  1985                                 // Doesn't contain messages that should be searched...
       
  1986                             }  else {
       
  1987                                 _subFolders.append(entryId);
       
  1988                             }
       
  1989                         } else {
       
  1990                             // We have retrieved all rows
       
  1991                             FreeProws(rows);
       
  1992                             break;
       
  1993                         }
       
  1994                     } else {
       
  1995                         *error = QMessageManager::ContentInaccessible;
       
  1996                         qWarning() << "Error getting rows from sub folder table.";
       
  1997                         break;
       
  1998                     }
       
  1999                 }
       
  2000             } else {
       
  2001                 *error = QMessageManager::ContentInaccessible;
       
  2002                 qWarning() << "Unable to set columns on folder table.";
       
  2003             }
       
  2004 
       
  2005             mapiRelease(subFolders);
       
  2006         } else {
       
  2007             *error = QMessageManager::ContentInaccessible;
       
  2008             qWarning() << "Unable to get hierarchy table for folder:" << name();
       
  2009         }
       
  2010     }
       
  2011 }
       
  2012 
       
  2013 MapiFolderPtr MapiFolder::nextSubFolder(QMessageManager::Error *error)
       
  2014 {
       
  2015     MapiFolderPtr result;
       
  2016 
       
  2017     if (!_hasSubFolders)
       
  2018         return result;
       
  2019 
       
  2020     if (!_init) {
       
  2021         _init = true;
       
  2022 
       
  2023         findSubFolders(error);
       
  2024         if (*error != QMessageManager::NoError) {
       
  2025             qWarning() << "Unable to find sub folders.";
       
  2026             return result;
       
  2027         }
       
  2028     }
       
  2029 
       
  2030     if (!_subFolders.isEmpty()) {
       
  2031         result = _store->openFolder(error, _subFolders.takeFirst());
       
  2032     } else {
       
  2033         // Allow the traversal to be restarted
       
  2034         _init = false;
       
  2035     }
       
  2036 
       
  2037     return result;
       
  2038 }
       
  2039 
       
  2040 LPMAPITABLE MapiFolder::queryBegin(QMessageManager::Error *error, const QMessageFilter &filter, const QMessageSortOrder &sortOrder)
       
  2041 {
       
  2042     if (!_valid || !_folder) {
       
  2043         Q_ASSERT(_valid && _folder);
       
  2044         *error = QMessageManager::FrameworkFault;
       
  2045         return 0;
       
  2046     }
       
  2047 
       
  2048     MapiRestriction restriction(filter);
       
  2049     if (!restriction.isValid()) {
       
  2050         *error = QMessageManager::ConstraintFailure;
       
  2051         return 0;
       
  2052     }
       
  2053 
       
  2054     LPMAPITABLE messagesTable(0);
       
  2055     HRESULT rv = _folder->GetContentsTable(MAPI_UNICODE, &messagesTable);
       
  2056     if (HR_SUCCEEDED(rv)) {
       
  2057         SizedSPropTagArray(2, columns) = {2, {PR_ENTRYID, PR_RECORD_KEY}};
       
  2058         rv = messagesTable->SetColumns(reinterpret_cast<LPSPropTagArray>(&columns), 0);
       
  2059         if (HR_SUCCEEDED(rv)) {
       
  2060             if (!sortOrder.isEmpty()) {
       
  2061                 QMessageSortOrderPrivate::sortTable(error, sortOrder, messagesTable);
       
  2062             }
       
  2063             if (!restriction.isEmpty()) {
       
  2064                 ULONG flags(0);
       
  2065                 if (messagesTable->Restrict(restriction.sRestriction(), flags) != S_OK)
       
  2066                     *error = QMessageManager::ConstraintFailure;
       
  2067             }
       
  2068             if (*error != QMessageManager::NoError) {
       
  2069                 return 0;
       
  2070             }
       
  2071             return messagesTable;
       
  2072         } else {
       
  2073             *error = QMessageManager::ContentInaccessible;
       
  2074             return 0;
       
  2075         }
       
  2076 
       
  2077         mapiRelease(messagesTable);
       
  2078         messagesTable = 0;
       
  2079     } else {
       
  2080         *error = QMessageManager::ContentInaccessible;
       
  2081         return 0;
       
  2082     }
       
  2083 
       
  2084     return 0;
       
  2085 }
       
  2086 
       
  2087 QMessageIdList MapiFolder::queryNext(QMessageManager::Error *error, LPMAPITABLE messagesTable, const QMessageFilter &filter)
       
  2088 {
       
  2089     QMessageIdList result;
       
  2090     while (true) {
       
  2091         LPSRowSet rows(0);
       
  2092         HRESULT rv = messagesTable->QueryRows(1, 0, &rows);
       
  2093         if (HR_SUCCEEDED(rv)) {
       
  2094             if (rows->cRows == 1) {
       
  2095                 LPSPropValue entryIdProp(&rows->aRow[0].lpProps[0]);
       
  2096                 LPSPropValue recordKeyProp(&rows->aRow[0].lpProps[1]);
       
  2097                 MapiRecordKey recordKey(recordKeyProp->Value.bin.lpb, recordKeyProp->Value.bin.cb);
       
  2098                 MapiEntryId entryId(entryIdProp->Value.bin.lpb, entryIdProp->Value.bin.cb);
       
  2099         #ifdef _WIN32_WCE
       
  2100                 QMessageId id(QMessageIdPrivate::from(_store->entryId(), entryId, recordKey, _entryId));
       
  2101         #else
       
  2102                 QMessageId id(QMessageIdPrivate::from(_store->recordKey(), entryId, recordKey, _key));
       
  2103         #endif
       
  2104                 FreeProws(rows);
       
  2105 
       
  2106                 if (QMessageFilterPrivate::matchesMessageRequired(filter)
       
  2107                     && !QMessageFilterPrivate::matchesMessageSimple(filter, QMessage(id)))
       
  2108                     continue;
       
  2109                 result.append(id);
       
  2110                 return result;
       
  2111             } else {
       
  2112                 // We have retrieved all rows
       
  2113                 FreeProws(rows);
       
  2114                 return result;
       
  2115             }
       
  2116         } else {
       
  2117             *error = QMessageManager::ContentInaccessible;
       
  2118             qWarning() << "Unable to query rows in message table.";
       
  2119             return result;
       
  2120         }
       
  2121     }
       
  2122 }
       
  2123 
       
  2124 void MapiFolder::queryEnd(LPMAPITABLE messagesTable)
       
  2125 {
       
  2126     mapiRelease(messagesTable);
       
  2127 }
       
  2128 
       
  2129 uint MapiFolder::countMessages(QMessageManager::Error *error, const QMessageFilter &filter) const
       
  2130 {
       
  2131     uint result(0);
       
  2132 
       
  2133     if (!_valid || !_folder) {
       
  2134         Q_ASSERT(_valid && _folder);
       
  2135         *error = QMessageManager::FrameworkFault;
       
  2136         return result;
       
  2137     }
       
  2138 
       
  2139     MapiRestriction restriction(filter);
       
  2140     if (!restriction.isValid()) {
       
  2141         *error = QMessageManager::ConstraintFailure;
       
  2142         return result;
       
  2143     }
       
  2144 
       
  2145     IMAPITable *messagesTable(0);
       
  2146     HRESULT rv = _folder->GetContentsTable(MAPI_UNICODE, &messagesTable);
       
  2147     if (HR_SUCCEEDED(rv)) {
       
  2148         if (!restriction.isEmpty()) {
       
  2149             ULONG flags(0);
       
  2150             rv = messagesTable->Restrict(restriction.sRestriction(), flags);
       
  2151             if (HR_FAILED(rv)) {
       
  2152                 *error = QMessageManager::ConstraintFailure;
       
  2153                 qWarning() << "Unable to set restriction on message table.";
       
  2154             }
       
  2155         }
       
  2156         if (*error == QMessageManager::NoError) {
       
  2157             ULONG count(0);
       
  2158             rv = messagesTable->GetRowCount(0, &count);
       
  2159             if (HR_SUCCEEDED(rv)) {
       
  2160                 result = count;
       
  2161             } else {
       
  2162                 qWarning() << "Unable to count messages in folder.";
       
  2163             }
       
  2164         }
       
  2165 
       
  2166         mapiRelease(messagesTable);
       
  2167     } else {
       
  2168         *error = QMessageManager::ContentInaccessible;
       
  2169         qWarning() << "Unable to get folder contents table.";
       
  2170     }
       
  2171 
       
  2172     return result;
       
  2173 }
       
  2174 
       
  2175 void MapiFolder::removeMessages(QMessageManager::Error *error, const QMessageIdList &ids)
       
  2176 {
       
  2177     SBinary *bin(0);
       
  2178     if (MAPIAllocateBuffer(ids.count() * sizeof(SBinary), reinterpret_cast<LPVOID*>(&bin)) != S_OK) {
       
  2179         return;
       
  2180     }
       
  2181 
       
  2182     int index = 0;
       
  2183     foreach (const QMessageId &id, ids) {
       
  2184         MapiEntryId entryId(QMessageIdPrivate::entryId(id));
       
  2185 
       
  2186         bin[index].cb = entryId.count();
       
  2187         if (MAPIAllocateMore(bin[index].cb, bin, reinterpret_cast<LPVOID*>(&bin[index].lpb)) != S_OK) {
       
  2188             break;
       
  2189         }
       
  2190 
       
  2191         memcpy(bin[index].lpb, entryId.constData(), bin[index].cb);
       
  2192         ++index;
       
  2193     }
       
  2194 
       
  2195     if (index > 0) {
       
  2196         ENTRYLIST entryList = { 0 };
       
  2197         entryList.cValues = index;
       
  2198         entryList.lpbin = bin;
       
  2199 
       
  2200         ULONG flags(0);
       
  2201         //flags |= DELETE_HARD_DELETE;  this flag is only available when the store is exchange...
       
  2202         HRESULT rv = _folder->DeleteMessages(&entryList, 0, 0, flags);
       
  2203         if (HR_FAILED(rv)) {
       
  2204             *error = QMessageManager::ContentInaccessible;
       
  2205             qWarning() << "Unable to delete messages from folder.";
       
  2206         }
       
  2207     }
       
  2208 
       
  2209     MAPIFreeBuffer(bin);
       
  2210 }
       
  2211 
       
  2212 MapiEntryId MapiFolder::messageEntryId(QMessageManager::Error *error, const MapiRecordKey &messageKey)
       
  2213 {
       
  2214     MapiEntryId result;
       
  2215     MapiRecordKey key(messageKey);
       
  2216 
       
  2217     IMAPITable *messagesTable(0);
       
  2218     HRESULT rv = _folder->GetContentsTable(MAPI_UNICODE, &messagesTable);
       
  2219     if (HR_SUCCEEDED(rv)) {
       
  2220         SPropValue keyProp;
       
  2221         keyProp.ulPropTag = PR_RECORD_KEY;
       
  2222         keyProp.Value.bin.cb = key.count();
       
  2223         keyProp.Value.bin.lpb = reinterpret_cast<LPBYTE>(key.data());
       
  2224 
       
  2225         SRestriction restriction;
       
  2226         restriction.rt = RES_PROPERTY;
       
  2227         restriction.res.resProperty.relop = RELOP_EQ;
       
  2228         restriction.res.resProperty.ulPropTag = PR_RECORD_KEY;
       
  2229         restriction.res.resProperty.lpProp = &keyProp;
       
  2230 
       
  2231         ULONG flags(0);
       
  2232         rv = messagesTable->Restrict(&restriction, flags);
       
  2233         if (HR_SUCCEEDED(rv)) {
       
  2234             SizedSPropTagArray(1, columns) = {1, {PR_ENTRYID}};
       
  2235             rv = messagesTable->SetColumns(reinterpret_cast<LPSPropTagArray>(&columns), 0);
       
  2236             if (HR_SUCCEEDED(rv)) {
       
  2237                 LPSRowSet rows(0);
       
  2238                 rv = messagesTable->QueryRows(1, 0, &rows);
       
  2239                 if (HR_SUCCEEDED(rv)) {
       
  2240                     if (rows->cRows == 1) {
       
  2241                         LPSPropValue entryIdProp(&rows->aRow[0].lpProps[0]);
       
  2242                         result = MapiEntryId(entryIdProp->Value.bin.lpb, entryIdProp->Value.bin.cb);
       
  2243                     } else {
       
  2244                         *error = QMessageManager::InvalidId;
       
  2245                     }
       
  2246                     FreeProws(rows);
       
  2247                 } else {
       
  2248                     *error = QMessageManager::ContentInaccessible;
       
  2249                     qWarning() << "Unable to query rows from message table.";
       
  2250                 }
       
  2251             } else {
       
  2252                 *error = QMessageManager::ContentInaccessible;
       
  2253                 qWarning() << "Unable to set columns on message table.";
       
  2254             }
       
  2255         } else {
       
  2256             *error = QMessageManager::ContentInaccessible;
       
  2257             qWarning() << "Unable to restrict content table.";
       
  2258         }
       
  2259 
       
  2260         mapiRelease(messagesTable);
       
  2261     } else {
       
  2262         *error = QMessageManager::ContentInaccessible;
       
  2263         qWarning() << "Unable to get contents table for folder.";
       
  2264     }
       
  2265 
       
  2266     return result;
       
  2267 }
       
  2268 
       
  2269 IMessage *MapiFolder::openMessage(QMessageManager::Error *error, const MapiEntryId &entryId)
       
  2270 {
       
  2271     IMessage *message(0);
       
  2272 
       
  2273     LPENTRYID entryIdPtr(reinterpret_cast<LPENTRYID>(const_cast<char*>(entryId.data())));
       
  2274     ULONG objectType(0);
       
  2275     HRESULT rv = _folder->OpenEntry(entryId.count(), entryIdPtr, 0, MAPI_BEST_ACCESS, &objectType, reinterpret_cast<LPUNKNOWN*>(&message));
       
  2276     if (rv != S_OK) {
       
  2277         *error = QMessageManager::InvalidId;
       
  2278         qWarning() << "Invalid message entryId:" << entryId.toBase64();
       
  2279     }
       
  2280 
       
  2281     return message;
       
  2282 }
       
  2283 
       
  2284 QMessageFolder MapiFolder::folder(QMessageManager::Error *error, const QMessageFolderId& id) const
       
  2285 {
       
  2286     return _store->folder(error, id);
       
  2287 }
       
  2288 
       
  2289 QMessage MapiFolder::message(QMessageManager::Error *error, const QMessageId& id) const
       
  2290 {
       
  2291     return _store->message(error, id);
       
  2292 }
       
  2293 
       
  2294 QMessage::StandardFolder MapiFolder::standardFolder() const
       
  2295 {
       
  2296     return _store->standardFolder(_entryId);
       
  2297 }
       
  2298 
       
  2299 QMessageFolderId MapiFolder::id() const
       
  2300 {
       
  2301 #ifdef _WIN32_WCE
       
  2302     return QMessageFolderIdPrivate::from(_key, _store->entryId(), _entryId);
       
  2303 #else
       
  2304     return QMessageFolderIdPrivate::from(_key, _store->recordKey(), _entryId);
       
  2305 #endif
       
  2306 }
       
  2307 
       
  2308 QMessageAccountId MapiFolder::accountId() const
       
  2309 {
       
  2310     return _store->id();
       
  2311 }
       
  2312 
       
  2313 QMessageFolderId MapiFolder::parentId() const
       
  2314 {
       
  2315     MapiEntryId parentEntryId;
       
  2316     if (getMapiProperty(_folder, PR_PARENT_ENTRYID, &parentEntryId)) {
       
  2317         QMessageManager::Error ignoredError(QMessageManager::NoError);
       
  2318         MapiFolderPtr parent(_store->openFolder(&ignoredError, parentEntryId));
       
  2319         if (!parent.isNull()) {
       
  2320             QMessageManager::Error ignoredError(QMessageManager::NoError);
       
  2321             MapiFolderPtr root(_store->rootFolder(&ignoredError));
       
  2322             if ((ignoredError != QMessageManager::NoError) 
       
  2323                 || !_store->session()->equal(parent->entryId(), root->entryId())) {
       
  2324                 return parent->id();
       
  2325             }
       
  2326         }
       
  2327     }
       
  2328 
       
  2329     return QMessageFolderId();
       
  2330 }
       
  2331 
       
  2332 QList<QMessageFolderId> MapiFolder::ancestorIds() const
       
  2333 {
       
  2334     QList<QMessageFolderId> result;
       
  2335 
       
  2336     QMessageManager::Error ignoredError(QMessageManager::NoError);
       
  2337     MapiFolderPtr root(_store->rootFolder(&ignoredError));
       
  2338 
       
  2339     if (ignoredError == QMessageManager::NoError) {
       
  2340         MapiFolderPtr current = _self.toStrongRef();
       
  2341         MapiSessionPtr session = _store->session();
       
  2342 
       
  2343         while ((ignoredError == QMessageManager::NoError) && !session->equal(current->entryId(), root->entryId())) {
       
  2344             // Find the parent of this folder and append to the list of ancestors
       
  2345             MapiEntryId parentEntryId;
       
  2346             if (getMapiProperty(current->_folder, PR_PARENT_ENTRYID, &parentEntryId)) {
       
  2347                 QMessageManager::Error ignoredError(QMessageManager::NoError);
       
  2348                 current = _store->openFolder(&ignoredError, parentEntryId);
       
  2349                 if (!current.isNull() && !session->equal(current->entryId(), root->entryId())) {
       
  2350                     result.append(current->id());
       
  2351                 }
       
  2352             }
       
  2353         }
       
  2354     }
       
  2355 
       
  2356     return result;
       
  2357 }
       
  2358 
       
  2359 MapiRecordKey MapiFolder::storeKey() const
       
  2360 {
       
  2361     return _store->recordKey();
       
  2362 }
       
  2363 
       
  2364 #ifdef _WIN32_WCE
       
  2365 MapiEntryId MapiFolder::storeEntryId() const
       
  2366 {
       
  2367     return _store->entryId();
       
  2368 }
       
  2369 #endif
       
  2370 
       
  2371 static unsigned long commonFolderMap(QMessage::StandardFolder folder)
       
  2372 {
       
  2373     static bool init = false;
       
  2374     static QMap<QMessage::StandardFolder,unsigned long> propertyMap;
       
  2375 
       
  2376     if(!init)
       
  2377     {
       
  2378         propertyMap.insert(QMessage::DraftsFolder,PROP_TAG(PT_BINARY,0x36D7));
       
  2379         propertyMap.insert(QMessage::TrashFolder,PROP_TAG(PT_BINARY,0x35E3));
       
  2380         propertyMap.insert(QMessage::OutboxFolder,PROP_TAG(PT_BINARY,0x35E2));
       
  2381         propertyMap.insert(QMessage::SentFolder,PROP_TAG(PT_BINARY,0x35E4));
       
  2382 
       
  2383         init = true;
       
  2384     }
       
  2385 
       
  2386     return propertyMap.value(folder);
       
  2387 }
       
  2388 
       
  2389 IMessage *MapiFolder::createMessage(QMessageManager::Error* error)
       
  2390 {
       
  2391     IMessage *message = 0;
       
  2392 
       
  2393     if(FAILED(_folder->CreateMessage(NULL, 0, &message)!=S_OK))
       
  2394     {
       
  2395         *error = QMessageManager::FrameworkFault;
       
  2396         mapiRelease(message);
       
  2397     }
       
  2398     return message;
       
  2399 }
       
  2400 
       
  2401 IMessage* MapiFolder::createMessage(QMessageManager::Error* error, const QMessage& source, const MapiSessionPtr &session, SavePropertyOption saveOption )
       
  2402 {
       
  2403     IMessage* mapiMessage(0);
       
  2404     HRESULT rv = _folder->CreateMessage(0, 0, &mapiMessage);
       
  2405     if (HR_SUCCEEDED(rv)) {
       
  2406         // Store the message properties
       
  2407         if (*error == QMessageManager::NoError) {
       
  2408             storeMessageProperties(error, source, mapiMessage);
       
  2409         }
       
  2410 
       
  2411 #ifndef _WIN32_WCE
       
  2412         //Ensure the message is moved to the sent folder after submission
       
  2413         //On Windows Mobile occurs by default and at discretion of mail client settings.
       
  2414 
       
  2415         MapiFolderPtr sentFolder = _store->findFolder(error,QMessage::SentFolder);
       
  2416         if (!sentFolder.isNull() && *error == QMessageManager::NoError) {
       
  2417             if (!setMapiProperty(mapiMessage, PR_SENTMAIL_ENTRYID, sentFolder->entryId())) {
       
  2418                 qWarning() << "Unable to set sent folder entry id on message";
       
  2419             }
       
  2420         }
       
  2421 #endif
       
  2422 
       
  2423         if (*error == QMessageManager::NoError) {
       
  2424             replaceMessageRecipients(error, source, mapiMessage, session->session());
       
  2425         }
       
  2426         if (*error == QMessageManager::NoError) {
       
  2427             replaceMessageBody(error, source, mapiMessage, _store);
       
  2428         }
       
  2429         if (*error == QMessageManager::NoError) {
       
  2430             addMessageAttachments(error, source, mapiMessage, saveOption );
       
  2431         }
       
  2432 #ifndef _WIN32_WCE //unsupported
       
  2433         if (*error == QMessageManager::NoError && saveOption == SavePropertyChanges ) {
       
  2434             if (HR_FAILED(mapiMessage->SaveChanges(0))) {
       
  2435                 qWarning() << "Unable to save changes for message.";
       
  2436             }
       
  2437         }
       
  2438 #endif
       
  2439         if (*error != QMessageManager::NoError) {
       
  2440             mapiRelease(mapiMessage);
       
  2441         }
       
  2442     } else {
       
  2443         qWarning() << "Failed to create MAPI message";
       
  2444         *error = QMessageManager::FrameworkFault;
       
  2445     }
       
  2446 
       
  2447     return mapiMessage;
       
  2448 }
       
  2449 
       
  2450 MapiStorePtr MapiStore::createStore(QMessageManager::Error *error, const MapiSessionPtr &session, IMsgStore *store, const MapiRecordKey &key, const MapiEntryId &entryId, const QString &name, bool cachedMode)
       
  2451 {
       
  2452     Q_UNUSED(error);
       
  2453     MapiStorePtr ptr = MapiStorePtr(new MapiStore(session, store, key, entryId, name, cachedMode));
       
  2454     if (!ptr.isNull()) {
       
  2455         ptr->_self = ptr;
       
  2456     }
       
  2457     return ptr;
       
  2458 }
       
  2459 
       
  2460 MapiStore::MapiStore()
       
  2461     :_valid(false),
       
  2462      _store(0),
       
  2463      _cachedMode(true),
       
  2464      _adviseConnection(0)
       
  2465 {
       
  2466 }
       
  2467 
       
  2468 MapiStore::MapiStore(const MapiSessionPtr &session, IMsgStore *store, const MapiRecordKey &key, const MapiEntryId &entryId, const QString &name, bool cachedMode)
       
  2469     :_session(session),
       
  2470      _valid(true),
       
  2471      _store(store),
       
  2472      _key(key),
       
  2473      _entryId(entryId),
       
  2474      _name(name),
       
  2475      _cachedMode(cachedMode),
       
  2476      _adviseConnection(0)
       
  2477 {
       
  2478     // Find which standard folders the store contains
       
  2479     foreach (QMessage::StandardFolder sf, QList<QMessage::StandardFolder>() << QMessage::InboxFolder
       
  2480                                                                             << QMessage::DraftsFolder
       
  2481                                                                             << QMessage::TrashFolder
       
  2482                                                                             << QMessage::SentFolder
       
  2483                                                                             << QMessage::OutboxFolder) {
       
  2484         QMessageManager::Error ignoredError(QMessageManager::NoError);
       
  2485         MapiEntryId entryId(standardFolderId(&ignoredError, sf));
       
  2486         if (!entryId.isEmpty()) {
       
  2487             _standardFolderId[sf] = entryId;
       
  2488         }
       
  2489     }
       
  2490 }
       
  2491 
       
  2492 MapiStore::~MapiStore()
       
  2493 {
       
  2494     _folderMap.clear();
       
  2495 
       
  2496     if (_adviseConnection != 0) {
       
  2497         _store->Unadvise(_adviseConnection);
       
  2498     }
       
  2499     mapiRelease(_store);
       
  2500     _valid = false;
       
  2501 }
       
  2502 
       
  2503 MapiFolderPtr MapiStore::findFolder(QMessageManager::Error *error, QMessage::StandardFolder sf)
       
  2504 {
       
  2505     MapiFolderPtr result;
       
  2506 
       
  2507     if (_standardFolderId.contains(sf)) {
       
  2508         result = openFolder(error, _standardFolderId[sf]);
       
  2509     }
       
  2510 
       
  2511     return result;
       
  2512 }
       
  2513 
       
  2514 MapiEntryId MapiStore::standardFolderId(QMessageManager::Error *error, QMessage::StandardFolder sf) const
       
  2515 {
       
  2516     MapiEntryId result;
       
  2517 
       
  2518 #ifndef _WIN32_WCE
       
  2519     //check if the store supports the common folder (not possible for mobile)
       
  2520     bool commonFolderSupported(false);
       
  2521 
       
  2522     unsigned long tag(0);
       
  2523     switch(sf)
       
  2524     {
       
  2525     case QMessage::InboxFolder:
       
  2526         tag = FOLDER_IPM_INBOX_VALID;
       
  2527         break;
       
  2528     case QMessage::OutboxFolder:
       
  2529         tag = FOLDER_IPM_OUTBOX_VALID;
       
  2530         break;
       
  2531     case QMessage::TrashFolder:
       
  2532         tag = FOLDER_IPM_WASTEBASKET_VALID;
       
  2533         break;
       
  2534     case QMessage::SentFolder:
       
  2535         tag = FOLDER_IPM_SENTMAIL_VALID;
       
  2536         break;
       
  2537     case QMessage::DraftsFolder:
       
  2538         //assume drafts exists in every store since there is no mask for it
       
  2539         commonFolderSupported = true;
       
  2540         break;
       
  2541     }
       
  2542 
       
  2543     if (tag) {
       
  2544         ULONG folderSupportMask(0);
       
  2545         if (getMapiProperty(_store, PR_VALID_FOLDER_MASK, &folderSupportMask)) {
       
  2546             if (folderSupportMask & tag) {
       
  2547                 commonFolderSupported = true;
       
  2548             }
       
  2549         } else {
       
  2550             qWarning() << "Could not get folder mask property for store:" << _name;
       
  2551         }
       
  2552     }
       
  2553 
       
  2554     if (!commonFolderSupported) {
       
  2555         return result;
       
  2556     }
       
  2557 #endif
       
  2558 
       
  2559     if (sf == QMessage::InboxFolder) {
       
  2560         result = receiveFolderId(error);
       
  2561     } else {
       
  2562         IMAPIProp* source = _store;
       
  2563         MapiFolderPtr rf;
       
  2564 
       
  2565 #ifndef _WIN32_WCE
       
  2566         //the drafts entryid can only be queried on the inbox or root folder.
       
  2567 
       
  2568         if(sf == QMessage::DraftsFolder) {
       
  2569             rf = receiveFolder(error);
       
  2570             if(*error != QMessageManager::NoError) {
       
  2571                 //inbox failed, try root folder.
       
  2572                 rf = rootFolder(error);
       
  2573                 if(*error != QMessageManager::NoError)
       
  2574                 {
       
  2575                     return result;
       
  2576                 }
       
  2577             }
       
  2578             source = rf->folder();
       
  2579         }
       
  2580 #endif
       
  2581 
       
  2582         MapiEntryId entryId;
       
  2583         if (getMapiProperty(source, commonFolderMap(sf), &entryId)) {
       
  2584             result = entryId;
       
  2585         } else {
       
  2586             *error = QMessageManager::ContentInaccessible;
       
  2587         }
       
  2588     }
       
  2589 
       
  2590 
       
  2591     return result;
       
  2592 }
       
  2593 
       
  2594 QMessageFolderIdList MapiStore::folderIds(QMessageManager::Error *error) const
       
  2595 {
       
  2596     QMessageFolderIdList folderIds;
       
  2597 
       
  2598     MapiFolderPtr root(rootFolder(error));
       
  2599     if (*error == QMessageManager::NoError) {
       
  2600         QList<MapiFolderPtr> folders;
       
  2601         folders.append(root);
       
  2602         // No valid reason to list the root folder.
       
  2603 
       
  2604         while (!folders.isEmpty()) {
       
  2605             MapiFolderPtr subFolder(folders.back()->nextSubFolder(error));
       
  2606             if (!subFolder.isNull() && subFolder->isValid()) {
       
  2607                 folderIds.append(subFolder->id());
       
  2608                 folders.append(subFolder);
       
  2609             } else {
       
  2610                 folders.pop_back();
       
  2611             }
       
  2612         }
       
  2613     } else {
       
  2614         qWarning() << "Unable to open root folder for store:" << _name;
       
  2615     }
       
  2616 
       
  2617     return folderIds;
       
  2618 }
       
  2619 
       
  2620 QMessageFolder MapiStore::folderFromId(QMessageManager::Error *error, const QMessageFolderId &folderId)
       
  2621 {
       
  2622     QMessageFolder result;
       
  2623     QList<MapiFolderPtr> folders;
       
  2624     MapiFolderPtr root(rootFolder(error));
       
  2625     if (!root.isNull() && root->isValid())
       
  2626         folders.append(root);
       
  2627 
       
  2628     while (!folders.isEmpty()) {
       
  2629         MapiFolderPtr subFolder(folders.back()->nextSubFolder(error));
       
  2630         if (!subFolder.isNull() && subFolder->isValid()) {
       
  2631             if (folderId == subFolder->id()) {
       
  2632                 QStringList path;
       
  2633                 for (int i = 0; i < folders.count(); ++i) {
       
  2634                     if (!folders[i]->name().isEmpty()) {
       
  2635                         path.append(folders[i]->name());
       
  2636                     }
       
  2637                 }
       
  2638                 path.append(subFolder->name());
       
  2639                 result = QMessageFolderPrivate::from(subFolder->id(), id(), folders.last()->id(), subFolder->name(), path.join("/"));
       
  2640                 return result;
       
  2641             }
       
  2642             folders.append(subFolder);
       
  2643         } else {
       
  2644             folders.pop_back();
       
  2645         }
       
  2646     }
       
  2647 
       
  2648     return result;
       
  2649 }
       
  2650 
       
  2651 QList<MapiFolderPtr> MapiStore::filterFolders(QMessageManager::Error *error, const QMessageFolderFilter &afilter) const
       
  2652 {
       
  2653     QList<MapiFolderPtr> result;
       
  2654     QMessageFolderFilter filter(QMessageFolderFilterPrivate::preprocess(error, _session, afilter));
       
  2655     if (*error != QMessageManager::NoError)
       
  2656         return result;
       
  2657 
       
  2658 #if 0 //(was ifndef _WIN32_WCE) TODO: fix issue with GetHierarchyTable only returning top level folders
       
  2659     MapiFolderPtr root(rootFolder(error));
       
  2660     if (*error == QMessageManager::NoError) {
       
  2661         IMAPITable *folderTable(0);
       
  2662         HRESULT rv = root->_folder->GetHierarchyTable(CONVENIENT_DEPTH | MAPI_UNICODE, &folderTable);
       
  2663         if (HR_SUCCEEDED(rv)) {
       
  2664             SizedSPropTagArray(5, columns) = {5, {PR_ENTRYID, PR_IS_NEWSGROUP, PR_IS_NEWSGROUP_ANCHOR, PR_EXTENDED_FOLDER_FLAGS, PR_FOLDER_TYPE}};
       
  2665             QueryAllRows qar(folderTable, reinterpret_cast<LPSPropTagArray>(&columns), 0, 0, false);
       
  2666             while (qar.query()) {
       
  2667                 for (uint n = 0; n < qar.rows()->cRows; ++n) {
       
  2668                     SPropValue *props = qar.rows()->aRow[n].lpProps;
       
  2669 
       
  2670                     bool isNewsGroup = (props[1].ulPropTag == PR_IS_NEWSGROUP && props[1].Value.b);
       
  2671                     bool isNewsGroupAnchor = (props[2].ulPropTag == PR_IS_NEWSGROUP_ANCHOR && props[2].Value.b);
       
  2672                     bool special(isNewsGroup || isNewsGroupAnchor);
       
  2673 
       
  2674                     // Skip slow folders, necessary evil
       
  2675                     if (props[3].ulPropTag == PR_EXTENDED_FOLDER_FLAGS) {
       
  2676                         QByteArray extendedFlags(reinterpret_cast<const char*>(props[3].Value.bin.lpb), props[3].Value.bin.cb);
       
  2677                         if (extendedFlags[2] & 8) { // Synchronization issues, skip like Outlook
       
  2678                             special = true;
       
  2679                         }
       
  2680                     } else if (props[4].ulPropTag == PR_FOLDER_TYPE) {
       
  2681                         if (props[4].Value.ul != FOLDER_GENERIC) {
       
  2682                             special = true;
       
  2683                         }
       
  2684                     } else {
       
  2685                         special = true;
       
  2686                     }
       
  2687 
       
  2688                     if (!special) {
       
  2689                         MapiEntryId entryId(props[0].Value.bin.lpb, props[0].Value.bin.cb);
       
  2690                         MapiFolderPtr folder(openFolder(error, entryId));
       
  2691                         if (QMessageFolderFilterPrivate::matchesFolder(filter, folder)) {
       
  2692                             result.append(folder);
       
  2693                         }
       
  2694                     }
       
  2695                 }
       
  2696             }
       
  2697 
       
  2698             *error = qar.error();
       
  2699 
       
  2700             mapiRelease(folderTable);
       
  2701         }
       
  2702     }
       
  2703 #else
       
  2704     // Windows mobile does not support CONVENIENT_DEPTH...
       
  2705     foreach (const QMessageFolderId &folderId, folderIds(error)) {
       
  2706         MapiEntryId entryId = QMessageFolderIdPrivate::entryId(folderId);
       
  2707         MapiFolderPtr folder(openFolder(error, entryId));
       
  2708         if (QMessageFolderFilterPrivate::matchesFolder(filter, folder)) {
       
  2709             result.append(folder);
       
  2710         }
       
  2711     }
       
  2712 #endif
       
  2713 
       
  2714     return result;
       
  2715 }
       
  2716 
       
  2717 MapiEntryId MapiStore::messageEntryId(QMessageManager::Error *error, const MapiRecordKey &folderKey, const MapiRecordKey &messageKey)
       
  2718 {
       
  2719     MapiEntryId result;
       
  2720 
       
  2721     MapiFolderPtr folder(openFolderWithKey(error, folderKey));
       
  2722     if (*error == QMessageManager::NoError) {
       
  2723         result = folder->messageEntryId(error, messageKey);
       
  2724     }
       
  2725 
       
  2726     return result;
       
  2727 }
       
  2728 
       
  2729 MapiFolderPtr MapiStore::openFolder(QMessageManager::Error *error, const MapiEntryId& entryId) const
       
  2730 {
       
  2731     MapiFolderPtr result(0);
       
  2732 
       
  2733     // See if we can create a new pointer to an existing folder
       
  2734     QWeakPointer<MapiFolder> &existing = _folderMap[entryId];
       
  2735     if (!existing.isNull()) {
       
  2736         // Get a pointer to the existing folder
       
  2737         result = existing.toStrongRef();
       
  2738         if (!result.isNull()) {
       
  2739             return result;
       
  2740         }
       
  2741     }
       
  2742 
       
  2743     // We need to create a new instance
       
  2744     IMAPIFolder *folder = openMapiFolder(error, entryId);
       
  2745     if (folder && (*error == QMessageManager::NoError)) {
       
  2746         SizedSPropTagArray(4, columns) = {4, {PR_DISPLAY_NAME, PR_RECORD_KEY, PR_CONTENT_COUNT, PR_SUBFOLDERS}};
       
  2747         SPropValue *properties(0);
       
  2748         ULONG count;
       
  2749         HRESULT rv = folder->GetProps(reinterpret_cast<LPSPropTagArray>(&columns), MAPI_UNICODE, &count, &properties);
       
  2750         if (HR_SUCCEEDED(rv)) {
       
  2751             QString name(QStringFromLpctstr(properties[0].Value.LPSZ));
       
  2752             MapiRecordKey recordKey(properties[1].Value.bin.lpb, properties[1].Value.bin.cb);
       
  2753             uint messageCount = properties[2].Value.ul;
       
  2754             bool hasSubFolders = properties[3].Value.b;
       
  2755 
       
  2756             MapiFolderPtr folderPtr = MapiFolder::createFolder(error, _self.toStrongRef(), folder, recordKey, name, entryId, hasSubFolders, messageCount);
       
  2757             if (*error == QMessageManager::NoError) {
       
  2758                 result = folderPtr;
       
  2759 
       
  2760                 // Add to the map
       
  2761                 _folderMap.insert(entryId, result);
       
  2762             } else {
       
  2763                 qWarning() << "Error creating folder object";
       
  2764             }
       
  2765 
       
  2766             MAPIFreeBuffer(properties);
       
  2767         } else {
       
  2768             qWarning() << "Unable to access folder properties";
       
  2769             mapiRelease(folder);
       
  2770         }
       
  2771     } else {
       
  2772         qWarning() << "Unable to open folder.";
       
  2773     }
       
  2774 
       
  2775     return result;
       
  2776 }
       
  2777 
       
  2778 MapiFolderPtr MapiStore::openFolderWithKey(QMessageManager::Error *error, const MapiRecordKey &recordKey) const
       
  2779 {
       
  2780 #if 0 // Doesn't work on desktop or mobile, only searches top level folders
       
  2781     MapiFolderPtr result;
       
  2782 
       
  2783     MapiFolderPtr root(rootFolder(error));
       
  2784     if (*error == QMessageManager::NoError) {
       
  2785         if (root->recordKey() == recordKey) {
       
  2786             result = root;
       
  2787         } else {
       
  2788             IMAPITable *folderTable(0);
       
  2789             // TODO: this won't work on windows mobile:
       
  2790             HRESULT rv = root->_folder->GetHierarchyTable(CONVENIENT_DEPTH | MAPI_UNICODE, &folderTable);
       
  2791             if (HR_SUCCEEDED(rv)) {
       
  2792                 // Find the entry ID corresponding to this recordKey
       
  2793                 SPropValue keyProp = { 0 };
       
  2794                 keyProp.ulPropTag = PR_RECORD_KEY;
       
  2795                 keyProp.Value.bin.cb = recordKey.count();
       
  2796                 keyProp.Value.bin.lpb = reinterpret_cast<LPBYTE>(const_cast<char*>(recordKey.data()));
       
  2797 
       
  2798                 SRestriction restriction = { 0 };
       
  2799                 restriction.rt = RES_PROPERTY;
       
  2800                 restriction.res.resProperty.relop = RELOP_EQ;
       
  2801                 restriction.res.resProperty.ulPropTag = PR_RECORD_KEY;
       
  2802                 restriction.res.resProperty.lpProp = &keyProp;
       
  2803 
       
  2804                 ULONG flags(0);
       
  2805                 rv = folderTable->Restrict(&restriction, flags);
       
  2806                 if (HR_SUCCEEDED(rv)) {
       
  2807                     SizedSPropTagArray(1, columns) = {1, {PR_ENTRYID}};
       
  2808                     rv = folderTable->SetColumns(reinterpret_cast<LPSPropTagArray>(&columns), 0);
       
  2809                     if (HR_SUCCEEDED(rv)) {
       
  2810                         SRowSet *rows(0);
       
  2811                         if (folderTable->QueryRows(1, 0, &rows) == S_OK) {
       
  2812                             if (rows->cRows == 1) {
       
  2813                                 MapiEntryId entryId(rows->aRow[0].lpProps[0].Value.bin.lpb, rows->aRow[0].lpProps[0].Value.bin.cb);
       
  2814 
       
  2815                                  // Open the folder using its entry ID
       
  2816                                 result = openFolder(error, entryId);
       
  2817                             } else {
       
  2818                                 *error = QMessageManager::InvalidId;
       
  2819                             }
       
  2820                             FreeProws(rows);
       
  2821                         } else {
       
  2822                             *error = QMessageManager::InvalidId;
       
  2823                         }
       
  2824                     } else {
       
  2825                         *error = QMessageManager::ContentInaccessible;
       
  2826                         qWarning() << "Unable to set columns for folder table";
       
  2827                     }
       
  2828                 } else {
       
  2829                     *error = QMessageManager::ContentInaccessible;
       
  2830                     qWarning() << "Unable to restrict folder table";
       
  2831                 }
       
  2832 
       
  2833                 mapiRelease(folderTable);
       
  2834             } else {
       
  2835                 *error = QMessageManager::ContentInaccessible;
       
  2836                 qWarning() << "Unable to open hierarchy table for store.";
       
  2837             }
       
  2838         }
       
  2839     }
       
  2840 
       
  2841     return result;
       
  2842 #else
       
  2843     foreach (const QMessageFolderId &folderId, folderIds(error)) {
       
  2844         MapiRecordKey key = QMessageFolderIdPrivate::folderRecordKey(folderId);
       
  2845         if (key == recordKey) {
       
  2846             MapiEntryId entryId = QMessageFolderIdPrivate::entryId(folderId);
       
  2847             MapiFolderPtr folder(openFolder(error, entryId));
       
  2848             return folder;
       
  2849         }
       
  2850     }
       
  2851 
       
  2852     return MapiFolderPtr();
       
  2853 #endif
       
  2854 }
       
  2855 
       
  2856 bool MapiStore::supports(ULONG featureFlag) const
       
  2857 {
       
  2858     LONG supportMask(0);
       
  2859 
       
  2860     if (getMapiProperty(store(), PR_STORE_SUPPORT_MASK, &supportMask)) {
       
  2861         return supportMask & featureFlag;
       
  2862     }
       
  2863 
       
  2864     // Otherwise, we can't query the store support, so just assume that the feature is supported
       
  2865     return true;
       
  2866 }
       
  2867 
       
  2868 IMessage *MapiStore::openMessage(QMessageManager::Error *error, const MapiEntryId &entryId)
       
  2869 {
       
  2870     IMessage *message(0);
       
  2871 
       
  2872     LPENTRYID entryIdPtr(reinterpret_cast<LPENTRYID>(const_cast<char*>(entryId.data())));
       
  2873     ULONG objectType(0);
       
  2874     HRESULT rv = _store->OpenEntry(entryId.count(), entryIdPtr, 0, MAPI_BEST_ACCESS, &objectType, reinterpret_cast<LPUNKNOWN*>(&message));
       
  2875     if (HR_SUCCEEDED(rv)) {
       
  2876         if (objectType != MAPI_MESSAGE) {
       
  2877             qWarning() << "Not a message - wrong object type:" << objectType;
       
  2878             mapiRelease(message);
       
  2879             *error = QMessageManager::InvalidId;
       
  2880         }
       
  2881     } else {
       
  2882         *error = QMessageManager::InvalidId;
       
  2883         qWarning() << "Invalid message entryId:" << entryId.toBase64();
       
  2884     }
       
  2885 
       
  2886     return message;
       
  2887 }
       
  2888 
       
  2889 IMAPIFolder *MapiStore::openMapiFolder(QMessageManager::Error *error, const MapiEntryId &entryId) const
       
  2890 {
       
  2891     IMAPIFolder *folder(0);
       
  2892 
       
  2893     // TODO:See MS KB article 312013, OpenEntry is not re-entrant, also true of MAPI functions in general?
       
  2894     // TODO:See ms859378, GetPropsExample for alternative memory allocation technique
       
  2895     LPENTRYID entryIdPtr(reinterpret_cast<LPENTRYID>(const_cast<char*>(entryId.data())));
       
  2896     ULONG objectType(0);
       
  2897     HRESULT rv = _store->OpenEntry(entryId.count(), entryIdPtr, 0, MAPI_BEST_ACCESS, &objectType, reinterpret_cast<LPUNKNOWN*>(&folder));
       
  2898     if (HR_SUCCEEDED(rv)) {
       
  2899         if (objectType != MAPI_FOLDER) {
       
  2900             qWarning() << "Not a folder - wrong object type:" << objectType;
       
  2901             mapiRelease(folder);
       
  2902             *error = QMessageManager::InvalidId;
       
  2903         }
       
  2904     } else {
       
  2905         *error = QMessageManager::InvalidId;
       
  2906         qWarning() << "Invalid folder entryId:" << entryId.toBase64();
       
  2907     }
       
  2908 
       
  2909     return folder;
       
  2910 }
       
  2911 
       
  2912 
       
  2913 QMessageAccountId MapiStore::id() const
       
  2914 {
       
  2915 #ifdef _WIN32_WCE
       
  2916     return QMessageAccountIdPrivate::from(_entryId);
       
  2917 #else
       
  2918     return QMessageAccountIdPrivate::from(_key);
       
  2919 #endif
       
  2920 }
       
  2921 
       
  2922 QMessage::TypeFlags MapiStore::types() const
       
  2923 {
       
  2924     QMessage::TypeFlags flags(QMessage::Email);
       
  2925 
       
  2926 #ifdef _WIN32_WCE
       
  2927     if (name().toUpper() == "SMS") {
       
  2928         // On Windows Mobile SMS store is named "SMS"
       
  2929         flags = QMessage::Sms;
       
  2930     }
       
  2931 #endif
       
  2932 
       
  2933     return flags;
       
  2934 }
       
  2935 
       
  2936 // TODO: can we remove this?
       
  2937 QMessageAddress MapiStore::address() const
       
  2938 {
       
  2939     QMessageAddress result;
       
  2940 
       
  2941 
       
  2942     return result;
       
  2943 }
       
  2944 
       
  2945 MapiSessionPtr MapiStore::session() const
       
  2946 {
       
  2947     return _session.toStrongRef();
       
  2948 }
       
  2949 
       
  2950 MapiFolderPtr MapiStore::rootFolder(QMessageManager::Error *error) const
       
  2951 {
       
  2952     MapiFolderPtr result;
       
  2953 
       
  2954     MapiEntryId entryId(rootFolderId(error));
       
  2955     if (*error == QMessageManager::NoError) {
       
  2956         result = openFolder(error, entryId);
       
  2957     }
       
  2958 
       
  2959     return result;
       
  2960 }
       
  2961 
       
  2962 MapiEntryId MapiStore::rootFolderId(QMessageManager::Error *error) const
       
  2963 {
       
  2964     MapiEntryId result;
       
  2965 
       
  2966     if (!_valid || !_store) {
       
  2967         Q_ASSERT(_valid && _store);
       
  2968         *error = QMessageManager::FrameworkFault;
       
  2969         return result;
       
  2970     }
       
  2971 
       
  2972     MapiEntryId entryId;
       
  2973     if (getMapiProperty(_store, PR_IPM_SUBTREE_ENTRYID, &entryId)) {
       
  2974         result = entryId;
       
  2975     } else {
       
  2976         *error = QMessageManager::ContentInaccessible;
       
  2977         qWarning() << "Unable to get root folder entry ID for store:" << _name;
       
  2978     }
       
  2979 
       
  2980     return result;
       
  2981 }
       
  2982 
       
  2983 MapiFolderPtr MapiStore::receiveFolder(QMessageManager::Error *error) const
       
  2984 {
       
  2985     MapiFolderPtr result;
       
  2986 
       
  2987     MapiEntryId entryId(receiveFolderId(error));
       
  2988     if (*error == QMessageManager::NoError) {
       
  2989         result = openFolder(error, entryId);
       
  2990     }
       
  2991 
       
  2992     return result;
       
  2993 }
       
  2994 
       
  2995 MapiEntryId MapiStore::receiveFolderId(QMessageManager::Error *error) const
       
  2996 {
       
  2997     MapiEntryId result;
       
  2998 
       
  2999     if (!_valid || !_store) {
       
  3000         Q_ASSERT(_valid && _store);
       
  3001         *error = QMessageManager::FrameworkFault;
       
  3002         return result;
       
  3003     }
       
  3004 
       
  3005     ULONG entryIdSize = 0;
       
  3006     LPENTRYID entryIdPtr = 0;
       
  3007     HRESULT rv = _store->GetReceiveFolder(0, 0, &entryIdSize, &entryIdPtr, 0);
       
  3008     if (HR_SUCCEEDED(rv)) {
       
  3009         result = MapiEntryId(entryIdPtr, entryIdSize);
       
  3010 
       
  3011         MAPIFreeBuffer(entryIdPtr);
       
  3012     } else {
       
  3013         *error = QMessageManager::FrameworkFault;
       
  3014     }
       
  3015 
       
  3016     return result;
       
  3017 }
       
  3018 
       
  3019 QMessageFolder MapiStore::folder(QMessageManager::Error *error, const QMessageFolderId& id) const
       
  3020 {
       
  3021     return _session.toStrongRef()->folder(error, id);
       
  3022 }
       
  3023 
       
  3024 QMessage MapiStore::message(QMessageManager::Error *error, const QMessageId& id) const
       
  3025 {
       
  3026     return _session.toStrongRef()->message(error, id);
       
  3027 }
       
  3028 
       
  3029 QMessage::StandardFolder MapiStore::standardFolder(const MapiEntryId &entryId) const
       
  3030 {
       
  3031     QMap<QMessage::StandardFolder, MapiEntryId>::const_iterator it = _standardFolderId.begin(), end = _standardFolderId.end();
       
  3032     for ( ; it != end; ++it) {
       
  3033         if (_session.toStrongRef()->equal(it.value(), entryId)) {
       
  3034             return it.key();
       
  3035         }
       
  3036     }
       
  3037 
       
  3038     return QMessage::DraftsFolder;
       
  3039 }
       
  3040 
       
  3041 bool MapiStore::setAdviseSink(ULONG mask, IMAPIAdviseSink *sink)
       
  3042 {
       
  3043     if (_adviseConnection != 0) {
       
  3044         _store->Unadvise(_adviseConnection);
       
  3045     }
       
  3046 
       
  3047     HRESULT rv = _store->Advise(0, 0, mask, sink, &_adviseConnection);
       
  3048     if (HR_FAILED(rv)) {
       
  3049         qWarning() << "Unable to register for notifications from store.";
       
  3050         return false;
       
  3051     }
       
  3052 
       
  3053     return true;
       
  3054 }
       
  3055 
       
  3056 void MapiStore::notifyEvents(ULONG mask)
       
  3057 {
       
  3058     // Test whether this store supports notifications
       
  3059 
       
  3060     if (supports(STORE_NOTIFY_OK)) {
       
  3061         AdviseSink *sink(new AdviseSink(this));
       
  3062         if (setAdviseSink(mask, sink)) {
       
  3063             // sink will be deleted when the store releases it
       
  3064         } else {
       
  3065             delete sink;
       
  3066         }
       
  3067     } else {
       
  3068         qWarning() << "Store does not support notifications.";
       
  3069     }
       
  3070 
       
  3071  }
       
  3072 
       
  3073 #ifdef _WIN32_WCE
       
  3074 
       
  3075 QString MapiStore::transportName() const
       
  3076 {
       
  3077     QString result;
       
  3078         if(!getMapiProperty(_store,PR_CE_TRANSPORT_NAME,&result))
       
  3079             qWarning() << "Could not query transport name for store " << name();
       
  3080 
       
  3081     return result;
       
  3082 }
       
  3083 
       
  3084 #endif
       
  3085 
       
  3086 HRESULT MapiStore::AdviseSink::QueryInterface(REFIID id, LPVOID FAR* o)
       
  3087 {
       
  3088     if (id == IID_IUnknown) {
       
  3089         *o = this;
       
  3090         AddRef();
       
  3091         return S_OK;
       
  3092     }
       
  3093 
       
  3094     return E_NOINTERFACE;
       
  3095 }
       
  3096 
       
  3097 ULONG MapiStore::AdviseSink::AddRef()
       
  3098 {
       
  3099     return InterlockedIncrement(&_refCount);
       
  3100 }
       
  3101 
       
  3102 ULONG MapiStore::AdviseSink::Release()
       
  3103 {
       
  3104     ULONG result = InterlockedDecrement(&_refCount);
       
  3105     if (result == 0) {
       
  3106         delete this;
       
  3107     }
       
  3108 
       
  3109     return result;
       
  3110 }
       
  3111 
       
  3112 ULONG MapiStore::AdviseSink::OnNotify(ULONG notificationCount, LPNOTIFICATION notifications)
       
  3113 {
       
  3114     MapiSessionPtr session = _store->_session.toStrongRef();
       
  3115     if (!session.isNull()) {
       
  3116         for (uint i = 0; i < notificationCount; ++i) {
       
  3117             NOTIFICATION &notification(notifications[i]);
       
  3118 
       
  3119             if (notification.ulEventType == fnevNewMail) {
       
  3120                 // TODO: Do we need to process this, or is handling object-created sufficient?
       
  3121                 //NEWMAIL_NOTIFICATION &newmail(notification.info.newmail);
       
  3122             } else {
       
  3123                 OBJECT_NOTIFICATION &object(notification.info.obj);
       
  3124 
       
  3125                 if (object.ulObjType == MAPI_MESSAGE) {
       
  3126                     MapiEntryId entryId(object.lpEntryID, object.cbEntryID);
       
  3127 
       
  3128                     if (!entryId.isEmpty()) {
       
  3129                         // Create a partial ID from the information we have (a message can be
       
  3130                         // retrieved using only the store key and the message entry ID)
       
  3131 #ifdef _WIN32_WCE
       
  3132                         QMessageId messageId(QMessageIdPrivate::from(_store->entryId(),entryId));
       
  3133 #else
       
  3134                         QMessageId messageId(QMessageIdPrivate::from(_store->recordKey(), entryId));
       
  3135 #endif
       
  3136 
       
  3137                         MapiSession::NotifyType notifyType;
       
  3138                         switch (notification.ulEventType)
       
  3139                         {
       
  3140                         case fnevObjectCopied: notifyType = MapiSession::Added; break;
       
  3141                         case fnevObjectCreated: notifyType = MapiSession::Added; break;
       
  3142                         case fnevObjectDeleted: notifyType = MapiSession::Removed; break;
       
  3143                         case fnevObjectModified: notifyType = MapiSession::Updated; break;
       
  3144                         case fnevObjectMoved: notifyType = MapiSession::Updated; break;
       
  3145                         }
       
  3146 
       
  3147                         // Create an event to be processed by the UI thread
       
  3148                         QCoreApplication::postEvent(session.data(), new MapiSession::NotifyEvent(_store, messageId, notifyType));
       
  3149                     } else {
       
  3150                         qWarning() << "Received notification, but no entry ID";
       
  3151                     }
       
  3152                 }
       
  3153             }
       
  3154         }
       
  3155     }
       
  3156 
       
  3157     return 0;
       
  3158 }
       
  3159 
       
  3160 class SessionManager : public QObject
       
  3161 {
       
  3162     Q_OBJECT
       
  3163 
       
  3164 public:
       
  3165     SessionManager();
       
  3166     bool initialize(MapiSession *newSession);
       
  3167 
       
  3168     const MapiSessionPtr &session() const;
       
  3169 
       
  3170 private slots:
       
  3171     void appDestroyed();
       
  3172 
       
  3173 private:
       
  3174     MapiSessionPtr ptr;
       
  3175 };
       
  3176 
       
  3177 SessionManager::SessionManager()
       
  3178 {
       
  3179     connect(QCoreApplication::instance(), SIGNAL(destroyed()), this, SLOT(appDestroyed()));
       
  3180 }
       
  3181 
       
  3182 bool SessionManager::initialize(MapiSession *newSession)
       
  3183 {
       
  3184     if(newSession->_mapiSession)
       
  3185     {
       
  3186         ptr = MapiSessionPtr(newSession);
       
  3187         ptr->_self = ptr;
       
  3188         return true;
       
  3189     }
       
  3190     return false;
       
  3191 }
       
  3192 
       
  3193 const MapiSessionPtr &SessionManager::session() const
       
  3194 {
       
  3195     return ptr;
       
  3196 }
       
  3197 
       
  3198 void SessionManager::appDestroyed()
       
  3199 {
       
  3200     // We need to terminate the MAPI session before main ends
       
  3201     ptr.clear();
       
  3202 }
       
  3203 
       
  3204 Q_GLOBAL_STATIC(SessionManager, manager);
       
  3205 
       
  3206 MapiSessionPtr MapiSession::createSession(QMessageManager::Error *error)
       
  3207 {
       
  3208     static bool initialized(manager()->initialize(new MapiSession(error)));
       
  3209 
       
  3210     MapiSessionPtr ptr(manager()->session());
       
  3211     if (ptr.isNull()) {
       
  3212         if (*error == QMessageManager::NoError) {
       
  3213             *error = QMessageManager::ContentInaccessible;
       
  3214         }
       
  3215         qWarning() << "Unable to instantiate session";
       
  3216     }
       
  3217 
       
  3218     return ptr;
       
  3219 }
       
  3220 
       
  3221 MapiSession::MapiSession()
       
  3222     :_token(0),
       
  3223      _mapiSession(0),
       
  3224      _filterId(0),
       
  3225      _registered(false)
       
  3226 {
       
  3227 }
       
  3228 
       
  3229 MapiSession::MapiSession(QMessageManager::Error *error)
       
  3230     :QObject(),
       
  3231      _token(WinHelpers::initializeMapi()),
       
  3232      _mapiSession(0),
       
  3233      _filterId(0),
       
  3234      _registered(false)
       
  3235 {
       
  3236     if (!_token->_initialized) {
       
  3237         *error = QMessageManager::ContentInaccessible;
       
  3238     } else {
       
  3239         // Attempt to start a MAPI session on the default profile
       
  3240         HRESULT rv = MAPILogonEx(0, (LPTSTR)0, 0, MAPI_EXTENDED | MAPI_USE_DEFAULT | MAPI_NEW_SESSION, &_mapiSession);
       
  3241         if (HR_FAILED(rv)) {
       
  3242             qWarning() << "Failed to login to MAPI session - rv:" << hex << (ULONG)rv;
       
  3243             *error = QMessageManager::ContentInaccessible;
       
  3244             _mapiSession = 0;
       
  3245         }
       
  3246 
       
  3247         // This thread must run the message pump for the hidden MAPI window
       
  3248         // http://blogs.msdn.com/stephen_griffin/archive/2009/05/22/the-fifth-mapi-multithreading-rule.aspx
       
  3249         dispatchNotifications();
       
  3250     }
       
  3251 }
       
  3252 
       
  3253 MapiSession::~MapiSession()
       
  3254 {
       
  3255     _storeMap.clear();
       
  3256 
       
  3257     if (_mapiSession) {
       
  3258         _mapiSession->Logoff(0, 0, 0);
       
  3259         mapiRelease(_mapiSession);
       
  3260     }
       
  3261 }
       
  3262 
       
  3263 MapiStorePtr MapiSession::findStore(QMessageManager::Error *error, const QMessageAccountId &id, bool cachedMode) const
       
  3264 {
       
  3265     MapiStorePtr result(0);
       
  3266     if (!_mapiSession) {
       
  3267         Q_ASSERT(_mapiSession);
       
  3268         *error = QMessageManager::FrameworkFault;
       
  3269         return result;
       
  3270     }
       
  3271 
       
  3272     IMAPITable *mapiMessageStoresTable(0);
       
  3273     if (_mapiSession->GetMsgStoresTable(0, &mapiMessageStoresTable) != S_OK) {
       
  3274         *error = QMessageManager::ContentInaccessible;
       
  3275         return result;
       
  3276     }
       
  3277 
       
  3278     const int nCols(3);
       
  3279     enum { defaultStoreColumn = 0, entryIdColumn, recordKeyColumn };
       
  3280     SizedSPropTagArray(nCols, columns) = {nCols, {PR_DEFAULT_STORE, PR_ENTRYID, PR_RECORD_KEY}};
       
  3281 
       
  3282     QueryAllRows qar(mapiMessageStoresTable, reinterpret_cast<LPSPropTagArray>(&columns), 0, 0, false);
       
  3283 
       
  3284     while(qar.query()) {
       
  3285         for (uint n = 0; n < qar.rows()->cRows; ++n) {
       
  3286             SPropValue *props = qar.rows()->aRow[n].lpProps;
       
  3287 #ifdef _WIN32_WCE
       
  3288             MapiEntryId storeKey(props[entryIdColumn].Value.bin.lpb, props[entryIdColumn].Value.bin.cb);
       
  3289 #else
       
  3290             MapiRecordKey storeKey(props[recordKeyColumn].Value.bin.lpb, props[recordKeyColumn].Value.bin.cb);
       
  3291 #endif
       
  3292 
       
  3293             if ((!id.isValid() && props[defaultStoreColumn].Value.b) ||  // default store found
       
  3294                 (id.isValid() && (id == QMessageAccountIdPrivate::from(storeKey)))) {    // specified store found
       
  3295                 MapiEntryId entryId(props[entryIdColumn].Value.bin.lpb, props[entryIdColumn].Value.bin.cb);
       
  3296                 result = openStore(error, entryId, cachedMode);
       
  3297                 break;
       
  3298             }
       
  3299         }
       
  3300     }
       
  3301 
       
  3302     *error = qar.error();
       
  3303 
       
  3304     mapiRelease(mapiMessageStoresTable);
       
  3305 
       
  3306     return result;
       
  3307 }
       
  3308 
       
  3309 template<typename Predicate, typename Ordering>
       
  3310 QList<MapiStorePtr> MapiSession::filterStores(QMessageManager::Error *error, Predicate predicate, Ordering sortOrder, uint limit, uint offset, bool cachedMode) const
       
  3311 {
       
  3312     if (!predicate._filter.isSupported()) {
       
  3313         *error = QMessageManager::ConstraintFailure;
       
  3314         return QList<MapiStorePtr>();
       
  3315     }
       
  3316 
       
  3317     QList<MapiStorePtr> result;
       
  3318     if (!_mapiSession) {
       
  3319         Q_ASSERT(_mapiSession);
       
  3320         *error = QMessageManager::FrameworkFault;
       
  3321         return result;
       
  3322     }
       
  3323 
       
  3324     IMAPITable *mapiMessageStoresTable(0);
       
  3325     if (_mapiSession->GetMsgStoresTable(0, &mapiMessageStoresTable) != S_OK) {
       
  3326         *error = QMessageManager::ContentInaccessible;
       
  3327     } else {
       
  3328         SizedSPropTagArray(1, columns) = {1, {PR_ENTRYID}};
       
  3329 
       
  3330         QueryAllRows qar(mapiMessageStoresTable, reinterpret_cast<LPSPropTagArray>(&columns), 0, 0, false);
       
  3331 
       
  3332         while (qar.query()) {
       
  3333             for (uint n = 0; n < qar.rows()->cRows; ++n) {
       
  3334                 MapiEntryId entryId(qar.rows()->aRow[n].lpProps[0].Value.bin.lpb, qar.rows()->aRow[n].lpProps[0].Value.bin.cb);
       
  3335                 MapiStorePtr store(openStore(error, entryId, cachedMode));
       
  3336                 if (!store.isNull()) {
       
  3337 #ifndef _WIN32_WCE
       
  3338                     // We only want stores that contain private messages
       
  3339                     if (store->supports(STORE_PUBLIC_FOLDERS)) {
       
  3340                         continue;
       
  3341                     }
       
  3342 #endif
       
  3343                     if (predicate(store)) {
       
  3344                         result.append(store);
       
  3345                     }
       
  3346                 }
       
  3347             }
       
  3348         }
       
  3349 
       
  3350         *error = qar.error();
       
  3351 
       
  3352         mapiRelease(mapiMessageStoresTable);
       
  3353     }
       
  3354 
       
  3355     if (!sortOrder.isEmpty()) {
       
  3356         QList<StoreSortHelper> accountList;
       
  3357         foreach (MapiStorePtr storePtr, result) {
       
  3358             accountList.append(StoreSortHelper(&sortOrder, storePtr));
       
  3359         }
       
  3360         qSort(accountList.begin(), accountList.end());
       
  3361         result.clear();
       
  3362         foreach (StoreSortHelper alt, accountList) {
       
  3363             result.append(alt.store());
       
  3364         }
       
  3365         accountList.clear();
       
  3366     }
       
  3367 
       
  3368     // See note in filerMessages explaining why IMAPITable::SeekRow can't be used to skip unwanted items
       
  3369     if (offset) {
       
  3370         return result.mid(offset, (limit ? limit : -1));
       
  3371     } else {
       
  3372         return result;
       
  3373     }
       
  3374 }
       
  3375 
       
  3376 QList<MapiStorePtr> MapiSession::filterStores(QMessageManager::Error *error, const QMessageAccountFilter &filter, const QMessageAccountSortOrder &sortOrder, uint limit, uint offset, bool cachedMode) const
       
  3377 {
       
  3378     AccountFilterPredicate pred(filter);
       
  3379     return filterStores<const AccountFilterPredicate&, const QMessageAccountSortOrder &>(error, pred, sortOrder, limit, offset, cachedMode);
       
  3380 }
       
  3381 
       
  3382 QList<MapiStorePtr> MapiSession::allStores(QMessageManager::Error *error, bool cachedMode) const
       
  3383 {
       
  3384     return filterStores(error, QMessageAccountFilter(), QMessageAccountSortOrder(), 0, 0, cachedMode);
       
  3385 }
       
  3386 
       
  3387 QList<MapiFolderPtr> MapiSession::filterFolders(QMessageManager::Error *error, const QMessageFolderFilter &filter, const QMessageFolderSortOrder &sortOrder, uint limit, uint offset, bool cachedMode) const
       
  3388 {
       
  3389     if (!filter.isSupported()) {
       
  3390         *error = QMessageManager::ConstraintFailure;
       
  3391         return QList<MapiFolderPtr>();
       
  3392     }
       
  3393 
       
  3394     QList<MapiFolderPtr> result;
       
  3395     if (!_mapiSession) {
       
  3396         Q_ASSERT(_mapiSession);
       
  3397         *error = QMessageManager::FrameworkFault;
       
  3398         return result;
       
  3399     }
       
  3400 
       
  3401     foreach (const MapiStorePtr &store, allStores(error, cachedMode)) {
       
  3402         result.append(store->filterFolders(error, filter));
       
  3403     }
       
  3404 
       
  3405     if (!sortOrder.isEmpty()) {
       
  3406         QList<FolderSortHelper> folderList;
       
  3407         foreach (MapiFolderPtr folderPtr, result) {
       
  3408             QMessageFolder orderFolder(folder(error, folderPtr->id()));
       
  3409             if (*error != QMessageManager::NoError) {
       
  3410                 folderList.clear();
       
  3411                 break;
       
  3412             }
       
  3413             folderList.append(FolderSortHelper(&sortOrder, folderPtr, orderFolder));
       
  3414         }
       
  3415         qSort(folderList.begin(), folderList.end());
       
  3416         result.clear();
       
  3417         foreach (FolderSortHelper helper, folderList) {
       
  3418             result.append(helper.mapiFolderPtr());
       
  3419         }
       
  3420         folderList.clear();
       
  3421     }
       
  3422 
       
  3423     // See note in filerMessages explaining why IMAPITable::SeekRow can't be used to skip unwanted items
       
  3424     if (offset) {
       
  3425         return result.mid(offset, (limit ? limit : -1));
       
  3426     } else {
       
  3427         return result;
       
  3428     }
       
  3429 }
       
  3430 
       
  3431 IMsgStore *MapiSession::openMapiStore(QMessageManager::Error *error, const MapiEntryId &entryId, bool cachedMode) const
       
  3432 {
       
  3433     IMsgStore *store(0);
       
  3434 
       
  3435     LPENTRYID entryIdPtr(reinterpret_cast<LPENTRYID>(const_cast<char*>(entryId.data())));
       
  3436     unsigned long openFlags = MAPI_BEST_ACCESS | MDB_WRITE;
       
  3437 
       
  3438     if(!cachedMode)
       
  3439         openFlags |= MDB_ONLINE;
       
  3440 
       
  3441     HRESULT rv = _mapiSession->OpenMsgStore(0, entryId.count(), entryIdPtr, 0, openFlags, reinterpret_cast<LPMDB*>(&store));
       
  3442     if (HR_FAILED(rv)) {
       
  3443         *error = QMessageManager::InvalidId;
       
  3444         qWarning() << "Invalid store entryId:" << entryId.toBase64();
       
  3445     }
       
  3446 
       
  3447     return store;
       
  3448 }
       
  3449 
       
  3450 MapiStorePtr MapiSession::openStore(QMessageManager::Error *error, const MapiEntryId& entryId, bool cachedMode) const
       
  3451 {
       
  3452     MapiStorePtr result(0);
       
  3453 
       
  3454     // See if we can create a new pointer to an existing store
       
  3455     MapiStorePtr &existing = _storeMap[entryId];
       
  3456     if (!existing.isNull()) {
       
  3457         return existing;
       
  3458     }
       
  3459 
       
  3460     // We need to create a new instance
       
  3461     IMsgStore *store = openMapiStore(error, entryId, cachedMode);
       
  3462     if (store && (*error == QMessageManager::NoError)) {
       
  3463         // Find the other properties of this store
       
  3464         SizedSPropTagArray(2, columns) = {2, {PR_DISPLAY_NAME, PR_RECORD_KEY}};
       
  3465         SPropValue *properties(0);
       
  3466         ULONG count;
       
  3467         HRESULT rv = store->GetProps(reinterpret_cast<LPSPropTagArray>(&columns), MAPI_UNICODE, &count, &properties);
       
  3468         if (HR_SUCCEEDED(rv)) {
       
  3469             QString name(QStringFromLpctstr(properties[0].Value.LPSZ));
       
  3470             MapiRecordKey recordKey(properties[1].Value.bin.lpb, properties[1].Value.bin.cb);
       
  3471 
       
  3472             MapiStorePtr storePtr = MapiStore::createStore(error, _self.toStrongRef(), store, recordKey, entryId, name, cachedMode);
       
  3473             if (*error == QMessageManager::NoError) {
       
  3474                 result = storePtr;
       
  3475 
       
  3476                 // Add to the map
       
  3477                 _storeMap.insert(entryId, result);
       
  3478             } else {
       
  3479                 qWarning() << "Error creating store object";
       
  3480             }
       
  3481 
       
  3482             MAPIFreeBuffer(properties);
       
  3483         } else {
       
  3484             qWarning() << "Unable to access store properties";
       
  3485             mapiRelease(store);
       
  3486         }
       
  3487     }
       
  3488 
       
  3489     return result;
       
  3490 }
       
  3491 
       
  3492 MapiStorePtr MapiSession::openStoreWithKey(QMessageManager::Error *error, const MapiRecordKey &recordKey, bool cachedMode) const
       
  3493 {
       
  3494     MapiStorePtr result;
       
  3495 
       
  3496     IMAPITable *storesTable(0);
       
  3497     HRESULT rv = _mapiSession->GetMsgStoresTable(0, &storesTable);
       
  3498     if (HR_SUCCEEDED(rv)) {
       
  3499         // Find the entry ID corresponding to this recordKey
       
  3500         SPropValue keyProp = { 0 };
       
  3501         keyProp.ulPropTag = PR_RECORD_KEY;
       
  3502         keyProp.Value.bin.cb = recordKey.count();
       
  3503         keyProp.Value.bin.lpb = reinterpret_cast<LPBYTE>(const_cast<char*>(recordKey.data()));
       
  3504 
       
  3505         SRestriction restriction = { 0 };
       
  3506         restriction.rt = RES_PROPERTY;
       
  3507         restriction.res.resProperty.relop = RELOP_EQ;
       
  3508         restriction.res.resProperty.ulPropTag = PR_RECORD_KEY;
       
  3509         restriction.res.resProperty.lpProp = &keyProp;
       
  3510 
       
  3511         ULONG flags(0);
       
  3512         rv = storesTable->Restrict(&restriction, flags);
       
  3513         if (HR_SUCCEEDED(rv)) {
       
  3514             SizedSPropTagArray(1, columns) = {1, {PR_ENTRYID}};
       
  3515             rv = storesTable->SetColumns(reinterpret_cast<LPSPropTagArray>(&columns), 0);
       
  3516             if (HR_SUCCEEDED(rv)) {
       
  3517                 SRowSet *rows(0);
       
  3518                 if (storesTable->QueryRows(1, 0, &rows) == S_OK) {
       
  3519                     if (rows->cRows == 1) {
       
  3520                         MapiEntryId entryId(rows->aRow[0].lpProps[0].Value.bin.lpb, rows->aRow[0].lpProps[0].Value.bin.cb);
       
  3521 
       
  3522                         result = openStore(error, entryId, cachedMode);
       
  3523                     } else {
       
  3524                         *error = QMessageManager::InvalidId;
       
  3525                     }
       
  3526                     FreeProws(rows);
       
  3527                 } else {
       
  3528                     *error = QMessageManager::InvalidId;
       
  3529                 }
       
  3530             } else {
       
  3531                 *error = QMessageManager::ContentInaccessible;
       
  3532                 qWarning() << "Unable to set columns for stores table";
       
  3533             }
       
  3534         } else {
       
  3535             *error = QMessageManager::ContentInaccessible;
       
  3536             qWarning() << "Unable to restrict stores table";
       
  3537         }
       
  3538 
       
  3539         mapiRelease(storesTable);
       
  3540     } else {
       
  3541         *error = QMessageManager::ContentInaccessible;
       
  3542         qWarning() << "Unable to open stores table.";
       
  3543     }
       
  3544 
       
  3545     return result;
       
  3546 }
       
  3547 
       
  3548 QMessageAccountId MapiSession::defaultAccountId(QMessageManager::Error *error, QMessage::Type type) const
       
  3549 {
       
  3550 #ifdef _WIN32_WCE
       
  3551     if (type == QMessage::Sms) {
       
  3552         foreach (MapiStorePtr store, allStores(error)) {
       
  3553             if (store->name() == "SMS") {
       
  3554                 return store->id();
       
  3555             }
       
  3556         }
       
  3557     }
       
  3558 
       
  3559 #endif
       
  3560     if (type == QMessage::Email) {
       
  3561         // Return the account of the default store
       
  3562         MapiStorePtr store(defaultStore(error));
       
  3563         if (*error == QMessageManager::NoError) {
       
  3564             return store->id();
       
  3565         }
       
  3566     }
       
  3567 
       
  3568     return QMessageAccountId();
       
  3569 }
       
  3570 
       
  3571 IMessage *MapiSession::openMapiMessage(QMessageManager::Error *error, const QMessageId &id, MapiStorePtr *storePtr) const
       
  3572 {
       
  3573     IMessage *message(0);
       
  3574 
       
  3575 #ifdef _WIN32_WCE
       
  3576     MapiEntryId storeRecordKey(QMessageIdPrivate::storeRecordKey(id));
       
  3577 #else
       
  3578     MapiRecordKey storeRecordKey(QMessageIdPrivate::storeRecordKey(id));
       
  3579 #endif
       
  3580 
       
  3581     MapiStorePtr mapiStore(findStore(error, QMessageAccountIdPrivate::from(storeRecordKey)));
       
  3582     if (*error == QMessageManager::NoError) {
       
  3583         MapiEntryId entryId(QMessageIdPrivate::entryId(id));
       
  3584 
       
  3585         message = mapiStore->openMessage(error, entryId);
       
  3586         if (*error == QMessageManager::NoError) {
       
  3587             if (storePtr) {
       
  3588                 *storePtr = mapiStore;
       
  3589             }
       
  3590         } else {
       
  3591             qWarning() << "Invalid message entryId:" << entryId.toBase64();
       
  3592             mapiRelease(message);
       
  3593         }
       
  3594     } else {
       
  3595         qWarning() << "Invalid store recordKey:" << storeRecordKey.toBase64();
       
  3596     }
       
  3597 
       
  3598     return message;
       
  3599 }
       
  3600 
       
  3601 MapiEntryId MapiSession::messageEntryId(QMessageManager::Error *error, const MapiRecordKey &storeKey, const MapiRecordKey &folderKey, const MapiRecordKey &messageKey)
       
  3602 {
       
  3603     MapiEntryId result;
       
  3604 
       
  3605     MapiStorePtr store(openStoreWithKey(error, storeKey));
       
  3606     if (*error == QMessageManager::NoError) {
       
  3607         result = store->messageEntryId(error, folderKey, messageKey);
       
  3608     }
       
  3609 
       
  3610     return result;
       
  3611 }
       
  3612 
       
  3613 MapiRecordKey MapiSession::messageRecordKey(QMessageManager::Error *error, const QMessageId &id)
       
  3614 {
       
  3615     MapiRecordKey result;
       
  3616 
       
  3617     IMessage *message = openMapiMessage(error, id);
       
  3618     if (*error == QMessageManager::NoError) {
       
  3619         if (!getMapiProperty(message, PR_RECORD_KEY, &result)) {
       
  3620             qWarning() << "Unable to query message record key.";
       
  3621         }
       
  3622 
       
  3623         mapiRelease(message);
       
  3624     }
       
  3625 
       
  3626     return result;
       
  3627 }
       
  3628 
       
  3629 MapiRecordKey MapiSession::folderRecordKey(QMessageManager::Error *error, const QMessageId &id)
       
  3630 {
       
  3631     MapiRecordKey result;
       
  3632 
       
  3633     MapiStorePtr store;
       
  3634     IMessage *message = openMapiMessage(error, id, &store);
       
  3635     if (*error == QMessageManager::NoError) {
       
  3636         // Find the parent folder for the message
       
  3637         MapiEntryId folderId;
       
  3638         if (getMapiProperty(message, PR_PARENT_ENTRYID, &folderId)) {
       
  3639             MapiFolderPtr folder = store->openFolder(error, folderId);
       
  3640             if (*error == QMessageManager::NoError) {
       
  3641                 result = folder->recordKey();
       
  3642             }
       
  3643         } else {
       
  3644             qWarning() << "Unable to query folder entry ID from message.";
       
  3645         }
       
  3646 
       
  3647         mapiRelease(message);
       
  3648     }
       
  3649 
       
  3650     return result;
       
  3651 }
       
  3652 
       
  3653 #ifdef _WIN32_WCE
       
  3654 
       
  3655 MapiEntryId MapiSession::folderEntryId(QMessageManager::Error *error, const QMessageId &id)
       
  3656 {
       
  3657     MapiEntryId result;
       
  3658 
       
  3659     IMessage *message = openMapiMessage(error, id);
       
  3660     if (*error == QMessageManager::NoError) {
       
  3661         // Find the parent folder for the message
       
  3662         MapiEntryId folderId;
       
  3663         if (getMapiProperty(message, PR_PARENT_ENTRYID, &folderId)) {
       
  3664             result = folderId;
       
  3665         } else {
       
  3666             qWarning() << "Unable to query folder entry ID from message.";
       
  3667         }
       
  3668 
       
  3669         mapiRelease(message);
       
  3670     }
       
  3671 
       
  3672     return result;
       
  3673 }
       
  3674 
       
  3675 #endif
       
  3676 
       
  3677 bool MapiSession::equal(const MapiEntryId &lhs, const MapiEntryId &rhs) const
       
  3678 {
       
  3679     ULONG result(0);
       
  3680 
       
  3681     LPENTRYID lhsEntryId(reinterpret_cast<LPENTRYID>(const_cast<char*>(lhs.data())));
       
  3682     LPENTRYID rhsEntryId(reinterpret_cast<LPENTRYID>(const_cast<char*>(rhs.data())));
       
  3683 
       
  3684     HRESULT rv = _mapiSession->CompareEntryIDs(lhs.count(), lhsEntryId, rhs.count(), rhsEntryId, 0, &result);
       
  3685     if (HR_FAILED(rv)) {
       
  3686         qWarning() << "Unable to compare entry IDs.";
       
  3687     }
       
  3688 
       
  3689     return (result != FALSE);
       
  3690 }
       
  3691 
       
  3692 QMessageFolder MapiSession::folder(QMessageManager::Error *error, const QMessageFolderId& id) const
       
  3693 {
       
  3694     QMessageFolder result;
       
  3695 
       
  3696     if(!id.isValid())
       
  3697     {
       
  3698         *error = QMessageManager::InvalidId;
       
  3699         return result;
       
  3700     }
       
  3701 
       
  3702 #ifdef _WIN32_WCE
       
  3703     MapiEntryId storeRecordKey(QMessageFolderIdPrivate::storeRecordKey(id));
       
  3704 #else
       
  3705     MapiRecordKey storeRecordKey(QMessageFolderIdPrivate::storeRecordKey(id));
       
  3706 #endif
       
  3707     MapiStorePtr mapiStore(findStore(error, QMessageAccountIdPrivate::from(storeRecordKey)));
       
  3708     if (*error != QMessageManager::NoError)
       
  3709         return result;
       
  3710 
       
  3711     // Find the root folder for this store
       
  3712     MapiFolderPtr storeRoot(mapiStore->rootFolder(error));
       
  3713     if (*error != QMessageManager::NoError)
       
  3714         return result;
       
  3715 
       
  3716     MapiEntryId entryId(QMessageFolderIdPrivate::entryId(id));
       
  3717     MapiFolderPtr folder = mapiStore->openFolder(error, entryId);
       
  3718     if (folder && (*error == QMessageManager::NoError)) {
       
  3719 #ifndef _WIN32_WCE
       
  3720         SizedSPropTagArray(3, columns) = {3, {PR_DISPLAY_NAME, PR_PARENT_ENTRYID, PR_RECORD_KEY}};
       
  3721 #else
       
  3722         SizedSPropTagArray(2, columns) = {2, {PR_DISPLAY_NAME, PR_PARENT_ENTRYID}};
       
  3723 #endif
       
  3724         SPropValue *properties(0);
       
  3725         ULONG count;
       
  3726         HRESULT rv = folder->folder()->GetProps(reinterpret_cast<LPSPropTagArray>(&columns), MAPI_UNICODE, &count, &properties);
       
  3727         if (HR_SUCCEEDED(rv)) {
       
  3728             QString name(QStringFromLpctstr(properties[0].Value.LPSZ));
       
  3729             MapiEntryId parentEntryId(properties[1].Value.bin.lpb, properties[1].Value.bin.cb);
       
  3730 #ifndef _WIN32_WCE
       
  3731             MapiRecordKey folderKey(properties[2].Value.bin.lpb, properties[2].Value.bin.cb);
       
  3732 #else
       
  3733             MapiRecordKey folderKey;
       
  3734 #endif
       
  3735 
       
  3736             MAPIFreeBuffer(properties);
       
  3737 
       
  3738             QMessageFolderId folderId(QMessageFolderIdPrivate::from(folderKey, storeRecordKey, entryId));
       
  3739 
       
  3740             if (equal(parentEntryId, storeRoot->entryId())) {
       
  3741                 // This folder is a direct child of the root folder
       
  3742                 QMessageAccountId accountId(QMessageAccountIdPrivate::from(storeRecordKey));
       
  3743                 return QMessageFolderPrivate::from(folderId, accountId, QMessageFolderId(), name, name);
       
  3744             }
       
  3745 
       
  3746             QStringList path;
       
  3747             path.append(name);
       
  3748 
       
  3749             QMessageFolderId parentId;
       
  3750             MapiEntryId ancestorEntryId(parentEntryId);
       
  3751             MapiFolderPtr ancestorFolder;
       
  3752 
       
  3753             // Iterate through ancestors towards the root
       
  3754             while ((ancestorFolder = mapiStore->openFolder(error, ancestorEntryId)) &&
       
  3755                    (ancestorFolder && (*error == QMessageManager::NoError))) {
       
  3756                 SPropValue *ancestorProperties(0);
       
  3757                 if (ancestorFolder->folder()->GetProps(reinterpret_cast<LPSPropTagArray>(&columns), MAPI_UNICODE, &count, &ancestorProperties) == S_OK) {
       
  3758 #ifndef _WIN32_WCE
       
  3759                     SPropValue &ancestorRecordKeyProp(ancestorProperties[2]);
       
  3760                     MapiRecordKey ancestorRecordKey(ancestorRecordKeyProp.Value.bin.lpb, ancestorRecordKeyProp.Value.bin.cb);
       
  3761                     bool reachedRoot(ancestorRecordKey == storeRoot->recordKey());
       
  3762 #else
       
  3763                     MapiRecordKey ancestorRecordKey;
       
  3764                     bool reachedRoot(equal(ancestorEntryId, storeRoot->entryId()));
       
  3765 #endif
       
  3766 
       
  3767                     if (ancestorEntryId == parentEntryId) {
       
  3768                         // This ancestor is the parent of the folder being retrieved, create a QMessageFolderId for the parent
       
  3769                         parentId = QMessageFolderIdPrivate::from(ancestorRecordKey, storeRecordKey, parentEntryId);
       
  3770                     }
       
  3771 
       
  3772                     SPropValue &entryIdProp(ancestorProperties[1]);
       
  3773                     ancestorEntryId = MapiEntryId(entryIdProp.Value.bin.lpb, entryIdProp.Value.bin.cb);
       
  3774 
       
  3775                     QString ancestorName(QStringFromLpctstr(ancestorProperties[0].Value.LPSZ));
       
  3776 
       
  3777                     MAPIFreeBuffer(ancestorProperties);
       
  3778 
       
  3779                     if (reachedRoot) {
       
  3780                         // Reached the root and have a complete path for the folder being retrieved
       
  3781                         QMessageAccountId accountId(QMessageAccountIdPrivate::from(storeRecordKey));
       
  3782                         return QMessageFolderPrivate::from(folderId, accountId, parentId, name, path.join("/"));
       
  3783                     }
       
  3784 
       
  3785                     // Prepare to consider next ancestor
       
  3786                     if (!ancestorName.isEmpty())
       
  3787                         path.prepend(ancestorName);
       
  3788                 } else {
       
  3789                     break;
       
  3790                 }
       
  3791             }
       
  3792         }
       
  3793     }
       
  3794 
       
  3795     // Failed to quickly retrieve the folder, fallback to an exhaustive search of all folders
       
  3796     result = mapiStore->folderFromId(error, id);
       
  3797     return result;
       
  3798 }
       
  3799 
       
  3800 QMessage MapiSession::message(QMessageManager::Error *error, const QMessageId& id) const
       
  3801 {
       
  3802     Q_UNUSED(error);
       
  3803 
       
  3804     QMessage result = QMessagePrivate::from(id);
       
  3805 
       
  3806     // How do we find what type of message this is?
       
  3807     result.setType(QMessage::Email);
       
  3808 
       
  3809     result.d_ptr->_elementsPresent.properties = 0;
       
  3810     result.d_ptr->_elementsPresent.recipients = 0;
       
  3811     result.d_ptr->_elementsPresent.body = 0;
       
  3812     result.d_ptr->_elementsPresent.attachments = 0;
       
  3813 
       
  3814     result.d_ptr->_modified = false;
       
  3815 
       
  3816     return result;
       
  3817 }
       
  3818 
       
  3819 bool MapiSession::updateMessageProperties(QMessageManager::Error *error, QMessage *msg) const
       
  3820 {
       
  3821     bool result(false);
       
  3822 
       
  3823     if (!msg->d_ptr->_elementsPresent.properties) {
       
  3824         bool isModified(msg->d_ptr->_modified);
       
  3825         msg->d_ptr->_elementsPresent.properties = 1;
       
  3826 
       
  3827         MapiStorePtr store;
       
  3828         IMessage *message = openMapiMessage(error, msg->id(), &store);
       
  3829         if (*error == QMessageManager::NoError) {
       
  3830 #ifndef _WIN32_WCE
       
  3831             const int np = 14;
       
  3832 #else
       
  3833             const int np = 12;
       
  3834 #endif
       
  3835             SizedSPropTagArray(np, msgCols) = {np, { PR_PARENT_ENTRYID,
       
  3836                                                      PR_MESSAGE_FLAGS,
       
  3837                                                      PR_MSG_STATUS,
       
  3838                                                      PR_MESSAGE_CLASS,
       
  3839                                                      PR_SENDER_NAME,
       
  3840                                                      PR_SENDER_EMAIL_ADDRESS,
       
  3841                                                      PR_CLIENT_SUBMIT_TIME,
       
  3842                                                      PR_MESSAGE_DELIVERY_TIME,
       
  3843                                                      PR_SUBJECT,
       
  3844                                                      PR_HASATTACH,
       
  3845                                                      PR_PRIORITY,
       
  3846 #ifndef _WIN32_WCE
       
  3847                                                      PR_MSG_EDITOR_FORMAT,
       
  3848                                                      PR_RTF_IN_SYNC,
       
  3849 #endif
       
  3850                                                      PR_MESSAGE_SIZE
       
  3851                                                      }};
       
  3852             ULONG count = 0;
       
  3853             LPSPropValue properties;
       
  3854             HRESULT rv = message->GetProps(reinterpret_cast<LPSPropTagArray>(&msgCols), MAPI_UNICODE, &count, &properties);
       
  3855             if (HR_SUCCEEDED(rv)) {
       
  3856                 QString senderName;
       
  3857                 QString senderAddress;
       
  3858                 QString messageClass;
       
  3859                 MapiEntryId parentEntryId;
       
  3860 
       
  3861                 QMessage::StatusFlags flags(0);
       
  3862 
       
  3863                 //assumption that stores support only a single message type
       
  3864                 msg->d_ptr->_type = store->types() & QMessage::Sms ? QMessage::Sms : QMessage::Email;
       
  3865 
       
  3866                 for (ULONG n = 0; n < count; ++n) {
       
  3867                     SPropValue &prop(properties[n]);
       
  3868 
       
  3869                     switch (prop.ulPropTag) {
       
  3870                     case PR_MESSAGE_FLAGS:
       
  3871                         if (prop.Value.ul & MSGFLAG_READ) {
       
  3872                             flags |= QMessage::Read;
       
  3873                         }
       
  3874                         break;
       
  3875                     case PR_PARENT_ENTRYID:
       
  3876                         parentEntryId = MapiEntryId(prop.Value.bin.lpb, prop.Value.bin.cb);
       
  3877                         break;
       
  3878                     case PR_MSG_STATUS:
       
  3879                         if (prop.Value.l & (MSGSTATUS_DELMARKED | MSGSTATUS_REMOTE_DELETE)) {
       
  3880                             flags |= QMessage::Removed;
       
  3881                         }
       
  3882 #ifdef _WIN32_WCE
       
  3883                         if (prop.Value.l & MSGSTATUS_HAS_PR_BODY) {
       
  3884                             msg->d_ptr->_contentFormat = EDITOR_FORMAT_PLAINTEXT;
       
  3885 #if(_WIN32_WCE > 0x501)
       
  3886                         } else if (prop.Value.l & MSGSTATUS_HAS_PR_BODY_HTML) {
       
  3887                             msg->d_ptr->_contentFormat = EDITOR_FORMAT_HTML;
       
  3888 #endif
       
  3889                         } else if (prop.Value.l & MSGSTATUS_HAS_PR_CE_MIME_TEXT) {
       
  3890                             // This is how MS providers store HTML, as per http://msdn.microsoft.com/en-us/library/bb446140.aspx
       
  3891                             msg->d_ptr->_contentFormat = EDITOR_FORMAT_MIME;
       
  3892                         }
       
  3893 #endif
       
  3894                         break;
       
  3895                     case PR_MESSAGE_CLASS:
       
  3896                         messageClass = QStringFromLpctstr(prop.Value.LPSZ);
       
  3897                         break;
       
  3898                     case PR_SENDER_NAME:
       
  3899                         senderName = QStringFromLpctstr(prop.Value.LPSZ);
       
  3900                         break;
       
  3901                     case PR_SENDER_EMAIL_ADDRESS:
       
  3902                         senderAddress = QStringFromLpctstr(prop.Value.LPSZ);
       
  3903                         break;
       
  3904                     case PR_CLIENT_SUBMIT_TIME:
       
  3905                         msg->setDate(fromFileTime(prop.Value.ft));
       
  3906                         break;
       
  3907                     case PR_MESSAGE_DELIVERY_TIME:
       
  3908                         msg->setReceivedDate(fromFileTime(prop.Value.ft));
       
  3909                         break;
       
  3910                     case PR_SUBJECT:
       
  3911                         msg->setSubject(QStringFromLpctstr(prop.Value.LPSZ));
       
  3912                         break;
       
  3913                     case PR_HASATTACH:
       
  3914                         msg->d_ptr->_hasAttachments = (prop.Value.b != FALSE);
       
  3915                         if (prop.Value.b) {
       
  3916                             flags |= QMessage::HasAttachments;
       
  3917                         }
       
  3918                         break;
       
  3919                     case PR_PRIORITY:
       
  3920                         if (prop.Value.l == PRIO_URGENT) {
       
  3921                             msg->setPriority(QMessage::HighPriority);
       
  3922                         } else if (prop.Value.l == PRIO_NONURGENT) {
       
  3923                             msg->setPriority(QMessage::LowPriority);
       
  3924                         } else {
       
  3925                             msg->setPriority(QMessage::NormalPriority);
       
  3926                         }
       
  3927                         break;
       
  3928 #ifndef _WIN32_WCE
       
  3929                     case PR_MSG_EDITOR_FORMAT:
       
  3930                         msg->d_ptr->_contentFormat = prop.Value.l;
       
  3931                         break;
       
  3932                     case PR_RTF_IN_SYNC:
       
  3933                         msg->d_ptr->_rtfInSync = (prop.Value.b != FALSE);;
       
  3934                         break;
       
  3935 #endif
       
  3936                     case PR_MESSAGE_SIZE:
       
  3937                         QMessagePrivate::setSize(*msg, prop.Value.l);
       
  3938                         break;
       
  3939                     default:
       
  3940                         break;
       
  3941                     }
       
  3942                 }
       
  3943 
       
  3944                 msg->setStatus(flags);
       
  3945                 msg->setParentAccountId(store->id());
       
  3946 
       
  3947                 if (!senderName.isEmpty() || !senderAddress.isEmpty()) {
       
  3948                     msg->setFrom(createAddress(senderName, senderAddress));
       
  3949                     QMessagePrivate::setSenderName(*msg, senderName);
       
  3950                 }
       
  3951 
       
  3952                 if (!parentEntryId.isEmpty()) {
       
  3953                     QMessagePrivate::setStandardFolder(*msg, store->standardFolder(parentEntryId));
       
  3954 
       
  3955                     // MAPI does not support the notion of incoming/outgoing messages.  Instead, we
       
  3956                     // will treat Outgoing as meaning 'in the Sent folder'
       
  3957                     msg->setStatus(QMessage::Incoming, (msg->standardFolder() != QMessage::SentFolder));
       
  3958 
       
  3959                     MapiFolderPtr parentFolder(store->openFolder(error, parentEntryId));
       
  3960                     if (parentFolder) {
       
  3961                         QMessagePrivate::setParentFolderId(*msg, parentFolder->id());
       
  3962                     }
       
  3963                 }
       
  3964 
       
  3965                 if (!isModified) {
       
  3966                     msg->d_ptr->_modified = false;
       
  3967                 }
       
  3968                 result = true;
       
  3969 
       
  3970                 MAPIFreeBuffer(properties);
       
  3971             } else {
       
  3972                 *error = QMessageManager::ContentInaccessible;
       
  3973             }
       
  3974 
       
  3975             mapiRelease(message);
       
  3976         }
       
  3977     }
       
  3978 
       
  3979     return result;
       
  3980 }
       
  3981 
       
  3982 bool MapiSession::updateMessageRecipients(QMessageManager::Error *error, QMessage *msg) const
       
  3983 {
       
  3984     bool result(false);
       
  3985 
       
  3986     if (!msg->d_ptr->_elementsPresent.recipients) {
       
  3987         bool isModified(msg->d_ptr->_modified);
       
  3988         msg->d_ptr->_elementsPresent.recipients = 1;
       
  3989 
       
  3990         IMessage *message = openMapiMessage(error, msg->id());
       
  3991         if (*error == QMessageManager::NoError) {
       
  3992             // Extract the recipients for the message
       
  3993             IMAPITable *recipientsTable(0);
       
  3994             HRESULT rv = message->GetRecipientTable(0, &recipientsTable);
       
  3995             if (HR_SUCCEEDED(rv)) {
       
  3996                 QMessageAddressList to;
       
  3997                 QMessageAddressList cc;
       
  3998                 QMessageAddressList bcc;
       
  3999 
       
  4000 #ifndef _WIN32_WCE
       
  4001                 SizedSPropTagArray(3, rcpCols) = {3, { PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_RECIPIENT_TYPE}};
       
  4002 
       
  4003                 QueryAllRows qar(recipientsTable, reinterpret_cast<LPSPropTagArray>(&rcpCols), 0, 0);
       
  4004 #else
       
  4005                 // CE does not support SetColumns on recipient tables
       
  4006                 QueryAllRows qar(recipientsTable, 0, 0, 0, false);
       
  4007 #endif
       
  4008 
       
  4009                 while(qar.query()) {
       
  4010                     for (uint n = 0; n < qar.rows()->cRows; ++n) {
       
  4011                         QString name;
       
  4012                         QString address;
       
  4013                         LONG type(0);
       
  4014 
       
  4015                         SPropValue *props = qar.rows()->aRow[n].lpProps;
       
  4016 #ifndef _WIN32_WCE
       
  4017                         if (props[0].ulPropTag == PR_DISPLAY_NAME)
       
  4018                             name = QStringFromLpctstr(props[0].Value.LPSZ);
       
  4019                         if (props[1].ulPropTag == PR_EMAIL_ADDRESS)
       
  4020                             address = QStringFromLpctstr(props[1].Value.LPSZ);
       
  4021                         if (props[2].ulPropTag == PR_RECIPIENT_TYPE)
       
  4022                             type = props[2].Value.l;
       
  4023 #else
       
  4024                         for (uint i = 0; i < qar.rows()->aRow[n].cValues; ++i) {
       
  4025                             if (props[i].ulPropTag == PR_DISPLAY_NAME) {
       
  4026                                 name = QStringFromLpctstr(props[i].Value.LPSZ);
       
  4027                             } else if (props[i].ulPropTag == PR_EMAIL_ADDRESS) {
       
  4028                                 address = QStringFromLpctstr(props[i].Value.LPSZ);
       
  4029                             } else if (props[i].ulPropTag == PR_RECIPIENT_TYPE){
       
  4030                                 type = props[i].Value.l;
       
  4031                             }
       
  4032                         }
       
  4033 #endif
       
  4034 
       
  4035                         if (!name.isEmpty() || !address.isEmpty()) {
       
  4036                             QMessageAddressList *list = 0;
       
  4037 
       
  4038                             switch (type) {
       
  4039                             case MAPI_TO:
       
  4040                                 list = &to;
       
  4041                                 break;
       
  4042                             case MAPI_CC:
       
  4043                                 list = &cc;
       
  4044                                 break;
       
  4045                             case MAPI_BCC:
       
  4046                                 list = &bcc;
       
  4047                                 break;
       
  4048                             default:
       
  4049                                 break;
       
  4050                             }
       
  4051 
       
  4052                             if (list) {
       
  4053                                 list->append(createAddress(name, address));
       
  4054                             }
       
  4055                         }
       
  4056                     }
       
  4057                 }
       
  4058 
       
  4059                 if (!to.isEmpty()) {
       
  4060                     msg->setTo(to);
       
  4061                 }
       
  4062                 if (!cc.isEmpty()) {
       
  4063                     msg->setCc(cc);
       
  4064                 }
       
  4065                 if (!bcc.isEmpty()) {
       
  4066                     msg->setBcc(bcc);
       
  4067                 }
       
  4068 
       
  4069                 if (!isModified) {
       
  4070                     msg->d_ptr->_modified = false;
       
  4071                 }
       
  4072 
       
  4073                 if (qar.error() != QMessageManager::NoError) {
       
  4074                     *error = qar.error();
       
  4075                     result = false;
       
  4076                 } else {
       
  4077                     result = true;
       
  4078                 }
       
  4079 
       
  4080                 mapiRelease(recipientsTable);
       
  4081             } else {
       
  4082 #ifdef _WIN32_WCE
       
  4083                 if (rv == MAPI_E_NO_RECIPIENTS) {
       
  4084                     updateMessageProperties(error, msg);
       
  4085                     if (*error == QMessageManager::NoError) {
       
  4086                         if (msg->d_ptr->_contentFormat == EDITOR_FORMAT_MIME) {
       
  4087                             // The recipient info can be extracted from the MIME body
       
  4088                             updateMessageBody(error, msg);
       
  4089                         }
       
  4090                     }
       
  4091                 } else {
       
  4092 #endif
       
  4093                     *error = QMessageManager::ContentInaccessible;
       
  4094 #ifdef _WIN32_WCE
       
  4095                 }
       
  4096 #endif
       
  4097             }
       
  4098 
       
  4099             mapiRelease(message);
       
  4100         }
       
  4101     }
       
  4102 
       
  4103     return result;
       
  4104 }
       
  4105 
       
  4106 bool MapiSession::updateMessageBody(QMessageManager::Error *error, QMessage *msg) const
       
  4107 {
       
  4108     bool result(false);
       
  4109 
       
  4110     // TODO: Signed messages are stored with the body as an attachment; we need to implement this
       
  4111 
       
  4112     if (!msg->d_ptr->_elementsPresent.body) {
       
  4113         if (!msg->d_ptr->_elementsPresent.properties) {
       
  4114             // We need the properties before we can fetch the body
       
  4115             if (!updateMessageProperties(error, msg)) {
       
  4116                 return false;
       
  4117             }
       
  4118         }
       
  4119 
       
  4120         bool isModified(msg->d_ptr->_modified);
       
  4121         msg->d_ptr->_elementsPresent.body = 1;
       
  4122 
       
  4123         QByteArray messageBody;
       
  4124         QByteArray bodySubType;
       
  4125 
       
  4126         MapiStorePtr store;
       
  4127         IMessage *message = openMapiMessage(error, msg->id(), &store);
       
  4128         if (*error == QMessageManager::NoError) {
       
  4129             //SMS body stored in subject on CEMAPI
       
  4130             if (store->types() & QMessage::Sms) {
       
  4131                 messageBody.append(reinterpret_cast<const char*>(msg->subject().utf16()),msg->subject().count()*sizeof(quint16));
       
  4132                 bodySubType = "plain";
       
  4133             } else {
       
  4134                 IStream *is(0);
       
  4135                 bool asciiData(false);
       
  4136                 LONG contentFormat(msg->d_ptr->_contentFormat);
       
  4137 
       
  4138                 if (contentFormat == EDITOR_FORMAT_DONTKNOW) {
       
  4139                     // Attempt to read HTML first
       
  4140                     contentFormat = EDITOR_FORMAT_HTML;
       
  4141                 }
       
  4142                 if (contentFormat == EDITOR_FORMAT_PLAINTEXT) {
       
  4143 #ifdef _WIN32_WCE
       
  4144                     ULONG tags[] = { PR_BODY, PR_BODY_W, PR_BODY_A };
       
  4145 #else
       
  4146                     ULONG tags[] = { PR_BODY };
       
  4147 #endif
       
  4148                     const int n = sizeof(tags)/sizeof(tags[0]);
       
  4149                     for (int i = 0; i < n; ++i) {
       
  4150                         HRESULT rv = message->OpenProperty(tags[i], &IID_IStream, STGM_READ, 0, (IUnknown**)&is);
       
  4151                         if (HR_SUCCEEDED(rv)) {
       
  4152                             messageBody = readStream(error, is);
       
  4153                             bodySubType = "plain";
       
  4154                             if (i == 2) {
       
  4155                                 asciiData = true;
       
  4156                             }
       
  4157                             break;
       
  4158                         } 
       
  4159                     }
       
  4160                     if (messageBody.isEmpty()) {
       
  4161                         qWarning() << "Unable to open PR_BODY!";
       
  4162                     }
       
  4163                 } else if (contentFormat == EDITOR_FORMAT_HTML) {
       
  4164                     // See if there is a body HTML property
       
  4165                     // Correct variants discussed at http://blogs.msdn.com/raffael/archive/2008/09/08/mapi-on-windows-mobile-6-programmatically-retrieve-mail-body-sample-code.aspx
       
  4166 #if(_WIN32_WCE > 0x501)
       
  4167                     ULONG tags[] = { PR_BODY_HTML, PR_BODY_HTML_W, PR_BODY_HTML_A };
       
  4168 #else
       
  4169                     ULONG tags[] = { PR_BODY_HTML };
       
  4170 #endif
       
  4171                     const int n = sizeof(tags)/sizeof(tags[0]);
       
  4172                     for (int i = 0; i < n; ++i) {
       
  4173                         HRESULT rv = message->OpenProperty(tags[i], &IID_IStream, STGM_READ, 0, (IUnknown**)&is);
       
  4174                         if (HR_SUCCEEDED(rv)) {
       
  4175                             messageBody = readStream(error, is);
       
  4176                             bodySubType = "html";
       
  4177                             if (i == 2) {
       
  4178                                 asciiData = true;
       
  4179                             }
       
  4180                             break;
       
  4181                         }
       
  4182                     }
       
  4183                     if (messageBody.isEmpty()) {
       
  4184 #ifdef _WIN32_WCE
       
  4185                         qWarning() << "Unable to open PR_BODY_HTML!";
       
  4186 #else
       
  4187                         // We couldn't get HTML; try RTF
       
  4188                         contentFormat = EDITOR_FORMAT_DONTKNOW;
       
  4189 #endif
       
  4190                     }
       
  4191 #ifdef _WIN32_WCE
       
  4192                 } else if (contentFormat == EDITOR_FORMAT_MIME) {
       
  4193                     // MIME format is only used on mobile
       
  4194                     HRESULT rv = message->OpenProperty(PR_CE_MIME_TEXT, &IID_IStream, STGM_READ, 0, (IUnknown**)&is);
       
  4195                     if (HR_SUCCEEDED(rv)) {
       
  4196                         messageBody = readStream(error, is);
       
  4197 
       
  4198                         QMailMessage mimeMsg(QMailMessage::fromRfc2822(messageBody));
       
  4199 
       
  4200                         // Extract the recipient info from the message headers
       
  4201                         QMessageAddressList addresses;
       
  4202                         foreach (const QMailAddress &addr, mimeMsg.to()) {
       
  4203                             QString addressString(addr.address());
       
  4204                             if(!addressString.isEmpty())
       
  4205                                 addresses.append(QMessageAddress(QMessageAddress::Email, addressString));
       
  4206                         }
       
  4207                         if (!addresses.isEmpty()) {
       
  4208                             msg->setTo(addresses);
       
  4209                         }
       
  4210 
       
  4211                         addresses.clear();
       
  4212                         foreach (const QMailAddress &addr, mimeMsg.cc()) {
       
  4213                             QString addressString(addr.address());
       
  4214                             if(!addressString.isEmpty())
       
  4215                                 addresses.append(QMessageAddress(QMessageAddress::Email, addressString));
       
  4216                         }
       
  4217                         if (!addresses.isEmpty()) {
       
  4218                             msg->setCc(addresses);
       
  4219                         }
       
  4220 
       
  4221                         addresses.clear();
       
  4222                         foreach (const QMailAddress &addr, mimeMsg.bcc()) {
       
  4223                             QString addressString(addr.address());
       
  4224                             addresses.append(QMessageAddress(QMessageAddress::Email, addressString));
       
  4225                         }
       
  4226                         if (!addresses.isEmpty()) {
       
  4227                             msg->setBcc(addresses);
       
  4228                         }
       
  4229 
       
  4230                         // Extract the text of the message body
       
  4231                         if (mimeMsg.multipartType() == QMailMessage::MultipartNone) {
       
  4232                             if (mimeMsg.contentType().type().toLower() == "text") {
       
  4233                                 QByteArray subType(mimeMsg.contentType().subType().toLower());
       
  4234                                 if ((subType == "plain") || (subType == "html") || (subType == "rtf")) {
       
  4235                                     messageBody = QTextCodec::codecForName("utf-16")->fromUnicode(mimeMsg.body().data());
       
  4236                                     bodySubType = subType;
       
  4237                                 }
       
  4238                             }
       
  4239                         } else if ((mimeMsg.multipartType() == QMailMessage::MultipartAlternative) ||
       
  4240                                    (mimeMsg.multipartType() == QMailMessage::MultipartMixed) ||
       
  4241                                    (mimeMsg.multipartType() == QMailMessage::MultipartRelated)) {
       
  4242                             // For multipart/related, just try the first part
       
  4243                             int maxParts(mimeMsg.multipartType() == QMailMessage::MultipartRelated ? 1 : mimeMsg.partCount());
       
  4244                             for (int i = 0; i < maxParts; ++i) {
       
  4245                                 const QMailMessagePart &part(mimeMsg.partAt(i));
       
  4246 
       
  4247                                 if (part.contentType().type().toLower() == "text") {
       
  4248                                     QByteArray subType(part.contentType().subType().toLower());
       
  4249                                     if ((subType == "plain") || (subType == "html") || (subType == "rtf")) {
       
  4250                                         messageBody = QTextCodec::codecForName("utf-16")->fromUnicode(part.body().data());
       
  4251                                         bodySubType = subType;
       
  4252                                         break;
       
  4253                                     }
       
  4254                                 }
       
  4255                             }
       
  4256                         }
       
  4257                     } else {
       
  4258                         qWarning() << "Unable to open PR_CE_MIME_TEXT!";
       
  4259                     }
       
  4260 #endif
       
  4261                 }
       
  4262 
       
  4263 #ifndef _WIN32_WCE // RTF not supported on mobile
       
  4264                 if (bodySubType.isEmpty()) {
       
  4265                     if (!msg->d_ptr->_rtfInSync) {
       
  4266                         // See if we need to sync the RTF
       
  4267                         if (!store->supports(STORE_RTF_OK)) {
       
  4268                             BOOL updated(FALSE);
       
  4269                             HRESULT rv = RTFSync(message, RTF_SYNC_BODY_CHANGED, &updated);
       
  4270                             if (HR_SUCCEEDED(rv)) {
       
  4271                                 if (updated) {
       
  4272                                     if (HR_FAILED(message->SaveChanges(0))) {
       
  4273                                         qWarning() << "Unable to save changes after synchronizing RTF.";
       
  4274                                     }
       
  4275                                 }
       
  4276                             } else {
       
  4277                                 qWarning() << "Unable to synchronize RTF.";
       
  4278                             }
       
  4279                         }
       
  4280                     }
       
  4281 
       
  4282                     // Either the body is in RTF, or we need to read the RTF to know that it is text...
       
  4283                     HRESULT rv = message->OpenProperty(PR_RTF_COMPRESSED, &IID_IStream, STGM_READ, 0, (IUnknown**)&is);
       
  4284                     if (HR_SUCCEEDED(rv)) {
       
  4285                         IStream *decompressor(0);
       
  4286                         if (WrapCompressedRTFStream(is, 0, &decompressor) == S_OK) {
       
  4287                             ULONG bytes = 0;
       
  4288                             char buffer[BUFSIZ] = { 0 };
       
  4289                             do {
       
  4290                                 decompressor->Read(buffer, BUFSIZ, &bytes);
       
  4291                                 messageBody.append(buffer, bytes);
       
  4292                             } while (bytes == BUFSIZ);
       
  4293 
       
  4294                             decompressor->Release();
       
  4295 
       
  4296                             // RTF is stored in ASCII
       
  4297                             asciiData = true;
       
  4298 
       
  4299                             if (contentFormat == EDITOR_FORMAT_DONTKNOW) {
       
  4300                                 // Inspect the message content to see if we can tell what is in it
       
  4301                                 if (!messageBody.isEmpty()) {
       
  4302                                     QByteArray initialText(messageBody.left(256));
       
  4303                                     if (initialText.indexOf("\\fromtext") != -1) {
       
  4304                                         // This message originally contained text
       
  4305                                         contentFormat = EDITOR_FORMAT_PLAINTEXT;
       
  4306 
       
  4307                                         // See if we can get the plain text version instead
       
  4308                                         IStream *ts(0);
       
  4309                                         rv = message->OpenProperty(PR_BODY, &IID_IStream, STGM_READ, 0, (IUnknown**)&ts);
       
  4310                                         if (HR_SUCCEEDED(rv)) {
       
  4311                                             messageBody = readStream(error, ts);
       
  4312                                             bodySubType = "plain";
       
  4313                                             asciiData = false;
       
  4314 
       
  4315                                             ts->Release();
       
  4316                                         } else {
       
  4317                                             qWarning() << "Unable to prefer plain text body.";
       
  4318                                         }
       
  4319                                     } else if (initialText.indexOf("\\fromhtml1") != -1) {
       
  4320                                         // This message originally contained text
       
  4321                                         contentFormat = EDITOR_FORMAT_HTML;
       
  4322                                     }
       
  4323                                 }
       
  4324                             }
       
  4325 
       
  4326                             if (bodySubType.isEmpty()) {
       
  4327                                 if (contentFormat == EDITOR_FORMAT_PLAINTEXT) {
       
  4328                                     messageBody = extractPlainText(messageBody);
       
  4329                                     bodySubType = "plain";
       
  4330                                 } else if (contentFormat == EDITOR_FORMAT_HTML) {
       
  4331                                     messageBody = extractHtml(messageBody);
       
  4332                                     bodySubType = "html";
       
  4333                                 } else {
       
  4334                                     // I guess we must just have RTF
       
  4335                                     bodySubType = "rtf";
       
  4336                                 }
       
  4337                             }
       
  4338                         } else {
       
  4339                             *error = QMessageManager::ContentInaccessible;
       
  4340                             qWarning() << "Unable to decompress RTF";
       
  4341                             bodySubType = "plain";
       
  4342                         }
       
  4343                     }
       
  4344                 }
       
  4345 #endif
       
  4346                 if (bodySubType.isEmpty()) {
       
  4347                     qWarning() << "Unable to locate body for message.";
       
  4348                 }
       
  4349 
       
  4350                 mapiRelease(is);
       
  4351 
       
  4352                 if (asciiData) {
       
  4353                     // Convert the ASCII content back to UTF-16
       
  4354                     messageBody = QTextCodec::codecForName("utf-16")->fromUnicode(decodeContent(messageBody, "Latin-1"));
       
  4355                 }
       
  4356             }
       
  4357 
       
  4358             if (*error == QMessageManager::NoError) {
       
  4359                 QMessageContentContainerPrivate *messageContainer(((QMessageContentContainer *)(msg))->d_ptr);
       
  4360 
       
  4361                 bool bodyDownloaded = true;
       
  4362 #ifdef _WIN32_WCE
       
  4363                 ULONG status = 0;
       
  4364                 if(getMapiProperty(message,PR_MSG_STATUS,&status)) {
       
  4365                     bodyDownloaded = !((status & MSGSTATUS_HEADERONLY) || (status & MSGSTATUS_PARTIAL));
       
  4366                 }
       
  4367 #else
       
  4368                 //TODO windows
       
  4369 #endif
       
  4370 
       
  4371                 if (!msg->d_ptr->_hasAttachments) {
       
  4372                     // Make the body the entire content of the message
       
  4373                     messageContainer->setContent(messageBody, QByteArray("text"), bodySubType, QByteArray("utf-16"));
       
  4374                     msg->d_ptr->_bodyId = messageContainer->bodyContentId();
       
  4375                     messageContainer->_available = bodyDownloaded;
       
  4376                 } else {
       
  4377                     // Add the message body data as the first part
       
  4378                     QMessageContentContainer bodyPart;
       
  4379                     {
       
  4380                         QMessageContentContainerPrivate *bodyContainer(((QMessageContentContainer *)(&bodyPart))->d_ptr);
       
  4381                         bodyContainer->setContent(messageBody, QByteArray("text"), bodySubType, QByteArray("utf-16"));
       
  4382                         bodyContainer->_available = bodyDownloaded;
       
  4383                     }
       
  4384 
       
  4385                     messageContainer->setContentType(QByteArray("multipart"), QByteArray("mixed"), QByteArray());
       
  4386                     msg->d_ptr->_bodyId = messageContainer->appendContent(bodyPart);
       
  4387                 }
       
  4388 
       
  4389                 if (!isModified) {
       
  4390                     msg->d_ptr->_modified = false;
       
  4391                 }
       
  4392                 result = true;
       
  4393             }
       
  4394 
       
  4395             mapiRelease(message);
       
  4396         }
       
  4397     }
       
  4398 
       
  4399     return result;
       
  4400 }
       
  4401 
       
  4402 bool MapiSession::updateMessageAttachments(QMessageManager::Error *error, QMessage *msg) const
       
  4403 {
       
  4404     bool result(false);
       
  4405 
       
  4406     if (!msg->d_ptr->_elementsPresent.attachments) {
       
  4407         if (!msg->d_ptr->_elementsPresent.properties) {
       
  4408             // We need the properties before we can fetch the attachments
       
  4409             if (!updateMessageProperties(error, msg)) {
       
  4410                 return false;
       
  4411             }
       
  4412         }
       
  4413 
       
  4414         bool isModified(msg->d_ptr->_modified);
       
  4415         msg->d_ptr->_elementsPresent.attachments = 1;
       
  4416 
       
  4417         if (msg->d_ptr->_hasAttachments) {
       
  4418             IMessage *message = openMapiMessage(error, msg->id());
       
  4419             if (*error == QMessageManager::NoError) {
       
  4420                 QMessageContentContainerPrivate *messageContainer(((QMessageContentContainer *)(msg))->d_ptr);
       
  4421 
       
  4422                 // Find any attachments for this message
       
  4423                 IMAPITable *attachmentsTable(0);
       
  4424                 HRESULT rv = message->GetAttachmentTable(0, &attachmentsTable);
       
  4425                 if (HR_SUCCEEDED(rv)) {
       
  4426                     // Find the properties of these attachments
       
  4427                     SizedSPropTagArray(7, attCols) = {7, { PR_ATTACH_NUM,
       
  4428                                                            PR_ATTACH_EXTENSION,
       
  4429                                                            PR_ATTACH_LONG_FILENAME,
       
  4430                                                            PR_ATTACH_FILENAME,
       
  4431                                                            PR_ATTACH_CONTENT_ID,
       
  4432                                                            PR_ATTACH_SIZE,
       
  4433                                                            PR_RENDERING_POSITION }};
       
  4434 
       
  4435                     QueryAllRows qar(attachmentsTable, reinterpret_cast<LPSPropTagArray>(&attCols), NULL, NULL);
       
  4436                     while(qar.query()) {
       
  4437                         for (uint n = 0; n < qar.rows()->cRows; ++n) {
       
  4438                             LONG number(0);
       
  4439                             QString extension;
       
  4440                             QString filename;
       
  4441                             QString contentId;
       
  4442                             LONG size(0);
       
  4443                             LONG renderingPosition(0);
       
  4444 
       
  4445                             // If not available, the output tag will not match our requested content tag
       
  4446                             if (qar.rows()->aRow[n].lpProps[0].ulPropTag == PR_ATTACH_NUM) {
       
  4447                                 number = qar.rows()->aRow[n].lpProps[0].Value.l;
       
  4448                             } else {
       
  4449                                 // We can't access this part...
       
  4450                                 continue;
       
  4451                             }
       
  4452 
       
  4453                             if (qar.rows()->aRow[n].lpProps[1].ulPropTag == PR_ATTACH_EXTENSION) {
       
  4454                                 extension = QStringFromLpctstr(qar.rows()->aRow[n].lpProps[1].Value.LPSZ);
       
  4455                             }
       
  4456                             if (qar.rows()->aRow[n].lpProps[2].ulPropTag == PR_ATTACH_LONG_FILENAME) {
       
  4457                                 filename = QStringFromLpctstr(qar.rows()->aRow[n].lpProps[2].Value.LPSZ);
       
  4458                             } else if (qar.rows()->aRow[n].lpProps[3].ulPropTag == PR_ATTACH_FILENAME) {
       
  4459                                 filename = QStringFromLpctstr(qar.rows()->aRow[n].lpProps[3].Value.LPSZ);
       
  4460                             }
       
  4461                             if (qar.rows()->aRow[n].lpProps[4].ulPropTag == PR_ATTACH_CONTENT_ID) {
       
  4462                                 contentId = QStringFromLpctstr(qar.rows()->aRow[n].lpProps[4].Value.LPSZ);
       
  4463                             }
       
  4464                             if (qar.rows()->aRow[n].lpProps[5].ulPropTag == PR_ATTACH_SIZE) {
       
  4465                                 // Increase the size estimate by a third to allow for transfer encoding
       
  4466                                 size = (qar.rows()->aRow[n].lpProps[5].Value.l * 4 / 3);
       
  4467                             }
       
  4468                             if (qar.rows()->aRow[n].lpProps[6].ulPropTag == PR_RENDERING_POSITION) {
       
  4469                                 renderingPosition = qar.rows()->aRow[n].lpProps[6].Value.l;
       
  4470                             }
       
  4471 
       
  4472                             WinHelpers::AttachmentLocator locator(msg->id(), number);
       
  4473 
       
  4474                             QMessageContentContainer attachment(WinHelpers::fromLocator(locator));
       
  4475                             QMessageContentContainerPrivate *container(((QMessageContentContainer *)(&attachment))->d_ptr);
       
  4476 
       
  4477                             if (!extension.isEmpty()) {
       
  4478                                 QByteArray contentType(contentTypeFromExtension(extension));
       
  4479                                 if (!contentType.isEmpty()) {
       
  4480                                     int index = contentType.indexOf('/');
       
  4481                                     if (index != -1) {
       
  4482                                         container->setContentType(contentType.left(index), contentType.mid(index + 1), QByteArray());
       
  4483                                     } else {
       
  4484                                         container->setContentType(contentType, QByteArray(), QByteArray());
       
  4485                                     }
       
  4486                                 }
       
  4487                             }
       
  4488                             if (!contentId.isEmpty()) {
       
  4489                                 container->setHeaderField("Content-ID", contentId.toAscii());
       
  4490                             }
       
  4491                             if (renderingPosition == -1) {
       
  4492                                 QByteArray value("attachment");
       
  4493                                 if (!filename.isEmpty()) {
       
  4494                                     value.append("; filename=" + filename.toAscii());
       
  4495                                 }
       
  4496                                 container->setHeaderField("Content-Disposition", value);
       
  4497                             }
       
  4498 
       
  4499                             container->_name = filename.toAscii();
       
  4500                             container->_size = size;
       
  4501                             container->_available = haveAttachmentData(error,msg->id(),number);
       
  4502 
       
  4503                             messageContainer->appendContent(attachment);
       
  4504                             if (!isModified) {
       
  4505                                 msg->d_ptr->_modified = false;
       
  4506                             }
       
  4507                         }
       
  4508                     }
       
  4509 
       
  4510                     if (qar.error() != QMessageManager::NoError) {
       
  4511                         *error = qar.error();
       
  4512                     } else {
       
  4513                         result = true;
       
  4514                     }
       
  4515 
       
  4516                     mapiRelease(attachmentsTable);
       
  4517                 } else {
       
  4518                     *error = QMessageManager::ContentInaccessible;
       
  4519                     qWarning() << "Unable to access attachments table.";
       
  4520                 }
       
  4521 
       
  4522                 mapiRelease(message);
       
  4523             }
       
  4524         }
       
  4525     }
       
  4526 
       
  4527     return result;
       
  4528 }
       
  4529 
       
  4530 bool MapiSession::haveAttachmentData(QMessageManager::Error *error, const QMessageId& id, ULONG number) const
       
  4531 {
       
  4532     bool result = false;
       
  4533 
       
  4534     IMessage *message = openMapiMessage(error, id);
       
  4535     if (*error == QMessageManager::NoError) {
       
  4536         LPATTACH attachment(0);
       
  4537         HRESULT rv = message->OpenAttach(number, 0, 0, &attachment);
       
  4538         if (HR_SUCCEEDED(rv)) {
       
  4539             IStream *is(0);
       
  4540             rv = attachment->OpenProperty(PR_ATTACH_DATA_BIN, &IID_IStream, STGM_READ, 0, (IUnknown**)&is);
       
  4541             if (HR_SUCCEEDED(rv)) {
       
  4542                 result = streamSize(error, is) > 0;
       
  4543                 mapiRelease(is);
       
  4544             }
       
  4545             mapiRelease(attachment);
       
  4546         } else {
       
  4547             qWarning() << "Unable to open attachment:" << number;
       
  4548         }
       
  4549 
       
  4550         mapiRelease(message);
       
  4551     }
       
  4552 
       
  4553     return result;
       
  4554 }
       
  4555 
       
  4556 
       
  4557 QByteArray MapiSession::attachmentData(QMessageManager::Error *error, const QMessageId& id, ULONG number) const
       
  4558 {
       
  4559     QByteArray result;
       
  4560 
       
  4561     IMessage *message = openMapiMessage(error, id);
       
  4562     if (*error == QMessageManager::NoError) {
       
  4563         LPATTACH attachment(0);
       
  4564         HRESULT rv = message->OpenAttach(number, 0, 0, &attachment);
       
  4565         if (HR_SUCCEEDED(rv)) {
       
  4566             IStream *is(0);
       
  4567             rv = attachment->OpenProperty(PR_ATTACH_DATA_BIN, &IID_IStream, STGM_READ, 0, (IUnknown**)&is);
       
  4568             if (HR_SUCCEEDED(rv)) {
       
  4569                 result = readStream(error, is);
       
  4570                 mapiRelease(is);
       
  4571             }
       
  4572 
       
  4573             mapiRelease(attachment);
       
  4574         } else {
       
  4575             qWarning() << "Unable to open attachment:" << number;
       
  4576         }
       
  4577 
       
  4578         mapiRelease(message);
       
  4579     }
       
  4580 
       
  4581     return result;
       
  4582 }
       
  4583 
       
  4584 QMessageIdList MapiSession::queryMessages(QMessageManager::Error *error, const QMessageFilter &filter, const QMessageSortOrder &sortOrder, uint limit, uint offset, const QString &body, QMessageDataComparator::MatchFlags matchFlags) const
       
  4585 {
       
  4586     if (!filter.isSupported()) {
       
  4587         *error = QMessageManager::ConstraintFailure;
       
  4588         return QMessageIdList();
       
  4589     }
       
  4590 
       
  4591     if (QMessageFilterPrivate::isNonMatching(filter)) { // Avoid unnecessary preprocessing/evaluation
       
  4592         return QMessageIdList();
       
  4593     }
       
  4594 
       
  4595     QList<FolderHeapNodePtr> folderNodes;
       
  4596     QMessageFilter processedFilter(QMessageFilterPrivate::preprocess(error, _self.toStrongRef(), filter));
       
  4597     if (*error != QMessageManager::NoError)
       
  4598         return  QMessageIdList();
       
  4599 
       
  4600     if (QMessageFilterPrivate::isNonMatching(processedFilter)) { // Filter maybe be simplified by preprocessing
       
  4601         return QMessageIdList();
       
  4602     }
       
  4603 
       
  4604     foreach (QMessageFilter subfilter, QMessageFilterPrivate::subfilters(processedFilter)) {
       
  4605         MapiStoreIterator storeIt(QMessageFilterPrivate::storeIterator(subfilter, error, _self.toStrongRef()));
       
  4606         for (MapiStorePtr store(storeIt.next()); store && store->isValid(); store = storeIt.next()) {
       
  4607             MapiFolderIterator folderIt(QMessageFilterPrivate::folderIterator(subfilter, error, store));
       
  4608             for (MapiFolderPtr folder(folderIt.next()); folder && folder->isValid(); folder = folderIt.next()) {
       
  4609                 QList<QMessageFilter> orderingFilters;
       
  4610                 orderingFilters.append(subfilter);
       
  4611                 foreach(QMessageFilter orderingFilter, QMessageSortOrderPrivate::normalize(orderingFilters, sortOrder)) {
       
  4612                     folderNodes.append(FolderHeapNodePtr(new FolderHeapNode(folder, orderingFilter)));
       
  4613                 }
       
  4614             }
       
  4615         }
       
  4616     }
       
  4617 
       
  4618     if (*error != QMessageManager::NoError)
       
  4619         return QMessageIdList();
       
  4620 
       
  4621     return filterMessages(error, folderNodes, sortOrder, limit, offset, body, matchFlags);
       
  4622 }
       
  4623 
       
  4624 void MapiSession::updateMessage(QMessageManager::Error* error, const QMessage& source)
       
  4625 {
       
  4626     MapiStorePtr store;
       
  4627     IMessage *mapiMessage = openMapiMessage(error, source.id(), &store);
       
  4628     if (*error == QMessageManager::NoError) {
       
  4629         // Update the stored properties
       
  4630         if (*error == QMessageManager::NoError) {
       
  4631             storeMessageProperties(error, source, mapiMessage);
       
  4632         }
       
  4633         if (*error == QMessageManager::NoError) {
       
  4634             replaceMessageRecipients(error, source, mapiMessage, _mapiSession);
       
  4635         }
       
  4636         if (*error == QMessageManager::NoError) {
       
  4637             replaceMessageBody(error, source, mapiMessage, store);
       
  4638         }
       
  4639         if (*error == QMessageManager::NoError) {
       
  4640             // Attachments cannot be removed from a QMessage but new ones can be added
       
  4641             addMessageAttachments(error, source, mapiMessage);
       
  4642         }
       
  4643 #ifndef _WIN32_WCE //unsupported
       
  4644         if (*error == QMessageManager::NoError) {
       
  4645             if (HR_FAILED(mapiMessage->SaveChanges(0))) {
       
  4646                 qWarning() << "Unable to save changes for message.";
       
  4647             }
       
  4648         }
       
  4649 #endif
       
  4650 
       
  4651         mapiRelease(mapiMessage);
       
  4652     }
       
  4653 }
       
  4654 
       
  4655 void MapiSession::removeMessages(QMessageManager::Error *error, const QMessageIdList &ids)
       
  4656 {
       
  4657 #ifdef _WIN32_WCE
       
  4658     typedef QPair<MapiEntryId, MapiEntryId> FolderKey;
       
  4659 #else
       
  4660     typedef QPair<MapiRecordKey, MapiRecordKey> FolderKey;
       
  4661 #endif
       
  4662 
       
  4663     QMap<FolderKey, QMessageIdList> folderMessageIds;
       
  4664 
       
  4665     // Group messages by folder
       
  4666     foreach (const QMessageId &id, ids) {
       
  4667         folderMessageIds[qMakePair(QMessageIdPrivate::storeRecordKey(id), QMessageIdPrivate::folderRecordKey(id))].append(id);
       
  4668     }
       
  4669 
       
  4670     QMap<FolderKey, QMessageIdList>::const_iterator it = folderMessageIds.begin(), end = folderMessageIds.end();
       
  4671     for ( ; it != end; ++it) {
       
  4672 
       
  4673 #ifdef _WIN32_WCE
       
  4674         const MapiEntryId storeKey(it.key().first);
       
  4675         const MapiEntryId folderKey(it.key().second);
       
  4676 #else
       
  4677         const MapiRecordKey storeKey(it.key().first);
       
  4678         const MapiRecordKey folderKey(it.key().second);
       
  4679 #endif
       
  4680 
       
  4681         QMessageManager::Error localError(QMessageManager::NoError);
       
  4682 
       
  4683 #ifdef _WIN32_WCE
       
  4684         MapiStorePtr store = openStore(&localError,storeKey,true);
       
  4685 #else
       
  4686         MapiStorePtr store = openStoreWithKey(&localError, storeKey, true);
       
  4687 #endif
       
  4688         if (localError == QMessageManager::NoError) {
       
  4689 #ifdef _WIN32_WCE
       
  4690             MapiFolderPtr folder = store->openFolder(&localError, folderKey);
       
  4691 #else
       
  4692             MapiFolderPtr folder = store->openFolderWithKey(&localError, folderKey);
       
  4693 #endif
       
  4694             if (localError == QMessageManager::NoError) {
       
  4695                 folder->removeMessages(&localError, it.value());
       
  4696             }
       
  4697         }
       
  4698 
       
  4699         if ((localError != QMessageManager::NoError) && (*error == QMessageManager::NoError)) {
       
  4700             *error = localError;
       
  4701         }
       
  4702     }
       
  4703 }
       
  4704 
       
  4705 bool MapiSession::event(QEvent *e)
       
  4706 {
       
  4707     if (e->type() == NotifyEvent::eventType()) {
       
  4708         if (NotifyEvent *ne = static_cast<NotifyEvent*>(e)) {
       
  4709 
       
  4710             QMutex* storeMutex = QMessageStorePrivate::mutex(QMessageManager());
       
  4711 
       
  4712             if(!storeMutex->tryLock())
       
  4713                 addToNotifyQueue(*ne);
       
  4714             else
       
  4715             {
       
  4716                 notify(ne->_store, ne->_id, ne->_notifyType);
       
  4717                 storeMutex->unlock();
       
  4718             }
       
  4719             return true;
       
  4720         }
       
  4721     }
       
  4722 
       
  4723     return QObject::event(e);
       
  4724 }
       
  4725 
       
  4726 void MapiSession::notify(MapiStore *store, const QMessageId &id, MapiSession::NotifyType notifyType)
       
  4727 {
       
  4728     QMessageManager::NotificationFilterIdSet matchingFilterIds;
       
  4729 
       
  4730     QMap<QMessageManager::NotificationFilterId, QMessageFilter>::const_iterator it = _filters.begin(), end = _filters.end();
       
  4731     for ( ; it != end; ++it) {
       
  4732         const QMessageFilter &filter(it.value());
       
  4733 
       
  4734         if (!filter.isSupported())
       
  4735             continue;
       
  4736 
       
  4737          // no message properties are available for a removed message, so only empty filter can match
       
  4738         if (notifyType == MapiSession::Removed) {
       
  4739             if (filter.isEmpty()) {
       
  4740                 matchingFilterIds.insert(it.key());
       
  4741                 break;
       
  4742             }
       
  4743             continue;
       
  4744         }
       
  4745 
       
  4746         QMessageManager::Error ignoredError(QMessageManager::NoError);
       
  4747         QMessageFilter processedFilter(QMessageFilterPrivate::preprocess(&ignoredError, _self.toStrongRef(), filter));
       
  4748         if (ignoredError != QMessageManager::NoError)
       
  4749             continue;
       
  4750 
       
  4751         QMessage message(id);
       
  4752         foreach (QMessageFilter subfilter, QMessageFilterPrivate::subfilters(processedFilter)) {
       
  4753             if (QMessageFilterPrivate::matchesMessage(subfilter, message, store)) {
       
  4754                 matchingFilterIds.insert(it.key());
       
  4755                 break; // subfilters are or'd together
       
  4756             }
       
  4757         }
       
  4758     }
       
  4759 
       
  4760     if (!matchingFilterIds.isEmpty()) {
       
  4761         void (MapiSession::*signal)(const QMessageId &, const QMessageManager::NotificationFilterIdSet &) =
       
  4762             ((notifyType == Added) ? &MapiSession::messageAdded
       
  4763                                    : ((notifyType == Removed) ? &MapiSession::messageRemoved
       
  4764                                                               : &MapiSession::messageUpdated));
       
  4765         emit (this->*signal)(id, matchingFilterIds);
       
  4766     }
       
  4767 }
       
  4768 
       
  4769 QMessageManager::NotificationFilterId MapiSession::registerNotificationFilter(QMessageManager::Error *error, const QMessageFilter &filter)
       
  4770 {
       
  4771     QMessageManager::NotificationFilterId filterId = ++_filterId;
       
  4772     _filters.insert(filterId, filter);
       
  4773 
       
  4774     if (!_registered) {
       
  4775         _registered = true;
       
  4776 
       
  4777         foreach (MapiStorePtr store, allStores(error)) {
       
  4778             store->notifyEvents(fnevNewMail | fnevObjectCreated | fnevObjectCopied | fnevObjectDeleted | fnevObjectModified | fnevObjectMoved);
       
  4779         }
       
  4780     }
       
  4781 
       
  4782     return filterId;
       
  4783 }
       
  4784 
       
  4785 void MapiSession::unregisterNotificationFilter(QMessageManager::Error *error, QMessageManager::NotificationFilterId filterId)
       
  4786 {
       
  4787     _filters.remove(filterId);
       
  4788 
       
  4789     Q_UNUSED(error)
       
  4790 }
       
  4791 
       
  4792 void MapiSession::dispatchNotifications()
       
  4793 {
       
  4794     MSG msg = { 0 };
       
  4795     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
       
  4796         TranslateMessage(&msg);
       
  4797         DispatchMessage(&msg);
       
  4798     }
       
  4799 
       
  4800     QTimer::singleShot(1000, this, SLOT(dispatchNotifications()));
       
  4801 }
       
  4802 
       
  4803 void MapiSession::processNotifyQueue()
       
  4804 {
       
  4805      foreach(const NotifyEvent& e, _notifyEventQueue)
       
  4806      {
       
  4807          QMutex* storeMutex = QMessageStorePrivate::mutex(QMessageStore::instance());
       
  4808          if(storeMutex->tryLock())
       
  4809          {
       
  4810              NotifyEvent ne = _notifyEventQueue.dequeue();
       
  4811              notify(e._store,e._id,e._notifyType);
       
  4812              storeMutex->unlock();
       
  4813          }
       
  4814          else break;
       
  4815      }
       
  4816      if(!_notifyEventQueue.isEmpty())
       
  4817         qWarning() << QString("Notify queue processing interrupted: pending %1").arg(_notifyEventQueue.length());
       
  4818 }
       
  4819 
       
  4820 QMessagePrivate *MapiSession::messageImpl(const QMessage &message)
       
  4821 {
       
  4822     return message.d_ptr;
       
  4823 }
       
  4824 
       
  4825 QMessageContentContainerPrivate *MapiSession::containerImpl(const QMessageContentContainer &container)
       
  4826 {
       
  4827     return container.d_ptr;
       
  4828 }
       
  4829 
       
  4830 void MapiSession::addToNotifyQueue(const NotifyEvent& e)
       
  4831 {
       
  4832     _notifyEventQueue.enqueue(e);
       
  4833     qWarning() << QString("Store busy...adding notify to queue: pending %1").arg(_notifyEventQueue.length());
       
  4834 }
       
  4835 
       
  4836 void MapiSession::flushNotifyQueue()
       
  4837 {
       
  4838     QTimer::singleShot(0, this, SLOT(processNotifyQueue()));
       
  4839 }
       
  4840 
       
  4841 #ifndef _WIN32_WCE
       
  4842 
       
  4843 MapiForm::MapiForm(IMsgStore* mapiStore,
       
  4844                    IMAPISession* mapiSession,
       
  4845                    IMAPIFolder* mapiFolder,
       
  4846                    IMessage* mapiMessage)
       
  4847 :
       
  4848 m_mapiStore(mapiStore),
       
  4849 m_mapiSession(mapiSession),
       
  4850 m_mapiFolder(mapiFolder),
       
  4851 m_mapiMessage(mapiMessage),
       
  4852 m_mapiPersistMessage(0),
       
  4853 m_referenceCount(1)
       
  4854 {
       
  4855     if (m_mapiStore) m_mapiStore->AddRef();
       
  4856     if (m_mapiSession) m_mapiSession->AddRef();
       
  4857     if (m_mapiFolder) m_mapiFolder->AddRef();
       
  4858     if (m_mapiMessage) m_mapiMessage->AddRef();
       
  4859 }
       
  4860 
       
  4861 MapiForm::~MapiForm()
       
  4862 {
       
  4863     releaseAll();
       
  4864 }
       
  4865 
       
  4866 void MapiForm::releaseAll()
       
  4867 {
       
  4868     releasePersistMessage();
       
  4869     mapiRelease(m_mapiMessage);
       
  4870     mapiRelease(m_mapiFolder);
       
  4871     mapiRelease(m_mapiStore);
       
  4872     mapiRelease(m_mapiSession);
       
  4873 }
       
  4874 
       
  4875 STDMETHODIMP MapiForm::QueryInterface(REFIID riid, void** iface)
       
  4876 {
       
  4877     *iface = 0;
       
  4878 
       
  4879     if (riid == IID_IMAPIMessageSite)
       
  4880     {
       
  4881         *iface = (IMAPIMessageSite*)this;
       
  4882         AddRef();
       
  4883         return S_OK;
       
  4884     }
       
  4885 
       
  4886     if (riid == IID_IUnknown)
       
  4887     {
       
  4888         *iface = (LPUNKNOWN)((IMAPIMessageSite*)this);
       
  4889         AddRef();
       
  4890         return S_OK;
       
  4891     }
       
  4892 
       
  4893     return E_NOINTERFACE;
       
  4894 }
       
  4895 
       
  4896 STDMETHODIMP_(ULONG) MapiForm::AddRef()
       
  4897 {
       
  4898     long refCount = InterlockedIncrement(&m_referenceCount);
       
  4899 
       
  4900     return refCount;
       
  4901 }
       
  4902 
       
  4903 STDMETHODIMP_(ULONG) MapiForm::Release()
       
  4904 {
       
  4905     long refCount = InterlockedDecrement(&m_referenceCount);
       
  4906 
       
  4907     if (!refCount)
       
  4908           delete this;
       
  4909 
       
  4910     return refCount;
       
  4911 }
       
  4912 
       
  4913 STDMETHODIMP MapiForm::GetSession (IMAPISession* FAR * mapiSession)
       
  4914 {
       
  4915     HRESULT hResult = S_FALSE;
       
  4916 
       
  4917     if (mapiSession)
       
  4918     {
       
  4919         *mapiSession = m_mapiSession;
       
  4920         if (m_mapiSession)
       
  4921         {
       
  4922             m_mapiSession->AddRef();
       
  4923             hResult = S_OK;
       
  4924         }
       
  4925     }
       
  4926     return hResult;
       
  4927 }
       
  4928 
       
  4929 STDMETHODIMP MapiForm::GetStore(IMsgStore* FAR * mapiStore)
       
  4930 {
       
  4931     HRESULT hResult = S_FALSE;
       
  4932 
       
  4933     if (mapiStore)
       
  4934     {
       
  4935         *mapiStore = m_mapiStore;
       
  4936         if (m_mapiStore)
       
  4937         {
       
  4938             m_mapiStore->AddRef();
       
  4939             hResult = S_OK;
       
  4940         }
       
  4941     }
       
  4942     return hResult;
       
  4943 }
       
  4944 
       
  4945 STDMETHODIMP MapiForm::GetFolder(IMAPIFolder* FAR * mapiFolder)
       
  4946 {
       
  4947     HRESULT hResult = S_FALSE;
       
  4948 
       
  4949     if (mapiFolder)
       
  4950     {
       
  4951         *mapiFolder = m_mapiFolder;
       
  4952         if (m_mapiFolder)
       
  4953         {
       
  4954             m_mapiFolder->AddRef();
       
  4955             hResult = S_OK;
       
  4956         }
       
  4957     }
       
  4958     return hResult;
       
  4959 }
       
  4960 
       
  4961 STDMETHODIMP MapiForm::GetMessage(IMessage* FAR * mapiMessage)
       
  4962 {
       
  4963     HRESULT hResult = S_FALSE;
       
  4964 
       
  4965     if (mapiMessage)
       
  4966     {
       
  4967         *mapiMessage = m_mapiMessage;
       
  4968         if(m_mapiMessage)
       
  4969         {
       
  4970             m_mapiMessage->AddRef();
       
  4971             hResult = S_OK;
       
  4972         }
       
  4973     }
       
  4974     return hResult;
       
  4975 }
       
  4976 
       
  4977 STDMETHODIMP MapiForm::GetFormManager(IMAPIFormMgr* FAR * mapiFormManager)
       
  4978 {
       
  4979     HRESULT hRes = S_OK;
       
  4980     hRes = MAPIOpenFormMgr(m_mapiSession,mapiFormManager);
       
  4981     if(FAILED(hRes))
       
  4982         qWarning() << "MAPIOpenFormMgr failed";
       
  4983     return hRes;
       
  4984 }
       
  4985 
       
  4986 STDMETHODIMP MapiForm::NewMessage(ULONG isComposeInFolder,
       
  4987                                         IMAPIFolder* mapiFolder,
       
  4988                                         IPersistMessage* mapiPersistMessage,
       
  4989                                         IMessage* FAR * mapiMessage,
       
  4990                                         IMAPIMessageSite* FAR * mapiMessageSite,
       
  4991                                         LPMAPIVIEWCONTEXT FAR * mapiViewContext)
       
  4992 {
       
  4993 
       
  4994     *mapiMessage = 0;
       
  4995     *mapiMessageSite = 0;
       
  4996 
       
  4997     HRESULT hRes = S_OK;
       
  4998 
       
  4999     if (mapiViewContext) *mapiViewContext = 0;
       
  5000 
       
  5001     if ((isComposeInFolder == false) || !mapiFolder)
       
  5002         mapiFolder = m_mapiFolder;
       
  5003 
       
  5004     if (mapiFolder)
       
  5005     {
       
  5006         hRes = mapiFolder->CreateMessage(0, 0, mapiMessage);
       
  5007 
       
  5008         if (*mapiMessage)
       
  5009         {
       
  5010             MapiForm *mapiForm = 0;
       
  5011             mapiForm = new MapiForm(m_mapiStore, m_mapiSession, mapiFolder, *mapiMessage);
       
  5012             if (mapiForm)
       
  5013             {
       
  5014                 hRes = mapiForm->setPersistMessage(NULL,mapiPersistMessage);
       
  5015                 *mapiMessageSite = (IMAPIMessageSite*)mapiForm;
       
  5016             }
       
  5017         }
       
  5018     }
       
  5019 
       
  5020     return hRes;
       
  5021 }
       
  5022 
       
  5023 STDMETHODIMP MapiForm::CopyMessage(IMAPIFolder*)
       
  5024 {
       
  5025     return MAPI_E_NO_SUPPORT;
       
  5026 }
       
  5027 
       
  5028 STDMETHODIMP MapiForm::MoveMessage(IMAPIFolder*, LPMAPIVIEWCONTEXT, LPCRECT)
       
  5029 {
       
  5030     return MAPI_E_NO_SUPPORT;
       
  5031 }
       
  5032 
       
  5033 STDMETHODIMP MapiForm::DeleteMessage(LPMAPIVIEWCONTEXT, LPCRECT)
       
  5034 {
       
  5035     return MAPI_E_NO_SUPPORT;
       
  5036 }
       
  5037 
       
  5038 STDMETHODIMP MapiForm::SaveMessage()
       
  5039 {
       
  5040     HRESULT hRes = S_OK;
       
  5041 
       
  5042     if (!m_mapiPersistMessage || !m_mapiMessage)
       
  5043         return MAPI_E_INVALID_PARAMETER;
       
  5044 
       
  5045     hRes = m_mapiPersistMessage->Save(0, true);
       
  5046 
       
  5047     if (FAILED(hRes))
       
  5048     {
       
  5049         qWarning() << "IMessage::Save failed";
       
  5050         return hRes;
       
  5051     }
       
  5052     else
       
  5053     {
       
  5054         hRes = (m_mapiMessage->SaveChanges(KEEP_OPEN_READWRITE));
       
  5055         hRes = (m_mapiPersistMessage->SaveCompleted(NULL));
       
  5056     }
       
  5057 
       
  5058     return hRes;
       
  5059 }
       
  5060 
       
  5061 STDMETHODIMP MapiForm::SubmitMessage(ulong flags)
       
  5062 {
       
  5063     Q_UNUSED(flags);
       
  5064 
       
  5065     HRESULT hRes = S_OK;
       
  5066     if (!m_mapiPersistMessage || !m_mapiMessage) return MAPI_E_INVALID_PARAMETER;
       
  5067 
       
  5068     hRes = (m_mapiPersistMessage->Save(m_mapiMessage,true));
       
  5069 
       
  5070     if (FAILED(hRes))
       
  5071     {
       
  5072         qWarning() << "IPersistMessage::Save failed";
       
  5073         return hRes;
       
  5074     }
       
  5075     else
       
  5076     {
       
  5077         hRes = (m_mapiPersistMessage->HandsOffMessage());
       
  5078         hRes = (m_mapiMessage->SubmitMessage(NULL));
       
  5079     }
       
  5080 
       
  5081     mapiRelease(m_mapiMessage);
       
  5082 
       
  5083     return hRes;
       
  5084 }
       
  5085 
       
  5086 STDMETHODIMP MapiForm::GetSiteStatus(ULONG* status)
       
  5087 {
       
  5088     *status = VCSTATUS_NEW_MESSAGE;
       
  5089 
       
  5090     if (m_mapiPersistMessage)
       
  5091         *status |= VCSTATUS_SAVE | VCSTATUS_SUBMIT | NULL;
       
  5092 
       
  5093     return S_OK;
       
  5094 }
       
  5095 
       
  5096 bool MapiForm::show()
       
  5097 {
       
  5098     IMAPIFormMgr* mapiFormManager = 0;
       
  5099 
       
  5100     if(GetFormManager(&mapiFormManager) != S_OK)
       
  5101         return false;
       
  5102 
       
  5103     IMAPIForm* MAPIForm = 0;
       
  5104 
       
  5105     if(mapiFormManager->LoadForm((ULONG_PTR) 0, 0, 0, 0, 0, m_mapiFolder,
       
  5106                                  this,
       
  5107                                  m_mapiMessage,
       
  5108                                  0,
       
  5109                                  IID_IMAPIForm,
       
  5110                                  (LPVOID *) &MAPIForm) != S_OK)
       
  5111     {
       
  5112         qWarning() << "IMAPIFormMgr::LoadForm failed";
       
  5113         mapiFormManager->Release();
       
  5114         return false;
       
  5115     }
       
  5116 
       
  5117     mapiFormManager->Release();
       
  5118 
       
  5119     RECT rect;
       
  5120     bool result = true;
       
  5121 
       
  5122     if (MAPIForm)
       
  5123     {
       
  5124             setPersistMessage(MAPIForm,NULL);
       
  5125 
       
  5126             HRESULT hRes = S_OK;
       
  5127 
       
  5128             hRes = MAPIForm->DoVerb(0,0,0,&rect);
       
  5129             if (S_OK != hRes)
       
  5130             {
       
  5131                 rect.left = 0;
       
  5132                 rect.right = 500;
       
  5133                 rect.top = 0;
       
  5134                 rect.bottom = 400;
       
  5135                 hRes = MAPIForm->DoVerb(0, 0, 0, &rect);
       
  5136             }
       
  5137 
       
  5138             if(S_OK != hRes)
       
  5139             {
       
  5140                 qWarning() << "IMapiForm::DoVerb Failed";
       
  5141                 result = false;
       
  5142             }
       
  5143 
       
  5144             MAPIForm->Release();
       
  5145     }
       
  5146 
       
  5147     return  result;
       
  5148 }
       
  5149 
       
  5150 STDMETHODIMP MapiForm::GetLastError(HRESULT, ULONG, LPMAPIERROR FAR*)
       
  5151 {
       
  5152     return MAPI_E_NO_SUPPORT;
       
  5153 }
       
  5154 
       
  5155 void MapiForm::releasePersistMessage()
       
  5156 {
       
  5157     if (m_mapiPersistMessage)
       
  5158     {
       
  5159         m_mapiPersistMessage->HandsOffMessage();
       
  5160         mapiRelease(m_mapiPersistMessage);
       
  5161     }
       
  5162 }
       
  5163 
       
  5164 HRESULT MapiForm::setPersistMessage(LPMAPIFORM lpForm, IPersistMessage* mapiPersistMessage)
       
  5165 {
       
  5166     HRESULT hRes = S_OK;
       
  5167     releasePersistMessage();
       
  5168 
       
  5169     if (mapiPersistMessage)
       
  5170     {
       
  5171         m_mapiPersistMessage = mapiPersistMessage;
       
  5172         m_mapiPersistMessage->AddRef();
       
  5173     }
       
  5174     else if (lpForm) hRes = (lpForm->QueryInterface(IID_IPersistMessage ,(LPVOID *)&m_mapiPersistMessage));
       
  5175 
       
  5176     return hRes;
       
  5177 }
       
  5178 
       
  5179 #endif
       
  5180 
       
  5181 #include "winhelpers.moc"
       
  5182 #include "moc_winhelpers_p.cpp"
       
  5183 
       
  5184 QTM_END_NAMESPACE