diff --git a/CMakeLists.txt b/CMakeLists.txt index d08880a..67a15bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,77 +1,77 @@ cmake_minimum_required(VERSION 3.5) -set(PIM_VERSION "5.14.40") +set(PIM_VERSION "5.14.41") project(KMbox VERSION ${PIM_VERSION}) # ECM setup set(KF5_MIN_VERSION "5.70.0") find_package(ECM ${KF5_MIN_VERSION} CONFIG REQUIRED) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) include(KDEInstallDirs) include(KDECMakeSettings) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(GenerateExportHeader) include(ECMGenerateHeaders) include(ECMGeneratePriFile) include(ECMSetupVersion) include(FeatureSummary) include(ECMQtDeclareLoggingCategory) set(KMBOX_LIB_VERSION ${PIM_VERSION}) set(KMIME_LIB_VERSION "5.14.40") ecm_setup_version(PROJECT VARIABLE_PREFIX KMBOX VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kmbox_version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5MboxConfigVersion.cmake" SOVERSION 5 ) ########### Find packages ########### find_package(KF5Mime ${KMIME_LIB_VERSION} CONFIG REQUIRED) if (EXISTS "${CMAKE_SOURCE_DIR}/.git") add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x050f00) add_definitions(-DKF_DISABLE_DEPRECATED_BEFORE_AND_AT=0x054700) endif() add_definitions(-DQT_NO_FOREACH) add_definitions(-DQT_NO_KEYWORDS) ########### Targets ########### add_subdirectory(src) if(BUILD_TESTING) add_subdirectory(autotests) endif() ########### CMake Config Files ########### set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KF5Mbox") configure_package_config_file( "${CMAKE_CURRENT_SOURCE_DIR}/KF5MboxConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KF5MboxConfig.cmake" INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KF5MboxConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KF5MboxConfigVersion.cmake" DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) install(EXPORT KF5MboxTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KF5MboxTargets.cmake NAMESPACE KF5::) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/kmbox_version.h DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5} COMPONENT Devel ) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/src/mbox.cpp b/src/mbox.cpp index fbfad31..7458bf1 100644 --- a/src/mbox.cpp +++ b/src/mbox.cpp @@ -1,711 +1,711 @@ /* Copyright (c) 1996-1998 Stefan Taferner Copyright (c) 2009 Bertjan Broeksema This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. NOTE: Most of the code inside here is an slightly adjusted version of kdepim/kmail/kmfoldermbox.cpp. This is why I added a line for Stefan Taferner. Bertjan Broeksema, april 2009 */ #include "mbox.h" #include "mbox_p.h" #include "mboxentry_p.h" #include "kmbox_debug.h" #include #include #include #include using namespace KMBox; /// public methods. MBox::MBox() : d(new MBoxPrivate(this)) { // Set some sane defaults d->mFileLocked = false; d->mLockType = None; d->mUnlockTimer.setInterval(0); d->mUnlockTimer.setSingleShot(true); } MBox::~MBox() { if (d->mFileLocked) { unlock(); } d->close(); delete d; } // Appended entries works as follows: When an mbox file is loaded from disk, // d->mInitialMboxFileSize is set to the file size at that moment. New entries // are stored in memory (d->mAppendedEntries). The initial file size and the size // of the buffer determine the offset for the next message to append. MBoxEntry MBox::appendMessage(const KMime::Message::Ptr &entry) { // It doesn't make sense to add entries when we don't have an reference file. Q_ASSERT(!d->mMboxFile.fileName().isEmpty()); const QByteArray rawEntry = MBoxPrivate::escapeFrom(entry->encodedContent()); if (rawEntry.size() <= 0) { qCDebug(KMBOX_LOG) << "Message added to folder `" << d->mMboxFile.fileName() << "' contains no data. Ignoring it."; return MBoxEntry(); } int nextOffset = d->mAppendedEntries.size(); // Offset of the appended message // Make sure the byte array is large enough to check for an end character. // Then check if the required newlines are there. if (nextOffset < 1 && d->mMboxFile.size() > 0) { // Empty, add one empty line d->mAppendedEntries.append("\n"); ++nextOffset; } else if (nextOffset == 1 && d->mAppendedEntries.at(0) != '\n') { // This should actually not happen, but catch it anyway. if (d->mMboxFile.size() < 0) { d->mAppendedEntries.append("\n"); ++nextOffset; } } else if (nextOffset >= 2) { if (d->mAppendedEntries.at(nextOffset - 1) != '\n') { if (d->mAppendedEntries.at(nextOffset) != '\n') { d->mAppendedEntries.append("\n\n"); nextOffset += 2; } else { d->mAppendedEntries.append("\n"); ++nextOffset; } } } const QByteArray separator = MBoxPrivate::mboxMessageSeparator(rawEntry); d->mAppendedEntries.append(separator); d->mAppendedEntries.append(rawEntry); if (rawEntry[rawEntry.size() - 1] != '\n') { d->mAppendedEntries.append("\n\n"); } else { d->mAppendedEntries.append("\n"); } MBoxEntry resultEntry; resultEntry.d->mOffset = d->mInitialMboxFileSize + nextOffset; resultEntry.d->mMessageSize = rawEntry.size(); resultEntry.d->mSeparatorSize = separator.size(); d->mEntries << resultEntry; return resultEntry; } MBoxEntry::List MBox::entries(const MBoxEntry::List &deletedEntries) const { if (deletedEntries.isEmpty()) { // fast path return d->mEntries; } MBoxEntry::List result; result.reserve(d->mEntries.size()); for (const MBoxEntry &entry : qAsConst(d->mEntries)) { if (!deletedEntries.contains(entry)) { result << entry; } } return result; } QString MBox::fileName() const { return d->mMboxFile.fileName(); } bool MBox::load(const QString &fileName) { if (d->mFileLocked) { return false; } d->initLoad(fileName); if (!lock()) { qCDebug(KMBOX_LOG) << "Failed to lock"; return false; } d->mInitialMboxFileSize = d->mMboxFile.size(); // AFTER the file has been locked QByteArray line; QByteArray prevSeparator; quint64 offs = 0; // The offset of the next message to read. while (!d->mMboxFile.atEnd()) { quint64 pos = d->mMboxFile.pos(); line = d->mMboxFile.readLine(); // if atEnd, use mail only if there was a separator line at all, // otherwise it's not a valid mbox if (d->isMBoxSeparator(line) || (d->mMboxFile.atEnd() && (prevSeparator.size() != 0))) { // if we are the at the file end, update pos to not forget the last line if (d->mMboxFile.atEnd()) { pos = d->mMboxFile.pos(); } // Found the separator or at end of file, the message starts at offs quint64 msgSize = pos - offs; if (pos > 0) { // This is not the separator of the first mail in the file. If pos == 0 // than we matched the separator of the first mail in the file. MBoxEntry entry; entry.d->mOffset = offs; entry.d->mSeparatorSize = prevSeparator.size(); entry.d->mMessageSize = msgSize - 1; // Don't add the separator size and the newline up to the message size. entry.d->mMessageSize -= prevSeparator.size() + 1; d->mEntries << entry; } if (d->isMBoxSeparator(line)) { prevSeparator = line; } offs += msgSize; // Mark the beginning of the next message. } } // FIXME: What if unlock fails? // if no separator was found, the file is still valid if it is empty const bool val = unlock() && (!prevSeparator.isEmpty() || (d->mMboxFile.size() == 0)); return val; } bool MBox::lock() { if (d->mMboxFile.fileName().isEmpty()) { return false; // We cannot lock if there is no file loaded. } // We can't load another file when the mbox currently is locked so if d->mFileLocked // is true atm just return true. if (locked()) { return true; } if (d->mLockType == None) { d->mFileLocked = true; if (d->open()) { d->startTimerIfNeeded(); return true; } d->mFileLocked = false; return false; } QStringList args; int rc = 0; switch (d->mLockType) { case ProcmailLockfile: args << QStringLiteral("-l20") << QStringLiteral("-r5"); if (!d->mLockFileName.isEmpty()) { args << QString::fromLocal8Bit(QFile::encodeName(d->mLockFileName)); } else { args << QString::fromLocal8Bit(QFile::encodeName(d->mMboxFile.fileName() +QLatin1String(".lock"))); } rc = QProcess::execute(QStringLiteral("lockfile"), args); if (rc != 0) { qCDebug(KMBOX_LOG) << "lockfile -l20 -r5 " << d->mMboxFile.fileName() << ": Failed (" << rc << ") switching to read only mode"; d->mReadOnly = true; // In case the MBox object was created read/write we // set it to read only when locking failed. } else { d->mFileLocked = true; } break; case MuttDotlock: args << QString::fromLocal8Bit(QFile::encodeName(d->mMboxFile.fileName())); rc = QProcess::execute(QStringLiteral("mutt_dotlock"), args); if (rc != 0) { qCDebug(KMBOX_LOG) << "mutt_dotlock " << d->mMboxFile.fileName() << ": Failed (" << rc << ") switching to read only mode"; d->mReadOnly = true; // In case the MBox object was created read/write we // set it to read only when locking failed. } else { d->mFileLocked = true; } break; case MuttDotlockPrivileged: args << QStringLiteral("-p") << QString::fromLocal8Bit(QFile::encodeName(d->mMboxFile.fileName())); rc = QProcess::execute(QStringLiteral("mutt_dotlock"), args); if (rc != 0) { qCDebug(KMBOX_LOG) << "mutt_dotlock -p " << d->mMboxFile.fileName() << ":" << ": Failed (" << rc << ") switching to read only mode"; d->mReadOnly = true; } else { d->mFileLocked = true; } break; case None: d->mFileLocked = true; break; default: break; } if (d->mFileLocked) { if (!d->open()) { const bool unlocked = unlock(); Q_ASSERT(unlocked); // If this fails we're in trouble. Q_UNUSED(unlocked); } } d->startTimerIfNeeded(); return d->mFileLocked; } bool MBox::locked() const { return d->mFileLocked; } static bool lessThanByOffset(const MBoxEntry &left, const MBoxEntry &right) { return left.messageOffset() < right.messageOffset(); } -bool MBox::purge(const MBoxEntry::List &deletedEntries, QList *movedEntries) +bool MBox::purge(const MBoxEntry::List &deletedEntries, QVector *movedEntries) { if (d->mMboxFile.fileName().isEmpty() || d->mReadOnly) { return false; // No file loaded yet or it's readOnly } if (deletedEntries.isEmpty()) { return true; // Nothing to do. } if (!lock()) { return false; } for (const MBoxEntry &entry : qAsConst(deletedEntries)) { d->mMboxFile.seek(entry.messageOffset()); const QByteArray line = d->mMboxFile.readLine(); if (!d->isMBoxSeparator(line)) { qCDebug(KMBOX_LOG) << "Found invalid separator at:" << entry.messageOffset(); unlock(); return false; // The file is messed up or the index is incorrect. } } // All entries are deleted, so just resize the file to a size of 0. if (deletedEntries.size() == d->mEntries.size()) { d->mEntries.clear(); d->mMboxFile.resize(0); qCDebug(KMBOX_LOG) << "Purge completed successfully, unlocking the file."; return unlock(); } std::sort(d->mEntries.begin(), d->mEntries.end(), lessThanByOffset); quint64 writeOffset = 0; bool writeOffSetInitialized = false; MBoxEntry::List resultingEntryList; - QList tmpMovedEntries; + QVector tmpMovedEntries; quint64 origFileSize = d->mMboxFile.size(); QVectorIterator i(d->mEntries); while (i.hasNext()) { MBoxEntry entry = i.next(); if (deletedEntries.contains(entry) && !writeOffSetInitialized) { writeOffset = entry.messageOffset(); writeOffSetInitialized = true; } else if (writeOffSetInitialized && writeOffset < entry.messageOffset() && !deletedEntries.contains(entry)) { // The current message doesn't have to be deleted, but must be moved. // First determine the size of the entry that must be moved. quint64 entrySize = 0; if (i.hasNext()) { entrySize = i.next().messageOffset() - entry.messageOffset(); i.previous(); // Go back to make sure that we also handle the next entry. } else { entrySize = origFileSize - entry.messageOffset(); } Q_ASSERT(entrySize > 0); // MBox entries really cannot have a size <= 0; // we map the whole area of the file starting at the writeOffset up to the // message that have to be moved into memory. This includes eventually the // messages that are the deleted between the first deleted message // encountered and the message that has to be moved. quint64 mapSize = entry.messageOffset() + entrySize - writeOffset; // Now map writeOffSet + mapSize into mem. uchar *memArea = d->mMboxFile.map(writeOffset, mapSize); // Now read the entry that must be moved to writeOffset. quint64 startOffset = entry.messageOffset() - writeOffset; memmove(memArea, memArea + startOffset, entrySize); d->mMboxFile.unmap(memArea); MBoxEntry resultEntry; resultEntry.d->mOffset = writeOffset; resultEntry.d->mSeparatorSize = entry.separatorSize(); resultEntry.d->mMessageSize = entry.messageSize(); resultingEntryList << resultEntry; tmpMovedEntries << MBoxEntry::Pair(MBoxEntry(entry.messageOffset()), MBoxEntry(resultEntry.messageOffset())); writeOffset += entrySize; } else if (!deletedEntries.contains(entry)) { // Unmoved and not deleted entry, can only ocure before the first deleted // entry. Q_ASSERT(!writeOffSetInitialized); resultingEntryList << entry; } } // Chop off remaining entry bits. d->mMboxFile.resize(writeOffset); d->mEntries = resultingEntryList; qCDebug(KMBOX_LOG) << "Purge completed successfully, unlocking the file."; if (movedEntries) { *movedEntries = tmpMovedEntries; } return unlock(); // FIXME: What if this fails? It will return false but the // file has changed. } QByteArray MBox::readRawMessage(const MBoxEntry &entry) { const bool wasLocked = locked(); if (!wasLocked) { if (!lock()) { return QByteArray(); } } // TODO: Add error handling in case locking failed. quint64 offset = entry.messageOffset(); Q_ASSERT(d->mFileLocked); Q_ASSERT(d->mMboxFile.isOpen()); Q_ASSERT((d->mInitialMboxFileSize + d->mAppendedEntries.size()) > offset); QByteArray message; if (offset < d->mInitialMboxFileSize) { d->mMboxFile.seek(offset); QByteArray line = d->mMboxFile.readLine(); if (!d->isMBoxSeparator(line)) { qCDebug(KMBOX_LOG) << "[MBox::readEntry] Invalid entry at:" << offset; if (!wasLocked) { unlock(); } return QByteArray(); // The file is messed up or the index is incorrect. } line = d->mMboxFile.readLine(); while (!d->isMBoxSeparator(line)) { message += line; if (d->mMboxFile.atEnd()) { break; } line = d->mMboxFile.readLine(); } } else { offset -= d->mInitialMboxFileSize; if (offset > static_cast(d->mAppendedEntries.size())) { if (!wasLocked) { unlock(); } return QByteArray(); } QBuffer buffer(&(d->mAppendedEntries)); buffer.open(QIODevice::ReadOnly); buffer.seek(offset); QByteArray line = buffer.readLine(); if (!d->isMBoxSeparator(line)) { qCDebug(KMBOX_LOG) << "[MBox::readEntry] Invalid appended entry at:" << offset; if (!wasLocked) { unlock(); } return QByteArray(); // The file is messed up or the index is incorrect. } line = buffer.readLine(); while (!d->isMBoxSeparator(line) && !buffer.atEnd()) { message += line; line = buffer.readLine(); } } // Remove te last '\n' added by writeEntry. if (message.endsWith('\n')) { message.chop(1); } MBoxPrivate::unescapeFrom(message.data(), message.size()); if (!wasLocked) { if (!d->startTimerIfNeeded()) { const bool unlocked = unlock(); Q_ASSERT(unlocked); Q_UNUSED(unlocked); } } return message; } KMime::Message *MBox::readMessage(const MBoxEntry &entry) { const QByteArray message = readRawMessage(entry); if (message.isEmpty()) { return nullptr; } KMime::Message *mail = new KMime::Message(); mail->setContent(KMime::CRLFtoLF(message)); mail->parse(); return mail; } QByteArray MBox::readMessageHeaders(const MBoxEntry &entry) { const bool wasLocked = d->mFileLocked; if (!wasLocked) { if (!lock()) { qCDebug(KMBOX_LOG) << "Failed to lock"; return QByteArray(); } } const quint64 offset = entry.messageOffset(); Q_ASSERT(d->mFileLocked); Q_ASSERT(d->mMboxFile.isOpen()); Q_ASSERT((d->mInitialMboxFileSize + d->mAppendedEntries.size()) > offset); QByteArray headers; if (offset < d->mInitialMboxFileSize) { d->mMboxFile.seek(offset); QByteArray line = d->mMboxFile.readLine(); while (line[0] != '\n' && !d->mMboxFile.atEnd()) { headers += line; line = d->mMboxFile.readLine(); } } else { QBuffer buffer(&(d->mAppendedEntries)); buffer.open(QIODevice::ReadOnly); buffer.seek(offset - d->mInitialMboxFileSize); QByteArray line = buffer.readLine(); while (line[0] != '\n' && !buffer.atEnd()) { headers += line; line = buffer.readLine(); } } if (!wasLocked) { unlock(); } return headers; } bool MBox::save(const QString &fileName) { if (!fileName.isEmpty() && QUrl::fromUserInput(fileName).toLocalFile() != d->mMboxFile.fileName()) { if (!d->mMboxFile.copy(fileName)) { return false; } else { // if the original file was read-only, also the copied file is read-only // Let's make it writable now QFile::setPermissions(fileName, d->mMboxFile.permissions() | QFile::WriteOwner); } if (d->mAppendedEntries.isEmpty()) { return true; // Nothing to do } QFile otherFile(fileName); Q_ASSERT(otherFile.exists()); if (!otherFile.open(QIODevice::ReadWrite)) { return false; } otherFile.seek(d->mMboxFile.size()); otherFile.write(d->mAppendedEntries); // Don't clear mAppendedEntries and don't update mInitialFileSize. These // are still valid for the original file. return true; } if (d->mReadOnly) { return false; } if (d->mAppendedEntries.isEmpty()) { return true; // Nothing to do. } if (!lock()) { return false; } Q_ASSERT(d->mMboxFile.isOpen()); d->mMboxFile.seek(d->mMboxFile.size()); d->mMboxFile.write(d->mAppendedEntries); d->mAppendedEntries.clear(); d->mInitialMboxFileSize = d->mMboxFile.size(); return unlock(); } bool MBox::setLockType(LockType ltype) { if (d->mFileLocked) { qCDebug(KMBOX_LOG) << "File is currently locked."; return false; // Don't change the method if the file is currently locked. } switch (ltype) { case ProcmailLockfile: if (QStandardPaths::findExecutable(QStringLiteral("lockfile")).isEmpty()) { qCDebug(KMBOX_LOG) << "Could not find the lockfile executable"; return false; } break; case MuttDotlock: // fall through case MuttDotlockPrivileged: if (QStandardPaths::findExecutable(QStringLiteral("mutt_dotlock")).isEmpty()) { qCDebug(KMBOX_LOG) << "Could not find the mutt_dotlock executable"; return false; } break; default: break; // We assume fcntl available and lock_none doesn't need a check. } d->mLockType = ltype; return true; } void MBox::setLockFile(const QString &lockFile) { d->mLockFileName = lockFile; } void MBox::setUnlockTimeout(int msec) { d->mUnlockTimer.setInterval(msec); } bool MBox::unlock() { if (d->mLockType == None && !d->mFileLocked) { d->mFileLocked = false; d->mMboxFile.close(); return true; } int rc = 0; QStringList args; switch (d->mLockType) { case ProcmailLockfile: // QFile::remove returns true on success so negate the result. if (!d->mLockFileName.isEmpty()) { rc = !QFile(d->mLockFileName).remove(); } else { rc = !QFile(d->mMboxFile.fileName() + QLatin1String(".lock")).remove(); } break; case MuttDotlock: args << QStringLiteral("-u") << QString::fromLocal8Bit(QFile::encodeName(d->mMboxFile.fileName())); rc = QProcess::execute(QStringLiteral("mutt_dotlock"), args); break; case MuttDotlockPrivileged: args << QStringLiteral("-u") << QStringLiteral("-p") << QString::fromLocal8Bit(QFile::encodeName(d->mMboxFile.fileName())); rc = QProcess::execute(QStringLiteral("mutt_dotlock"), args); break; case None: // Fall through. default: break; } if (rc == 0) { // Unlocking succeeded d->mFileLocked = false; } d->mMboxFile.close(); return !d->mFileLocked; } void MBox::setReadOnly(bool ro) { d->mReadOnly = ro; } bool MBox::isReadOnly() const { return d->mReadOnly; } diff --git a/src/mbox.h b/src/mbox.h index 7ee6ee5..dc94e2a 100644 --- a/src/mbox.h +++ b/src/mbox.h @@ -1,267 +1,267 @@ /* Copyright (c) 2009 Bertjan Broeksema This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KMBOX_MBOX_H #define KMBOX_MBOX_H #include "kmbox_export.h" #include "mboxentry.h" #include namespace KMBox { class MBoxPrivate; /** * @short A class to access mail storages in MBox format. * * @author Bertjan Broeksema * @since 4.6 */ class KMBOX_EXPORT MBox { public: /** * Describes the type of locking that will be used. */ enum LockType { ProcmailLockfile, MuttDotlock, MuttDotlockPrivileged, None }; /** * Creates a new mbox object. */ MBox(); /** * Destroys the mbox object. * * The file will be unlocked if it is still open. */ ~MBox(); /** * Appends @p message to the MBox and returns the corresponding mbox entry for it. * You must load a mbox file by making a call to load( const QString& ) before * appending entries. * The returned mbox entry is only valid for that particular file. * * @param message The message to append to the mbox. * @return the corresponding mbox entry for the message in the file or an invalid mbox entry * if the message was not added. */ Q_REQUIRED_RESULT MBoxEntry appendMessage(const KMime::Message::Ptr &message); /** * Retrieve the mbox entry objects for all emails from the file except the * @p deleteEntries. * The @p deletedEntries should be a list of mbox entries with offsets of deleted messages. * @param deletedEntries list of mbox entries that have been deleted and need not be retrieved * Note: One must call load() before calling this method. */ Q_REQUIRED_RESULT MBoxEntry::List entries(const MBoxEntry::List &deletedEntries = MBoxEntry::List()) const; /** * Returns the file name that was passed to the last call to load(). */ Q_REQUIRED_RESULT QString fileName() const; /** * Loads the raw mbox data from disk into the current MBox object. Messages * already present are not preserved. This method does not load the * full messages into memory but only the offsets of the messages and their * sizes. If the file currently is locked this method will do nothing and * return false. Appended messages that are not written yet will get lost. * * @param fileName the name of the mbox on disk. * @return true, if successful, false on error. * * @see save( const QString & ) */ Q_REQUIRED_RESULT bool load(const QString &fileName); /** * Locks the mbox file using the configured lock method. This can be used * for consecutive calls to readMessage and readMessageHeaders. Calling lock() * before these calls prevents the mbox file being locked for every call. * * NOTE: Even when the lock method is None the mbox is internally marked as * locked. This means that it must be unlocked before calling load(). * * @return true if locked successful, false on error. * * @see setLockType( LockType ), unlock() */ Q_REQUIRED_RESULT bool lock(); /** * Returns whether or not the mbox currently is locked. */ Q_REQUIRED_RESULT bool locked() const; /** * Removes all messages for the given mbox entries from the current reference file * (the file that is loaded with load( const QString & ). * This method will first check if all lines at the offsets are actually * separator lines if this is not then no message will be deleted to prevent * corruption. * * @param deletedEntries The mbox entries of the messages that should be removed from * the file. * @param movedEntries Optional list for storing pairs of mbox entries that got moved * within the file due to the deletions. * The @c first member of the pair is the entry with the original offsets * the @c second member is the entry with the new (current) offset * * @return true if all offsets refer to a mbox separator line and a file was * loaded, false otherwise. If the latter, the physical file has * not changed. */ - Q_REQUIRED_RESULT bool purge(const MBoxEntry::List &deletedEntries, QList *movedEntries = nullptr); + Q_REQUIRED_RESULT bool purge(const MBoxEntry::List &deletedEntries, QVector *movedEntries = nullptr); /** * Reads the entire message from the file for the given mbox @p entry. If the * mbox file is not locked this method will lock the file before reading and * unlock it after reading. If the file already is locked, it will not * unlock the file after reading the entry. * * @param entry The entry in the mbox file. * @return Message for the given entry or 0 if the file could not be locked * or the entry offset > fileSize. * * @see lock(), unlock() */ KMime::Message *readMessage(const MBoxEntry &entry); /** * Reads the headers of the message for the given mbox @p entry. If the * mbox file is not locked this method will lock the file before reading and * unlock it after reading. If the file already is locked, it will not * unlock the file after reading the entry. * * @param entry The entry in the mbox file. * @return QByteArray containing the raw message header data. * * @see lock(), unlock() */ Q_REQUIRED_RESULT QByteArray readMessageHeaders(const MBoxEntry &entry); /** * Reads the entire message from the file for the given mbox @p entry. If the * mbox file is not locked this method will lock the file before reading and * unlock it after reading. If the file already is locked, it will not * unlock the file after reading the entry. * * @param entry The entry in the mbox file. * @return QByteArray containing the raw message data. * * @see lock(), unlock() */ Q_REQUIRED_RESULT QByteArray readRawMessage(const MBoxEntry &entry); /** * Writes the mbox to disk. If the fileName is empty only appended messages * will be written to the file that was passed to load( const QString & ). * Otherwise the contents of the file that was loaded with load is copied to * @p fileName first. * * @param fileName the name of the file * @return true if the save was successful; false otherwise. * * @see load( const QString & ) */ Q_REQUIRED_RESULT bool save(const QString &fileName = QString()); /** * Sets the locktype that should be used for locking the mbox file. If the * new LockType cannot be used (e.g. the lockfile executable could not be * found) the LockType will not be changed. * @param ltype the locktype to set * This method will not do anything if the mbox object is currently locked * to make sure that it doesn't leave a locked file for one of the lockfile * / mutt_dotlock methods. */ Q_REQUIRED_RESULT bool setLockType(LockType ltype); /** * Sets the lockfile that should be used by the procmail or the KDE lock * file method. If this method is not called and one of the before mentioned * lock methods is used the name of the lock file will be equal to * MBOXFILENAME.lock. * @param lockFile the lockfile to set */ void setLockFile(const QString &lockFile); /** * By default the unlock method will directly unlock the file. However this * is expensive in case of many consecutive calls to readEntry. Setting the * time out to a non zero value will keep the lock open until the timeout has * passed. On each read the timer will be reset. * @param msec the time out to set for file lock */ void setUnlockTimeout(int msec); /** * Unlock the mbox file. * * @return true if the unlock was successful, false otherwise. * * @see lock() */ Q_REQUIRED_RESULT bool unlock(); /** * Set the access mode of the mbox file to read only. * * If this is set to true, the mbox file can only be read from disk. * When the mbox file given in load() can not be opened in readWrite mode, * but can be opened in readOnly mode, this flag is automatically set to true. * You can still append messages, which are stored in memory * until save() is called, but the mbox can not be saved/purged to itself. * However it is possible to save it to a different file. * @param ro the readOnly flag to use * * @see save( const QString & ) * * @since 4.14.5 */ void setReadOnly(bool ro = true); /** * Returns if the current access mode is set to readOnly. * * The access mode can either be set explicitly with setReadOnly() or * implicitly by calling load() on a readOnly file. * * @since 4.14.5 */ Q_REQUIRED_RESULT bool isReadOnly() const; private: //@cond PRIVATE Q_DISABLE_COPY(MBox) friend class MBoxPrivate; MBoxPrivate *const d; //@endcond }; } #endif // KMBOX_MBOX_H