/* -*- 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/. */ /** Class for handling Maildir stores. */ #include "prprf.h" #include "msgCore.h" #include "nsMsgMaildirStore.h" #include "nsIMsgFolder.h" #include "nsIMsgFolderNotificationService.h" #include "nsISimpleEnumerator.h" #include "nsIDirectoryEnumerator.h" #include "nsIInputStream.h" #include "nsMsgFolderFlags.h" #include "nsCOMArray.h" #include "nsIFile.h" #include "nsNetUtil.h" #include "nsIMsgDatabase.h" #include "nsNativeCharsetUtils.h" #include "nsMsgUtils.h" #include "nsIDBFolderInfo.h" #include "nsMailHeaders.h" #include "nsParseMailbox.h" #include "nsIMsgLocalMailFolder.h" #include "nsITimer.h" #include "nsIMailboxUrl.h" #include "nsIMsgMailNewsUrl.h" #include "nsIMsgFilterPlugin.h" #include "nsLocalUndoTxn.h" #include "nsIMessenger.h" #include "mozilla/Logging.h" #include "mozilla/SlicedInputStream.h" #include "mozilla/UniquePtr.h" static mozilla::LazyLogModule MailDirLog("MailDirStore"); // Helper function to produce a safe filename from a Message-ID value. // We'll percent-encode anything not in this set: [-+.%=@_0-9a-zA-Z] // This is an overly-picky set, but should: // - leave most sane Message-IDs unchanged // - be safe on windows (the pickiest case) // - avoid chars that can trip up shell scripts (spaces, semicolons etc) // If input contains malicious binary (or multibyte chars) it'll be // safely encoded as individual bytes. static void percentEncode(nsACString const& in, nsACString& out) { const char* end = in.EndReading(); const char* cur; // We know the output will be at least as long as the input. out.SetLength(0); out.SetCapacity(in.Length()); for (cur = in.BeginReading(); cur < end; ++cur) { const char c = *cur; bool whitelisted = (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '-' || c == '+' || c == '.' || c == '%' || c == '=' || c == '@' || c == '_'; if (whitelisted) { out.Append(c); } else { out.AppendPrintf("%%%02x", (unsigned char)c); } } } nsMsgMaildirStore::nsMsgMaildirStore() {} nsMsgMaildirStore::~nsMsgMaildirStore() {} NS_IMPL_ISUPPORTS(nsMsgMaildirStore, nsIMsgPluggableStore) // Iterates over the folders in the "path" directory, and adds subfolders to // parent for each Maildir folder found. nsresult nsMsgMaildirStore::AddSubFolders(nsIMsgFolder* parent, nsIFile* path, bool deep) { nsCOMArray currentDirEntries; nsCOMPtr directoryEnumerator; nsresult rv = path->GetDirectoryEntries(getter_AddRefs(directoryEnumerator)); NS_ENSURE_SUCCESS(rv, rv); bool hasMore; while (NS_SUCCEEDED(directoryEnumerator->HasMoreElements(&hasMore)) && hasMore) { nsCOMPtr currentFile; rv = directoryEnumerator->GetNextFile(getter_AddRefs(currentFile)); if (NS_SUCCEEDED(rv) && currentFile) { nsAutoString leafName; currentFile->GetLeafName(leafName); bool isDirectory = false; currentFile->IsDirectory(&isDirectory); // Make sure this really is a mail folder dir (i.e., a directory that // contains cur and tmp sub-dirs, and not a .sbd or .mozmsgs dir). if (isDirectory && !nsShouldIgnoreFile(leafName, currentFile)) currentDirEntries.AppendObject(currentFile); } } // add the folders int32_t count = currentDirEntries.Count(); for (int32_t i = 0; i < count; ++i) { nsCOMPtr currentFile(currentDirEntries[i]); nsAutoString leafName; currentFile->GetLeafName(leafName); nsCOMPtr child; rv = parent->AddSubfolder(leafName, getter_AddRefs(child)); if (child) { nsString folderName; child->GetName(folderName); // try to get it from cache/db if (folderName.IsEmpty()) child->SetPrettyName(leafName); if (deep) { nsCOMPtr path; rv = child->GetFilePath(getter_AddRefs(path)); NS_ENSURE_SUCCESS(rv, rv); // Construct the .sbd directory path for the possible children of the // folder. GetDirectoryForFolder(path); bool directory = false; // Check that .sbd really is a directory. path->IsDirectory(&directory); if (directory) AddSubFolders(child, path, true); } } } return rv == NS_MSG_FOLDER_EXISTS ? NS_OK : rv; } NS_IMETHODIMP nsMsgMaildirStore::DiscoverSubFolders(nsIMsgFolder* aParentFolder, bool aDeep) { NS_ENSURE_ARG_POINTER(aParentFolder); nsCOMPtr path; nsresult rv = aParentFolder->GetFilePath(getter_AddRefs(path)); NS_ENSURE_SUCCESS(rv, rv); bool isServer, directory = false; aParentFolder->GetIsServer(&isServer); if (!isServer) GetDirectoryForFolder(path); path->IsDirectory(&directory); if (directory) rv = AddSubFolders(aParentFolder, path, aDeep); return (rv == NS_MSG_FOLDER_EXISTS) ? NS_OK : rv; } /** * Create if missing a Maildir-style folder with "tmp" and "cur" subfolders * but no "new" subfolder, because it doesn't make sense in the mail client * context. ("new" directory is for messages on the server that haven't been * seen by a mail client). * aFolderName is already "safe" - it has been through NS_MsgHashIfNecessary. */ nsresult nsMsgMaildirStore::CreateMaildir(nsIFile* path) { nsresult rv = path->Create(nsIFile::DIRECTORY_TYPE, 0700); if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) { NS_WARNING("Could not create root directory for message folder"); return rv; } // Create tmp, cur leaves nsCOMPtr leaf(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv)); NS_ENSURE_SUCCESS(rv, rv); leaf->InitWithFile(path); leaf->AppendNative("tmp"_ns); rv = leaf->Create(nsIFile::DIRECTORY_TYPE, 0700); if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) { NS_WARNING("Could not create tmp directory for message folder"); return rv; } leaf->SetNativeLeafName("cur"_ns); rv = leaf->Create(nsIFile::DIRECTORY_TYPE, 0700); if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) { NS_WARNING("Could not create cur directory for message folder"); return rv; } return NS_OK; } NS_IMETHODIMP nsMsgMaildirStore::CreateFolder(nsIMsgFolder* aParent, const nsAString& aFolderName, nsIMsgFolder** aResult) { NS_ENSURE_ARG_POINTER(aParent); NS_ENSURE_ARG_POINTER(aResult); if (aFolderName.IsEmpty()) return NS_MSG_ERROR_INVALID_FOLDER_NAME; nsCOMPtr path; nsresult rv = aParent->GetFilePath(getter_AddRefs(path)); NS_ENSURE_SUCCESS(rv, rv); // Get a directory based on our current path bool isServer; aParent->GetIsServer(&isServer); rv = CreateDirectoryForFolder(path, isServer); NS_ENSURE_SUCCESS(rv, rv); // Make sure the new folder name is valid nsAutoString safeFolderName(aFolderName); NS_MsgHashIfNecessary(safeFolderName); path->Append(safeFolderName); bool exists; path->Exists(&exists); if (exists) // check this because localized names are different from disk // names return NS_MSG_FOLDER_EXISTS; rv = CreateMaildir(path); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr child; // GetFlags and SetFlags in AddSubfolder will fail because we have no db at // this point but mFlags is set. rv = aParent->AddSubfolder(safeFolderName, getter_AddRefs(child)); if (!child || NS_FAILED(rv)) { path->Remove(true); // recursive return rv; } // Create an empty database for this mail folder, set its name from the user nsCOMPtr msgDBService = do_GetService("@mozilla.org/msgDatabase/msgDBService;1", &rv); if (msgDBService) { nsCOMPtr unusedDB; rv = msgDBService->OpenFolderDB(child, true, getter_AddRefs(unusedDB)); if (rv == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING) rv = msgDBService->CreateNewDB(child, getter_AddRefs(unusedDB)); if ((NS_SUCCEEDED(rv) || rv == NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE) && unusedDB) { // need to set the folder name nsCOMPtr folderInfo; rv = unusedDB->GetDBFolderInfo(getter_AddRefs(folderInfo)); if (NS_SUCCEEDED(rv)) folderInfo->SetMailboxName(safeFolderName); unusedDB->SetSummaryValid(true); unusedDB->Close(true); aParent->UpdateSummaryTotals(true); } else { MOZ_LOG(MailDirLog, mozilla::LogLevel::Info, ("CreateFolder - failed creating db for new folder")); path->Remove(true); // recursive rv = NS_MSG_CANT_CREATE_FOLDER; } } child.forget(aResult); return rv; } NS_IMETHODIMP nsMsgMaildirStore::HasSpaceAvailable(nsIMsgFolder* aFolder, int64_t aSpaceRequested, bool* aResult) { NS_ENSURE_ARG_POINTER(aResult); NS_ENSURE_ARG_POINTER(aFolder); nsCOMPtr pathFile; nsresult rv = aFolder->GetFilePath(getter_AddRefs(pathFile)); NS_ENSURE_SUCCESS(rv, rv); *aResult = DiskSpaceAvailableInStore(pathFile, aSpaceRequested); if (!*aResult) return NS_ERROR_FILE_NO_DEVICE_SPACE; return NS_OK; } NS_IMETHODIMP nsMsgMaildirStore::IsSummaryFileValid(nsIMsgFolder* aFolder, nsIMsgDatabase* aDB, bool* aResult) { NS_ENSURE_ARG_POINTER(aFolder); NS_ENSURE_ARG_POINTER(aDB); NS_ENSURE_ARG_POINTER(aResult); *aResult = true; nsCOMPtr dbFolderInfo; aDB->GetDBFolderInfo(getter_AddRefs(dbFolderInfo)); nsresult rv = dbFolderInfo->GetBooleanProperty("maildirValid", false, aResult); if (!*aResult) { nsCOMPtr newFile; rv = aFolder->GetFilePath(getter_AddRefs(newFile)); NS_ENSURE_SUCCESS(rv, rv); newFile->Append(u"cur"_ns); // If the "cur" sub-dir doesn't exist, and there are no messages // in the db, then the folder is probably new and the db is valid. bool exists; newFile->Exists(&exists); if (!exists) { int32_t numMessages; dbFolderInfo->GetNumMessages(&numMessages); if (!numMessages) *aResult = true; } } return rv; } NS_IMETHODIMP nsMsgMaildirStore::SetSummaryFileValid(nsIMsgFolder* aFolder, nsIMsgDatabase* aDB, bool aValid) { NS_ENSURE_ARG_POINTER(aFolder); NS_ENSURE_ARG_POINTER(aDB); nsCOMPtr dbFolderInfo; aDB->GetDBFolderInfo(getter_AddRefs(dbFolderInfo)); NS_ENSURE_STATE(dbFolderInfo); return dbFolderInfo->SetBooleanProperty("maildirValid", aValid); } NS_IMETHODIMP nsMsgMaildirStore::DeleteFolder(nsIMsgFolder* aFolder) { NS_ENSURE_ARG_POINTER(aFolder); bool exists; // Delete the Maildir itself. nsCOMPtr pathFile; nsresult rv = aFolder->GetFilePath(getter_AddRefs(pathFile)); NS_ENSURE_SUCCESS(rv, rv); exists = false; pathFile->Exists(&exists); if (exists) { rv = pathFile->Remove(true); NS_ENSURE_SUCCESS(rv, rv); } // Delete any subfolders (.sbd-suffixed directories). AddDirectorySeparator(pathFile); exists = false; pathFile->Exists(&exists); if (exists) { rv = pathFile->Remove(true); NS_ENSURE_SUCCESS(rv, rv); } return NS_OK; } NS_IMETHODIMP nsMsgMaildirStore::RenameFolder(nsIMsgFolder* aFolder, const nsAString& aNewName, nsIMsgFolder** aNewFolder) { NS_ENSURE_ARG_POINTER(aFolder); NS_ENSURE_ARG_POINTER(aNewFolder); // old path nsCOMPtr oldPathFile; nsresult rv = aFolder->GetFilePath(getter_AddRefs(oldPathFile)); NS_ENSURE_SUCCESS(rv, rv); // old sbd directory nsCOMPtr sbdPathFile; uint32_t numChildren; aFolder->GetNumSubFolders(&numChildren); if (numChildren > 0) { sbdPathFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = sbdPathFile->InitWithFile(oldPathFile); NS_ENSURE_SUCCESS(rv, rv); GetDirectoryForFolder(sbdPathFile); } // old summary nsCOMPtr oldSummaryFile; rv = aFolder->GetSummaryFile(getter_AddRefs(oldSummaryFile)); NS_ENSURE_SUCCESS(rv, rv); // Validate new name nsAutoString safeName(aNewName); NS_MsgHashIfNecessary(safeName); aFolder->ForceDBClosed(); // rename folder rv = oldPathFile->MoveTo(nullptr, safeName); NS_ENSURE_SUCCESS(rv, rv); if (numChildren > 0) { // rename "*.sbd" directory nsAutoString sbdName = safeName; sbdName.AppendLiteral(FOLDER_SUFFIX); sbdPathFile->MoveTo(nullptr, sbdName); } // rename summary nsAutoString summaryName(safeName); summaryName.AppendLiteral(SUMMARY_SUFFIX); oldSummaryFile->MoveTo(nullptr, summaryName); nsCOMPtr parentFolder; rv = aFolder->GetParent(getter_AddRefs(parentFolder)); if (!parentFolder) return NS_ERROR_NULL_POINTER; return parentFolder->AddSubfolder(safeName, aNewFolder); } NS_IMETHODIMP nsMsgMaildirStore::CopyFolder( nsIMsgFolder* aSrcFolder, nsIMsgFolder* aDstFolder, bool aIsMoveFolder, nsIMsgWindow* aMsgWindow, nsIMsgCopyServiceListener* aListener, const nsAString& aNewName) { NS_ENSURE_ARG_POINTER(aSrcFolder); NS_ENSURE_ARG_POINTER(aDstFolder); nsAutoString folderName; if (aNewName.IsEmpty()) aSrcFolder->GetName(folderName); else folderName.Assign(aNewName); nsAutoString safeFolderName(folderName); NS_MsgHashIfNecessary(safeFolderName); aSrcFolder->ForceDBClosed(); nsCOMPtr oldPath; nsresult rv = aSrcFolder->GetFilePath(getter_AddRefs(oldPath)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr summaryFile; GetSummaryFileLocation(oldPath, getter_AddRefs(summaryFile)); nsCOMPtr newPath; rv = aDstFolder->GetFilePath(getter_AddRefs(newPath)); NS_ENSURE_SUCCESS(rv, rv); // create target directory based on our current path bool isServer; aDstFolder->GetIsServer(&isServer); rv = CreateDirectoryForFolder(newPath, isServer); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr origPath; oldPath->Clone(getter_AddRefs(origPath)); rv = oldPath->CopyTo(newPath, safeFolderName); NS_ENSURE_SUCCESS(rv, rv); // will fail if a file by that name exists // Copy to dir can fail if file does not exist. If copy fails, we test // if the file exists or not, if it does not that's ok, we continue // without copying it. If it fails and file exist and is not zero sized // there is real problem. nsAutoString dbName(safeFolderName); dbName.AppendLiteral(SUMMARY_SUFFIX); rv = summaryFile->CopyTo(newPath, dbName); if (!NS_SUCCEEDED(rv)) { // Test if the file is not empty bool exists; int64_t fileSize; summaryFile->Exists(&exists); summaryFile->GetFileSize(&fileSize); if (exists && fileSize > 0) NS_ENSURE_SUCCESS(rv, rv); // Yes, it should have worked! // else case is file is zero sized, no need to copy it, // not an error // else case is file does not exist - not an error } nsCOMPtr newMsgFolder; rv = aDstFolder->AddSubfolder(safeFolderName, getter_AddRefs(newMsgFolder)); NS_ENSURE_SUCCESS(rv, rv); newMsgFolder->SetPrettyName(folderName); uint32_t flags; aSrcFolder->GetFlags(&flags); newMsgFolder->SetFlags(flags); bool changed = false; rv = aSrcFolder->MatchOrChangeFilterDestination(newMsgFolder, true, &changed); if (changed) aSrcFolder->AlertFilterChanged(aMsgWindow); nsTArray> subFolders; rv = aSrcFolder->GetSubFolders(subFolders); NS_ENSURE_SUCCESS(rv, rv); // Copy subfolders to the new location nsresult copyStatus = NS_OK; nsCOMPtr localNewFolder( do_QueryInterface(newMsgFolder, &rv)); if (NS_SUCCEEDED(rv)) { for (nsIMsgFolder* folder : subFolders) { copyStatus = localNewFolder->CopyFolderLocal(folder, false, aMsgWindow, aListener); // Test if the call succeeded, if not we have to stop recursive call if (NS_FAILED(copyStatus)) { // Copy failed we have to notify caller to handle the error and stop // moving the folders. In case this happens to the topmost level of // recursive call, then we just need to break from the while loop and // go to error handling code. if (!aIsMoveFolder) return copyStatus; break; } } } if (aIsMoveFolder && NS_SUCCEEDED(copyStatus)) { if (localNewFolder) { nsCOMPtr srcSupport(do_QueryInterface(aSrcFolder)); localNewFolder->OnCopyCompleted(srcSupport, true); } // Notify that the folder that was dragged and dropped has been created. // No need to do this for its subfolders - isMoveFolder will be true for // folder. aDstFolder->NotifyFolderAdded(newMsgFolder); nsCOMPtr msgParent; aSrcFolder->GetParent(getter_AddRefs(msgParent)); aSrcFolder->SetParent(nullptr); if (msgParent) { // The files have already been moved, so delete storage false msgParent->PropagateDelete(aSrcFolder, false); oldPath->Remove(true); aSrcFolder->DeleteStorage(); nsCOMPtr parentPath; rv = msgParent->GetFilePath(getter_AddRefs(parentPath)); NS_ENSURE_SUCCESS(rv, rv); AddDirectorySeparator(parentPath); nsCOMPtr children; parentPath->GetDirectoryEntries(getter_AddRefs(children)); bool more; // checks if the directory is empty or not if (children && NS_SUCCEEDED(children->HasMoreElements(&more)) && !more) parentPath->Remove(true); } } else { // This is the case where the copy of a subfolder failed. // We have to delete the newDirectory tree to make a "rollback". // Someone should add a popup to warn the user that the move was not // possible. if (aIsMoveFolder && NS_FAILED(copyStatus)) { nsCOMPtr msgParent; newMsgFolder->ForceDBClosed(); newMsgFolder->GetParent(getter_AddRefs(msgParent)); newMsgFolder->SetParent(nullptr); if (msgParent) { msgParent->PropagateDelete(newMsgFolder, false); newMsgFolder->DeleteStorage(); AddDirectorySeparator(newPath); newPath->Remove(true); // berkeley mailbox } return NS_ERROR_FAILURE; } } return NS_OK; } NS_IMETHODIMP nsMsgMaildirStore::GetNewMsgOutputStream(nsIMsgFolder* aFolder, nsIMsgDBHdr** aNewMsgHdr, nsIOutputStream** aResult) { NS_ENSURE_ARG_POINTER(aFolder); NS_ENSURE_ARG_POINTER(aNewMsgHdr); NS_ENSURE_ARG_POINTER(aResult); nsCOMPtr db; nsresult rv = aFolder->GetMsgDatabase(getter_AddRefs(db)); NS_ENSURE_SUCCESS(rv, rv); if (!*aNewMsgHdr) { rv = db->CreateNewHdr(nsMsgKey_None, aNewMsgHdr); NS_ENSURE_SUCCESS(rv, rv); } // With maildir, messages have whole file to themselves. (*aNewMsgHdr)->SetMessageOffset(0); // We're going to save the new message into the maildir 'tmp' folder. // When the message is completed, it can be moved to 'cur'. nsCOMPtr newFile; rv = aFolder->GetFilePath(getter_AddRefs(newFile)); NS_ENSURE_SUCCESS(rv, rv); newFile->Append(u"tmp"_ns); // let's check if the folder exists // XXX TODO: kill this and make sure maildir creation includes cur/tmp bool exists; newFile->Exists(&exists); if (!exists) { MOZ_LOG(MailDirLog, mozilla::LogLevel::Info, ("GetNewMsgOutputStream - tmp subfolder does not exist!!")); rv = newFile->Create(nsIFile::DIRECTORY_TYPE, 0755); NS_ENSURE_SUCCESS(rv, rv); } // Generate the 'tmp' file name based on timestamp. // (We'll use the Message-ID as the basis for the final filename, // but we don't have headers at this point). nsAutoCString newName; newName.AppendInt(static_cast(PR_Now())); newFile->AppendNative(newName); // CreateUnique, in case we get more than one message per millisecond :-) rv = newFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600); NS_ENSURE_SUCCESS(rv, rv); newFile->GetNativeLeafName(newName); // save the file name in the message header - otherwise no way to retrieve it (*aNewMsgHdr)->SetStringProperty("storeToken", newName); return MsgNewBufferedFileOutputStream(aResult, newFile, PR_WRONLY | PR_CREATE_FILE, 00600); } NS_IMETHODIMP nsMsgMaildirStore::DiscardNewMessage(nsIOutputStream* aOutputStream, nsIMsgDBHdr* aNewHdr) { NS_ENSURE_ARG_POINTER(aOutputStream); NS_ENSURE_ARG_POINTER(aNewHdr); aOutputStream->Close(); // file path is stored in message header property "storeToken" nsAutoCString fileName; aNewHdr->GetStringProperty("storeToken", fileName); if (fileName.IsEmpty()) return NS_ERROR_FAILURE; nsCOMPtr path; nsCOMPtr folder; nsresult rv = aNewHdr->GetFolder(getter_AddRefs(folder)); NS_ENSURE_SUCCESS(rv, rv); rv = folder->GetFilePath(getter_AddRefs(path)); NS_ENSURE_SUCCESS(rv, rv); // path to the message download folder path->Append(u"tmp"_ns); path->AppendNative(fileName); return path->Remove(false); } NS_IMETHODIMP nsMsgMaildirStore::FinishNewMessage(nsIOutputStream* aOutputStream, nsIMsgDBHdr* aNewHdr) { NS_ENSURE_ARG_POINTER(aOutputStream); NS_ENSURE_ARG_POINTER(aNewHdr); aOutputStream->Close(); nsCOMPtr folderPath; nsCOMPtr folder; nsresult rv = aNewHdr->GetFolder(getter_AddRefs(folder)); NS_ENSURE_SUCCESS(rv, rv); rv = folder->GetFilePath(getter_AddRefs(folderPath)); NS_ENSURE_SUCCESS(rv, rv); // tmp filename is stored in "storeToken". // By now we'll have the Message-ID, which we'll base the final filename on. nsAutoCString tmpName; aNewHdr->GetStringProperty("storeToken", tmpName); if (tmpName.IsEmpty()) { NS_ERROR("FinishNewMessage - no storeToken in msg hdr!!"); return NS_ERROR_FAILURE; } // path to the new destination nsCOMPtr curPath; folderPath->Clone(getter_AddRefs(curPath)); curPath->Append(u"cur"_ns); // let's check if the folder exists // XXX TODO: kill this and make sure maildir creation includes cur/tmp bool exists; curPath->Exists(&exists); if (!exists) { rv = curPath->Create(nsIFile::DIRECTORY_TYPE, 0755); NS_ENSURE_SUCCESS(rv, rv); } // path to the downloaded message nsCOMPtr fromPath; folderPath->Clone(getter_AddRefs(fromPath)); fromPath->Append(u"tmp"_ns); fromPath->AppendNative(tmpName); // Check that the message is still in tmp. // XXX TODO: revisit this. I think it's needed because the // pairing rules for: // GetNewMsgOutputStream(), FinishNewMessage(), // MoveNewlyDownloadedMessage() and DiscardNewMessage() // are not well defined. // If they are sorted out, this code can be removed. fromPath->Exists(&exists); if (!exists) { // Perhaps the message has already moved. See bug 1028372 to fix this. nsCOMPtr existingPath; curPath->Clone(getter_AddRefs(existingPath)); existingPath->AppendNative(tmpName); existingPath->Exists(&exists); if (exists) // then there is nothing to do return NS_OK; NS_ERROR("FinishNewMessage - oops! file does not exist!"); return NS_ERROR_FILE_NOT_FOUND; } nsCString msgID; aNewHdr->GetMessageId(getter_Copies(msgID)); nsCString baseName; // For missing or suspiciously-short Message-IDs, use a timestamp // instead. // This also avoids some special filenames we can't use in windows (CON, // AUX, NUL, LPT1 etc...). With an extension (eg "LPT4.txt") they're all // below 9 chars. if (msgID.Length() < 9) { baseName.AppendInt(static_cast(PR_Now())); } else { percentEncode(msgID, baseName); // No length limit on Message-Id header, but lets clip our filenames // well below any MAX_PATH limits. if (baseName.Length() > (128 - 4)) { baseName.SetLength(128 - 4); // (4 for ".eml") } } nsCOMPtr toPath; curPath->Clone(getter_AddRefs(toPath)); nsCString toName(baseName); toName.Append(".eml"); toPath->AppendNative(toName); // Using CreateUnique in case we have duplicate Message-Ids rv = toPath->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600); if (NS_FAILED(rv)) { // NS_ERROR_FILE_TOO_BIG means CreateUnique() bailed out at 10000 attempts. if (rv != NS_ERROR_FILE_TOO_BIG) { NS_ENSURE_SUCCESS(rv, rv); } // As a last resort, fall back to using timestamp as filename. toName.SetLength(0); toName.AppendInt(static_cast(PR_Now())); toName.Append(".eml"); toPath->SetNativeLeafName(toName); rv = toPath->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600); NS_ENSURE_SUCCESS(rv, rv); } // Move into place (using whatever name CreateUnique() settled upon). toPath->GetNativeLeafName(toName); rv = fromPath->MoveToNative(curPath, toName); NS_ENSURE_SUCCESS(rv, rv); // Update the db to reflect the final filename. aNewHdr->SetStringProperty("storeToken", toName); return NS_OK; } NS_IMETHODIMP nsMsgMaildirStore::MoveNewlyDownloadedMessage(nsIMsgDBHdr* aHdr, nsIMsgFolder* aDestFolder, bool* aResult) { NS_ENSURE_ARG_POINTER(aHdr); NS_ENSURE_ARG_POINTER(aDestFolder); NS_ENSURE_ARG_POINTER(aResult); nsCOMPtr folderPath; nsCOMPtr folder; nsresult rv = aHdr->GetFolder(getter_AddRefs(folder)); NS_ENSURE_SUCCESS(rv, rv); rv = folder->GetFilePath(getter_AddRefs(folderPath)); NS_ENSURE_SUCCESS(rv, rv); // file path is stored in message header property nsAutoCString fileName; aHdr->GetStringProperty("storeToken", fileName); if (fileName.IsEmpty()) { NS_ERROR("FinishNewMessage - no storeToken in msg hdr!!"); return NS_ERROR_FAILURE; } // path to the downloaded message nsCOMPtr fromPath; folderPath->Clone(getter_AddRefs(fromPath)); fromPath->Append(u"cur"_ns); fromPath->AppendNative(fileName); // let's check if the tmp file exists bool exists; fromPath->Exists(&exists); if (!exists) { NS_ERROR("FinishNewMessage - oops! file does not exist!"); return NS_ERROR_FAILURE; } // move to the "cur" subfolder nsCOMPtr toPath; aDestFolder->GetFilePath(getter_AddRefs(folderPath)); folderPath->Clone(getter_AddRefs(toPath)); toPath->Append(u"cur"_ns); // let's check if the folder exists // XXX TODO: kill this and make sure maildir creation includes cur/tmp toPath->Exists(&exists); if (!exists) { rv = toPath->Create(nsIFile::DIRECTORY_TYPE, 0755); NS_ENSURE_SUCCESS(rv, rv); } nsCOMPtr destMailDB; rv = aDestFolder->GetMsgDatabase(getter_AddRefs(destMailDB)); NS_WARNING_ASSERTION(destMailDB && NS_SUCCEEDED(rv), "failed to open mail db moving message"); nsCOMPtr newHdr; if (destMailDB) rv = destMailDB->CopyHdrFromExistingHdr(nsMsgKey_None, aHdr, true, getter_AddRefs(newHdr)); if (NS_SUCCEEDED(rv) && !newHdr) rv = NS_ERROR_UNEXPECTED; if (NS_FAILED(rv)) { aDestFolder->ThrowAlertMsg("filterFolderHdrAddFailed", nullptr); return rv; } nsCOMPtr existingPath; toPath->Clone(getter_AddRefs(existingPath)); existingPath->AppendNative(fileName); existingPath->Exists(&exists); if (exists) { rv = existingPath->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600); NS_ENSURE_SUCCESS(rv, rv); existingPath->GetNativeLeafName(fileName); newHdr->SetStringProperty("storeToken", fileName); } rv = fromPath->MoveToNative(toPath, fileName); *aResult = NS_SUCCEEDED(rv); if (NS_FAILED(rv)) aDestFolder->ThrowAlertMsg("filterFolderWriteFailed", nullptr); if (NS_FAILED(rv)) { if (destMailDB) destMailDB->Close(true); return NS_MSG_ERROR_WRITING_MAIL_FOLDER; } bool movedMsgIsNew = false; // if we have made it this far then the message has successfully been // written to the new folder now add the header to the destMailDB. uint32_t newFlags; newHdr->GetFlags(&newFlags); nsMsgKey msgKey; newHdr->GetMessageKey(&msgKey); if (!(newFlags & nsMsgMessageFlags::Read)) { nsCString junkScoreStr; (void)newHdr->GetStringProperty("junkscore", junkScoreStr); if (atoi(junkScoreStr.get()) != nsIJunkMailPlugin::IS_SPAM_SCORE) { newHdr->OrFlags(nsMsgMessageFlags::New, &newFlags); destMailDB->AddToNewList(msgKey); movedMsgIsNew = true; } } nsCOMPtr notifier( do_GetService("@mozilla.org/messenger/msgnotificationservice;1")); if (notifier) notifier->NotifyMsgAdded(newHdr); if (movedMsgIsNew) { aDestFolder->SetHasNewMessages(true); // Notify the message was moved. if (notifier) { notifier->NotifyMsgUnincorporatedMoved(folder, newHdr); } } nsCOMPtr sourceDB; rv = folder->GetMsgDatabase(getter_AddRefs(sourceDB)); if (NS_SUCCEEDED(rv) && sourceDB) sourceDB->RemoveHeaderMdbRow(aHdr); destMailDB->SetSummaryValid(true); aDestFolder->UpdateSummaryTotals(true); destMailDB->Commit(nsMsgDBCommitType::kLargeCommit); return rv; } NS_IMETHODIMP nsMsgMaildirStore::GetMsgInputStream(nsIMsgFolder* aMsgFolder, const nsACString& aMsgToken, nsIInputStream** aResult) { NS_ENSURE_ARG_POINTER(aMsgFolder); NS_ENSURE_ARG_POINTER(aResult); // construct path to file nsCOMPtr path; nsresult rv = aMsgFolder->GetFilePath(getter_AddRefs(path)); NS_ENSURE_SUCCESS(rv, rv); if (aMsgToken.IsEmpty()) { MOZ_LOG(MailDirLog, mozilla::LogLevel::Info, ("GetMsgInputStream - empty storeToken!!")); return NS_ERROR_FAILURE; } path->Append(u"cur"_ns); // let's check if the folder exists // XXX TODO: kill this and make sure maildir creation includes cur/tmp bool exists; path->Exists(&exists); if (!exists) { MOZ_LOG(MailDirLog, mozilla::LogLevel::Info, ("GetMsgInputStream - oops! cur subfolder does not exist!")); rv = path->Create(nsIFile::DIRECTORY_TYPE, 0755); NS_ENSURE_SUCCESS(rv, rv); } path->AppendNative(aMsgToken); return NS_NewLocalFileInputStream(aResult, path); } NS_IMETHODIMP nsMsgMaildirStore::DeleteMessages( const nsTArray>& aHdrArray) { nsCOMPtr folder; for (auto msgHdr : aHdrArray) { msgHdr->GetFolder(getter_AddRefs(folder)); nsCOMPtr path; nsresult rv = folder->GetFilePath(getter_AddRefs(path)); NS_ENSURE_SUCCESS(rv, rv); nsAutoCString fileName; msgHdr->GetStringProperty("storeToken", fileName); if (fileName.IsEmpty()) { MOZ_LOG(MailDirLog, mozilla::LogLevel::Info, ("DeleteMessages - empty storeToken!!")); // Perhaps an offline store has not downloaded this particular message. continue; } path->Append(u"cur"_ns); path->AppendNative(fileName); // Let's check if the message exists. bool exists; path->Exists(&exists); if (!exists) { MOZ_LOG(MailDirLog, mozilla::LogLevel::Info, ("DeleteMessages - file does not exist !!")); // Perhaps an offline store has not downloaded this particular message. continue; } path->Remove(false); } return NS_OK; } NS_IMETHODIMP nsMsgMaildirStore::CopyMessages(bool aIsMove, const nsTArray>& aHdrArray, nsIMsgFolder* aDstFolder, nsTArray>& aDstHdrs, nsITransaction** aUndoAction, bool* aCopyDone) { NS_ENSURE_ARG_POINTER(aDstFolder); NS_ENSURE_ARG_POINTER(aCopyDone); NS_ENSURE_ARG_POINTER(aUndoAction); *aCopyDone = false; if (aHdrArray.IsEmpty()) { return NS_ERROR_INVALID_ARG; } nsCOMPtr srcFolder; nsresult rv; nsIMsgDBHdr* msgHdr = aHdrArray[0]; rv = msgHdr->GetFolder(getter_AddRefs(srcFolder)); NS_ENSURE_SUCCESS(rv, rv); // Both source and destination folders must use maildir type store. nsCOMPtr srcStore; nsAutoCString srcType; srcFolder->GetMsgStore(getter_AddRefs(srcStore)); if (srcStore) srcStore->GetStoreType(srcType); nsCOMPtr dstStore; nsAutoCString dstType; aDstFolder->GetMsgStore(getter_AddRefs(dstStore)); if (dstStore) dstStore->GetStoreType(dstType); if (!srcType.EqualsLiteral("maildir") || !dstType.EqualsLiteral("maildir")) return NS_OK; // Both source and destination must be local folders. In theory we could // do efficient copies of the offline store of IMAP, but this is not // supported yet. For that, we need to deal with both correct handling // of deletes from the src server, and msgKey = UIDL in the dst folder. nsCOMPtr destLocalFolder( do_QueryInterface(aDstFolder)); if (!destLocalFolder) return NS_OK; nsCOMPtr srcLocalFolder(do_QueryInterface(srcFolder)); if (!srcLocalFolder) return NS_OK; // We should be able to use a file move for an efficient copy. nsCOMPtr destFolderPath; nsCOMPtr destDB; aDstFolder->GetMsgDatabase(getter_AddRefs(destDB)); rv = aDstFolder->GetFilePath(getter_AddRefs(destFolderPath)); NS_ENSURE_SUCCESS(rv, rv); destFolderPath->Append(u"cur"_ns); nsCOMPtr srcFolderPath; rv = srcFolder->GetFilePath(getter_AddRefs(srcFolderPath)); NS_ENSURE_SUCCESS(rv, rv); srcFolderPath->Append(u"cur"_ns); nsCOMPtr srcDB; srcFolder->GetMsgDatabase(getter_AddRefs(srcDB)); RefPtr msgTxn = new nsLocalMoveCopyMsgTxn; NS_ENSURE_TRUE(msgTxn, NS_ERROR_OUT_OF_MEMORY); if (NS_SUCCEEDED(msgTxn->Init(srcFolder, aDstFolder, aIsMove))) { if (aIsMove) msgTxn->SetTransactionType(nsIMessenger::eMoveMsg); else msgTxn->SetTransactionType(nsIMessenger::eCopyMsg); } aDstHdrs.Clear(); aDstHdrs.SetCapacity(aHdrArray.Length()); for (auto srcHdr : aHdrArray) { nsMsgKey srcKey; srcHdr->GetMessageKey(&srcKey); msgTxn->AddSrcKey(srcKey); nsAutoCString fileName; srcHdr->GetStringProperty("storeToken", fileName); if (fileName.IsEmpty()) { MOZ_LOG(MailDirLog, mozilla::LogLevel::Info, ("GetMsgInputStream - empty storeToken!!")); return NS_ERROR_FAILURE; } nsCOMPtr srcFile; rv = srcFolderPath->Clone(getter_AddRefs(srcFile)); NS_ENSURE_SUCCESS(rv, rv); srcFile->AppendNative(fileName); nsCOMPtr destFile; destFolderPath->Clone(getter_AddRefs(destFile)); destFile->AppendNative(fileName); bool exists; destFile->Exists(&exists); if (exists) { rv = destFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600); NS_ENSURE_SUCCESS(rv, rv); destFile->GetNativeLeafName(fileName); } if (aIsMove) rv = srcFile->MoveToNative(destFolderPath, fileName); else rv = srcFile->CopyToNative(destFolderPath, fileName); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr destHdr; if (destDB) { rv = destDB->CopyHdrFromExistingHdr(nsMsgKey_None, srcHdr, true, getter_AddRefs(destHdr)); NS_ENSURE_SUCCESS(rv, rv); destHdr->SetStringProperty("storeToken", fileName); aDstHdrs.AppendElement(destHdr); nsMsgKey dstKey; destHdr->GetMessageKey(&dstKey); msgTxn->AddDstKey(dstKey); } } nsCOMPtr notifier( do_GetService("@mozilla.org/messenger/msgnotificationservice;1")); if (notifier) { notifier->NotifyMsgsMoveCopyCompleted(aIsMove, aHdrArray, aDstFolder, aDstHdrs); } // For now, we only support local dest folders, and for those we are done and // can delete the messages. Perhaps this should be moved into the folder // when we try to support other folder types. if (aIsMove) { for (auto msgDBHdr : aHdrArray) { srcDB->DeleteHeader(msgDBHdr, nullptr, false, true); } } *aCopyDone = true; msgTxn.forget(aUndoAction); return NS_OK; } NS_IMETHODIMP nsMsgMaildirStore::GetSupportsCompaction(bool* aSupportsCompaction) { NS_ENSURE_ARG_POINTER(aSupportsCompaction); *aSupportsCompaction = false; return NS_OK; } NS_IMETHODIMP nsMsgMaildirStore::CompactFolder(nsIMsgFolder* aFolder, nsIUrlListener* aListener, nsIMsgWindow* aMsgWindow) { return NS_OK; } class MaildirStoreParser { public: MaildirStoreParser(nsIMsgFolder* aFolder, nsIMsgDatabase* aMsgDB, nsIDirectoryEnumerator* aDirectoryEnumerator, nsIUrlListener* aUrlListener); virtual ~MaildirStoreParser(); nsresult ParseNextMessage(nsIFile* aFile); static void TimerCallback(nsITimer* aTimer, void* aClosure); nsresult StartTimer(); nsCOMPtr m_directoryEnumerator; nsCOMPtr m_folder; nsCOMPtr m_db; nsCOMPtr m_timer; nsCOMPtr m_listener; }; MaildirStoreParser::MaildirStoreParser(nsIMsgFolder* aFolder, nsIMsgDatabase* aMsgDB, nsIDirectoryEnumerator* aDirEnum, nsIUrlListener* aUrlListener) { m_folder = aFolder; m_db = aMsgDB; m_directoryEnumerator = aDirEnum; m_listener = aUrlListener; } MaildirStoreParser::~MaildirStoreParser() {} nsresult MaildirStoreParser::ParseNextMessage(nsIFile* aFile) { nsresult rv; NS_ENSURE_TRUE(m_db, NS_ERROR_NULL_POINTER); nsCOMPtr inputStream; nsCOMPtr msgParser = do_CreateInstance("@mozilla.org/messenger/messagestateparser;1", &rv); NS_ENSURE_SUCCESS(rv, rv); msgParser->SetMailDB(m_db); nsCOMPtr newMsgHdr; rv = m_db->CreateNewHdr(nsMsgKey_None, getter_AddRefs(newMsgHdr)); NS_ENSURE_SUCCESS(rv, rv); newMsgHdr->SetMessageOffset(0); rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), aFile); if (NS_SUCCEEDED(rv) && inputStream) { RefPtr inputStreamBuffer = new nsMsgLineStreamBuffer(FILE_IO_BUFFER_SIZE, true, false); int64_t fileSize; aFile->GetFileSize(&fileSize); msgParser->SetNewMsgHdr(newMsgHdr); msgParser->SetState(nsIMsgParseMailMsgState::ParseHeadersState); bool needMoreData = false; char* newLine = nullptr; uint32_t numBytesInLine = 0; // we only have to read the headers, because we know the message size // from the file size. So we can do this in one time slice. do { newLine = inputStreamBuffer->ReadNextLine(inputStream, numBytesInLine, needMoreData); if (newLine) { msgParser->ParseAFolderLine(newLine, numBytesInLine); free(newLine); } } while (newLine && numBytesInLine > 0); msgParser->FinishHeader(); // A single message needs to be less than 4GB newMsgHdr->SetMessageSize((uint32_t)fileSize); m_db->AddNewHdrToDB(newMsgHdr, true); nsAutoCString storeToken; aFile->GetNativeLeafName(storeToken); newMsgHdr->SetStringProperty("storeToken", storeToken); } NS_ENSURE_SUCCESS(rv, rv); return rv; } void MaildirStoreParser::TimerCallback(nsITimer* aTimer, void* aClosure) { MaildirStoreParser* parser = (MaildirStoreParser*)aClosure; bool hasMore; parser->m_directoryEnumerator->HasMoreElements(&hasMore); if (!hasMore) { nsCOMPtr store; parser->m_folder->GetMsgStore(getter_AddRefs(store)); parser->m_timer->Cancel(); if (parser->m_db) parser->m_db->SetSummaryValid(true); // store->SetSummaryFileValid(parser->m_folder, parser->m_db, true); if (parser->m_listener) { nsresult rv; nsCOMPtr mailboxurl = do_CreateInstance("@mozilla.org/messenger/mailboxurl;1", &rv); if (NS_SUCCEEDED(rv) && mailboxurl) { nsCOMPtr url = do_QueryInterface(mailboxurl); url->SetUpdatingFolder(true); nsAutoCString uriSpec("mailbox://"); // ### TODO - what if SetSpec fails? (void)url->SetSpecInternal(uriSpec); parser->m_listener->OnStopRunningUrl(url, NS_OK); } } // Parsing complete and timer cancelled, so we release the parser object. delete parser; return; } nsCOMPtr currentFile; nsresult rv = parser->m_directoryEnumerator->GetNextFile(getter_AddRefs(currentFile)); if (NS_SUCCEEDED(rv)) rv = parser->ParseNextMessage(currentFile); if (NS_FAILED(rv) && parser->m_listener) parser->m_listener->OnStopRunningUrl(nullptr, NS_ERROR_FAILURE); } nsresult MaildirStoreParser::StartTimer() { nsresult rv; m_timer = do_CreateInstance("@mozilla.org/timer;1", &rv); NS_ENSURE_SUCCESS(rv, rv); m_timer->InitWithNamedFuncCallback(TimerCallback, (void*)this, 0, nsITimer::TYPE_REPEATING_SLACK, "MaildirStoreParser::TimerCallback"); return NS_OK; } NS_IMETHODIMP nsMsgMaildirStore::RebuildIndex(nsIMsgFolder* aFolder, nsIMsgDatabase* aMsgDB, nsIMsgWindow* aMsgWindow, nsIUrlListener* aListener) { NS_ENSURE_ARG_POINTER(aFolder); // This code needs to iterate over the maildir files, and parse each // file and add a msg hdr to the db for the file. nsCOMPtr path; nsresult rv = aFolder->GetFilePath(getter_AddRefs(path)); NS_ENSURE_SUCCESS(rv, rv); path->Append(u"cur"_ns); nsCOMPtr directoryEnumerator; rv = path->GetDirectoryEntries(getter_AddRefs(directoryEnumerator)); NS_ENSURE_SUCCESS(rv, rv); MaildirStoreParser* fileParser = new MaildirStoreParser(aFolder, aMsgDB, directoryEnumerator, aListener); NS_ENSURE_TRUE(fileParser, NS_ERROR_OUT_OF_MEMORY); fileParser->StartTimer(); ResetForceReparse(aMsgDB); return NS_OK; } NS_IMETHODIMP nsMsgMaildirStore::ChangeFlags( const nsTArray>& aHdrArray, uint32_t aFlags, bool aSet) { for (auto msgHdr : aHdrArray) { // get output stream for header nsCOMPtr outputStream; nsresult rv = GetOutputStream(msgHdr, outputStream); NS_ENSURE_SUCCESS(rv, rv); // Work out the flags we want to write. uint32_t flags = 0; (void)msgHdr->GetFlags(&flags); flags &= ~(nsMsgMessageFlags::RuntimeOnly | nsMsgMessageFlags::Offline); if (aSet) { flags |= aFlags; } else { flags &= ~aFlags; } // Rewrite X-Mozilla-Status headers. nsCOMPtr seekable(do_QueryInterface(outputStream, &rv)); NS_ENSURE_SUCCESS(rv, rv); rv = RewriteMsgFlags(seekable, flags); if (NS_FAILED(rv)) NS_WARNING("updateFolderFlag failed"); } return NS_OK; } // get output stream from header nsresult nsMsgMaildirStore::GetOutputStream( nsIMsgDBHdr* aHdr, nsCOMPtr& aOutputStream) { // file name is stored in message header property "storeToken" nsAutoCString fileName; aHdr->GetStringProperty("storeToken", fileName); if (fileName.IsEmpty()) return NS_ERROR_FAILURE; nsCOMPtr folder; nsresult rv = aHdr->GetFolder(getter_AddRefs(folder)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr folderPath; rv = folder->GetFilePath(getter_AddRefs(folderPath)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr maildirFile; folderPath->Clone(getter_AddRefs(maildirFile)); maildirFile->Append(u"cur"_ns); maildirFile->AppendNative(fileName); return MsgGetFileStream(maildirFile, getter_AddRefs(aOutputStream)); } NS_IMETHODIMP nsMsgMaildirStore::ChangeKeywords( const nsTArray>& aHdrArray, const nsACString& aKeywords, bool aAdd) { if (aHdrArray.IsEmpty()) return NS_ERROR_INVALID_ARG; nsTArray keywordsToAdd; nsTArray keywordsToRemove; if (aAdd) { ParseString(aKeywords, ' ', keywordsToAdd); } else { ParseString(aKeywords, ' ', keywordsToRemove); } for (auto msgHdr : aHdrArray) { // Open the message file. nsCOMPtr output; nsresult rv = GetOutputStream(msgHdr, output); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr seekable(do_QueryInterface(output, &rv)); NS_ENSURE_SUCCESS(rv, rv); bool notEnoughRoom; rv = ChangeKeywordsHelper(seekable, keywordsToAdd, keywordsToRemove, notEnoughRoom); NS_ENSURE_SUCCESS(rv, rv); if (notEnoughRoom) { // The growKeywords property indicates that the X-Mozilla-Keys header // doesn't have enough space, and should be rebuilt during the next // folder compaction. // TODO: For maildir there is no compaction, so this'll have no effect! msgHdr->SetUint32Property("growKeywords", 1); } output->Close(); } return NS_OK; } NS_IMETHODIMP nsMsgMaildirStore::GetStoreType(nsACString& aType) { aType.AssignLiteral("maildir"); return NS_OK; } /** * Finds the directory associated with this folder. That is if the path is * c:\Inbox, it will return c:\Inbox.sbd if it succeeds. Path is strictly * an out parameter. */ nsresult nsMsgMaildirStore::GetDirectoryForFolder(nsIFile* path) { // add directory separator to the path nsAutoString leafName; path->GetLeafName(leafName); leafName.AppendLiteral(FOLDER_SUFFIX); return path->SetLeafName(leafName); } nsresult nsMsgMaildirStore::CreateDirectoryForFolder(nsIFile* path, bool aIsServer) { nsresult rv = NS_OK; if (!aIsServer) { rv = GetDirectoryForFolder(path); NS_ENSURE_SUCCESS(rv, rv); } bool pathIsDirectory = false; path->IsDirectory(&pathIsDirectory); if (!pathIsDirectory) { bool pathExists; path->Exists(&pathExists); // If for some reason there's a file with the directory separator // then we are going to fail. rv = pathExists ? NS_MSG_COULD_NOT_CREATE_DIRECTORY : path->Create(nsIFile::DIRECTORY_TYPE, 0700); } return rv; } NS_IMETHODIMP nsMsgMaildirStore::SliceStream(nsIInputStream* inStream, uint64_t start, uint32_t length, nsIInputStream** result) { nsCOMPtr in(inStream); RefPtr slicedStream = new mozilla::SlicedInputStream(in.forget(), start, uint64_t(length)); slicedStream.forget(result); return NS_OK; }