mirror of
https://bitbucket.org/smil3y/kde-playground.git
synced 2025-02-23 18:32:51 +00:00
219 lines
5.3 KiB
C++
219 lines
5.3 KiB
C++
/*
|
|
Copyright (c) 2009 Bertjan Broeksema <broeksema@kde.org>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "mbox_p.h"
|
|
|
|
#include <KDebug>
|
|
#include <KUrl>
|
|
#include <QLocale>
|
|
|
|
using namespace KMBox;
|
|
|
|
MBoxPrivate::MBoxPrivate( MBox *mbox )
|
|
: mInitialMboxFileSize( 0 ), mMBox( mbox ), mReadOnly( false ),
|
|
mSeparatorMatcher( QLatin1String( "^From .*[0-9][0-9]:[0-9][0-9]" ) )
|
|
{
|
|
connect( &mUnlockTimer, SIGNAL(timeout()), SLOT(unlockMBox()) );
|
|
}
|
|
|
|
MBoxPrivate::~MBoxPrivate()
|
|
{
|
|
if ( mMboxFile.isOpen() ) {
|
|
mMboxFile.close();
|
|
}
|
|
}
|
|
|
|
bool MBoxPrivate::open()
|
|
{
|
|
if ( mMboxFile.isOpen() ) {
|
|
return true; // already open
|
|
}
|
|
|
|
if ( !mMboxFile.open( QIODevice::ReadWrite ) ) { // messages file
|
|
kDebug() << "Cannot open mbox file `" << mMboxFile.fileName() << "' FileError:"
|
|
<< mMboxFile.errorString();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void MBoxPrivate::close()
|
|
{
|
|
if ( mMboxFile.isOpen() ) {
|
|
mMboxFile.close();
|
|
}
|
|
|
|
mFileLocked = false;
|
|
}
|
|
|
|
void MBoxPrivate::initLoad( const QString &fileName )
|
|
{
|
|
mMboxFile.setFileName( KUrl( fileName ).toLocalFile() );
|
|
mAppendedEntries.clear();
|
|
mEntries.clear();
|
|
}
|
|
|
|
bool MBoxPrivate::startTimerIfNeeded()
|
|
{
|
|
if ( mUnlockTimer.interval() > 0 ) {
|
|
mUnlockTimer.start();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void MBoxPrivate::unlockMBox()
|
|
{
|
|
mMBox->unlock();
|
|
}
|
|
|
|
QByteArray MBoxPrivate::mboxMessageSeparator( const QByteArray &msg )
|
|
{
|
|
KMime::Message mail;
|
|
QByteArray body, header;
|
|
KMime::HeaderParsing::extractHeaderAndBody( KMime::CRLFtoLF( msg ), header, body );
|
|
body.clear();
|
|
mail.setHead( header );
|
|
mail.parse();
|
|
|
|
QByteArray separator = "From ";
|
|
|
|
KMime::Headers::From *from = mail.from( false );
|
|
if ( !from || from->addresses().isEmpty() ) {
|
|
separator += "unknown@unknown.invalid";
|
|
} else {
|
|
separator += from->addresses().first() + ' ';
|
|
}
|
|
|
|
// format dateTime according to the mbox "standard" RFC4155
|
|
KMime::Headers::Date *date = mail.date( false );
|
|
QDateTime dateTime;
|
|
if ( !date || date->isEmpty() ) {
|
|
dateTime = QDateTime::currentDateTimeUtc();
|
|
} else {
|
|
dateTime = date->dateTime().toUtc().dateTime();
|
|
}
|
|
separator += QLocale::c().toString(dateTime, QLatin1String("ddd MMM dd HH:mm:ss yyyy")).toUtf8() + '\n';
|
|
|
|
return separator;
|
|
}
|
|
|
|
#define STRDIM(x) (sizeof(x)/sizeof(*x)-1)
|
|
|
|
QByteArray MBoxPrivate::escapeFrom( const QByteArray &str )
|
|
{
|
|
const unsigned int strLen = str.length();
|
|
if ( strLen <= STRDIM( "From " ) ) {
|
|
return str;
|
|
}
|
|
|
|
// worst case: \nFrom_\nFrom_\nFrom_... => grows to 7/6
|
|
QByteArray result( int( strLen + 5 ) / 6 * 7 + 1, '\0' );
|
|
|
|
const char *s = str.data();
|
|
const char *const e = s + strLen - STRDIM( "From " );
|
|
char *d = result.data();
|
|
|
|
bool onlyAnglesAfterLF = false; // dont' match ^From_
|
|
while ( s < e ) {
|
|
switch ( *s ) {
|
|
case '\n':
|
|
onlyAnglesAfterLF = true;
|
|
break;
|
|
case '>':
|
|
break;
|
|
case 'F':
|
|
if ( onlyAnglesAfterLF && qstrncmp( s+1, "rom ", STRDIM( "rom " ) ) == 0 ) {
|
|
*d++ = '>';
|
|
}
|
|
// fall through
|
|
default:
|
|
onlyAnglesAfterLF = false;
|
|
break;
|
|
}
|
|
*d++ = *s++;
|
|
}
|
|
|
|
while ( s < str.data() + strLen ) {
|
|
*d++ = *s++;
|
|
}
|
|
|
|
result.truncate( d - result.data() );
|
|
|
|
return result;
|
|
}
|
|
|
|
// performs (\n|^)>{n}From_ -> \1>{n-1}From_ conversion
|
|
void MBoxPrivate::unescapeFrom( char *str, size_t strLen )
|
|
{
|
|
if ( !str ) {
|
|
return;
|
|
}
|
|
|
|
if ( strLen <= STRDIM( ">From " ) ) {
|
|
return;
|
|
}
|
|
|
|
// yes, *d++ = *s++ is a no-op as long as d == s (until after the
|
|
// first >From_), but writes are cheap compared to reads and the
|
|
// data is already in the cache from the read, so special-casing
|
|
// might even be slower...
|
|
const char *s = str;
|
|
char *d = str;
|
|
const char *const e = str + strLen - STRDIM( ">From " );
|
|
|
|
while ( s < e ) {
|
|
if ( *s == '\n' && *( s + 1 ) == '>' ) { // we can do the lookahead,
|
|
// since e is 6 chars from the end!
|
|
*d++ = *s++; // == '\n'
|
|
*d++ = *s++; // == '>'
|
|
|
|
while ( s < e && *s == '>' ) {
|
|
*d++ = *s++;
|
|
}
|
|
|
|
if ( qstrncmp( s, "From ", STRDIM( "From " ) ) == 0 ) {
|
|
--d;
|
|
}
|
|
}
|
|
|
|
*d++ = *s++; // yes, s might be e here, but e is not the end :-)
|
|
}
|
|
// copy the rest:
|
|
while ( s < str + strLen ) {
|
|
*d++ = *s++;
|
|
}
|
|
|
|
if ( d < s ) { // only NUL-terminate if it's shorter
|
|
*d = 0;
|
|
}
|
|
}
|
|
|
|
bool MBoxPrivate::isMBoxSeparator( const QByteArray &line ) const
|
|
{
|
|
if ( !line.startsWith( "From " ) ) { //krazy:exclude=strings
|
|
return false;
|
|
}
|
|
|
|
return mSeparatorMatcher.indexIn( QString::fromLatin1( line ) ) >= 0;
|
|
}
|
|
|
|
#undef STRDIM
|