diff options
Diffstat (limited to 'mailnews/import/becky/src/nsBeckyMail.cpp')
-rw-r--r-- | mailnews/import/becky/src/nsBeckyMail.cpp | 641 |
1 files changed, 641 insertions, 0 deletions
diff --git a/mailnews/import/becky/src/nsBeckyMail.cpp b/mailnews/import/becky/src/nsBeckyMail.cpp new file mode 100644 index 000000000..9c837d190 --- /dev/null +++ b/mailnews/import/becky/src/nsBeckyMail.cpp @@ -0,0 +1,641 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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 "nsString.h" +#include "nsCOMPtr.h" +#include "nsIFile.h" +#include "nsIInputStream.h" +#include "nsIOutputStream.h" +#include "nsILineInputStream.h" +#include "nsNetUtil.h" +#include "nsIArray.h" +#include "nsIImportService.h" +#include "nsIImportMailboxDescriptor.h" +#include "nsIMsgHdr.h" +#include "nsIMsgFolder.h" +#include "nsIMsgPluggableStore.h" +#include "nsIMutableArray.h" +#include "nsMsgUtils.h" +#include "nsMsgLocalFolderHdrs.h" +#include "nsMsgMessageFlags.h" +#include "nsTArray.h" +#include "nspr.h" +#include "nsIStringBundle.h" +#include "nsThreadUtils.h" + +#include "nsBeckyMail.h" +#include "nsBeckyUtils.h" +#include "nsBeckyStringBundle.h" + +#define FROM_LINE "From - Mon Jan 1 00:00:00 1965" MSG_LINEBREAK +#define X_BECKY_STATUS_HEADER "X-Becky-Status" +#define X_BECKY_INCLUDE_HEADER "X-Becky-Include" + +enum { + BECKY_STATUS_READ = 1 << 0, + BECKY_STATUS_FORWARDED = 1 << 1, + BECKY_STATUS_REPLIED = 1 << 2 +}; + +NS_IMPL_ISUPPORTS(nsBeckyMail, nsIImportMail) + +nsresult +nsBeckyMail::Create(nsIImportMail **aImport) +{ + NS_ENSURE_ARG_POINTER(aImport); + + *aImport = new nsBeckyMail(); + + NS_ADDREF(*aImport); + return NS_OK; +} + +nsBeckyMail::nsBeckyMail() +: mReadBytes(0) +{ +} + +nsBeckyMail::~nsBeckyMail() +{ +} + +NS_IMETHODIMP +nsBeckyMail::GetDefaultLocation(nsIFile **aLocation, + bool *aFound, + bool *aUserVerify) +{ + NS_ENSURE_ARG_POINTER(aFound); + NS_ENSURE_ARG_POINTER(aLocation); + NS_ENSURE_ARG_POINTER(aUserVerify); + + *aLocation = nullptr; + *aUserVerify = true; + *aFound = false; + if (NS_SUCCEEDED(nsBeckyUtils::GetDefaultMailboxDirectory(aLocation))) + *aFound = true; + + return NS_OK; +} + +nsresult +nsBeckyMail::CreateMailboxDescriptor(nsIImportMailboxDescriptor **aDescriptor) +{ + nsresult rv; + nsCOMPtr<nsIImportService> importService; + importService = do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + return importService->CreateNewMailboxDescriptor(aDescriptor); +} + +nsresult +nsBeckyMail::GetMailboxName(nsIFile *aMailbox, nsAString &aName) +{ + nsCOMPtr<nsIFile> iniFile; + nsBeckyUtils::GetMailboxINIFile(aMailbox, getter_AddRefs(iniFile)); + if (iniFile) { + nsCOMPtr<nsIFile> convertedFile; + nsBeckyUtils::ConvertToUTF8File(iniFile, getter_AddRefs(convertedFile)); + if (convertedFile) { + nsAutoCString utf8Name; + nsBeckyUtils::GetMailboxNameFromINIFile(convertedFile, utf8Name); + convertedFile->Remove(false); + CopyUTF8toUTF16(utf8Name, aName); + } + } + + if (aName.IsEmpty()) { + nsAutoString name; + aMailbox->GetLeafName(name); + name.Trim("!", true, false); + aName.Assign(name); + } + + return NS_OK; +} + +nsresult +nsBeckyMail::AppendMailboxDescriptor(nsIFile *aEntry, + const nsString &aName, + uint32_t aDepth, + nsIMutableArray *aCollected) +{ + nsresult rv; + nsCOMPtr<nsIImportMailboxDescriptor> descriptor; + rv = CreateMailboxDescriptor(getter_AddRefs(descriptor)); + NS_ENSURE_SUCCESS(rv, rv); + + int64_t size; + rv = aEntry->GetFileSize(&size); + NS_ENSURE_SUCCESS(rv, rv); + + rv = descriptor->SetSize(size); + NS_ENSURE_SUCCESS(rv, rv); + + rv = descriptor->SetDisplayName(aName.get()); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIFile> mailboxFile; + rv = descriptor->GetFile(getter_AddRefs(mailboxFile)); + NS_ENSURE_SUCCESS(rv, rv); + + descriptor->SetDepth(aDepth); + + mailboxFile->InitWithFile(aEntry); + aCollected->AppendElement(descriptor, false); + + return NS_OK; +} + +nsresult +nsBeckyMail::CollectMailboxesInFolderListFile(nsIFile *aListFile, + uint32_t aDepth, + nsIMutableArray *aCollected) +{ + nsresult rv; + nsCOMPtr<nsILineInputStream> lineStream; + rv = nsBeckyUtils::CreateLineInputStream(aListFile, + getter_AddRefs(lineStream)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIFile> parent; + rv = aListFile->GetParent(getter_AddRefs(parent)); + NS_ENSURE_SUCCESS(rv, rv); + + bool more = true; + nsAutoCString folderName; + bool isEmpty = true; + while (more && NS_SUCCEEDED(rv)) { + rv = lineStream->ReadLine(folderName, &more); + NS_ENSURE_SUCCESS(rv, rv); + + if (folderName.IsEmpty()) + continue; + + nsCOMPtr<nsIFile> folder; + rv = parent->Clone(getter_AddRefs(folder)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = folder->AppendNative(folderName); + NS_ENSURE_SUCCESS(rv, rv); + + isEmpty = false; + rv = CollectMailboxesInDirectory(folder, aDepth + 1, aCollected); + } + + return isEmpty ? NS_ERROR_FILE_NOT_FOUND : NS_OK; +} + +nsresult +nsBeckyMail::CollectMailboxesInDirectory(nsIFile *aDirectory, + uint32_t aDepth, + nsIMutableArray *aCollected) +{ + nsAutoString mailboxName; + nsresult rv = GetMailboxName(aDirectory, mailboxName); + NS_ENSURE_SUCCESS(rv, rv); + + if (aDepth != 0) + AppendMailboxDescriptor(aDirectory, mailboxName, aDepth, aCollected); + + nsCOMPtr<nsIFile> folderListFile; + rv = nsBeckyUtils::GetFolderListFile(aDirectory, getter_AddRefs(folderListFile)); + bool folderListExists = false; + + if (NS_SUCCEEDED(rv)) { + rv = CollectMailboxesInFolderListFile(folderListFile, aDepth, aCollected); + folderListExists = true; + } + + nsCOMPtr<nsISimpleEnumerator> entries; + rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries)); + NS_ENSURE_SUCCESS(rv, rv); + + bool more; + while (NS_SUCCEEDED(entries->HasMoreElements(&more)) && more) { + nsCOMPtr<nsISupports> entry; + rv = entries->GetNext(getter_AddRefs(entry)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIFile> file = do_QueryInterface(entry, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoString name; + rv = file->GetLeafName(name); + NS_ENSURE_SUCCESS(rv, rv); + + if (StringEndsWith(name, NS_LITERAL_STRING(".bmf"))) { + AppendMailboxDescriptor(file, mailboxName, aDepth, aCollected); + } + + // The Folder.lst file is not created if there is only one sub folder, + // so we need to find the sub folder by our hands. + // The folder name does not begin with # or ! maybe. Yes, maybe... + if (!folderListExists) { + if (StringBeginsWith(name, NS_LITERAL_STRING("#")) || + StringBeginsWith(name, NS_LITERAL_STRING("!"))) + continue; + + bool isDirectory = false; + rv = file->IsDirectory(&isDirectory); + if (isDirectory) { + CollectMailboxesInDirectory(file, aDepth + 1, aCollected); + continue; + } + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsBeckyMail::FindMailboxes(nsIFile *aLocation, nsIArray **_retval) +{ + NS_ENSURE_ARG_POINTER(aLocation); + NS_ENSURE_ARG_POINTER(_retval); + + nsresult rv; + nsCOMPtr<nsIMutableArray> array(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = CollectMailboxesInDirectory(aLocation, 0, array); + NS_ENSURE_SUCCESS(rv, rv); + + array.forget(_retval); + + return NS_OK; +} + +static nsresult +GetBeckyStatusValue(const nsCString &aHeader, nsACString &aValue) +{ + int32_t valueStartPosition; + + valueStartPosition = aHeader.FindChar(':'); + if (valueStartPosition < 0) + return NS_ERROR_UNEXPECTED; + + valueStartPosition++; + + int32_t commaPosition = aHeader.FindChar(',', valueStartPosition); + if (commaPosition < 0) + return NS_ERROR_UNEXPECTED; + + nsAutoCString value(Substring(aHeader, + valueStartPosition, + commaPosition - valueStartPosition)); + value.Trim(" \t"); + + aValue.Assign(value); + + return NS_OK; +} + +static nsresult +GetBeckyIncludeValue(const nsCString &aHeader, nsACString &aValue) +{ + int32_t valueStartPosition; + + valueStartPosition = aHeader.FindChar(':'); + if (valueStartPosition < 0) + return NS_ERROR_FAILURE; + + valueStartPosition++; + nsAutoCString value(Substring(aHeader, valueStartPosition)); + value.Trim(" \t"); + + aValue.Assign(value); + + return NS_OK; +} + +static bool +ConvertBeckyStatusToMozillaStatus(const nsCString &aHeader, + nsMsgMessageFlagType *aMozillaStatusFlag) +{ + nsresult rv; + nsAutoCString statusString; + rv = GetBeckyStatusValue(aHeader, statusString); + NS_ENSURE_SUCCESS(rv, false); + + nsresult errorCode; + uint32_t beckyStatusFlag = static_cast<uint32_t>(statusString.ToInteger(&errorCode, 16)); + if (NS_FAILED(errorCode)) + return false; + + if (beckyStatusFlag & BECKY_STATUS_READ) + *aMozillaStatusFlag |= nsMsgMessageFlags::Read; + if (beckyStatusFlag & BECKY_STATUS_FORWARDED) + *aMozillaStatusFlag |= nsMsgMessageFlags::Forwarded; + if (beckyStatusFlag & BECKY_STATUS_REPLIED) + *aMozillaStatusFlag |= nsMsgMessageFlags::Replied; + + return true; +} + +static inline bool +CheckHeaderKey(const nsCString &aHeader, const char *aKeyString) +{ + nsAutoCString key(StringHead(aHeader, aHeader.FindChar(':'))); + key.Trim(" \t"); + return key.Equals(aKeyString); +} + +static inline bool +IsBeckyStatusHeader(const nsCString &aHeader) +{ + return CheckHeaderKey(aHeader, X_BECKY_STATUS_HEADER); +} + +static inline bool +IsBeckyIncludeLine(const nsCString &aLine) +{ + return CheckHeaderKey(aLine, X_BECKY_INCLUDE_HEADER); +} + +static inline bool +IsEndOfHeaders(const nsCString &aLine) +{ + return aLine.IsEmpty(); +} + +static inline bool +IsEndOfMessage(const nsCString &aLine) +{ + return aLine.Equals("."); +} + +class ImportMessageRunnable: public mozilla::Runnable +{ +public: + ImportMessageRunnable(nsIFile *aMessageFile, + nsIMsgFolder *aFolder); + NS_DECL_NSIRUNNABLE +private: + nsresult WriteHeaders(nsCString &aHeaders, nsIOutputStream *aOutputStream); + nsresult HandleHeaderLine(const nsCString &aHeaderLine, nsACString &aHeaders); + nsresult GetAttachmentFile(nsIFile *aMailboxFile, + const nsCString &aHeader, + nsIFile **_retval); + nsresult WriteAttachmentFile(nsIFile *aMailboxFile, + const nsCString &aHeader, + nsIOutputStream *aOutputStream); + + nsCOMPtr<nsIFile> mMessageFile; + nsCOMPtr<nsIMsgFolder> mFolder; +}; + +ImportMessageRunnable::ImportMessageRunnable(nsIFile *aMessageFile, + nsIMsgFolder *aFolder) : + mMessageFile(aMessageFile), mFolder(aFolder) +{ +} + +nsresult +ImportMessageRunnable::WriteHeaders(nsCString &aHeaders, + nsIOutputStream *aOutputStream) +{ + nsresult rv; + uint32_t writtenBytes = 0; + + rv = aOutputStream->Write(FROM_LINE, strlen(FROM_LINE), &writtenBytes); + NS_ENSURE_SUCCESS(rv, rv); + rv = aOutputStream->Write(aHeaders.get(), aHeaders.Length(), &writtenBytes); + NS_ENSURE_SUCCESS(rv, rv); + rv = aOutputStream->Write(MSG_LINEBREAK, strlen(MSG_LINEBREAK), &writtenBytes); + NS_ENSURE_SUCCESS(rv, rv); + aHeaders.Truncate(); + + return NS_OK; +} + +nsresult +ImportMessageRunnable::HandleHeaderLine(const nsCString &aHeaderLine, + nsACString &aHeaders) +{ + aHeaders.Append(aHeaderLine); + aHeaders.AppendLiteral(MSG_LINEBREAK); + + nsMsgMessageFlagType flag = 0; + if (IsBeckyStatusHeader(aHeaderLine) && + ConvertBeckyStatusToMozillaStatus(aHeaderLine, &flag)) { + char *statusLine; + statusLine = PR_smprintf(X_MOZILLA_STATUS_FORMAT MSG_LINEBREAK, flag); + aHeaders.Append(statusLine); + PR_smprintf_free(statusLine); + aHeaders.AppendLiteral(X_MOZILLA_KEYWORDS); + } + + return NS_OK; +} + +nsresult +ImportMessageRunnable::GetAttachmentFile(nsIFile *aMailboxFile, + const nsCString &aHeader, + nsIFile **_retval) +{ + nsresult rv; + nsCOMPtr<nsIFile> attachmentFile; + + rv = aMailboxFile->Clone(getter_AddRefs(attachmentFile)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = attachmentFile->Append(NS_LITERAL_STRING("#Attach")); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoCString nativeAttachmentPath; + rv = GetBeckyIncludeValue(aHeader, nativeAttachmentPath); + NS_ENSURE_SUCCESS(rv, rv); + + rv = attachmentFile->AppendRelativeNativePath(nativeAttachmentPath); + NS_ENSURE_SUCCESS(rv, rv); + + bool exists = false; + attachmentFile->Exists(&exists); + if (!exists) + return NS_ERROR_FILE_NOT_FOUND; + + attachmentFile.forget(_retval); + return NS_OK; +} + +nsresult +ImportMessageRunnable::WriteAttachmentFile(nsIFile *aMailboxFile, + const nsCString &aHeader, + nsIOutputStream *aOutputStream) +{ + nsresult rv; + nsCOMPtr<nsIFile> parentDirectory; + rv = aMailboxFile->GetParent(getter_AddRefs(parentDirectory)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIFile> attachmentFile; + rv = GetAttachmentFile(parentDirectory, + aHeader, + getter_AddRefs(attachmentFile)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIInputStream> inputStream; + rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), + attachmentFile); + NS_ENSURE_SUCCESS(rv, rv); + + char buffer[FILE_IO_BUFFER_SIZE]; + uint32_t readBytes = 0; + uint32_t writtenBytes = 0; + rv = aOutputStream->Write(MSG_LINEBREAK, strlen(MSG_LINEBREAK), &writtenBytes); + while (NS_SUCCEEDED(inputStream->Read(buffer, sizeof(buffer), &readBytes)) && + readBytes > 0) { + rv = aOutputStream->Write(buffer, readBytes, &writtenBytes); + if (NS_FAILED(rv)) + break; + } + + return rv; +} + +NS_IMETHODIMP ImportMessageRunnable::Run() +{ + nsCOMPtr<nsIMsgPluggableStore> msgStore; + nsresult rv = mFolder->GetMsgStore(getter_AddRefs(msgStore)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsILineInputStream> lineStream; + rv = nsBeckyUtils::CreateLineInputStream(mMessageFile, + getter_AddRefs(lineStream)); + NS_ENSURE_SUCCESS(rv, rv); + + bool reusable; + nsCOMPtr<nsIMsgDBHdr> msgHdr; + nsCOMPtr<nsIOutputStream> outputStream; + rv = msgStore->GetNewMsgOutputStream(mFolder, getter_AddRefs(msgHdr), &reusable, + getter_AddRefs(outputStream)); + NS_ENSURE_SUCCESS(rv, rv); + + bool inHeader = true; + bool more = true; + nsAutoCString headers; + while (NS_SUCCEEDED(rv) && more) { + nsAutoCString line; + rv = lineStream->ReadLine(line, &more); + if (NS_FAILED(rv)) + break; + + if (inHeader) { + if (IsEndOfHeaders(line)) { + inHeader = false; + rv = WriteHeaders(headers, outputStream); + } else { + rv = HandleHeaderLine(line, headers); + } + } else if (IsEndOfMessage(line)) { + inHeader = true; + rv = msgStore->FinishNewMessage(outputStream, msgHdr); + if (!reusable) + outputStream->Close(); + rv = msgStore->GetNewMsgOutputStream(mFolder, getter_AddRefs(msgHdr), &reusable, + getter_AddRefs(outputStream)); + } else if (IsBeckyIncludeLine(line)) { + rv = WriteAttachmentFile(mMessageFile, line, outputStream); + } else { + uint32_t writtenBytes = 0; + if (StringBeginsWith(line, NS_LITERAL_CSTRING(".."))) + line.Cut(0, 1); + else if (CheckHeaderKey(line, "From")) + line.Insert('>', 0); + + line.AppendLiteral(MSG_LINEBREAK); + rv = outputStream->Write(line.get(), line.Length(), &writtenBytes); + } + } + + if (outputStream) { + if (NS_FAILED(rv)) + msgStore->DiscardNewMessage(outputStream, msgHdr); + outputStream->Close(); + } + + return rv; +} + +static +nsresult ProxyImportMessage(nsIFile *aMessageFile, + nsIMsgFolder *aFolder) +{ + RefPtr<ImportMessageRunnable> importMessage = + new ImportMessageRunnable(aMessageFile, aFolder); + return NS_DispatchToMainThread(importMessage, NS_DISPATCH_SYNC); +} + +nsresult +nsBeckyMail::ImportMailFile(nsIFile *aMailFile, + nsIMsgFolder *aDestination) +{ + int64_t size; + aMailFile->GetFileSize(&size); + if (size == 0) + return NS_OK; + + return ProxyImportMessage(aMailFile, aDestination); +} + +NS_IMETHODIMP +nsBeckyMail::ImportMailbox(nsIImportMailboxDescriptor *aSource, + nsIMsgFolder *aDestination, + char16_t **aErrorLog, + char16_t **aSuccessLog, + bool *aFatalError) +{ + NS_ENSURE_ARG_POINTER(aSource); + NS_ENSURE_ARG_POINTER(aDestination); + NS_ENSURE_ARG_POINTER(aErrorLog); + NS_ENSURE_ARG_POINTER(aSuccessLog); + NS_ENSURE_ARG_POINTER(aFatalError); + + mReadBytes = 0; + + nsresult rv; + nsCOMPtr<nsIFile> mailboxFolder; + rv = aSource->GetFile(getter_AddRefs(mailboxFolder)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = ImportMailFile(mailboxFolder, aDestination); + NS_ENSURE_SUCCESS(rv, rv); + + uint32_t finalSize; + aSource->GetSize(&finalSize); + mReadBytes = finalSize; + + nsAutoString name; + aSource->GetDisplayName(getter_Copies(name)); + + nsAutoString successMessage; + const char16_t *format = { name.get() }; + rv = + nsBeckyStringBundle::FormatStringFromName(u"BeckyImportMailboxSuccess", + &format, + 1, + getter_Copies(successMessage)); + successMessage.AppendLiteral("\n"); + *aSuccessLog = ToNewUnicode(successMessage); + + return rv; +} + +NS_IMETHODIMP +nsBeckyMail::GetImportProgress(uint32_t *_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + *_retval = mReadBytes; + return NS_OK; +} + +NS_IMETHODIMP +nsBeckyMail::TranslateFolderName(const nsAString & aFolderName, + nsAString & _retval) +{ + return nsBeckyUtils::TranslateFolderName(aFolderName, _retval); +} + |