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;
530 Q_ASSERT( d->mFileLocked );
531 Q_ASSERT( d->mMboxFile.isOpen() );
532 Q_ASSERT( ( d->mInitialMboxFileSize + d->mAppendedEntries.size() ) > offset );
535 if ( offset < d->mInitialMboxFileSize ) {
536 d->mMboxFile.seek( offset );
537 QByteArray line = d->mMboxFile.readLine();
539 while ( line[0] !=
'\n' && !d->mMboxFile.atEnd() ) {
541 line = d->mMboxFile.readLine();
544 QBuffer buffer( &( d->mAppendedEntries ) );
545 buffer.open( QIODevice::ReadOnly );
546 buffer.seek( offset - d->mInitialMboxFileSize );
547 QByteArray line = buffer.readLine();
549 while ( line[0] !=
'\n' && !buffer.atEnd() ) {
551 line = buffer.readLine();
564 if ( !fileName.isEmpty() && KUrl( fileName ).toLocalFile() != d->mMboxFile.fileName() ) {
565 if ( !d->mMboxFile.copy( fileName ) ) {
569 if ( d->mAppendedEntries.size() == 0 ) {
573 QFile otherFile( fileName );
574 Q_ASSERT( otherFile.exists() );
575 if ( !otherFile.open( QIODevice::ReadWrite ) ) {
579 otherFile.seek( d->mMboxFile.size() );
580 otherFile.write( d->mAppendedEntries );
587 if ( d->mAppendedEntries.size() == 0 ) {
595 Q_ASSERT( d->mMboxFile.isOpen() );
597 d->mMboxFile.seek( d->mMboxFile.size() );
598 d->mMboxFile.write( d->mAppendedEntries );
599 d->mAppendedEntries.clear();
600 d->mInitialMboxFileSize = d->mMboxFile.size();
607 if ( d->mFileLocked ) {
608 kDebug() <<
"File is currently locked.";
613 case ProcmailLockfile:
614 if ( KStandardDirs::findExe( QLatin1String(
"lockfile" ) ).isEmpty() ) {
615 kDebug() <<
"Could not find the lockfile executable";
620 case MuttDotlockPrivileged:
621 if ( KStandardDirs::findExe( QLatin1String(
"mutt_dotlock" ) ).isEmpty() ) {
622 kDebug() <<
"Could not find the mutt_dotlock executable";
630 d->mLockType = ltype;
636 d->mLockFileName = lockFile;
641 d->mUnlockTimer.setInterval( msec );
646 if ( d->mLockType == None && !d->mFileLocked ) {
647 d->mFileLocked =
false;
648 d->mMboxFile.close();
655 switch ( d->mLockType ) {
656 case ProcmailLockfile:
658 if ( !d->mLockFileName.isEmpty() ) {
659 rc = !QFile( d->mLockFileName ).remove();
661 rc = !QFile( d->mMboxFile.fileName() + QLatin1String(
".lock" ) ).
remove();
666 args << QLatin1String(
"-u" )
667 << QString::fromLocal8Bit( QFile::encodeName( d->mMboxFile.fileName() ) );
668 rc = QProcess::execute( QLatin1String(
"mutt_dotlock" ), args );
671 case MuttDotlockPrivileged:
672 args << QLatin1String(
"-u" ) << QLatin1String(
"-p" )
673 << QString::fromLocal8Bit( QFile::encodeName( d->mMboxFile.fileName() ) );
674 rc = QProcess::execute( QLatin1String(
"mutt_dotlock" ), args );
683 d->mFileLocked =
false;
686 d->mMboxFile.close();
688 return !d->mFileLocked;