diff options
author | wolfbeast <mcwerewolf@wolfbeast.com> | 2019-12-06 23:39:47 +0100 |
---|---|---|
committer | wolfbeast <mcwerewolf@wolfbeast.com> | 2019-12-06 23:39:47 +0100 |
commit | 642032029f65e9dc0d38fbb6d35ef656c73a292c (patch) | |
tree | 78a22bd12d679ab532db490d631ee69fa085aec1 /mailnews/local/src/nsMsgLocalStoreUtils.cpp | |
parent | 50ef259a2df60d020ccb02d76dc5aa4835ee319e (diff) | |
parent | 2529b2edece0a0ed86553d1e73eef13c3848bf64 (diff) | |
download | UXP-642032029f65e9dc0d38fbb6d35ef656c73a292c.tar UXP-642032029f65e9dc0d38fbb6d35ef656c73a292c.tar.gz UXP-642032029f65e9dc0d38fbb6d35ef656c73a292c.tar.lz UXP-642032029f65e9dc0d38fbb6d35ef656c73a292c.tar.xz UXP-642032029f65e9dc0d38fbb6d35ef656c73a292c.zip |
Merge branch 'master' into release
Diffstat (limited to 'mailnews/local/src/nsMsgLocalStoreUtils.cpp')
-rw-r--r-- | mailnews/local/src/nsMsgLocalStoreUtils.cpp | 345 |
1 files changed, 345 insertions, 0 deletions
diff --git a/mailnews/local/src/nsMsgLocalStoreUtils.cpp b/mailnews/local/src/nsMsgLocalStoreUtils.cpp new file mode 100644 index 000000000..17d9b0f29 --- /dev/null +++ b/mailnews/local/src/nsMsgLocalStoreUtils.cpp @@ -0,0 +1,345 @@ +/* -*- 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 "msgCore.h" // precompiled header... +#include "nsMsgLocalStoreUtils.h" +#include "nsIFile.h" +#include "nsIDBFolderInfo.h" +#include "nsIMsgDatabase.h" +#include "prprf.h" + +#define EXTRA_SAFETY_SPACE 0x400000 // (4MiB) + +nsMsgLocalStoreUtils::nsMsgLocalStoreUtils() +{ +} + +nsresult +nsMsgLocalStoreUtils::AddDirectorySeparator(nsIFile *path) +{ + nsAutoString leafName; + path->GetLeafName(leafName); + leafName.AppendLiteral(FOLDER_SUFFIX); + return path->SetLeafName(leafName); +} + +bool +nsMsgLocalStoreUtils::nsShouldIgnoreFile(nsAString& name) +{ + char16_t firstChar = name.First(); + if (firstChar == '.' || firstChar == '#' || + name.CharAt(name.Length() - 1) == '~') + return true; + + if (name.LowerCaseEqualsLiteral("msgfilterrules.dat") || + name.LowerCaseEqualsLiteral("rules.dat") || + name.LowerCaseEqualsLiteral("filterlog.html") || + name.LowerCaseEqualsLiteral("junklog.html") || + name.LowerCaseEqualsLiteral("rulesbackup.dat")) + return true; + + // don't add summary files to the list of folders; + // don't add popstate files to the list either, or rules (sort.dat). + if (StringEndsWith(name, NS_LITERAL_STRING(".snm")) || + name.LowerCaseEqualsLiteral("popstate.dat") || + name.LowerCaseEqualsLiteral("sort.dat") || + name.LowerCaseEqualsLiteral("mailfilt.log") || + name.LowerCaseEqualsLiteral("filters.js") || + StringEndsWith(name, NS_LITERAL_STRING(".toc"))) + return true; + + // ignore RSS data source files + if (name.LowerCaseEqualsLiteral("feeds.rdf") || + name.LowerCaseEqualsLiteral("feeditems.rdf") || + StringBeginsWith(name, NS_LITERAL_STRING("feeditems_error"))) + return true; + + // The .mozmsgs dir is for spotlight support + return (StringEndsWith(name, NS_LITERAL_STRING(".mozmsgs")) || + StringEndsWith(name, NS_LITERAL_STRING(".sbd")) || + StringEndsWith(name, NS_LITERAL_STRING(SUMMARY_SUFFIX))); +} + +/** + * We're passed a stream positioned at the start of the message. + * We start reading lines, looking for x-mozilla-keys: headers; If we're + * adding the keyword and we find a header with the desired keyword already + * in it, we don't need to do anything. Likewise, if removing keyword and we + * don't find it,we don't need to do anything. Otherwise, if adding, we need + * to see if there's an x-mozilla-keys header with room for the new keyword. + * If so, we replace the corresponding number of spaces with the keyword. + * If no room, we can't do anything until the folder is compacted and another + * x-mozilla-keys header is added. In that case, we set a property + * on the header, which the compaction code will check. + * This is not true for maildir, however, since it won't require compaction. + */ + +void +nsMsgLocalStoreUtils::ChangeKeywordsHelper(nsIMsgDBHdr *message, + uint64_t desiredOffset, + nsLineBuffer<char> *lineBuffer, + nsTArray<nsCString> &keywordArray, + bool aAdd, + nsIOutputStream *outputStream, + nsISeekableStream *seekableStream, + nsIInputStream *inputStream) +{ + uint32_t bytesWritten; + + for (uint32_t i = 0; i < keywordArray.Length(); i++) + { + nsAutoCString header; + nsAutoCString keywords; + bool done = false; + uint32_t len = 0; + nsAutoCString keywordToWrite(" "); + + keywordToWrite.Append(keywordArray[i]); + seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, desiredOffset); + // need to reset lineBuffer, which is cheaper than creating a new one. + lineBuffer->start = lineBuffer->end = lineBuffer->buf; + bool inKeywordHeader = false; + bool foundKeyword = false; + int64_t offsetToAddKeyword = 0; + bool more; + message->GetMessageSize(&len); + // loop through + while (!done) + { + int64_t lineStartPos; + seekableStream->Tell(&lineStartPos); + // we need to adjust the linestart pos by how much extra the line + // buffer has read from the stream. + lineStartPos -= (lineBuffer->end - lineBuffer->start); + // NS_ReadLine doesn't return line termination chars. + nsCString keywordHeaders; + nsresult rv = NS_ReadLine(inputStream, lineBuffer, keywordHeaders, &more); + if (NS_SUCCEEDED(rv)) + { + if (keywordHeaders.IsEmpty()) + break; // passed headers; no x-mozilla-keywords header; give up. + if (StringBeginsWith(keywordHeaders, + NS_LITERAL_CSTRING(HEADER_X_MOZILLA_KEYWORDS))) + inKeywordHeader = true; + else if (inKeywordHeader && (keywordHeaders.CharAt(0) == ' ' || + keywordHeaders.CharAt(0) == '\t')) + ; // continuation header line + else if (inKeywordHeader) + break; + else + continue; + uint32_t keywordHdrLength = keywordHeaders.Length(); + int32_t startOffset, keywordLength; + // check if we have the keyword + if (MsgFindKeyword(keywordArray[i], keywordHeaders, &startOffset, + &keywordLength)) + { + foundKeyword = true; + if (!aAdd) // if we're removing, remove it, and break; + { + keywordHeaders.Cut(startOffset, keywordLength); + for (int32_t j = keywordLength; j > 0; j--) + keywordHeaders.Append(' '); + seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, lineStartPos); + outputStream->Write(keywordHeaders.get(), keywordHeaders.Length(), + &bytesWritten); + } + offsetToAddKeyword = 0; + // if adding and we already have the keyword, done + done = true; + break; + } + // argh, we need to check all the lines to see if we already have the + // keyword, but if we don't find it, we want to remember the line and + // position where we have room to add the keyword. + if (aAdd) + { + nsAutoCString curKeywordHdr(keywordHeaders); + // strip off line ending spaces. + curKeywordHdr.Trim(" ", false, true); + if (!offsetToAddKeyword && curKeywordHdr.Length() + + keywordToWrite.Length() < keywordHdrLength) + offsetToAddKeyword = lineStartPos + curKeywordHdr.Length(); + } + } + } + if (aAdd && !foundKeyword) + { + if (!offsetToAddKeyword) + message->SetUint32Property("growKeywords", 1); + else + { + seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, + offsetToAddKeyword); + outputStream->Write(keywordToWrite.get(), keywordToWrite.Length(), + &bytesWritten); + } + } + } +} + +nsresult +nsMsgLocalStoreUtils::UpdateFolderFlag(nsIMsgDBHdr *mailHdr, bool bSet, + nsMsgMessageFlagType flag, + nsIOutputStream *fileStream) +{ + uint32_t statusOffset; + uint64_t msgOffset; + nsresult rv = mailHdr->GetStatusOffset(&statusOffset); + // This probably means there's no x-mozilla-status header, so + // we just ignore this. + if (NS_FAILED(rv) || (statusOffset == 0)) + return NS_OK; + rv = mailHdr->GetMessageOffset(&msgOffset); + NS_ENSURE_SUCCESS(rv, rv); + uint64_t statusPos = msgOffset + statusOffset; + nsCOMPtr<nsISeekableStream> seekableStream(do_QueryInterface(fileStream, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + rv = seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, statusPos); + NS_ENSURE_SUCCESS(rv, rv); + char buf[50]; + buf[0] = '\0'; + nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(fileStream, &rv); + NS_ENSURE_SUCCESS(rv, rv); + uint32_t bytesRead; + if (NS_SUCCEEDED(inputStream->Read(buf, X_MOZILLA_STATUS_LEN + 6, + &bytesRead))) + { + buf[bytesRead] = '\0'; + if (strncmp(buf, X_MOZILLA_STATUS, X_MOZILLA_STATUS_LEN) == 0 && + strncmp(buf + X_MOZILLA_STATUS_LEN, ": ", 2) == 0 && + strlen(buf) >= X_MOZILLA_STATUS_LEN + 6) + { + uint32_t flags; + uint32_t bytesWritten; + (void)mailHdr->GetFlags(&flags); + if (!(flags & nsMsgMessageFlags::Expunged)) + { + char *p = buf + X_MOZILLA_STATUS_LEN + 2; + + nsresult errorCode = NS_OK; + flags = nsDependentCString(p).ToInteger(&errorCode, 16); + + uint32_t curFlags; + (void)mailHdr->GetFlags(&curFlags); + flags = (flags & nsMsgMessageFlags::Queued) | + (curFlags & ~nsMsgMessageFlags::RuntimeOnly); + if (bSet) + flags |= flag; + else + flags &= ~flag; + } + else + { + flags &= ~nsMsgMessageFlags::RuntimeOnly; + } + seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, statusPos); + // We are filing out x-mozilla-status flags here + PR_snprintf(buf, sizeof(buf), X_MOZILLA_STATUS_FORMAT, + flags & 0x0000FFFF); + int32_t lineLen = PL_strlen(buf); + uint64_t status2Pos = statusPos + lineLen; + fileStream->Write(buf, lineLen, &bytesWritten); + + if (flag & 0xFFFF0000) + { + // Time to update x-mozilla-status2, + // first find it by finding end of previous line, see bug 234935. + seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, status2Pos); + do + { + rv = inputStream->Read(buf, 1, &bytesRead); + status2Pos++; + } while (NS_SUCCEEDED(rv) && (*buf == '\n' || *buf == '\r')); + status2Pos--; + seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, status2Pos); + if (NS_SUCCEEDED(inputStream->Read(buf, X_MOZILLA_STATUS2_LEN + 10, + &bytesRead))) + { + if (strncmp(buf, X_MOZILLA_STATUS2, X_MOZILLA_STATUS2_LEN) == 0 && + strncmp(buf + X_MOZILLA_STATUS2_LEN, ": ", 2) == 0 && + strlen(buf) >= X_MOZILLA_STATUS2_LEN + 10) + { + uint32_t dbFlags; + (void)mailHdr->GetFlags(&dbFlags); + dbFlags &= 0xFFFF0000; + seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, status2Pos); + PR_snprintf(buf, sizeof(buf), X_MOZILLA_STATUS2_FORMAT, dbFlags); + fileStream->Write(buf, PL_strlen(buf), &bytesWritten); + } + } + } + } + else + { +#ifdef DEBUG + printf("Didn't find %s where expected at position %ld\n" + "instead, found %s.\n", + X_MOZILLA_STATUS, (long) statusPos, buf); +#endif + rv = NS_ERROR_FAILURE; + } + } + else + rv = NS_ERROR_FAILURE; + return rv; +} + +/** + * Returns true if there is enough space on disk. + * + * @param aFile Any file in the message store that is on a logical + * disk volume so that it can be queried for disk space. + * @param aSpaceRequested The size of free space there must be on the disk + * to return true. + */ +bool +nsMsgLocalStoreUtils::DiskSpaceAvailableInStore(nsIFile *aFile, uint64_t aSpaceRequested) +{ + int64_t diskFree; + nsresult rv = aFile->GetDiskSpaceAvailable(&diskFree); + if (NS_SUCCEEDED(rv)) { +#ifdef DEBUG + printf("GetDiskSpaceAvailable returned: %lld bytes\n", (long long)diskFree); +#endif + // When checking for disk space available, take into consideration + // possible database changes, therefore ask for a little more + // (EXTRA_SAFETY_SPACE) than what the requested size is. Also, due to disk + // sector sizes, allocation blocks, etc. The space "available" may be greater + // than the actual space usable. + return ((aSpaceRequested + EXTRA_SAFETY_SPACE) < (uint64_t) diskFree); + } else if (rv == NS_ERROR_NOT_IMPLEMENTED) { + // The call to GetDiskSpaceAvailable is not implemented! + // This will happen on certain platforms where GetDiskSpaceAvailable + // is not implemented. Since people on those platforms still need + // to download mail, we will simply bypass the disk-space check. + // + // We'll leave a debug message to warn people. +#ifdef DEBUG + printf("Call to GetDiskSpaceAvailable FAILED because it is not implemented!\n"); +#endif + return true; + } else { + printf("Call to GetDiskSpaceAvailable FAILED!\n"); + return false; + } +} + +/** + * Resets forceReparse in the database. + * + * @param aMsgDb The database to reset. + */ +void +nsMsgLocalStoreUtils::ResetForceReparse(nsIMsgDatabase *aMsgDB) +{ + if (aMsgDB) + { + nsCOMPtr<nsIDBFolderInfo> folderInfo; + aMsgDB->GetDBFolderInfo(getter_AddRefs(folderInfo)); + if (folderInfo) + folderInfo->SetBooleanProperty("forceReparse", false); + } +} |