diff -r cfcbf08528c4 -r 2b40d63a9c3d qtmobility/tests/auto/support/support_win.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qtmobility/tests/auto/support/support_win.cpp Fri Apr 16 15:51:22 2010 +0300 @@ -0,0 +1,1464 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "support.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#ifdef _WIN32_WCE +#include +#endif + +// Missing definitions +#ifndef PR_PST_CONFIG_FLAGS +#define PR_PST_CONFIG_FLAGS PROP_TAG( PT_LONG, 0x6770 ) +#endif +#ifndef PST_CONFIG_UNICODE +#define PST_CONFIG_UNICODE 0x80000000 +#endif +#ifndef PR_PST_PATH_A +#define PR_PST_PATH_A PROP_TAG( PT_STRING8, 0x6700 ) +#endif + +namespace { + +class Lptstr : public QVector +{ +public: + Lptstr(int length) : QVector(length){} + operator TCHAR* (){ return QVector::data(); } +}; + +Lptstr LptstrFromQString(const QString &src) +{ + uint length(src.length()); + Lptstr dst(length+1); + + const quint16 *data = src.utf16(); + const quint16 *it = data, *end = data + length; + TCHAR *oit = dst; + for ( ; it != end; ++it, ++oit) { + *oit = static_cast(*it); + } + *oit = TCHAR('\0'); + return dst; +} + +QString QStringFromLptstr(LPCTSTR data) +{ + if (!data) + return QString(); + + return QString::fromUtf16(reinterpret_cast(data)); +} + +class QueryAllRows +{ + static const int BatchSize = 20; +public: + QueryAllRows(LPMAPITABLE ptable, + LPSPropTagArray ptaga, + LPSRestriction pres, + LPSSortOrderSet psos, + bool setPosition = true); + ~QueryAllRows(); + + bool query(); + LPSRowSet rows() const; + QMessageManager::Error error() const; + +private: + LPMAPITABLE m_table; + LPSPropTagArray m_tagArray; + LPSRestriction m_restriction; + LPSSortOrderSet m_sortOrderSet; + LPSRowSet m_rows; + QMessageManager::Error m_error; +}; + +QueryAllRows::QueryAllRows(LPMAPITABLE ptable, + LPSPropTagArray ptaga, + LPSRestriction pres, + LPSSortOrderSet psos, + bool setPosition) + : + m_table(ptable), + m_tagArray(ptaga), + m_restriction(pres), + m_sortOrderSet(psos), + m_rows(0), + m_error(QMessageManager::NoError) +{ +#ifndef _WIN32_WCE + const ULONG options(TBL_BATCH); +#else + const ULONG options(0); +#endif + + bool initFailed = false; + + initFailed |= FAILED(m_table->SetColumns(m_tagArray, options)); + + if(m_restriction) + initFailed |= FAILED(m_table->Restrict(m_restriction, options)); + + if(m_sortOrderSet) + initFailed |= FAILED(m_table->SortTable(m_sortOrderSet, options)); + + if(setPosition) + initFailed |= FAILED(m_table->SeekRow(BOOKMARK_BEGINNING, 0, NULL)); + + if(initFailed) m_error = QMessageManager::ContentInaccessible; +} + +QueryAllRows::~QueryAllRows() +{ + FreeProws(m_rows); + m_rows = 0; +} + +bool QueryAllRows::query() +{ + if(m_error != QMessageManager::NoError) + return false; + + FreeProws(m_rows); + m_rows = 0; + m_error = QMessageManager::NoError; + + bool failed = FAILED(m_table->QueryRows( QueryAllRows::BatchSize, NULL, &m_rows)); + + if(failed) + m_error = QMessageManager::ContentInaccessible; + + if(failed || m_rows && !m_rows->cRows) return false; + + return true; +} + +LPSRowSet QueryAllRows::rows() const +{ + return m_rows; +} + +QMessageManager::Error QueryAllRows::error() const +{ + return m_error; +} + +#ifndef _WIN32_WCE +GUID GuidPublicStrings = { 0x00020329, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 }; +#else +GUID GuidPSMAPI = { 0x00020328, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 }; +#endif + +void doInit() +{ + static QMessageManager manager; + Q_UNUSED(manager) +} + +QByteArray binaryResult(const SPropValue &prop) +{ + return QByteArray(reinterpret_cast(prop.Value.bin.lpb), prop.Value.bin.cb); +} + +#ifndef _WIN32_WCE +IProfAdmin *openProfileAdmin() +{ + IProfAdmin *profAdmin(0); + HRESULT rv = MAPIAdminProfiles(0, &profAdmin); + if (HR_FAILED(rv)) { + qWarning() << "openProfileAdmin: MAPIAdminProfiles failed"; + } + + return profAdmin; +} + +IMsgServiceAdmin *openServiceAdmin(const QByteArray &profileName, IProfAdmin *profAdmin) +{ + IMsgServiceAdmin *svcAdmin(0); + + HRESULT rv = profAdmin->AdminServices(reinterpret_cast(const_cast(profileName.data())), 0, 0, 0, &svcAdmin); + if (HR_FAILED(rv)) { + qWarning() << "openServiceAdmin: AdminServices failed"; + } + + return svcAdmin; +} + +typedef QPair ProfileDetail; + +QList profileDetails(LPPROFADMIN profAdmin) +{ + QList result; + + LPMAPITABLE profileTable(0); + HRESULT rv = profAdmin->GetProfileTable(0, &profileTable); + if (HR_SUCCEEDED(rv)) { + + SizedSPropTagArray(2, cols) = {2, {PR_DISPLAY_NAME_A, PR_DEFAULT_PROFILE}}; + + QueryAllRows qar(profileTable, reinterpret_cast(&cols), NULL, NULL); + while(qar.query()) { + for (uint n = 0; n < qar.rows()->cRows; ++n) { + if (qar.rows()->aRow[n].lpProps[0].ulPropTag == PR_DISPLAY_NAME_A) { + QByteArray profileName(qar.rows()->aRow[n].lpProps[0].Value.lpszA); + bool defaultProfile(qar.rows()->aRow[n].lpProps[1].Value.b); + result.append(qMakePair(profileName, defaultProfile)); + } + } + } + + if(qar.error() != QMessageManager::NoError) + qWarning() << "profileNames: QueryAllRows failed"; + + profileTable->Release(); + + } else { + qWarning() << "profileNames: GetProfileTable failed"; + } + + return result; +} + +QByteArray findDefaultProfileName(IProfAdmin *profAdmin) +{ + QByteArray defaultProfileName; + + foreach (const ProfileDetail &profile, profileDetails(profAdmin)) { + if (profile.second) { + defaultProfileName = profile.first; + break; + } + } + + if (defaultProfileName.isEmpty()) { + qWarning() << "findDefaultProfileName: no default profile!"; + } + + return defaultProfileName; +} + +typedef QPair, MAPIUID> ServiceDetail; + +QList serviceDetails(LPSERVICEADMIN svcAdmin) +{ + QList result; + + IMAPITable *svcTable(0); + HRESULT rv = svcAdmin->GetMsgServiceTable(0, &svcTable); + if (HR_SUCCEEDED(rv)) { + + SizedSPropTagArray(3, cols) = {3, {PR_SERVICE_NAME_A, PR_DISPLAY_NAME_A, PR_SERVICE_UID}}; + + QueryAllRows qar(svcTable, reinterpret_cast(&cols), 0, 0); + while(qar.query()) { + for (uint n = 0; n < qar.rows()->cRows; ++n) { + if (qar.rows()->aRow[n].lpProps[0].ulPropTag == PR_SERVICE_NAME_A) { + QByteArray svcName(qar.rows()->aRow[n].lpProps[0].Value.lpszA); + QByteArray displayName; + if (qar.rows()->aRow[n].lpProps[1].ulPropTag == PR_DISPLAY_NAME_A) { + displayName = QByteArray(qar.rows()->aRow[n].lpProps[1].Value.lpszA); + } + MAPIUID svcUid(*(reinterpret_cast(qar.rows()->aRow[n].lpProps[2].Value.bin.lpb))); + result.append(qMakePair(qMakePair(svcName, displayName), svcUid)); + } + } + } + + if(qar.error() != QMessageManager::NoError) + qWarning() << "serviceDetails: QueryAllRows failed"; + + svcTable->Release(); + + } else { + qWarning() << "serviceDetails: GetMsgServiceTable failed"; + } + + return result; +} +#endif + +#ifndef _WIN32_WCE +typedef QPair > StoreDetail; +#else +typedef QPair StoreDetail; +#endif + +QList storeDetails(LPMAPISESSION session) +{ + QList result; + + IMAPITable *storesTable(0); + HRESULT rv = session->GetMsgStoresTable(0, &storesTable); + if (HR_SUCCEEDED(rv)) { +#ifndef _WIN32_WCE + SizedSPropTagArray(3, cols) = {3, {PR_DISPLAY_NAME_A, PR_RECORD_KEY, PR_ENTRYID}}; +#else + SizedSPropTagArray(2, cols) = {2, {PR_DISPLAY_NAME, PR_ENTRYID}}; +#endif + + QueryAllRows qar(storesTable, reinterpret_cast(&cols), 0, 0, false); + while(qar.query()) { + for (uint n = 0; n < qar.rows()->cRows; ++n) { + SPropValue *props(qar.rows()->aRow[n].lpProps); + if (props[0].ulPropTag == cols.aulPropTag[0]) { +#ifndef _WIN32_WCE + QByteArray storeName(props[0].Value.lpszA); + QByteArray recordKey(binaryResult(props[1])); + QByteArray entryId(binaryResult(props[2])); + result.append(qMakePair(storeName, qMakePair(recordKey, entryId))); +#else + QString storeName(QStringFromLptstr(props[0].Value.lpszW)); + QByteArray entryId(binaryResult(props[1])); + result.append(qMakePair(storeName, entryId)); +#endif + } + } + } + + if(qar.error() != QMessageManager::NoError) + qWarning() << "storeDetails: QueryAllRows failed"; + + storesTable->Release(); + } else { + qWarning() << "storeDetails: GetMsgStoresTable failed"; + } + + return result; +} + +QMessageAccountId accountIdFromRecordKey(const QByteArray &recordKey) +{ + QByteArray encodedId; + { + QDataStream encodedIdStream(&encodedId, QIODevice::WriteOnly); + encodedIdStream << recordKey; + } + + return QMessageAccountId(encodedId.toBase64()); +} + +QMessageFolderId folderIdFromProperties(const QByteArray &recordKey, const QByteArray &entryId, const QByteArray &storeKey) +{ + QByteArray encodedId; + { + QDataStream encodedIdStream(&encodedId, QIODevice::WriteOnly); +#ifndef _WIN32_WCE + encodedIdStream << recordKey << storeKey; + if (!entryId.isEmpty()) { + encodedIdStream << entryId; + } +#else + encodedIdStream << entryId << storeKey; + if (!recordKey.isEmpty()) { + encodedIdStream << recordKey; + } +#endif + } + + return QMessageFolderId(encodedId.toBase64()); +} + +QByteArray objectProperty(IMAPIProp *object, ULONG tag) +{ + QByteArray result; + + if (object) { + SPropValue *prop(0); + HRESULT rv = HrGetOneProp(object, tag, &prop); + if (HR_SUCCEEDED(rv)) { + result = binaryResult(*prop); + + MAPIFreeBuffer(prop); + } else { + qWarning() << "objectProperty: HrGetOneProp failed"; + } + } + + return result; +} + +QString stringProperty(IMAPIProp *object, ULONG tag) +{ + QString result; + + if (object) { + SPropValue *prop(0); + HRESULT rv = HrGetOneProp(object, tag, &prop); + if (HR_SUCCEEDED(rv)) { + result = QString::fromUtf16(reinterpret_cast(prop->Value.LPSZ)); + + MAPIFreeBuffer(prop); + } else if (rv != MAPI_E_NOT_FOUND) { + qWarning() << "stringProperty: HrGetOneProp failed"; + } + } + + return result; +} + +ULONG createNamedProperty(IMAPIProp *object, const QString &name) +{ + ULONG result = 0; + + if (!name.isEmpty()) { + Lptstr nameBuffer = LptstrFromQString(name); + + MAPINAMEID propName = { 0 }; +#ifndef _WIN32_WCE + propName.lpguid = &GuidPublicStrings; +#else + propName.lpguid = &GuidPSMAPI; +#endif + propName.ulKind = MNID_STRING; + propName.Kind.lpwstrName = nameBuffer; + + LPMAPINAMEID propNames = &propName; + + SPropTagArray *props; + HRESULT rv = object->GetIDsFromNames(1, &propNames, MAPI_CREATE, &props); + if (HR_SUCCEEDED(rv)) { + result = props->aulPropTag[0] | PT_UNICODE; + + MAPIFreeBuffer(props); + } else { + qWarning() << "createNamedProperty: GetIDsFromNames failed"; + } + } + + return result; +} + +ULONG getNamedPropertyTag(IMAPIProp *object, const QString &name) +{ + ULONG result = 0; + + if (!name.isEmpty()) { + Lptstr nameBuffer = LptstrFromQString(name); + + MAPINAMEID propName = { 0 }; +#ifndef _WIN32_WCE + propName.lpguid = &GuidPublicStrings; +#else + propName.lpguid = &GuidPSMAPI; +#endif + propName.ulKind = MNID_STRING; + propName.Kind.lpwstrName = nameBuffer; + + LPMAPINAMEID propNames = &propName; + + SPropTagArray *props; + HRESULT rv = object->GetIDsFromNames(1, &propNames, 0, &props); + if (HR_SUCCEEDED(rv)) { + if (props->aulPropTag[0] != PT_ERROR) { + result = props->aulPropTag[0] | PT_UNICODE; + } + + MAPIFreeBuffer(props); + } else { + qWarning() << "getNamedPropertyTag: GetIDsFromNames failed"; + } + } + + return result; +} + +bool setNamedProperty(IMAPIProp *object, ULONG tag, const QString &value) +{ + if (object && tag && !value.isEmpty()) { + SPropValue prop = { 0 }; + prop.ulPropTag = tag; + prop.Value.LPSZ = reinterpret_cast(const_cast(value.utf16())); + + HRESULT rv = object->SetProps(1, &prop, 0); + if (HR_SUCCEEDED(rv)) { + return true; + } else { + qWarning() << "setNamedProperty: SetProps failed"; + } + } + + return false; +} + +QString getNamedProperty(IMAPIProp *object, ULONG tag) +{ + QString result; + + if (object && tag) { + SPropValue *prop(0); + HRESULT rv = HrGetOneProp(object, tag, &prop); + if (HR_SUCCEEDED(rv)) { + result = QString::fromUtf16(reinterpret_cast(prop->Value.LPSZ)); + + MAPIFreeBuffer(prop); + } else if (rv != MAPI_E_NOT_FOUND) { + qWarning() << "getNamedProperty: HrGetOneProp failed"; + } + } + + return result; +} + +#ifndef _WIN32_WCE +IProviderAdmin *serviceProvider(const MAPIUID &svcUid, LPSERVICEADMIN svcAdmin) +{ + IProviderAdmin *provider(0); + + if (svcAdmin) { + HRESULT rv = svcAdmin->AdminProviders(const_cast(&svcUid), 0, &provider); + if (HR_FAILED(rv)) { + provider = 0; + qWarning() << "serviceProvider: AdminProviders failed"; + } + } + + return provider; +} + +MAPIUID findProviderUid(const QByteArray &name, IProviderAdmin *providerAdmin) +{ + MAPIUID result = { 0 }; + IMAPITable *providerTable(0); + HRESULT rv = providerAdmin->GetProviderTable(0, &providerTable); + if (HR_SUCCEEDED(rv)) { + + SizedSPropTagArray(2, cols) = {2, {PR_SERVICE_NAME_A, PR_PROVIDER_UID}}; + + QueryAllRows qar(providerTable, reinterpret_cast(&cols), 0, 0); + while(qar.query()) { + for (uint n = 0; n < qar.rows()->cRows; ++n) { + SPropValue *props(qar.rows()->aRow[n].lpProps); + if (props[0].ulPropTag == PR_SERVICE_NAME_A) { + QByteArray serviceName(props[0].Value.lpszA); + if (name.isEmpty() || (serviceName.toLower() == name.toLower())) { + result = *(reinterpret_cast(props[1].Value.bin.lpb)); + break; + } + } + } + } + + if(qar.error() != QMessageManager::NoError) + qWarning() << "findProviderUid: QueryAllRows failed"; + + providerTable->Release(); + } else { + qWarning() << "findProviderUid: GetProviderTable failed"; + } + + return result; +} + +IProfSect *openProfileSection(const MAPIUID &providerUid, IProviderAdmin *providerAdmin) +{ + IProfSect *profileSection(0); + + // Bypass the MAPI_E_NO_ACCESS_ERROR, as described at http://support.microsoft.com/kb/822977 + const ULONG MAPI_FORCE_ACCESS = 0x00080000; + + HRESULT rv = providerAdmin->OpenProfileSection(const_cast(&providerUid), 0, MAPI_FORCE_ACCESS, &profileSection); + if (HR_FAILED(rv)) { + qWarning() << "openProfileSection: OpenProfileSection failed"; + profileSection = 0; + } + + return profileSection; +} + +template +bool isEmpty(const T &v) +{ + const char empty[sizeof(T)] = { 0 }; + return (memcmp(empty, &v, sizeof(T)) == 0); +} + +bool deleteExistingService(const MAPIUID &svcUid, LPSERVICEADMIN svcAdmin) +{ + if (svcAdmin) { + QByteArray storePath; + + // Find the Provider for this service + IProviderAdmin *provider = serviceProvider(svcUid, svcAdmin); + if (provider) { + MAPIUID providerUid = findProviderUid("MSUPST MS", provider); + if (!isEmpty(providerUid)) { + IProfSect *profileSection = openProfileSection(providerUid, provider); + if (profileSection) { + SPropValue *prop(0); + HRESULT rv = HrGetOneProp(profileSection, PR_PST_PATH_A, &prop); + if (HR_SUCCEEDED(rv)) { + storePath = QByteArray(prop->Value.lpszA); + + MAPIFreeBuffer(prop); + } else { + qWarning() << "deleteExistingService: HrGetOneProp failed"; + } + + profileSection->Release(); + } + } + + provider->Release(); + } + + if (!storePath.isEmpty()) { + // Delete the existing service + HRESULT rv = svcAdmin->DeleteMsgService(const_cast(&svcUid)); + if (HR_SUCCEEDED(rv)) { + // Delete the storage file + if (QFile::exists(storePath)) { + if (!QFile::remove(storePath)) { + qWarning() << "deleteExistingService: Unable to remove PST file at:" << storePath; + } + } + return true; + } else { + qWarning() << "deleteExistingService: DeleteMsgService failed"; + } + } + } + + return false; +} + +QByteArray defaultProfile() +{ + QByteArray result; + + LPPROFADMIN profAdmin(0); + HRESULT rv = MAPIAdminProfiles(0, &profAdmin); + if (HR_SUCCEEDED(rv)) { + // Find the default profile + foreach (const ProfileDetail &profile, profileDetails(profAdmin)) { + if (profile.second) { + result = profile.first; + break; + } + } + } else { + qWarning() << "defaultProfile: MAPIAdminProfiles failed"; + } + return result; +} + +IMAPISession *profileSession(const QByteArray &profileName) +{ + IMAPISession *session(0); + + if (!profileName.isEmpty()) { + // Open a session on the profile + QByteArray name(profileName); + HRESULT rv = MAPILogonEx(0, reinterpret_cast(name.data()), 0, MAPI_EXTENDED | MAPI_NEW_SESSION | MAPI_NO_MAIL, &session); + if (HR_FAILED(rv)) { + session = 0; + qWarning() << "profileSession: MAPILogonEx failed"; + } + } + + return session; +} +#endif + +#ifndef _WIN32_WCE +IMAPISession *defaultSession() { return profileSession(defaultProfile()); } +#else +ICEMAPISession *defaultSession() +{ + ICEMAPISession *session(0); + + // Open a session on the profile + HRESULT rv = MAPILogonEx(0, 0, 0, 0, reinterpret_cast(&session)); + if (HR_FAILED(rv)) { + session = 0; + qWarning() << "defaultSession: MAPILogonEx failed"; + } + + return session; +} +#endif + +#ifdef _WIN32_WCE +bool deleteExistingStore(const QByteArray &entryId, ICEMAPISession *session) +{ + if (session) { + HRESULT rv = session->DeleteMsgStore(entryId.count(), reinterpret_cast(const_cast(entryId.data()))); + if (HR_SUCCEEDED(rv)) { + return true; + } else { + qWarning() << "deleteExistingStore: DeleteMsgStore failed"; + } + } + + return false; +} +#endif + +IMsgStore *openStore(const QByteArray &entryId, IMAPISession* session) +{ + IMsgStore *store(0); + + if (session && !entryId.isEmpty()) { + HRESULT rv = session->OpenMsgStore(0, entryId.length(), reinterpret_cast(const_cast(entryId.data())), 0, MDB_NO_MAIL | MDB_WRITE, reinterpret_cast(&store)); + if (HR_FAILED(rv)) { + store = 0; + qWarning() << "openStore: OpenMsgStore failed"; + } + } + + return store; +} + +#ifndef _WIN32_WCE +IMsgStore *openStoreByName(const QByteArray &storeName, IMAPISession* session) +#else +IMsgStore *openStoreByName(const QString &storeName, IMAPISession* session) +#endif +{ + IMsgStore *store(0); + + if (session && !storeName.isEmpty()) { + QByteArray entryId; + + // Find the store with the specified name + IMAPITable *storesTable(0); + HRESULT rv = session->GetMsgStoresTable(0, &storesTable); + if (HR_SUCCEEDED(rv)) { +#ifndef _WIN32_WCE + SizedSPropTagArray(2, cols) = {2, {PR_DISPLAY_NAME_A, PR_ENTRYID}}; +#else + SizedSPropTagArray(2, cols) = {2, {PR_DISPLAY_NAME, PR_ENTRYID}}; +#endif + + QueryAllRows qar(storesTable, reinterpret_cast(&cols), 0, 0, false); + while(qar.query()) { + for (uint n = 0; n < qar.rows()->cRows; ++n) { + if (qar.rows()->aRow[n].lpProps[0].ulPropTag == cols.aulPropTag[0]) { +#ifndef _WIN32_WCE + QByteArray name(qar.rows()->aRow[n].lpProps[0].Value.lpszA); +#else + QString name(QStringFromLptstr(qar.rows()->aRow[n].lpProps[0].Value.lpszW)); +#endif + if (name.toLower() == storeName.toLower()) { + entryId = binaryResult(qar.rows()->aRow[n].lpProps[1]); + break; + } + } + } + } + + if(qar.error() != QMessageManager::NoError) + qWarning() << "openStoreByName: QueryAllRows failed"; + + storesTable->Release(); + } else { + qWarning() << "openStoreByName: GetMsgStoresTable failed"; + } + + if (!entryId.isEmpty()) { + store = openStore(entryId, session); + } + } + + return store; +} + +QByteArray rootFolderEntryId(IMsgStore *store) +{ + return objectProperty(store, PR_IPM_SUBTREE_ENTRYID); +} + +IMAPIFolder *openFolder(const QByteArray &entryId, IMsgStore *store) +{ + IMAPIFolder *folder(0); + + if (store && !entryId.isEmpty()) { + ULONG type(0); + QByteArray entry(entryId); + HRESULT rv = store->OpenEntry(entry.length(), reinterpret_cast(entry.data()), 0, MAPI_MODIFY, &type, reinterpret_cast(&folder)); + if (HR_FAILED(rv)) { + folder = 0; + qWarning() << "openFolder: OpenEntry failed"; + } + } + + return folder; +} + +IMAPIFolder *openFolder(const QByteArray &entryId, IMAPIFolder *container) +{ + IMAPIFolder *folder(0); + + if (container && !entryId.isEmpty()) { + ULONG type(0); + QByteArray entry(entryId); + HRESULT rv = container->OpenEntry(entry.length(), reinterpret_cast(entry.data()), 0, MAPI_MODIFY, &type, reinterpret_cast(&folder)); + if (HR_FAILED(rv)) { + folder = 0; + qWarning() << "openFolder: OpenEntry failed"; + } + } + + return folder; +} + +QList subFolderEntryIds(IMAPIFolder *folder) +{ + QList result; + + if (folder) { + IMAPITable *hierarchyTable(0); + HRESULT rv = folder->GetHierarchyTable(MAPI_UNICODE, &hierarchyTable); + if (HR_SUCCEEDED(rv)) { +#ifndef _WIN32_WCE + SizedSPropTagArray(2, cols) = {2, {PR_OBJECT_TYPE, PR_ENTRYID}}; +#else + SizedSPropTagArray(1, cols) = {1, {PR_ENTRYID}}; +#endif + + QueryAllRows qar(hierarchyTable, reinterpret_cast(&cols), NULL, NULL, false); + while(qar.query()) { + for (uint n = 0; n < qar.rows()->cRows; ++n) { +#ifndef _WIN32_WCE + if ((qar.rows()->aRow[n].lpProps[0].ulPropTag == PR_OBJECT_TYPE) && + (qar.rows()->aRow[n].lpProps[0].Value.l == MAPI_FOLDER)) { + result.append(binaryResult(qar.rows()->aRow[n].lpProps[1])); + } +#else + if (qar.rows()->aRow[n].lpProps[0].ulPropTag == PR_ENTRYID) { + result.append(binaryResult(qar.rows()->aRow[n].lpProps[0])); + } +#endif + } + } + + if(qar.error() != QMessageManager::NoError) + qWarning() << "subFolderEntryIds: QueryAllRows failed"; + + hierarchyTable->Release(); + } + } + + return result; +} + +IMAPIFolder *subFolder(const QString &path, IMAPIFolder *folder, const QString &rootPath) +{ + IMAPIFolder *result(0); + + if (folder && !path.isEmpty()) { + // Find all folders in the current folder + foreach (const QByteArray &entryId, subFolderEntryIds(folder)) { + IMAPIFolder *childFolder = openFolder(entryId, folder); + if (childFolder) { + QString childPath; + +#ifndef _WIN32_WCE + ULONG tag = getNamedPropertyTag(childFolder, "path"); + if (tag) { + childPath = getNamedProperty(childFolder, tag); + } +#else + // Folders do not support named properties on CE... + if (!rootPath.isEmpty()) { + childPath = rootPath + '/'; + } + childPath += stringProperty(childFolder, PR_DISPLAY_NAME); +#endif + + if (childPath == path) { + // This is the folder we're looking for + result = childFolder; + } else if (path.startsWith(childPath)) { + // This must be a parent of the folder we're looking for + result = subFolder(path, childFolder, childPath); + } + + if (childFolder != result) { + childFolder->Release(); + } + } + + if (result) { + break; + } + } + } + + return result; + +#ifndef _WIN32_WCE + Q_UNUSED(rootPath) +#endif +} + +IMAPIFolder *createFolder(const QString &name, IMAPIFolder *folder) +{ + IMAPIFolder *newFolder(0); + + if (folder && !name.isEmpty()) { + HRESULT rv = folder->CreateFolder(FOLDER_GENERIC, reinterpret_cast(const_cast(name.utf16())), 0, 0, MAPI_UNICODE, &newFolder); + if (HR_FAILED(rv)) { + newFolder = 0; + qWarning() << "createFolder: CreateFolder failed"; + } + } + + return newFolder; +} + +QByteArray folderRecordKey(IMAPIFolder *folder) +{ + return objectProperty(folder, PR_RECORD_KEY); +} + +QByteArray folderEntryId(IMAPIFolder *folder) +{ + return objectProperty(folder, PR_ENTRYID); +} + +QByteArray storeRecordKey(IMsgStore *store) +{ +#ifndef _WIN32_WCE + return objectProperty(store, PR_RECORD_KEY); +#else + return objectProperty(store, PR_ENTRYID); +#endif +} + +} + +namespace Support { + +void clearMessageStore() +{ + // Ensure the store is instantiated + doInit(); + + // Remove any existing stores that we added previously +#ifndef _WIN32_WCE + IProfAdmin *profAdmin = openProfileAdmin(); + if (profAdmin) { + QByteArray defaultProfileName = findDefaultProfileName(profAdmin); + if (!defaultProfileName.isEmpty()) { + IMAPISession *session = profileSession(defaultProfileName); + if (session) { + IMsgServiceAdmin *svcAdmin = openServiceAdmin(defaultProfileName, profAdmin); + if (svcAdmin) { + char *providerName = "MSUPST MS"; + QList obsoleteUids; + + foreach (const ServiceDetail &svc, serviceDetails(svcAdmin)) { + // Find all services that have our provider + if (svc.first.first.toLower() == QByteArray(providerName).toLower()) { + IMsgStore *store = openStoreByName(svc.first.second, session); + if (store) { + // Did we create this store + ULONG tag = getNamedPropertyTag(store, "origin"); + if (tag) { + if (getNamedProperty(store, tag) == "QMF") { + // This is an existing service we need to remove + obsoleteUids.append(svc.second); + } + } + + store->Release(); + } + } + } + + foreach (const MAPIUID &uid, obsoleteUids) { + deleteExistingService(uid, svcAdmin); + } + + svcAdmin->Release(); + } + + session->Release(); + } + } + + profAdmin->Release(); + } +#else + ICEMAPISession *session = defaultSession(); + if (session) { + QList obsoleteEntryIds; + + foreach (const StoreDetail &detail, storeDetails(session)) { + IMsgStore *store = openStore(detail.second, session); + if (store) { + // Did we create this store? + ULONG tag = getNamedPropertyTag(store, "origin"); + if (tag) { + if (getNamedProperty(store, tag) == "QMF") { + // This is an existing store we need to remove + obsoleteEntryIds.append(detail.second); + } + } + + store->Release(); + } + } + + foreach (const QByteArray &entryId, obsoleteEntryIds) { + deleteExistingStore(entryId, session); + } + + session->Release(); + } +#endif +} + +QMessageAccountId addAccount(const Parameters ¶ms) +{ + QMessageAccountId result; + + doInit(); + + QString accountName(params["name"]); + QString fromAddress(params["fromAddress"]); + + if (!accountName.isEmpty()) { + // Profile name must be ASCII + QByteArray name(accountName.toAscii()); + +#ifndef _WIN32_WCE + // See if a profile exists with the given name + IProfAdmin *profAdmin = openProfileAdmin(); + if (profAdmin) { + QByteArray defaultProfileName = findDefaultProfileName(profAdmin); + if (!defaultProfileName.isEmpty()) { + IMAPISession *session = profileSession(defaultProfileName); + if (session) { + IMsgServiceAdmin *svcAdmin = openServiceAdmin(defaultProfileName, profAdmin); + if (svcAdmin) { + char *providerName = "MSUPST MS"; + QList existingServices; + + foreach (const ServiceDetail &svc, serviceDetails(svcAdmin)) { + // Find all services that have our provider + if (svc.first.first.toLower() == QByteArray(providerName).toLower()) { + existingServices.append(QByteArray(reinterpret_cast(&svc.second), sizeof(MAPIUID))); + } + } + + // Create a message service for this profile using the standard provider + HRESULT rv = svcAdmin->CreateMsgService(reinterpret_cast(providerName), 0, 0, 0); + if (HR_SUCCEEDED(rv)) { + // Find which of the now-extant services was not in the previous set + foreach (const ServiceDetail &svc, serviceDetails(svcAdmin)) { + QByteArray uidData(reinterpret_cast(&svc.second), sizeof(MAPIUID)); + if ((svc.first.first.toLower() == QByteArray(providerName).toLower()) && + !existingServices.contains(uidData)) { + // Create a .PST message store for this service + QByteArray path(QString("%1.pst").arg(name.constData()).toAscii()); + + SPropValue props[3] = { 0 }; + props[0].ulPropTag = PR_DISPLAY_NAME_A; + props[0].Value.lpszA = name.data(); + props[1].ulPropTag = PR_PST_PATH_A; + props[1].Value.lpszA = path.data(); + props[2].ulPropTag = PR_PST_CONFIG_FLAGS; + props[2].Value.l = PST_CONFIG_UNICODE; + + MAPIUID svcUid = svc.second; + rv = svcAdmin->ConfigureMsgService(&svcUid, 0, 0, 3, props); + if (HR_SUCCEEDED(rv)) { + foreach (const StoreDetail &store, storeDetails(session)) { + if (store.first.toLower() == name.toLower()) { + result = accountIdFromRecordKey(store.second.first); + + IMsgStore *newStore = openStore(store.second.second, session); + if (newStore) { + // Add an origin tag to this store + ULONG originTag = createNamedProperty(newStore, "origin"); + if (originTag) { + setNamedProperty(newStore, originTag, "QMF"); + } + + if (!fromAddress.isEmpty()) { + // Try to set the address for this service, as a property of the store + ULONG addressTag = createNamedProperty(newStore, "fromAddress"); + if (addressTag) { + setNamedProperty(newStore, addressTag, fromAddress); + } + } + + newStore->Release(); + } + break; + } + } + } else { + qWarning() << "ConfigureMsgService failed"; + } + break; + } + } + } else { + qWarning() << "CreateMsgService failed"; + } + + svcAdmin->Release(); + } + + session->Release(); + } + } + + profAdmin->Release(); + } +#else + ICEMAPISession *session(defaultSession()); + if (session) { + QSet existingStoreNames; + foreach (const StoreDetail &detail, storeDetails(session)) { + existingStoreNames.insert(detail.first); + } + + if (existingStoreNames.contains(accountName)) { + qWarning() << "Store name already in use:" << accountName; + } else { + IMsgStore *newStore(0); + HRESULT rv = session->CreateMsgStore(reinterpret_cast(accountName.constData()), &newStore); + if (HR_SUCCEEDED(rv)) { + // Add an origin tag to this store + ULONG originTag = createNamedProperty(newStore, "origin"); + if (originTag) { + setNamedProperty(newStore, originTag, "QMF"); + } + + if (!fromAddress.isEmpty()) { + // Try to set the address for this service, as a property of the store + ULONG addressTag = createNamedProperty(newStore, "fromAddress"); + if (addressTag) { + setNamedProperty(newStore, addressTag, fromAddress); + } + } + + // Create an ID from the record key - actually, the entry ID for CE... + result = accountIdFromRecordKey(objectProperty(newStore, PR_ENTRYID)); + + newStore->Release(); + } else { + qWarning() << "Unable to create new store:" << name; + qDebug() << "rv:" << hex << (ULONG) rv; + } + } + + session->Release(); + } +#endif + } + + return result; +} + +QMessageFolderId addFolder(const Parameters ¶ms) +{ + QMessageFolderId result; + + doInit(); + + QString folderPath(params["path"]); + QString folderName(params["name"]); + QString parentPath(params["parentFolderPath"]); + QByteArray accountName(params["parentAccountName"].toAscii()); + + if (folderName.isEmpty()) { + int index = folderPath.lastIndexOf('/'); + folderName = folderPath.mid(index + 1); + } + + if (!folderName.isEmpty() && !folderPath.isEmpty() && !accountName.isEmpty()) { + // Open a session on the default profile + IMAPISession *session(defaultSession()); + if (session) { + // Open the store for modification + IMsgStore *store = openStoreByName(accountName, session); + if (store) { + // Open the root folder for modification + IMAPIFolder *folder = openFolder(rootFolderEntryId(store), store); + if (folder) { + // Find the parent folder for the new folder + if (!parentPath.isEmpty()) { + IMAPIFolder *parentFolder = subFolder(parentPath, folder, QString()); + folder->Release(); + folder = parentFolder; + } + + if (folder) { + IMAPIFolder *newFolder = createFolder(folderName, folder); + if (newFolder) { +#ifndef _WIN32_WCE + // Named properties not supported by folders on CE... + ULONG tag = createNamedProperty(newFolder, "path"); + if (tag) { + setNamedProperty(newFolder, tag, folderPath); + } +#endif + +#ifndef _WIN32_WCE + QByteArray recordKey = folderRecordKey(newFolder); +#else + QByteArray recordKey; +#endif + QByteArray entryId = folderEntryId(newFolder); + QByteArray storeKey = storeRecordKey(store); + result = folderIdFromProperties(recordKey, entryId, storeKey); + + newFolder->Release(); + } + + folder->Release(); + } else { + qWarning() << "Unable to locate parent folder for addition"; + } + } else { + qWarning() << "Unable to open root folder for addition"; + } + + store->Release(); + } + + session->Release(); + } + } + + return result; +} + +} + +QTM_BEGIN_NAMESPACE + +// The class 'MapiSession' is a friend of QMessageContentContainer - hijack it here +class QTM_PREPEND_NAMESPACE(MapiSession) +{ +public: + static QMessageId addMessage(const Support::Parameters ¶ms) + { + QString parentAccountName(params["parentAccountName"]); + QString parentFolderPath(params["parentFolderPath"]); + QString to(params["to"]); + QString cc(params["cc"]); + QString from(params["from"]); + QString date(params["date"]); + QString receivedDate(params["receivedDate"]); + QString subject(params["subject"]); + QString text(params["text"]); + QString mimeType(params["mimeType"]); + QString attachments(params["attachments"]); + QString priority(params["priority"]); + QString size(params["size"]); + QString type(params["type"]); + QString read(params["status-read"]); + QString hasAttachments(params["status-hasAttachments"]); + + QMessageManager manager; + + if (!to.isEmpty() && !from.isEmpty() && !date.isEmpty() && !subject.isEmpty() && + !parentAccountName.isEmpty() && !parentFolderPath.isEmpty()) { + // Find the named account + QMessageAccountIdList accountIds(manager.queryAccounts(QMessageAccountFilter::byName(parentAccountName))); + if (accountIds.count() == 1) { + // Find the specified folder + QMessageFolderFilter filter(QMessageFolderFilter::byPath(parentFolderPath, QMessageDataComparator::Equal) & QMessageFolderFilter::byParentAccountId(accountIds.first())); + QMessageFolderIdList folderIds(manager.queryFolders(filter)); + if (folderIds.count() == 1) { + QMessage message; + + message.setParentAccountId(accountIds.first()); + message.d_ptr->_parentFolderId = folderIds.first(); + + QList toList; + foreach (const QString &addr, to.split(",", QString::SkipEmptyParts)) { + toList.append(QMessageAddress(QMessageAddress::Email, addr.trimmed())); + } + message.setTo(toList); + + QList ccList; + foreach (const QString &addr, cc.split(",", QString::SkipEmptyParts)) { + ccList.append(QMessageAddress(QMessageAddress::Email, addr.trimmed())); + } + if (!ccList.isEmpty()) { + message.setCc(ccList); + } + + message.setFrom(QMessageAddress(QMessageAddress::Email, from)); + message.setSubject(subject); + + QDateTime dt(QDateTime::fromString(date, Qt::ISODate)); + dt.setTimeSpec(Qt::UTC); + message.setDate(dt); + + if (type.isEmpty()) { + message.setType(QMessage::Email); + } else { + if (type.toLower() == "mms") { + message.setType(QMessage::Mms); + } else if (type.toLower() == "sms") { + message.setType(QMessage::Sms); + } else if (type.toLower() == "xmpp") { + message.setType(QMessage::Xmpp); + } else { + message.setType(QMessage::Email); + } + } + + if (!receivedDate.isEmpty()) { + QDateTime dt(QDateTime::fromString(receivedDate, Qt::ISODate)); + dt.setTimeSpec(Qt::UTC); + message.setReceivedDate(dt); + } + + if (!priority.isEmpty()) { + if (priority.toLower() == "high") { + message.setPriority(QMessage::HighPriority); + } else if (priority.toLower() == "low") { + message.setPriority(QMessage::LowPriority); + } + } + + if (!size.isEmpty()) { + message.d_ptr->_size = size.toUInt(); + } + + if (!text.isEmpty()) { + message.setBody(text, mimeType.toAscii()); + } + + if (!attachments.isEmpty()) { + message.appendAttachments(attachments.split("\n")); + } + + QMessage::StatusFlags flags(0); + if (read.toLower() == "true") { + flags |= QMessage::Read; + } + if (hasAttachments.toLower() == "true") { + flags |= QMessage::HasAttachments; + } + message.setStatus(flags); + + if (!manager.addMessage(&message)) { + qWarning() << "Unable to addMessage:" << to << from << date << subject; + } else { + return message.id(); + } + } else { + qWarning() << "Unable to locate parent folder:" << parentFolderPath; + } + } else { + qWarning() << "Unable to locate parent account:" << parentAccountName; + } + } else { + qWarning() << "Necessary information missing"; + } + + return QMessageId(); + } +}; + +QTM_END_NAMESPACE + +namespace Support { + +QMessageId addMessage(const Parameters ¶ms) +{ + return MapiSession::addMessage(params); +} + +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) +/* + * Returns true if a MAPI subsystem is available, as per the + * information at + * http://msdn.microsoft.com/en-us/library/cc815368.aspx + * + * Returns false if a MAPI subsystem could not be found. + */ +bool mapiAvailable() +{ + bool mapix = false; + LONG res = -1; + HKEY key; + res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, + L"SOFTWARE\\Microsoft\\Windows Messaging Subsystem", + 0, + KEY_READ, + &key); + + if (res == ERROR_SUCCESS) { + unsigned long type = REG_SZ; + unsigned long size = 512; + char ret[512] = ""; + res = RegQueryValueExW(key, + L"MAPIX", + 0, + &type, + (LPBYTE)&ret[0], + &size); + + if (res == ERROR_SUCCESS && (QString::fromUtf16((const ushort*)ret).toInt() == 1)) + mapix = true; + } + + RegCloseKey(key); + + return mapix; +} +#endif + +}