2
Copyright (c) 2007 Volker Krause <vkrause@kde.org>
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.
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.
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
20
#include "nntpresource.h"
21
#include "nntpcollectionattribute.h"
22
#include "configdialog.h"
24
#include "settingsadaptor.h"
26
#include <akonadi/attributefactory.h>
27
#include <akonadi/cachepolicy.h>
28
#include <akonadi/collectionmodifyjob.h>
29
#include <akonadi/itemcreatejob.h>
30
#include <akonadi/kmime/messageparts.h>
31
#include <akonadi/monitor.h>
32
#include <akonadi/session.h>
33
#include <akonadi/changerecorder.h>
35
#include <kmime/kmime_message.h>
36
#include <kmime/kmime_newsarticle.h>
37
#include <kmime/kmime_util.h>
39
#include <KWindowSystem>
43
#include <QInputDialog>
46
#include <boost/shared_ptr.hpp>
47
typedef boost::shared_ptr<KMime::Message> MessagePtr;
49
using namespace Akonadi;
51
NntpResource::NntpResource(const QString & id)
54
AttributeFactory::registerAttribute<NntpCollectionAttribute>();
55
changeRecorder()->fetchCollection( true );
56
new SettingsAdaptor( Settings::self() );
57
QDBusConnection::sessionBus().registerObject( QLatin1String( "/Settings" ),
58
Settings::self(), QDBusConnection::ExportAdaptors );
61
NntpResource::~ NntpResource()
65
bool NntpResource::retrieveItem(const Akonadi::Item& item, const QSet<QByteArray> &parts)
68
KIO::Job* job = KIO::storedGet( KUrl( item.remoteId() ), KIO::NoReload, KIO::HideProgressInfo );
70
connect( job, SIGNAL( result(KJob*) ), SLOT( fetchArticleResult(KJob*) ) );
74
void NntpResource::retrieveCollections()
76
remoteCollections.clear();
77
Collection rootCollection;
78
rootCollection.setParent( Collection::root() );
79
rootCollection.setRemoteId( baseUrl().url() );
80
rootCollection.setName( Settings::self()->name() );
82
policy.setInheritFromParent( false );
83
policy.setSyncOnDemand( true );
84
policy.setLocalParts( QStringList( MessagePart::Envelope ) );
85
rootCollection.setCachePolicy( policy );
86
QStringList contentTypes;
87
contentTypes << Collection::mimeType();
88
rootCollection.setContentMimeTypes( contentTypes );
89
remoteCollections << rootCollection;
92
QDate lastList = Settings::self()->lastGroupList().date();
93
if ( lastList.isValid() ) {
95
url.addQueryItem( "since", QString("%1%2%3 000000")
96
.arg( lastList.year() % 100, 2, 10, QChar( '0' ) )
97
.arg( lastList.month(), 2, 10, QChar( '0' ) )
98
.arg( lastList.day(), 2, 10, QChar( '0' ) ) );
100
mIncremental = false;
103
KIO::Job* job = KIO::listDir( url, KIO::HideProgressInfo, true );
105
connect( job, SIGNAL(entries(KIO::Job*, const KIO::UDSEntryList&)),
106
SLOT(listGroups(KIO::Job*, const KIO::UDSEntryList&)) );
107
connect( job, SIGNAL( result(KJob*) ), SLOT( listGroupsResult(KJob*) ) );
110
void NntpResource::retrieveItems( const Akonadi::Collection & col )
112
if ( !(col.contentMimeTypes().count() == 1 && col.contentMimeTypes().first() == "message/news" ) ) {
113
// not a newsgroup, skip it
114
itemsRetrievalDone();
118
KUrl url = baseUrl();
119
url.setPath( col.remoteId() );
121
NntpCollectionAttribute *attr = col.attribute<NntpCollectionAttribute>();
122
if ( attr && attr->lastArticle() > 0 )
123
url.addQueryItem( "first", QString::number( attr->lastArticle() + 1 ) );
125
url.addQueryItem( "max", QString::number( Settings::self()->maxDownload() ) );
127
KIO::Job* job = KIO::listDir( url, KIO::HideProgressInfo, true );
129
connect( job, SIGNAL(entries(KIO::Job*, const KIO::UDSEntryList&)),
130
SLOT(listGroup(KIO::Job*, const KIO::UDSEntryList&)) );
131
connect( job, SIGNAL( result(KJob*) ), SLOT( listGroupResult(KJob*) ) );
134
void NntpResource::listGroups(KIO::Job * job, const KIO::UDSEntryList & list)
138
QStringList contentTypes;
139
contentTypes << "message/news";
140
for( KIO::UDSEntryList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it ) {
141
name = (*it).stringValue( KIO::UDSEntry::UDS_NAME );
142
if ( name.isEmpty() )
146
c.setRemoteId( name );
147
c.setContentMimeTypes( contentTypes );
149
if ( Settings::self()->flatHierarchy() ) {
151
c.setParentRemoteId( baseUrl().url() );
153
const QStringList path = name.split( '.' );
154
Q_ASSERT( !path.isEmpty() );
155
c.setName( path.last() );
156
c.setParentRemoteId( findParent( path ) );
159
remoteCollections << c;
163
void NntpResource::listGroupsResult(KJob * job)
165
if ( job->error() ) {
166
emit error( job->errorString() );
169
Settings::self()->setLastGroupList( QDateTime::currentDateTime() );
172
collectionsRetrievedIncremental( remoteCollections, Collection::List() );
174
collectionsRetrieved( remoteCollections );
177
void NntpResource::listGroup(KIO::Job * job, const KIO::UDSEntryList & list)
180
foreach ( const KIO::UDSEntry &entry, list ) {
181
KUrl url = baseUrl();
182
url.setPath( currentCollection().remoteId() + '/' + entry.stringValue( KIO::UDSEntry::UDS_NAME ) );
184
item.setRemoteId( url.url() );
185
item.setMimeType( "message/news" );
187
KMime::NewsArticle *art = new KMime::NewsArticle();
188
foreach ( uint field, entry.listFields() ) {
189
if ( field >= KIO::UDSEntry::UDS_EXTRA && field <= KIO::UDSEntry::UDS_EXTRA_END ) {
190
const QString value = entry.stringValue( field );
191
int pos = value.indexOf( ':' );
192
if ( pos >= value.length() - 1 )
193
continue; // value is empty
194
const QString hdrName = value.left( pos );
195
const QString hdrValue = value.right( value.length() - ( hdrName.length() + 2 ) );
197
if ( hdrName == "Subject" ) {
198
art->subject()->from7BitString( hdrValue.toLatin1() );
199
if ( art->subject()->isEmpty() )
200
art->subject()->fromUnicodeString( i18n("no subject"), art->defaultCharset() );
201
} else if ( hdrName == "From" ) {
202
art->from()->from7BitString( hdrValue.toLatin1() );
203
} else if ( hdrName == "Date" ) {
204
art->date()->from7BitString( hdrValue.toLatin1() );
205
} else if ( hdrName == "Message-ID" ) {
206
art->messageID()->from7BitString( hdrValue.simplified().toLatin1() );
207
} else if ( hdrName == "References" ) {
208
if( !hdrValue.isEmpty() )
209
art->references()->from7BitString( hdrValue.toLatin1() );
210
} else if ( hdrName == "Lines" ) {
211
art->lines()->setNumberOfLines( hdrValue.toInt() );
213
// optional extra headers
214
art->setHeader( new KMime::Headers::Generic( hdrName.toLatin1(), art, hdrValue.toLatin1() ) );
219
item.setPayload( MessagePtr( art ) );
220
ItemCreateJob *append = new ItemCreateJob( item, currentCollection() );
225
void NntpResource::listGroupResult(KJob * job)
227
if ( job->error() ) {
228
emit error( job->errorString() );
230
// store last serial number
231
Collection col = currentCollection();
232
NntpCollectionAttribute *attr = col.attribute<NntpCollectionAttribute>( Collection::AddIfMissing );
233
KIO::Job *j = static_cast<KIO::Job*>( job );
234
if ( j->metaData().contains( "LastSerialNumber" ) )
235
attr->setLastArticle( j->metaData().value("LastSerialNumber").toInt() );
236
CollectionModifyJob *modify = new CollectionModifyJob( col );
237
// TODO: check result signal
240
itemsRetrievalDone();
243
KUrl NntpResource::baseUrl() const
246
if ( Settings::self()->encryption() == Settings::SSL )
247
url.setProtocol( "nntps" );
249
url.setProtocol( "nntp" );
250
url.setHost( Settings::self()->server() );
251
url.setPort( Settings::self()->port() );
252
if ( Settings::self()->requiresAuthentication() ) {
253
url.setUser( Settings::self()->userName() );
254
url.setPass( Settings::self()->password() );
259
void NntpResource::fetchArticleResult(KJob * job)
261
if ( job->error() ) {
262
emit error( job->errorString() );
265
KIO::StoredTransferJob *j = static_cast<KIO::StoredTransferJob*>( job );
266
KMime::Message *msg = new KMime::Message();
267
msg->setContent( KMime::CRLFtoLF( j->data() ) );
269
Item item = currentItem();
270
item.setMimeType( "message/news" );
271
item.setPayload( MessagePtr( msg ) );
272
itemRetrieved( item );
275
void NntpResource::configure( WId windowId )
279
KWindowSystem::setMainWindow( &dlg, windowId );
281
if ( !Settings::self()->name().isEmpty() )
282
setName( Settings::self()->name() );
285
void NntpResource::setupKioJob(KIO::Job * job) const
288
if ( Settings::self()->encryption() == Settings::TLS )
289
job->addMetaData( "tls", "on" );
291
job->addMetaData( "tls", "off" );
292
// TODO connect percent and status message signals to something
295
QString NntpResource::findParent(const QStringList & _path)
297
QStringList path = _path;
299
if ( path.isEmpty() )
300
return baseUrl().url();
301
QString rid = path.join( "." );
302
foreach ( const Collection &col, remoteCollections )
303
if ( col.remoteId() == rid )
304
return col.remoteId();
306
parent.setRemoteId( rid );
308
ct << Collection::mimeType();
309
parent.setContentMimeTypes( ct );
310
parent.setParentRemoteId( findParent( path ) );
311
parent.setName( path.last() );
312
remoteCollections << parent;
313
return parent.remoteId();
316
void NntpResource::collectionChanged(const Akonadi::Collection & collection)
318
if ( collection.remoteId() == baseUrl().url() && !collection.name().isEmpty() ) {
319
Settings::self()->setName( collection.name() );
320
setName( collection.name() );
322
changeCommitted( collection );
325
AKONADI_RESOURCE_MAIN( NntpResource )
327
#include "nntpresource.moc"