1 | /* |
2 | Copyright (c) 2007 Till Adam <adam@kde.org> |
3 | |
4 | This library is free software; you can redistribute it and/or modify it |
5 | under the terms of the GNU Library General Public License as published by |
6 | the Free Software Foundation; either version 2 of the License, or (at your |
7 | option) any later version. |
8 | |
9 | This library is distributed in the hope that it will be useful, but WITHOUT |
10 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
11 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public |
12 | License for more details. |
13 | |
14 | You should have received a copy of the GNU Library General Public License |
15 | along with this library; see the file COPYING.LIB. If not, write to the |
16 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
17 | 02110-1301, USA. |
18 | */ |
19 | |
20 | #include "akonadi_serializer_mail.h" |
21 | |
22 | #include <QtCore/qplugin.h> |
23 | |
24 | #include <kdebug.h> |
25 | #include <kmime/kmime_message.h> |
26 | #include <boost/shared_ptr.hpp> |
27 | |
28 | #include <akonadi/item.h> |
29 | #include <akonadi/kmime/messageparts.h> |
30 | #include <akonadi/private/imapparser_p.h> |
31 | |
32 | using namespace Akonadi; |
33 | using namespace KMime; |
34 | |
35 | QString StringPool::sharedValue( const QString &value ) |
36 | { |
37 | QMutexLocker lock(&m_mutex); |
38 | QSet<QString>::const_iterator it = m_pool.constFind(value); |
39 | if ( it != m_pool.constEnd() ) |
40 | return *it; |
41 | m_pool.insert(value); |
42 | return value; |
43 | } |
44 | |
45 | template <typename T> static void parseAddrList( const QVarLengthArray<QByteArray, 16> &addrList, T *hdr, |
46 | int version, StringPool& pool ) |
47 | { |
48 | hdr->clear(); |
49 | const int count = addrList.count(); |
50 | QVarLengthArray<QByteArray, 16> addr; |
51 | for ( int i = 0; i < count; ++i ) { |
52 | ImapParser::parseParenthesizedList( addrList[ i ], addr ); |
53 | if ( addr.count() != 4 ) { |
54 | kWarning( 5264 ) << "Error parsing envelope address field: " << addrList[ i ]; |
55 | continue; |
56 | } |
57 | KMime::Types::Mailbox addrField; |
58 | if ( version == 0 ) |
59 | addrField.setNameFrom7Bit( addr[0] ); |
60 | else if ( version == 1 ) |
61 | addrField.setName( pool.sharedValue( QString::fromUtf8( addr[0] ) ) ); |
62 | KMime::Types::AddrSpec addrSpec; |
63 | addrSpec.localPart = pool.sharedValue( QString::fromUtf8( addr[2] ) ); |
64 | addrSpec.domain = pool.sharedValue( QString::fromUtf8( addr[3] ) ); |
65 | addrField.setAddress( addrSpec ); |
66 | hdr->addAddress( addrField ); |
67 | } |
68 | } |
69 | |
70 | |
71 | bool SerializerPluginMail::deserialize( Item& item, const QByteArray& label, QIODevice& data, int version ) |
72 | { |
73 | if ( label != MessagePart::Body && label != MessagePart::Envelope && label != MessagePart::Header ) |
74 | return false; |
75 | |
76 | KMime::Message::Ptr msg; |
77 | if ( !item.hasPayload() ) { |
78 | Message *m = new Message(); |
79 | msg = KMime::Message::Ptr( m ); |
80 | item.setPayload( msg ); |
81 | } else { |
82 | msg = item.payload<KMime::Message::Ptr>(); |
83 | } |
84 | |
85 | QByteArray buffer = data.readAll(); |
86 | if ( buffer.isEmpty() ) |
87 | return true; |
88 | if ( label == MessagePart::Body ) { |
89 | msg->setContent( buffer ); |
90 | msg->parse(); |
91 | } else if ( label == MessagePart::Header ) { |
92 | if ( msg->body().isEmpty() && msg->contents().isEmpty() ) { |
93 | msg->setHead( buffer ); |
94 | msg->parse(); |
95 | } |
96 | } else if ( label == MessagePart::Envelope ) { |
97 | QVarLengthArray<QByteArray, 16> env; |
98 | ImapParser::parseParenthesizedList( buffer, env ); |
99 | if ( env.count() < 10 ) { |
100 | kWarning( 5264 ) << "Akonadi KMime Deserializer: Got invalid envelope: " << buffer; |
101 | return false; |
102 | } |
103 | Q_ASSERT( env.count() >= 10 ); |
104 | // date |
105 | msg->date()->from7BitString( env[0] ); |
106 | // subject |
107 | msg->subject()->from7BitString( env[1] ); |
108 | // from |
109 | QVarLengthArray<QByteArray, 16> addrList; |
110 | ImapParser::parseParenthesizedList( env[2], addrList ); |
111 | if ( !addrList.isEmpty() ) |
112 | parseAddrList( addrList, msg->from(), version, m_stringPool ); |
113 | // sender |
114 | ImapParser::parseParenthesizedList( env[3], addrList ); |
115 | if ( !addrList.isEmpty() ) |
116 | parseAddrList( addrList, msg->sender(), version, m_stringPool ); |
117 | // reply-to |
118 | ImapParser::parseParenthesizedList( env[4], addrList ); |
119 | if ( !addrList.isEmpty() ) |
120 | parseAddrList( addrList, msg->replyTo(), version, m_stringPool ); |
121 | // to |
122 | ImapParser::parseParenthesizedList( env[5], addrList ); |
123 | if ( !addrList.isEmpty() ) |
124 | parseAddrList( addrList, msg->to(), version, m_stringPool ); |
125 | // cc |
126 | ImapParser::parseParenthesizedList( env[6], addrList ); |
127 | if ( !addrList.isEmpty() ) |
128 | parseAddrList( addrList, msg->cc(), version, m_stringPool ); |
129 | // bcc |
130 | ImapParser::parseParenthesizedList( env[7], addrList ); |
131 | if ( !addrList.isEmpty() ) |
132 | parseAddrList( addrList, msg->bcc(), version, m_stringPool ); |
133 | // in-reply-to |
134 | msg->inReplyTo()->from7BitString( env[8] ); |
135 | // message id |
136 | msg->messageID()->from7BitString( env[9] ); |
137 | // references |
138 | if ( env.count() > 10 ) |
139 | msg->references()->from7BitString( env[10] ); |
140 | } |
141 | |
142 | return true; |
143 | } |
144 | |
145 | static QByteArray quoteImapListEntry( const QByteArray &b ) |
146 | { |
147 | if ( b.isEmpty() ) |
148 | return "NIL" ; |
149 | return ImapParser::quote( b ); |
150 | } |
151 | |
152 | static QByteArray buildImapList( const QList<QByteArray> &list ) |
153 | { |
154 | if ( list.isEmpty() ) |
155 | return "NIL" ; |
156 | return QByteArray( "(" ) + ImapParser::join( list, " " ) + QByteArray( ")" ); |
157 | } |
158 | |
159 | template <typename T> static QByteArray buildAddrStruct( T const *hdr ) |
160 | { |
161 | QList<QByteArray> addrList; |
162 | KMime::Types::Mailbox::List mb = hdr->mailboxes(); |
163 | foreach ( const KMime::Types::Mailbox &mbox, mb ) { |
164 | QList<QByteArray> addrStruct; |
165 | addrStruct << quoteImapListEntry( mbox.name().toUtf8() ); |
166 | addrStruct << quoteImapListEntry( QByteArray() ); |
167 | addrStruct << quoteImapListEntry( mbox.addrSpec().localPart.toUtf8() ); |
168 | addrStruct << quoteImapListEntry( mbox.addrSpec().domain.toUtf8() ); |
169 | addrList << buildImapList( addrStruct ); |
170 | } |
171 | return buildImapList( addrList ); |
172 | } |
173 | |
174 | void SerializerPluginMail::serialize( const Item& item, const QByteArray& label, QIODevice& data, int &version ) |
175 | { |
176 | version = 1; |
177 | |
178 | boost::shared_ptr<Message> m = item.payload< boost::shared_ptr<Message> >(); |
179 | if ( label == MessagePart::Body ) { |
180 | data.write( m->encodedContent() ); |
181 | } else if ( label == MessagePart::Envelope ) { |
182 | QList<QByteArray> env; |
183 | env << quoteImapListEntry( m->date()->as7BitString( false ) ); |
184 | env << quoteImapListEntry( m->subject()->as7BitString( false ) ); |
185 | env << buildAddrStruct( m->from() ); |
186 | env << buildAddrStruct( m->sender() ); |
187 | env << buildAddrStruct( m->replyTo() ); |
188 | env << buildAddrStruct( m->to() ); |
189 | env << buildAddrStruct( m->cc() ); |
190 | env << buildAddrStruct( m->bcc() ); |
191 | env << quoteImapListEntry( m->inReplyTo()->as7BitString( false ) ); |
192 | env << quoteImapListEntry( m->messageID()->as7BitString( false ) ); |
193 | env << quoteImapListEntry( m->references()->as7BitString( false ) ); |
194 | data.write( buildImapList( env ) ); |
195 | } else if ( label == MessagePart::Header ) { |
196 | data.write( m->head() ); |
197 | } |
198 | } |
199 | |
200 | QSet<QByteArray> SerializerPluginMail::parts( const Item &item ) const |
201 | { |
202 | QSet<QByteArray> set; |
203 | |
204 | if ( !item.hasPayload<KMime::Message::Ptr>() ) { |
205 | return set; |
206 | } |
207 | |
208 | KMime::Message::Ptr msg = item.payload<KMime::Message::Ptr>(); |
209 | if ( !msg ) { |
210 | return set; |
211 | } |
212 | |
213 | // FIXME: we really want "has any header" here, but the kmime api doesn't offer that yet |
214 | if ( msg->hasContent() || msg->hasHeader( "Message-ID" ) ) { |
215 | set << MessagePart::Envelope << MessagePart::Header; |
216 | if ( !msg->body().isEmpty() || !msg->contents().isEmpty() ) { |
217 | set << MessagePart::Body; |
218 | } |
219 | } |
220 | return set; |
221 | } |
222 | |
223 | QString SerializerPluginMail::(const Item& item) const |
224 | { |
225 | if (!item.hasPayload<KMime::Message::Ptr>()) |
226 | return QString(); |
227 | const KMime::Message::Ptr msg = item.payload<KMime::Message::Ptr>(); |
228 | KMime::Headers::MessageID *mid = msg->messageID( false ); |
229 | if (mid) |
230 | return mid->asUnicodeString(); |
231 | return QString(); |
232 | } |
233 | |
234 | Q_EXPORT_PLUGIN2( akonadi_serializer_mail, SerializerPluginMail ) |
235 | |
236 | #include "moc_akonadi_serializer_mail.cpp" |
237 | |