2 KNode, the KDE newsreader
3 Copyright (c) 1999-2006 the KNode authors.
4 See file AUTHORS for details
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 You should have received a copy of the GNU General Public License
11 along with this program; if not, write to the Free Software Foundation,
12 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US
15 #include "kngroupmanager.h"
17 #include "articlewidget.h"
18 #include "knmainwidget.h"
19 #include "knarticlemanager.h"
20 #include "kngroupdialog.h"
21 #include "knnntpaccount.h"
22 #include "kncleanup.h"
23 #include "scheduler.h"
24 #include "knglobals.h"
25 #include "knconfigmanager.h"
28 #include "knarticlewindow.h"
29 #include "knmemorymanager.h"
31 #include "utils/locale.h"
39 #include <kmessagebox.h>
40 #include <kiconloader.h>
42 #include <kcharsets.h>
45 using namespace KNode
;
46 using namespace KNode::Utilities
;
49 //=================================================================================
51 // helper classes for the group selection dialog (getting the server's grouplist,
52 // getting recently created groups)
55 KNGroupInfo::KNGroupInfo()
60 KNGroupInfo::KNGroupInfo(const QString
&n_ame
, const QString
&d_escription
, bool n_ewGroup
, bool s_ubscribed
, KNGroup::Status s_tatus
)
61 : name(n_ame
), description(d_escription
), newGroup(n_ewGroup
), subscribed(s_ubscribed
),
67 KNGroupInfo::~KNGroupInfo()
72 bool KNGroupInfo::operator== (const KNGroupInfo
&gi2
) const
74 return (name
== gi2
.name
);
78 bool KNGroupInfo::operator< (const KNGroupInfo
&gi2
) const
80 return (name
< gi2
.name
);
84 //===============================================================================
87 KNGroupListData::KNGroupListData()
88 : codecForDescriptions(0)
90 groups
= new QList
<KNGroupInfo
>;
95 KNGroupListData::~KNGroupListData()
102 bool KNGroupListData::readIn(KNJobData
*job
)
104 QFile
f( path
+ "groups" );
107 QString name
,description
;
109 KNGroup::Status status
=KNGroup::unknown
;
111 uint size
=f
.size()+2;
118 if(f
.open(QIODevice::ReadOnly
)) {
121 sepPos1
= line
.indexOf( ' ' );
123 if (sepPos1
==-1) { // no description
124 name
= QString::fromUtf8(line
);
126 status
= KNGroup::unknown
;
128 name
= QString::fromUtf8(line
.left(sepPos1
));
130 sepPos2
= line
.indexOf( ' ', sepPos1
+ 1 );
131 if (sepPos2
==-1) { // no status
132 description
= QString::fromUtf8(line
.right(line
.length()-sepPos1
-1));
133 status
= KNGroup::unknown
;
135 description
= QString::fromUtf8( line
.right( line
.length() - sepPos2
- 1 ).trimmed() );
136 switch (line
[sepPos1
+1]) {
137 case 'u': status
= KNGroup::unknown
;
139 case 'n': status
= KNGroup::readOnly
;
141 case 'y': status
= KNGroup::postingAllowed
;
143 case 'm': status
= KNGroup::moderated
;
149 if (subscribed
.contains(name
)) {
150 subscribed
.removeAll( name
); // group names are unique, we wont find it again anyway...
155 groups
->append(KNGroupInfo(name
,description
,false,sub
,status
));
157 if (timer
.elapsed() > 200) { // don't flicker
160 job
->setProgress( (f
.pos()*100)/size
);
168 kWarning(5003) <<"unable to open" << f
.fileName() <<" reason" << f
.error();
175 bool KNGroupListData::writeOut()
177 QFile
f(path
+"groups");
180 if(f
.open(QIODevice::WriteOnly
)) {
181 Q_FOREACH(const KNGroupInfo
& i
, *groups
) {
182 temp
= i
.name
.toUtf8();
184 case KNGroup::unknown
: temp
+= " u ";
186 case KNGroup::readOnly
: temp
+= " n ";
188 case KNGroup::postingAllowed
: temp
+= " y ";
190 case KNGroup::moderated
: temp
+= " m ";
193 temp
+= i
.description
.toUtf8() + '\n';
194 f
.write(temp
.data(),temp
.length());
199 kWarning(5003) <<"unable to open" << f
.fileName() <<" reason" << f
.error();
206 // merge in new groups, we want to preserve the "subscribed"-flag
207 // of the loaded groups and the "new"-flag of the new groups.
208 void KNGroupListData::merge(QList
<KNGroupInfo
>* newGroups
)
212 Q_FOREACH(const KNGroupInfo
& i
, *newGroups
) {
214 if ( (current
=groups
->indexOf(i
)) != -1) {
215 subscribed
= groups
->at(current
).subscribed
;
216 groups
->removeAt(current
); // avoid duplicates
219 groups
->append(KNGroupInfo(i
.name
,i
.description
,true,subscribed
,i
.status
));
224 QList
<KNGroupInfo
>* KNGroupListData::extractList()
226 QList
<KNGroupInfo
>* temp
= groups
;
232 //===============================================================================
235 KNGroupManager::KNGroupManager( QObject
* parent
)
239 a_rticleMgr
= knGlobals
.articleManager();
243 KNGroupManager::~KNGroupManager()
245 qDeleteAll( mGroupList
);
249 void KNGroupManager::syncGroups()
251 for ( KNGroup::List::Iterator it
= mGroupList
.begin(); it
!= mGroupList
.end(); ++it
) {
252 (*it
)->syncDynamicData();
258 void KNGroupManager::loadGroups(KNNntpAccount
*a
)
262 QString
dir(a
->path());
267 QStringList
entries(d
.entryList(QStringList("*.grpinfo")));
268 for(QStringList::Iterator it
=entries
.begin(); it
!= entries
.end(); ++it
) {
269 group
=new KNGroup(a
);
270 if (group
->readInfo(dir
+(*it
))) {
271 mGroupList
.append( group
);
272 emit
groupAdded(group
);
275 kError(5003) <<"Unable to load" << (*it
) <<"!";
281 void KNGroupManager::getSubscribed(KNNntpAccount
*a
, QStringList
&l
)
284 for ( KNGroup::List::Iterator it
= mGroupList
.begin(); it
!= mGroupList
.end(); ++it
)
285 if ( (*it
)->account() == a
)
286 l
.append( (*it
)->groupname() );
290 KNGroup::List
KNGroupManager::groupsOfAccount( KNNntpAccount
*a
)
293 for ( KNGroup::List::Iterator it
= mGroupList
.begin(); it
!= mGroupList
.end(); ++it
)
294 if ( (*it
)->account() == a
)
300 bool KNGroupManager::loadHeaders(KNGroup
*g
)
308 // we want to delete old stuff first => reduce vm fragmentation
309 knGlobals
.memoryManager()->prepareLoad(g
);
312 knGlobals
.memoryManager()->updateCacheEntry( g
);
320 bool KNGroupManager::unloadHeaders(KNGroup
*g
, bool force
)
322 if(!g
|| g
->isLocked())
328 if (!force
&& (c_urrentGroup
== g
))
331 if (g
->unloadHdrs(force
))
332 knGlobals
.memoryManager()->removeCacheEntry(g
);
340 KNGroup
* KNGroupManager::group(const QString
&gName
, const KNServerInfo
*s
)
342 for ( KNGroup::List::Iterator it
= mGroupList
.begin(); it
!= mGroupList
.end(); ++it
)
343 if ( (*it
)->account() == s
&& (*it
)->groupname() == gName
)
350 KNGroup
* KNGroupManager::firstGroupOfAccount(const KNServerInfo
*s
)
352 for ( KNGroup::List::Iterator it
= mGroupList
.begin(); it
!= mGroupList
.end(); ++it
)
353 if ( (*it
)->account() == s
)
360 void KNGroupManager::expireAll(KNCleanUp
*cup
)
362 for ( KNGroup::List::Iterator it
= mGroupList
.begin(); it
!= mGroupList
.end(); ++it
) {
363 if( (*it
)->isLocked() || (*it
)->lockedArticles() > 0 )
365 if ( !(*it
)->activeCleanupConfig()->expireToday() )
367 cup
->appendCollection( *(it
) );
372 void KNGroupManager::expireAll(KNNntpAccount
*a
)
374 KNCleanUp
*cup
= new KNCleanUp();
376 for ( KNGroup::List::Iterator it
= mGroupList
.begin(); it
!= mGroupList
.end(); ++it
) {
377 if( (*it
)->account() != a
|| (*it
)->isLocked() || (*it
)->lockedArticles() > 0 )
380 ArticleWindow::closeAllWindowsForCollection( (*it
) );
381 cup
->appendCollection( (*it
) );
386 for ( KNGroup::List::Iterator it
= mGroupList
.begin(); it
!= mGroupList
.end(); ++it
) {
387 if( (*it
)->account() != a
|| (*it
)->isLocked() || (*it
)->lockedArticles() > 0 )
390 emit
groupUpdated( (*it
) );
391 if ( (*it
) == c_urrentGroup
) {
392 if ( loadHeaders( (*it
) ) )
393 a_rticleMgr
->showHdrs();
395 a_rticleMgr
->setGroup(0);
403 void KNGroupManager::showGroupDialog(KNNntpAccount
*a
, QWidget
*parent
)
405 KNGroupDialog
* gDialog
=new KNGroupDialog((parent
!=0)? parent
:knGlobals
.topWidget
, a
);
407 connect(gDialog
, SIGNAL(loadList(KNNntpAccount
*)), this, SLOT(slotLoadGroupList(KNNntpAccount
*)));
408 connect(gDialog
, SIGNAL(fetchList(KNNntpAccount
*)), this, SLOT(slotFetchGroupList(KNNntpAccount
*)));
409 connect(gDialog
, SIGNAL(checkNew(KNNntpAccount
*,QDate
)), this, SLOT(slotCheckForNewGroups(KNNntpAccount
*,QDate
)));
410 connect(this, SIGNAL(newListReady(KNGroupListData
*)), gDialog
, SLOT(slotReceiveList(KNGroupListData
*)));
412 QWidget
*oldTopWidget
= knGlobals
.topWidget
;
413 // if the list of groups is empty, the parent of the message box
414 // asking whether to fetch will be "knGlobals.topWidget"
415 knGlobals
.topWidget
= gDialog
;
416 int accept
= gDialog
->exec();
417 knGlobals
.topWidget
= oldTopWidget
;
422 gDialog
->toUnsubscribe(&lst
);
424 if (KMessageBox::Yes
== KMessageBox::questionYesNoList((parent
!=0)? parent
:knGlobals
.topWidget
,i18n("Do you really want to unsubscribe\nfrom these groups?"),
425 lst
, QString(), KGuiItem(i18n("Unsubscribe")), KStandardGuiItem::cancel())) {
426 for ( QStringList::Iterator it
= lst
.begin(); it
!= lst
.end(); ++it
) {
427 if((g
=group(*it
, a
)))
433 QList
<KNGroupInfo
> lst2
;
434 gDialog
->toSubscribe(&lst2
);
435 Q_FOREACH( const KNGroupInfo
& var
, lst2
) {
436 subscribeGroup(&var
, a
);
444 void KNGroupManager::subscribeGroup(const KNGroupInfo
*gi
, KNNntpAccount
*a
)
449 grp
->setGroupname(gi
->name
);
450 grp
->setDescription(gi
->description
);
451 grp
->setStatus(gi
->status
);
453 mGroupList
.append( grp
);
454 emit
groupAdded(grp
);
458 bool KNGroupManager::unsubscribeGroup(KNGroup
*g
)
461 if(!g
) g
=c_urrentGroup
;
464 if((g
->isLocked()) || (g
->lockedArticles()>0)) {
465 KMessageBox::sorry(knGlobals
.topWidget
, i18n("The group \"%1\" is being updated currently.\nIt is not possible to unsubscribe from it at the moment.", g
->groupname()));
469 ArticleWindow::closeAllWindowsForCollection( g
);
470 ArticleWidget::collectionRemoved( g
);
474 QDir
dir( acc
->path(), g
->groupname() + '*' );
476 if (unloadHeaders(g
, true)) {
477 if(c_urrentGroup
==g
) {
479 a_rticleMgr
->updateStatusString();
482 QFileInfoList list
= dir
.entryInfoList(); // get list of matching files and delete all
483 Q_FOREACH( const QFileInfo
&it
, list
) {
484 if ( it
.fileName() == g
->groupname()+".dynamic" ||
485 it
.fileName() == g
->groupname()+".static" ||
486 it
.fileName() == g
->groupname()+".grpinfo" )
487 dir
.remove( it
.fileName() );
489 kDebug(5003) <<"Files deleted!";
491 emit
groupRemoved(g
);
492 mGroupList
.removeAll( g
);
503 void KNGroupManager::showGroupProperties(KNGroup
*g
)
505 if(!g
) g
=c_urrentGroup
;
511 void KNGroupManager::checkGroupForNewHeaders(KNGroup
*g
)
513 if(!g
) g
=c_urrentGroup
;
516 kDebug(5003) <<"KNGroupManager::checkGroupForNewHeaders() : group locked - returning";
520 g
->setMaxFetch( knGlobals
.settings()->maxToFetch() );
521 emitJob( new ArticleListJob( this, g
->account(), g
) );
525 void KNGroupManager::expireGroupNow(KNGroup
*g
)
529 if((g
->isLocked()) || (g
->lockedArticles()>0)) {
530 KMessageBox::sorry(knGlobals
.topWidget
,
531 i18n("This group cannot be expired because it is currently being updated.\n Please try again later."));
535 ArticleWindow::closeAllWindowsForCollection( g
);
538 cup
.expireGroup(g
, true);
540 emit
groupUpdated(g
);
541 if(g
==c_urrentGroup
) {
543 a_rticleMgr
->showHdrs();
545 a_rticleMgr
->setGroup(0);
550 void KNGroupManager::reorganizeGroup(KNGroup
*g
)
552 if(!g
) g
=c_urrentGroup
;
556 a_rticleMgr
->showHdrs();
560 void KNGroupManager::setCurrentGroup(KNGroup
*g
)
563 a_rticleMgr
->setGroup(g
);
564 kDebug(5003) <<"KNGroupManager::setCurrentGroup() : group changed";
567 if( !loadHeaders(g
) ) {
568 //KMessageBox::error(knGlobals.topWidget, i18n("Cannot load saved headers"));
571 a_rticleMgr
->showHdrs();
572 if ( knGlobals
.settings()->autoCheckGroups() )
573 checkGroupForNewHeaders(g
);
578 void KNGroupManager::checkAll(KNNntpAccount
*a
, bool silent
)
582 for ( KNGroup::List::Iterator it
= mGroupList
.begin(); it
!= mGroupList
.end(); ++it
) {
583 if ( (*it
)->account() == a
) {
584 (*it
)->setMaxFetch( knGlobals
.settings()->maxToFetch() );
586 emitJob( new ArticleListJob( this, (*it
)->account(), (*it
), true ) );
588 emitJob( new ArticleListJob( this, (*it
)->account(), (*it
) ) );
594 void KNGroupManager::processJob(KNJobData
*j
)
596 if ( j
->type()==KNJobData::JTLoadGroups
|| j
->type()==KNJobData::JTFetchGroups
) {
597 KNGroupListData
*d
=static_cast<KNGroupListData
*>(j
->data());
599 if (!j
->canceled()) {
601 if ( j
->type() == KNJobData::JTFetchGroups
) {
602 // update the descriptions of the subscribed groups
603 for ( KNGroup::List::Iterator it
= mGroupList
.begin(); it
!= mGroupList
.end(); ++it
) {
604 if ( (*it
)->account() == j
->account() ) {
605 Q_FOREACH( const KNGroupInfo
& inf
, *d
->groups
)
606 if ( inf
.name
== (*it
)->groupname() ) {
607 (*it
)->setDescription( inf
.description
);
608 (*it
)->setStatus( inf
.status
);
614 emit(newListReady(d
));
616 KMessageBox::error(knGlobals
.topWidget
, j
->errorString());
617 emit(newListReady(0));
620 emit(newListReady(0));
626 } else { //KNJobData::JTfetchNewHeaders
627 KNGroup
*group
=static_cast<KNGroup
*>(j
->data());
629 if (!j
->canceled()) {
631 if(group
->lastFetchCount()>0) {
632 group
->scoreArticles();
633 group
->processXPostBuffer(true);
634 emit
groupUpdated(group
);
636 knGlobals
.memoryManager()->updateCacheEntry(group
);
640 // stop all other active fetch jobs, this prevents that
641 // we show multiple error dialogs if a server is unavailable
642 knGlobals
.scheduler()->cancelJobs( KNJobData::JTfetchNewHeaders
);
643 ArticleListJob
*lj
= static_cast<ArticleListJob
*>( j
);
644 if ( !lj
->silent() ) {
645 KMessageBox::error(knGlobals
.topWidget
, j
->errorString());
649 if(group
==c_urrentGroup
)
650 a_rticleMgr
->showHdrs(false);
657 // load group list from disk (if this fails: ask user if we should fetch the list)
658 void KNGroupManager::slotLoadGroupList(KNNntpAccount
*a
)
660 KNGroupListData
*d
= new KNGroupListData();
663 if(!QFileInfo(d
->path
+"groups").exists()) {
664 if (KMessageBox::Yes
==KMessageBox::questionYesNo(knGlobals
.topWidget
,i18n("You do not have any groups for this account;\ndo you want to fetch a current list?"), QString(), KGuiItem(i18n("Fetch List")), KGuiItem(i18n("Do Not Fetch")))) {
666 slotFetchGroupList(a
);
669 emit(newListReady(d
));
675 getSubscribed(a
,d
->subscribed
);
676 d
->getDescriptions
= a
->fetchDescriptions();
678 emitJob( new GroupLoadJob( this, a
, d
) );
682 // fetch group list from server
683 void KNGroupManager::slotFetchGroupList(KNNntpAccount
*a
)
685 KNGroupListData
*d
= new KNGroupListData();
687 getSubscribed(a
,d
->subscribed
);
688 d
->getDescriptions
= a
->fetchDescriptions();
689 d
->codecForDescriptions
= KGlobal::charsets()->codecForName( Locale::defaultCharset() );
691 emitJob( new GroupListJob( this, a
, d
) );
695 // check for new groups (created after the given date)
696 void KNGroupManager::slotCheckForNewGroups(KNNntpAccount
*a
, QDate date
)
698 KNGroupListData
*d
= new KNGroupListData();
700 getSubscribed(a
,d
->subscribed
);
701 d
->getDescriptions
= a
->fetchDescriptions();
702 d
->fetchSince
= date
;
703 d
->codecForDescriptions
= KGlobal::charsets()->codecForName( Locale::defaultCharset() );
705 emitJob( new GroupListJob( this, a
, d
, true ) );
709 //--------------------------------
711 #include "kngroupmanager.moc"