diff options
Diffstat (limited to 'mailnews/import/oexpress')
20 files changed, 5783 insertions, 0 deletions
diff --git a/mailnews/import/oexpress/OEDebugLog.h b/mailnews/import/oexpress/OEDebugLog.h new file mode 100644 index 000000000..47cc6b2ea --- /dev/null +++ b/mailnews/import/oexpress/OEDebugLog.h @@ -0,0 +1,20 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef OEDebugLog_h___ +#define OEDebugLog_h___ + +// Use MOZ_LOG for logging. +#include "mozilla/Logging.h" +extern PRLogModuleInfo *OELOGMODULE; // Logging module + +#define IMPORT_LOG0(x) MOZ_LOG(OELOGMODULE, mozilla::LogLevel::Debug, (x)) +#define IMPORT_LOG1(x, y) MOZ_LOG(OELOGMODULE, mozilla::LogLevel::Debug, (x, y)) +#define IMPORT_LOG2(x, y, z) MOZ_LOG(OELOGMODULE, mozilla::LogLevel::Debug, (x, y, z)) +#define IMPORT_LOG3(a, b, c, d) MOZ_LOG(OELOGMODULE, mozilla::LogLevel::Debug, (a, b, c, d)) + + + +#endif /* OEDebugLog_h___ */ diff --git a/mailnews/import/oexpress/WabObject.cpp b/mailnews/import/oexpress/WabObject.cpp new file mode 100644 index 000000000..e206b74f8 --- /dev/null +++ b/mailnews/import/oexpress/WabObject.cpp @@ -0,0 +1,1132 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <tchar.h> +#include "nscore.h" +#include "nsOE5File.h" +#include "wabobject.h" +#include <algorithm> + +enum { + ieidPR_DISPLAY_NAME = 0, + ieidPR_ENTRYID, + ieidPR_OBJECT_TYPE, + ieidMax +}; + +static const SizedSPropTagArray(ieidMax, ptaEid)= +{ + ieidMax, + { + PR_DISPLAY_NAME, + PR_ENTRYID, + PR_OBJECT_TYPE, + } +}; + + +enum { + iemailPR_DISPLAY_NAME = 0, + iemailPR_ENTRYID, + iemailPR_EMAIL_ADDRESS, + iemailPR_OBJECT_TYPE, + iemailMax +}; +static const SizedSPropTagArray(iemailMax, ptaEmail)= +{ + iemailMax, + { + PR_DISPLAY_NAME, + PR_ENTRYID, + PR_EMAIL_ADDRESS, + PR_OBJECT_TYPE + } +}; + +typedef struct { + bool multiLine; + ULONG tag; + char * pLDIF; +} AddrImportField; + +#define kExtraUserFields 10 +AddrImportField extraUserFields[kExtraUserFields] = { + {true, PR_COMMENT, "description:"}, + {false, PR_BUSINESS_TELEPHONE_NUMBER, "telephonenumber:"}, + {false, PR_HOME_TELEPHONE_NUMBER, "homephone:"}, + {false, PR_COMPANY_NAME, "o:"}, + {false, PR_TITLE, "title:"}, + {false, PR_BUSINESS_FAX_NUMBER, "facsimiletelephonenumber:"}, + {false, PR_LOCALITY, "locality:"}, + {false, PR_STATE_OR_PROVINCE, "st:"}, + {true, PR_STREET_ADDRESS, "streetaddress:"}, + {false, PR_POSTAL_CODE, "postalcode:"} +}; + +#define kWhitespace " \t\b\r\n" + +#define TR_OUTPUT_EOL "\r\n" + +#define kLDIFPerson "objectclass: top" TR_OUTPUT_EOL "objectclass: person" TR_OUTPUT_EOL +#define kLDIFGroup "objectclass: top" TR_OUTPUT_EOL "objectclass: groupOfNames" TR_OUTPUT_EOL + +/*********************************************************************************/ + + +// contructor for CWAB object +// +// pszFileName - FileName of WAB file to open +// if no file name is specified, opens the default +// +CWAB::CWAB(nsIFile *file) +{ + // Here we load the WAB Object and initialize it + m_pUniBuff = NULL; + m_uniBuffLen = 0; + + m_bInitialized = false; + m_lpAdrBook = NULL; + m_lpWABObject = NULL; + m_hinstWAB = NULL; + + { + TCHAR szWABDllPath[MAX_PATH]; + DWORD dwType = 0; + ULONG cbData = sizeof(szWABDllPath); + HKEY hKey = NULL; + + *szWABDllPath = '\0'; + + // First we look under the default WAB DLL path location in the + // Registry. + // WAB_DLL_PATH_KEY is defined in wabapi.h + // + if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, WAB_DLL_PATH_KEY, 0, KEY_READ, &hKey)) { + RegQueryValueEx(hKey, "", NULL, &dwType, (LPBYTE) szWABDllPath, &cbData); + if (dwType == REG_EXPAND_SZ) { + // Expand the environment variables + DWORD bufferSize = ExpandEnvironmentStrings(szWABDllPath, NULL, 0); + if (bufferSize && bufferSize < MAX_PATH) { + TCHAR tmp[MAX_PATH]; + ExpandEnvironmentStrings(szWABDllPath, tmp, bufferSize); + _tcscpy(szWABDllPath, tmp); + } + else { + // This is an error condition. Nothing else is initialized yet, so simply return. + return; + } + + } + } + else { + if (GetSystemDirectory(szWABDllPath, MAX_PATH)) { + _tcsncat(szWABDllPath, WAB_DLL_NAME, + std::min(_tcslen(WAB_DLL_NAME), MAX_PATH - _tcslen(szWABDllPath) - 1)); + } + else { + // Yet another error condition. + return; + } + } + + if(hKey) RegCloseKey(hKey); + + // if the Registry came up blank, we do a loadlibrary on the wab32.dll + // WAB_DLL_NAME is defined in wabapi.h + // + m_hinstWAB = LoadLibrary((lstrlen(szWABDllPath)) ? szWABDllPath : WAB_DLL_NAME); + } + + if(m_hinstWAB) + { + // if we loaded the dll, get the entry point + // + m_lpfnWABOpen = (LPWABOPEN) GetProcAddress(m_hinstWAB, "WABOpen"); + + if(m_lpfnWABOpen) + { + char fName[2] = {0, 0}; + HRESULT hr = E_FAIL; + WAB_PARAM wp = {0}; + wp.cbSize = sizeof(WAB_PARAM); + if (file != nullptr) { + nsCString path; + file->GetNativePath(path); + wp.szFileName = (LPTSTR) ToNewCString(path); + } + else + wp.szFileName = (LPTSTR) fName; + + // if we choose not to pass in a WAB_PARAM object, + // the default WAB file will be opened up + // + hr = m_lpfnWABOpen(&m_lpAdrBook,&m_lpWABObject,&wp,0); + + if(!hr) + m_bInitialized = TRUE; + + } + } + +} + + +// Destructor +// +CWAB::~CWAB() +{ + if (m_pUniBuff) + delete [] m_pUniBuff; + + if(m_bInitialized) + { + if(m_lpAdrBook) + m_lpAdrBook->Release(); + + if(m_lpWABObject) + m_lpWABObject->Release(); + + if(m_hinstWAB) + FreeLibrary(m_hinstWAB); + } +} + + +HRESULT CWAB::IterateWABContents(CWabIterator *pIter, int *pDone) +{ + if (!m_bInitialized || !m_lpAdrBook) + return E_FAIL; + + ULONG ulObjType = 0; + LPMAPITABLE lpAB = NULL; + ULONG cRows = 0; + LPSRowSet lpRowAB = NULL; + LPABCONT lpContainer = NULL; + int cNumRows = 0; + nsresult keepGoing; + + HRESULT hr = E_FAIL; + + ULONG lpcbEID = 0; + LPENTRYID lpEID = NULL; + ULONG rowCount = 0; + ULONG curCount = 0; + + nsString uniStr; + + // Get the entryid of the root PAB container + // + hr = m_lpAdrBook->GetPAB(&lpcbEID, &lpEID); + + if (HR_FAILED(hr)) + goto exit; + + ulObjType = 0; + + // Open the root PAB container + // This is where all the WAB contents reside + // + hr = m_lpAdrBook->OpenEntry(lpcbEID, + (LPENTRYID)lpEID, + NULL, + 0, + &ulObjType, + (LPUNKNOWN *)&lpContainer); + + m_lpWABObject->FreeBuffer(lpEID); + + lpEID = NULL; + + if(HR_FAILED(hr)) + goto exit; + + // Get a contents table of all the contents in the + // WABs root container + // + hr = lpContainer->GetContentsTable(0, &lpAB); + + if(HR_FAILED(hr)) + goto exit; + + hr = lpAB->GetRowCount(0, &rowCount); + if (HR_FAILED(hr)) + rowCount = 100; + if (rowCount == 0) + rowCount = 1; + + // Order the columns in the ContentsTable to conform to the + // ones we want - which are mainly DisplayName, EntryID and + // ObjectType + // The table is gauranteed to set the columns in the order + // requested + // + hr =lpAB->SetColumns((LPSPropTagArray)&ptaEid, 0); + + if(HR_FAILED(hr)) + goto exit; + + + // Reset to the beginning of the table + // + hr = lpAB->SeekRow(BOOKMARK_BEGINNING, 0, NULL); + + if(HR_FAILED(hr)) + goto exit; + + // Read all the rows of the table one by one + // + + do { + + hr = lpAB->QueryRows(1, 0, &lpRowAB); + + if(HR_FAILED(hr)) + break; + + if(lpRowAB) + { + cNumRows = lpRowAB->cRows; + + if (cNumRows) + { + LPTSTR lpsz = lpRowAB->aRow[0].lpProps[ieidPR_DISPLAY_NAME].Value.lpszA; + LPENTRYID lpEID = (LPENTRYID) lpRowAB->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.lpb; + ULONG cbEID = lpRowAB->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.cb; + + // There are 2 kinds of objects - the MAPI_MAILUSER contact object + // and the MAPI_DISTLIST contact object + // For the purposes of this sample, we will only consider MAILUSER + // objects + // + if(lpRowAB->aRow[0].lpProps[ieidPR_OBJECT_TYPE].Value.l == MAPI_MAILUSER) + { + // We will now take the entry-id of each object and cache it + // on the listview item representing that object. This enables + // us to uniquely identify the object later if we need to + // + CStrToUnicode(lpsz, uniStr); + keepGoing = pIter->EnumUser(uniStr.get(), lpEID, cbEID); + curCount++; + if (pDone) { + *pDone = (curCount * 100) / rowCount; + if (*pDone > 100) + *pDone = 100; + } + } + } + FreeProws(lpRowAB); + } + + + } while (SUCCEEDED(hr) && cNumRows && lpRowAB && NS_SUCCEEDED(keepGoing)) ; + + hr = lpAB->SeekRow(BOOKMARK_BEGINNING, 0, NULL); + + if(HR_FAILED(hr)) + goto exit; + + // Read all the rows of the table one by one + // + keepGoing = NS_OK; + do { + + hr = lpAB->QueryRows(1, 0, &lpRowAB); + + if(HR_FAILED(hr)) + break; + + if(lpRowAB) + { + cNumRows = lpRowAB->cRows; + + if (cNumRows) + { + LPTSTR lpsz = lpRowAB->aRow[0].lpProps[ieidPR_DISPLAY_NAME].Value.lpszA; + LPENTRYID lpEID = (LPENTRYID) lpRowAB->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.lpb; + ULONG cbEID = lpRowAB->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.cb; + + // There are 2 kinds of objects - the MAPI_MAILUSER contact object + // and the MAPI_DISTLIST contact object + // For the purposes of this sample, we will only consider MAILUSER + // objects + // + if(lpRowAB->aRow[0].lpProps[ieidPR_OBJECT_TYPE].Value.l == MAPI_DISTLIST) + { + LPABCONT distListContainer = NULL; + // We will now take the entry-id of each object and cache it + // on the listview item representing that object. This enables + // us to uniquely identify the object later if we need to + // + hr = m_lpAdrBook->OpenEntry(cbEID, lpEID, NULL, + 0,&ulObjType,(LPUNKNOWN *)&distListContainer); + + LPMAPITABLE distListTable = NULL; + + + // Get a contents table of the dist list + // + hr = distListContainer->GetContentsTable(0, &distListTable); + if (lpAB) + { + hr = distListTable->GetRowCount(0, &rowCount); + if (HR_FAILED(hr)) + rowCount = 100; + if (rowCount == 0) + rowCount = 1; + + // Order the columns in the ContentsTable to conform to the + // ones we want - which are mainly DisplayName, EntryID and + // ObjectType + // The table is gauranteed to set the columns in the order + // requested + // + hr = distListTable->SetColumns((LPSPropTagArray)&ptaEid, 0); + CStrToUnicode(lpsz, uniStr); + keepGoing = pIter->EnumList(uniStr.get(), lpEID, cbEID, distListTable); + curCount++; + if (pDone) { + *pDone = (curCount * 100) / rowCount; + if (*pDone > 100) + *pDone = 100; + } + } + if (distListContainer) + distListContainer->Release(); + if (distListTable) + distListTable->Release(); + } + } + FreeProws(lpRowAB); + } + + } while (SUCCEEDED(hr) && cNumRows && lpRowAB && NS_SUCCEEDED(keepGoing)) ; + + +exit: + + if (lpContainer) + lpContainer->Release(); + + if (lpAB) + lpAB->Release(); + + return hr; +} + + + + + + +void CWAB::FreeProws(LPSRowSet prows) +{ + ULONG irow; + if (!prows) + return; + for (irow = 0; irow < prows->cRows; ++irow) + m_lpWABObject->FreeBuffer(prows->aRow[irow].lpProps); + m_lpWABObject->FreeBuffer(prows); +} + + +LPDISTLIST CWAB::GetDistList(ULONG cbEid, LPENTRYID pEid) +{ + if (!m_bInitialized || !m_lpAdrBook) + return NULL; + + LPDISTLIST lpDistList = NULL; + ULONG ulObjType; + + m_lpAdrBook->OpenEntry(cbEid, pEid, NULL, 0, &ulObjType, (LPUNKNOWN *)&lpDistList); + return lpDistList; +} + +LPSPropValue CWAB::GetListProperty(LPDISTLIST pUser, ULONG tag) +{ + if (!pUser) + return NULL; + + int sz = CbNewSPropTagArray(1); + SPropTagArray *pTag = (SPropTagArray *) new char[sz]; + pTag->cValues = 1; + pTag->aulPropTag[0] = tag; + LPSPropValue lpProp = NULL; + ULONG cValues = 0; + HRESULT hr = pUser->GetProps(pTag, 0, &cValues, &lpProp); + delete [] pTag; + if (HR_FAILED(hr) || (cValues != 1)) { + if (lpProp) + m_lpWABObject->FreeBuffer(lpProp); + return NULL; + } + return lpProp; +} + +LPMAILUSER CWAB::GetUser(ULONG cbEid, LPENTRYID pEid) +{ + if (!m_bInitialized || !m_lpAdrBook) + return NULL; + + LPMAILUSER lpMailUser = NULL; + ULONG ulObjType; + + m_lpAdrBook->OpenEntry(cbEid, pEid, NULL, 0, &ulObjType, (LPUNKNOWN *)&lpMailUser); + return lpMailUser; +} + +LPSPropValue CWAB::GetUserProperty(LPMAILUSER pUser, ULONG tag) +{ + if (!pUser) + return NULL; + + ULONG uTag = tag; + /* + Getting Unicode does not help with getting the right + international charset. Windoze bloze. + */ + /* + if (PROP_TYPE(uTag) == PT_STRING8) { + uTag = CHANGE_PROP_TYPE(tag, PT_UNICODE); + } + */ + + int sz = CbNewSPropTagArray(1); + SPropTagArray *pTag = (SPropTagArray *) new char[sz]; + pTag->cValues = 1; + pTag->aulPropTag[0] = uTag; + LPSPropValue lpProp = NULL; + ULONG cValues = 0; + HRESULT hr = pUser->GetProps(pTag, 0, &cValues, &lpProp); + if (HR_FAILED(hr) || (cValues != 1)) { + if (lpProp) + m_lpWABObject->FreeBuffer(lpProp); + lpProp = NULL; + if (uTag != tag) { + pTag->cValues = 1; + pTag->aulPropTag[0] = tag; + cValues = 0; + hr = pUser->GetProps(pTag, 0, &cValues, &lpProp); + if (HR_FAILED(hr) || (cValues != 1)) { + if (lpProp) + m_lpWABObject->FreeBuffer(lpProp); + lpProp = NULL; + } + } + } + delete [] pTag; + return lpProp; +} + +void CWAB::CStrToUnicode(const char *pStr, nsString& result) +{ + result.Truncate(); + int wLen = MultiByteToWideChar(CP_ACP, 0, pStr, -1, wwc(m_pUniBuff), 0); + if (wLen >= m_uniBuffLen) { + if (m_pUniBuff) + delete [] m_pUniBuff; + m_pUniBuff = new char16_t[wLen + 64]; + m_uniBuffLen = wLen + 64; + } + if (wLen) { + MultiByteToWideChar(CP_ACP, 0, pStr, -1, wwc(m_pUniBuff), m_uniBuffLen); + result = m_pUniBuff; + } +} + +// If the value is a string, get it... +void CWAB::GetValueString(LPSPropValue pVal, nsString& val) +{ + val.Truncate(); + + if (!pVal) + return; + + switch(PROP_TYPE(pVal->ulPropTag)) { + case PT_STRING8: + CStrToUnicode((const char *) (pVal->Value.lpszA), val); + break; + case PT_UNICODE: + val = (char16_t *) (pVal->Value.lpszW); + break; + case PT_MV_STRING8: { + nsString tmp; + ULONG j; + for(j = 0; j < pVal->Value.MVszA.cValues; j++) { + CStrToUnicode((const char *) (pVal->Value.MVszA.lppszA[j]), tmp); + val += tmp; + val.Append(NS_ConvertASCIItoUTF16(TR_OUTPUT_EOL)); + } + break; + } + case PT_MV_UNICODE: { + ULONG j; + for(j = 0; j < pVal->Value.MVszW.cValues; j++) { + val += (char16_t *) (pVal->Value.MVszW.lppszW[j]); + val.Append(NS_ConvertASCIItoUTF16(TR_OUTPUT_EOL)); + } + break; + } + case PT_I2: + case PT_LONG: + case PT_R4: + case PT_DOUBLE: + case PT_BOOLEAN: { + /* + TCHAR sz[256]; + wsprintf(sz,"%d", pVal->Value.l); + val = sz; + */ + break; + } + + case PT_BINARY: + break; + + default: + break; + } + + val.Trim(kWhitespace, true, true); +} + + +void CWAB::GetValueTime(LPSPropValue pVal, PRTime& val) +{ + if (!pVal) + return; + + if (PROP_TYPE(pVal->ulPropTag) != PT_SYSTIME) + return; + + nsOE5File::FileTimeToPRTime(&pVal->Value.ft, &val); +} + +bool CWAB::IsAvailable() +{ + if (!m_bInitialized || !m_lpAdrBook) + return false; + + ULONG lpcbEID = 0; + LPENTRYID lpEID = NULL; + HRESULT hr = m_lpAdrBook->GetPAB(&lpcbEID, &lpEID); + if (HR_FAILED(hr)) + return false; + + ULONG ulObjType = 0; + LPABCONT lpContainer = NULL; + hr = m_lpAdrBook->OpenEntry(lpcbEID, + (LPENTRYID)lpEID, + NULL, + 0, + &ulObjType, + (LPUNKNOWN *)&lpContainer); + m_lpWABObject->FreeBuffer(lpEID); + + LPMAPITABLE lpAB = NULL; + hr = lpContainer->GetContentsTable(0, &lpAB); + if(HR_FAILED(hr)) { + lpContainer->Release(); + return false; + } + + ULONG rowCount = 0; + hr = lpAB->GetRowCount(0, &rowCount); + lpContainer->Release(); + lpAB->Release(); + return (rowCount != 0); +} + +/* +BOOL CWabIterateProcess::SanitizeMultiLine(CString& val) +{ + val.TrimLeft(); + val.TrimRight(); + int idx = val.FindOneOf("\x0D\x0A"); + if (idx == -1) + return FALSE; + + // needs encoding + U32 bufSz = UMimeEncode::GetBufferSize(val.GetLength()); + P_U8 pBuf = new U8[bufSz]; + U32 len = UMimeEncode::ConvertBuffer((PC_U8)((PC_S8)val), val.GetLength(), pBuf, 66, 52, "\x0D\x0A "); + pBuf[len] = 0; + val = pBuf; + delete pBuf; + return TRUE; +} + +BOOL CWabIterateProcess::EnumUser(LPCTSTR pName, LPENTRYID pEid, ULONG cbEid) +{ + TRACE1("User: %s\n", pName); + + LPMAILUSER pUser = m_pWab->GetUser(cbEid, pEid); + + // Get the "required" strings first + CString lastName; + CString firstName; + CString eMail; + CString nickName; + CString middleName; + + if (!pUser) { + UDialogs::ErrMessage1(IDS_ENTRY_ERROR, pName); + return FALSE; + } + + LPSPropValue pProp = m_pWab->GetUserProperty(pUser, PR_EMAIL_ADDRESS); + if (pProp) { + m_pWab->GetValueString(pProp, eMail); + SanitizeValue(eMail); + m_pWab->FreeProperty(pProp); + } + pProp = m_pWab->GetUserProperty(pUser, PR_GIVEN_NAME); + if (pProp) { + m_pWab->GetValueString(pProp, firstName); + SanitizeValue(firstName); + m_pWab->FreeProperty(pProp); + } + pProp = m_pWab->GetUserProperty(pUser, PR_SURNAME); + if (pProp) { + m_pWab->GetValueString(pProp, lastName); + SanitizeValue(lastName); + m_pWab->FreeProperty(pProp); + } + pProp = m_pWab->GetUserProperty(pUser, PR_MIDDLE_NAME); + if (pProp) { + m_pWab->GetValueString(pProp, middleName); + SanitizeValue(middleName); + m_pWab->FreeProperty(pProp); + } + pProp = m_pWab->GetUserProperty(pUser, PR_NICKNAME); + if (pProp) { + m_pWab->GetValueString(pProp, nickName); + SanitizeValue(nickName); + m_pWab->FreeProperty(pProp); + } + if (nickName.IsEmpty()) + nickName = pName; + if (firstName.IsEmpty()) { + firstName = nickName; + middleName.Empty(); + lastName.Empty(); + } + if (lastName.IsEmpty()) + middleName.Empty(); + + if (eMail.IsEmpty()) + eMail = nickName; + + + // We now have the required fields + // write them out followed by any optional fields! + BOOL result = TRUE; + + if (m_recordsDone) + result = m_out.WriteEol(); + + CString line; + CString header; + line.LoadString(IDS_LDIF_DN_START); + line += firstName; + if (!middleName.IsEmpty()) { + line += ' '; + line += middleName; + } + if (!lastName.IsEmpty()) { + line += ' '; + line += lastName; + } + header.LoadString(IDS_LDIF_DN_MIDDLE); + line += header; + line += eMail; + result = result && m_out.WriteStr(line); + result = result && m_out.WriteEol(); + + line.LoadString(IDS_FIELD_LDIF_FULLNAME); + line += ' '; + line += firstName; + if (!middleName.IsEmpty()) { + line += ' '; + line += middleName; + } + if (!lastName.IsEmpty()) { + line += ' '; + line += lastName; + } + result = result && m_out.WriteStr(line); + result = result && m_out.WriteEol(); + + + line.LoadString(IDS_FIELD_LDIF_GIVENNAME); + line += ' '; + line += firstName; + result = result && m_out.WriteStr(line); + result = result && m_out.WriteEol(); + + if (!lastName.IsEmpty()) { + line.LoadString(IDS_FIELD_LDIF_LASTNAME); + if (!middleName.IsEmpty()) { + line += ' '; + line += middleName; + } + line += ' '; + line += lastName; + result = result && m_out.WriteStr(line); + result = result && m_out.WriteEol(); + } + + result = result && m_out.WriteStr(kLDIFPerson); + + line.LoadString(IDS_FIELD_LDIF_EMAIL); + line += ' '; + line += eMail; + result = result && m_out.WriteStr(line); + result = result && m_out.WriteEol(); + + line.LoadString(IDS_FIELD_LDIF_NICKNAME); + line += ' '; + line += nickName; + result = result && m_out.WriteStr(line); + result = result && m_out.WriteEol(); + + // Do all of the extra fields! + CString value; + BOOL encoded = FALSE; + for (int i = 0; i < kExtraUserFields; i++) { + value.Empty(); + pProp = m_pWab->GetUserProperty(pUser, extraUserFields[i].tag); + if (pProp) { + m_pWab->GetValueString(pProp, value); + m_pWab->FreeProperty(pProp); + } + if (extraUserFields[i].multiLine) { + encoded = SanitizeMultiLine(value); + } + else + SanitizeValue(value); + if (!value.IsEmpty()) { + line = extraUserFields[i].pLDIF; + if (encoded) { + line += ": "; + encoded = FALSE; + } + else + line += ' '; + line += value; + result = result && m_out.WriteStr(line); + result = result && m_out.WriteEol(); + } + } + + m_pWab->ReleaseUser(pUser); + + if (!result) { + UDialogs::ErrMessage0(IDS_ADDRESS_SAVE_ERROR); + } + + m_totalDone += kValuePerUser; + m_recordsDone++; + + return result; +} +*/ + + + + +/* +BOOL CWabIterateProcess::EnumList(LPCTSTR pName, LPENTRYID pEid, ULONG cbEid) +{ + TRACE1("List: %s\n", pName); + + LPDISTLIST pList = m_pWab->GetDistList(cbEid, pEid); + if (!pList) { + UDialogs::ErrMessage1(IDS_ENTRY_ERROR, pName); + return FALSE; + } + + // Find out if this is just a regular entry or a true list... + CString eMail; + LPSPropValue pProp = m_pWab->GetListProperty(pList, PR_EMAIL_ADDRESS); + if (pProp) { + m_pWab->GetValueString(pProp, eMail); + SanitizeValue(eMail); + m_pWab->FreeProperty(pProp); + // Treat this like a regular entry... + if (!eMail.IsEmpty()) { + m_pWab->ReleaseDistList(pList); + return WriteListUserEntry(pName, eMail); + } + } + + // This may very well be a list, find the entries... + m_pListTable = OpenDistList(pList); + if (m_pListTable) { + m_pList = pList; + m_listName = pName; + m_listDone = 0; + m_listHeaderDone = FALSE; + m_state = kEnumListState; + } + else { + m_pWab->ReleaseDistList(pList); + m_recordsDone++; + m_totalDone += kValuePerUser; + } + + return TRUE; +} + +BOOL CWabIterateProcess::EnumNextListUser(BOOL *pDone) +{ + HRESULT hr; + int cNumRows = 0; + LPSRowSet lpRowAB = NULL; + BOOL keepGoing = TRUE; + + if (!m_pListTable) + return FALSE; + + hr = m_pListTable->QueryRows(1, 0, &lpRowAB); + + if(HR_FAILED(hr)) { + UDialogs::ErrMessage0(IDS_ERROR_READING_WAB); + return FALSE; + } + + if(lpRowAB) { + cNumRows = lpRowAB->cRows; + + if (cNumRows) { + LPTSTR lpsz = lpRowAB->aRow[0].lpProps[ieidPR_DISPLAY_NAME].Value.lpszA; + LPENTRYID lpEID = (LPENTRYID) lpRowAB->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.lpb; + ULONG cbEID = lpRowAB->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.cb; + if(lpRowAB->aRow[0].lpProps[ieidPR_OBJECT_TYPE].Value.l == MAPI_DISTLIST) { + keepGoing = HandleListList(lpsz, lpEID, cbEID); + } + else if (lpRowAB->aRow[0].lpProps[ieidPR_OBJECT_TYPE].Value.l == MAPI_MAILUSER) { + keepGoing = HandleListUser(lpsz, lpEID, cbEID); + } + } + m_pWab->FreeProws(lpRowAB); + } + + if (!cNumRows || !lpRowAB) { + *pDone = TRUE; + m_pListTable->Release(); + m_pListTable = NULL; + if (m_pList) + m_pWab->ReleaseDistList(m_pList); + m_pList = NULL; + if (m_listDone < kValuePerUser) + m_totalDone += (kValuePerUser - m_listDone); + m_recordsDone++; + return keepGoing; + } + + if (!keepGoing) + return FALSE; + + if (m_listDone < kValuePerUser) { + m_listDone++; + m_totalDone++; + } + + return TRUE; +} + +BOOL CWabIterateProcess::HandleListList(LPCTSTR pName, LPENTRYID lpEid, ULONG cbEid) +{ + BOOL result; + LPDISTLIST pList = m_pWab->GetDistList(cbEid, lpEid); + if (!pList) { + UDialogs::ErrMessage1(IDS_ENTRY_ERROR, pName); + return FALSE; + } + + CString eMail; + LPSPropValue pProp = m_pWab->GetListProperty(pList, PR_EMAIL_ADDRESS); + if (pProp) { + m_pWab->GetValueString(pProp, eMail); + SanitizeValue(eMail); + m_pWab->FreeProperty(pProp); + // Treat this like a regular entry... + if (!eMail.IsEmpty()) { + // write out a member based on pName and eMail + result = WriteGroupMember(pName, eMail); + m_pWab->ReleaseDistList(pList); + return result; + } + } + + // iterate the list and add each member to the top level list + LPMAPITABLE pTable = OpenDistList(pList); + if (!pTable) { + TRACE0("Error opening table for list\n"); + m_pWab->ReleaseDistList(pList); + UDialogs::ErrMessage1(IDS_ENTRY_ERROR, pName); + return FALSE; + } + + int cNumRows = 0; + LPSRowSet lpRowAB = NULL; + HRESULT hr; + BOOL keepGoing = TRUE; + + do { + hr = pTable->QueryRows(1, 0, &lpRowAB); + + if(HR_FAILED(hr)) { + UDialogs::ErrMessage0(IDS_ERROR_READING_WAB); + pTable->Release(); + m_pWab->ReleaseDistList(pList); + return FALSE; + } + + if(lpRowAB) { + cNumRows = lpRowAB->cRows; + + if (cNumRows) { + LPTSTR lpsz = lpRowAB->aRow[0].lpProps[ieidPR_DISPLAY_NAME].Value.lpszA; + LPENTRYID lpEID = (LPENTRYID) lpRowAB->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.lpb; + ULONG cbEID = lpRowAB->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.cb; + if(lpRowAB->aRow[0].lpProps[ieidPR_OBJECT_TYPE].Value.l == MAPI_DISTLIST) { + keepGoing = HandleListList(lpsz, lpEID, cbEID); + } + else if (lpRowAB->aRow[0].lpProps[ieidPR_OBJECT_TYPE].Value.l == MAPI_MAILUSER) { + keepGoing = HandleListUser(lpsz, lpEID, cbEID); + } + } + m_pWab->FreeProws(lpRowAB); + } + } + while (keepGoing && cNumRows && lpRowAB); + + pTable->Release(); + m_pWab->ReleaseDistList(pList); + return keepGoing; +} + +BOOL CWabIterateProcess::HandleListUser(LPCTSTR pName, LPENTRYID lpEid, ULONG cbEid) +{ + // Get the basic properties for building the member line + LPMAILUSER pUser = m_pWab->GetUser(cbEid, lpEid); + + // Get the "required" strings first + CString lastName; + CString firstName; + CString eMail; + CString nickName; + CString middleName; + + if (!pUser) { + UDialogs::ErrMessage1(IDS_ENTRY_ERROR, pName); + return FALSE; + } + + LPSPropValue pProp = m_pWab->GetUserProperty(pUser, PR_EMAIL_ADDRESS); + if (pProp) { + m_pWab->GetValueString(pProp, eMail); + SanitizeValue(eMail); + m_pWab->FreeProperty(pProp); + } + pProp = m_pWab->GetUserProperty(pUser, PR_GIVEN_NAME); + if (pProp) { + m_pWab->GetValueString(pProp, firstName); + SanitizeValue(firstName); + m_pWab->FreeProperty(pProp); + } + pProp = m_pWab->GetUserProperty(pUser, PR_SURNAME); + if (pProp) { + m_pWab->GetValueString(pProp, lastName); + SanitizeValue(lastName); + m_pWab->FreeProperty(pProp); + } + pProp = m_pWab->GetUserProperty(pUser, PR_MIDDLE_NAME); + if (pProp) { + m_pWab->GetValueString(pProp, middleName); + SanitizeValue(middleName); + m_pWab->FreeProperty(pProp); + } + pProp = m_pWab->GetUserProperty(pUser, PR_NICKNAME); + if (pProp) { + m_pWab->GetValueString(pProp, nickName); + SanitizeValue(nickName); + m_pWab->FreeProperty(pProp); + } + if (nickName.IsEmpty()) + nickName = pName; + if (firstName.IsEmpty()) { + firstName = nickName; + middleName.Empty(); + lastName.Empty(); + } + if (lastName.IsEmpty()) + middleName.Empty(); + + if (eMail.IsEmpty()) + eMail = nickName; + + m_pWab->ReleaseUser(pUser); + + CString name = firstName; + if (!middleName.IsEmpty()) { + name += ' '; + name += middleName; + } + if (!lastName.IsEmpty()) { + name += ' '; + name += lastName; + } + return WriteGroupMember(name, eMail); +} + +BOOL CWabIterateProcess::WriteGroupMember(const char *pName, const char *pEmail) +{ + CString middle; + CString line; + BOOL result; + + // Check for the header first + if (!m_listHeaderDone) { + if (m_recordsDone) + result = m_out.WriteEol(); + else + result = TRUE; + line.LoadString(IDS_LDIF_DN_START); + line += m_listName; + line += TR_OUTPUT_EOL; + middle.LoadString(IDS_FIELD_LDIF_FULLNAME); + line += middle; + line += m_listName; + line += TR_OUTPUT_EOL; + if (!result || !m_out.WriteStr(line) || !m_out.WriteStr(kLDIFGroup)) { + UDialogs::ErrMessage0(IDS_ADDRESS_SAVE_ERROR); + return FALSE; + } + m_listHeaderDone = TRUE; + } + + + line.LoadString(IDS_FIELD_LDIF_MEMBER_START); + line += pName; + middle.LoadString(IDS_LDIF_DN_MIDDLE); + line += middle; + line += pEmail; + line += TR_OUTPUT_EOL; + if (!m_out.WriteStr(line)) { + UDialogs::ErrMessage0(IDS_ADDRESS_SAVE_ERROR); + return FALSE; + } + + if (m_listDone < kValuePerUser) { + m_listDone++; + m_totalDone++; + } + + return TRUE; +} +*/ + diff --git a/mailnews/import/oexpress/WabObject.h b/mailnews/import/oexpress/WabObject.h new file mode 100644 index 000000000..482615697 --- /dev/null +++ b/mailnews/import/oexpress/WabObject.h @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef WabObject_h___ +#define WabObject_h___ + +#include "nscore.h" +#include "nsStringGlue.h" +#include "nsIFile.h" + +#include <windows.h> +#include <wab.h> + + +class CWabIterator { +public: + virtual nsresult EnumUser(const char16_t *pName, LPENTRYID pEid, ULONG cbEid) = 0; + virtual nsresult EnumList(const char16_t *pName, LPENTRYID pEid, ULONG cbEid, LPMAPITABLE lpTable) = 0; +}; + + +class CWAB +{ +public: + CWAB(nsIFile *fileName); + ~CWAB(); + + bool Loaded(void) { return m_bInitialized;} + + HRESULT IterateWABContents(CWabIterator *pIter, int *pDone); + + // Methods for User entries + LPDISTLIST GetDistList(ULONG cbEid, LPENTRYID pEid); + void ReleaseDistList(LPDISTLIST pList) { if (pList) pList->Release();} + LPMAILUSER GetUser(ULONG cbEid, LPENTRYID pEid); + void ReleaseUser(LPMAILUSER pUser) { if (pUser) pUser->Release();} + LPSPropValue GetUserProperty(LPMAILUSER pUser, ULONG tag); + LPSPropValue GetListProperty(LPDISTLIST pList, ULONG tag); + void FreeProperty(LPSPropValue pVal) { if (pVal) m_lpWABObject->FreeBuffer(pVal);} + void GetValueString(LPSPropValue pVal, nsString& val); + void GetValueTime(LPSPropValue pVal, PRTime& val); + + void CStrToUnicode(const char *pStr, nsString& result); + + // Utility stuff used by iterate + void FreeProws(LPSRowSet prows); + + bool IsAvailable(); + +private: + char16_t * m_pUniBuff; + int m_uniBuffLen; + bool m_bInitialized; + HINSTANCE m_hinstWAB; + LPWABOPEN m_lpfnWABOpen; + LPADRBOOK m_lpAdrBook; + LPWABOBJECT m_lpWABObject; +}; + +#endif // WABOBJECT_INCLUDED + + diff --git a/mailnews/import/oexpress/moz.build b/mailnews/import/oexpress/moz.build new file mode 100644 index 000000000..5a34ce8e6 --- /dev/null +++ b/mailnews/import/oexpress/moz.build @@ -0,0 +1,19 @@ +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +SOURCES += [ + 'nsOE5File.cpp', + 'nsOEAddressIterator.cpp', + 'nsOEImport.cpp', + 'nsOEMailbox.cpp', + 'nsOERegUtil.cpp', + 'nsOEScanBoxes.cpp', + 'nsOESettings.cpp', + 'nsOEStringBundle.cpp', + 'WabObject.cpp', +] + +FINAL_LIBRARY = 'import' + diff --git a/mailnews/import/oexpress/nsOE5File.cpp b/mailnews/import/oexpress/nsOE5File.cpp new file mode 100644 index 000000000..fd1fd0e15 --- /dev/null +++ b/mailnews/import/oexpress/nsOE5File.cpp @@ -0,0 +1,631 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsOE5File.h" +#include "OEDebugLog.h" +#include "nsMsgUtils.h" +#include "msgCore.h" +#include "prprf.h" +#include "nsMsgLocalFolderHdrs.h" +#include "nsIOutputStream.h" +#include "nsIInputStream.h" +#include "nsIMsgPluggableStore.h" +#include "nsIMsgHdr.h" +#include "nsNetUtil.h" +#include "nsISeekableStream.h" +#include "nsMsgMessageFlags.h" +#include <windows.h> + +#define kIndexGrowBy 100 +#define kSignatureSize 12 +#define kDontSeek 0xFFFFFFFF +#define MARKED 0x20 // 4 +#define READ 0x80 // 1 +#define HASATTACHMENT 0x4000 // 268435456 10000000h +#define ISANSWERED 0x80000 // 2 +#define ISFORWARDED 0x100000 // 4096 +#define ISWATCHED 0x400000 // 256 +#define ISIGNORED 0x800000 // 262144 +#define XLATFLAGS(s) (((MARKED & s) ? nsMsgMessageFlags::Marked : 0) | \ + ((READ & s) ? nsMsgMessageFlags::Read : 0) | \ + ((HASATTACHMENT & s) ? nsMsgMessageFlags::Attachment : 0) | \ + ((ISANSWERED & s) ? nsMsgMessageFlags::Replied : 0) | \ + ((ISFORWARDED & s) ? nsMsgMessageFlags::Forwarded : 0) | \ + ((ISWATCHED & s) ? nsMsgMessageFlags::Watched : 0) | \ + ((ISIGNORED & s) ? nsMsgMessageFlags::Ignored : 0)) + +static char *gSig = + "\xCF\xAD\x12\xFE\xC5\xFD\x74\x6F\x66\xE3\xD1\x11"; + +// copied from nsprpub/pr/src/{io/prfile.c | md/windows/w95io.c} : +// PR_FileTimeToPRTime and _PR_FileTimeToPRTime +void nsOE5File::FileTimeToPRTime(const FILETIME *filetime, PRTime *prtm) +{ +#ifdef __GNUC__ + const PRTime _pr_filetime_offset = 116444736000000000LL; +#else + const PRTime _pr_filetime_offset = 116444736000000000i64; +#endif + + PR_ASSERT(sizeof(FILETIME) == sizeof(PRTime)); + ::CopyMemory(prtm, filetime, sizeof(PRTime)); +#ifdef __GNUC__ + *prtm = (*prtm - _pr_filetime_offset) / 10LL; +#else + *prtm = (*prtm - _pr_filetime_offset) / 10i64; +#endif +} + +bool nsOE5File::VerifyLocalMailFile(nsIFile *pFile) +{ + char sig[kSignatureSize]; + + nsCOMPtr <nsIInputStream> inputStream; + + if (NS_FAILED(NS_NewLocalFileInputStream(getter_AddRefs(inputStream), pFile))) + return false; + + if (!ReadBytes(inputStream, sig, 0, kSignatureSize)) + return false; + + bool result = true; + + for (int i = 0; (i < kSignatureSize) && result; i++) { + if (sig[i] != gSig[i]) + result = false; + } + + char storeName[14]; + if (!ReadBytes(inputStream, storeName, 0x24C1, 12)) + result = false; + + storeName[12] = 0; + + if (PL_strcasecmp("LocalStore", storeName)) + result = false; + + return result; +} + +bool nsOE5File::IsLocalMailFile(nsIFile *pFile) +{ + nsresult rv; + bool isFile = false; + + rv = pFile->IsFile(&isFile); + if (NS_FAILED(rv) || !isFile) + return false; + + bool result = VerifyLocalMailFile(pFile); + + return result; +} + +bool nsOE5File::ReadIndex(nsIInputStream *pInputStream, uint32_t **ppIndex, uint32_t *pSize) +{ + *ppIndex = nullptr; + *pSize = 0; + + char signature[4]; + if (!ReadBytes(pInputStream, signature, 0, 4)) + return false; + + for (int i = 0; i < 4; i++) { + if (signature[i] != gSig[i]) { + IMPORT_LOG0("*** Outlook 5.0 dbx file signature doesn't match\n"); + return false; + } + } + + uint32_t offset = 0x00e4; + uint32_t indexStart = 0; + if (!ReadBytes(pInputStream, &indexStart, offset, 4)) { + IMPORT_LOG0("*** Unable to read offset to index start\n"); + return false; + } + + PRUint32Array array; + array.count = 0; + array.alloc = kIndexGrowBy; + array.pIndex = new uint32_t[kIndexGrowBy]; + + uint32_t next = ReadMsgIndex(pInputStream, indexStart, &array); + while (next) { + next = ReadMsgIndex(pInputStream, next, &array); + } + + if (array.count) { + *pSize = array.count; + *ppIndex = array.pIndex; + return true; + } + + delete [] array.pIndex; + return false; +} + + +uint32_t nsOE5File::ReadMsgIndex(nsIInputStream *pInputStream, uint32_t offset, PRUint32Array *pArray) +{ + // Record is: + // 4 byte marker + // 4 byte unknown + // 4 byte nextSubIndex + // 4 byte (parentIndex?) + // 2 bytes unknown + // 1 byte length - # of entries in this record + // 1 byte unknown + // 4 byte unknown + // length records consisting of 3 longs + // 1 - pointer to record + // 2 - child index pointer + // 3 - number of records in child + + uint32_t marker; + + if (!ReadBytes(pInputStream, &marker, offset, 4)) + return 0; + + if (marker != offset) + return 0; + + + uint32_t vals[3]; + + if (!ReadBytes(pInputStream, vals, offset + 4, 12)) + return 0; + + + uint8_t len[4]; + if (!ReadBytes(pInputStream, len, offset + 16, 4)) + return 0; + + + + uint32_t cnt = (uint32_t) len[1]; + cnt *= 3; + uint32_t *pData = new uint32_t[cnt]; + + if (!ReadBytes(pInputStream, pData, offset + 24, cnt * 4)) { + delete [] pData; + return 0; + } + + uint32_t next; + uint32_t indexOffset; + uint32_t * pRecord = pData; + uint32_t * pNewIndex; + + for (uint8_t i = 0; i < (uint8_t)len[1]; i++, pRecord += 3) { + indexOffset = pRecord[0]; + + if (pArray->count >= pArray->alloc) { + pNewIndex = new uint32_t[ pArray->alloc + kIndexGrowBy]; + memcpy(pNewIndex, pArray->pIndex, (pArray->alloc * 4)); + (pArray->alloc) += kIndexGrowBy; + delete [] pArray->pIndex; + pArray->pIndex = pNewIndex; + } + + /* + We could do some checking here if we wanted - + make sure the index is within the file, + make sure there isn't a duplicate index, etc. + */ + + pArray->pIndex[pArray->count] = indexOffset; + (pArray->count)++; + + + + next = pRecord[1]; + if (next) + while ((next = ReadMsgIndex(pInputStream, next, pArray)) != 0); + } + delete [] pData; + + // return the pointer to the next subIndex + return vals[1]; +} + +bool nsOE5File::IsFromLine(char *pLine, uint32_t len) +{ + return (len > 5 && (pLine[0] == 'F') && (pLine[1] == 'r') && (pLine[2] == 'o') && (pLine[3] == 'm') && (pLine[4] == ' ')); +} + +// Anything over 16K will be assumed BAD, BAD, BAD! +#define kMailboxBufferSize 0x4000 +#define kMaxAttrCount 0x0030 +const char *nsOE5File::m_pFromLineSep = "From - Mon Jan 1 00:00:00 1965\x0D\x0A"; + +nsresult nsOE5File::ImportMailbox(uint32_t *pBytesDone, bool *pAbort, + nsString& name, nsIFile *inFile, + nsIMsgFolder *dstFolder, uint32_t *pCount) +{ + int32_t msgCount = 0; + if (pCount) + *pCount = 0; + + nsCOMPtr<nsIInputStream> inputStream; + nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), inFile); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr<nsIMsgPluggableStore> msgStore; + rv = dstFolder->GetMsgStore(getter_AddRefs(msgStore)); + NS_ENSURE_SUCCESS(rv, rv); + + uint32_t * pIndex; + uint32_t indexSize; + uint32_t * pFlags; + uint64_t * pTime; + + if (!ReadIndex(inputStream, &pIndex, &indexSize)) { + IMPORT_LOG1("No messages found in mailbox: %s\n", NS_LossyConvertUTF16toASCII(name.get())); + return NS_OK; + } + + pTime = new uint64_t[ indexSize]; + pFlags = new uint32_t[ indexSize]; + char * pBuffer = new char[kMailboxBufferSize]; + if (!(*pAbort)) + ConvertIndex(inputStream, pBuffer, pIndex, indexSize, pFlags, pTime); + + uint32_t block[4]; + int32_t sepLen = (int32_t) strlen(m_pFromLineSep); + uint32_t written; + + /* + Each block is: + marker - matches file offset + block length + text length in block + pointer to next block. (0 if end) + + Each message is made up of a linked list of block data. + So what we do for each message is: + 1. Read the first block data. + 2. Write out the From message separator if the message doesn't already + start with one. + 3. If the block of data doesn't end with CRLF then a line is broken into two blocks, + so save the incomplete line for later process when we read the next block. Then + write out the block excluding the partial line at the end of the block (if exists). + 4. If there's next block of data then read next data block. Otherwise we're done. + If we found a partial line in step #3 then find the rest of the line from the + current block and write out this line separately. + 5. Reset some of the control variables and repeat step #3. + */ + + uint32_t didBytes = 0; + uint32_t next, size; + char *pStart, *pEnd, *partialLineStart; + nsAutoCString partialLine, tempLine; + nsCOMPtr<nsIOutputStream> outputStream; + rv = NS_OK; + + for (uint32_t i = 0; (i < indexSize) && !(*pAbort); i++) + { + if (! pIndex[i]) + continue; + + nsCOMPtr<nsIMsgDBHdr> msgHdr; + bool reusable; + + rv = msgStore->GetNewMsgOutputStream(dstFolder, getter_AddRefs(msgHdr), &reusable, + getter_AddRefs(outputStream)); + if (NS_FAILED(rv)) + { + IMPORT_LOG1( "Mbx getting outputstream error: 0x%lx\n", rv); + break; + } + + if (ReadBytes(inputStream, block, pIndex[i], 16) && (block[0] == pIndex[i]) && + (block[2] < kMailboxBufferSize) && (ReadBytes(inputStream, pBuffer, kDontSeek, block[2]))) + { + // block[2] contains the chars in the buffer (ie, buf content size). + // block[3] contains offset to the next block of data (0 means no more data). + size = block[2]; + pStart = pBuffer; + pEnd = pStart + size; + + // write out the from separator. + rv = NS_ERROR_FAILURE; + if (IsFromLine(pBuffer, size)) + { + char *pChar = pStart; + while ((pChar < pEnd) && (*pChar != '\r') && (*(pChar+1) != '\n')) + pChar++; + + if (pChar < pEnd) + { + // Get the "From " line so write it out. + rv = outputStream->Write(pStart, pChar-pStart+2, &written); + if (NS_SUCCEEDED(rv)) + // Now buffer starts from the 2nd line. + pStart = pChar + 2; + } + } + else if (pTime[i]) + { + char result[156] = ""; + PRExplodedTime xpldTime; + char buffer[128] = ""; + PRTime prt; + + nsOE5File::FileTimeToPRTime((FILETIME *)&pTime[i], &prt); + // modeled after nsMsgSend.cpp + PR_ExplodeTime(prt, PR_LocalTimeParameters, &xpldTime); + PR_FormatTimeUSEnglish(buffer, sizeof(buffer), + "%a %b %d %H:%M:%S %Y", + &xpldTime); + PL_strcpy(result, "From - "); + PL_strcpy(result + 7, buffer); + PL_strcpy(result + 7 + 24, CRLF); + + rv = outputStream->Write(result, (int32_t) strlen(result), &written); + } + if (NS_FAILED(rv)) + { + // Write out the default from line since there is none in the msg. + rv = outputStream->Write(m_pFromLineSep, sepLen, &written); + // FIXME: Do I need to check the return value of written??? + if (NS_FAILED(rv)) + break; + } + + char statusLine[50]; + uint32_t msgFlags = XLATFLAGS(pFlags[i]); + PR_snprintf(statusLine, sizeof(statusLine), X_MOZILLA_STATUS_FORMAT MSG_LINEBREAK, msgFlags & 0xFFFF); + rv = outputStream->Write(statusLine, strlen(statusLine), &written); + NS_ENSURE_SUCCESS(rv,rv); + PR_snprintf(statusLine, sizeof(statusLine), X_MOZILLA_STATUS2_FORMAT MSG_LINEBREAK, msgFlags & 0xFFFF0000); + rv = outputStream->Write(statusLine, strlen(statusLine), &written); + NS_ENSURE_SUCCESS(rv,rv); + + do + { + partialLine.Truncate(); + partialLineStart = pEnd; + + // If the buffer doesn't end with CRLF then a line is broken into two blocks, + // so save the incomplete line for later process when we read the next block. + if ((size > 1) && !(*(pEnd - 2) == '\r' && *(pEnd - 1) == '\n')) + { + partialLineStart -= 2; + while ((partialLineStart >= pStart) && (*partialLineStart != '\r') && (*(partialLineStart+1) != '\n')) + partialLineStart--; + if (partialLineStart != (pEnd - 2)) + partialLineStart += 2; // skip over CRLF if we find them. + partialLine.Assign(partialLineStart, pEnd - partialLineStart); + } + + // Now process the block of data which ends with CRLF. + rv = EscapeFromSpaceLine(outputStream, pStart, partialLineStart); + if (NS_FAILED(rv)) + break; + + didBytes += block[2]; + + next = block[3]; + if (! next) + { + // OK, we're done so flush out the partial line if it's not empty. + if (partialLine.Length()) + rv = EscapeFromSpaceLine(outputStream, (char *)partialLine.get(), (partialLine.get()+partialLine.Length())); + } + else + if (ReadBytes(inputStream, block, next, 16) && (block[0] == next) && + (block[2] < kMailboxBufferSize) && (ReadBytes(inputStream, pBuffer, kDontSeek, block[2]))) + { + // See if we have a partial line from previous block. If so then build a complete + // line (ie, take the remaining chars from this block) and process this line. Need + // to adjust where data start and size in this case. + size = block[2]; + pStart = pBuffer; + pEnd = pStart + size; + if (partialLine.Length()) + { + while ((pStart < pEnd) && (*pStart != '\r') && (*(pStart+1) != '\n')) + pStart++; + if (pStart < pEnd) // if we found a CRLF .. + pStart += 2; // .. then copy that too. + tempLine.Assign(pBuffer, pStart - pBuffer); + partialLine.Append(tempLine); + rv = EscapeFromSpaceLine(outputStream, (char *)partialLine.get(), (partialLine.get()+partialLine.Length())); + if (NS_FAILED(rv)) + break; + + // Adjust where data start and size (since some of the data has been processed). + size -= (pStart - pBuffer); + } + } + else + { + IMPORT_LOG2("Error reading message from %s at 0x%lx\n", NS_LossyConvertUTF16toASCII(name.get()), pIndex[i]); + rv = outputStream->Write("\x0D\x0A", 2, &written); + next = 0; + } + } while (next); + + // Always end a msg with CRLF. This will make sure that OE msgs without body is + // correctly recognized as msgs. Otherwise, we'll end up with the following in + // the msg folder where the 2nd msg starts right after the headers of the 1st msg: + // + // From - Jan 1965 00:00:00 <<<--- 1st msg starts here + // Subject: Test msg + // . . . (more headers) + // To: <someone@example.com> + // From - Jan 1965 00:00:00 <<<--- 2nd msg starts here + // Subject: How are you + // . . .(more headers) + // + // In this case, the 1st msg is not recognized as a msg (it's skipped) + // when you open the folder. + rv = outputStream->Write("\x0D\x0A", 2, &written); + + if (NS_FAILED(rv)) { + IMPORT_LOG0( "Error writing message during OE import\n"); + msgStore->DiscardNewMessage(outputStream, msgHdr); + break; + } + + msgStore->FinishNewMessage(outputStream, msgHdr); + + if (!reusable) + outputStream->Close(); + + msgCount++; + if (pCount) + *pCount = msgCount; + if (pBytesDone) + *pBytesDone = didBytes; + } + else { + // Error reading message, should this be logged??? + IMPORT_LOG2("Error reading message from %s at 0x%lx\n", NS_LossyConvertUTF16toASCII(name.get()), pIndex[i]); + *pAbort = true; + } + } + if (outputStream) + outputStream->Close(); + delete [] pBuffer; + delete [] pFlags; + delete [] pTime; + + if (NS_FAILED(rv)) + *pAbort = true; + + return rv; +} + + +/* + A message index record consists of: + 4 byte marker - matches record offset + 4 bytes size - size of data after this header + 2 bytes header length - not dependable + 1 bytes - number of attributes + 1 byte changes on this object + Each attribute is a 4 byte value with the 1st byte being the tag + and the remaing 3 bytes being data. The data is either a direct + offset of an offset within the message index that points to the + data for the tag. + attr[0]: + -hi bit== 1 means PRUint24 data = attr[1] + -hi bit== 0 means (PRUint24) attr[1] = offset into data segment for data + -attr[0] & 7f == tag index + Header above is 0xC bytes, attr's are number * 4 bytes then follows data segment + The data segment length is undefined. The index data is either part of the + index structure or the index structure points to address in data that follows + index structure table. Use that location to calculate file position of data. + MSDN indicates 0x28 attributes possible. + + Current known tags are: + 0x01 - flags addressed + 0x81 - flags in attr's next 3 bytes + 0x02 - a time value - addressed- 8 bytes + 0x04 - text offset pointer, the data is the offset after the attribute + of a 4 byte pointer to the message text <-- addr into data + 0x05 - offset to truncated subject + 0x08 - offste to subject + 0x0D - offset to from + 0x0E - offset to from addresses + 0x13 - offset to to name + 0x45 - offset to to address <----correct --> 0x14 + 0x80 - msgId <-correction-> 0x07 addr to msg id + 0x84 - direct text offset, direct pointer to message text +*/ + +void nsOE5File::ConvertIndex(nsIInputStream *pFile, char *pBuffer, + uint32_t *pIndex, uint32_t size, + uint32_t *pFlags, uint64_t *pTime) +{ + // for each index record, get the actual message offset! If there is a + // problem just record the message offset as 0 and the message reading code + // can log that error information. + // XXXTODO- above error reporting is not done + + uint8_t recordHead[12]; + uint32_t marker; + uint32_t recordSize; + uint32_t numAttrs; + uint32_t offset; + uint32_t attrIndex; + uint32_t attrOffset; + uint8_t tag; + uint32_t tagData; + uint32_t flags; + uint64_t time; + uint32_t dataStart; + + for (uint32_t i = 0; i < size; i++) { + offset = 0; + flags = 0; + time = 0; + if (ReadBytes(pFile, recordHead, pIndex[i], 12)) { + memcpy(&marker, recordHead, 4); + memcpy(&recordSize, recordHead + 4, 4); + numAttrs = (uint32_t) recordHead[10]; + if (marker == pIndex[i] && numAttrs <= kMaxAttrCount) { + dataStart = pIndex[i] + 12 + (numAttrs * 4); + if (ReadBytes(pFile, pBuffer, kDontSeek, numAttrs * 4)) { + attrOffset = 0; + for (attrIndex = 0; attrIndex < numAttrs; attrIndex++, attrOffset += 4) { + tag = (uint8_t) pBuffer[attrOffset]; + if (tag == (uint8_t) 0x84) { + tagData = 0; + memcpy(&tagData, pBuffer + attrOffset + 1, 3); + offset = tagData; + } + else if (tag == (uint8_t) 0x04) { + tagData = 0; + memcpy(&tagData, pBuffer + attrOffset + 1, 3); + ReadBytes(pFile, &offset, dataStart + tagData, 4); + } + else if (tag == (uint8_t) 0x81) { + tagData = 0; + memcpy(&tagData, pBuffer + attrOffset +1, 3); + flags = tagData; + } + else if (tag == (uint8_t) 0x01) { + tagData = 0; + memcpy(&tagData, pBuffer + attrOffset +1, 3); + ReadBytes(pFile, &flags, dataStart + tagData, 4); + } + else if (tag == (uint8_t) 0x02) { + tagData = 0; + memcpy(&tagData, pBuffer + attrOffset +1, 3); + ReadBytes(pFile, &time, dataStart + tagData, 4); + } + } + } + } + } + pIndex[i] = offset; + pFlags[i] = flags; + pTime[i] = time; + } +} + + +bool nsOE5File::ReadBytes(nsIInputStream *stream, void *pBuffer, uint32_t offset, uint32_t bytes) +{ + nsresult rv; + + nsCOMPtr <nsISeekableStream> seekableStream = do_QueryInterface(stream); + if (offset != kDontSeek) { + rv = seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, offset); + if (NS_FAILED(rv)) + return false; + } + + if (!bytes) + return true; + + uint32_t cntRead; + char * pReadTo = (char *)pBuffer; + rv = stream->Read(pReadTo, bytes, &cntRead); + return NS_SUCCEEDED(rv) && cntRead == bytes; + +} + diff --git a/mailnews/import/oexpress/nsOE5File.h b/mailnews/import/oexpress/nsOE5File.h new file mode 100644 index 000000000..07498acfd --- /dev/null +++ b/mailnews/import/oexpress/nsOE5File.h @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsOE5File_h___ +#define nsOE5File_h___ + +#include "nsStringGlue.h" +#include "nsIFile.h" +#include "nsIMsgFolder.h" +#include <windows.h> + +class nsIInputStream; + +class nsOE5File +{ +public: + /* pFile must already be open for reading. */ + static bool VerifyLocalMailFile(nsIFile *pFile); + /* pFile must NOT be open for reading */ + static bool IsLocalMailFile(nsIFile *pFile); + + static bool ReadIndex(nsIInputStream *pFile, uint32_t **ppIndex, uint32_t *pSize); + + static nsresult ImportMailbox(uint32_t *pBytesDone, bool *pAbort, + nsString& name, nsIFile *inFile, + nsIMsgFolder *pDstFolder, uint32_t *pCount); + + static void FileTimeToPRTime(const FILETIME *filetime, PRTime *prtm); + +private: + typedef struct { + uint32_t * pIndex; + uint32_t count; + uint32_t alloc; + } PRUint32Array; + + static const char *m_pFromLineSep; + + static bool ReadBytes(nsIInputStream *stream, void *pBuffer, uint32_t offset, uint32_t bytes); + static uint32_t ReadMsgIndex(nsIInputStream *file, uint32_t offset, PRUint32Array *pArray); + static void ConvertIndex(nsIInputStream *pFile, char *pBuffer, uint32_t *pIndex, + uint32_t size, uint32_t *pFlags, uint64_t *pTime); + static bool IsFromLine(char *pLine, uint32_t len); + + +}; + + + +#endif /* nsOE5File_h___ */ diff --git a/mailnews/import/oexpress/nsOEAddressIterator.cpp b/mailnews/import/oexpress/nsOEAddressIterator.cpp new file mode 100644 index 000000000..bc33b45fb --- /dev/null +++ b/mailnews/import/oexpress/nsOEAddressIterator.cpp @@ -0,0 +1,396 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +/* + + A sample of XPConnect. This file contains an implementation of + nsISample. + +*/ +#include "nscore.h" +#include "nsCOMPtr.h" +#include "nsStringGlue.h" +#include "nsMsgUtils.h" +#include "nsComponentManagerUtils.h" +#include "nsIServiceManager.h" +#include "nsIImportService.h" +#include "nsIImportFieldMap.h" +#include "nsABBaseCID.h" +#include "nsIAbCard.h" +#include "prprf.h" + +#include "nsOEAddressIterator.h" + +#include "OEDebugLog.h" + +typedef struct { + int32_t mozField; + int32_t multiLine; + ULONG mapiTag; +} MAPIFields; + +enum { + ieidPR_DISPLAY_NAME = 0, + ieidPR_ENTRYID, + ieidPR_OBJECT_TYPE, + ieidMax +}; + +/* + Fields in MAPI, not in Mozilla + PR_OFFICE_LOCATION + FIX - PR_BIRTHDAY - stored as PT_SYSTIME - FIX to extract for moz address book birthday + PR_DISPLAY_NAME_PREFIX - Mr., Mrs. Dr., etc. + PR_SPOUSE_NAME + PR_GENDER - integer, not text + FIX - PR_CONTACT_EMAIL_ADDRESSES - multiuline strings for email addresses, needs + parsing to get secondary email address for mozilla +*/ + +#define kIsMultiLine -2 +#define kNoMultiLine -1 + +static MAPIFields gMapiFields[] = { + { 35, kIsMultiLine, PR_COMMENT}, + { 6, kNoMultiLine, PR_BUSINESS_TELEPHONE_NUMBER}, + { 7, kNoMultiLine, PR_HOME_TELEPHONE_NUMBER}, + { 25, kNoMultiLine, PR_COMPANY_NAME}, + { 23, kNoMultiLine, PR_TITLE}, + { 10, kNoMultiLine, PR_CELLULAR_TELEPHONE_NUMBER}, + { 9, kNoMultiLine, PR_PAGER_TELEPHONE_NUMBER}, + { 8, kNoMultiLine, PR_BUSINESS_FAX_NUMBER}, + { 8, kNoMultiLine, PR_HOME_FAX_NUMBER}, + { 22, kNoMultiLine, PR_COUNTRY}, + { 19, kNoMultiLine, PR_LOCALITY}, + { 20, kNoMultiLine, PR_STATE_OR_PROVINCE}, + { 17, 18, PR_STREET_ADDRESS}, + { 21, kNoMultiLine, PR_POSTAL_CODE}, + { 27, kNoMultiLine, PR_PERSONAL_HOME_PAGE}, + { 26, kNoMultiLine, PR_BUSINESS_HOME_PAGE}, + { 13, kNoMultiLine, PR_HOME_ADDRESS_CITY}, + { 16, kNoMultiLine, PR_HOME_ADDRESS_COUNTRY}, + { 15, kNoMultiLine, PR_HOME_ADDRESS_POSTAL_CODE}, + { 14, kNoMultiLine, PR_HOME_ADDRESS_STATE_OR_PROVINCE}, + { 11, 12, PR_HOME_ADDRESS_STREET}, + { 24, kNoMultiLine, PR_DEPARTMENT_NAME} +}; + +nsOEAddressIterator::nsOEAddressIterator(CWAB *pWab, nsIAddrDatabase *database) +{ + m_pWab = pWab; + m_database = database; +} + +nsOEAddressIterator::~nsOEAddressIterator() +{ +} + +nsresult nsOEAddressIterator::EnumUser(const char16_t * pName, LPENTRYID pEid, ULONG cbEid) +{ + IMPORT_LOG1("User: %S\n", pName); + nsresult rv = NS_OK; + + if (m_database) { + LPMAILUSER pUser = m_pWab->GetUser(cbEid, pEid); + if (pUser) { + // Get a new row from the database! + nsCOMPtr <nsIMdbRow> newRow; + rv = m_database->GetNewRow(getter_AddRefs(newRow)); + NS_ENSURE_SUCCESS(rv, rv); + if (newRow && BuildCard(pName, newRow, pUser)) + { + rv = m_database->AddCardRowToDB(newRow); + NS_ENSURE_SUCCESS(rv, rv); + IMPORT_LOG0("* Added entry to address book database\n"); + nsString eMail; + + LPSPropValue pProp = m_pWab->GetUserProperty(pUser, PR_EMAIL_ADDRESS); + if (pProp) + { + m_pWab->GetValueString(pProp, eMail); + SanitizeValue(eMail); + m_pWab->FreeProperty(pProp); + m_listRows.Put(eMail, newRow); + } + } + m_pWab->ReleaseUser(pUser); + } + } + + return rv; +} + +void nsOEAddressIterator::FindListRow(nsString &eMail, nsIMdbRow **cardRow) +{ + m_listRows.Get(eMail,cardRow); +} + +nsresult nsOEAddressIterator::EnumList(const char16_t * pName, LPENTRYID pEid, ULONG cbEid, LPMAPITABLE lpTable) +{ + // If no name provided then we're done. + if (!pName || !(*pName)) + return NS_OK; + + nsresult rv = NS_ERROR_FAILURE; + HRESULT hr = E_FAIL; + // Make sure we have db to work with. + if (!m_database) + return rv; + + nsCOMPtr <nsIMdbRow> listRow; + rv = m_database->GetNewListRow(getter_AddRefs(listRow)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = m_database->AddListName(listRow, NS_ConvertUTF16toUTF8(pName).get()); + NS_ENSURE_SUCCESS(rv, rv); + rv = m_database->AddCardRowToDB(listRow); + NS_ENSURE_SUCCESS(rv, rv); + rv = m_database->AddListDirNode(listRow); + NS_ENSURE_SUCCESS(rv, rv); + + LPSRowSet lpRowAB = NULL; + ULONG lpcbEID = 0; + LPENTRYID lpEID = NULL; + ULONG rowCount = 0; + int cNumRows = 0; + int numListElems = 0; + nsAutoString uniStr; + + hr = lpTable->GetRowCount(0, &rowCount); + // + hr = lpTable->SeekRow(BOOKMARK_BEGINNING, 0, NULL); + + if(HR_FAILED(hr)) + return NS_ERROR_FAILURE; + + // Read all the rows of the table one by one + do + { + hr = lpTable->QueryRows(1, 0, &lpRowAB); + + if(HR_FAILED(hr)) + break; + + if(lpRowAB) + { + cNumRows = lpRowAB->cRows; + if (cNumRows) + { + LPTSTR lpsz = lpRowAB->aRow[0].lpProps[ieidPR_DISPLAY_NAME].Value.lpszA; + LPENTRYID lpEID = (LPENTRYID) lpRowAB->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.lpb; + ULONG cbEID = lpRowAB->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.cb; + + // There are 2 kinds of objects - the MAPI_MAILUSER contact object + // and the MAPI_DISTLIST contact object + // For distribution lists, we will only consider MAILUSER + // objects since we can't nest lists yet. + if(lpRowAB->aRow[0].lpProps[ieidPR_OBJECT_TYPE].Value.l == MAPI_MAILUSER) + { + LPMAILUSER pUser = m_pWab->GetUser(cbEID, lpEID); + LPSPropValue pProp = m_pWab->GetUserProperty(pUser, PR_EMAIL_ADDRESS); + nsString eMail; + + nsCOMPtr <nsIMdbRow> cardRow; + + m_pWab->GetValueString(pProp, eMail); + SanitizeValue(eMail); + FindListRow(eMail, getter_AddRefs(cardRow)); + if (cardRow) + { + nsCOMPtr <nsIAbCard> userCard; + nsCOMPtr <nsIAbCard> newCard; + userCard = do_CreateInstance(NS_ABMDBCARD_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + m_database->InitCardFromRow(userCard,cardRow); + + m_database->AddListCardColumnsToRow(userCard, listRow, ++numListElems, + getter_AddRefs(newCard), + true, nullptr, nullptr); + } + m_pWab->FreeProperty(pProp); + m_pWab->ReleaseUser(pUser); + } + } + m_pWab->FreeProws(lpRowAB); + } + } while (SUCCEEDED(hr) && cNumRows && lpRowAB); + + m_database->SetListAddressTotal(listRow, numListElems); + return rv; +} + +void nsOEAddressIterator::SanitizeValue(nsString& val) +{ + MsgReplaceSubstring(val, NS_LITERAL_STRING("\r\n"), NS_LITERAL_STRING(", ")); + MsgReplaceChar(val, "\r\n", ','); +} + +void nsOEAddressIterator::SplitString(nsString& val1, nsString& val2) +{ + // Find the last line if there is more than one! + int32_t idx = val1.RFind("\x0D\x0A"); + int32_t cnt = 2; + if (idx == -1) { + cnt = 1; + idx = val1.RFindChar(13); + } + if (idx == -1) + idx= val1.RFindChar(10); + if (idx != -1) { + val2 = Substring(val1, idx + cnt); + val1.SetLength(idx); + SanitizeValue(val1); + } +} + +void nsOEAddressIterator::SetBirthDay(nsIMdbRow *newRow, PRTime& birthDay) +{ + PRExplodedTime exploded; + PR_ExplodeTime(birthDay, PR_LocalTimeParameters, &exploded); + + char stringValue[5]; + PR_snprintf(stringValue, sizeof(stringValue), "%04d", exploded.tm_year); + m_database->AddBirthYear(newRow, stringValue); + PR_snprintf(stringValue, sizeof(stringValue), "%02d", exploded.tm_month + 1); + m_database->AddBirthMonth(newRow, stringValue); + PR_snprintf(stringValue, sizeof(stringValue), "%02d", exploded.tm_mday); + m_database->AddBirthDay(newRow, stringValue); +} + +bool nsOEAddressIterator::BuildCard(const char16_t * pName, nsIMdbRow *newRow, LPMAILUSER pUser) +{ + + nsString lastName; + nsString firstName; + nsString eMail; + nsString nickName; + nsString middleName; + PRTime birthDay = 0; + + LPSPropValue pProp = m_pWab->GetUserProperty(pUser, PR_EMAIL_ADDRESS); + if (pProp) { + m_pWab->GetValueString(pProp, eMail); + SanitizeValue(eMail); + m_pWab->FreeProperty(pProp); + } + pProp = m_pWab->GetUserProperty(pUser, PR_GIVEN_NAME); + if (pProp) { + m_pWab->GetValueString(pProp, firstName); + SanitizeValue(firstName); + m_pWab->FreeProperty(pProp); + } + pProp = m_pWab->GetUserProperty(pUser, PR_SURNAME); + if (pProp) { + m_pWab->GetValueString(pProp, lastName); + SanitizeValue(lastName); + m_pWab->FreeProperty(pProp); + } + pProp = m_pWab->GetUserProperty(pUser, PR_MIDDLE_NAME); + if (pProp) { + m_pWab->GetValueString(pProp, middleName); + SanitizeValue(middleName); + m_pWab->FreeProperty(pProp); + } + pProp = m_pWab->GetUserProperty(pUser, PR_NICKNAME); + if (pProp) { + m_pWab->GetValueString(pProp, nickName); + SanitizeValue(nickName); + m_pWab->FreeProperty(pProp); + } + + // The idea here is that firstName and lastName cannot both be empty! + if (firstName.IsEmpty() && lastName.IsEmpty()) + firstName = pName; + + nsString displayName; + pProp = m_pWab->GetUserProperty(pUser, PR_DISPLAY_NAME); + if (pProp) { + m_pWab->GetValueString(pProp, displayName); + SanitizeValue(displayName); + m_pWab->FreeProperty(pProp); + } + if (displayName.IsEmpty()) { + if (firstName.IsEmpty()) + displayName = pName; + else { + displayName = firstName; + if (!middleName.IsEmpty()) { + displayName.Append(char16_t(' ')); + displayName.Append(middleName); + } + if (!lastName.IsEmpty()) { + displayName.Append(char16_t(' ')); + displayName.Append(lastName); + } + } + } + + pProp = m_pWab->GetUserProperty(pUser, PR_BIRTHDAY); + if (pProp) { + m_pWab->GetValueTime(pProp, birthDay); + m_pWab->FreeProperty(pProp); + } + + // We now have the required fields + // write them out followed by any optional fields! + if (!displayName.IsEmpty()) + m_database->AddDisplayName(newRow, NS_ConvertUTF16toUTF8(displayName).get()); + if (!firstName.IsEmpty()) + m_database->AddFirstName(newRow, NS_ConvertUTF16toUTF8(firstName).get()); + if (!lastName.IsEmpty()) + m_database->AddLastName(newRow, NS_ConvertUTF16toUTF8(lastName).get()); + if (!nickName.IsEmpty()) + m_database->AddNickName(newRow, NS_ConvertUTF16toUTF8(nickName).get()); + if (!eMail.IsEmpty()) + m_database->AddPrimaryEmail(newRow, NS_ConvertUTF16toUTF8(eMail).get()); + + if (birthDay) + SetBirthDay(newRow, birthDay); + + // Do all of the extra fields! + + nsString value; + nsString line2; + nsresult rv; + // Create a field map + + nsCOMPtr<nsIImportService> impSvc(do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv)); + if (NS_SUCCEEDED(rv)) { + nsIImportFieldMap * pFieldMap = nullptr; + rv = impSvc->CreateNewFieldMap(&pFieldMap); + if (NS_SUCCEEDED(rv) && pFieldMap) { + int max = sizeof(gMapiFields) / sizeof(MAPIFields); + for (int i = 0; i < max; i++) { + pProp = m_pWab->GetUserProperty(pUser, gMapiFields[i].mapiTag); + if (pProp) { + m_pWab->GetValueString(pProp, value); + m_pWab->FreeProperty(pProp); + if (!value.IsEmpty()) { + if (gMapiFields[i].multiLine == kNoMultiLine) { + SanitizeValue(value); + pFieldMap->SetFieldValue(m_database, newRow, gMapiFields[i].mozField, value.get()); + } + else if (gMapiFields[i].multiLine == kIsMultiLine) { + pFieldMap->SetFieldValue(m_database, newRow, gMapiFields[i].mozField, value.get()); + } + else { + line2.Truncate(); + SplitString(value, line2); + if (!value.IsEmpty()) + pFieldMap->SetFieldValue(m_database, newRow, gMapiFields[i].mozField, value.get()); + if (!line2.IsEmpty()) + pFieldMap->SetFieldValue(m_database, newRow, gMapiFields[i].multiLine, line2.get()); + } + } + } + } + // call fieldMap SetFieldValue based on the table of fields + + NS_RELEASE(pFieldMap); + } + } + return true; +} diff --git a/mailnews/import/oexpress/nsOEAddressIterator.h b/mailnews/import/oexpress/nsOEAddressIterator.h new file mode 100644 index 000000000..fe6082736 --- /dev/null +++ b/mailnews/import/oexpress/nsOEAddressIterator.h @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsOEAddressIterator_h___ +#define nsOEAddressIterator_h___ + +#include "mozilla/Attributes.h" +#include "WabObject.h" +#include "nsIAddrDatabase.h" +#include "mdb.h" +#include "nsStringGlue.h" +#include "nsInterfaceHashtable.h" + +class nsOEAddressIterator : public CWabIterator { +public: + nsOEAddressIterator(CWAB *pWab, nsIAddrDatabase *database); + ~nsOEAddressIterator(); + + virtual nsresult EnumUser(const char16_t * pName, LPENTRYID pEid, ULONG cbEid) override; + virtual nsresult EnumList(const char16_t * pName, LPENTRYID pEid, ULONG cbEid, LPMAPITABLE table) override; + void FindListRow(nsString &eMail, nsIMdbRow **cardRow); + +private: + bool BuildCard(const char16_t * pName, nsIMdbRow *card, LPMAILUSER pUser); + void SanitizeValue(nsString& val); + void SplitString(nsString& val1, nsString& val2); + void SetBirthDay(nsIMdbRow *card, PRTime& birthDay); + + CWAB * m_pWab; + nsCOMPtr<nsIAddrDatabase> m_database; + nsInterfaceHashtable <nsStringHashKey, nsIMdbRow> m_listRows; +}; + +#endif diff --git a/mailnews/import/oexpress/nsOEImport.cpp b/mailnews/import/oexpress/nsOEImport.cpp new file mode 100644 index 000000000..21016c59e --- /dev/null +++ b/mailnews/import/oexpress/nsOEImport.cpp @@ -0,0 +1,657 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +/* + + Outlook Express (Win32) import mail and addressbook interfaces + +*/ +#include "nscore.h" +#include "nsMsgUtils.h" +#include "nsStringGlue.h" +#include "nsComponentManagerUtils.h" +#include "nsIImportService.h" +#include "nsOEImport.h" +#include "nsIMemory.h" +#include "nsOEScanBoxes.h" +#include "nsIImportService.h" +#include "nsIImportMail.h" +#include "nsIImportMailboxDescriptor.h" +#include "nsIImportGeneric.h" +#include "nsOEMailbox.h" +#include "nsIImportAddressBooks.h" +#include "nsIImportABDescriptor.h" +#include "nsIImportFieldMap.h" +#include "nsIMutableArray.h" +#include "nsXPCOM.h" +#include "nsISupportsPrimitives.h" +#include "WabObject.h" +#include "nsOEAddressIterator.h" +#include "nsIOutputStream.h" +#include "nsOE5File.h" +#include "nsIAddrDatabase.h" +#include "nsOESettings.h" +#include "nsTextFormatter.h" +#include "nsOEStringBundle.h" +#include "nsIStringBundle.h" +#include "nsUnicharUtils.h" +#include "nsDirectoryServiceUtils.h" +#include "nsDirectoryServiceDefs.h" + +#include "OEDebugLog.h" + +static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); +PRLogModuleInfo *OELOGMODULE = nullptr; + +class ImportOEMailImpl : public nsIImportMail +{ +public: + ImportOEMailImpl(); + + static nsresult Create(nsIImportMail** aImport); + + // nsISupports interface + NS_DECL_THREADSAFE_ISUPPORTS + + // nsIImportmail interface + + /* void GetDefaultLocation (out nsIFile location, out boolean found, out boolean userVerify); */ + NS_IMETHOD GetDefaultLocation(nsIFile **location, bool *found, bool *userVerify); + + /* nsIArray FindMailboxes (in nsIFile location); */ + NS_IMETHOD FindMailboxes(nsIFile *location, nsIArray **_retval); + + NS_IMETHOD ImportMailbox(nsIImportMailboxDescriptor *source, + nsIMsgFolder *dstFolder, + char16_t **pErrorLog, char16_t **pSuccessLog, + bool *fatalError); + + /* unsigned long GetImportProgress (); */ + NS_IMETHOD GetImportProgress(uint32_t *_retval); + + NS_IMETHOD TranslateFolderName(const nsAString & aFolderName, nsAString & _retval); + +public: + static void ReportSuccess(nsString& name, int32_t count, nsString *pStream); + static void ReportError(int32_t errorNum, nsString& name, nsString *pStream); + static void AddLinebreak(nsString *pStream); + static void SetLogs(nsString& success, nsString& error, char16_t **pError, char16_t **pSuccess); + +private: + virtual ~ImportOEMailImpl(); + uint32_t m_bytesDone; +}; + + +class ImportOEAddressImpl : public nsIImportAddressBooks +{ +public: + ImportOEAddressImpl(); + + static nsresult Create(nsIImportAddressBooks** aImport); + + // nsISupports interface + NS_DECL_THREADSAFE_ISUPPORTS + + // nsIImportAddressBooks interface + + NS_IMETHOD GetSupportsMultiple(bool *_retval) { *_retval = false; return NS_OK;} + + NS_IMETHOD GetAutoFind(char16_t **description, bool *_retval); + + NS_IMETHOD GetNeedsFieldMap(nsIFile *pLoc, bool *_retval) { *_retval = false; return NS_OK;} + + NS_IMETHOD GetDefaultLocation(nsIFile **location, bool *found, bool *userVerify); + + NS_IMETHOD FindAddressBooks(nsIFile *location, nsIArray **_retval); + + NS_IMETHOD InitFieldMap(nsIImportFieldMap *fieldMap) + { return NS_ERROR_FAILURE; } + + NS_IMETHOD ImportAddressBook(nsIImportABDescriptor *source, + nsIAddrDatabase *destination, + nsIImportFieldMap *fieldMap, + nsISupports *aSupportService, + char16_t **errorLog, + char16_t **successLog, + bool *fatalError); + + NS_IMETHOD GetImportProgress(uint32_t *_retval); + + NS_IMETHOD GetSampleData(int32_t index, bool *pFound, char16_t **pStr) + { return NS_ERROR_FAILURE;} + + NS_IMETHOD SetSampleLocation(nsIFile *) { return NS_OK; } + +private: + virtual ~ImportOEAddressImpl(); + static void ReportSuccess(nsString& name, nsString *pStream); + +private: + CWAB * m_pWab; + int m_doneSoFar; +}; +//////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////// + + +nsOEImport::nsOEImport() +{ + // Init logging module. + if (!OELOGMODULE) + OELOGMODULE = PR_NewLogModule("IMPORT"); + IMPORT_LOG0("nsOEImport Module Created\n"); + nsOEStringBundle::GetStringBundle(); +} + + +nsOEImport::~nsOEImport() +{ + IMPORT_LOG0("nsOEImport Module Deleted\n"); +} + +NS_IMPL_ISUPPORTS(nsOEImport, nsIImportModule) + +NS_IMETHODIMP nsOEImport::GetName(char16_t **name) +{ + NS_ENSURE_ARG_POINTER(name); + + *name = nsOEStringBundle::GetStringByID(OEIMPORT_NAME); + + return NS_OK; +} + +NS_IMETHODIMP nsOEImport::GetDescription(char16_t **name) +{ + NS_ENSURE_ARG_POINTER(name); + + *name = nsOEStringBundle::GetStringByID(OEIMPORT_DESCRIPTION); + return NS_OK; +} + +NS_IMETHODIMP nsOEImport::GetSupports(char **supports) +{ + NS_PRECONDITION(supports != nullptr, "null ptr"); + if (! supports) + return NS_ERROR_NULL_POINTER; + + *supports = strdup(kOESupportsString); + return NS_OK; +} + + +NS_IMETHODIMP nsOEImport::GetSupportsUpgrade(bool *pUpgrade) +{ + NS_PRECONDITION(pUpgrade != nullptr, "null ptr"); + if (! pUpgrade) + return NS_ERROR_NULL_POINTER; + + *pUpgrade = true; + return NS_OK; +} + +NS_IMETHODIMP nsOEImport::GetImportInterface(const char *pImportType, nsISupports **ppInterface) +{ + NS_ENSURE_ARG_POINTER(pImportType); + NS_ENSURE_ARG_POINTER(ppInterface); + + *ppInterface = nullptr; + nsresult rv; + if (!strcmp(pImportType, "mail")) { + // create the nsIImportMail interface and return it! + nsIImportMail * pMail = nullptr; + nsIImportGeneric *pGeneric = nullptr; + rv = ImportOEMailImpl::Create(&pMail); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr<nsIImportService> impSvc(do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv)); + if (NS_SUCCEEDED(rv)) { + rv = impSvc->CreateNewGenericMail(&pGeneric); + if (NS_SUCCEEDED(rv)) { + pGeneric->SetData("mailInterface", pMail); + nsString name; + nsOEStringBundle::GetStringByID(OEIMPORT_NAME, name); + nsCOMPtr<nsISupportsString> nameString (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv)); + if (NS_SUCCEEDED(rv)) { + nameString->SetData(name); + pGeneric->SetData("name", nameString); + rv = pGeneric->QueryInterface(kISupportsIID, (void **)ppInterface); + } + } + } + } + NS_IF_RELEASE(pMail); + NS_IF_RELEASE(pGeneric); + return rv; + } + + if (!strcmp(pImportType, "addressbook")) { + // create the nsIImportMail interface and return it! + nsIImportAddressBooks * pAddress = nullptr; + nsIImportGeneric * pGeneric = nullptr; + rv = ImportOEAddressImpl::Create(&pAddress); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr<nsIImportService> impSvc(do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv)); + if (NS_SUCCEEDED(rv)) { + rv = impSvc->CreateNewGenericAddressBooks(&pGeneric); + if (NS_SUCCEEDED(rv)) { + pGeneric->SetData("addressInterface", pAddress); + rv = pGeneric->QueryInterface(kISupportsIID, (void **)ppInterface); + } + } + } + NS_IF_RELEASE(pAddress); + NS_IF_RELEASE(pGeneric); + return rv; + } + + if (!strcmp(pImportType, "settings")) { + nsIImportSettings *pSettings = nullptr; + rv = nsOESettings::Create(&pSettings); + if (NS_SUCCEEDED(rv)) + pSettings->QueryInterface(kISupportsIID, (void **)ppInterface); + NS_IF_RELEASE(pSettings); + return rv; + } + + return NS_ERROR_NOT_AVAILABLE; +} + +///////////////////////////////////////////////////////////////////////////////// +nsresult ImportOEMailImpl::Create(nsIImportMail** aImport) +{ + NS_ENSURE_ARG_POINTER(aImport); + *aImport = new ImportOEMailImpl(); + NS_ENSURE_TRUE(*aImport, NS_ERROR_OUT_OF_MEMORY); + NS_ADDREF(*aImport); + return NS_OK; +} + +ImportOEMailImpl::ImportOEMailImpl() +{ +} + + +ImportOEMailImpl::~ImportOEMailImpl() +{ +} + +NS_IMPL_ISUPPORTS(ImportOEMailImpl, nsIImportMail) + +NS_IMETHODIMP ImportOEMailImpl::TranslateFolderName(const nsAString & aFolderName, nsAString & _retval) +{ + if (aFolderName.LowerCaseEqualsLiteral("deleted items")) + _retval = NS_LITERAL_STRING(kDestTrashFolderName); + else if (aFolderName.LowerCaseEqualsLiteral("sent items")) + _retval = NS_LITERAL_STRING(kDestSentFolderName); + else if (aFolderName.LowerCaseEqualsLiteral("outbox")) + _retval = NS_LITERAL_STRING(kDestUnsentMessagesFolderName); + else + _retval = aFolderName; + + return NS_OK; +} + +NS_IMETHODIMP ImportOEMailImpl::GetDefaultLocation(nsIFile **ppLoc, bool *found, bool *userVerify) +{ + NS_PRECONDITION(ppLoc != nullptr, "null ptr"); + NS_PRECONDITION(found != nullptr, "null ptr"); + NS_PRECONDITION(userVerify != nullptr, "null ptr"); + if (!ppLoc || !found || !userVerify) + return NS_ERROR_NULL_POINTER; + + // use scanboxes to find the location. + nsresult rv; + nsCOMPtr <nsIFile> file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv); + if (NS_FAILED(rv)) + return rv; + + if (nsOEScanBoxes::FindMail(file)) { + *found = true; + NS_IF_ADDREF(*ppLoc = file); + } + else { + *found = false; + *ppLoc = nullptr; + } + *userVerify = true; + return NS_OK; +} + + +NS_IMETHODIMP ImportOEMailImpl::FindMailboxes(nsIFile *pLoc, nsIArray **ppArray) +{ + NS_PRECONDITION(pLoc != nullptr, "null ptr"); + NS_PRECONDITION(ppArray != nullptr, "null ptr"); + if (!pLoc || !ppArray) + return NS_ERROR_NULL_POINTER; + + bool exists = false; + nsresult rv = pLoc->Exists(&exists); + if (NS_FAILED(rv) || !exists) + return NS_ERROR_FAILURE; + + nsOEScanBoxes scan; + + if (!scan.GetMailboxes(pLoc, ppArray)) + *ppArray = nullptr; + + return NS_OK; +} + +void ImportOEMailImpl::AddLinebreak(nsString *pStream) +{ + if (pStream) + pStream->Append(char16_t('\n')); +} + +void ImportOEMailImpl::ReportSuccess(nsString& name, int32_t count, nsString *pStream) +{ + if (!pStream) + return; + // load the success string + char16_t *pFmt = nsOEStringBundle::GetStringByID(OEIMPORT_MAILBOX_SUCCESS); + char16_t *pText = nsTextFormatter::smprintf(pFmt, name.get(), count); + pStream->Append(pText); + nsTextFormatter::smprintf_free(pText); + nsOEStringBundle::FreeString(pFmt); + AddLinebreak(pStream); +} + +void ImportOEMailImpl::ReportError(int32_t errorNum, nsString& name, nsString *pStream) +{ + if (!pStream) + return; + // load the error string + char16_t *pFmt = nsOEStringBundle::GetStringByID(errorNum); + char16_t *pText = nsTextFormatter::smprintf(pFmt, name.get()); + pStream->Append(pText); + nsTextFormatter::smprintf_free(pText); + nsOEStringBundle::FreeString(pFmt); + AddLinebreak(pStream); +} + + +void ImportOEMailImpl::SetLogs(nsString& success, nsString& error, char16_t **pError, char16_t **pSuccess) +{ + if (pError) + *pError = ToNewUnicode(error); + if (pSuccess) + *pSuccess = ToNewUnicode(success); +} + +NS_IMETHODIMP ImportOEMailImpl::ImportMailbox(nsIImportMailboxDescriptor *pSource, + nsIMsgFolder *dstFolder, + char16_t **pErrorLog, + char16_t **pSuccessLog, + bool *fatalError) +{ + NS_ENSURE_ARG_POINTER(pSource); + NS_ENSURE_ARG_POINTER(dstFolder); + NS_ENSURE_ARG_POINTER(fatalError); + + nsString success; + nsString error; + bool abort = false; + nsString name; + nsString pName; + if (NS_SUCCEEDED(pSource->GetDisplayName(getter_Copies(pName)))) + name = pName; + + uint32_t mailSize = 0; + pSource->GetSize(&mailSize); + if (mailSize == 0) { + ReportSuccess(name, 0, &success); + SetLogs(success, error, pErrorLog, pSuccessLog); + return NS_OK; + } + + nsCOMPtr <nsIFile> inFile; + if (NS_FAILED(pSource->GetFile(getter_AddRefs(inFile)))) { + ReportError(OEIMPORT_MAILBOX_BADSOURCEFILE, name, &error); + SetLogs(success, error, pErrorLog, pSuccessLog); + return NS_ERROR_FAILURE; + } + + nsCString pPath; + inFile->GetNativePath(pPath); + IMPORT_LOG1("Importing Outlook Express mailbox: %s\n", pPath.get()); + + m_bytesDone = 0; + uint32_t msgCount = 0; + nsresult rv; + if (nsOE5File::IsLocalMailFile(inFile)) { + IMPORT_LOG1("Importing OE5 mailbox: %s!\n", NS_LossyConvertUTF16toASCII(name.get())); + rv = nsOE5File::ImportMailbox( &m_bytesDone, &abort, name, inFile, dstFolder, &msgCount); + } + else { + if (CImportMailbox::ImportMailbox( &m_bytesDone, &abort, name, inFile, dstFolder, &msgCount)) + rv = NS_OK; + else + rv = NS_ERROR_FAILURE; + } + + if (NS_SUCCEEDED(rv)) + ReportSuccess(name, msgCount, &success); + else + ReportError(OEIMPORT_MAILBOX_CONVERTERROR, name, &error); + + SetLogs(success, error, pErrorLog, pSuccessLog); + + return rv; +} + +NS_IMETHODIMP ImportOEMailImpl::GetImportProgress(uint32_t *pDoneSoFar) +{ + NS_ENSURE_ARG_POINTER(pDoneSoFar); + *pDoneSoFar = m_bytesDone; + return NS_OK; +} + +nsresult ImportOEAddressImpl::Create(nsIImportAddressBooks** aImport) +{ + NS_ENSURE_ARG_POINTER(aImport); + + *aImport = new ImportOEAddressImpl(); + NS_ENSURE_TRUE(*aImport, NS_ERROR_OUT_OF_MEMORY); + NS_ADDREF(*aImport); + return NS_OK; +} + +ImportOEAddressImpl::ImportOEAddressImpl() +{ + m_pWab = nullptr; +} + + +ImportOEAddressImpl::~ImportOEAddressImpl() +{ + if (m_pWab) + delete m_pWab; +} + +NS_IMPL_ISUPPORTS(ImportOEAddressImpl, nsIImportAddressBooks) + +NS_IMETHODIMP ImportOEAddressImpl::GetDefaultLocation(nsIFile **aLocation, + bool *aFound, + bool *aUserVerify) +{ + NS_ENSURE_ARG_POINTER(aLocation); + NS_ENSURE_ARG_POINTER(aFound); + NS_ENSURE_ARG_POINTER(aUserVerify); + + *aLocation = nullptr; + *aUserVerify = true; + + CWAB *wab = new CWAB(nullptr); + *aFound = wab->IsAvailable(); + delete wab; + + if (*aFound) { + // Unfortunately WAB interface has no function to obtain address book location. + // So we set a fake location here. + if (NS_SUCCEEDED(NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR, aLocation))) + *aUserVerify = false; + } + + return NS_OK; +} + +NS_IMETHODIMP ImportOEAddressImpl::GetAutoFind(char16_t **description, bool *_retval) +{ + NS_PRECONDITION(description != nullptr, "null ptr"); + NS_PRECONDITION(_retval != nullptr, "null ptr"); + if (! description || !_retval) + return NS_ERROR_NULL_POINTER; + + *_retval = false; + nsString str; + str.Append(nsOEStringBundle::GetStringByID(OEIMPORT_AUTOFIND)); + *description = ToNewUnicode(str); + return NS_OK; +} + + + +NS_IMETHODIMP ImportOEAddressImpl::FindAddressBooks(nsIFile *location, nsIArray **_retval) +{ + NS_PRECONDITION(_retval != nullptr, "null ptr"); + if (!_retval) + return NS_ERROR_NULL_POINTER; + + nsresult rv; + nsCOMPtr<nsIMutableArray> array(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv)); + if (NS_FAILED(rv)) + return rv; + + // Make sure we can load up the windows address book... + rv = NS_ERROR_FAILURE; + + if (m_pWab) + delete m_pWab; + + nsCOMPtr<nsIFile> currentProcessDir; + rv = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR, + getter_AddRefs(currentProcessDir)); + bool equals = false; + currentProcessDir->Equals(location, &equals); + // If the location is not a fake, use it. + if (location && !equals) { + nsCOMPtr<nsIFile> localFile = do_QueryInterface(location, &rv); + NS_ENSURE_SUCCESS(rv, rv); + m_pWab = new CWAB(localFile); + } else { + m_pWab = new CWAB(nullptr); + } + + nsIImportABDescriptor * pID; + nsISupports * pInterface; + nsString str; + str.Append(nsOEStringBundle::GetStringByID(OEIMPORT_DEFAULT_NAME)); + + if (m_pWab->Loaded()) { + // create a new nsIImportABDescriptor and add it to the array + nsCOMPtr<nsIImportService> impSvc(do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv)); + if (NS_SUCCEEDED(rv)) { + rv = impSvc->CreateNewABDescriptor(&pID); + if (NS_SUCCEEDED(rv)) { + pID->SetIdentifier(0x4F453334); + pID->SetRef(1); + pID->SetSize(100); + pID->SetPreferredName(str); + rv = pID->QueryInterface(kISupportsIID, (void **) &pInterface); + array->AppendElement(pInterface, false); + pInterface->Release(); + pID->Release(); + } + } + } + + if (NS_FAILED(rv)) { + delete m_pWab; + m_pWab = nullptr; + } + array.forget(_retval); + return NS_OK; +} + + + +NS_IMETHODIMP ImportOEAddressImpl::ImportAddressBook(nsIImportABDescriptor *source, + nsIAddrDatabase *destination, + nsIImportFieldMap *fieldMap, + nsISupports *aSupportService, + char16_t **errorLog, + char16_t **successLog, + bool *fatalError) +{ + NS_PRECONDITION(source != nullptr, "null ptr"); + // NS_PRECONDITION(destination != nullptr, "null ptr"); + // NS_PRECONDITION(fieldMap != nullptr, "null ptr"); + NS_PRECONDITION(fatalError != nullptr, "null ptr"); + if (!source || !fatalError) + return NS_ERROR_NULL_POINTER; + + // we assume it is our one and only address book. + if (!m_pWab) { + IMPORT_LOG0("Wab not loaded in ImportAddressBook call\n"); + return NS_ERROR_FAILURE; + } + + IMPORT_LOG0("IMPORTING OUTLOOK EXPRESS ADDRESS BOOK\n"); + + nsString success; + nsString error; + if (!source || !destination || !fatalError) + { + nsOEStringBundle::GetStringByID(OEIMPORT_ADDRESS_BADPARAM, error); + if (fatalError) + *fatalError = true; + ImportOEMailImpl::SetLogs(success, error, errorLog, successLog); + return NS_ERROR_NULL_POINTER; + } + + m_doneSoFar = 0; + nsOEAddressIterator * pIter = new nsOEAddressIterator(m_pWab, destination); + HRESULT hr = m_pWab->IterateWABContents(pIter, &m_doneSoFar); + delete pIter; + + nsString name; + if (SUCCEEDED(hr) && NS_SUCCEEDED(source->GetPreferredName(name))) + ReportSuccess(name, &success); + else + ImportOEMailImpl::ReportError(OEIMPORT_ADDRESS_CONVERTERROR, name, &error); + + ImportOEMailImpl::SetLogs(success, error, errorLog, successLog); + + nsresult rv = destination->Commit(nsAddrDBCommitType::kLargeCommit); + return rv; +} + + +NS_IMETHODIMP ImportOEAddressImpl::GetImportProgress(uint32_t *_retval) +{ + NS_PRECONDITION(_retval != nullptr, "null ptr"); + if (! _retval) + return NS_ERROR_NULL_POINTER; + + *_retval = (uint32_t) m_doneSoFar; + return NS_OK; +} + +void ImportOEAddressImpl::ReportSuccess(nsString& name, nsString *pStream) +{ + if (!pStream) + return; + // load the success string + char16_t *pFmt = nsOEStringBundle::GetStringByID(OEIMPORT_ADDRESS_SUCCESS); + char16_t *pText = nsTextFormatter::smprintf(pFmt, name.get()); + pStream->Append(pText); + nsTextFormatter::smprintf_free(pText); + nsOEStringBundle::FreeString(pFmt); + ImportOEMailImpl::AddLinebreak(pStream); +} diff --git a/mailnews/import/oexpress/nsOEImport.h b/mailnews/import/oexpress/nsOEImport.h new file mode 100644 index 000000000..c637a7461 --- /dev/null +++ b/mailnews/import/oexpress/nsOEImport.h @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsOEImport_h___ +#define nsOEImport_h___ + +#include "nsIImportModule.h" +#include "nsCOMPtr.h" + +#define NS_OEIMPORT_CID \ +{ /* be0bc880-1742-11d3-a206-00a0cc26da63 */ \ + 0xbe0bc880, 0x1742, 0x11d3, \ + {0xa2, 0x06, 0x0, 0xa0, 0xcc, 0x26, 0xda, 0x63}} + + + +#define kOESupportsString NS_IMPORT_MAIL_STR "," NS_IMPORT_ADDRESS_STR "," NS_IMPORT_SETTINGS_STR + +class nsOEImport : public nsIImportModule +{ +public: + + nsOEImport(); + + NS_DECL_ISUPPORTS + + //////////////////////////////////////////////////////////////////////////////////////// + // we suppport the nsIImportModule interface + //////////////////////////////////////////////////////////////////////////////////////// + + + NS_DECL_NSIIMPORTMODULE + +protected: + virtual ~nsOEImport(); +}; + + + +#endif /* nsOEImport_h___ */ diff --git a/mailnews/import/oexpress/nsOEMailbox.cpp b/mailnews/import/oexpress/nsOEMailbox.cpp new file mode 100644 index 000000000..7a1fb0be7 --- /dev/null +++ b/mailnews/import/oexpress/nsOEMailbox.cpp @@ -0,0 +1,673 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsOEMailbox.h" + +#include "OEDebugLog.h" +#include "msgCore.h" +#include "prprf.h" +#include "nsMsgLocalFolderHdrs.h" +#include "nsCRT.h" +#include "nsNetUtil.h" +#include "nsIInputStream.h" +#include "nsIOutputStream.h" +#include "nsIMsgFolder.h" +#include "nsIMsgHdr.h" +#include "nsIMsgPluggableStore.h" +#include "nsISeekableStream.h" +#include "nsMsgUtils.h" + +class CMbxScanner { +public: + CMbxScanner(nsString& name, nsIFile * mbxFile, nsIMsgFolder *dstFolder); + ~CMbxScanner(); + + virtual bool Initialize(void); + virtual bool DoWork(bool *pAbort, uint32_t *pDone, uint32_t *pCount); + + bool WasErrorFatal(void) { return m_fatalError;} + uint32_t BytesProcessed(void) { return m_didBytes;} + +protected: + bool WriteMailItem(uint32_t flags, uint32_t offset, uint32_t size, uint32_t *pTotalMsgSize = nullptr); + virtual void CleanUp(void); + +private: + void ReportWriteError(nsIMsgFolder *folder, bool fatal = true); + void ReportReadError(nsIFile * file, bool fatal = true); + bool CopyMbxFileBytes(uint32_t flags, uint32_t numBytes); + bool IsFromLineKey(uint8_t *pBuf, uint32_t max); + +public: + uint32_t m_msgCount; + +protected: + uint32_t * m_pDone; + nsString m_name; + nsCOMPtr<nsIFile> m_mbxFile; + nsCOMPtr<nsIMsgFolder> m_dstFolder; + nsCOMPtr<nsIInputStream> m_mbxFileInputStream; + nsCOMPtr<nsIOutputStream> m_dstOutputStream; + nsCOMPtr<nsIMsgPluggableStore> m_msgStore; + uint8_t * m_pInBuffer; + uint8_t * m_pOutBuffer; + uint32_t m_bufSz; + uint32_t m_didBytes; + bool m_fatalError; + int64_t m_mbxFileSize; + uint32_t m_mbxOffset; + + static const char * m_pFromLine; + +}; + + +class CIndexScanner : public CMbxScanner { +public: + CIndexScanner(nsString& name, nsIFile * idxFile, nsIFile * mbxFile, nsIMsgFolder *dstFolder); + ~CIndexScanner(); + + virtual bool Initialize(void); + virtual bool DoWork(bool *pAbort, uint32_t *pDone, uint32_t *pCount); + +protected: + virtual void CleanUp(void); + +private: + bool ValidateIdxFile(void); + bool GetMailItem(uint32_t *pFlags, uint32_t *pOffset, uint32_t *pSize); + + +private: + nsCOMPtr <nsIFile> m_idxFile; + nsCOMPtr <nsIInputStream> m_idxFileInputStream; + uint32_t m_numMessages; + uint32_t m_idxOffset; + uint32_t m_curItemIndex; +}; + + +bool CImportMailbox::ImportMailbox(uint32_t *pDone, bool *pAbort, + nsString& name, nsIFile * inFile, + nsIMsgFolder *outFolder, uint32_t *pCount) +{ + bool done = false; + nsresult rv; + nsCOMPtr <nsIFile> idxFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv); + if (NS_SUCCEEDED(rv)) + rv = idxFile->InitWithFile(inFile); + if (NS_FAILED(rv)) { + IMPORT_LOG0("New file spec failed!\n"); + return false; + } + + if (GetIndexFile(idxFile)) { + + IMPORT_LOG1("Using index file for: %S\n", name.get()); + + CIndexScanner *pIdxScanner = new CIndexScanner(name, idxFile, inFile, outFolder); + if (pIdxScanner->Initialize()) { + if (pIdxScanner->DoWork(pAbort, pDone, pCount)) { + done = true; + } + else { + IMPORT_LOG0("CIndexScanner::DoWork() failed\n"); + } + } + else { + IMPORT_LOG0("CIndexScanner::Initialize() failed\n"); + } + + delete pIdxScanner; + } + + if (done) + return done; + + /* + something went wrong with the index file, just scan the mailbox + file itself. + */ + CMbxScanner *pMbx = new CMbxScanner(name, inFile, outFolder); + if (pMbx->Initialize()) { + if (pMbx->DoWork(pAbort, pDone, pCount)) { + done = true; + } + else { + IMPORT_LOG0("CMbxScanner::DoWork() failed\n"); + } + } + else { + IMPORT_LOG0("CMbxScanner::Initialize() failed\n"); + } + + delete pMbx; + return done; +} + + +bool CImportMailbox::GetIndexFile(nsIFile* file) +{ + nsCString pLeaf; + if (NS_FAILED(file->GetNativeLeafName(pLeaf))) + return false; + int32_t len = pLeaf.Length(); + if (len < 5) + return false; + + pLeaf.Replace(len - 3, 3, NS_LITERAL_CSTRING("idx")); + + IMPORT_LOG1("Looking for index leaf name: %s\n", pLeaf); + + nsresult rv; + rv = file->SetNativeLeafName(pLeaf); + + bool isFile = false; + bool exists = false; + if (NS_SUCCEEDED(rv)) rv = file->IsFile(&isFile); + if (NS_SUCCEEDED(rv)) rv = file->Exists(&exists); + + return (isFile && exists); +} + + +const char *CMbxScanner::m_pFromLine = "From - Mon Jan 1 00:00:00 1965\x0D\x0A"; +// let's try a 16K buffer and see how well that works? +#define kBufferKB 16 + + +CMbxScanner::CMbxScanner(nsString& name, nsIFile* mbxFile, + nsIMsgFolder* dstFolder) +{ + m_msgCount = 0; + m_name = name; + m_mbxFile = mbxFile; + m_dstFolder = dstFolder; + m_pInBuffer = nullptr; + m_pOutBuffer = nullptr; + m_bufSz = 0; + m_fatalError = false; + m_didBytes = 0; + m_mbxFileSize = 0; + m_mbxOffset = 0; +} + +CMbxScanner::~CMbxScanner() +{ + CleanUp(); +} + +void CMbxScanner::ReportWriteError(nsIMsgFolder * folder, bool fatal) +{ + m_fatalError = fatal; +} + +void CMbxScanner::ReportReadError(nsIFile * file, bool fatal) +{ + m_fatalError = fatal; +} + +bool CMbxScanner::Initialize(void) +{ + m_bufSz = (kBufferKB * 1024); + m_pInBuffer = new uint8_t[m_bufSz]; + m_pOutBuffer = new uint8_t[m_bufSz]; + if (!m_pInBuffer || !m_pOutBuffer) { + return false; + } + + m_mbxFile->GetFileSize(&m_mbxFileSize); + // open the mailbox file... + if (NS_FAILED(NS_NewLocalFileInputStream(getter_AddRefs(m_mbxFileInputStream), m_mbxFile))) { + CleanUp(); + return false; + } + + if (NS_FAILED(m_dstFolder->GetMsgStore(getter_AddRefs(m_msgStore)))) { + CleanUp(); + return false; + } + + return true; +} + + +#define kMbxHeaderSize 0x0054 +#define kMbxMessageHeaderSz 16 + +bool CMbxScanner::DoWork(bool *pAbort, uint32_t *pDone, uint32_t *pCount) +{ + m_mbxOffset = kMbxHeaderSize; + m_didBytes = kMbxHeaderSize; + + while (!(*pAbort) && ((m_mbxOffset + kMbxMessageHeaderSz) < m_mbxFileSize)) { + uint32_t msgSz; + if (!WriteMailItem(0, m_mbxOffset, 0, &msgSz)) { + if (!WasErrorFatal()) + ReportReadError(m_mbxFile); + return false; + } + m_mbxOffset += msgSz; + m_didBytes += msgSz; + m_msgCount++; + if (pDone) + *pDone = m_didBytes; + if (pCount) + *pCount = m_msgCount; + } + + CleanUp(); + + return true; +} + + +void CMbxScanner::CleanUp(void) +{ + if (m_mbxFileInputStream) + m_mbxFileInputStream->Close(); + if (m_dstOutputStream) + m_dstOutputStream->Close(); + + delete [] m_pInBuffer; + m_pInBuffer = nullptr; + + delete [] m_pOutBuffer; + m_pOutBuffer = nullptr; +} + + +#define kNumMbxLongsToRead 4 + +bool CMbxScanner::WriteMailItem(uint32_t flags, uint32_t offset, uint32_t size, + uint32_t *pTotalMsgSize) +{ + uint32_t values[kNumMbxLongsToRead]; + int32_t cnt = kNumMbxLongsToRead * sizeof(uint32_t); + nsresult rv; + uint32_t cntRead; + int8_t * pChar = (int8_t *) values; + + nsCOMPtr <nsISeekableStream> seekableStream = do_QueryInterface(m_mbxFileInputStream); + rv = seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, offset); + + if (NS_FAILED(rv)) { + IMPORT_LOG1("Mbx seek error: 0x%lx\n", offset); + return false; + } + rv = m_mbxFileInputStream->Read((char *) pChar, cnt, &cntRead); + if (NS_FAILED(rv) || (cntRead != cnt)) { + IMPORT_LOG1("Mbx read error at: 0x%lx\n", offset); + return false; + } + if (values[0] != 0x7F007F00) { + IMPORT_LOG2("Mbx tag field doesn't match: 0x%lx, at offset: 0x%lx\n", values[0], offset); + return false; + } + if (size && (values[2] != size)) { + IMPORT_LOG3("Mbx size doesn't match idx, mbx: %ld, idx: %ld, at offset: 0x%lx\n", values[2], size, offset); + return false; + } + + if (pTotalMsgSize != nullptr) + *pTotalMsgSize = values[2]; + + nsCOMPtr<nsIMsgDBHdr> msgHdr; + bool reusable; + + rv = m_msgStore->GetNewMsgOutputStream(m_dstFolder, getter_AddRefs(msgHdr), &reusable, + getter_AddRefs(m_dstOutputStream)); + if (NS_FAILED(rv)) + { + IMPORT_LOG1( "Mbx getting outputstream error: 0x%lx\n", rv); + return false; + } + + // everything looks kosher... + // the actual message text follows and is values[3] bytes long... + bool copyOK = CopyMbxFileBytes(flags, values[3]); + if (copyOK) + m_msgStore->FinishNewMessage(m_dstOutputStream, msgHdr); + else { + m_msgStore->DiscardNewMessage(m_dstOutputStream, msgHdr); + IMPORT_LOG0( "Mbx CopyMbxFileBytes failed\n"); + } + if (!reusable) + { + m_dstOutputStream->Close(); + m_dstOutputStream = nullptr; + } + return copyOK; +} + +bool CMbxScanner::IsFromLineKey(uint8_t * pBuf, uint32_t max) +{ + return (max > 5 && (pBuf[0] == 'F') && (pBuf[1] == 'r') && (pBuf[2] == 'o') && (pBuf[3] == 'm') && (pBuf[4] == ' ')); +} + + +#define IS_ANY_SPACE(_ch) ((_ch == ' ') || (_ch == '\t') || (_ch == 10) || (_ch == 13)) + + +bool CMbxScanner::CopyMbxFileBytes(uint32_t flags, uint32_t numBytes) +{ + if (!numBytes) + return true; + + uint32_t cnt; + uint8_t last[2] = {0, 0}; + uint32_t inIdx = 0; + bool first = true; + uint8_t * pIn; + uint8_t * pStart; + int32_t fromLen = strlen(m_pFromLine); + nsresult rv; + uint32_t cntRead; + uint8_t * pChar; + + while (numBytes) { + if (numBytes > (m_bufSz - inIdx)) + cnt = m_bufSz - inIdx; + else + cnt = numBytes; + // Read some of the message from the file... + pChar = m_pInBuffer + inIdx; + rv = m_mbxFileInputStream->Read((char *) pChar, (int32_t)cnt, &cntRead); + if (NS_FAILED(rv) || (cntRead != (int32_t)cnt)) { + ReportReadError(m_mbxFile); + return false; + } + // Keep track of the last 2 bytes of the message for terminating EOL logic + if (cnt < 2) { + last[0] = last[1]; + last[1] = m_pInBuffer[cnt - 1]; + } + else { + last[0] = m_pInBuffer[cnt - 2]; + last[1] = m_pInBuffer[cnt - 1]; + } + + inIdx = 0; + // Handle the beginning line, don't duplicate an existing From separator + if (first) { + // check the first buffer to see if it already starts with a From line + // If it does, throw it away and use our own + if (IsFromLineKey(m_pInBuffer, cnt)) { + // skip past the first line + while ((inIdx < cnt) && (m_pInBuffer[inIdx] != nsCRT::CR)) + inIdx++; + while ((inIdx < cnt) && (IS_ANY_SPACE(m_pInBuffer[inIdx]))) + inIdx++; + if (inIdx >= cnt) { + // This should not occurr - it means the message starts + // with a From separator line that is longer than our + // file buffer! In this bizarre case, just skip this message + // since it is probably bogus anyway. + return true; + } + + } + // Begin every message with a From separator + rv = m_dstOutputStream->Write(m_pFromLine, fromLen, &cntRead); + if (NS_FAILED(rv) || (cntRead != fromLen)) { + ReportWriteError(m_dstFolder); + return false; + } + char statusLine[50]; + uint32_t msgFlags = flags; // need to convert from OE flags to mozilla flags + PR_snprintf(statusLine, sizeof(statusLine), X_MOZILLA_STATUS_FORMAT MSG_LINEBREAK, msgFlags & 0xFFFF); + rv = m_dstOutputStream->Write(statusLine, strlen(statusLine), &cntRead); + if (NS_SUCCEEDED(rv) && cntRead == fromLen) + { + PR_snprintf(statusLine, sizeof(statusLine), X_MOZILLA_STATUS2_FORMAT MSG_LINEBREAK, msgFlags & 0xFFFF0000); + rv = m_dstOutputStream->Write(statusLine, strlen(statusLine), &cntRead); + } + if (NS_FAILED(rv) || (cntRead != fromLen)) { + ReportWriteError(m_dstFolder); + return false; + } + first = false; + } + + // Handle generic data, escape any lines that begin with "From " + pIn = m_pInBuffer + inIdx; + numBytes -= cnt; + m_didBytes += cnt; + pStart = pIn; + cnt -= inIdx; + inIdx = 0; + while (cnt) { + if (*pIn == nsCRT::CR) { + // need more in buffer? + if ((cnt < 7) && numBytes) + break; + + if (cnt > 6) { + if ((pIn[1] == nsCRT::LF) && IsFromLineKey(pIn + 2, cnt)) { + inIdx += 2; + // Match, escape it + rv = m_dstOutputStream->Write((const char *)pStart, (int32_t)inIdx, &cntRead); + if (NS_SUCCEEDED(rv) && (cntRead == (int32_t)inIdx)) + rv = m_dstOutputStream->Write(">", 1, &cntRead); + if (NS_FAILED(rv) || (cntRead != 1)) { + ReportWriteError(m_dstFolder); + return false; + } + + cnt -= 2; + pIn += 2; + inIdx = 0; + pStart = pIn; + continue; + } + } + } // == nsCRT::CR + + cnt--; + inIdx++; + pIn++; + } + rv = m_dstOutputStream->Write((const char *)pStart, (int32_t)inIdx, &cntRead); + if (NS_FAILED(rv) || (cntRead != (int32_t)inIdx)) { + ReportWriteError(m_dstFolder); + return false; + } + + if (cnt) { + inIdx = cnt; + memcpy(m_pInBuffer, pIn, cnt); + } + else + inIdx = 0; + } + + // I used to check for an eol before writing one but + // it turns out that adding a proper EOL before the next + // separator never really hurts so better to be safe + // and always do it. + // if ((last[0] != nsCRT::CR) || (last[1] != nsCRT::LF)) { + rv = m_dstOutputStream->Write("\x0D\x0A", 2, &cntRead); + if (NS_FAILED(rv) || (cntRead != 2)) { + ReportWriteError(m_dstFolder); + return false; + } + // } // != nsCRT::CR || != nsCRT::LF + + return true; +} + +CIndexScanner::CIndexScanner(nsString& name, nsIFile * idxFile, + nsIFile * mbxFile, nsIMsgFolder * dstFolder) + : CMbxScanner( name, mbxFile, dstFolder) +{ + m_idxFile = idxFile; + m_curItemIndex = 0; + m_idxOffset = 0; +} + +CIndexScanner::~CIndexScanner() +{ + CleanUp(); +} + +bool CIndexScanner::Initialize(void) +{ + if (!CMbxScanner::Initialize()) + return false; + + + nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(m_idxFileInputStream), m_idxFile); + if (NS_FAILED(rv)) { + CleanUp(); + return false; + } + + return true; +} + +bool CIndexScanner::ValidateIdxFile(void) +{ + int8_t id[4]; + int32_t cnt = 4; + nsresult rv; + uint32_t cntRead; + int8_t * pReadTo; + + pReadTo = id; + rv = m_idxFileInputStream->Read((char *) pReadTo, cnt, &cntRead); + if (NS_FAILED(rv) || (cntRead != cnt)) + return false; + if ((id[0] != 'J') || (id[1] != 'M') || (id[2] != 'F') || (id[3] != '9')) + return false; + cnt = 4; + uint32_t subId; + pReadTo = (int8_t *) &subId; + rv = m_idxFileInputStream->Read((char *) pReadTo, cnt, &cntRead); + if (NS_FAILED(rv) || (cntRead != cnt)) + return false; + if (subId != 0x00010004) { + IMPORT_LOG1("Idx file subid doesn't match: 0x%lx\n", subId); + return false; + } + + pReadTo = (int8_t *) &m_numMessages; + rv = m_idxFileInputStream->Read((char *) pReadTo, cnt, &cntRead); + if (NS_FAILED(rv) || (cntRead != cnt)) + return false; + + IMPORT_LOG1("Idx file num messages: %ld\n", m_numMessages); + + m_didBytes += 80; + m_idxOffset = 80; + return true; +} + +/* +Idx file... +Header is 80 bytes, JMF9, subId? 0x00010004, numMessages, fileSize, 1, 0x00010010 +Entries start at byte 80 +4 byte numbers +Flags? maybe +?? who knows +index +start of this entry in the file +length of this record +msg offset in mbx +msg length in mbx + +*/ + +// #define DEBUG_SUBJECT_AND_FLAGS 1 +#define kNumIdxLongsToRead 7 + +bool CIndexScanner::GetMailItem(uint32_t *pFlags, uint32_t *pOffset, uint32_t *pSize) +{ + uint32_t values[kNumIdxLongsToRead]; + int32_t cnt = kNumIdxLongsToRead * sizeof(uint32_t); + int8_t * pReadTo = (int8_t *) values; + uint32_t cntRead; + nsresult rv; + + nsCOMPtr <nsISeekableStream> seekableStream = do_QueryInterface(m_idxFileInputStream); + rv = seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, m_idxOffset); + if (NS_FAILED(rv)) + return false; + + rv = m_idxFileInputStream->Read((char *) pReadTo, cnt, &cntRead); + if (NS_FAILED(rv) || (cntRead != cnt)) + return false; + + if (values[3] != m_idxOffset) { + IMPORT_LOG2("Self pointer invalid: m_idxOffset=0x%lx, self=0x%lx\n", m_idxOffset, values[3]); + return false; + } + + // So... what do we have here??? +#ifdef DEBUG_SUBJECT_AND_FLAGS + IMPORT_LOG2("Number: %ld, msg offset: 0x%lx, ", values[2], values[5]); + IMPORT_LOG2("msg length: %ld, Flags: 0x%lx\n", values[6], values[0]); + seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, m_idxOffset + 212); + uint32_t subSz = 0; + cnt = 4; + pReadTo = (int8_t *) &subSz; + m_idxFileInputStream->Read((char *) pReadTo, cnt, &cntRead); + if ((subSz >= 0) && (subSz < 1024)) { + char *pSub = new char[subSz + 1]; + m_idxFileInputStream->Read(pSub, subSz, &cntRead); + pSub[subSz] = 0; + IMPORT_LOG1(" Subject: %s\n", pSub); + delete [] pSub; + } +#endif + + m_idxOffset += values[4]; + m_didBytes += values[4]; + + *pFlags = values[0]; + *pOffset = values[5]; + *pSize = values[6]; + return true; +} + +#define kOEDeletedFlag 0x0001 + +bool CIndexScanner::DoWork(bool *pAbort, uint32_t *pDone, uint32_t *pCount) +{ + m_didBytes = 0; + if (!ValidateIdxFile()) + return false; + + bool failed = false; + while ((m_curItemIndex < m_numMessages) && !failed && !(*pAbort)) { + uint32_t flags, offset, size; + if (!GetMailItem(&flags, &offset, &size)) { + CleanUp(); + return false; + } + m_curItemIndex++; + if (!(flags & kOEDeletedFlag)) { + if (!WriteMailItem(flags, offset, size)) + failed = true; + else { + m_msgCount++; + } + } + m_didBytes += size; + if (pDone) + *pDone = m_didBytes; + if (pCount) + *pCount = m_msgCount; + } + + CleanUp(); + return !failed; +} + + +void CIndexScanner::CleanUp(void) +{ + CMbxScanner::CleanUp(); + m_idxFileInputStream->Close(); +} diff --git a/mailnews/import/oexpress/nsOEMailbox.h b/mailnews/import/oexpress/nsOEMailbox.h new file mode 100644 index 000000000..656313aad --- /dev/null +++ b/mailnews/import/oexpress/nsOEMailbox.h @@ -0,0 +1,27 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsOEMailbox_h___ +#define nsOEMailbox_h___ + +#include "nscore.h" +#include "nsStringGlue.h" +#include "nsIFile.h" + +class nsIMsgFolder; + +class CImportMailbox { +public: + static bool ImportMailbox(uint32_t *pDone, bool *pAbort, nsString& name, + nsIFile * inFile, nsIMsgFolder * outFolder, + uint32_t *pCount); + +private: + static bool GetIndexFile(nsIFile* mbxFile); +}; + + + +#endif // nsOEMailbox_h__ diff --git a/mailnews/import/oexpress/nsOERegUtil.cpp b/mailnews/import/oexpress/nsOERegUtil.cpp new file mode 100644 index 000000000..e3aa9d647 --- /dev/null +++ b/mailnews/import/oexpress/nsOERegUtil.cpp @@ -0,0 +1,27 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "nsOERegUtil.h" + +#include "OEDebugLog.h" +#include <windows.h> +#include "nsIWindowsRegKey.h" +#include "nsComponentManagerUtils.h" + +nsresult nsOERegUtil::GetDefaultUserId(nsAString &aUserId) +{ + nsresult rv; + nsCOMPtr<nsIWindowsRegKey> key = + do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = key->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, + NS_LITERAL_STRING("Identities"), + nsIWindowsRegKey::ACCESS_QUERY_VALUE); + if (NS_FAILED(rv)) + return rv; + + return key->ReadStringValue(NS_LITERAL_STRING("Default User ID"), aUserId); +} + diff --git a/mailnews/import/oexpress/nsOERegUtil.h b/mailnews/import/oexpress/nsOERegUtil.h new file mode 100644 index 000000000..9a873d63b --- /dev/null +++ b/mailnews/import/oexpress/nsOERegUtil.h @@ -0,0 +1,20 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsOERegUtil_h___ +#define nsOERegUtil_h___ + +#include <windows.h> +#include "nsStringGlue.h" + +class nsOERegUtil +{ +public: + static nsresult GetDefaultUserId(nsAString &aUserId); +}; + + + +#endif /* nsOERegUtil_h___ */ diff --git a/mailnews/import/oexpress/nsOEScanBoxes.cpp b/mailnews/import/oexpress/nsOEScanBoxes.cpp new file mode 100644 index 000000000..7c30aa68d --- /dev/null +++ b/mailnews/import/oexpress/nsOEScanBoxes.cpp @@ -0,0 +1,859 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "nsOEScanBoxes.h" +#include "nsMsgUtils.h" +#include "nsIComponentManager.h" +#include "nsIServiceManager.h" +#include "nsIImportService.h" +#include "nsIFile.h" +#include "nsIImportMailboxDescriptor.h" +#include "nsOERegUtil.h" +#include "nsOE5File.h" +#include "nsNetUtil.h" +#include "OEDebugLog.h" +#include "nsIInputStream.h" +#include "nsISeekableStream.h" +#include "plstr.h" +#include <windows.h> +#include "nsIWindowsRegKey.h" + +#ifdef MOZILLA_INTERNAL_API +#include "nsNativeCharsetUtils.h" +#else +#include "nsMsgI18N.h" +#define NS_CopyNativeToUnicode(source, dest) \ + nsMsgI18NConvertToUnicode(nsMsgI18NFileSystemCharset(), source, dest) +#define NS_CopyUnicodeToNative(source, dest) \ + nsMsgI18NConvertFromUnicode(nsMsgI18NFileSystemCharset(), source, dest) +#endif + +/* + .nch file format??? + + offset 20 - long = offset to first record + +*/ + +static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); + +nsOEScanBoxes::nsOEScanBoxes() +{ + m_pFirst = nullptr; +} + +nsOEScanBoxes::~nsOEScanBoxes() +{ + int i, max; + MailboxEntry *pEntry; + for (i = 0, max = m_entryArray.Length(); i < max; i++) { + pEntry = m_entryArray.ElementAt(i); + delete pEntry; + } + // Now free the unprocessed child entries (ie, those without parents for some reason). + for (i = 0, max = m_pendingChildArray.Length(); i < max; i++) + { + pEntry = m_pendingChildArray.ElementAt(i); + if (!pEntry->processed) + delete pEntry; + } +} + +/* + 3.x & 4.x registry + Software/Microsoft/Outlook Express/ + + 5.0 registry + Identies - value of "Default User ID" is {GUID} + Identities/{GUID}/Software/Microsoft/Outlook Express/5.0/ +*/ + +bool nsOEScanBoxes::Find50Mail(nsIFile *pWhere) +{ + nsAutoString userId; + nsresult rv = nsOERegUtil::GetDefaultUserId(userId); + NS_ENSURE_SUCCESS(rv, false); + + nsAutoString path(NS_LITERAL_STRING("Identities\\")); + path.Append(userId); + path.AppendLiteral("\\Software\\Microsoft\\Outlook Express\\5.0"); + + nsCOMPtr<nsIWindowsRegKey> key = + do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv); + NS_ENSURE_SUCCESS(rv, false); + + rv = key->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, + path, + nsIWindowsRegKey::ACCESS_QUERY_VALUE); + NS_ENSURE_SUCCESS(rv, false); + + nsAutoString storeRoot; + key->ReadStringValue(NS_LITERAL_STRING("Store Root"), storeRoot); + NS_ENSURE_SUCCESS(rv, false); + + nsCOMPtr<nsIFile> localWhere = do_QueryInterface(pWhere); + localWhere->InitWithPath(storeRoot); + + nsAutoCString nativeStoreRoot; + NS_CopyUnicodeToNative(storeRoot, nativeStoreRoot); + IMPORT_LOG1("Setting native path: %s\n", nativeStoreRoot.get()); + + bool isDir = false; + rv = localWhere->IsDirectory(&isDir); + return isDir; +} + +bool nsOEScanBoxes::FindMail(nsIFile *pWhere) +{ + if (Find50Mail(pWhere)) + return true; + + nsresult rv; + nsCOMPtr<nsIWindowsRegKey> key = + do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv); + NS_ENSURE_SUCCESS(rv, false); + + rv = key->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, + NS_LITERAL_STRING("Software\\Microsoft\\Outlook Express"), + nsIWindowsRegKey::ACCESS_QUERY_VALUE); + NS_ENSURE_SUCCESS(rv, false); + + nsAutoString storeRoot; + key->ReadStringValue(NS_LITERAL_STRING("Store Root"), storeRoot); + NS_ENSURE_SUCCESS(rv, false); + + nsCOMPtr<nsIFile> localWhere = do_QueryInterface(pWhere); + localWhere->InitWithPath(storeRoot); + localWhere->AppendNative(NS_LITERAL_CSTRING("Mail")); + + bool isDir = false; + localWhere->IsDirectory(&isDir); + + return isDir; +} + +bool nsOEScanBoxes::GetMailboxes(nsIFile *pWhere, nsIArray **pArray) +{ + nsCString path; + pWhere->GetNativePath(path); + if (!path.IsEmpty()) { + IMPORT_LOG1("Looking for mail in: %s\n", path.get()); + } + else { + pWhere->GetNativeLeafName(path); + if (!path.IsEmpty()) + IMPORT_LOG1("Looking for mail in: %s\n", path.get()); + else + IMPORT_LOG0("Unable to get info about where to look for mail\n"); + } + + nsCOMPtr <nsIFile> location; + pWhere->Clone(getter_AddRefs(location)); + // 1. Look for 5.0 folders.dbx + // 2. Look for 3.x & 4.x folders.nch + // 3. Look for 5.0 *.dbx mailboxes + // 4. Look for 3.x & 4.x *.mbx mailboxes + + bool result; + + location->AppendNative(NS_LITERAL_CSTRING("folders.dbx")); + if (Find50MailBoxes(location)) { + result = GetMailboxList(pWhere, pArray); + } + else { + // 2. Look for 4.x mailboxes + pWhere->Clone(getter_AddRefs(location)); + location->AppendNative(NS_LITERAL_CSTRING("folders.nch")); + + if (FindMailBoxes(location)) { + result = GetMailboxList(pWhere, pArray); + } + else { + // 3 & 4, look for the specific mailbox files. + pWhere->Clone(getter_AddRefs(location)); + ScanMailboxDir(location); + result = GetMailboxList(pWhere, pArray); + } + } + + return result; +} + + + +void nsOEScanBoxes::Reset(void) +{ + int max = m_entryArray.Length(); + for (int i = 0; i < max; i++) { + MailboxEntry *pEntry = m_entryArray.ElementAt(i); + delete pEntry; + } + m_entryArray.Clear(); + m_pFirst = nullptr; +} + + +bool nsOEScanBoxes::FindMailBoxes(nsIFile* descFile) +{ + Reset(); + + nsresult rv; + bool isFile = false; + + rv = descFile->IsFile(&isFile); + if (NS_FAILED(rv) || !isFile) + return false; + + nsCOMPtr <nsIInputStream> descInputStream; + rv = NS_NewLocalFileInputStream(getter_AddRefs(descInputStream), descFile); + NS_ENSURE_SUCCESS(rv, false); + + IMPORT_LOG0("Reading the folders.nch file\n"); + + uint32_t curRec; + if (!ReadLong(descInputStream, curRec, 20)) { + return false; + } + + // Now for each record + bool done = false; + uint32_t equal; + uint32_t size; + uint32_t previous; + uint32_t next; + MailboxEntry * pEntry; + bool failed; + + while (!done) { + if (!ReadLong(descInputStream, equal, curRec)) return false; + if (curRec != equal) { + IMPORT_LOG1("Record start invalid: %ld\n", curRec); + break; + } + if (!ReadLong(descInputStream, size, curRec + 4)) return false; + if (!ReadLong(descInputStream, previous, curRec + 8)) return false; + if (!ReadLong(descInputStream, next, curRec + 12)) return false; + failed = false; + pEntry = new MailboxEntry; + if (!ReadLong(descInputStream, pEntry->index, curRec + 16)) failed = true; + if (!ReadString(descInputStream, pEntry->mailName, curRec + 20)) failed = true; + if (!ReadString(descInputStream, pEntry->fileName, curRec + 279)) failed = true; + if (!ReadLong(descInputStream, pEntry->parent, curRec + 539)) failed = true; + if (!ReadLong(descInputStream, pEntry->child, curRec + 543)) failed = true; + if (!ReadLong(descInputStream, pEntry->sibling, curRec + 547)) failed = true; + if (!ReadLong(descInputStream, pEntry->type, curRec + 551)) failed = true; + if (failed) { + delete pEntry; + return false; + } + + #ifdef _TRACE_MAILBOX_ENTRIES + IMPORT_LOG0("------------\n"); + IMPORT_LOG2(" Offset: %lx, index: %ld\n", curRec, pEntry->index); + IMPORT_LOG2(" previous: %lx, next: %lx\n", previous, next); + IMPORT_LOG2(" Name: %S, File: %s\n", (char16_t *) pEntry->mailName, (const char *) pEntry->fileName); + IMPORT_LOG3(" Parent: %ld, Child: %ld, Sibling: %ld\n", pEntry->parent, pEntry->child, pEntry->sibling); + #endif + + if (!StringEndsWith(pEntry->fileName, NS_LITERAL_CSTRING(".mbx"))) + pEntry->fileName.Append(".mbx"); + + m_entryArray.AppendElement(pEntry); + + curRec = next; + if (!next) + done = true; + } + + MailboxEntry *pZero = GetIndexEntry(0); + if (pZero) + m_pFirst = GetIndexEntry(pZero->child); + + IMPORT_LOG1("Read the folders.nch file, found %ld mailboxes\n", (long) m_entryArray.Length()); + + return true; +} + +bool nsOEScanBoxes::Find50MailBoxes(nsIFile* descFile) +{ + Reset(); + + nsresult rv; + bool isFile = false; + + rv = descFile->IsFile(&isFile); + if (NS_FAILED(rv) || !isFile) + return false; + + nsCOMPtr <nsIInputStream> descInputStream; + rv = NS_NewLocalFileInputStream(getter_AddRefs(descInputStream), descFile); + NS_ENSURE_SUCCESS(rv, false); + + IMPORT_LOG0("Reading the folders.dbx file\n"); + + uint32_t *pIndex; + uint32_t indexSize = 0; + if (!nsOE5File::ReadIndex(descInputStream, &pIndex, &indexSize)) { + IMPORT_LOG0("*** NOT USING FOLDERS.DBX!!!\n"); + return false; + } + + uint32_t marker; + uint32_t size; + char *pBytes; + uint32_t cntRead; + int32_t recordId; + int32_t strOffset; + + uint8_t tag; + uint32_t data; + int32_t dataOffset; + + uint32_t id; + uint32_t parent; + uint32_t numMessages; + char * pFileName; + char * pDataSource; + + MailboxEntry * pEntry; + MailboxEntry * pLastEntry = nullptr; + + uint32_t localStoreId = 0; + + for (uint32_t i = 0; i < indexSize; i++) { + if (!ReadLong(descInputStream, marker, pIndex[i])) continue; + if (marker != pIndex[i]) continue; + if (!ReadLong(descInputStream, size, pIndex[i] + 4)) continue; + size += 4; + pBytes = new char[size]; + rv = descInputStream->Read(pBytes, size, &cntRead); + if (NS_FAILED(rv) || ((uint32_t)cntRead != size)) { + delete [] pBytes; + continue; + } + recordId = pBytes[2]; + strOffset = (recordId * 4) + 4; + if (recordId == 4) + strOffset += 4; + + id = 0; + parent = 0; + numMessages = 0; + pFileName = nullptr; + pDataSource = nullptr; + dataOffset = 4; + while (dataOffset < strOffset) { + tag = (uint8_t) pBytes[dataOffset]; + + data = 0; // make sure all bytes are 0 before copying 3 bytes over. + memcpy(&data, &(pBytes[dataOffset + 1]), 3); + switch(tag) { + case 0x80: // id record + id = data; + break; + case 0x81: // parent id + parent = data; + break; + case 0x87: // number of messages in this mailbox + numMessages = data; + break; + case 0x03: // file name for this mailbox + if (((uint32_t)strOffset + data) < size) + pFileName = (char *)(pBytes + strOffset + data); + break; + case 0x05: // data source for this record (this is not a mailbox!) + if (((uint32_t)strOffset + data) < size) + pDataSource = (char *) (pBytes + strOffset + data); + break; + } + dataOffset += 4; + } + + // now build an entry if necessary! + if (pDataSource) { + if (!PL_strcasecmp(pDataSource, "LocalStore")) + { + localStoreId = id; + // See if we have any child folders that need to be added/processed for this top level parent. + ProcessPendingChildEntries(localStoreId, localStoreId, m_pendingChildArray); + // Clean up the pending list. + RemoveProcessedChildEntries(); + } + } + else if (id && localStoreId && parent) { + // veryify that this mailbox is in the local store + data = parent; + while (data && (data != localStoreId)) { + pEntry = GetIndexEntry(data); + if (pEntry) + data = pEntry->parent; + else + data = 0; + } + if (data == localStoreId) { + // Create an entry for this bugger + pEntry = NewMailboxEntry(id, parent, (const char *) (pBytes + strOffset), pFileName); + if (pEntry) + { + AddChildEntry(pEntry, localStoreId); + pEntry->processed = true; + // See if we have any child folders that need to be added/processed. + ProcessPendingChildEntries(id, localStoreId, m_pendingChildArray); + // Clean up the pending list. + RemoveProcessedChildEntries(); + } + } + else + { + // Put this folder into child array and process it when its parent shows up. + pEntry = NewMailboxEntry(id, parent, (const char *) (pBytes + strOffset), pFileName); + if (pEntry) + m_pendingChildArray.AppendElement(pEntry); + } + } + else if (pFileName) + { + // Put this folder into child array and process it when its parent shows up. + // For some reason, it's likely that child folders come before their parents. + pEntry = NewMailboxEntry(id, parent, (const char *) (pBytes + strOffset), pFileName); + if (pEntry) + m_pendingChildArray.AppendElement(pEntry); + } + + delete [] pBytes; + } + + + delete [] pIndex; + + return m_entryArray.Length(); +} + +nsOEScanBoxes::MailboxEntry *nsOEScanBoxes::NewMailboxEntry(uint32_t id, uint32_t parent, const char *prettyName, char *pFileName) +{ + MailboxEntry *pEntry = new MailboxEntry(); + if (!pEntry) + return nullptr; + + pEntry->index = id; + pEntry->parent = parent; + pEntry->child = 0; + pEntry->type = 0; + pEntry->sibling = -1; + pEntry->processed = false; + NS_CopyNativeToUnicode(nsDependentCString(prettyName), pEntry->mailName); + if (pFileName) + pEntry->fileName = pFileName; + return pEntry; +} + +void nsOEScanBoxes::ProcessPendingChildEntries(uint32_t parent, uint32_t rootIndex, nsTArray<MailboxEntry*> &childArray) +{ + int32_t i, max; + MailboxEntry *pEntry; + for (i = 0, max = childArray.Length(); i < max; i++) + { + pEntry = childArray.ElementAt(i); + if ((!pEntry->processed) && (pEntry->parent == parent)) + { + AddChildEntry(pEntry, rootIndex); + pEntry->processed = true; // indicate it's been processed. + // See if there are unprocessed child folders for this child in the + // array as well (ie, both child and grand-child are on the list). + ProcessPendingChildEntries(pEntry->index, rootIndex, childArray); + } + } +} + +void nsOEScanBoxes::RemoveProcessedChildEntries() +{ + // Remove already processed entries from the pending list. Note that these entries are also + // on 'm_entryArray' list so we don't want to deallocate the space for the entries now. + MailboxEntry * pEntry; + int32_t i; + for (i = m_pendingChildArray.Length()-1; i >= 0; i--) + { + pEntry = m_pendingChildArray.ElementAt(i); + if (pEntry->processed) + m_pendingChildArray.RemoveElementAt(i); + } +} + +void nsOEScanBoxes::AddChildEntry(MailboxEntry *pEntry, uint32_t rootIndex) +{ + if (!m_pFirst) { + if (pEntry->parent == rootIndex) { + m_pFirst = pEntry; + m_entryArray.AppendElement(pEntry); + } + else { + delete pEntry; + } + return; + } + + MailboxEntry * pParent = nullptr; + MailboxEntry * pSibling = nullptr; + if (pEntry->parent == rootIndex) { + pSibling = m_pFirst; + } + else { + pParent = GetIndexEntry(pEntry->parent); + } + + if (!pParent && !pSibling) { + delete pEntry; + return; + } + + if (pParent && (pParent->child == 0)) { + pParent->child = pEntry->index; + m_entryArray.AppendElement(pEntry); + return; + } + + if (!pSibling) + pSibling = GetIndexEntry(pParent->child); + + while (pSibling && (pSibling->sibling != -1)) { + pSibling = GetIndexEntry(pSibling->sibling); + } + + if (!pSibling) { + delete pEntry; + return; + } + + pSibling->sibling = pEntry->index; + m_entryArray.AppendElement(pEntry); +} + +bool nsOEScanBoxes::Scan50MailboxDir(nsIFile * srcDir) +{ + Reset(); + + MailboxEntry *pEntry; + int32_t index = 1; + char *pLeaf; + + bool hasMore; + nsCOMPtr<nsISimpleEnumerator> directoryEnumerator; + nsresult rv = srcDir->GetDirectoryEntries(getter_AddRefs(directoryEnumerator)); + NS_ENSURE_SUCCESS(rv, false); + + directoryEnumerator->HasMoreElements(&hasMore); + bool isFile; + nsCOMPtr<nsIFile> entry; + nsCString fName; + + while (hasMore && NS_SUCCEEDED(rv)) + { + nsCOMPtr<nsISupports> aSupport; + rv = directoryEnumerator->GetNext(getter_AddRefs(aSupport)); + nsCOMPtr<nsIFile> entry(do_QueryInterface(aSupport, &rv)); + directoryEnumerator->HasMoreElements(&hasMore); + + isFile = false; + rv = entry->IsFile(&isFile); + if (NS_SUCCEEDED(rv) && isFile) { + pLeaf = nullptr; + rv = entry->GetNativeLeafName(fName); + if (NS_SUCCEEDED(rv) && + (StringEndsWith(fName, NS_LITERAL_CSTRING(".dbx")))) { + // This is a *.dbx file in the mail directory + if (nsOE5File::IsLocalMailFile(entry)) { + pEntry = new MailboxEntry; + pEntry->index = index; + index++; + pEntry->parent = 0; + pEntry->child = 0; + pEntry->sibling = index; + pEntry->type = -1; + fName.SetLength(fName.Length() - 4); + pEntry->fileName = fName.get(); + NS_CopyNativeToUnicode(fName, pEntry->mailName); + m_entryArray.AppendElement(pEntry); + } + } + } + } + + if (m_entryArray.Length() > 0) { + pEntry = m_entryArray.ElementAt(m_entryArray.Length() - 1); + pEntry->sibling = -1; + return true; + } + + return false; +} + +void nsOEScanBoxes::ScanMailboxDir(nsIFile * srcDir) +{ + if (Scan50MailboxDir(srcDir)) + return; + + Reset(); + + MailboxEntry * pEntry; + int32_t index = 1; + nsAutoCString pLeaf; + uint32_t sLen; + + bool hasMore; + nsCOMPtr<nsISimpleEnumerator> directoryEnumerator; + nsresult rv = srcDir->GetDirectoryEntries(getter_AddRefs(directoryEnumerator)); + NS_ENSURE_SUCCESS_VOID(rv); + + directoryEnumerator->HasMoreElements(&hasMore); + bool isFile; + nsCOMPtr<nsIFile> entry; + nsCString fName; + nsCString ext; + nsCString name; + + while (hasMore && NS_SUCCEEDED(rv)) + { + nsCOMPtr<nsISupports> aSupport; + rv = directoryEnumerator->GetNext(getter_AddRefs(aSupport)); + nsCOMPtr<nsIFile> entry(do_QueryInterface(aSupport, &rv)); + directoryEnumerator->HasMoreElements(&hasMore); + + isFile = false; + rv = entry->IsFile(&isFile); + if (NS_SUCCEEDED(rv) && isFile) + { + rv = entry->GetNativeLeafName(pLeaf); + if (NS_SUCCEEDED(rv) && !pLeaf.IsEmpty() && + ((sLen = pLeaf.Length()) > 4) && + (!PL_strcasecmp(pLeaf.get() + sLen - 3, "mbx"))) + { + // This is a *.mbx file in the mail directory + pEntry = new MailboxEntry; + pEntry->index = index; + index++; + pEntry->parent = 0; + pEntry->child = 0; + pEntry->sibling = index; + pEntry->type = -1; + pEntry->fileName = pLeaf; + pLeaf.SetLength(sLen - 4); + NS_CopyNativeToUnicode(pLeaf, pEntry->mailName); + m_entryArray.AppendElement(pEntry); + } + } + } + + if (m_entryArray.Length() > 0) { + pEntry = m_entryArray.ElementAt(m_entryArray.Length() - 1); + pEntry->sibling = -1; + } +} + +uint32_t nsOEScanBoxes::CountMailboxes(MailboxEntry *pBox) +{ + if (pBox == nullptr) { + if (m_pFirst != nullptr) + pBox = m_pFirst; + else { + if (m_entryArray.Length() > 0) + pBox = m_entryArray.ElementAt(0); + } + } + uint32_t count = 0; + + MailboxEntry * pChild; + while (pBox) { + count++; + if (pBox->child) { + pChild = GetIndexEntry(pBox->child); + if (pChild != nullptr) + count += CountMailboxes(pChild); + } + if (pBox->sibling != -1) { + pBox = GetIndexEntry(pBox->sibling); + } + else + pBox = nullptr; + } + + return count; +} + +bool nsOEScanBoxes::GetMailboxList(nsIFile * root, nsIArray **pArray) +{ + nsresult rv; + nsCOMPtr<nsIMutableArray> array(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv)); + if (NS_FAILED(rv)) { + IMPORT_LOG0("FAILED to allocate the nsIArray\n"); + return false; + } + + BuildMailboxList(nullptr, root, 1, array); + array.forget(pArray); + + return true; +} + +void nsOEScanBoxes::BuildMailboxList(MailboxEntry *pBox, nsIFile * root, int32_t depth, nsIMutableArray *pArray) +{ + if (pBox == nullptr) { + if (m_pFirst != nullptr) { + pBox = m_pFirst; + + IMPORT_LOG0("Assigning start of mailbox list to m_pFirst\n"); + } + else { + if (m_entryArray.Length() > 0) { + pBox = m_entryArray.ElementAt(0); + + IMPORT_LOG0("Assigning start of mailbox list to entry at index 0\n"); + } + } + + if (pBox == nullptr) { + IMPORT_LOG0("ERROR ASSIGNING STARTING MAILBOX\n"); + } + + } + + nsresult rv; + nsCOMPtr<nsIFile> file; + MailboxEntry *pChild; + nsIImportMailboxDescriptor *pID; + nsISupports *pInterface; + int64_t size; + + nsCOMPtr<nsIImportService> impSvc(do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS_VOID(rv); + + while (pBox) { + rv = impSvc->CreateNewMailboxDescriptor(&pID); + if (NS_SUCCEEDED(rv)) { + pID->SetDepth(depth); + pID->SetIdentifier(pBox->index); + pID->SetDisplayName((char16_t *)pBox->mailName.get()); + if (!pBox->fileName.IsEmpty()) { + pID->GetFile(getter_AddRefs(file)); + file->InitWithFile(root); + file->AppendNative(pBox->fileName); + size = 0; + file->GetFileSize(&size); + pID->SetSize(size); + } + rv = pID->QueryInterface(kISupportsIID, (void **) &pInterface); + pArray->AppendElement(pInterface, false); + pInterface->Release(); + pID->Release(); + } + + if (pBox->child) { + pChild = GetIndexEntry(pBox->child); + if (pChild != nullptr) + BuildMailboxList(pChild, root, depth + 1, pArray); + } + if (pBox->sibling != -1) { + pBox = GetIndexEntry(pBox->sibling); + } + else + pBox = nullptr; + } + +} + +nsOEScanBoxes::MailboxEntry * nsOEScanBoxes::GetIndexEntry(uint32_t index) +{ + int32_t max = m_entryArray.Length(); + for (int32_t i = 0; i < max; i++) { + MailboxEntry *pEntry = m_entryArray.ElementAt(i); + if (pEntry->index == index) + return pEntry; + } + + return nullptr; +} + + +// ------------------------------------------------------- +// File utility routines +// ------------------------------------------------------- + +bool nsOEScanBoxes::ReadLong(nsIInputStream * stream, int32_t& val, uint32_t offset) +{ + nsresult rv; + nsCOMPtr <nsISeekableStream> seekStream = do_QueryInterface(stream, &rv); + NS_ENSURE_SUCCESS(rv, false); + + rv = seekStream->Seek(nsISeekableStream::NS_SEEK_SET, offset); + NS_ENSURE_SUCCESS(rv, false); + + uint32_t cntRead; + char * pReadTo = (char *)&val; + rv = stream->Read(pReadTo, sizeof(val), &cntRead); + + return NS_SUCCEEDED(rv) && cntRead == sizeof(val); +} + +bool nsOEScanBoxes::ReadLong(nsIInputStream * stream, uint32_t& val, uint32_t offset) +{ + nsresult rv; + nsCOMPtr <nsISeekableStream> seekStream = do_QueryInterface(stream, &rv); + NS_ENSURE_SUCCESS(rv, false); + rv = seekStream->Seek(nsISeekableStream::NS_SEEK_SET, offset); + NS_ENSURE_SUCCESS(rv, false); + + uint32_t cntRead; + char * pReadTo = (char *)&val; + rv = stream->Read(pReadTo, sizeof(val), &cntRead); + if (NS_FAILED(rv) || (cntRead != sizeof(val))) + return false; + + return true; +} + +// It appears as though the strings for file name and mailbox +// name are at least 254 chars - verified - they are probably 255 +// but why bother going that far! If a file name is that long then +// the heck with it. +#define kOutlookExpressStringLength 252 +bool nsOEScanBoxes::ReadString(nsIInputStream * stream, nsString& str, uint32_t offset) +{ + nsresult rv; + nsCOMPtr<nsISeekableStream> seekStream = do_QueryInterface(stream, &rv); + NS_ENSURE_SUCCESS(rv, false); + + rv = seekStream->Seek(nsISeekableStream::NS_SEEK_SET, offset); + NS_ENSURE_SUCCESS(rv, false); + + uint32_t cntRead; + char buffer[kOutlookExpressStringLength]; + char *pReadTo = buffer; + rv = stream->Read(pReadTo, kOutlookExpressStringLength, &cntRead); + if (NS_FAILED(rv) || (cntRead != kOutlookExpressStringLength)) + return false; + + buffer[kOutlookExpressStringLength - 1] = 0; + str.AssignASCII(buffer); + return true; +} + +bool nsOEScanBoxes::ReadString(nsIInputStream * stream, nsCString& str, uint32_t offset) +{ + nsresult rv; + nsCOMPtr<nsISeekableStream> seekStream = do_QueryInterface(stream, &rv); + NS_ENSURE_SUCCESS(rv, false); + + rv = seekStream->Seek(nsISeekableStream::NS_SEEK_SET, offset); + NS_ENSURE_SUCCESS(rv, false); + + uint32_t cntRead; + char buffer[kOutlookExpressStringLength]; + char *pReadTo = buffer; + rv = stream->Read(pReadTo, kOutlookExpressStringLength, &cntRead); + if (NS_FAILED(rv) || (cntRead != kOutlookExpressStringLength)) + return false; + + buffer[kOutlookExpressStringLength - 1] = 0; + str = buffer; + return true; +} diff --git a/mailnews/import/oexpress/nsOEScanBoxes.h b/mailnews/import/oexpress/nsOEScanBoxes.h new file mode 100644 index 000000000..e2e62b238 --- /dev/null +++ b/mailnews/import/oexpress/nsOEScanBoxes.h @@ -0,0 +1,76 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsOEScanBoxes_h___ +#define nsOEScanBoxes_h___ + +#include "nsStringGlue.h" +#include "nsIImportModule.h" +#include "nsTArray.h" +#include "nsCOMPtr.h" +#include "nsIArray.h" +#include "nsIMutableArray.h" +#include "nsIFile.h" +#include "nsIImportService.h" + +class nsIInputStream; + +class nsOEScanBoxes { +public: + nsOEScanBoxes(); + ~nsOEScanBoxes(); + + static bool FindMail(nsIFile *pWhere); + + bool GetMailboxes(nsIFile *pWhere, nsIArray **pArray); + + +private: + typedef struct { + uint32_t index; + uint32_t parent; + int32_t child; + int32_t sibling; + int32_t type; + nsString mailName; + nsCString fileName; + bool processed; // used by entries on m_pendingChildArray list + } MailboxEntry; + + static bool Find50Mail(nsIFile *pWhere); + + void Reset(void); + bool FindMailBoxes(nsIFile * descFile); + bool Find50MailBoxes(nsIFile * descFile); + + // If find mailboxes fails you can use this routine to get the raw mailbox file names + void ScanMailboxDir(nsIFile * srcDir); + bool Scan50MailboxDir(nsIFile * srcDir); + + MailboxEntry * GetIndexEntry(uint32_t index); + void AddChildEntry(MailboxEntry *pEntry, uint32_t rootIndex); + MailboxEntry * NewMailboxEntry(uint32_t id, uint32_t parent, const char *prettyName, char *pFileName); + void ProcessPendingChildEntries(uint32_t parent, uint32_t rootIndex, nsTArray<MailboxEntry*> &childArray); + void RemoveProcessedChildEntries(); + + + bool ReadLong(nsIInputStream * stream, int32_t& val, uint32_t offset); + bool ReadLong(nsIInputStream * stream, uint32_t& val, uint32_t offset); + bool ReadString(nsIInputStream * stream, nsString& str, uint32_t offset); + bool ReadString(nsIInputStream * stream, nsCString& str, uint32_t offset); + uint32_t CountMailboxes(MailboxEntry *pBox); + + void BuildMailboxList(MailboxEntry *pBox, nsIFile * root, int32_t depth, nsIMutableArray *pArray); + bool GetMailboxList(nsIFile * root, nsIArray **pArray); + +private: + MailboxEntry * m_pFirst; + nsTArray<MailboxEntry*> m_entryArray; + nsTArray<MailboxEntry*> m_pendingChildArray; // contains child folders whose parent folders have not showed up. + + nsCOMPtr<nsIImportService> mService; +}; + +#endif // nsOEScanBoxes_h__ diff --git a/mailnews/import/oexpress/nsOESettings.cpp b/mailnews/import/oexpress/nsOESettings.cpp new file mode 100644 index 000000000..600acf74c --- /dev/null +++ b/mailnews/import/oexpress/nsOESettings.cpp @@ -0,0 +1,921 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + + Outlook Express (Win32) settings + +*/ + +#include "nsOESettings.h" +#include "mozilla/WindowsVersion.h" +#include "nsCOMPtr.h" +#include "nscore.h" +#include "nsMsgUtils.h" +#include "nsOEImport.h" +#include "nsIComponentManager.h" +#include "nsIServiceManager.h" +#include "nsOERegUtil.h" +#include "nsIMsgAccountManager.h" +#include "nsIMsgAccount.h" +#include "nsIImportSettings.h" +#include "nsMsgBaseCID.h" +#include "nsMsgCompCID.h" +#include "nsMsgI18N.h" +#include "nsISmtpService.h" +#include "nsISmtpServer.h" +#include "nsOEStringBundle.h" +#include "OEDebugLog.h" +#include "nsIPop3IncomingServer.h" +#include "nsIImapIncomingServer.h" +#include "nsINntpIncomingServer.h" +#include "stdlib.h" +#include <windows.h> +#include "nsIWindowsRegKey.h" +#include "nsComponentManagerUtils.h" + +#ifdef MOZILLA_INTERNAL_API +#include "nsNativeCharsetUtils.h" +#else +#include "nsMsgI18N.h" +#define NS_CopyNativeToUnicode(source, dest) \ + nsMsgI18NConvertToUnicode(nsMsgI18NFileSystemCharset(), source, dest) +#define NS_CopyUnicodeToNative(source, dest) \ + nsMsgI18NConvertFromUnicode(nsMsgI18NFileSystemCharset(), source, dest) +#endif + +class OESettings { +public: + static nsresult GetDefaultMailAccount(nsAString &aMailAccount); + static nsresult GetCheckMailInterval(uint32_t *aInterval); + static nsresult Find50Key(nsIWindowsRegKey **aKey); + static nsresult Find40Key(nsIWindowsRegKey **aKey); + static nsresult FindAccountsKey(nsIWindowsRegKey **aKey); + + static bool DoImport(nsIMsgAccount **ppAccount); + + static bool DoIMAPServer(nsIMsgAccountManager *aMgr, + nsIWindowsRegKey *aKey, + const nsString &aServerName, + nsIMsgAccount **ppAccount); + static bool DoPOP3Server(nsIMsgAccountManager *aMgr, + nsIWindowsRegKey *aKey, + const nsString &aServerName, + nsIMsgAccount **ppAccount); + static bool DoNNTPServer(nsIMsgAccountManager *aMgr, + nsIWindowsRegKey *aKey, + const nsString &aServerName, + nsIMsgAccount **ppAccount); + + static void SetIncomingServerProperties(nsIMsgIncomingServer *aServer, + nsIWindowsRegKey *aKey, + const nsString &aKeyNamePrefix); + + static void SetIdentities(nsIMsgAccountManager *aMgr, + nsIMsgAccount *aAccount, + nsIWindowsRegKey *aKey, + const nsString &aIncomgUserName, + int32_t authMethodIncoming, bool isNNTP); + static void SetSmtpServer(const nsString &aSmtpServer, + nsIWindowsRegKey *aKey, + nsIMsgIdentity *aId, + const nsString &aIncomgUserName, + int32_t authMethodIncoming); + static nsresult GetAccountName(nsIWindowsRegKey *aKey, + const nsString &aDefaultName, + nsAString &aAccountName); + static bool IsKB933612Applied(); +}; + +static uint32_t checkNewMailTime;// OE global setting, let's default to 30 +static bool checkNewMail; // OE global setting, let's default to false + // This won't cause unwanted autodownloads- + // user can set prefs after import + +//////////////////////////////////////////////////////////////////////// +nsresult nsOESettings::Create(nsIImportSettings** aImport) +{ + NS_PRECONDITION(aImport != nullptr, "null ptr"); + if (! aImport) + return NS_ERROR_NULL_POINTER; + + *aImport = new nsOESettings(); + if (! *aImport) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(*aImport); + return NS_OK; +} + +nsOESettings::nsOESettings() +{ +} + +nsOESettings::~nsOESettings() +{ +} + +NS_IMPL_ISUPPORTS(nsOESettings, nsIImportSettings) + +NS_IMETHODIMP nsOESettings::AutoLocate(char16_t **description, nsIFile **location, bool *_retval) +{ + NS_PRECONDITION(description != nullptr, "null ptr"); + NS_PRECONDITION(_retval != nullptr, "null ptr"); + if (!description || !_retval) + return NS_ERROR_NULL_POINTER; + + *description = nsOEStringBundle::GetStringByID(OEIMPORT_NAME); + + if (location) + *location = nullptr; + + *_retval = false; + nsCOMPtr<nsIWindowsRegKey> key; + if (NS_FAILED(OESettings::Find50Key(getter_AddRefs(key))) && + NS_FAILED(OESettings::Find40Key(getter_AddRefs(key)))) + return NS_OK; + + if (NS_SUCCEEDED(OESettings::FindAccountsKey(getter_AddRefs(key)))) + *_retval = true; + + return NS_OK; +} + +NS_IMETHODIMP nsOESettings::SetLocation(nsIFile *location) +{ + return NS_OK; +} + +NS_IMETHODIMP nsOESettings::Import(nsIMsgAccount **localMailAccount, bool *_retval) +{ + NS_PRECONDITION(_retval != nullptr, "null ptr"); + + if (OESettings::DoImport(localMailAccount)) { + *_retval = true; + IMPORT_LOG0("Settings import appears successful\n"); + } + else { + *_retval = false; + IMPORT_LOG0("Settings import returned FALSE\n"); + } + + return NS_OK; +} + +nsresult OESettings::GetDefaultMailAccount(nsAString &aMailAccount) +{ + nsresult rv; + nsCOMPtr<nsIWindowsRegKey> key = + do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoString userId; + rv = nsOERegUtil::GetDefaultUserId(userId); + // OE has default mail account here when it has been + // set up by transfer or has multiple identities + // look below for orig code that looked in new OE installs + if (NS_SUCCEEDED(rv)) { + nsAutoString path(NS_LITERAL_STRING("Identities\\")); + path.Append(userId); + path.AppendLiteral("\\Software\\Microsoft\\Internet Account Manager"); + rv = key->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, + path, + nsIWindowsRegKey::ACCESS_QUERY_VALUE); + if (NS_SUCCEEDED(rv)) + key->ReadStringValue(NS_LITERAL_STRING("Default Mail Account"), aMailAccount); + } + + if (!aMailAccount.IsEmpty()) + return NS_OK; + + // else it must be here in original install location from orig code + rv = key->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, + NS_LITERAL_STRING("Software\\Microsoft\\Outlook Express"), + nsIWindowsRegKey::ACCESS_QUERY_VALUE); + if (NS_FAILED(rv)) + return rv; + + return key->ReadStringValue(NS_LITERAL_STRING("Default Mail Account"), aMailAccount); +} + +nsresult OESettings::GetCheckMailInterval(uint32_t *aInterval) +{ + nsCOMPtr<nsIWindowsRegKey> key; + // 'poll for messages' setting in OE is a global setting + // in OE options general tab and in following global OE + // registry location. + // for all accounts poll interval is a 32 bit value, 0 for + // "don't poll", else milliseconds + nsresult rv = Find50Key(getter_AddRefs(key)); + if (NS_FAILED(rv)) + rv = Find40Key(getter_AddRefs(key)); + + if (NS_FAILED(rv)) + return rv; + + nsCOMPtr<nsIWindowsRegKey> subKey; + rv = key->OpenChild(NS_LITERAL_STRING("Mail"), + nsIWindowsRegKey::ACCESS_QUERY_VALUE, + getter_AddRefs(subKey)); + if (NS_FAILED(rv)) + return rv; + + uint32_t intValue; + rv = subKey->ReadIntValue(NS_LITERAL_STRING("Poll For Mail"), &intValue); + if (NS_SUCCEEDED(rv) && intValue != PR_UINT32_MAX) + *aInterval = intValue / 60000; + + return rv; +} + +nsresult OESettings::FindAccountsKey(nsIWindowsRegKey **aKey) +{ + nsAutoString userId; + nsresult rv = nsOERegUtil::GetDefaultUserId(userId); + if (NS_FAILED(rv)) + return rv; + + nsAutoString path(NS_LITERAL_STRING("Identities\\")); + path.Append(userId); + path.AppendLiteral("\\Software\\Microsoft\\Internet Account Manager\\Accounts"); + + nsCOMPtr<nsIWindowsRegKey> key = + do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = key->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, + path, + nsIWindowsRegKey::ACCESS_QUERY_VALUE | + nsIWindowsRegKey::ACCESS_ENUMERATE_SUB_KEYS); + if (NS_SUCCEEDED(rv)) { + NS_ADDREF(*aKey = key); + return rv; + } + + rv = key->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, + NS_LITERAL_STRING("Software\\Microsoft\\Internet Account Manager\\Accounts"), + nsIWindowsRegKey::ACCESS_QUERY_VALUE | + nsIWindowsRegKey::ACCESS_ENUMERATE_SUB_KEYS); + NS_IF_ADDREF(*aKey = key); + return rv; +} + +nsresult OESettings::Find50Key(nsIWindowsRegKey **aKey) +{ + nsresult rv; + nsCOMPtr<nsIWindowsRegKey> key = + do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoString userId; + rv = nsOERegUtil::GetDefaultUserId(userId); + if (NS_FAILED(rv)) + return rv; + + nsAutoString path(NS_LITERAL_STRING("Identities\\")); + path.Append(userId); + path.AppendLiteral("\\Software\\Microsoft\\Outlook Express\\5.0"); + rv = key->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, + path, + nsIWindowsRegKey::ACCESS_QUERY_VALUE); + NS_IF_ADDREF(*aKey = key); + + return rv; +} + +nsresult OESettings::Find40Key(nsIWindowsRegKey **aKey) +{ + nsresult rv; + nsCOMPtr<nsIWindowsRegKey> key = + do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = key->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, + NS_LITERAL_STRING("Software\\Microsoft\\Outlook Express"), + nsIWindowsRegKey::ACCESS_QUERY_VALUE); + NS_IF_ADDREF(*aKey = key); + + return rv; +} + +bool OESettings::DoImport(nsIMsgAccount **aAccount) +{ + nsCOMPtr<nsIWindowsRegKey> key; + nsresult rv = FindAccountsKey(getter_AddRefs(key)); + if (NS_FAILED(rv)) { + IMPORT_LOG0( "*** Error finding Outlook Express registry account keys\n"); + return false; + } + + nsCOMPtr<nsIMsgAccountManager> accMgr = + do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv); + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Failed to create a account manager!\n"); + return false; + } + + nsAutoString defMailName; + rv = GetDefaultMailAccount(defMailName); + + checkNewMail = false; + checkNewMailTime = 30; + rv = GetCheckMailInterval(&checkNewMailTime); + if (NS_SUCCEEDED(rv)) + checkNewMail = true; + + // Iterate the accounts looking for POP3 & IMAP accounts... + // Ignore LDAP for now! + uint32_t accounts = 0; + nsAutoString keyComp; + uint32_t childCount = 0; + key->GetChildCount(&childCount); + for (uint32_t i = 0; i < childCount; i++) { + nsAutoString keyName; + key->GetChildName(i, keyName); + + nsCOMPtr<nsIWindowsRegKey> subKey; + rv = key->OpenChild(keyName, + nsIWindowsRegKey::ACCESS_QUERY_VALUE, + getter_AddRefs(subKey)); + if (NS_FAILED(rv)) + continue; + + nsAutoCString nativeKeyName; + NS_CopyUnicodeToNative(keyName, nativeKeyName); + IMPORT_LOG1("Opened Outlook Express account: %s\n", + nativeKeyName.get()); + + nsIMsgAccount *anAccount = nullptr; + nsAutoString value; + rv = subKey->ReadStringValue(NS_LITERAL_STRING("IMAP Server"), value); + if (NS_SUCCEEDED(rv) && DoIMAPServer(accMgr, subKey, value, &anAccount)) + accounts++; + + rv = subKey->ReadStringValue(NS_LITERAL_STRING("NNTP Server"), value); + if (NS_SUCCEEDED(rv) && DoNNTPServer(accMgr, subKey, value, &anAccount)) + accounts++; + + rv = subKey->ReadStringValue(NS_LITERAL_STRING("POP3 Server"), value); + if (NS_SUCCEEDED(rv) && DoPOP3Server(accMgr, subKey, value, &anAccount)) + accounts++; + + if (anAccount) { + // Is this the default account? + keyComp = keyName; + if (keyComp.Equals(defMailName)) + accMgr->SetDefaultAccount(anAccount); + NS_RELEASE(anAccount); + } + } + + // Now save the new acct info to pref file. + rv = accMgr->SaveAccountInfo(); + NS_ASSERTION(NS_SUCCEEDED(rv), "Can't save account info to pref file"); + + return accounts != 0; +} + +nsresult OESettings::GetAccountName(nsIWindowsRegKey *aKey, + const nsString &aDefaultName, + nsAString &aAccountName) +{ + nsresult rv; + rv = aKey->ReadStringValue(NS_LITERAL_STRING("Account Name"), aAccountName); + if (NS_FAILED(rv)) + aAccountName.Assign(aDefaultName); + + return NS_OK; +} + +bool OESettings::DoIMAPServer(nsIMsgAccountManager *aMgr, + nsIWindowsRegKey *aKey, + const nsString &aServerName, + nsIMsgAccount **ppAccount) +{ + if (ppAccount) + *ppAccount = nullptr; + + nsAutoString userName; + nsresult rv; + rv = aKey->ReadStringValue(NS_LITERAL_STRING("IMAP User Name"), userName); + if (NS_FAILED(rv)) + return false; + + nsAutoCString nativeUserName; + NS_CopyUnicodeToNative(userName, nativeUserName); + nsAutoCString nativeServerName; + NS_CopyUnicodeToNative(aServerName, nativeServerName); + // I now have a user name/server name pair, find out if it already exists? + nsCOMPtr<nsIMsgIncomingServer> in; + rv = aMgr->FindServer(nativeUserName, + nativeServerName, + NS_LITERAL_CSTRING("imap"), + getter_AddRefs(in)); + if (NS_SUCCEEDED(rv)) { + // for an existing server we create another identity, + // TB lists under 'manage identities' + nsCOMPtr<nsIMsgAccount> account; + rv = aMgr->FindAccountForServer(in, getter_AddRefs(account)); + if (NS_FAILED(rv)) + return false; + + IMPORT_LOG0("Created an identity and added to existing IMAP incoming server\n"); + // Fiddle with the identities + int32_t authMethod; + in->GetAuthMethod(&authMethod); + SetIdentities(aMgr, account, aKey, userName, authMethod, false); + if (ppAccount) + account->QueryInterface(NS_GET_IID(nsIMsgAccount), + (void **)ppAccount); + return true; + } + + // Create the incoming server and an account for it? + rv = aMgr->CreateIncomingServer(nativeUserName, + nativeServerName, + NS_LITERAL_CSTRING("imap"), + getter_AddRefs(in)); + NS_ENSURE_SUCCESS(rv, false); + + nsAutoString rootFolder; + rv = aKey->ReadStringValue(NS_LITERAL_STRING("IMAP Root Folder"), rootFolder); + if (NS_SUCCEEDED(rv) && !rootFolder.IsEmpty()) { + nsCOMPtr<nsIImapIncomingServer> imapServer = do_QueryInterface(in); + nsAutoCString nativeRootFolder; + NS_CopyUnicodeToNative(rootFolder, nativeRootFolder); + imapServer->SetServerDirectory(nativeRootFolder); + } + + SetIncomingServerProperties(in, aKey, NS_LITERAL_STRING("IMAP ")); + + IMPORT_LOG2("Created IMAP server named: %s, userName: %s\n", + nativeServerName.get(), nativeUserName.get()); + + nsAutoString prettyName; + if (NS_SUCCEEDED(GetAccountName(aKey, aServerName, prettyName))) + rv = in->SetPrettyName(prettyName); + + // We have a server, create an account. + nsCOMPtr<nsIMsgAccount> account; + rv = aMgr->CreateAccount(getter_AddRefs(account)); + if (NS_SUCCEEDED(rv) && account) { + rv = account->SetIncomingServer(in); + + IMPORT_LOG0("Created an account and set the IMAP server as the incoming server\n"); + + // Fiddle with the identities + int32_t authMethod; + in->GetAuthMethod(&authMethod); + SetIdentities(aMgr, account, aKey, userName, authMethod, false); + if (ppAccount) + account->QueryInterface(NS_GET_IID(nsIMsgAccount), (void **)ppAccount); + return true; + } + + return false; +} + +bool OESettings::DoPOP3Server(nsIMsgAccountManager *aMgr, + nsIWindowsRegKey *aKey, + const nsString &aServerName, + nsIMsgAccount **ppAccount) +{ + if (ppAccount) + *ppAccount = nullptr; + + nsAutoString userName; + nsresult rv; + rv = aKey->ReadStringValue(NS_LITERAL_STRING("POP3 User Name"), userName); + if (NS_FAILED(rv)) + return false; + + nsAutoCString nativeUserName; + NS_CopyUnicodeToNative(userName, nativeUserName); + nsAutoCString nativeServerName; + NS_CopyUnicodeToNative(aServerName, nativeServerName); + + // I now have a user name/server name pair, find out if it already exists? + nsCOMPtr<nsIMsgIncomingServer> in; + rv = aMgr->FindServer(nativeUserName, + nativeServerName, + NS_LITERAL_CSTRING("pop3"), + getter_AddRefs(in)); + if (NS_SUCCEEDED(rv)) { + IMPORT_LOG2("Existing POP3 server named: %s, userName: %s\n", + nativeUserName.get(), nativeServerName.get()); + // for an existing server we create another identity, + // TB listed under 'manage identities' + nsCOMPtr<nsIMsgAccount> account; + rv = aMgr->FindAccountForServer(in, getter_AddRefs(account)); + if (NS_SUCCEEDED(rv) && account) { + IMPORT_LOG0("Created identity and added to existing POP3 incoming server.\n"); + // Fiddle with the identities + int32_t authMethod; + in->GetAuthMethod(&authMethod); + SetIdentities(aMgr, account, aKey, userName, authMethod, false); + if (ppAccount) + account->QueryInterface(NS_GET_IID(nsIMsgAccount), (void **)ppAccount); + return true; + } + return false; + } + + // Create the incoming server and an account for it? + rv = aMgr->CreateIncomingServer(nativeUserName, + nativeServerName, + NS_LITERAL_CSTRING("pop3"), + getter_AddRefs( in)); + if (NS_FAILED(rv)) + return false; + + SetIncomingServerProperties(in, aKey, NS_LITERAL_STRING("POP3 ")); + + nsCOMPtr<nsIPop3IncomingServer> pop3Server = do_QueryInterface(in); + if (pop3Server) { + // set local folders as the Inbox to use for this POP3 server + nsCOMPtr<nsIMsgIncomingServer> localFoldersServer; + aMgr->GetLocalFoldersServer(getter_AddRefs(localFoldersServer)); + + if (!localFoldersServer) + { + // If Local Folders does not exist already, create it + + if (NS_FAILED(aMgr->CreateLocalMailAccount())) { + IMPORT_LOG0("*** Failed to create Local Folders!\n"); + return false; + } + + aMgr->GetLocalFoldersServer(getter_AddRefs(localFoldersServer)); + } + + // now get the account for this server + nsCOMPtr<nsIMsgAccount> localFoldersAccount; + aMgr->FindAccountForServer(localFoldersServer, getter_AddRefs(localFoldersAccount)); + if (localFoldersAccount) + { + nsCString localFoldersAcctKey; + localFoldersAccount->GetKey(localFoldersAcctKey); + pop3Server->SetDeferredToAccount(localFoldersAcctKey); + } + + uint32_t intValue; + rv = aKey->ReadIntValue(NS_LITERAL_STRING("POP3 Skip Account"), &intValue); + // OE:0=='Include this account when receiving mail or synchronizing'== + // TB:1==AM:Server:advanced:Include this server when getting new mail + pop3Server->SetDeferGetNewMail(NS_SUCCEEDED(rv) && intValue == 0); + + rv = aKey->ReadIntValue(NS_LITERAL_STRING("Leave Mail On Server"), + &intValue); + pop3Server->SetLeaveMessagesOnServer(NS_SUCCEEDED(rv) && intValue == 1); + + rv = aKey->ReadIntValue(NS_LITERAL_STRING("Remove When Deleted"), + &intValue); + pop3Server->SetDeleteMailLeftOnServer(NS_SUCCEEDED(rv) && intValue == 1); + + rv = aKey->ReadIntValue(NS_LITERAL_STRING("Remove When Expired"), + &intValue); + pop3Server->SetDeleteByAgeFromServer(NS_SUCCEEDED(rv) && intValue == 1); + + rv = aKey->ReadIntValue(NS_LITERAL_STRING("Expire Days"), + &intValue); + if (NS_SUCCEEDED(rv)) + pop3Server->SetNumDaysToLeaveOnServer(static_cast<int32_t>(intValue)); + } + IMPORT_LOG2("Created POP3 server named: %s, userName: %s\n", + nativeServerName.get(), nativeUserName.get()); + nsString prettyName; + if (NS_SUCCEEDED(GetAccountName(aKey, aServerName, prettyName))) + rv = in->SetPrettyName(prettyName); + + // We have a server, create an account. + nsCOMPtr<nsIMsgAccount> account; + rv = aMgr->CreateAccount(getter_AddRefs( account)); + if (NS_SUCCEEDED( rv) && account) { + rv = account->SetIncomingServer(in); + IMPORT_LOG0("Created a new account and set the incoming server to the POP3 server.\n"); + + int32_t authMethod; + in->GetAuthMethod(&authMethod); + // Fiddle with the identities + SetIdentities(aMgr, account, aKey, userName, authMethod, false); + if (ppAccount) + account->QueryInterface(NS_GET_IID(nsIMsgAccount), + (void **)ppAccount); + return true; + } + + return false; +} + +bool OESettings::DoNNTPServer(nsIMsgAccountManager *aMgr, + nsIWindowsRegKey *aKey, + const nsString &aServerName, + nsIMsgAccount **ppAccount) +{ + if (ppAccount) + *ppAccount = nullptr; + + nsAutoString userName; + nsresult rv; + // this only exists if NNTP server requires it or not anon login + rv = aKey->ReadStringValue(NS_LITERAL_STRING("NNTP User Name"), userName); + + bool result = false; + + nsAutoCString nativeServerName; + NS_CopyUnicodeToNative(aServerName, nativeServerName); + // I now have a user name/server name pair, find out if it already exists? + // NNTP can have empty user name. This is wild card in findserver + nsCOMPtr<nsIMsgIncomingServer> in; + rv = aMgr->FindServer(EmptyCString(), + nativeServerName, + NS_LITERAL_CSTRING("nntp"), + getter_AddRefs(in)); + if (NS_FAILED(rv) || (in == nullptr)) { + // Create the incoming server and an account for it? + rv = aMgr->CreateIncomingServer(EmptyCString(), + nativeServerName, + NS_LITERAL_CSTRING("nntp"), + getter_AddRefs(in)); + if (NS_SUCCEEDED(rv) && in) { + uint32_t port = 0; + rv = aKey->ReadIntValue(NS_LITERAL_STRING("NNTP Port"), + &port); + if (NS_SUCCEEDED(rv) && port && port != 119) + in->SetPort(static_cast<int32_t>(port)); + + nsAutoCString nativeUserName; + NS_CopyUnicodeToNative(userName, nativeUserName); + // do nntpincomingserver stuff + nsCOMPtr<nsINntpIncomingServer> nntpServer = do_QueryInterface(in); + if (nntpServer && !userName.IsEmpty()) { + nntpServer->SetPushAuth(true); + in->SetUsername(nativeUserName); + } + + IMPORT_LOG2("Created NNTP server named: %s, userName: %s\n", + nativeServerName.get(), nativeUserName.get()); + + nsString prettyName; + if (NS_SUCCEEDED(GetAccountName(aKey, aServerName, prettyName))) + rv = in->SetPrettyName(prettyName); + + // We have a server, create an account. + nsCOMPtr<nsIMsgAccount> account; + rv = aMgr->CreateAccount(getter_AddRefs(account)); + if (NS_SUCCEEDED(rv) && account) { + rv = account->SetIncomingServer(in); + + IMPORT_LOG0("Created an account and set the NNTP server as the incoming server\n"); + + // Fiddle with the identities + SetIdentities(aMgr, account, aKey, userName, 0, true); + result = true; + if (ppAccount) + account->QueryInterface(NS_GET_IID(nsIMsgAccount), (void **)ppAccount); + } + } + } + else if (NS_SUCCEEDED(rv) && in) { + // for the existing server... + nsCOMPtr<nsIMsgAccount> account; + rv = aMgr->FindAccountForServer(in, getter_AddRefs(account)); + if (NS_SUCCEEDED(rv) && account) { + IMPORT_LOG0("Using existing account and set the NNTP server as the incoming server\n"); + // Fiddle with the identities + SetIdentities(aMgr, account, aKey, userName, 0, true); + if (ppAccount) + account->QueryInterface(NS_GET_IID(nsIMsgAccount), + (void **)ppAccount); + return true; + } + } + else + result = true; + + return result; +} + +void OESettings::SetIncomingServerProperties(nsIMsgIncomingServer *aServer, + nsIWindowsRegKey *aKey, + const nsString &aKeyNamePrefix) +{ + nsresult rv; + uint32_t secureConnection = 0; + nsString keyName(aKeyNamePrefix); + keyName.AppendLiteral("Secure Connection"); + rv = aKey->ReadIntValue(keyName, &secureConnection); + if (NS_SUCCEEDED(rv) && secureConnection == 1) + aServer->SetSocketType(nsMsgSocketType::SSL); + + uint32_t port = 0; + keyName.SetLength(aKeyNamePrefix.Length()); + keyName.AppendLiteral("Port"); + rv = aKey->ReadIntValue(keyName, &port); + if (NS_SUCCEEDED(rv) && port) + aServer->SetPort(static_cast<int32_t>(port)); + + int32_t authMethod; + uint32_t useSicily = 0; + keyName.SetLength(aKeyNamePrefix.Length()); + keyName.AppendLiteral("Use Sicily"); + rv = aKey->ReadIntValue(keyName, &useSicily); + if (NS_SUCCEEDED(rv) && useSicily) + authMethod = nsMsgAuthMethod::secure; + else + authMethod = nsMsgAuthMethod::passwordCleartext; + aServer->SetAuthMethod(authMethod); + + aServer->SetDoBiff(checkNewMail); + aServer->SetBiffMinutes(checkNewMailTime); +} + +void OESettings::SetIdentities(nsIMsgAccountManager *aMgr, + nsIMsgAccount *pAcc, + nsIWindowsRegKey *aKey, + const nsString &aIncomgUserName, + int32_t authMethodIncoming, + bool isNNTP) +{ + // Get the relevant information for an identity + nsresult rv; + nsAutoString name; + rv = aKey->ReadStringValue(isNNTP ? + NS_LITERAL_STRING("NNTP Display Name") : + NS_LITERAL_STRING("SMTP Display Name"), + name); + nsAutoString email; + rv = aKey->ReadStringValue(isNNTP ? + NS_LITERAL_STRING("NNTP Email Address") : + NS_LITERAL_STRING("SMTP Email Address"), + email); + nsAutoString reply; + rv = aKey->ReadStringValue(isNNTP ? + NS_LITERAL_STRING("NNTP Reply To Email Address") : + NS_LITERAL_STRING("SMTP Reply To Email Address"), + reply); + nsAutoString orgName; + rv = aKey->ReadStringValue(isNNTP ? + NS_LITERAL_STRING("NNTP Organization Name") : + NS_LITERAL_STRING("SMTP Organization Name"), + orgName); + + nsCOMPtr<nsIMsgIdentity> id; + rv = aMgr->CreateIdentity(getter_AddRefs(id)); + if (NS_FAILED(rv)) + return; + + id->SetFullName(name); + id->SetOrganization(orgName); + + nsAutoCString nativeEmail; + NS_CopyUnicodeToNative(email, nativeEmail); + id->SetEmail(nativeEmail); + if (!reply.IsEmpty()) { + nsAutoCString nativeReply; + NS_CopyUnicodeToNative(reply, nativeReply); + id->SetReplyTo(nativeReply); + } + + // Outlook Express users are used to top style quoting. + id->SetReplyOnTop(isNNTP ? 0 : 1); + pAcc->AddIdentity(id); + + nsAutoCString nativeName; + NS_CopyUnicodeToNative(name, nativeName); + IMPORT_LOG0("Created identity and added to the account\n"); + IMPORT_LOG1("\tname: %s\n", nativeName.get()); + IMPORT_LOG1("\temail: %s\n", nativeEmail.get()); + + if (isNNTP) // NNTP does not use SMTP in OE or TB + return; + nsAutoString smtpServer; + rv = aKey->ReadStringValue(NS_LITERAL_STRING("SMTP Server"), smtpServer); + SetSmtpServer(smtpServer, aKey, id, aIncomgUserName, authMethodIncoming); +} + +void OESettings::SetSmtpServer(const nsString &aSmtpServer, + nsIWindowsRegKey *aKey, + nsIMsgIdentity *aId, + const nsString &aIncomgUserName, + int32_t authMethodIncoming) +{ + // set the id.smtpserver accordingly + // first we have to calculate the smtp user name which is based on sicily + if (!aKey || !aId || aIncomgUserName.IsEmpty() || aSmtpServer.IsEmpty()) + return; + nsCString smtpServerKey; + // smtp user name depends on sicily which may or not exist + uint32_t useSicily = 0; + nsresult rv = aKey->ReadIntValue(NS_LITERAL_STRING("SMTP Use Sicily"), + &useSicily); + nsAutoString userName; + switch (useSicily) { + case 1: + case 3: + // has to go in whether empty or no + // shouldn't be empty but better safe than sorry + aKey->ReadStringValue(NS_LITERAL_STRING("SMTP User Name"), userName); + break; + case 2: + userName = aIncomgUserName; + break; + default: + break; // initial userName == "" + } + + nsCOMPtr<nsISmtpService> smtpService(do_GetService(NS_SMTPSERVICE_CONTRACTID, &rv)); + if (NS_SUCCEEDED(rv) && smtpService) { + nsCOMPtr<nsISmtpServer> foundServer; + // don't try to make another server + // regardless if username doesn't match + nsAutoCString nativeUserName; + NS_CopyUnicodeToNative(userName, nativeUserName); + nsAutoCString nativeSmtpServer; + NS_CopyUnicodeToNative(aSmtpServer, nativeSmtpServer); + rv = smtpService->FindServer(nativeUserName.get(), + nativeSmtpServer.get(), + getter_AddRefs(foundServer)); + if (NS_SUCCEEDED(rv) && foundServer) { + // set our account keyed to this smptserver key + foundServer->GetKey(getter_Copies(smtpServerKey)); + aId->SetSmtpServerKey(smtpServerKey); + + IMPORT_LOG1("SMTP server already exists: %s\n", + nativeSmtpServer.get()); + } + else { + nsCOMPtr<nsISmtpServer> smtpServer; + rv = smtpService->CreateServer(getter_AddRefs(smtpServer)); + if (NS_SUCCEEDED(rv) && smtpServer) { + uint32_t port = 0; + rv = aKey->ReadIntValue(NS_LITERAL_STRING("SMTP Port"), + &port); + if (NS_SUCCEEDED(rv) && port) + smtpServer->SetPort(static_cast<int32_t>(port)); + + int32_t socketType = nsMsgSocketType::plain; + uint32_t secureConnection = 0; + rv = aKey->ReadIntValue(NS_LITERAL_STRING("SMTP Secure Connection"), + &secureConnection); + if (NS_SUCCEEDED(rv) && secureConnection == 1) { + // Outlook Express does not support STARTTLS without KB933612 fix. + if (IsKB933612Applied() && port != 465) + socketType = nsMsgSocketType::alwaysSTARTTLS; + else + socketType = nsMsgSocketType::SSL; + } + smtpServer->SetSocketType(socketType); + smtpServer->SetUsername(nativeUserName); + switch (useSicily) { + case 1 : + smtpServer->SetAuthMethod(nsMsgAuthMethod::secure); + break; + case 2 : // requires SMTP authentication to use the incoming server settings + smtpServer->SetAuthMethod(authMethodIncoming); + break; + case 3 : + smtpServer->SetAuthMethod(nsMsgAuthMethod::passwordCleartext); + break; + default: + smtpServer->SetAuthMethod(nsMsgAuthMethod::none); + } + + smtpServer->SetHostname(nativeSmtpServer); + + smtpServer->GetKey(getter_Copies(smtpServerKey)); + aId->SetSmtpServerKey(smtpServerKey); + + IMPORT_LOG1("Created new SMTP server: %s\n", + nativeSmtpServer.get()); + } + } + } +} + +bool OESettings::IsKB933612Applied() +{ + // The following versions of Windows include KB933612 fix: + // - Windows 7 and future versions of Windows + // - Windows Vista, SP1 or later + // - Windows Server 2003, SP2 or later + // - Windows XP, SP3 or later + // + // The following versions do not: + // - Windows Vista SP0 + // - Windows Server 2003, SP1 or earlier + // - Windows XP, SP2 or earlier + // + // See http://support.microsoft.com/kb/929123 and + // http://support.microsoft.com/kb/933612 + // + // Note that mozilla::IsWin2003SP2OrLater() will return true for + // Windows Vista and mozilla::IsXPSP3OrLater() will return true + // for Windows Server 2003. + return mozilla::IsVistaSP1OrLater() || + !mozilla::IsWin2003OrLater() && mozilla::IsXPSP3OrLater() || + !mozilla::IsVistaOrLater() && mozilla::IsWin2003SP2OrLater(); +} + diff --git a/mailnews/import/oexpress/nsOESettings.h b/mailnews/import/oexpress/nsOESettings.h new file mode 100644 index 000000000..8d2525423 --- /dev/null +++ b/mailnews/import/oexpress/nsOESettings.h @@ -0,0 +1,22 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsOESettings_h___ +#define nsOESettings_h___ + +#include "nsIImportSettings.h" + +class nsOESettings : public nsIImportSettings { +public: + nsOESettings(); + static nsresult Create(nsIImportSettings** aImport); + NS_DECL_ISUPPORTS + NS_DECL_NSIIMPORTSETTINGS + +private: + virtual ~nsOESettings(); +}; + +#endif /* nsOESettings_h___ */ diff --git a/mailnews/import/oexpress/nsOEStringBundle.cpp b/mailnews/import/oexpress/nsOEStringBundle.cpp new file mode 100644 index 000000000..3d3b4035a --- /dev/null +++ b/mailnews/import/oexpress/nsOEStringBundle.cpp @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "prprf.h" +#include "prmem.h" +#include "nsCOMPtr.h" +#include "nsMsgUtils.h" +#include "nsIStringBundle.h" +#include "nsOEStringBundle.h" +#include "nsIServiceManager.h" +#include "nsIURI.h" +#include "mozilla/Services.h" + +#define OE_MSGS_URL "chrome://messenger/locale/oeImportMsgs.properties" + +nsIStringBundle * nsOEStringBundle::m_pBundle = nullptr; + +nsIStringBundle *nsOEStringBundle::GetStringBundle(void) +{ + if (m_pBundle) + return m_pBundle; + + char* propertyURL = OE_MSGS_URL; + nsIStringBundle* sBundle = nullptr; + + nsCOMPtr<nsIStringBundleService> sBundleService = + mozilla::services::GetStringBundleService(); + if (sBundleService) { + sBundleService->CreateBundle(propertyURL, &sBundle); + } + + m_pBundle = sBundle; + + return sBundle; +} + + +void nsOEStringBundle::GetStringByID(int32_t stringID, nsString& result) +{ + char16_t *ptrv = GetStringByID(stringID); + result.Adopt(ptrv); +} + +char16_t *nsOEStringBundle::GetStringByID(int32_t stringID) +{ + if (!m_pBundle) + m_pBundle = GetStringBundle(); + + if (m_pBundle) { + char16_t *ptrv = nullptr; + nsresult rv = m_pBundle->GetStringFromID(stringID, &ptrv); + + if (NS_SUCCEEDED(rv) && ptrv) + return ptrv; + } + + nsString resultString; + resultString.AppendLiteral("[StringID "); + resultString.AppendInt(stringID); + resultString.AppendLiteral("?]"); + + return ToNewUnicode(resultString); +} + +void nsOEStringBundle::Cleanup(void) +{ + if (m_pBundle) + m_pBundle->Release(); + m_pBundle = nullptr; +} diff --git a/mailnews/import/oexpress/nsOEStringBundle.h b/mailnews/import/oexpress/nsOEStringBundle.h new file mode 100644 index 000000000..36829e5e3 --- /dev/null +++ b/mailnews/import/oexpress/nsOEStringBundle.h @@ -0,0 +1,38 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _nsOEStringBundle_H__ +#define _nsOEStringBundle_H__ + +#include "nsStringGlue.h" + +class nsIStringBundle; + +class nsOEStringBundle { +public: + static char16_t * GetStringByID(int32_t stringID); + static void GetStringByID(int32_t stringID, nsString& result); + static nsIStringBundle * GetStringBundle(void); // don't release + static void FreeString(char16_t *pStr) { NS_Free(pStr);} + static void Cleanup(void); + +private: + static nsIStringBundle * m_pBundle; +}; + + + +#define OEIMPORT_NAME 2000 +#define OEIMPORT_DESCRIPTION 2011 +#define OEIMPORT_MAILBOX_SUCCESS 2002 +#define OEIMPORT_MAILBOX_BADPARAM 2003 +#define OEIMPORT_MAILBOX_BADSOURCEFILE 2004 +#define OEIMPORT_MAILBOX_CONVERTERROR 2005 +#define OEIMPORT_DEFAULT_NAME 2006 +#define OEIMPORT_AUTOFIND 2007 +#define OEIMPORT_ADDRESS_SUCCESS 2008 +#define OEIMPORT_ADDRESS_CONVERTERROR 2009 +#define OEIMPORT_ADDRESS_BADPARAM 2010 + +#endif /* _nsOEStringBundle_H__ */ |