KMbox

mbox.cpp
1/*
2 SPDX-FileCopyrightText: 1996-1998 Stefan Taferner <taferner@kde.org>
3 SPDX-FileCopyrightText: 2009 Bertjan Broeksema <broeksema@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6
7 NOTE: Most of the code inside here is an slightly adjusted version of
8 kdepim/kmail/kmfoldermbox.cpp. This is why I added a line for Stefan Taferner.
9
10 Bertjan Broeksema, april 2009
11*/
12
13#include "mbox.h"
14#include "mbox_p.h"
15#include "mboxentry_p.h"
16
17#include "kmbox_debug.h"
18#include <QStandardPaths>
19#include <QUrl>
20
21#include <QBuffer>
22#include <QProcess>
23
24using namespace KMBox;
25/// public methods.
26
28 : d(new MBoxPrivate(this))
29{
30 // Set some sane defaults
31 d->mFileLocked = false;
32 d->mLockType = None;
33
34 d->mUnlockTimer.setInterval(0);
35 d->mUnlockTimer.setSingleShot(true);
36}
37
39{
40 if (d->mFileLocked) {
41 unlock();
42 }
43
44 d->close();
45}
46
47// Appended entries works as follows: When an mbox file is loaded from disk,
48// d->mInitialMboxFileSize is set to the file size at that moment. New entries
49// are stored in memory (d->mAppendedEntries). The initial file size and the size
50// of the buffer determine the offset for the next message to append.
52{
53 // It doesn't make sense to add entries when we don't have an reference file.
54 Q_ASSERT(!d->mMboxFile.fileName().isEmpty());
55
56 const QByteArray rawEntry = MBoxPrivate::escapeFrom(entry->encodedContent());
57
58 if (rawEntry.size() <= 0) {
59 qCDebug(KMBOX_LOG) << "Message added to folder `" << d->mMboxFile.fileName() << "' contains no data. Ignoring it.";
60 return MBoxEntry();
61 }
62
63 int nextOffset = d->mAppendedEntries.size(); // Offset of the appended message
64
65 // Make sure the byte array is large enough to check for an end character.
66 // Then check if the required newlines are there.
67 if (nextOffset < 1 && d->mMboxFile.size() > 0) { // Empty, add one empty line
68 d->mAppendedEntries.append("\n");
69 ++nextOffset;
70 } else if (nextOffset == 1 && d->mAppendedEntries.at(0) != '\n') {
71 // This should actually not happen, but catch it anyway.
72 if (d->mMboxFile.size() < 0) {
73 d->mAppendedEntries.append("\n");
74 ++nextOffset;
75 }
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");
80 nextOffset += 2;
81 } else {
82 d->mAppendedEntries.append("\n");
83 ++nextOffset;
84 }
85 }
86 }
87
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");
93 } else {
94 d->mAppendedEntries.append("\n");
95 }
96
97 MBoxEntry resultEntry;
98 resultEntry.d->mOffset = d->mInitialMboxFileSize + nextOffset;
99 resultEntry.d->mMessageSize = rawEntry.size();
100 resultEntry.d->mSeparatorSize = separator.size();
101 d->mEntries << resultEntry;
102
103 return resultEntry;
104}
105
107{
108 if (deletedEntries.isEmpty()) {
109 // fast path
110 return d->mEntries;
111 }
112
113 MBoxEntry::List result;
114 result.reserve(d->mEntries.size());
115
116 for (const MBoxEntry &entry : std::as_const(d->mEntries)) {
117 if (!deletedEntries.contains(entry)) {
118 result << entry;
119 }
120 }
121
122 return result;
123}
124
126{
127 return d->mMboxFile.fileName();
128}
129
130bool MBox::load(const QString &fileName)
131{
132 if (d->mFileLocked) {
133 return false;
134 }
135
136 d->initLoad(fileName);
137
138 if (!lock()) {
139 qCDebug(KMBOX_LOG) << "Failed to lock";
140 return false;
141 }
142
143 d->mInitialMboxFileSize = d->mMboxFile.size(); // AFTER the file has been locked
144
145 QByteArray line;
146 QByteArray prevSeparator;
147 quint64 offs = 0; // The offset of the next message to read.
148
149 while (!d->mMboxFile.atEnd()) {
150 quint64 pos = d->mMboxFile.pos();
151
152 line = d->mMboxFile.readLine();
153
154 // if atEnd, use mail only if there was a separator line at all,
155 // otherwise it's not a valid mbox
156 if (d->isMBoxSeparator(line) || (d->mMboxFile.atEnd() && (prevSeparator.size() != 0))) {
157 // if we are the at the file end, update pos to not forget the last line
158 if (d->mMboxFile.atEnd()) {
159 pos = d->mMboxFile.pos();
160 }
161
162 // Found the separator or at end of file, the message starts at offs
163 quint64 msgSize = pos - offs;
164
165 if (pos > 0) {
166 // This is not the separator of the first mail in the file. If pos == 0
167 // than we matched the separator of the first mail in the file.
168 MBoxEntry entry;
169 entry.d->mOffset = offs;
170 entry.d->mSeparatorSize = prevSeparator.size();
171 entry.d->mMessageSize = msgSize - 1;
172
173 // Don't add the separator size and the newline up to the message size.
174 entry.d->mMessageSize -= prevSeparator.size() + 1;
175
176 d->mEntries << entry;
177 }
178
179 if (d->isMBoxSeparator(line)) {
180 prevSeparator = line;
181 }
182
183 offs += msgSize; // Mark the beginning of the next message.
184 }
185 }
186
187 // FIXME: What if unlock fails?
188 // if no separator was found, the file is still valid if it is empty
189 const bool val = unlock() && (!prevSeparator.isEmpty() || (d->mMboxFile.size() == 0));
190 return val;
191}
192
194{
195 if (d->mMboxFile.fileName().isEmpty()) {
196 return false; // We cannot lock if there is no file loaded.
197 }
198
199 // We can't load another file when the mbox currently is locked so if d->mFileLocked
200 // is true atm just return true.
201 if (locked()) {
202 return true;
203 }
204
205 if (d->mLockType == None) {
206 d->mFileLocked = true;
207 if (d->open()) {
208 d->startTimerIfNeeded();
209 return true;
210 }
211
212 d->mFileLocked = false;
213 return false;
214 }
215
216 QStringList args;
217 int rc = 0;
218
219 switch (d->mLockType) {
220 case ProcmailLockfile:
221 args << QStringLiteral("-l20") << QStringLiteral("-r5");
222 if (!d->mLockFileName.isEmpty()) {
223 args << QString::fromLocal8Bit(QFile::encodeName(d->mLockFileName));
224 } else {
225 args << QString::fromLocal8Bit(QFile::encodeName(d->mMboxFile.fileName() + QLatin1StringView(".lock")));
226 }
227
228 rc = QProcess::execute(QStringLiteral("lockfile"), args);
229 if (rc != 0) {
230 qCDebug(KMBOX_LOG) << "lockfile -l20 -r5 " << d->mMboxFile.fileName() << ": Failed (" << rc << ") switching to read only mode";
231 d->mReadOnly = true; // In case the MBox object was created read/write we
232 // set it to read only when locking failed.
233 } else {
234 d->mFileLocked = true;
235 }
236 break;
237
238 case MuttDotlock:
239 args << QString::fromLocal8Bit(QFile::encodeName(d->mMboxFile.fileName()));
240 rc = QProcess::execute(QStringLiteral("mutt_dotlock"), args);
241
242 if (rc != 0) {
243 qCDebug(KMBOX_LOG) << "mutt_dotlock " << d->mMboxFile.fileName() << ": Failed (" << rc << ") switching to read only mode";
244 d->mReadOnly = true; // In case the MBox object was created read/write we
245 // set it to read only when locking failed.
246 } else {
247 d->mFileLocked = true;
248 }
249 break;
250
251 case MuttDotlockPrivileged:
252 args << QStringLiteral("-p") << QString::fromLocal8Bit(QFile::encodeName(d->mMboxFile.fileName()));
253 rc = QProcess::execute(QStringLiteral("mutt_dotlock"), args);
254
255 if (rc != 0) {
256 qCDebug(KMBOX_LOG) << "mutt_dotlock -p " << d->mMboxFile.fileName() << ":"
257 << ": Failed (" << rc << ") switching to read only mode";
258 d->mReadOnly = true;
259 } else {
260 d->mFileLocked = true;
261 }
262 break;
263
264 case None:
265 d->mFileLocked = true;
266 break;
267 default:
268 break;
269 }
270
271 if (d->mFileLocked) {
272 if (!d->open()) {
273 const bool unlocked = unlock();
274 Q_ASSERT(unlocked); // If this fails we're in trouble.
275 Q_UNUSED(unlocked)
276 }
277 }
278
279 d->startTimerIfNeeded();
280 return d->mFileLocked;
281}
282
283bool MBox::locked() const
284{
285 return d->mFileLocked;
286}
287
288static bool lessThanByOffset(const MBoxEntry &left, const MBoxEntry &right)
289{
290 return left.messageOffset() < right.messageOffset();
291}
292
293bool MBox::purge(const MBoxEntry::List &deletedEntries, QList<MBoxEntry::Pair> *movedEntries)
294{
295 if (d->mMboxFile.fileName().isEmpty() || d->mReadOnly) {
296 return false; // No file loaded yet or it's readOnly
297 }
298
299 if (deletedEntries.isEmpty()) {
300 return true; // Nothing to do.
301 }
302
303 if (!lock()) {
304 return false;
305 }
306
307 for (const MBoxEntry &entry : std::as_const(deletedEntries)) {
308 d->mMboxFile.seek(entry.messageOffset());
309 const QByteArray line = d->mMboxFile.readLine();
310
311 if (!d->isMBoxSeparator(line)) {
312 qCDebug(KMBOX_LOG) << "Found invalid separator at:" << entry.messageOffset();
313 unlock();
314 return false; // The file is messed up or the index is incorrect.
315 }
316 }
317
318 // All entries are deleted, so just resize the file to a size of 0.
319 if (deletedEntries.size() == d->mEntries.size()) {
320 d->mEntries.clear();
321 d->mMboxFile.resize(0);
322 qCDebug(KMBOX_LOG) << "Purge completed successfully, unlocking the file.";
323 return unlock();
324 }
325
326 std::sort(d->mEntries.begin(), d->mEntries.end(), lessThanByOffset);
327 quint64 writeOffset = 0;
328 bool writeOffSetInitialized = false;
329 MBoxEntry::List resultingEntryList;
330 QList<MBoxEntry::Pair> tmpMovedEntries;
331
332 quint64 origFileSize = d->mMboxFile.size();
333
334 QListIterator<MBoxEntry> i(d->mEntries);
335 while (i.hasNext()) {
336 MBoxEntry entry = i.next();
337
338 if (deletedEntries.contains(entry) && !writeOffSetInitialized) {
339 writeOffset = entry.messageOffset();
340 writeOffSetInitialized = true;
341 } else if (writeOffSetInitialized && writeOffset < entry.messageOffset() && !deletedEntries.contains(entry)) {
342 // The current message doesn't have to be deleted, but must be moved.
343 // First determine the size of the entry that must be moved.
344 quint64 entrySize = 0;
345 if (i.hasNext()) {
346 entrySize = i.next().messageOffset() - entry.messageOffset();
347 i.previous(); // Go back to make sure that we also handle the next entry.
348 } else {
349 entrySize = origFileSize - entry.messageOffset();
350 }
351
352 Q_ASSERT(entrySize > 0); // MBox entries really cannot have a size <= 0;
353
354 // we map the whole area of the file starting at the writeOffset up to the
355 // message that have to be moved into memory. This includes eventually the
356 // messages that are the deleted between the first deleted message
357 // encountered and the message that has to be moved.
358 quint64 mapSize = entry.messageOffset() + entrySize - writeOffset;
359
360 // Now map writeOffSet + mapSize into mem.
361 uchar *memArea = d->mMboxFile.map(writeOffset, mapSize);
362
363 // Now read the entry that must be moved to writeOffset.
364 quint64 startOffset = entry.messageOffset() - writeOffset;
365 memmove(memArea, memArea + startOffset, entrySize);
366
367 d->mMboxFile.unmap(memArea);
368
369 MBoxEntry resultEntry;
370 resultEntry.d->mOffset = writeOffset;
371 resultEntry.d->mSeparatorSize = entry.separatorSize();
372 resultEntry.d->mMessageSize = entry.messageSize();
373
374 resultingEntryList << resultEntry;
375 tmpMovedEntries << MBoxEntry::Pair(MBoxEntry(entry.messageOffset()), MBoxEntry(resultEntry.messageOffset()));
376 writeOffset += entrySize;
377 } else if (!deletedEntries.contains(entry)) {
378 // Unmoved and not deleted entry, can only ocure before the first deleted
379 // entry.
380 Q_ASSERT(!writeOffSetInitialized);
381 resultingEntryList << entry;
382 }
383 }
384
385 // Chop off remaining entry bits.
386 d->mMboxFile.resize(writeOffset);
387 d->mEntries = resultingEntryList;
388
389 qCDebug(KMBOX_LOG) << "Purge completed successfully, unlocking the file.";
390 if (movedEntries) {
391 *movedEntries = tmpMovedEntries;
392 }
393 return unlock(); // FIXME: What if this fails? It will return false but the
394 // file has changed.
395}
396
398{
399 const bool wasLocked = locked();
400 if (!wasLocked) {
401 if (!lock()) {
402 return QByteArray();
403 }
404 }
405
406 // TODO: Add error handling in case locking failed.
407
408 quint64 offset = entry.messageOffset();
409
410 Q_ASSERT(d->mFileLocked);
411 Q_ASSERT(d->mMboxFile.isOpen());
412 Q_ASSERT((d->mInitialMboxFileSize + d->mAppendedEntries.size()) > offset);
413
414 QByteArray message;
415
416 if (offset < d->mInitialMboxFileSize) {
417 d->mMboxFile.seek(offset);
418
419 QByteArray line = d->mMboxFile.readLine();
420
421 if (!d->isMBoxSeparator(line)) {
422 qCDebug(KMBOX_LOG) << "[MBox::readEntry] Invalid entry at:" << offset;
423 if (!wasLocked) {
424 unlock();
425 }
426 return QByteArray(); // The file is messed up or the index is incorrect.
427 }
428
429 line = d->mMboxFile.readLine();
430 while (!d->isMBoxSeparator(line)) {
431 message += line;
432 if (d->mMboxFile.atEnd()) {
433 break;
434 }
435 line = d->mMboxFile.readLine();
436 }
437 } else {
438 offset -= d->mInitialMboxFileSize;
439 if (offset > static_cast<quint64>(d->mAppendedEntries.size())) {
440 if (!wasLocked) {
441 unlock();
442 }
443 return QByteArray();
444 }
445
446 QBuffer buffer(&(d->mAppendedEntries));
448 buffer.seek(offset);
449
450 QByteArray line = buffer.readLine();
451
452 if (!d->isMBoxSeparator(line)) {
453 qCDebug(KMBOX_LOG) << "[MBox::readEntry] Invalid appended entry at:" << offset;
454 if (!wasLocked) {
455 unlock();
456 }
457 return QByteArray(); // The file is messed up or the index is incorrect.
458 }
459
460 line = buffer.readLine();
461 while (!d->isMBoxSeparator(line) && !buffer.atEnd()) {
462 message += line;
463 line = buffer.readLine();
464 }
465 }
466
467 // Remove the last '\n' added by writeEntry.
468 if (message.endsWith('\n')) {
469 message.chop(1);
470 }
471
472 MBoxPrivate::unescapeFrom(message.data(), message.size());
473
474 if (!wasLocked) {
475 if (!d->startTimerIfNeeded()) {
476 const bool unlocked = unlock();
477 Q_ASSERT(unlocked);
478 Q_UNUSED(unlocked)
479 }
480 }
481
482 return message;
483}
484
486{
487 const QByteArray message = readRawMessage(entry);
488 if (message.isEmpty()) {
489 return nullptr;
490 }
491
492 auto mail = new KMime::Message();
493 mail->setContent(KMime::CRLFtoLF(message));
494 mail->parse();
495
496 return mail;
497}
498
500{
501 const bool wasLocked = d->mFileLocked;
502 if (!wasLocked) {
503 if (!lock()) {
504 qCDebug(KMBOX_LOG) << "Failed to lock";
505 return QByteArray();
506 }
507 }
508
509 const quint64 offset = entry.messageOffset();
510
511 Q_ASSERT(d->mFileLocked);
512 Q_ASSERT(d->mMboxFile.isOpen());
513 Q_ASSERT((d->mInitialMboxFileSize + d->mAppendedEntries.size()) > offset);
514
515 QByteArray headers;
516 if (offset < d->mInitialMboxFileSize) {
517 d->mMboxFile.seek(offset);
518 QByteArray line = d->mMboxFile.readLine();
519
520 while (line[0] != '\n' && !d->mMboxFile.atEnd()) {
521 headers += line;
522 line = d->mMboxFile.readLine();
523 }
524 } else {
525 QBuffer buffer(&(d->mAppendedEntries));
527 buffer.seek(offset - d->mInitialMboxFileSize);
528 QByteArray line = buffer.readLine();
529
530 while (line[0] != '\n' && !buffer.atEnd()) {
531 headers += line;
532 line = buffer.readLine();
533 }
534 }
535
536 if (!wasLocked) {
537 unlock();
538 }
539
540 return headers;
541}
542
543bool MBox::save(const QString &fileName)
544{
545 if (!fileName.isEmpty() && QUrl::fromUserInput(fileName).toLocalFile() != d->mMboxFile.fileName()) {
546 if (!d->mMboxFile.copy(fileName)) {
547 return false;
548 } else {
549 // if the original file was read-only, also the copied file is read-only
550 // Let's make it writable now
551 QFile::setPermissions(fileName, d->mMboxFile.permissions() | QFile::WriteOwner);
552 }
553
554 if (d->mAppendedEntries.isEmpty()) {
555 return true; // Nothing to do
556 }
557
558 QFile otherFile(fileName);
559 Q_ASSERT(otherFile.exists());
560 if (!otherFile.open(QIODevice::ReadWrite)) {
561 return false;
562 }
563
564 otherFile.seek(d->mMboxFile.size());
565 otherFile.write(d->mAppendedEntries);
566
567 // Don't clear mAppendedEntries and don't update mInitialFileSize. These
568 // are still valid for the original file.
569 return true;
570 }
571
572 if (d->mReadOnly) {
573 return false;
574 }
575
576 if (d->mAppendedEntries.isEmpty()) {
577 return true; // Nothing to do.
578 }
579
580 if (!lock()) {
581 return false;
582 }
583
584 Q_ASSERT(d->mMboxFile.isOpen());
585
586 d->mMboxFile.seek(d->mMboxFile.size());
587 d->mMboxFile.write(d->mAppendedEntries);
588 d->mAppendedEntries.clear();
589 d->mInitialMboxFileSize = d->mMboxFile.size();
590 return unlock();
591}
592
594{
595 if (d->mFileLocked) {
596 qCDebug(KMBOX_LOG) << "File is currently locked.";
597 return false; // Don't change the method if the file is currently locked.
598 }
599
600 switch (ltype) {
601 case ProcmailLockfile:
602 if (QStandardPaths::findExecutable(QStringLiteral("lockfile")).isEmpty()) {
603 qCDebug(KMBOX_LOG) << "Could not find the lockfile executable";
604 return false;
605 }
606 break;
607 case MuttDotlock: // fall through
608 case MuttDotlockPrivileged:
609 if (QStandardPaths::findExecutable(QStringLiteral("mutt_dotlock")).isEmpty()) {
610 qCDebug(KMBOX_LOG) << "Could not find the mutt_dotlock executable";
611 return false;
612 }
613 break;
614 default:
615 break; // We assume fcntl available and lock_none doesn't need a check.
616 }
617
618 d->mLockType = ltype;
619 return true;
620}
621
622void MBox::setLockFile(const QString &lockFile)
623{
624 d->mLockFileName = lockFile;
625}
626
628{
629 d->mUnlockTimer.setInterval(msec);
630}
631
633{
634 if (d->mLockType == None && !d->mFileLocked) {
635 d->mFileLocked = false;
636 d->mMboxFile.close();
637 return true;
638 }
639
640 int rc = 0;
641 QStringList args;
642
643 switch (d->mLockType) {
644 case ProcmailLockfile:
645 // QFile::remove returns true on success so negate the result.
646 if (!d->mLockFileName.isEmpty()) {
647 rc = !QFile(d->mLockFileName).remove();
648 } else {
649 rc = !QFile(d->mMboxFile.fileName() + QLatin1StringView(".lock")).remove();
650 }
651 break;
652
653 case MuttDotlock:
654 args << QStringLiteral("-u") << QString::fromLocal8Bit(QFile::encodeName(d->mMboxFile.fileName()));
655 rc = QProcess::execute(QStringLiteral("mutt_dotlock"), args);
656 break;
657
658 case MuttDotlockPrivileged:
659 args << QStringLiteral("-u") << QStringLiteral("-p") << QString::fromLocal8Bit(QFile::encodeName(d->mMboxFile.fileName()));
660 rc = QProcess::execute(QStringLiteral("mutt_dotlock"), args);
661 break;
662
663 case None: // Fall through.
664 default:
665 break;
666 }
667
668 if (rc == 0) { // Unlocking succeeded
669 d->mFileLocked = false;
670 }
671
672 d->mMboxFile.close();
673
674 return !d->mFileLocked;
675}
676
677void MBox::setReadOnly(bool ro)
678{
679 d->mReadOnly = ro;
680}
681
683{
684 return d->mReadOnly;
685}
A class that encapsulates an entry of a MBox.
Definition mboxentry.h:25
quint64 separatorSize() const
Returns the separator size of the message that is referenced by this mbox entry object.
Definition mboxentry.cpp:67
QPair< MBoxEntry, MBoxEntry > Pair
Describes a pair of mbox entry objects.
Definition mboxentry.h:35
quint64 messageOffset() const
Returns the offset of the message that is referenced by this mbox entry object.
Definition mboxentry.cpp:57
quint64 messageSize() const
Returns the size of the message that is referenced by this mbox entry object.
Definition mboxentry.cpp:62
QByteArray readRawMessage(const MBoxEntry &entry)
Reads the entire message from the file for the given mbox entry.
Definition mbox.cpp:397
void setReadOnly(bool ro=true)
Set the access mode of the mbox file to read only.
Definition mbox.cpp:677
bool lock()
Locks the mbox file using the configured lock method.
Definition mbox.cpp:193
bool load(const QString &fileName)
Loads the raw mbox data from disk into the current MBox object.
Definition mbox.cpp:130
void setLockFile(const QString &lockFile)
Sets the lockfile that should be used by the procmail or the KDE lock file method.
Definition mbox.cpp:622
~MBox()
Destroys the mbox object.
Definition mbox.cpp:38
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...
Definition mbox.cpp:293
MBox()
Creates a new mbox object.
Definition mbox.cpp:27
MBoxEntry appendMessage(const KMime::Message::Ptr &message)
Appends message to the MBox and returns the corresponding mbox entry for it.
Definition mbox.cpp:51
bool setLockType(LockType ltype)
Sets the locktype that should be used for locking the mbox file.
Definition mbox.cpp:593
bool isReadOnly() const
Returns if the current access mode is set to readOnly.
Definition mbox.cpp:682
bool save(const QString &fileName=QString())
Writes the mbox to disk.
Definition mbox.cpp:543
QString fileName() const
Returns the file name that was passed to the last call to load().
Definition mbox.cpp:125
void setUnlockTimeout(int msec)
By default the unlock method will directly unlock the file.
Definition mbox.cpp:627
MBoxEntry::List entries(const MBoxEntry::List &deletedEntries=MBoxEntry::List()) const
Retrieve the mbox entry objects for all emails from the file except the deleteEntries.
Definition mbox.cpp:106
bool locked() const
Returns whether or not the mbox currently is locked.
Definition mbox.cpp:283
bool unlock()
Unlock the mbox file.
Definition mbox.cpp:632
LockType
Describes the type of locking that will be used.
Definition mbox.h:31
QByteArray readMessageHeaders(const MBoxEntry &entry)
Reads the headers of the message for the given mbox entry.
Definition mbox.cpp:499
KMime::Message * readMessage(const MBoxEntry &entry)
Reads the entire message from the file for the given mbox entry.
Definition mbox.cpp:485
virtual bool atEnd() const const override
virtual bool open(OpenMode flags) override
virtual bool seek(qint64 pos) override
void chop(qsizetype n)
char * data()
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)
bool remove()
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
const T & next()
const T & previous()
int execute(const QString &program, const QStringList &arguments)
QString findExecutable(const QString &executableName, const QStringList &paths)
QString fromLocal8Bit(QByteArrayView str)
bool isEmpty() const const
QUrl fromUserInput(const QString &userInput, const QString &workingDirectory, UserInputResolutionOptions options)
QString toLocalFile() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:14:12 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.