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();
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:
239 if ( !d->mLockFileName.isEmpty() ) {
243 QLatin1String(
".lock" ) ) );
248 kDebug() <<
"lockfile -l20 -r5 " << d->mMboxFile.fileName()
249 <<
": Failed (" << rc <<
") switching to read only mode";
253 d->mFileLocked =
true;
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" )
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;
353 quint64 origFileSize = d->mMboxFile.
size();
359 if ( deletedEntries.
contains( entry ) && !writeOffSetInitialized ) {
361 writeOffSetInitialized =
true;
362 }
else if ( writeOffSetInitialized &&
364 !deletedEntries.
contains( entry ) ) {
367 quint64 entrySize = 0;
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 );
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 );
476 if ( !d->isMBoxSeparator( line ) ) {
477 kDebug() <<
"[MBox::readEntry] Invalid appended entry at:" << offset;
485 while ( !d->isMBoxSeparator( line ) && !buffer.
atEnd() ) {
496 MBoxPrivate::unescapeFrom( message.
data(), message.
size() );
499 if ( !d->startTimerIfNeeded() ) {
500 const bool unlocked =
unlock();
501 Q_ASSERT( unlocked );
502 Q_UNUSED( unlocked );
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 );
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 );
554 while ( line[0] !=
'\n' && !buffer.
atEnd() ) {
569 if ( !fileName.
isEmpty() && KUrl( fileName ).toLocalFile() != d->mMboxFile.fileName() ) {
570 if ( !d->mMboxFile.copy( fileName ) ) {
578 if ( d->mAppendedEntries.size() == 0 ) {
582 QFile otherFile( fileName );
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() ) {
683 case MuttDotlockPrivileged:
695 d->mFileLocked =
false;
698 d->mMboxFile.close();
700 return !d->mFileLocked;
KMime::Message * readMessage(const MBoxEntry &entry)
Reads the entire message from the file for the given mbox entry.
int execute(const QString &program, const QStringList &arguments)
bool save(const QString &fileName=QString())
Writes the mbox to disk.
virtual bool seek(qint64 pos)
bool unlock()
Unlock the mbox file.
MBox()
Creates a new mbox object.
void setReadOnly(bool ro=true)
Set the access mode of the mbox file to read only.
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 (the file that is loa...
QByteArray readMessageHeaders(const MBoxEntry &entry)
Reads the headers of the message for the given mbox entry.
bool setPermissions(QFlags< QFile::Permission > permissions)
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.
virtual bool atEnd() const
QString fromLocal8Bit(const char *str, int size)
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.
virtual bool open(QFlags< QIODevice::OpenModeFlag > flags)
bool locked() const
Returns whether or not the mbox currently is locked.
virtual bool open(QFlags< QIODevice::OpenModeFlag > mode)
quint64 separatorSize() const
Returns the separator size of the message that is referenced by this mbox entry object.
bool contains(const T &value) const
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.
qint64 write(const char *data, qint64 maxSize)
QByteArray readRawMessage(const MBoxEntry &entry)
Reads the entire message from the file for the given mbox entry.
virtual bool seek(qint64 pos)
bool endsWith(const QByteArray &ba) const
QByteArray encodeName(const QString &fileName)
bool setLockType(LockType ltype)
Sets the locktype that should be used for locking the mbox file.
qint64 readLine(char *data, qint64 maxSize)
bool isReadOnly() const
Returns if the current access mode is set to readOnly.
bool load(const QString &fileName)
Loads the raw mbox data from disk into the current MBox object.