15#include "mboxentry_p.h"
17#include "kmbox_debug.h"
18#include <QStandardPaths>
28 : d(new MBoxPrivate(this))
31 d->mFileLocked =
false;
34 d->mUnlockTimer.setInterval(0);
35 d->mUnlockTimer.setSingleShot(
true);
54 Q_ASSERT(!d->mMboxFile.fileName().isEmpty());
56 const QByteArray rawEntry = MBoxPrivate::escapeFrom(entry->encodedContent());
58 if (rawEntry.
size() <= 0) {
59 qCDebug(KMBOX_LOG) <<
"Message added to folder `" << d->mMboxFile.fileName() <<
"' contains no data. Ignoring it.";
63 int nextOffset = d->mAppendedEntries.size();
67 if (nextOffset < 1 && d->mMboxFile.size() > 0) {
68 d->mAppendedEntries.append(
"\n");
70 }
else if (nextOffset == 1 && d->mAppendedEntries.at(0) !=
'\n') {
72 if (d->mMboxFile.size() < 0) {
73 d->mAppendedEntries.append(
"\n");
76 }
else if (nextOffset >= 2) {
77 if (d->mAppendedEntries.at(nextOffset - 1) !=
'\n') {
78 if (d->mAppendedEntries.at(nextOffset) !=
'\n') {
79 d->mAppendedEntries.append(
"\n\n");
82 d->mAppendedEntries.append(
"\n");
88 const QByteArray separator = MBoxPrivate::mboxMessageSeparator(rawEntry);
89 d->mAppendedEntries.append(separator);
90 d->mAppendedEntries.append(rawEntry);
91 if (rawEntry[rawEntry.
size() - 1] !=
'\n') {
92 d->mAppendedEntries.append(
"\n\n");
94 d->mAppendedEntries.append(
"\n");
98 resultEntry.d->mOffset = d->mInitialMboxFileSize + nextOffset;
99 resultEntry.d->mMessageSize = rawEntry.
size();
100 resultEntry.d->mSeparatorSize = separator.
size();
101 d->mEntries << resultEntry;
108 if (deletedEntries.
isEmpty()) {
114 result.
reserve(d->mEntries.size());
116 for (
const MBoxEntry &entry : std::as_const(d->mEntries)) {
117 if (!deletedEntries.
contains(entry)) {
127 return d->mMboxFile.fileName();
132 if (d->mFileLocked) {
139 qCDebug(KMBOX_LOG) <<
"Failed to lock";
143 d->mInitialMboxFileSize = d->mMboxFile.size();
149 while (!d->mMboxFile.atEnd()) {
150 quint64 pos = d->mMboxFile.pos();
152 line = d->mMboxFile.readLine();
156 if (d->isMBoxSeparator(line) || (d->mMboxFile.atEnd() && (prevSeparator.
size() != 0))) {
158 if (d->mMboxFile.atEnd()) {
159 pos = d->mMboxFile.pos();
163 quint64 msgSize = pos - offs;
169 entry.d->mOffset = offs;
170 entry.d->mSeparatorSize = prevSeparator.
size();
171 entry.d->mMessageSize = msgSize - 1;
174 entry.d->mMessageSize -= prevSeparator.
size() + 1;
176 d->mEntries << entry;
179 if (d->isMBoxSeparator(line)) {
180 prevSeparator = line;
189 const bool val =
unlock() && (!prevSeparator.
isEmpty() || (d->mMboxFile.size() == 0));
195 if (d->mMboxFile.fileName().isEmpty()) {
205 if (d->mLockType == None) {
206 d->mFileLocked =
true;
208 d->startTimerIfNeeded();
212 d->mFileLocked =
false;
219 switch (d->mLockType) {
220 case ProcmailLockfile:
221 args << QStringLiteral(
"-l20") << QStringLiteral(
"-r5");
222 if (!d->mLockFileName.isEmpty()) {
230 qCDebug(KMBOX_LOG) <<
"lockfile -l20 -r5 " << d->mMboxFile.fileName() <<
": Failed (" << rc <<
") switching to read only mode";
234 d->mFileLocked =
true;
243 qCDebug(KMBOX_LOG) <<
"mutt_dotlock " << d->mMboxFile.fileName() <<
": Failed (" << rc <<
") switching to read only mode";
247 d->mFileLocked =
true;
251 case MuttDotlockPrivileged:
256 qCDebug(KMBOX_LOG) <<
"mutt_dotlock -p " << d->mMboxFile.fileName() <<
":"
257 <<
": Failed (" << rc <<
") switching to read only mode";
260 d->mFileLocked =
true;
265 d->mFileLocked =
true;
271 if (d->mFileLocked) {
273 const bool unlocked =
unlock();
279 d->startTimerIfNeeded();
280 return d->mFileLocked;
285 return d->mFileLocked;
290 return left.messageOffset() < right.messageOffset();
295 if (d->mMboxFile.fileName().isEmpty() || d->mReadOnly) {
299 if (deletedEntries.
isEmpty()) {
307 for (
const MBoxEntry &entry : std::as_const(deletedEntries)) {
308 d->mMboxFile.seek(entry.messageOffset());
309 const QByteArray line = d->mMboxFile.readLine();
311 if (!d->isMBoxSeparator(line)) {
312 qCDebug(KMBOX_LOG) <<
"Found invalid separator at:" << entry.messageOffset();
319 if (deletedEntries.
size() == d->mEntries.size()) {
321 d->mMboxFile.resize(0);
322 qCDebug(KMBOX_LOG) <<
"Purge completed successfully, unlocking the file.";
326 std::sort(d->mEntries.begin(), d->mEntries.end(), lessThanByOffset);
327 quint64 writeOffset = 0;
328 bool writeOffSetInitialized =
false;
332 quint64 origFileSize = d->mMboxFile.
size();
338 if (deletedEntries.
contains(entry) && !writeOffSetInitialized) {
340 writeOffSetInitialized =
true;
341 }
else if (writeOffSetInitialized && writeOffset < entry.
messageOffset() && !deletedEntries.
contains(entry)) {
344 quint64 entrySize = 0;
352 Q_ASSERT(entrySize > 0);
358 quint64 mapSize = entry.
messageOffset() + entrySize - writeOffset;
361 uchar *memArea = d->mMboxFile.map(writeOffset, mapSize);
365 memmove(memArea, memArea + startOffset, entrySize);
367 d->mMboxFile.unmap(memArea);
370 resultEntry.d->mOffset = writeOffset;
374 resultingEntryList << resultEntry;
376 writeOffset += entrySize;
377 }
else if (!deletedEntries.
contains(entry)) {
380 Q_ASSERT(!writeOffSetInitialized);
381 resultingEntryList << entry;
386 d->mMboxFile.resize(writeOffset);
387 d->mEntries = resultingEntryList;
389 qCDebug(KMBOX_LOG) <<
"Purge completed successfully, unlocking the file.";
391 *movedEntries = tmpMovedEntries;
399 const bool wasLocked =
locked();
410 Q_ASSERT(d->mFileLocked);
411 Q_ASSERT(d->mMboxFile.isOpen());
412 Q_ASSERT((d->mInitialMboxFileSize + d->mAppendedEntries.size()) > offset);
416 if (offset < d->mInitialMboxFileSize) {
417 d->mMboxFile.seek(offset);
421 if (!d->isMBoxSeparator(line)) {
422 qCDebug(KMBOX_LOG) <<
"[MBox::readEntry] Invalid entry at:" << offset;
429 line = d->mMboxFile.readLine();
430 while (!d->isMBoxSeparator(line)) {
432 if (d->mMboxFile.atEnd()) {
435 line = d->mMboxFile.readLine();
438 offset -= d->mInitialMboxFileSize;
439 if (offset >
static_cast<quint64
>(d->mAppendedEntries.size())) {
446 QBuffer buffer(&(d->mAppendedEntries));
452 if (!d->isMBoxSeparator(line)) {
453 qCDebug(KMBOX_LOG) <<
"[MBox::readEntry] Invalid appended entry at:" << offset;
461 while (!d->isMBoxSeparator(line) && !buffer.
atEnd()) {
472 MBoxPrivate::unescapeFrom(message.
data(), message.
size());
475 if (!d->startTimerIfNeeded()) {
476 const bool unlocked =
unlock();
493 mail->setContent(KMime::CRLFtoLF(message));
501 const bool wasLocked = d->mFileLocked;
504 qCDebug(KMBOX_LOG) <<
"Failed to lock";
511 Q_ASSERT(d->mFileLocked);
512 Q_ASSERT(d->mMboxFile.isOpen());
513 Q_ASSERT((d->mInitialMboxFileSize + d->mAppendedEntries.size()) > offset);
516 if (offset < d->mInitialMboxFileSize) {
517 d->mMboxFile.seek(offset);
520 while (line[0] !=
'\n' && !d->mMboxFile.atEnd()) {
522 line = d->mMboxFile.readLine();
525 QBuffer buffer(&(d->mAppendedEntries));
527 buffer.
seek(offset - d->mInitialMboxFileSize);
530 while (line[0] !=
'\n' && !buffer.
atEnd()) {
554 if (d->mAppendedEntries.isEmpty()) {
559 Q_ASSERT(otherFile.
exists());
564 otherFile.
seek(d->mMboxFile.size());
565 otherFile.
write(d->mAppendedEntries);
576 if (d->mAppendedEntries.isEmpty()) {
584 Q_ASSERT(d->mMboxFile.isOpen());
586 d->mMboxFile.seek(d->mMboxFile.size());
587 d->mMboxFile.write(d->mAppendedEntries);
588 d->mAppendedEntries.clear();
589 d->mInitialMboxFileSize = d->mMboxFile.size();
595 if (d->mFileLocked) {
596 qCDebug(KMBOX_LOG) <<
"File is currently locked.";
601 case ProcmailLockfile:
603 qCDebug(KMBOX_LOG) <<
"Could not find the lockfile executable";
608 case MuttDotlockPrivileged:
610 qCDebug(KMBOX_LOG) <<
"Could not find the mutt_dotlock executable";
618 d->mLockType = ltype;
624 d->mLockFileName = lockFile;
629 d->mUnlockTimer.setInterval(msec);
634 if (d->mLockType == None && !d->mFileLocked) {
635 d->mFileLocked =
false;
636 d->mMboxFile.close();
643 switch (d->mLockType) {
644 case ProcmailLockfile:
646 if (!d->mLockFileName.isEmpty()) {
658 case MuttDotlockPrivileged:
669 d->mFileLocked =
false;
672 d->mMboxFile.close();
674 return !d->mFileLocked;
A class that encapsulates an entry of a MBox.
quint64 separatorSize() const
Returns the separator size of the message that is referenced by this mbox entry object.
QPair< MBoxEntry, MBoxEntry > Pair
Describes a pair of mbox entry objects.
quint64 messageOffset() const
Returns the offset of the message that is referenced by this mbox entry object.
quint64 messageSize() const
Returns the size of the message that is referenced by this mbox entry object.
QByteArray readRawMessage(const MBoxEntry &entry)
Reads the entire message from the file for the given mbox entry.
void setReadOnly(bool ro=true)
Set the access mode of the mbox file to read only.
bool lock()
Locks the mbox file using the configured lock method.
bool load(const QString &fileName)
Loads the raw mbox data from disk into the current MBox object.
void setLockFile(const QString &lockFile)
Sets the lockfile that should be used by the procmail or the KDE lock file method.
~MBox()
Destroys the mbox object.
bool purge(const MBoxEntry::List &deletedEntries, QList< MBoxEntry::Pair > *movedEntries=nullptr)
Removes all messages for the given mbox entries from the current reference file (the file that is loa...
MBox()
Creates a new mbox object.
MBoxEntry appendMessage(const KMime::Message::Ptr &message)
Appends message to the MBox and returns the corresponding mbox entry for it.
bool setLockType(LockType ltype)
Sets the locktype that should be used for locking the mbox file.
bool isReadOnly() const
Returns if the current access mode is set to readOnly.
bool save(const QString &fileName=QString())
Writes the mbox to disk.
QString fileName() const
Returns the file name that was passed to the last call to load().
void setUnlockTimeout(int msec)
By default the unlock method will directly unlock the file.
MBoxEntry::List entries(const MBoxEntry::List &deletedEntries=MBoxEntry::List()) const
Retrieve the mbox entry objects for all emails from the file except the deleteEntries.
bool locked() const
Returns whether or not the mbox currently is locked.
bool unlock()
Unlock the mbox file.
LockType
Describes the type of locking that will be used.
QByteArray readMessageHeaders(const MBoxEntry &entry)
Reads the headers of the message for the given mbox entry.
KMime::Message * readMessage(const MBoxEntry &entry)
Reads the entire message from the file for the given mbox entry.
virtual bool atEnd() const const override
virtual bool open(OpenMode flags) override
virtual bool seek(qint64 pos) override
bool endsWith(QByteArrayView bv) const const
bool isEmpty() const const
qsizetype size() const const
QByteArray encodeName(const QString &fileName)
bool exists(const QString &fileName)
bool open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags)
virtual bool setPermissions(Permissions permissions) override
virtual bool seek(qint64 pos) override
QByteArray readLine(qint64 maxSize)
qint64 write(const QByteArray &data)
bool contains(const AT &value) const const
bool isEmpty() const const
void reserve(qsizetype size)
qsizetype size() const const
bool hasNext() const const
int execute(const QString &program, const QStringList &arguments)
QString findExecutable(const QString &executableName, const QStringList &paths)
QString fromLocal8Bit(QByteArrayView str)
bool isEmpty() const const
QString toLocalFile() const const