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