28 #include "mboxentry_p.h"
31 #include <KStandardDirs>
34 #include <QtCore/QBuffer>
35 #include <QtCore/QProcess>
37 using namespace KMBox;
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 ) {
152 d->initLoad( fileName );
155 kDebug() <<
"Failed to lock";
160 QByteArray prevSeparator;
163 while ( !d->mMboxFile.atEnd() ) {
164 quint64 pos = d->mMboxFile.pos();
166 line = d->mMboxFile.readLine();
170 if ( d->isMBoxSeparator( line ) ||
171 ( d->mMboxFile.atEnd() && ( prevSeparator.size() != 0 ) ) ) {
174 if ( d->mMboxFile.atEnd() ) {
175 pos = d->mMboxFile.pos();
179 quint64 msgSize = pos - offs;
185 entry.d->mOffset = offs;
186 entry.d->mSeparatorSize = prevSeparator.size();
187 entry.d->mMessageSize = msgSize - 1;
190 entry.d->mMessageSize -= prevSeparator.size() + 1;
192 d->mEntries << entry;
195 if ( d->isMBoxSeparator( line ) ) {
196 prevSeparator = line;
205 return unlock() && ( ( prevSeparator.size() != 0 ) || ( d->mMboxFile.size() == 0 ) );
210 if ( d->mMboxFile.fileName().isEmpty() ) {
220 if ( d->mLockType == None ) {
221 d->mFileLocked =
true;
223 d->startTimerIfNeeded();
227 d->mFileLocked =
false;
234 switch ( d->mLockType ) {
235 case ProcmailLockfile:
236 args << QLatin1String(
"-l20" ) << QLatin1String(
"-r5" );
237 if ( !d->mLockFileName.isEmpty() ) {
238 args << QString::fromLocal8Bit( QFile::encodeName( d->mLockFileName ) );
240 args << QString::fromLocal8Bit( QFile::encodeName( d->mMboxFile.fileName() +
241 QLatin1String(
".lock" ) ) );
244 rc = QProcess::execute( QLatin1String(
"lockfile" ), args );
246 kDebug() <<
"lockfile -l20 -r5 " << d->mMboxFile.fileName()
247 <<
": Failed (" << rc <<
") switching to read only mode";
251 d->mFileLocked =
true;
256 args << QString::fromLocal8Bit( QFile::encodeName( d->mMboxFile.fileName() ) );
257 rc = QProcess::execute( QLatin1String(
"mutt_dotlock" ), args );
260 kDebug() <<
"mutt_dotlock " << d->mMboxFile.fileName()
261 <<
": Failed (" << rc <<
") switching to read only mode";
265 d->mFileLocked =
true;
269 case MuttDotlockPrivileged:
270 args << QLatin1String(
"-p" )
271 << QString::fromLocal8Bit( QFile::encodeName( d->mMboxFile.fileName() ) );
272 rc = QProcess::execute( QLatin1String(
"mutt_dotlock" ), args );
275 kDebug() <<
"mutt_dotlock -p " << d->mMboxFile.fileName() <<
":"
276 <<
": Failed (" << rc <<
") switching to read only mode";
279 d->mFileLocked =
true;
284 d->mFileLocked =
true;
290 if ( d->mFileLocked ) {
292 const bool unlocked =
unlock();
293 Q_ASSERT( unlocked );
294 Q_UNUSED( unlocked );
298 d->startTimerIfNeeded();
299 return d->mFileLocked;
304 return d->mFileLocked;
314 if ( d->mMboxFile.fileName().isEmpty() ) {
318 if ( deletedEntries.isEmpty() ) {
326 foreach (
const MBoxEntry &entry, deletedEntries ) {
328 const QByteArray line = d->mMboxFile.readLine();
330 if ( !d->isMBoxSeparator( line ) ) {
331 qDebug() <<
"Found invalid separator at:" << entry.
messageOffset();
338 if ( deletedEntries.size() == d->mEntries.size() ) {
340 d->mMboxFile.resize( 0 );
341 kDebug() <<
"Purge comleted successfully, unlocking the file.";
345 qSort( d->mEntries.begin(), d->mEntries.end(), lessThanByOffset );
346 quint64 writeOffset = 0;
347 bool writeOffSetInitialized =
false;
349 QList<MBoxEntry::Pair> tmpMovedEntries;
351 quint64 origFileSize = d->mMboxFile.size();
353 QListIterator<MBoxEntry> i( d->mEntries );
354 while ( i.hasNext() ) {
357 if ( deletedEntries.contains( entry ) && !writeOffSetInitialized ) {
359 writeOffSetInitialized =
true;
360 }
else if ( writeOffSetInitialized &&
362 !deletedEntries.contains( entry ) ) {
365 quint64 entrySize = 0;
367 entrySize = i.next().messageOffset() - entry.
messageOffset();
373 Q_ASSERT( entrySize > 0 );
379 quint64 mapSize = entry.
messageOffset() + entrySize - writeOffset;
382 uchar *memArea = d->mMboxFile.map( writeOffset, mapSize );
386 memmove( memArea, memArea + startOffset, entrySize );
388 d->mMboxFile.unmap( memArea );
391 resultEntry.d->mOffset = writeOffset;
395 resultingEntryList << resultEntry;
397 MBoxEntry( resultEntry.messageOffset() ) );
398 writeOffset += entrySize;
399 }
else if ( !deletedEntries.contains( entry ) ) {
402 Q_ASSERT( !writeOffSetInitialized );
403 resultingEntryList << entry;
408 d->mMboxFile.resize( writeOffset );
409 d->mEntries = resultingEntryList;
411 kDebug() <<
"Purge comleted successfully, unlocking the file.";
412 if ( movedEntries ) {
413 *movedEntries = tmpMovedEntries;
421 const bool wasLocked =
locked();
432 Q_ASSERT( d->mFileLocked );
433 Q_ASSERT( d->mMboxFile.isOpen() );
434 Q_ASSERT( ( d->mInitialMboxFileSize + d->mAppendedEntries.size() ) > offset );
438 if ( offset < d->mInitialMboxFileSize ) {
439 d->mMboxFile.seek( offset );
441 QByteArray line = d->mMboxFile.readLine();
443 if ( !d->isMBoxSeparator( line ) ) {
444 kDebug() <<
"[MBox::readEntry] Invalid entry at:" << offset;
451 line = d->mMboxFile.readLine();
452 while ( !d->isMBoxSeparator( line ) ) {
454 if ( d->mMboxFile.atEnd() ) {
457 line = d->mMboxFile.readLine();
460 offset -= d->mInitialMboxFileSize;
461 if ( offset > static_cast<quint64>( d->mAppendedEntries.size() ) ) {
468 QBuffer buffer( &( d->mAppendedEntries ) );
469 buffer.open( QIODevice::ReadOnly );
470 buffer.seek( offset );
472 QByteArray line = buffer.readLine();
474 if ( !d->isMBoxSeparator( line ) ) {
475 kDebug() <<
"[MBox::readEntry] Invalid appended entry at:" << offset;
482 line = buffer.readLine();
483 while ( !d->isMBoxSeparator( line ) && !buffer.atEnd() ) {
485 line = buffer.readLine();
490 if ( message.endsWith(
'\n' ) ) {
494 MBoxPrivate::unescapeFrom( message.data(), message.size() );
497 if ( !d->startTimerIfNeeded() ) {
498 const bool unlocked =
unlock();
499 Q_ASSERT( unlocked );
500 Q_UNUSED( unlocked );
510 if ( message.isEmpty() ) {
514 KMime::Message *mail =
new KMime::Message();
515 mail->setContent( KMime::CRLFtoLF( message ) );
523 const bool wasLocked = d->mFileLocked;
526 kDebug() <<
"Failed to lock";
533 Q_ASSERT( d->mFileLocked );
534 Q_ASSERT( d->mMboxFile.isOpen() );
535 Q_ASSERT( ( d->mInitialMboxFileSize + d->mAppendedEntries.size() ) > offset );
538 if ( offset < d->mInitialMboxFileSize ) {
539 d->mMboxFile.seek( offset );
540 QByteArray line = d->mMboxFile.readLine();
542 while ( line[0] !=
'\n' && !d->mMboxFile.atEnd() ) {
544 line = d->mMboxFile.readLine();
547 QBuffer buffer( &( d->mAppendedEntries ) );
548 buffer.open( QIODevice::ReadOnly );
549 buffer.seek( offset - d->mInitialMboxFileSize );
550 QByteArray line = buffer.readLine();
552 while ( line[0] !=
'\n' && !buffer.atEnd() ) {
554 line = buffer.readLine();
567 if ( !fileName.isEmpty() && KUrl( fileName ).toLocalFile() != d->mMboxFile.fileName() ) {
568 if ( !d->mMboxFile.copy( fileName ) ) {
572 if ( d->mAppendedEntries.size() == 0 ) {
576 QFile otherFile( fileName );
577 Q_ASSERT( otherFile.exists() );
578 if ( !otherFile.open( QIODevice::ReadWrite ) ) {
582 otherFile.seek( d->mMboxFile.size() );
583 otherFile.write( d->mAppendedEntries );
590 if ( d->mAppendedEntries.size() == 0 ) {
598 Q_ASSERT( d->mMboxFile.isOpen() );
600 d->mMboxFile.seek( d->mMboxFile.size() );
601 d->mMboxFile.write( d->mAppendedEntries );
602 d->mAppendedEntries.clear();
603 d->mInitialMboxFileSize = d->mMboxFile.size();
610 if ( d->mFileLocked ) {
611 kDebug() <<
"File is currently locked.";
616 case ProcmailLockfile:
617 if ( KStandardDirs::findExe( QLatin1String(
"lockfile" ) ).isEmpty() ) {
618 kDebug() <<
"Could not find the lockfile executable";
623 case MuttDotlockPrivileged:
624 if ( KStandardDirs::findExe( QLatin1String(
"mutt_dotlock" ) ).isEmpty() ) {
625 kDebug() <<
"Could not find the mutt_dotlock executable";
633 d->mLockType = ltype;
639 d->mLockFileName = lockFile;
644 d->mUnlockTimer.setInterval( msec );
649 if ( d->mLockType == None && !d->mFileLocked ) {
650 d->mFileLocked =
false;
651 d->mMboxFile.close();
658 switch ( d->mLockType ) {
659 case ProcmailLockfile:
661 if ( !d->mLockFileName.isEmpty() ) {
662 rc = !QFile( d->mLockFileName ).remove();
664 rc = !QFile( d->mMboxFile.fileName() + QLatin1String(
".lock" ) ).
remove();
669 args << QLatin1String(
"-u" )
670 << QString::fromLocal8Bit( QFile::encodeName( d->mMboxFile.fileName() ) );
671 rc = QProcess::execute( QLatin1String(
"mutt_dotlock" ), args );
674 case MuttDotlockPrivileged:
675 args << QLatin1String(
"-u" ) << QLatin1String(
"-p" )
676 << QString::fromLocal8Bit( QFile::encodeName( d->mMboxFile.fileName() ) );
677 rc = QProcess::execute( QLatin1String(
"mutt_dotlock" ), args );
686 d->mFileLocked =
false;
689 d->mMboxFile.close();
691 return !d->mFileLocked;
KMime::Message * readMessage(const MBoxEntry &entry)
Reads the entire message from the file for the given mbox entry.
bool save(const QString &fileName=QString())
Writes the mbox to disk.
bool unlock()
Unlock the mbox file.
MBox()
Creates a new mbox object.
QList< MBoxEntry > List
Describes a list of mbox entry objects.
void setUnlockTimeout(int msec)
By default the unlock method will directly unlock the file.
bool purge(const MBoxEntry::List &deletedEntries, QList< MBoxEntry::Pair > *movedEntries=0)
Removes all messages for the given mbox entries from the current reference file (i.e.
QByteArray readMessageHeaders(const MBoxEntry &entry)
Reads the headers of the message for the given mbox entry.
quint64 messageOffset() const
Returns the offset of the message that is referenced by this mbox entry object.
LockType
Describes the type of locking that will be used.
MBoxEntry appendMessage(const KMime::Message::Ptr &message)
Appends message to the MBox and returns the corresponding mbox entry for it.
quint64 messageSize() const
Returns the size of the message that is referenced by this mbox entry object.
~MBox()
Destroys the mbox object.
QString fileName() const
Returns the file name that was passed to the last call to load().
MBoxEntry::List entries(const MBoxEntry::List &deletedEntries=MBoxEntry::List()) const
Retrieve the mbox entry objects for all emails from the file except the deleteEntries.
A class that encapsulates an entry of a MBox.
bool locked() const
Returns whether or not the mbox currently is locked.
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.
void setLockFile(const QString &lockFile)
Sets the lockfile that should be used by the procmail or the KDE lock file method.
bool lock()
Locks the mbox file using the configured lock method.
QByteArray readRawMessage(const MBoxEntry &entry)
Reads the entire message from the file for the given mbox entry.
bool setLockType(LockType ltype)
Sets the locktype that should be used for locking the mbox file.
bool load(const QString &fileName)
Loads the raw mbox data from disk into the current MBox object.