1 // -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
3 #include "mailinglist.h"
6 #include <kconfiggroup.h>
10 #include <QtCore/QSharedData>
11 #include <QtCore/QStringList>
13 #include <boost/shared_ptr.hpp>
15 using namespace MessageCore
;
17 typedef QString (*MagicDetectorFunc
)( const KMime::Message::Ptr
&, QByteArray
&, QString
& );
19 /* Sender: (owner-([^@]+)|([^@+]-owner)@ */
20 static QString
check_sender( const KMime::Message::Ptr
&message
,
21 QByteArray
&headerName
,
22 QString
&headerValue
)
24 QString header
= message
->sender()->asUnicodeString();
26 if ( header
.isEmpty() )
29 if ( header
.left( 6 ) == QLatin1String("owner-") ) {
30 headerName
= "Sender";
32 header
= header
.mid( 6, header
.indexOf( '@' ) - 6 );
34 const int index
= header
.indexOf( "-owner@ " );
38 header
.truncate( index
);
39 headerName
= "Sender";
46 /* X-BeenThere: ([^@]+) */
47 static QString
check_x_beenthere( const KMime::Message::Ptr
&message
,
48 QByteArray
&headerName
,
49 QString
&headerValue
)
51 QString header
= message
->headerByType( "X-BeenThere" ) ? message
->headerByType( "X-BeenThere" )->asUnicodeString() : "";
52 if ( header
.isNull() || header
.indexOf( '@' ) == -1 )
55 headerName
= "X-BeenThere";
57 header
.truncate( header
.indexOf( '@' ) );
62 /* Delivered-To:: <([^@]+) */
63 static QString
check_delivered_to( const KMime::Message::Ptr
&message
,
64 QByteArray
&headerName
,
65 QString
&headerValue
)
67 QString header
= message
->headerByType( "Delivered-To" ) ? message
->headerByType( "Delivered-To" )->asUnicodeString() : "";
68 if ( header
.isNull() || header
.left( 13 ) != "mailing list"
69 || header
.indexOf( '@' ) == -1 )
72 headerName
= "Delivered-To";
75 return header
.mid( 13, header
.indexOf( '@' ) - 13 );
78 /* X-Mailing-List: <?([^@]+) */
79 static QString
check_x_mailing_list( const KMime::Message::Ptr
&message
,
80 QByteArray
&headerName
,
81 QString
&headerValue
)
83 QString header
= message
->headerByType( "X-Mailing-List" ) ? message
->headerByType( "X-Mailing-List" )->asUnicodeString() : "";
84 if ( header
.isEmpty() )
87 if ( header
.indexOf( '@' ) < 1 )
90 headerName
= "X-Mailing-List";
92 if ( header
[0] == '<' )
93 header
= header
.mid( 1, header
.indexOf( '@' ) - 1 );
95 header
.truncate( header
.indexOf( '@' ) );
100 /* List-Id: [^<]* <([^.]+) */
101 static QString
check_list_id( const KMime::Message::Ptr
&message
,
102 QByteArray
&headerName
,
103 QString
&headerValue
)
105 QString header
= message
->headerByType( "List-Id" ) ? message
->headerByType( "List-Id" )->asUnicodeString() : "";
106 if ( header
.isEmpty() )
109 const int leftAnglePos
= header
.indexOf( '<' );
110 if ( leftAnglePos
< 0 )
113 const int firstDotPos
= header
.indexOf( '.', leftAnglePos
);
114 if ( firstDotPos
< 0 )
117 headerName
= "List-Id";
118 headerValue
= header
.mid( leftAnglePos
);
119 header
= header
.mid( leftAnglePos
+ 1, firstDotPos
- leftAnglePos
- 1 );
125 /* List-Post: <mailto:[^< ]*>) */
126 static QString
check_list_post( const KMime::Message::Ptr
&message
,
127 QByteArray
&headerName
,
128 QString
&headerValue
)
130 QString header
= message
->headerByType( "List-Post" ) ? message
->headerByType( "List-Post" )->asUnicodeString() : "";
131 if ( header
.isEmpty() )
134 int leftAnglePos
= header
.indexOf( "<mailto:" );
135 if ( leftAnglePos
< 0 )
138 headerName
= "List-Post";
139 headerValue
= header
;
140 header
= header
.mid( leftAnglePos
+ 8, header
.length() );
141 header
.truncate( header
.indexOf( '@' ) );
146 /* Mailing-List: list ([^@]+) */
147 static QString
check_mailing_list( const KMime::Message::Ptr
&message
,
148 QByteArray
&headerName
,
149 QString
&headerValue
)
151 QString header
= message
->headerByType( "Mailing-List" ) ? message
->headerByType( "Mailing-List" )->asUnicodeString() : "";
152 if ( header
.isEmpty() )
155 if ( header
.left( 5 ) != "list " || header
.indexOf( '@' ) < 5 )
158 headerName
= "Mailing-List";
159 headerValue
= header
;
160 header
= header
.mid( 5, header
.indexOf( '@' ) - 5 );
166 /* X-Loop: ([^@]+) */
167 static QString
check_x_loop( const KMime::Message::Ptr
&message
,
168 QByteArray
&headerName
,
169 QString
&headerValue
)
171 QString header
= message
->headerByType( "X-Loop" ) ? message
->headerByType( "X-Loop" )->asUnicodeString() : "";
172 if ( header
.isEmpty() )
175 if (header
.indexOf( '@' ) < 2 )
178 headerName
= "X-Loop";
179 headerValue
= header
;
180 header
.truncate( header
.indexOf( '@' ) );
185 /* X-ML-Name: (.+) */
186 static QString
check_x_ml_name( const KMime::Message::Ptr
&message
,
187 QByteArray
&headerName
,
188 QString
&headerValue
)
190 QString header
= message
->headerByType( "X-ML-Name" ) ? message
->headerByType( "X-ML-Name" )->asUnicodeString() : "";
191 if ( header
.isEmpty() )
194 headerName
= "X-ML-Name";
195 headerValue
= header
;
196 header
.truncate( header
.indexOf( '@' ) );
201 MagicDetectorFunc magic_detector
[] =
206 check_x_mailing_list
,
214 static const int num_detectors
= sizeof( magic_detector
) / sizeof( magic_detector
[0] );
216 static QStringList
headerToAddress( const QString
&header
)
218 QStringList addresses
;
222 if ( header
.isEmpty() )
225 while ( (start
= header
.indexOf( "<", start
)) != -1 ) {
226 if ( (end
= header
.indexOf( ">", ++start
) ) == -1 ) {
227 kWarning() << "Serious mailing list header parsing error!";
231 addresses
.append( header
.mid( start
, end
- start
) );
237 class MessageCore::MailingList::Private
: public QSharedData
246 Private( const Private
&other
)
247 : QSharedData( other
)
249 mFeatures
= other
.mFeatures
;
250 mHandler
= other
.mHandler
;
251 mPostUrls
= other
.mPostUrls
;
252 mSubscribeUrls
= other
.mSubscribeUrls
;
253 mUnsubscribeUrls
= other
.mUnsubscribeUrls
;
254 mHelpUrls
= other
.mHelpUrls
;
255 mArchiveUrls
= other
.mArchiveUrls
;
256 mOwnerUrls
= other
.mOwnerUrls
;
257 mArchivedAtUrls
= other
.mArchivedAtUrls
;
263 KUrl::List mPostUrls
;
264 KUrl::List mSubscribeUrls
;
265 KUrl::List mUnsubscribeUrls
;
266 KUrl::List mHelpUrls
;
267 KUrl::List mArchiveUrls
;
268 KUrl::List mOwnerUrls
;
269 KUrl::List mArchivedAtUrls
;
273 MailingList
MailingList::detect( const KMime::Message::Ptr
&message
)
275 MailingList mailingList
;
277 if ( message
->headerByType( "List-Post" ) )
278 mailingList
.setPostUrls( headerToAddress( message
->headerByType( "List-Post" )->asUnicodeString() ) );
280 if ( message
->headerByType( "List-Help" ) )
281 mailingList
.setHelpUrls( headerToAddress( message
->headerByType( "List-Help" )->asUnicodeString() ) );
283 if ( message
->headerByType( "List-Subscribe" ) )
284 mailingList
.setSubscribeUrls( headerToAddress( message
->headerByType( "List-Subscribe" )->asUnicodeString() ) );
286 if ( message
->headerByType( "List-Unsubscribe" ) )
287 mailingList
.setUnsubscribeUrls( headerToAddress( message
->headerByType( "List-Unsubscribe" )->asUnicodeString() ) );
289 if ( message
->headerByType( "List-Archive" ) )
290 mailingList
.setArchiveUrls( headerToAddress( message
->headerByType( "List-Archive" )->asUnicodeString() ) );
292 if ( message
->headerByType( "List-Owner" ) )
293 mailingList
.setOwnerUrls( headerToAddress( message
->headerByType( "List-Owner" )->asUnicodeString() ) );
295 if ( message
->headerByType( "Archived-At" ) ) {
296 mailingList
.setArchivedAtUrls( headerToAddress( message
->headerByType( "Archived-At" )->asUnicodeString() ) );
299 if ( message
->headerByType( "List-Id" ) )
300 mailingList
.setId( message
->headerByType( "List-Id" )->asUnicodeString() );
305 QString
MailingList::name( const KMime::Message::Ptr
&message
,
306 QByteArray
&headerName
, QString
&headerValue
)
309 headerName
= QByteArray();
315 for ( int i
= 0; i
< num_detectors
; i
++ ) {
316 mailingList
= magic_detector
[i
]( message
, headerName
, headerValue
);
317 if ( !mailingList
.isNull() )
324 MailingList::MailingList()
329 MailingList::MailingList( const MailingList
&other
)
334 MailingList
& MailingList::operator=( const MailingList
&other
)
336 if ( this != &other
)
342 MailingList::~MailingList()
346 MailingList::Features
MailingList::features() const
351 void MailingList::setHandler( MailingList::Handler handler
)
353 d
->mHandler
= handler
;
356 MailingList::Handler
MailingList::handler() const
361 void MailingList::setPostUrls( const KUrl::List
&urls
)
363 d
->mFeatures
|= Post
;
365 if ( urls
.empty() ) {
366 d
->mFeatures
^= Post
;
372 KUrl::List
MailingList::postUrls() const
377 void MailingList::setSubscribeUrls( const KUrl::List
&urls
)
379 d
->mFeatures
|= Subscribe
;
381 if ( urls
.empty() ) {
382 d
->mFeatures
^= Subscribe
;
385 d
->mSubscribeUrls
= urls
;
388 KUrl::List
MailingList::subscribeUrls() const
390 return d
->mSubscribeUrls
;
393 void MailingList::setUnsubscribeUrls( const KUrl::List
&urls
)
395 d
->mFeatures
|= Unsubscribe
;
397 if ( urls
.empty() ) {
398 d
->mFeatures
^= Unsubscribe
;
401 d
->mUnsubscribeUrls
= urls
;
404 KUrl::List
MailingList::unsubscribeUrls() const
406 return d
->mUnsubscribeUrls
;
409 void MailingList::setHelpUrls( const KUrl::List
&urls
)
411 d
->mFeatures
|= Help
;
413 if ( urls
.empty() ) {
414 d
->mFeatures
^= Help
;
420 KUrl::List
MailingList::helpUrls() const
425 void MailingList::setArchiveUrls( const KUrl::List
&urls
)
427 d
->mFeatures
|= Archive
;
429 if ( urls
.empty() ) {
430 d
->mFeatures
^= Archive
;
433 d
->mArchiveUrls
= urls
;
436 KUrl::List
MailingList::archiveUrls() const
438 return d
->mArchiveUrls
;
441 void MailingList::setOwnerUrls( const KUrl::List
&urls
)
443 d
->mFeatures
|= Owner
;
445 if ( urls
.empty() ) {
446 d
->mFeatures
^= Owner
;
449 d
->mOwnerUrls
= urls
;
452 KUrl::List
MailingList::ownerUrls() const
454 return d
->mOwnerUrls
;
457 void MailingList::setArchivedAtUrls( const KUrl::List
&urls
)
459 d
->mFeatures
|= ArchivedAt
;
461 if ( urls
.isEmpty() ) {
462 d
->mFeatures
^= ArchivedAt
;
465 d
->mArchivedAtUrls
= urls
;
468 KUrl::List
MailingList::archivedAtUrls() const
470 return d
->mArchivedAtUrls
;
473 void MailingList::setId( const QString
&id
)
477 if ( id
.isEmpty() ) {
484 QString
MailingList::id() const
489 void MailingList::writeConfig( KConfigGroup
&group
) const
491 group
.writeEntry( "MailingListFeatures", static_cast<int>( d
->mFeatures
) );
492 group
.writeEntry( "MailingListHandler", static_cast<int>( d
->mHandler
) );
493 group
.writeEntry( "MailingListId", d
->mId
);
494 group
.writeEntry( "MailingListPostingAddress", d
->mPostUrls
.toStringList() );
495 group
.writeEntry( "MailingListSubscribeAddress", d
->mSubscribeUrls
.toStringList() );
496 group
.writeEntry( "MailingListUnsubscribeAddress", d
->mUnsubscribeUrls
.toStringList() );
497 group
.writeEntry( "MailingListArchiveAddress", d
->mArchiveUrls
.toStringList() );
498 group
.writeEntry( "MailingListOwnerAddress", d
->mOwnerUrls
.toStringList() );
499 group
.writeEntry( "MailingListHelpAddress", d
->mHelpUrls
.toStringList() );
500 /* Note: mArchivedAtUrl deliberately not saved here as it refers to a single
501 * instance of a message rather than an element of a general mailing list.
502 * http://reviewboard.kde.org/r/1768/#review2783
506 void MailingList::readConfig( const KConfigGroup
&group
)
508 d
->mFeatures
= static_cast<MailingList::Features
>( group
.readEntry( "MailingListFeatures", 0 ) );
509 d
->mHandler
= static_cast<MailingList::Handler
>( group
.readEntry( "MailingListHandler",
510 static_cast<int>( MailingList::KMail
) ) );
511 d
->mId
= group
.readEntry("MailingListId");
512 d
->mPostUrls
= group
.readEntry( "MailingListPostingAddress", QStringList() );
513 d
->mSubscribeUrls
= group
.readEntry( "MailingListSubscribeAddress", QStringList() );
514 d
->mUnsubscribeUrls
= group
.readEntry( "MailingListUnsubscribeAddress", QStringList() );
515 d
->mArchiveUrls
= group
.readEntry( "MailingListArchiveAddress", QStringList() );
516 d
->mOwnerUrls
= group
.readEntry( "MailingListOwnerddress", QStringList() );
517 d
->mHelpUrls
= group
.readEntry( "MailingListHelpAddress", QStringList() );