/* -*- 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 #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 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 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 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 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 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 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 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 &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 directoryEnumerator; nsresult rv = srcDir->GetDirectoryEntries(getter_AddRefs(directoryEnumerator)); NS_ENSURE_SUCCESS(rv, false); directoryEnumerator->HasMoreElements(&hasMore); bool isFile; nsCOMPtr entry; nsCString fName; while (hasMore && NS_SUCCEEDED(rv)) { nsCOMPtr aSupport; rv = directoryEnumerator->GetNext(getter_AddRefs(aSupport)); nsCOMPtr 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 directoryEnumerator; nsresult rv = srcDir->GetDirectoryEntries(getter_AddRefs(directoryEnumerator)); NS_ENSURE_SUCCESS_VOID(rv); directoryEnumerator->HasMoreElements(&hasMore); bool isFile; nsCOMPtr entry; nsCString fName; nsCString ext; nsCString name; while (hasMore && NS_SUCCEEDED(rv)) { nsCOMPtr aSupport; rv = directoryEnumerator->GetNext(getter_AddRefs(aSupport)); nsCOMPtr 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 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 file; MailboxEntry *pChild; nsIImportMailboxDescriptor *pID; nsISupports *pInterface; int64_t size; nsCOMPtr 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 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 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 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 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; }