/* Exchange notifier plugin for Miranda IM Copyright © 2006 Cristian Libotean, Attila Vajda This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /******************************************************************** created: 2006/04/11 created: 11:4:2006 17:28 filename: MirandaExchange.cpp file base: MirandaExchange file ext: cpp author: Attila Vajda purpose: Miranda Exchange Plugin *********************************************************************/ #include "stdafx.h" #include "MirandaExchange.h" #include "commonheaders.h" #include "utils.h" #include #ifndef NO_EXCHANGE_TEST ////////////////////////////////////////////////////////////////////////// HRESULT HrMAPIFindDefaultMsgStore( // RETURNS: return code IN LPMAPISESSION lplhSession, // session pointer OUT ULONG *lpcbeid, // count of bytes in entry ID OUT LPENTRYID *lppeid) // entry ID of default store { HRESULT hr = NOERROR; HRESULT hrT = NOERROR; SCODE sc = 0; LPMAPITABLE lpTable = NULL; LPSRowSet lpRows = NULL; LPENTRYID lpeid = NULL; ULONG cbeid = 0; ULONG cRows = 0; ULONG i = 0; SizedSPropTagArray(2, rgPropTagArray)={2,{PR_DEFAULT_STORE,PR_ENTRYID}}; // Get the list of available message stores from MAPI hrT = MAPICALL(lplhSession)->GetMsgStoresTable( 0, &lpTable); if(!FAILED(hrT)) { // Get the row count for the message recipient table hrT = MAPICALL(lpTable)->GetRowCount(0, &cRows); if(!FAILED(hrT)) { // Set the columns to return hrT = MAPICALL(lpTable)->SetColumns((LPSPropTagArray)&rgPropTagArray, 0); if(!FAILED(hrT)) { // Go to the beginning of the recipient table for the envelope hrT = MAPICALL(lpTable)->SeekRow( BOOKMARK_BEGINNING, 0, NULL); if(!FAILED(hrT)) { // Read all the rows of the table hrT = MAPICALL(lpTable)->QueryRows( cRows, 0, &lpRows); if(SUCCEEDED(hrT) && (lpRows != NULL) && (lpRows->cRows == 0)) { FreeProws(lpRows); lpRows = NULL; hrT = MAPI_E_NOT_FOUND; } } } } } if( !FAILED(hrT) ) { bool bGetOut = false; for(i = 0; (i < cRows) && (!bGetOut); i++) { if(lpRows->aRow[i].lpProps[0].Value.b == TRUE) { cbeid = lpRows->aRow[i].lpProps[1].Value.bin.cb; sc = MAPIAllocateBuffer(cbeid, (void **)&lpeid); if(FAILED(sc)) { cbeid = 0; lpeid = NULL; bGetOut = true; } else { // Copy entry ID of message store CopyMemory(lpeid,lpRows->aRow[i].lpProps[1].Value.bin.lpb,cbeid); bGetOut = true; } } } } if(lpRows != NULL) { FreeProws(lpRows); } UlRelease(lpTable); *lpcbeid = cbeid; *lppeid = lpeid; return hr; } CKeeper::CKeeper( LPTSTR szSender, LPTSTR szSubject, LPSTR szEntryID) { m_szSender = NULL ; m_szSubject = NULL ; m_szEntryID = NULL ; m_nSizeSender = 0 ; m_nSizeSubject = 0 ; m_nSizeEntryID = 0 ; if (NULL != szSender ) { m_nSizeSender = _tcslen ( szSender ) +1; m_szSender = new TCHAR[ m_nSizeSender ]; memset ( m_szSender, 0, m_nSizeSender * sizeof(TCHAR) ); _tcscpy( m_szSender, szSender); } if ( NULL != szSubject ) { m_nSizeSubject = _tcslen( szSubject ) +1; m_szSubject = new TCHAR[m_nSizeSubject]; memset ( m_szSubject, 0, m_nSizeSubject * sizeof(TCHAR) ); _tcscpy( m_szSubject, szSubject ); } if ( NULL != szEntryID ) { m_nSizeEntryID = strlen( szEntryID ) +1; m_szEntryID = new char[m_nSizeEntryID]; memset ( m_szEntryID, 0, m_nSizeEntryID * sizeof(char) ); strcpy( m_szEntryID, szEntryID ); } } CKeeper::~CKeeper() { if ( m_nSizeSender>0 && NULL != m_szSender ) { m_nSizeSender =0; delete[] m_szSender; m_szSender = NULL; } if ( m_nSizeSubject>0 && NULL != m_szSubject ) { m_nSizeSender =0; delete[] m_szSubject; m_szSubject = NULL; } if ( m_nSizeEntryID>0 && NULL != m_szEntryID ) { m_nSizeEntryID = 0; delete[] m_szEntryID; m_szEntryID = NULL; } } CMirandaExchange::CMirandaExchange() { UINT nSize = 0; short nSizeOfTCHAR = sizeof( TCHAR ); m_szUsername = NULL ; m_szPassword = NULL ; m_szExchangeServer = NULL ; m_lpMAPISession = NULL ; m_lpInbox = NULL ; m_lpMDB = NULL; m_bLoginOK = false ; m_bFolderInboxOK = false ; m_nNumberOfHeaders = 0 ; } CMirandaExchange::~CMirandaExchange() { if ( NULL != m_szUsername ) { delete[] m_szUsername; m_szUsername = NULL; } if ( NULL != m_szPassword ) { delete[] m_szPassword; m_szPassword = NULL; } if ( NULL != m_szExchangeServer ) { delete[] m_szExchangeServer; m_szExchangeServer = NULL; } if ( NULL != m_lpInbox ) { UlRelease(m_lpInbox); m_lpInbox = NULL; } if ( NULL != m_lpMDB ) { UlRelease(m_lpMDB ); m_lpMDB = NULL; } if( NULL!= m_lpMAPISession ) { m_lpMAPISession->Logoff(NULL,NULL,NULL); UlRelease(m_lpMAPISession ); m_lpMAPISession = NULL; } if ( m_nNumberOfHeaders>0 && NULL != m_HeadersKeeper ) { for( UINT i=0; iOpenEntry( cbEntryID, lpEntryID, NULL,//no interface ulFlags, &ulObjType, &lpUnk); if (MAPI_E_UNKNOWN_FLAGS == hRes && ulNoCacheFlags) { hRes = S_OK; if (lpUnk) (lpUnk)->Release(); lpUnk = NULL; (lpMDB->OpenEntry( cbEntryID, lpEntryID, NULL,//no interface ulNoCacheFlags, &ulObjType, &lpUnk)); } if (FAILED(hRes)) { if (lpUnk) (lpUnk)->Release(); lpUnk = NULL; } } if (lpAB && !lpUnk) { hRes = S_OK; //Log(_T("CallOpenEntry: Calling OpenEntry on AB with ulFlags = 0x%X\n"),ulFlags); (lpAB->OpenEntry( cbEntryID, lpEntryID, NULL,//no interface ulFlags, &ulObjType, &lpUnk)); if (MAPI_E_UNKNOWN_FLAGS == hRes && ulNoCacheFlags) { hRes = S_OK; if (lpUnk) (lpUnk)->Release(); lpUnk = NULL; (lpAB->OpenEntry( cbEntryID, lpEntryID, NULL,//no interface ulNoCacheFlags, &ulObjType, &lpUnk)); } if (FAILED(hRes)) { if (lpUnk) (lpUnk)->Release(); lpUnk = NULL; } } if (lpContainer && !lpUnk) { hRes = S_OK; //Log(_T("CallOpenEntry: Calling OpenEntry on Container with ulFlags = 0x%X\n"),ulFlags); (lpContainer->OpenEntry( cbEntryID, lpEntryID, NULL,//no interface ulFlags, &ulObjType, &lpUnk)); if (MAPI_E_UNKNOWN_FLAGS == hRes && ulNoCacheFlags) { hRes = S_OK; if (lpUnk) (lpUnk)->Release(); lpUnk = NULL; (lpContainer->OpenEntry( cbEntryID, lpEntryID, NULL,//no interface ulNoCacheFlags, &ulObjType, &lpUnk)); } if (FAILED(hRes)) { if (lpUnk) (lpUnk)->Release(); lpUnk = NULL; } } if (lpMAPISession && !lpUnk) { hRes = S_OK; //Log(_T("CallOpenEntry: Calling OpenEntry on Session with ulFlags = 0x%X\n"),ulFlags); (lpMAPISession->OpenEntry( cbEntryID, lpEntryID, NULL,//no interface ulFlags, &ulObjType, &lpUnk)); if (MAPI_E_UNKNOWN_FLAGS == hRes && ulNoCacheFlags) { hRes = S_OK; if (lpUnk) (lpUnk)->Release(); lpUnk = NULL; (lpMAPISession->OpenEntry( cbEntryID, lpEntryID, NULL,//no interface ulNoCacheFlags, &ulObjType, &lpUnk)); } if (FAILED(hRes)) { if (lpUnk) (lpUnk)->Release(); lpUnk = NULL; } } if (lpUnk) { //Log(_T("OnOpenEntryID: Got object (0x%08X) of type 0x%08X = %s\n"),lpUnk,ulObjType,ObjectTypeToString(ulObjType)); *lppUnk = lpUnk; } if (ulObjTypeRet) *ulObjTypeRet = ulObjType; return hRes; } HRESULT CallOpenEntry( LPMDB lpMDB, LPADRBOOK lpAB, LPMAPICONTAINER lpContainer, LPMAPISESSION lpMAPISession, LPSBinary lpSBinary, ULONG ulFlags, ULONG* ulObjTypeRet, LPUNKNOWN* lppUnk) { return CallOpenEntry( lpMDB, lpAB, lpContainer, lpMAPISession, lpSBinary?lpSBinary->cb:0, (LPENTRYID)(lpSBinary?lpSBinary->lpb:0), ulFlags, ulObjTypeRet, lppUnk); } HRESULT CMirandaExchange::InitializeAndLogin( LPCTSTR szUsername, LPCTSTR szPassword, LPCTSTR szExchangeServer ) { _popupUtil("Connecting to Exchange ..."); UINT nSize = 0; short nSizeOfTCHAR = sizeof( TCHAR ); if ( ( m_szUsername == NULL ) && ( NULL != szUsername ) ) { nSize = _tcslen(szUsername); if (nSize>0) { nSize++; m_szUsername = new TCHAR[ nSize ]; memset ( m_szUsername, 0, nSize * nSizeOfTCHAR ); _tcscpy( m_szUsername, szUsername ); } } if ( ( m_szPassword == NULL ) && ( NULL != szPassword ) ) { nSize = _tcslen( szPassword ); if ( nSize>0 ) { nSize++; m_szPassword = new TCHAR[ nSize ]; memset ( m_szPassword, 0, nSize * nSizeOfTCHAR ); _tcscpy( m_szPassword, szPassword ); } } if ( ( m_szExchangeServer == NULL ) && ( NULL != szExchangeServer ) ) { nSize = _tcslen(szExchangeServer); if ( nSize>0 ) { nSize++; m_szExchangeServer = new TCHAR[ nSize ]; memset ( m_szExchangeServer, 0, nSize * nSizeOfTCHAR ); _tcscpy( m_szExchangeServer, szExchangeServer ); } } if ( !m_bLoginOK || m_lpInbox || NULL == m_lpMAPISession ) { HRESULT hr = S_OK; LPMDB lpMDB = NULL; MAPIINIT_0 mapiInit = { MAPI_INIT_VERSION , MAPI_MULTITHREAD_NOTIFICATIONS }; if( !m_bNoInitAgain) { m_bNoInitAgain = true; hr = MAPIInitialize( &mapiInit) ; } if( FAILED(hr) ) { //Log("Failed to initialize MAPI\n"); } else { TCHAR szPIDandName[128]; TCHAR szPID[20]; _tstrtime( szPID ); _tcscpy(szPIDandName,m_szUsername); _tcscat(szPIDandName,szPID ); hr = CreateProfile( szPIDandName); if ( HR_FAILED(hr ) ) { //Log("Create profile failed: 0x%08X", hr); return hr; } DWORD dwFlags = MAPI_EXPLICIT_PROFILE|MAPI_EXTENDED|MAPI_NEW_SESSION|MAPI_NO_MAIL ; hr = MAPILogonEx( 0, szPIDandName, m_szPassword, dwFlags, &m_lpMAPISession ); if (FAILED(hr)) { //Log( _T("MAPI Logon failed: 0x%08X"), hr ); return hr; } LPPROFADMIN pProfAdmin = NULL; hr = MAPIAdminProfiles( 0, &pProfAdmin ); if ((FAILED(hr)) || (NULL == pProfAdmin)) { //Log("Admin profile interface creation failed: 0x%08X", hr); } else { hr = pProfAdmin->DeleteProfile( szPIDandName, 0 ); if ( FAILED(hr) ) { //Log( "Failed to delete the profile: 0x%08X", hr ); } } if (pProfAdmin) { pProfAdmin->Release(); } ULONG cbDefStoreEid = 0; CMAPIBuffer< LPENTRYID> pDefStoreEid = NULL; hr = HrMAPIFindDefaultMsgStore(m_lpMAPISession, &cbDefStoreEid, &pDefStoreEid ); if (FAILED(hr)) { return hr; } // Open default message store LPMDB pDefMsgStore = NULL; hr = m_lpMAPISession->OpenMsgStore(0, cbDefStoreEid, pDefStoreEid, NULL, MAPI_BEST_ACCESS, &pDefMsgStore); HRESULT hRes = S_OK; ULONG cbInboxEID = NULL; CMAPIBuffer< LPENTRYID> lpInboxEID = NULL; if (NULL == pDefMsgStore ) { return hr; } hRes = pDefMsgStore->GetReceiveFolder( _T("IPM"), NULL, &cbInboxEID, &lpInboxEID, NULL); m_lpMDB = pDefMsgStore; if (cbInboxEID && lpInboxEID) { hRes = CallOpenEntry( pDefMsgStore, NULL, NULL, NULL, cbInboxEID, lpInboxEID, MAPI_BEST_ACCESS, NULL, (LPUNKNOWN*)&m_lpInbox); if ( m_lpInbox && hRes == S_OK) { m_bFolderInboxOK = true; } } } } return S_OK; } HRESULT CMirandaExchange::CreateProfile( LPTSTR szProfileName ) { //TCHAR sz[200];_stprintf(sz, "Create Profile('%s', '%s', '%s')", szProfileName, m_szUsername, m_szExchangeServer);Log(sz); HRESULT hr = S_OK; CMAPIInterface pProfAdmin = NULL; CMAPIInterface pMsgSvcAdmin = NULL; CMAPIInterface pMsgSvcTable = NULL; LPSRowSet pRows = NULL; enum {iSvcName, iSvcUID, cptaSvc}; SizedSPropTagArray(cptaSvc, sptCols) = { cptaSvc, PR_SERVICE_NAME, PR_SERVICE_UID }; hr = MAPIAdminProfiles(0, &pProfAdmin); if (!(FAILED(hr)) || (pProfAdmin)) { hr = pProfAdmin->CreateProfile(szProfileName, NULL, NULL, 0); if (!FAILED(hr)) { hr = pProfAdmin->AdminServices( szProfileName, NULL, NULL, 0, &pMsgSvcAdmin); if ( !(FAILED(hr)) || (pMsgSvcAdmin) ) { hr = pMsgSvcAdmin->CreateMsgService((LPTSTR)"MSEMS", (LPTSTR)""/*"Microsoft Exchange Server"*/, NULL, 0); if (!FAILED(hr)) { hr = pMsgSvcAdmin->GetMsgServiceTable(0, &pMsgSvcTable); if ( !(FAILED(hr)) || ( pMsgSvcTable) ) { SRestriction sres; sres.rt = RES_CONTENT; sres.res.resContent.ulFuzzyLevel = FL_FULLSTRING; sres.res.resContent.ulPropTag = PR_SERVICE_NAME_A; SPropValue spv; sres.res.resContent.lpProp = &spv; spv.ulPropTag = PR_SERVICE_NAME_A; spv.Value.lpszA = (LPSTR)"MSEMS"; hr = HrQueryAllRows(pMsgSvcTable, (LPSPropTagArray) &sptCols, &sres, NULL, 0, &pRows); if (!FAILED(hr)) { UINT nSize = _tcslen(m_szUsername)+2; TCHAR* szUniqName = new TCHAR[nSize]; memset( szUniqName, 0, nSize*sizeof(TCHAR) ); _tcscpy( szUniqName,_T("=")); _tcscat( szUniqName, m_szUsername); // Set values for PR_PROFILE_UNRESOLVED_NAME and PR_PROFILE_UNRESOLVED_SERVER SPropValue spval[2]; spval[0].ulPropTag = PR_PROFILE_UNRESOLVED_NAME; spval[0].Value.lpszA = mir_t2a(szUniqName); spval[1].ulPropTag = PR_PROFILE_UNRESOLVED_SERVER; spval[1].Value.lpszA = mir_t2a(m_szExchangeServer); // Configure msg service /*hr =*/ pMsgSvcAdmin->ConfigureMsgService( (LPMAPIUID) pRows->aRow->lpProps[iSvcUID].Value.bin.lpb, 0, NULL, 2, spval); if (NULL!=szUniqName) { delete[] szUniqName; } } } } } } else { pProfAdmin->DeleteProfile(szProfileName, 0); } } if (pRows) { FreeProws(pRows); } return hr; } HRESULT CMirandaExchange::isMapiSessionOK( LPMAPISESSION lpSession ) { return S_OK; } HRESULT CMirandaExchange::CheckForNewMails( int &nNewMails) { HRESULT hRes; if ( m_nNumberOfHeaders>0 && NULL != m_HeadersKeeper ) { for( UINT i=0; iLogoff( NULL, NULL, NULL ); m_lpMAPISession->Release(); m_lpMAPISession = NULL; } if ( m_nNumberOfHeaders>0 && NULL != m_HeadersKeeper ) { for( UINT i=0; iSetReadFlag( 0 ); hr = lpMessage->SaveChanges(FORCE_SAVE); lpMessage->Release(); lpMessage = NULL; } return 0; } HRESULT CMirandaExchange::CheckInFolder( LPMAPIFOLDER lpFolder ) { HRESULT hr = NOERROR; if ( lpFolder != NULL && m_bFolderInboxOK ) { CMAPIInterface lpTable = NULL ; LPSRowSet lpRow = NULL ; LPSPropValue lpRowProp = NULL ; ULONG i = 0L ; ULONG *lpcbeid = 0 ; SizedSPropTagArray(5,sptaDETAILS) = { 5, { PR_ENTRYID, PR_MESSAGE_FLAGS, PR_SENDER_NAME, PR_ORIGINAL_SENDER_EMAIL_ADDRESS, PR_SUBJECT } }; CMAPIInterface lpMessageTable; hr = lpFolder->GetContentsTable( 0, &lpMessageTable ); if( HR_FAILED( hr ) ) { return -1; } LPSRowSet lpRowsR = NULL; ////////////////////////////////////////////////////////////////////////// SRestriction srRoot; srRoot.rt = RES_BITMASK; srRoot.res.resBitMask.relBMR = BMR_EQZ; srRoot.res.resBitMask.ulPropTag = PR_MESSAGE_FLAGS; srRoot.res.resBitMask.ulMask = MSGFLAG_READ; SizedSSortOrderSet( 1, sso ) = { 1, 0, 0, { PR_MESSAGE_DELIVERY_TIME, TABLE_SORT_DESCEND } }; hr = HrQueryAllRows( lpMessageTable, (LPSPropTagArray) &sptaDETAILS,&srRoot,(LPSSortOrderSet) & sso,0L, &lpRowsR ); ////////////////////////////////////////////////////////////////////////// if( HR_FAILED( hr ) ) { return -1; } else { TCHAR* szSenderName = NULL; TCHAR* szSubject = NULL; LPSTR szEntryID = NULL; for( i = 0; ( i < lpRowsR->cRows) && ( m_nNumberOfHeaders < MAX_NUMBER_OF_HEADERS ); ++i ) { if ( !(lpRowsR->aRow[ i ].lpProps[ 1 ].Value.l & MSGFLAG_READ) ) { if( !FAILED(lpRowsR->aRow[i].lpProps[2].Value.err) ) { szSenderName = lpRowsR->aRow[i].lpProps[2].Value.lpszW; } if ( NULL == szSenderName) { if( !FAILED(lpRowsR->aRow[i].lpProps[3].Value.err)) { szSenderName = lpRowsR->aRow[i].lpProps[3].Value.lpszW; } } if( !FAILED(lpRowsR->aRow[i].lpProps[4].Value.err) ) { szSubject = lpRowsR->aRow[i].lpProps[4].Value.lpszW; } szEntryID = BinToHex( lpRowsR->aRow[i].lpProps[0].Value.bin.cb, lpRowsR->aRow[i].lpProps[0].Value.bin.lpb ); m_HeadersKeeper[m_nNumberOfHeaders] = new CKeeper( szSenderName, szSubject, szEntryID ); m_nNumberOfHeaders++; delete[] szEntryID; szEntryID = NULL; szSubject = NULL; szSenderName = NULL; } } if (lpRowsR) { FreeProws(lpRowsR); } } if (m_nNumberOfHeadersGetHierarchyTable( MAPI_DEFERRED_ERRORS, &lpTable); if (!FAILED(hr)) { hr = HrQueryAllRows( lpTable, (LPSPropTagArray)&rgColProps, NULL, NULL, 0L, &lpRow); if (!FAILED(hr)) { for(i = 0; i < lpRow->cRows; i++) { lpRowProp = lpRow->aRow[i].lpProps; CMAPIInterface lpSubFolder = NULL; hr = CallOpenEntry( m_lpMDB, NULL, NULL, NULL, lpRowProp[IENTRYID].Value.bin.cb, (LPENTRYID)lpRowProp[IENTRYID].Value.bin.lpb, MAPI_BEST_ACCESS, NULL, (LPUNKNOWN*)&lpSubFolder ); if( !FAILED(hr) ) { hr = CheckInFolder( lpSubFolder ); //if (FAILED(hr) ){//Log("failed checkinfolder for %s\n",lpRowProp[IDISPNAME].Value.lpszA );} } } } if (NULL != lpRow) { FreeProws(lpRow); lpRow = NULL; } } } } return hr; } HRESULT CMirandaExchange::OpenTheMessage( LPTSTR szEntryID ) { //(Default)//// HKEY_CLASSES_ROOT\mailto\shell\open\command HKEY hTheKey; HRESULT hRes = E_FAIL; TCHAR szRegValue[ 512 ]; DWORD dwLength = 512 ; DWORD dwType = REG_SZ; if( RegOpenKeyEx(HKEY_CLASSES_ROOT, _T("mailto\\shell\\open\\command"), 0, KEY_ALL_ACCESS | KEY_EXECUTE | KEY_QUERY_VALUE , &hTheKey) == ERROR_SUCCESS ) { LONG lResult = RegQueryValueEx( hTheKey, NULL, NULL, (LPDWORD)&dwType, (LPBYTE)szRegValue, &dwLength); RegCloseKey( hTheKey ); if( lResult != ERROR_SUCCESS ) { hRes = E_FAIL; } else { TCHAR* szTheEnd = _tcsstr( szRegValue,_T(".EXE") ); if( NULL != szTheEnd ) { szRegValue[ _tcslen(szRegValue) - _tcslen(szTheEnd) +5 ] = _T('\0'); _tcscat( szRegValue, _T(" /recycle") ); STARTUPINFO si; PROCESS_INFORMATION pi; DWORD dwCode = 0; ZeroMemory ( &si, sizeof ( STARTUPINFO)); si.cb = sizeof ( STARTUPINFO); si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_SHOWNORMAL; if( CreateProcess ( NULL, szRegValue, NULL, NULL, 0, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi )) { hRes = S_OK; } } } } else { hRes = E_FAIL; } return hRes; } #endif //NO_EXCHANGE_TEST