diff options
Diffstat (limited to 'knode/kngroupmanager.cpp')
-rw-r--r-- | knode/kngroupmanager.cpp | 704 |
1 files changed, 704 insertions, 0 deletions
diff --git a/knode/kngroupmanager.cpp b/knode/kngroupmanager.cpp new file mode 100644 index 000000000..584896091 --- /dev/null +++ b/knode/kngroupmanager.cpp @@ -0,0 +1,704 @@ +/* + 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; |