28#include "mboxentry_p.h"
31#include <KStandardDirs>
34#include <QtCore/QBuffer>
35#include <QtCore/QProcess>
42 : d( new MBoxPrivate( this ) )
45 d->mFileLocked =
false;
48 d->mUnlockTimer.setInterval( 0 );
49 d->mUnlockTimer.setSingleShot(
true );
54 if ( d->mFileLocked ) {
70 Q_ASSERT( !d->mMboxFile.fileName().isEmpty() );
72 const QByteArray rawEntry = MBoxPrivate::escapeFrom( entry->encodedContent() );
74 if ( rawEntry.size() <= 0 ) {
75 kDebug() <<
"Message added to folder `" << d->mMboxFile.fileName()
76 <<
"' contains no data. Ignoring it.";
80 int nextOffset = d->mAppendedEntries.size();
84 if ( nextOffset < 1 && d->mMboxFile.size() > 0 ) {
85 d->mAppendedEntries.append(
"\n" );
87 }
else if ( nextOffset == 1 && d->mAppendedEntries.at( 0 ) !=
'\n' ) {
89 if ( d->mMboxFile.size() < 0 ) {
90 d->mAppendedEntries.append(
"\n" );
93 }
else if ( nextOffset >= 2 ) {
94 if ( d->mAppendedEntries.at( nextOffset - 1 ) !=
'\n' ) {
95 if ( d->mAppendedEntries.at( nextOffset ) !=
'\n' ) {
96 d->mAppendedEntries.append(
"\n\n" );
99 d->mAppendedEntries.append(
"\n" );
105 const QByteArray separator = MBoxPrivate::mboxMessageSeparator( rawEntry );
106 d->mAppendedEntries.append( separator );
107 d->mAppendedEntries.append( rawEntry );
108 if ( rawEntry[rawEntry.size() - 1] !=
'\n' ) {
109 d->mAppendedEntries.append(
"\n\n" );
111 d->mAppendedEntries.append(
"\n" );
115 resultEntry.d->mOffset = d->mInitialMboxFileSize + nextOffset;
116 resultEntry.d->mMessageSize = rawEntry.size();
117 resultEntry.d->mSeparatorSize = separator.size();
118 d->mEntries << resultEntry;
125 if ( deletedEntries.isEmpty() ) {
132 foreach (
const MBoxEntry &entry, d->mEntries ) {
133 if ( !deletedEntries.contains( entry ) ) {
143 return d->mMboxFile.fileName();
148 if ( d->mFileLocked ) {
155 kDebug() <<
"Failed to lock";
159 d->mInitialMboxFileSize = d->mMboxFile.size();
162 QByteArray prevSeparator;
165 while ( !d->mMboxFile.atEnd() ) {
166 quint64 pos = d->mMboxFile.pos();
168 line = d->mMboxFile.readLine();
172 if ( d->isMBoxSeparator( line ) ||
173 ( d->mMboxFile.atEnd() && ( prevSeparator.size() != 0 ) ) ) {
176 if ( d->mMboxFile.atEnd() ) {
177 pos = d->mMboxFile.pos();
181 quint64 msgSize = pos - offs;
187 entry.d->mOffset = offs;
188 entry.d->mSeparatorSize = prevSeparator.size();
189 entry.d->mMessageSize = msgSize - 1;
192 entry.d->mMessageSize -= prevSeparator.size() + 1;
194 d->mEntries << entry;
197 if ( d->isMBoxSeparator( line ) ) {
198 prevSeparator = line;
207 return unlock() && ( ( prevSeparator.size() != 0 ) || ( d->mMboxFile.size() == 0 ) );
212 if ( d->mMboxFile.fileName().isEmpty() ) {
222 if ( d->mLockType == None ) {
223 d->mFileLocked =
true;
225 d->startTimerIfNeeded();
229 d->mFileLocked =
false;
236 switch ( d->mLockType ) {
237 case ProcmailLockfile:
238 args << QLatin1String(
"-l20" ) << QLatin1String(
"-r5" );
239 if ( !d->mLockFileName.isEmpty() ) {
240 args << QString::fromLocal8Bit( QFile::encodeName( d->mLockFileName ) );
242 args << QString::fromLocal8Bit( QFile::encodeName( d->mMboxFile.fileName() +
243 QLatin1String(
".lock" ) ) );
246 rc = QProcess::execute( QLatin1String(
"lockfile" ), args );
248 kDebug() <<
"lockfile -l20 -r5 " << d->mMboxFile.fileName()
249 <<
": Failed (" << rc <<
") switching to read only mode";
253 d->mFileLocked =
true;
258 args << QString::fromLocal8Bit( QFile::encodeName( d->mMboxFile.fileName() ) );
259 rc = QProcess::execute( QLatin1String(
"mutt_dotlock" ), args );
262 kDebug() <<
"mutt_dotlock " << d->mMboxFile.fileName()
263 <<
": Failed (" << rc <<
") switching to read only mode";
267 d->mFileLocked =
true;
271 case MuttDotlockPrivileged:
272 args << QLatin1String(
"-p" )
273 << QString::fromLocal8Bit( QFile::encodeName( d->mMboxFile.fileName() ) );
274 rc = QProcess::execute( QLatin1String(
"mutt_dotlock" ), args );
277 kDebug() <<
"mutt_dotlock -p " << d->mMboxFile.fileName() <<
":"
278 <<
": Failed (" << rc <<
") switching to read only mode";
281 d->mFileLocked =
true;
286 d->mFileLocked =
true;
292 if ( d->mFileLocked ) {
294 const bool unlocked =
unlock();
295 Q_ASSERT( unlocked );
296 Q_UNUSED( unlocked );
300 d->startTimerIfNeeded();
301 return d->mFileLocked;
306 return d->mFileLocked;
316 if ( d->mMboxFile.fileName().isEmpty() || d->mReadOnly ) {
320 if ( deletedEntries.isEmpty() ) {
328 foreach (
const MBoxEntry &entry, deletedEntries ) {
330 const QByteArray line = d->mMboxFile.readLine();
332 if ( !d->isMBoxSeparator( line ) ) {
333 qDebug() <<
"Found invalid separator at:" << entry.
messageOffset();
340 if ( deletedEntries.size() == d->mEntries.size() ) {
342 d->mMboxFile.resize( 0 );
343 kDebug() <<
"Purge comleted successfully, unlocking the file.";
347 qSort( d->mEntries.begin(), d->mEntries.end(), lessThanByOffset );
348 quint64 writeOffset = 0;
349 bool writeOffSetInitialized =
false;
351 QList<MBoxEntry::Pair> tmpMovedEntries;
353 quint64 origFileSize = d->mMboxFile.size();
355 QListIterator<MBoxEntry> i( d->mEntries );
356 while ( i.hasNext() ) {
359 if ( deletedEntries.contains( entry ) && !writeOffSetInitialized ) {
361 writeOffSetInitialized =
true;
362 }
else if ( writeOffSetInitialized &&
364 !deletedEntries.contains( entry ) ) {
367 quint64 entrySize = 0;
369 entrySize = i.next().messageOffset() - entry.
messageOffset();
375 Q_ASSERT( entrySize > 0 );
381 quint64 mapSize = entry.
messageOffset() + entrySize - writeOffset;
384 uchar *memArea = d->mMboxFile.map( writeOffset, mapSize );
388 memmove( memArea, memArea + startOffset, entrySize );
390 d->mMboxFile.unmap( memArea );
393 resultEntry.d->mOffset = writeOffset;
397 resultingEntryList << resultEntry;
400 writeOffset += entrySize;
401 }
else if ( !deletedEntries.contains( entry ) ) {
404 Q_ASSERT( !writeOffSetInitialized );
405 resultingEntryList << entry;
410 d->mMboxFile.resize( writeOffset );
411 d->mEntries = resultingEntryList;
413 kDebug() <<
"Purge comleted successfully, unlocking the file.";
414 if ( movedEntries ) {
415 *movedEntries = tmpMovedEntries;
423 const bool wasLocked =
locked();
434 Q_ASSERT( d->mFileLocked );
435 Q_ASSERT( d->mMboxFile.isOpen() );
436 Q_ASSERT( ( d->mInitialMboxFileSize + d->mAppendedEntries.size() ) > offset );
440 if ( offset < d->mInitialMboxFileSize ) {
441 d->mMboxFile.seek( offset );
443 QByteArray line = d->mMboxFile.readLine();
445 if ( !d->isMBoxSeparator( line ) ) {
446 kDebug() <<
"[MBox::readEntry] Invalid entry at:" << offset;
453 line = d->mMboxFile.readLine();
454 while ( !d->isMBoxSeparator( line ) ) {
456 if ( d->mMboxFile.atEnd() ) {
459 line = d->mMboxFile.readLine();
462 offset -= d->mInitialMboxFileSize;
463 if ( offset >
static_cast<quint64
>( d->mAppendedEntries.size() ) ) {
470 QBuffer buffer( &( d->mAppendedEntries ) );
471 buffer.open( QIODevice::ReadOnly );
472 buffer.seek( offset );
474 QByteArray line = buffer.readLine();
476 if ( !d->isMBoxSeparator( line ) ) {
477 kDebug() <<
"[MBox::readEntry] Invalid appended entry at:" << offset;
484 line = buffer.readLine();
485 while ( !d->isMBoxSeparator( line ) && !buffer.atEnd() ) {
487 line = buffer.readLine();
492 if ( message.endsWith(
'\n' ) ) {
496 MBoxPrivate::unescapeFrom( message.data(), message.size() );
499 if ( !d->startTimerIfNeeded() ) {
500 const bool unlocked =
unlock();
501 Q_ASSERT( unlocked );
502 Q_UNUSED( unlocked );
512 if ( message.isEmpty() ) {
516 KMime::Message *mail =
new KMime::Message();
517 mail->setContent( KMime::CRLFtoLF( message ) );
525 const bool wasLocked = d->mFileLocked;
528 kDebug() <<
"Failed to lock";
535 Q_ASSERT( d->mFileLocked );
536 Q_ASSERT( d->mMboxFile.isOpen() );
537 Q_ASSERT( ( d->mInitialMboxFileSize + d->mAppendedEntries.size() ) > offset );
540 if ( offset < d->mInitialMboxFileSize ) {
541 d->mMboxFile.seek( offset );
542 QByteArray line = d->mMboxFile.readLine();
544 while ( line[0] !=
'\n' && !d->mMboxFile.atEnd() ) {
546 line = d->mMboxFile.readLine();
549 QBuffer buffer( &( d->mAppendedEntries ) );
550 buffer.open( QIODevice::ReadOnly );
551 buffer.seek( offset - d->mInitialMboxFileSize );
552 QByteArray line = buffer.readLine();
554 while ( line[0] !=
'\n' && !buffer.atEnd() ) {
556 line = buffer.readLine();
569 if ( !
fileName.isEmpty() && KUrl(
fileName ).toLocalFile() != d->mMboxFile.fileName() ) {
570 if ( !d->mMboxFile.copy(
fileName ) ) {
575 QFile::setPermissions(
fileName, d->mMboxFile.permissions() | QFile::WriteOwner);
578 if ( d->mAppendedEntries.size() == 0 ) {
583 Q_ASSERT( otherFile.exists() );
584 if ( !otherFile.open( QIODevice::ReadWrite ) ) {
588 otherFile.seek( d->mMboxFile.size() );
589 otherFile.write( d->mAppendedEntries );
599 if ( d->mAppendedEntries.size() == 0 ) {
607 Q_ASSERT( d->mMboxFile.isOpen() );
609 d->mMboxFile.seek( d->mMboxFile.size() );
610 d->mMboxFile.write( d->mAppendedEntries );
611 d->mAppendedEntries.clear();
612 d->mInitialMboxFileSize = d->mMboxFile.size();
619 if ( d->mFileLocked ) {
620 kDebug() <<
"File is currently locked.";
625 case ProcmailLockfile:
626 if ( KStandardDirs::findExe( QLatin1String(
"lockfile" ) ).isEmpty() ) {
627 kDebug() <<
"Could not find the lockfile executable";
632 case MuttDotlockPrivileged:
633 if ( KStandardDirs::findExe( QLatin1String(
"mutt_dotlock" ) ).isEmpty() ) {
634 kDebug() <<
"Could not find the mutt_dotlock executable";
642 d->mLockType = ltype;
648 d->mLockFileName = lockFile;
653 d->mUnlockTimer.setInterval( msec );
658 if ( d->mLockType == None && !d->mFileLocked ) {
659 d->mFileLocked =
false;
660 d->mMboxFile.close();
667 switch ( d->mLockType ) {
668 case ProcmailLockfile:
670 if ( !d->mLockFileName.isEmpty() ) {
671 rc = !QFile( d->mLockFileName ).remove();
673 rc = !QFile( d->mMboxFile.fileName() + QLatin1String(
".lock" ) ).remove();
678 args << QLatin1String(
"-u" )
679 << QString::fromLocal8Bit( QFile::encodeName( d->mMboxFile.fileName() ) );
680 rc = QProcess::execute( QLatin1String(
"mutt_dotlock" ), args );
683 case MuttDotlockPrivileged:
684 args << QLatin1String(
"-u" ) << QLatin1String(
"-p" )
685 << QString::fromLocal8Bit( QFile::encodeName( d->mMboxFile.fileName() ) );
686 rc = QProcess::execute( QLatin1String(
"mutt_dotlock" ), args );
695 d->mFileLocked =
false;
698 d->mMboxFile.close();
700 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.
QList< MBoxEntry > List
Describes a list of mbox entry objects.
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.
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 purge(const MBoxEntry::List &deletedEntries, QList< MBoxEntry::Pair > *movedEntries=0)
Removes all messages for the given mbox entries from the current reference file (the file that is loa...
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.