/* Copyright (c) 2007 Till Adam 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 "akonadi_serializer_mail.h" #include #include #include #include #include #include #include using namespace Akonadi; using namespace KMime; QString StringPool::sharedValue( const QString &value ) { QMutexLocker lock(&m_mutex); QSet::const_iterator it = m_pool.constFind(value); if ( it != m_pool.constEnd() ) return *it; m_pool.insert(value); return value; } template static void parseAddrList( const QVarLengthArray &addrList, T *hdr, int version, StringPool& pool ) { hdr->clear(); const int count = addrList.count(); QVarLengthArray addr; for ( int i = 0; i < count; ++i ) { ImapParser::parseParenthesizedList( addrList[ i ], addr ); if ( addr.count() != 4 ) { kWarning( 5264 ) << "Error parsing envelope address field: " << addrList[ i ]; continue; } KMime::Types::Mailbox addrField; if ( version == 0 ) addrField.setNameFrom7Bit( addr[0] ); else if ( version == 1 ) addrField.setName( pool.sharedValue( QString::fromUtf8( addr[0] ) ) ); KMime::Types::AddrSpec addrSpec; addrSpec.localPart = pool.sharedValue( QString::fromUtf8( addr[2] ) ); addrSpec.domain = pool.sharedValue( QString::fromUtf8( addr[3] ) ); addrField.setAddress( addrSpec ); hdr->addAddress( addrField ); } } bool SerializerPluginMail::deserialize( Item& item, const QByteArray& label, QIODevice& data, int version ) { if ( label != MessagePart::Body && label != MessagePart::Envelope && label != MessagePart::Header ) return false; KMime::Message::Ptr msg; if ( !item.hasPayload() ) { Message *m = new Message(); msg = KMime::Message::Ptr( m ); item.setPayload( msg ); } else { msg = item.payload(); } QByteArray buffer = data.readAll(); if ( buffer.isEmpty() ) return true; if ( label == MessagePart::Body ) { msg->setContent( buffer ); msg->parse(); } else if ( label == MessagePart::Header ) { if ( msg->body().isEmpty() && msg->contents().isEmpty() ) { msg->setHead( buffer ); msg->parse(); } } else if ( label == MessagePart::Envelope ) { QVarLengthArray env; ImapParser::parseParenthesizedList( buffer, env ); if ( env.count() < 10 ) { kWarning( 5264 ) << "Akonadi KMime Deserializer: Got invalid envelope: " << buffer; return false; } Q_ASSERT( env.count() >= 10 ); // date msg->date()->from7BitString( env[0] ); // subject msg->subject()->from7BitString( env[1] ); // from QVarLengthArray addrList; ImapParser::parseParenthesizedList( env[2], addrList ); if ( !addrList.isEmpty() ) parseAddrList( addrList, msg->from(), version, m_stringPool ); // sender ImapParser::parseParenthesizedList( env[3], addrList ); if ( !addrList.isEmpty() ) parseAddrList( addrList, msg->sender(), version, m_stringPool ); // reply-to ImapParser::parseParenthesizedList( env[4], addrList ); if ( !addrList.isEmpty() ) parseAddrList( addrList, msg->replyTo(), version, m_stringPool ); // to ImapParser::parseParenthesizedList( env[5], addrList ); if ( !addrList.isEmpty() ) parseAddrList( addrList, msg->to(), version, m_stringPool ); // cc ImapParser::parseParenthesizedList( env[6], addrList ); if ( !addrList.isEmpty() ) parseAddrList( addrList, msg->cc(), version, m_stringPool ); // bcc ImapParser::parseParenthesizedList( env[7], addrList ); if ( !addrList.isEmpty() ) parseAddrList( addrList, msg->bcc(), version, m_stringPool ); // in-reply-to msg->inReplyTo()->from7BitString( env[8] ); // message id msg->messageID()->from7BitString( env[9] ); // references if ( env.count() > 10 ) msg->references()->from7BitString( env[10] ); } return true; } static QByteArray quoteImapListEntry( const QByteArray &b ) { if ( b.isEmpty() ) return "NIL"; return ImapParser::quote( b ); } static QByteArray buildImapList( const QList &list ) { if ( list.isEmpty() ) return "NIL"; return QByteArray( "(" ) + ImapParser::join( list, " " ) + QByteArray( ")" ); } template static QByteArray buildAddrStruct( T const *hdr ) { QList addrList; KMime::Types::Mailbox::List mb = hdr->mailboxes(); foreach ( const KMime::Types::Mailbox &mbox, mb ) { QList addrStruct; addrStruct << quoteImapListEntry( mbox.name().toUtf8() ); addrStruct << quoteImapListEntry( QByteArray() ); addrStruct << quoteImapListEntry( mbox.addrSpec().localPart.toUtf8() ); addrStruct << quoteImapListEntry( mbox.addrSpec().domain.toUtf8() ); addrList << buildImapList( addrStruct ); } return buildImapList( addrList ); } void SerializerPluginMail::serialize( const Item& item, const QByteArray& label, QIODevice& data, int &version ) { version = 1; boost::shared_ptr m = item.payload< boost::shared_ptr >(); if ( label == MessagePart::Body ) { data.write( m->encodedContent() ); } else if ( label == MessagePart::Envelope ) { QList env; env << quoteImapListEntry( m->date()->as7BitString( false ) ); env << quoteImapListEntry( m->subject()->as7BitString( false ) ); env << buildAddrStruct( m->from() ); env << buildAddrStruct( m->sender() ); env << buildAddrStruct( m->replyTo() ); env << buildAddrStruct( m->to() ); env << buildAddrStruct( m->cc() ); env << buildAddrStruct( m->bcc() ); env << quoteImapListEntry( m->inReplyTo()->as7BitString( false ) ); env << quoteImapListEntry( m->messageID()->as7BitString( false ) ); env << quoteImapListEntry( m->references()->as7BitString( false ) ); data.write( buildImapList( env ) ); } else if ( label == MessagePart::Header ) { data.write( m->head() ); } } QSet SerializerPluginMail::parts( const Item &item ) const { QSet set; if ( !item.hasPayload() ) { return set; } KMime::Message::Ptr msg = item.payload(); if ( !msg ) { return set; } // FIXME: we really want "has any header" here, but the kmime api doesn't offer that yet if ( msg->hasContent() || msg->hasHeader( "Message-ID" ) ) { set << MessagePart::Envelope << MessagePart::Header; if ( !msg->body().isEmpty() || !msg->contents().isEmpty() ) { set << MessagePart::Body; } } return set; } QString SerializerPluginMail::extractGid(const Item& item) const { if (!item.hasPayload()) return QString(); const KMime::Message::Ptr msg = item.payload(); KMime::Headers::MessageID *mid = msg->messageID( false ); if (mid) return mid->asUnicodeString(); return QString(); } Q_EXPORT_PLUGIN2( akonadi_serializer_mail, SerializerPluginMail ) #include "moc_akonadi_serializer_mail.cpp"