You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdepim/knode/kngroupmanager.cpp

705 lines
18 KiB

/*
KNode, the KDE newsreader
Copyright (c) 1999-2005 the KNode authors.
See file AUTHORS for details
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US
*/
#include <stdio.h>
#include <stdlib.h>
#include <qdir.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kiconloader.h>
#include <kdebug.h>
#include <kcharsets.h>
#include "articlewidget.h"
#include "knmainwidget.h"
#include "knarticlemanager.h"
#include "kngroupdialog.h"
#include "knnntpaccount.h"
#include "knprotocolclient.h"
#include "kncleanup.h"
#include "knnetaccess.h"
#include "knglobals.h"
#include "knconfigmanager.h"
#include "resource.h"
#include "utilities.h"
#include "knarticlewindow.h"
#include "knmemorymanager.h"
using namespace KNode;
//=================================================================================
// helper classes for the group selection dialog (getting the server's grouplist,
// getting recently created groups)
KNGroupInfo::KNGroupInfo()
{
}
KNGroupInfo::KNGroupInfo(const QString &n_ame, const QString &d_escription, bool n_ewGroup, bool s_ubscribed, KNGroup::Status s_tatus)
: name(n_ame), description(d_escription), newGroup(n_ewGroup), subscribed(s_ubscribed),
status(s_tatus)
{
}
KNGroupInfo::~KNGroupInfo()
{
}
bool KNGroupInfo::operator== (const KNGroupInfo &gi2)
{
return (name == gi2.name);
}
bool KNGroupInfo::operator< (const KNGroupInfo &gi2)
{
return (name < gi2.name);
}
//===============================================================================
KNGroupListData::KNGroupListData()
: codecForDescriptions(0)
{
groups = new QSortedList<KNGroupInfo>;
groups->setAutoDelete(true);
}
KNGroupListData::~KNGroupListData()
{
delete groups;
}
bool KNGroupListData::readIn(KNProtocolClient *client)
{
KNFile f(path+"groups");
QCString line;
int sepPos1,sepPos2;
QString name,description;
bool sub;
KNGroup::Status status=KNGroup::unknown;
QTime timer;
uint size=f.size()+2;
timer.start();
if (client) client->updatePercentage(0);
if(f.open(IO_ReadOnly)) {
while(!f.atEnd()) {
line = f.readLine();
sepPos1 = line.find(' ');
if (sepPos1==-1) { // no description
name = QString::fromUtf8(line);
description = QString::null;
status = KNGroup::unknown;
} else {
name = QString::fromUtf8(line.left(sepPos1));
sepPos2 = line.find(' ',sepPos1+1);
if (sepPos2==-1) { // no status
description = QString::fromUtf8(line.right(line.length()-sepPos1-1));
status = KNGroup::unknown;
} else {
description = QString::fromUtf8(line.right(line.length()-sepPos2-1));
switch (line[sepPos1+1]) {
case 'u': status = KNGroup::unknown;
break;
case 'n': status = KNGroup::readOnly;
break;
case 'y': status = KNGroup::postingAllowed;
break;
case 'm': status = KNGroup::moderated;
break;
}
}
}
if (subscribed.contains(name)) {
subscribed.remove(name); // group names are unique, we wont find it again anyway...
sub = true;
} else
sub = false;
groups->append(new KNGroupInfo(name,description,false,sub,status));
if (timer.elapsed() > 200) { // don't flicker
timer.restart();
if (client) client->updatePercentage((f.at()*100)/size);
}
}
f.close();
return true;
} else {
kdWarning(5003) << "unable to open " << f.name() << " reason " << f.status() << endl;
return false;
}
}
bool KNGroupListData::writeOut()
{
QFile f(path+"groups");
QCString temp;
if(f.open(IO_WriteOnly)) {
for (KNGroupInfo *i=groups->first(); i; i=groups->next()) {
temp = i->name.utf8();
switch (i->status) {
case KNGroup::unknown: temp += " u ";
break;
case KNGroup::readOnly: temp += " n ";
break;
case KNGroup::postingAllowed: temp += " y ";
break;
case KNGroup::moderated: temp += " m ";
break;
}
temp += i->description.utf8() + "\n";
f.writeBlock(temp.data(),temp.length());
}
f.close();
return true;
} else {
kdWarning(5003) << "unable to open " << f.name() << " reason " << f.status() << endl;
return false;
}
}
// merge in new groups, we want to preserve the "subscribed"-flag
// of the loaded groups and the "new"-flag of the new groups.
void KNGroupListData::merge(QSortedList<KNGroupInfo>* newGroups)
{
bool subscribed;
for (KNGroupInfo *i=newGroups->first(); i; i=newGroups->next()) {
if (groups->find(i)>=0) {
subscribed = groups->current()->subscribed;
groups->remove(); // avoid duplicates
} else
subscribed = false;
groups->append(new KNGroupInfo(i->name,i->description,true,subscribed,i->status));
}
groups->sort();
}
QSortedList<KNGroupInfo>* KNGroupListData::extractList()
{
QSortedList<KNGroupInfo>* temp = groups;
groups = 0;
return temp;
}
//===============================================================================
KNGroupManager::KNGroupManager(QObject * parent, const char * name)
: QObject(parent,name)
{
c_urrentGroup=0;
a_rticleMgr = knGlobals.articleManager();
}
KNGroupManager::~KNGroupManager()
{
for ( QValueList<KNGroup*>::Iterator it = mGroupList.begin(); it != mGroupList.end(); ++it )
delete (*it);
}
void KNGroupManager::syncGroups()
{
for ( QValueList<KNGroup*>::Iterator it = mGroupList.begin(); it != mGroupList.end(); ++it ) {
(*it)->syncDynamicData();
(*it)->saveInfo();
}
}
void KNGroupManager::loadGroups(KNNntpAccount *a)
{
KNGroup *group;
QString dir(a->path());
if (dir.isNull())
return;
QDir d(dir);
QStringList entries(d.entryList("*.grpinfo"));
for(QStringList::Iterator it=entries.begin(); it != entries.end(); ++it) {
group=new KNGroup(a);
if (group->readInfo(dir+(*it))) {
mGroupList.append( group );
emit groupAdded(group);
} else {
delete group;
kdError(5003) << "Unable to load " << (*it) << "!" << endl;
}
}
}
void KNGroupManager::getSubscribed(KNNntpAccount *a, QStringList &l)
{
l.clear();
for ( QValueList<KNGroup*>::Iterator it = mGroupList.begin(); it != mGroupList.end(); ++it )
if ( (*it)->account() == a )
l.append( (*it)->groupname() );
}
QValueList<KNGroup*> KNGroupManager::groupsOfAccount( KNNntpAccount *a )
{
QValueList<KNGroup*> ret;
for ( QValueList<KNGroup*>::Iterator it = mGroupList.begin(); it != mGroupList.end(); ++it )
if ( (*it)->account() == a )
ret.append( (*it) );
return ret;
}
bool KNGroupManager::loadHeaders(KNGroup *g)
{
if (!g)
return false;
if (g->isLoaded())
return true;
// we want to delete old stuff first => reduce vm fragmentation
knGlobals.memoryManager()->prepareLoad(g);
if (g->loadHdrs()) {
knGlobals.memoryManager()->updateCacheEntry( g );
return true;
}
return false;
}
bool KNGroupManager::unloadHeaders(KNGroup *g, bool force)
{
if(!g || g->isLocked())
return false;
if(!g->isLoaded())
return true;
if (!force && (c_urrentGroup == g))
return false;
if (g->unloadHdrs(force))
knGlobals.memoryManager()->removeCacheEntry(g);
else
return false;
return true;
}
KNGroup* KNGroupManager::group(const QString &gName, const KNServerInfo *s)
{
for ( QValueList<KNGroup*>::Iterator it = mGroupList.begin(); it != mGroupList.end(); ++it )
if ( (*it)->account() == s && (*it)->groupname() == gName )
return (*it);
return 0;
}
KNGroup* KNGroupManager::firstGroupOfAccount(const KNServerInfo *s)
{
for ( QValueList<KNGroup*>::Iterator it = mGroupList.begin(); it != mGroupList.end(); ++it )
if ( (*it)->account() == s )
return (*it);
return 0;
}
void KNGroupManager::expireAll(KNCleanUp *cup)
{
for ( QValueList<KNGroup*>::Iterator it = mGroupList.begin(); it != mGroupList.end(); ++it ) {
if( (*it)->isLocked() || (*it)->lockedArticles() > 0 )
continue;
if ( !(*it)->activeCleanupConfig()->expireToday() )
continue;
cup->appendCollection( *(it) );
}
}
void KNGroupManager::expireAll(KNNntpAccount *a)
{
KNCleanUp *cup = new KNCleanUp();
for ( QValueList<KNGroup*>::Iterator it = mGroupList.begin(); it != mGroupList.end(); ++it ) {
if( (*it)->account() != a || (*it)->isLocked() || (*it)->lockedArticles() > 0 )
continue;
KNArticleWindow::closeAllWindowsForCollection( (*it) );
cup->appendCollection( (*it) );
}
cup->start();
for ( QValueList<KNGroup*>::Iterator it = mGroupList.begin(); it != mGroupList.end(); ++it ) {
if( (*it)->account() != a || (*it)->isLocked() || (*it)->lockedArticles() > 0 )
continue;
emit groupUpdated( (*it) );
if ( (*it) == c_urrentGroup ) {
if ( loadHeaders( (*it) ) )
a_rticleMgr->showHdrs();
else
a_rticleMgr->setGroup(0);
}
}
delete cup;
}
void KNGroupManager::showGroupDialog(KNNntpAccount *a, QWidget *parent)
{
KNGroupDialog* gDialog=new KNGroupDialog((parent!=0)? parent:knGlobals.topWidget, a);
connect(gDialog, SIGNAL(loadList(KNNntpAccount*)), this, SLOT(slotLoadGroupList(KNNntpAccount*)));
connect(gDialog, SIGNAL(fetchList(KNNntpAccount*)), this, SLOT(slotFetchGroupList(KNNntpAccount*)));
connect(gDialog, SIGNAL(checkNew(KNNntpAccount*,QDate)), this, SLOT(slotCheckForNewGroups(KNNntpAccount*,QDate)));
connect(this, SIGNAL(newListReady(KNGroupListData*)), gDialog, SLOT(slotReceiveList(KNGroupListData*)));
if(gDialog->exec()) {
KNGroup *g=0;
QStringList lst;
gDialog->toUnsubscribe(&lst);
if (lst.count()>0) {
if (KMessageBox::Yes == KMessageBox::questionYesNoList((parent!=0)? parent:knGlobals.topWidget,i18n("Do you really want to unsubscribe\nfrom these groups?"),
lst, QString::null, i18n("Unsubscribe"), KStdGuiItem::cancel())) {
for ( QStringList::Iterator it = lst.begin(); it != lst.end(); ++it ) {
if((g=group(*it, a)))
unsubscribeGroup(g);
}
}
}
QSortedList<KNGroupInfo> lst2;
gDialog->toSubscribe(&lst2);
for(KNGroupInfo *var=lst2.first(); var; var=lst2.next()) {
subscribeGroup(var, a);
}
}
delete gDialog;
}
void KNGroupManager::subscribeGroup(const KNGroupInfo *gi, KNNntpAccount *a)
{
KNGroup *grp;
grp=new KNGroup(a);
grp->setGroupname(gi->name);
grp->setDescription(gi->description);
grp->setStatus(gi->status);
grp->saveInfo();
mGroupList.append( grp );
emit groupAdded(grp);
}
bool KNGroupManager::unsubscribeGroup(KNGroup *g)
{
KNNntpAccount *acc;
if(!g) g=c_urrentGroup;
if(!g) return false;
if((g->isLocked()) || (g->lockedArticles()>0)) {
KMessageBox::sorry(knGlobals.topWidget, i18n("The group \"%1\" is being updated currently.\nIt is not possible to unsubscribe from it at the moment.").arg(g->groupname()));
return false;
}
KNArticleWindow::closeAllWindowsForCollection(g);
ArticleWidget::collectionRemoved( g );
acc=g->account();
QDir dir(acc->path(),g->groupname()+"*");
if (dir.exists()) {
if (unloadHeaders(g, true)) {
if(c_urrentGroup==g) {
setCurrentGroup(0);
a_rticleMgr->updateStatusString();
}
const QFileInfoList *list = dir.entryInfoList(); // get list of matching files and delete all
if (list) {
QFileInfoListIterator it( *list );
while (it.current()) {
if (it.current()->fileName() == g->groupname()+".dynamic" ||
it.current()->fileName() == g->groupname()+".static" ||
it.current()->fileName() == g->groupname()+".grpinfo")
dir.remove(it.current()->fileName());
++it;
}
}
kdDebug(5003) << "Files deleted!" << endl;
emit groupRemoved(g);
mGroupList.remove( g );
delete g;
return true;
}
}
return false;
}
void KNGroupManager::showGroupProperties(KNGroup *g)
{
if(!g) g=c_urrentGroup;
if(!g) return;
g->showProperties();
}
void KNGroupManager::checkGroupForNewHeaders(KNGroup *g)
{
if(!g) g=c_urrentGroup;
if(!g) return;
if(g->isLocked()) {
kdDebug(5003) << "KNGroupManager::checkGroupForNewHeaders() : group locked - returning" << endl;
return;
}
g->setMaxFetch(knGlobals.configManager()->readNewsGeneral()->maxToFetch());
emitJob( new KNJobData(KNJobData::JTfetchNewHeaders, this, g->account(), g) );
}
void KNGroupManager::expireGroupNow(KNGroup *g)
{
if(!g) return;
if((g->isLocked()) || (g->lockedArticles()>0)) {
KMessageBox::sorry(knGlobals.topWidget,
i18n("This group cannot be expired because it is currently being updated.\n Please try again later."));
return;
}
KNArticleWindow::closeAllWindowsForCollection(g);
KNCleanUp cup;
cup.expireGroup(g, true);
emit groupUpdated(g);
if(g==c_urrentGroup) {
if( loadHeaders(g) )
a_rticleMgr->showHdrs();
else
a_rticleMgr->setGroup(0);
}
}
void KNGroupManager::reorganizeGroup(KNGroup *g)
{
if(!g) g=c_urrentGroup;
if(!g) return;
g->reorganize();
if(g==c_urrentGroup)
a_rticleMgr->showHdrs();
}
void KNGroupManager::setCurrentGroup(KNGroup *g)
{
c_urrentGroup=g;
a_rticleMgr->setGroup(g);
kdDebug(5003) << "KNGroupManager::setCurrentGroup() : group changed" << endl;
if(g) {
if( !loadHeaders(g) ) {
//KMessageBox::error(knGlobals.topWidget, i18n("Cannot load saved headers"));
return;
}
a_rticleMgr->showHdrs();
if(knGlobals.configManager()->readNewsGeneral()->autoCheckGroups())
checkGroupForNewHeaders(g);
}
}
void KNGroupManager::checkAll(KNNntpAccount *a, bool silent)
{
if(!a) return;
for ( QValueList<KNGroup*>::Iterator it = mGroupList.begin(); it != mGroupList.end(); ++it ) {
if ( (*it)->account() == a ) {
(*it)->setMaxFetch( knGlobals.configManager()->readNewsGeneral()->maxToFetch() );
if ( silent )
emitJob( new KNJobData(KNJobData::JTsilentFetchNewHeaders, this, (*it)->account(), (*it) ) );
else
emitJob( new KNJobData(KNJobData::JTfetchNewHeaders, this, (*it)->account(), (*it) ) );
}
}
}
void KNGroupManager::processJob(KNJobData *j)
{
if((j->type()==KNJobData::JTLoadGroups)||(j->type()==KNJobData::JTFetchGroups)||(j->type()==KNJobData::JTCheckNewGroups)) {
KNGroupListData *d=static_cast<KNGroupListData*>(j->data());
if (!j->canceled()) {
if (j->success()) {
if ((j->type()==KNJobData::JTFetchGroups)||(j->type()==KNJobData::JTCheckNewGroups)) {
// update the descriptions of the subscribed groups
for ( QValueList<KNGroup*>::Iterator it = mGroupList.begin(); it != mGroupList.end(); ++it ) {
if ( (*it)->account() == j->account() ) {
for ( KNGroupInfo* inf = d->groups->first(); inf; inf = d->groups->next() )
if ( inf->name == (*it)->groupname() ) {
(*it)->setDescription( inf->description );
(*it)->setStatus( inf->status );
break;
}
}
}
}
emit(newListReady(d));
} else {
KMessageBox::error(knGlobals.topWidget, j->errorString());
emit(newListReady(0));
}
} else
emit(newListReady(0));
delete j;
delete d;
} else { //KNJobData::JTfetchNewHeaders or KNJobData::JTsilentFetchNewHeaders
KNGroup *group=static_cast<KNGroup*>(j->data());
if (!j->canceled()) {
if (j->success()) {
if(group->lastFetchCount()>0) {
group->scoreArticles();
group->processXPostBuffer(true);
emit groupUpdated(group);
group->saveInfo();
knGlobals.memoryManager()->updateCacheEntry(group);
}
} else {
// ok, hack (?):
// stop all other active fetch jobs, this prevents that
// we show multiple error dialogs if a server is unavailable
knGlobals.netAccess()->stopJobsNntp(KNJobData::JTfetchNewHeaders);
knGlobals.netAccess()->stopJobsNntp(KNJobData::JTsilentFetchNewHeaders);
if(!(j->type()==KNJobData::JTsilentFetchNewHeaders)) {
KMessageBox::error(knGlobals.topWidget, j->errorString());
}
}
}
if(group==c_urrentGroup)
a_rticleMgr->showHdrs(false);
delete j;
}
}
// load group list from disk (if this fails: ask user if we should fetch the list)
void KNGroupManager::slotLoadGroupList(KNNntpAccount *a)
{
KNGroupListData *d = new KNGroupListData();
d->path = a->path();
if(!QFileInfo(d->path+"groups").exists()) {
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::null, i18n("Fetch List"), i18n("Do Not Fetch"))) {
delete d;
slotFetchGroupList(a);
return;
} else {
emit(newListReady(d));
delete d;
return;
}
}
getSubscribed(a,d->subscribed);
d->getDescriptions = a->fetchDescriptions();
emitJob( new KNJobData(KNJobData::JTLoadGroups, this, a, d) );
}
// fetch group list from server
void KNGroupManager::slotFetchGroupList(KNNntpAccount *a)
{
KNGroupListData *d = new KNGroupListData();
d->path = a->path();
getSubscribed(a,d->subscribed);
d->getDescriptions = a->fetchDescriptions();
d->codecForDescriptions=KGlobal::charsets()->codecForName(knGlobals.configManager()->postNewsTechnical()->charset());
emitJob( new KNJobData(KNJobData::JTFetchGroups, this, a, d) );
}
// check for new groups (created after the given date)
void KNGroupManager::slotCheckForNewGroups(KNNntpAccount *a, QDate date)
{
KNGroupListData *d = new KNGroupListData();
d->path = a->path();
getSubscribed(a,d->subscribed);
d->getDescriptions = a->fetchDescriptions();
d->fetchSince = date;
d->codecForDescriptions=KGlobal::charsets()->codecForName(knGlobals.configManager()->postNewsTechnical()->charset());
emitJob( new KNJobData(KNJobData::JTCheckNewGroups, this, a, d) );
}
//--------------------------------
#include "kngroupmanager.moc"
// kate: space-indent on; indent-width 2;