KMBox Library
mbox.cpp
00001 /* 00002 Copyright (c) 1996-1998 Stefan Taferner <taferner@kde.org> 00003 Copyright (c) 2009 Bertjan Broeksema <broeksema@kde.org> 00004 00005 This library is free software; you can redistribute it and/or modify it 00006 under the terms of the GNU Library General Public License as published by 00007 the Free Software Foundation; either version 2 of the License, or (at your 00008 option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, but WITHOUT 00011 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00012 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 00013 License for more details. 00014 00015 You should have received a copy of the GNU Library General Public License 00016 along with this library; see the file COPYING.LIB. If not, write to the 00017 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 00018 02110-1301, USA. 00019 00020 NOTE: Most of the code inside here is an slightly adjusted version of 00021 kdepim/kmail/kmfoldermbox.cpp. This is why I added a line for Stefan 00022 Taferner. 00023 00024 Bertjan Broeksema, april 2009 00025 */ 00026 00027 #include "mbox.h" 00028 #include "mbox_p.h" 00029 #include "mboxentry_p.h" 00030 00031 #include <KDebug> 00032 #include <KStandardDirs> 00033 #include <KUrl> 00034 00035 #include <QtCore/QBuffer> 00036 #include <QtCore/QProcess> 00037 00038 using namespace KMBox; 00039 00041 00042 MBox::MBox() 00043 : d( new MBoxPrivate( this ) ) 00044 { 00045 // Set some sane defaults 00046 d->mFileLocked = false; 00047 d->mLockType = None; 00048 00049 d->mUnlockTimer.setInterval( 0 ); 00050 d->mUnlockTimer.setSingleShot( true ); 00051 } 00052 00053 MBox::~MBox() 00054 { 00055 if ( d->mFileLocked ) { 00056 unlock(); 00057 } 00058 00059 d->close(); 00060 00061 delete d; 00062 } 00063 00064 // Appended entries works as follows: When an mbox file is loaded from disk, 00065 // d->mInitialMboxFileSize is set to the file size at that moment. New entries 00066 // are stored in memory (d->mAppendedEntries). The initial file size and the size 00067 // of the buffer determine the offset for the next message to append. 00068 MBoxEntry MBox::appendMessage( const KMime::Message::Ptr &entry ) 00069 { 00070 // It doesn't make sense to add entries when we don't have an reference file. 00071 Q_ASSERT( !d->mMboxFile.fileName().isEmpty() ); 00072 00073 const QByteArray rawEntry = MBoxPrivate::escapeFrom( entry->encodedContent() ); 00074 00075 if ( rawEntry.size() <= 0 ) { 00076 kDebug() << "Message added to folder `" << d->mMboxFile.fileName() 00077 << "' contains no data. Ignoring it."; 00078 return MBoxEntry(); 00079 } 00080 00081 int nextOffset = d->mAppendedEntries.size(); // Offset of the appended message 00082 00083 // Make sure the byte array is large enough to check for an end character. 00084 // Then check if the required newlines are there. 00085 if ( nextOffset < 1 && d->mMboxFile.size() > 0 ) { // Empty, add one empty line 00086 d->mAppendedEntries.append( "\n" ); 00087 ++nextOffset; 00088 } else if ( nextOffset == 1 && d->mAppendedEntries.at( 0 ) != '\n' ) { 00089 // This should actually not happen, but catch it anyway. 00090 if ( d->mMboxFile.size() < 0 ) { 00091 d->mAppendedEntries.append( "\n" ); 00092 ++nextOffset; 00093 } 00094 } else if ( nextOffset >= 2 ) { 00095 if ( d->mAppendedEntries.at( nextOffset - 1 ) != '\n' ) { 00096 if ( d->mAppendedEntries.at( nextOffset ) != '\n' ) { 00097 d->mAppendedEntries.append( "\n\n" ); 00098 nextOffset += 2; 00099 } else { 00100 d->mAppendedEntries.append( "\n" ); 00101 ++nextOffset; 00102 } 00103 } 00104 } 00105 00106 const QByteArray separator = MBoxPrivate::mboxMessageSeparator( rawEntry ); 00107 d->mAppendedEntries.append( separator ); 00108 d->mAppendedEntries.append( rawEntry ); 00109 if ( rawEntry[rawEntry.size() - 1] != '\n' ) { 00110 d->mAppendedEntries.append( "\n\n" ); 00111 } else { 00112 d->mAppendedEntries.append( "\n" ); 00113 } 00114 00115 MBoxEntry resultEntry; 00116 resultEntry.d->mOffset = d->mInitialMboxFileSize + nextOffset; 00117 resultEntry.d->mMessageSize = rawEntry.size(); 00118 resultEntry.d->mSeparatorSize = separator.size(); 00119 d->mEntries << resultEntry; 00120 00121 return resultEntry; 00122 } 00123 00124 MBoxEntry::List MBox::entries( const MBoxEntry::List &deletedEntries ) const 00125 { 00126 MBoxEntry::List result; 00127 00128 foreach ( const MBoxEntry &entry, d->mEntries ) { 00129 if ( !deletedEntries.contains( entry ) ) { 00130 result << entry; 00131 } 00132 } 00133 00134 return result; 00135 } 00136 00137 QString MBox::fileName() const 00138 { 00139 return d->mMboxFile.fileName(); 00140 } 00141 00142 bool MBox::load( const QString &fileName ) 00143 { 00144 if ( d->mFileLocked ) { 00145 return false; 00146 } 00147 00148 d->initLoad( fileName ); 00149 00150 if ( !lock() ) { 00151 kDebug() << "Failed to lock"; 00152 return false; 00153 } 00154 00155 QByteArray line; 00156 QByteArray prevSeparator; 00157 quint64 offs = 0; // The offset of the next message to read. 00158 00159 while ( !d->mMboxFile.atEnd() ) { 00160 quint64 pos = d->mMboxFile.pos(); 00161 00162 line = d->mMboxFile.readLine(); 00163 00164 // if atEnd, use mail only if there was a separator line at all, 00165 // otherwise it's not a valid mbox 00166 if ( d->isMBoxSeparator( line ) || 00167 ( d->mMboxFile.atEnd() && ( prevSeparator.size() != 0 ) ) ) { 00168 00169 // Found the separator or at end of file, the message starts at offs 00170 quint64 msgSize = pos - offs; 00171 00172 if( pos > 0 ) { 00173 // This is not the separator of the first mail in the file. If pos == 0 00174 // than we matched the separator of the first mail in the file. 00175 MBoxEntry entry; 00176 entry.d->mOffset = offs; 00177 entry.d->mSeparatorSize = prevSeparator.size(); 00178 00179 // There is always a blank line and a separator line between two emails. 00180 // Sometimes there are two '\n' characters added to the email (i.e. when 00181 // the mail self did not end with a '\n' char) and sometimes only one to 00182 // achieve this. When reading the file it is not possible to see which 00183 // was the case. 00184 if ( d->mMboxFile.atEnd() ) { 00185 entry.d->mMessageSize = msgSize; // We use readLine so there's no additional '\n' 00186 } else { 00187 entry.d->mMessageSize = msgSize - 1; 00188 } 00189 00190 // Don't add the separator size and the newline up to the message size. 00191 entry.d->mMessageSize -= prevSeparator.size() + 1; 00192 00193 d->mEntries << entry; 00194 } 00195 00196 if ( d->isMBoxSeparator( line ) ) { 00197 prevSeparator = line; 00198 } 00199 00200 offs += msgSize; // Mark the beginning of the next message. 00201 } 00202 } 00203 00204 // FIXME: What if unlock fails? 00205 // if no separator was found, the file is still valid if it is empty 00206 return unlock() && ( ( prevSeparator.size() != 0 ) || ( d->mMboxFile.size() == 0 ) ); 00207 } 00208 00209 bool MBox::lock() 00210 { 00211 if ( d->mMboxFile.fileName().isEmpty() ) { 00212 return false; // We cannot lock if there is no file loaded. 00213 } 00214 00215 // We can't load another file when the mbox currently is locked so if d->mFileLocked 00216 // is true atm just return true. 00217 if ( locked() ) { 00218 return true; 00219 } 00220 00221 if ( d->mLockType == None ) { 00222 d->mFileLocked = true; 00223 if ( d->open() ) { 00224 d->startTimerIfNeeded(); 00225 return true; 00226 } 00227 00228 d->mFileLocked = false; 00229 return false; 00230 } 00231 00232 QStringList args; 00233 int rc = 0; 00234 00235 switch( d->mLockType ) { 00236 case ProcmailLockfile: 00237 args << QLatin1String( "-l20" ) << QLatin1String( "-r5" ); 00238 if ( !d->mLockFileName.isEmpty() ) { 00239 args << QString::fromLocal8Bit( QFile::encodeName( d->mLockFileName ) ); 00240 } else { 00241 args << QString::fromLocal8Bit( QFile::encodeName( d->mMboxFile.fileName() + 00242 QLatin1String( ".lock" ) ) ); 00243 } 00244 00245 rc = QProcess::execute( QLatin1String( "lockfile" ), args ); 00246 if ( rc != 0 ) { 00247 kDebug() << "lockfile -l20 -r5 " << d->mMboxFile.fileName() 00248 << ": Failed ("<< rc << ") switching to read only mode"; 00249 d->mReadOnly = true; // In case the MBox object was created read/write we 00250 // set it to read only when locking failed. 00251 } else { 00252 d->mFileLocked = true; 00253 } 00254 break; 00255 00256 case MuttDotlock: 00257 args << QString::fromLocal8Bit( QFile::encodeName( d->mMboxFile.fileName() ) ); 00258 rc = QProcess::execute( QLatin1String( "mutt_dotlock" ), args ); 00259 00260 if ( rc != 0 ) { 00261 kDebug() << "mutt_dotlock " << d->mMboxFile.fileName() 00262 << ": Failed (" << rc << ") switching to read only mode"; 00263 d->mReadOnly = true; // In case the MBox object was created read/write we 00264 // set it to read only when locking failed. 00265 } else { 00266 d->mFileLocked = true; 00267 } 00268 break; 00269 00270 case MuttDotlockPrivileged: 00271 args << QLatin1String( "-p" ) 00272 << QString::fromLocal8Bit( QFile::encodeName( d->mMboxFile.fileName() ) ); 00273 rc = QProcess::execute( QLatin1String( "mutt_dotlock" ), args ); 00274 00275 if ( rc != 0 ) { 00276 kDebug() << "mutt_dotlock -p " << d->mMboxFile.fileName() << ":" 00277 << ": Failed (" << rc << ") switching to read only mode"; 00278 d->mReadOnly = true; 00279 } else { 00280 d->mFileLocked = true; 00281 } 00282 break; 00283 00284 case None: 00285 d->mFileLocked = true; 00286 break; 00287 default: 00288 break; 00289 } 00290 00291 if ( d->mFileLocked ) { 00292 if ( !d->open() ) { 00293 const bool unlocked = unlock(); 00294 Q_ASSERT( unlocked ); // If this fails we're in trouble. 00295 Q_UNUSED( unlocked ); 00296 } 00297 } 00298 00299 d->startTimerIfNeeded(); 00300 return d->mFileLocked; 00301 } 00302 00303 bool MBox::locked() const 00304 { 00305 return d->mFileLocked; 00306 } 00307 00308 static bool lessThanByOffset( const MBoxEntry &left, const MBoxEntry &right ) 00309 { 00310 return left.messageOffset() < right.messageOffset(); 00311 } 00312 00313 bool MBox::purge( const MBoxEntry::List &deletedEntries, QList<MBoxEntry::Pair> *movedEntries ) 00314 { 00315 if ( d->mMboxFile.fileName().isEmpty() ) { 00316 return false; // No file loaded yet. 00317 } 00318 00319 if ( deletedEntries.isEmpty() ) { 00320 return true; // Nothing to do. 00321 } 00322 00323 if ( !lock() ) { 00324 return false; 00325 } 00326 00327 foreach ( const MBoxEntry &entry, deletedEntries ) { 00328 d->mMboxFile.seek( entry.messageOffset() ); 00329 const QByteArray line = d->mMboxFile.readLine(); 00330 00331 if ( !d->isMBoxSeparator( line ) ) { 00332 qDebug() << "Found invalid separator at:" << entry.messageOffset(); 00333 unlock(); 00334 return false; // The file is messed up or the index is incorrect. 00335 } 00336 } 00337 00338 // All entries are deleted, so just resize the file to a size of 0. 00339 if ( deletedEntries.size() == d->mEntries.size() ) { 00340 d->mEntries.clear(); 00341 d->mMboxFile.resize( 0 ); 00342 kDebug() << "Purge comleted successfully, unlocking the file."; 00343 return unlock(); 00344 } 00345 00346 qSort( d->mEntries.begin(), d->mEntries.end(), lessThanByOffset ); 00347 quint64 writeOffset = 0; 00348 bool writeOffSetInitialized = false; 00349 MBoxEntry::List resultingEntryList; 00350 QList<MBoxEntry::Pair> tmpMovedEntries; 00351 00352 quint64 origFileSize = d->mMboxFile.size(); 00353 00354 QListIterator<MBoxEntry> i( d->mEntries ); 00355 while ( i.hasNext() ) { 00356 MBoxEntry entry = i.next(); 00357 00358 if ( deletedEntries.contains( entry ) && !writeOffSetInitialized ) { 00359 writeOffset = entry.messageOffset(); 00360 writeOffSetInitialized = true; 00361 } else if ( writeOffSetInitialized && 00362 writeOffset < entry.messageOffset() && 00363 !deletedEntries.contains( entry ) ) { 00364 // The current message doesn't have to be deleted, but must be moved. 00365 // First determine the size of the entry that must be moved. 00366 quint64 entrySize = 0; 00367 if ( i.hasNext() ) { 00368 entrySize = i.next().messageOffset() - entry.messageOffset(); 00369 i.previous(); // Go back to make sure that we also handle the next entry. 00370 } else { 00371 entrySize = origFileSize - entry.messageOffset(); 00372 } 00373 00374 Q_ASSERT( entrySize > 0 ); // MBox entries really cannot have a size <= 0; 00375 00376 // we map the whole area of the file starting at the writeOffset up to the 00377 // message that have to be moved into memory. This includes eventually the 00378 // messages that are the deleted between the first deleted message 00379 // encountered and the message that has to be moved. 00380 quint64 mapSize = entry.messageOffset() + entrySize - writeOffset; 00381 00382 // Now map writeOffSet + mapSize into mem. 00383 uchar *memArea = d->mMboxFile.map( writeOffset, mapSize ); 00384 00385 // Now read the entry that must be moved to writeOffset. 00386 quint64 startOffset = entry.messageOffset() - writeOffset; 00387 memmove( memArea, memArea + startOffset, entrySize ); 00388 00389 d->mMboxFile.unmap( memArea ); 00390 00391 MBoxEntry resultEntry; 00392 resultEntry.d->mOffset = writeOffset; 00393 resultEntry.d->mSeparatorSize = entry.separatorSize(); 00394 resultEntry.d->mMessageSize = entry.messageSize(); 00395 00396 resultingEntryList << resultEntry; 00397 tmpMovedEntries << MBoxEntry::Pair( MBoxEntry( entry.messageOffset() ), 00398 MBoxEntry( resultEntry.messageOffset() ) ); 00399 writeOffset += entrySize; 00400 } else if ( !deletedEntries.contains( entry ) ) { 00401 // Unmoved and not deleted entry, can only ocure before the first deleted 00402 // entry. 00403 Q_ASSERT( !writeOffSetInitialized ); 00404 resultingEntryList << entry; 00405 } 00406 } 00407 00408 // Chop off remaining entry bits. 00409 d->mMboxFile.resize( writeOffset ); 00410 d->mEntries = resultingEntryList; 00411 00412 kDebug() << "Purge comleted successfully, unlocking the file."; 00413 if ( movedEntries ) { 00414 *movedEntries = tmpMovedEntries; 00415 } 00416 return unlock(); // FIXME: What if this fails? It will return false but the 00417 // file has changed. 00418 } 00419 00420 QByteArray MBox::readRawMessage( const MBoxEntry &entry ) 00421 { 00422 const bool wasLocked = locked(); 00423 if ( !wasLocked ) { 00424 if ( !lock() ) { 00425 return QByteArray(); 00426 } 00427 } 00428 00429 // TODO: Add error handling in case locking failed. 00430 00431 quint64 offset = entry.messageOffset(); 00432 00433 Q_ASSERT( d->mFileLocked ); 00434 Q_ASSERT( d->mMboxFile.isOpen() ); 00435 Q_ASSERT( ( d->mInitialMboxFileSize + d->mAppendedEntries.size() ) > offset ); 00436 00437 QByteArray message; 00438 00439 if ( offset < d->mInitialMboxFileSize ) { 00440 d->mMboxFile.seek( offset ); 00441 00442 QByteArray line = d->mMboxFile.readLine(); 00443 00444 if ( !d->isMBoxSeparator( line ) ) { 00445 kDebug() << "[MBox::readEntry] Invalid entry at:" << offset; 00446 if ( !wasLocked ) { 00447 unlock(); 00448 } 00449 return QByteArray(); // The file is messed up or the index is incorrect. 00450 } 00451 00452 line = d->mMboxFile.readLine(); 00453 while ( !d->isMBoxSeparator( line ) && !d->mMboxFile.atEnd() ) { 00454 message += line; 00455 line = d->mMboxFile.readLine(); 00456 } 00457 } else { 00458 offset -= d->mInitialMboxFileSize; 00459 if ( offset > static_cast<quint64>( d->mAppendedEntries.size() ) ) { 00460 if ( !wasLocked ) { 00461 unlock(); 00462 } 00463 return QByteArray(); 00464 } 00465 00466 QBuffer buffer( &(d->mAppendedEntries) ); 00467 buffer.open( QIODevice::ReadOnly ); 00468 buffer.seek( offset ); 00469 00470 QByteArray line = buffer.readLine(); 00471 00472 if ( !d->isMBoxSeparator( line ) ) { 00473 kDebug() << "[MBox::readEntry] Invalid appended entry at:" << offset; 00474 if ( !wasLocked ) { 00475 unlock(); 00476 } 00477 return QByteArray(); // The file is messed up or the index is incorrect. 00478 } 00479 00480 line = buffer.readLine(); 00481 while ( !d->isMBoxSeparator( line ) && !buffer.atEnd() ) { 00482 message += line; 00483 line = buffer.readLine(); 00484 } 00485 } 00486 00487 // Remove te last '\n' added by writeEntry. 00488 if ( message.endsWith( '\n' ) ) { 00489 message.chop( 1 ); 00490 } 00491 00492 MBoxPrivate::unescapeFrom( message.data(), message.size() ); 00493 00494 if ( !wasLocked ) { 00495 if ( !d->startTimerIfNeeded() ) { 00496 const bool unlocked = unlock(); 00497 Q_ASSERT( unlocked ); 00498 Q_UNUSED( unlocked ); 00499 } 00500 } 00501 00502 return message; 00503 } 00504 00505 KMime::Message *MBox::readMessage( const MBoxEntry &entry ) 00506 { 00507 const QByteArray message = readRawMessage( entry ); 00508 if ( message.isEmpty() ) { 00509 return 0; 00510 } 00511 00512 KMime::Message *mail = new KMime::Message(); 00513 mail->setContent( KMime::CRLFtoLF( message ) ); 00514 mail->parse(); 00515 00516 return mail; 00517 } 00518 00519 QByteArray MBox::readMessageHeaders( const MBoxEntry &entry ) 00520 { 00521 const bool wasLocked = d->mFileLocked; 00522 if ( !wasLocked ) { 00523 lock(); 00524 } 00525 00526 const quint64 offset = entry.messageOffset(); 00527 00528 Q_ASSERT( d->mFileLocked ); 00529 Q_ASSERT( d->mMboxFile.isOpen() ); 00530 Q_ASSERT( ( d->mInitialMboxFileSize + d->mAppendedEntries.size() ) > offset ); 00531 00532 QByteArray headers; 00533 if ( offset < d->mInitialMboxFileSize ) { 00534 d->mMboxFile.seek( offset ); 00535 QByteArray line = d->mMboxFile.readLine(); 00536 00537 while ( line[0] != '\n' && !d->mMboxFile.atEnd() ) { 00538 headers += line; 00539 line = d->mMboxFile.readLine(); 00540 } 00541 } else { 00542 QBuffer buffer( &(d->mAppendedEntries) ); 00543 buffer.open( QIODevice::ReadOnly ); 00544 buffer.seek( offset - d->mInitialMboxFileSize ); 00545 QByteArray line = buffer.readLine(); 00546 00547 while ( line[0] != '\n' && !buffer.atEnd() ) { 00548 headers += line; 00549 line = buffer.readLine(); 00550 } 00551 } 00552 00553 if ( !wasLocked ) { 00554 unlock(); 00555 } 00556 00557 return headers; 00558 } 00559 00560 bool MBox::save( const QString &fileName ) 00561 { 00562 if ( !fileName.isEmpty() && KUrl( fileName ).toLocalFile() != d->mMboxFile.fileName() ) { 00563 if ( !d->mMboxFile.copy( fileName ) ) { 00564 return false; 00565 } 00566 00567 if ( d->mAppendedEntries.size() == 0 ) { 00568 return true; // Nothing to do 00569 } 00570 00571 QFile otherFile( fileName ); 00572 Q_ASSERT( otherFile.exists() ); 00573 if ( !otherFile.open( QIODevice::ReadWrite ) ) { 00574 return false; 00575 } 00576 00577 otherFile.seek( d->mMboxFile.size() ); 00578 otherFile.write( d->mAppendedEntries ); 00579 00580 // Don't clear mAppendedEntries and don't update mInitialFileSize. These 00581 // are still valid for the original file. 00582 return true; 00583 } 00584 00585 if ( d->mAppendedEntries.size() == 0 ) { 00586 return true; // Nothing to do. 00587 } 00588 00589 if ( !lock() ) { 00590 return false; 00591 } 00592 00593 Q_ASSERT( d->mMboxFile.isOpen() ); 00594 00595 d->mMboxFile.seek( d->mMboxFile.size() ); 00596 d->mMboxFile.write( d->mAppendedEntries ); 00597 d->mAppendedEntries.clear(); 00598 d->mInitialMboxFileSize = d->mMboxFile.size(); 00599 00600 return unlock(); 00601 } 00602 00603 bool MBox::setLockType( LockType ltype ) 00604 { 00605 if ( d->mFileLocked ) { 00606 kDebug() << "File is currently locked."; 00607 return false; // Don't change the method if the file is currently locked. 00608 } 00609 00610 switch ( ltype ) { 00611 case ProcmailLockfile: 00612 if ( KStandardDirs::findExe( QLatin1String( "lockfile" ) ).isEmpty() ) { 00613 kDebug() << "Could not find the lockfile executable"; 00614 return false; 00615 } 00616 break; 00617 case MuttDotlock: // fall through 00618 case MuttDotlockPrivileged: 00619 if ( KStandardDirs::findExe( QLatin1String( "mutt_dotlock" ) ).isEmpty() ) { 00620 kDebug() << "Could not find the mutt_dotlock executable"; 00621 return false; 00622 } 00623 break; 00624 default: 00625 break; // We assume fcntl available and lock_none doesn't need a check. 00626 } 00627 00628 d->mLockType = ltype; 00629 return true; 00630 } 00631 00632 void MBox::setLockFile( const QString &lockFile ) 00633 { 00634 d->mLockFileName = lockFile; 00635 } 00636 00637 void MBox::setUnlockTimeout( int msec ) 00638 { 00639 d->mUnlockTimer.setInterval( msec ); 00640 } 00641 00642 bool MBox::unlock() 00643 { 00644 if ( d->mLockType == None && !d->mFileLocked ) { 00645 d->mFileLocked = false; 00646 d->mMboxFile.close(); 00647 return true; 00648 } 00649 00650 int rc = 0; 00651 QStringList args; 00652 00653 switch( d->mLockType ) { 00654 case ProcmailLockfile: 00655 // QFile::remove returns true on succes so negate the result. 00656 if ( !d->mLockFileName.isEmpty() ) { 00657 rc = !QFile( d->mLockFileName ).remove(); 00658 } else { 00659 rc = !QFile( d->mMboxFile.fileName() + QLatin1String( ".lock" ) ).remove(); 00660 } 00661 break; 00662 00663 case MuttDotlock: 00664 args << QLatin1String( "-u" ) 00665 << QString::fromLocal8Bit( QFile::encodeName( d->mMboxFile.fileName() ) ); 00666 rc = QProcess::execute( QLatin1String( "mutt_dotlock" ), args ); 00667 break; 00668 00669 case MuttDotlockPrivileged: 00670 args << QLatin1String( "-u" ) << QLatin1String( "-p" ) 00671 << QString::fromLocal8Bit( QFile::encodeName( d->mMboxFile.fileName() ) ); 00672 rc = QProcess::execute( QLatin1String( "mutt_dotlock" ), args ); 00673 break; 00674 00675 case None: // Fall through. 00676 default: 00677 break; 00678 } 00679 00680 if ( rc == 0 ) { // Unlocking succeeded 00681 d->mFileLocked = false; 00682 } 00683 00684 d->mMboxFile.close(); 00685 00686 return !d->mFileLocked; 00687 }