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";
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() ) {
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;
399 MBoxEntry( resultEntry.messageOffset() ) );
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 ) ) {
574 if ( d->mAppendedEntries.size() == 0 ) {
578 QFile otherFile( fileName );
579 Q_ASSERT( otherFile.exists() );
580 if ( !otherFile.open( QIODevice::ReadWrite ) ) {
584 otherFile.seek( d->mMboxFile.size() );
585 otherFile.write( d->mAppendedEntries );
592 if ( d->mAppendedEntries.size() == 0 ) {
600 Q_ASSERT( d->mMboxFile.isOpen() );
602 d->mMboxFile.seek( d->mMboxFile.size() );
603 d->mMboxFile.write( d->mAppendedEntries );
604 d->mAppendedEntries.clear();
605 d->mInitialMboxFileSize = d->mMboxFile.size();
612 if ( d->mFileLocked ) {
613 kDebug() <<
"File is currently locked.";
618 case ProcmailLockfile:
619 if ( KStandardDirs::findExe( QLatin1String(
"lockfile" ) ).isEmpty() ) {
620 kDebug() <<
"Could not find the lockfile executable";
625 case MuttDotlockPrivileged:
626 if ( KStandardDirs::findExe( QLatin1String(
"mutt_dotlock" ) ).isEmpty() ) {
627 kDebug() <<
"Could not find the mutt_dotlock executable";
635 d->mLockType = ltype;
641 d->mLockFileName = lockFile;
646 d->mUnlockTimer.setInterval( msec );
651 if ( d->mLockType == None && !d->mFileLocked ) {
652 d->mFileLocked =
false;
653 d->mMboxFile.close();
660 switch ( d->mLockType ) {
661 case ProcmailLockfile:
663 if ( !d->mLockFileName.isEmpty() ) {
664 rc = !QFile( d->mLockFileName ).remove();
666 rc = !QFile( d->mMboxFile.fileName() + QLatin1String(
".lock" ) ).
remove();
671 args << QLatin1String(
"-u" )
672 << QString::fromLocal8Bit( QFile::encodeName( d->mMboxFile.fileName() ) );
673 rc = QProcess::execute( QLatin1String(
"mutt_dotlock" ), args );
676 case MuttDotlockPrivileged:
677 args << QLatin1String(
"-u" ) << QLatin1String(
"-p" )
678 << QString::fromLocal8Bit( QFile::encodeName( d->mMboxFile.fileName() ) );
679 rc = QProcess::execute( QLatin1String(
"mutt_dotlock" ), args );
688 d->mFileLocked =
false;
691 d->mMboxFile.close();
693 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.