/*
grouplist.* - nntp newsgroups list cache code
Copyright (C) 2002 Matthew Mueller <donut AT dakotacom.net>
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.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "grouplist.h"
#include <memory>
#include <errno.h>
#include "nget.h"
#include "status.h"
#include "file.h"
#include "strtoker.h"
//####ugly, these should go in some header but there isn't an obvious choice.
void setfilenamegz(string &file, int gz=-2);
c_file *dofileopen(string file, string mode, int gz=-2);
c_nntp_grouplist_server_info::ptr c_nntp_grouplist::getserverinfo(ulong serverid){
c_nntp_grouplist_server_info::ptr servinfo=server_info[serverid];
if (!servinfo){
servinfo=new c_nntp_grouplist_server_info(serverid);
server_info[serverid]=servinfo;
}
return servinfo;
}
bool c_group_availability::addserverdescription(ulong serverid, const string &desc){
t_server_group_description_map::iterator di=servergroups.find(desc.c_str());
t_server_group_description_map::iterator si;
for (si=servergroups.begin();si!=servergroups.end(); ++si)
if (si->second->serverids.find(serverid)!=si->second->serverids.end())
break;
if (si!=servergroups.end()) {
if (si==di) return false;
si->second->serverids.erase(serverid);
if (si->second->serverids.empty())
servergroups.erase(si);
}
if (di!=servergroups.end()) {
di->second->serverids.insert(serverid);
}else {
c_server_group_description::ptr sgd = new c_server_group_description(desc);
sgd->serverids.insert(serverid);
servergroups.insert(t_server_group_description_map::value_type(sgd->description.c_str(), sgd));
}
return true;
}
void c_nntp_grouplist::addgroup(ulong serverid, string name){
c_group_availability::ptr group;
t_group_availability_map::iterator i=groups.find(name.c_str());
if (i!=groups.end()){
group = i->second;
}else{
group = new c_group_availability(name);
groups.insert(t_group_availability_map::value_type(group->groupname.c_str(), group));
}
if (group->serverids.find(serverid)==group->serverids.end()) {
group->serverids.insert(serverid);
saveit=true;
}
}
void c_nntp_grouplist::addgroupdesc(ulong serverid, string name, string desc){
c_group_availability::ptr group;
t_group_availability_map::iterator i=groups.find(name.c_str());
if (i!=groups.end()){
group = i->second;
}else{
group = new c_group_availability(name);
groups.insert(t_group_availability_map::value_type(group->groupname.c_str(), group));
}
if (group->addserverdescription(serverid, desc))
saveit=true;
}
void c_nntp_grouplist::flushserver(ulong serverid) {
ulong countg=0, countgs=0, countd=0, countds=0;
int servinfoerased=server_info.erase(serverid);
t_group_availability_map::iterator gi, gitmp;
c_group_availability::ptr group;
if (quiet<2) {printf("Flushing grouplist for server %lu:", serverid);fflush(stdout);}
for (gi = groups.begin(); gi != groups.end();) {
gitmp = gi;
++gi;
group = gitmp->second;
for (t_server_group_description_map::iterator gdi=group->servergroups.begin(); gdi!=group->servergroups.end();) {
t_server_group_description_map::iterator gditmp=gdi;
++gdi;
c_server_group_description::ptr gd = gditmp->second;
countds += gd->serverids.erase(serverid);
if (gd->serverids.empty()) {
group->servergroups.erase(gditmp);
++countd;
}
}
countgs += group->serverids.erase(serverid);
if (group->serverids.empty() && group->servergroups.empty()) {
groups.erase(gitmp);
++countg;
}
}
if (quiet<2){printf(" %lu groups (%lu dead), %lu descriptions (%lu dead)\n",countgs,countg,countds,countd);}
if (servinfoerased || countgs || countds) saveit=1;
}
static void nntp_grouplist_printinfo(const t_grouplist_getinfo_list &getinfos, const c_group_availability::ptr &g) {
t_grouplist_getinfo_list::const_iterator gii, giibegin=getinfos.begin(), giiend=getinfos.end();
c_grouplist_getinfo::ptr info;
for (gii=giibegin; gii!=giiend; ++gii) {
info = *gii;
if ((*info->pred)(g.gimmethepointer())) {
for (set<ulong>::const_iterator sii=g->serverids.begin(); sii!=g->serverids.end(); ++sii) {
for (t_server_list_range servers = nconfig.getservers(*sii); servers.first!=servers.second; ++servers.first) {
printf("%s",servers.first->second->shortname.c_str());
}
}
printf("\t%s",g->groupname.c_str());
for (t_server_group_description_map::const_iterator sgdi=g->servergroups.begin(); sgdi!=g->servergroups.end(); ++sgdi) {
printf("\t%s [",sgdi->first);
for (set<ulong>::const_iterator sii=sgdi->second->serverids.begin(); sii!=sgdi->second->serverids.end(); ++sii) {
for (t_server_list_range servers = nconfig.getservers(*sii); servers.first!=servers.second; ++servers.first) {
printf("%s",servers.first->second->shortname.c_str());
}
}
printf("]");
}
printf("\n");
}
}
}
void c_nntp_grouplist::printinfos(const t_grouplist_getinfo_list &getinfos) const {
t_group_availability_map::const_iterator gi;
for (gi = groups.begin(); gi != groups.end(); ++gi)
nntp_grouplist_printinfo(getinfos, gi->second);
}
class c_nntp_grouplist_reader {
protected:
c_file *f;
public:
ulong curline, num, nums, numd, numsd, countdeads, countdeadg;
c_nntp_grouplist_reader(c_file *cf, t_nntp_grouplist_server_info_map &server_infoi);
c_group_availability::ptr read_group(void);
void check_counts(void);
};
c_nntp_grouplist_reader::c_nntp_grouplist_reader(c_file *cf, t_nntp_grouplist_server_info_map &server_info):f(cf) {
curline=0; num=0; nums=0; numd=0; numsd=0; countdeads=0; countdeadg=0;
char *t[2];
int i;
if (f->bgets()<=0)
throw CacheEx(Ex_INIT, "unexpected EOF on grouplist file line %lu",curline);
curline++;
if (strcmp(f->rbufp(),GLCACHE_VERSION)!=0){
if (strncmp(f->rbufp(),"NGETGL",6)==0)
throw CacheEx(Ex_INIT,"grouplist is from a different version of nget");
else
throw CacheEx(Ex_INIT,"does not seem to be an nget grouplist file");
}
while (1){
if (f->beof())
throw CacheEx(Ex_INIT, "unexpected EOF on grouplist file line %lu",curline);
curline++;
if (f->bpeek()=='.') {
if (f->bgetsp()[1]!=0) {
printf("grouplist: stuff after . line %lu mode SERVERINFO\n",curline);
set_cache_warn_status();
}
//mode=FILE_MODE;//start new file mode
return;
}
i = f->btoks('\t',t,2);
if (i==2) {
ulong serverid=atoul(t[0]);
if (nconfig.hasserver(serverid)) {
server_info.insert(t_nntp_grouplist_server_info_map::value_type(serverid, new c_nntp_grouplist_server_info(serverid, t[1])));
}else{
printf("warning: serverid %lu not found in server list\n",serverid);
set_cache_warn_status();
}
}else {
printf("grouplist: invalid line %lu (%i toks)\n",curline,i);
set_cache_warn_status();
}
}
}
c_group_availability::ptr c_nntp_grouplist_reader::read_group(void) {
char *t[2];
int i;
char *buf;
c_group_availability::ptr gd;
while (!f->beof()){
curline++;
i = f->btoks('\t',t,2);
if (i==2){
char * cp;
buf = t[0];
gd = new c_group_availability(t[1]);
while ((cp = goodstrtok(&buf, ',')) && *cp) {
ulong serverid=atoul(cp);
if (nconfig.hasserver(serverid)) {
gd->serverids.insert(serverid);
nums++;
}else{
countdeads++;
}
}
num++;
while (!f->beof()){
curline++;
if (f->bpeek()=='.') {
if (f->bgetsp()[1]!=0) {
printf("grouplist: stuff after . line %lu",curline);
set_cache_warn_status();
}
break;
}
i = f->btoks('\t',t,2, false);
if (i==2){
char * cp;
buf = t[0];
while ((cp = goodstrtok(&buf, ','))) {
ulong serverid=atoul(cp);
if (nconfig.hasserver(serverid)) {
gd->addserverdescription(serverid, t[1]);
numsd++;
}else{
countdeads++;
}
}
}else {
printf("grouplist: invalid line %lu (%i toks)\n",curline,i);
set_cache_warn_status();
}
}
if (!gd->servergroups.empty())
numd+=gd->servergroups.size();
if (!gd->servergroups.empty() || !gd->serverids.empty()){
return gd;
} else
++countdeadg;
}else {
printf("grouplist: invalid line %lu (%i toks)\n",curline,i);
set_cache_warn_status();
}
}
if (gd)
throw CacheEx(Ex_INIT, "unexpected EOF on grouplist file line %lu",curline);
return NULL;
}
void c_nntp_grouplist_reader::check_counts(void) {
if (countdeads||countdeadg){
printf("c_nntp_grouplist: read (and ignored) %lu bad serverids (%lu dead groups)\n",countdeads,countdeadg);
set_cache_warn_status();
}
}
c_nntp_grouplist::c_nntp_grouplist(void){
}
c_nntp_grouplist::c_nntp_grouplist(string path){
c_file *f=NULL;
saveit=0;
//file=nid;
filename=path;
setfilenamegz(filename);
try {
f=dofileopen(filename.c_str(),"rb");
}catch(FileNOENTEx &e){
return;
}
auto_ptr<c_file> fcloser(f);
try{
c_nntp_grouplist_reader reader(f, server_info);
c_group_availability::ptr ng;
while ((ng=reader.read_group()))
groups.insert(t_group_availability_map::value_type(ng->groupname.c_str(), ng));
PDEBUG(DEBUG_MIN,"read %lu groups, %lu descriptions (%lu sgs, %lu sgds)",(ulong)groups.size(),reader.numd,reader.nums,reader.numsd);
reader.check_counts();
} catch (CacheEx &e) {
set_cache_warn_status();
printf("%s: %s\n", filename.c_str(), e.getExStr());
}
f->close();
}
void c_nntp_grouplist::save(void){
if (!saveit)
return;
if (filename.empty())
return;
int nums=0, numsd=0, num=0, numd=0;
string tmpfn=filename+".tmp";
c_file *f=f=dofileopen(tmpfn,"wb");
try {
auto_ptr<c_file> fcloser(f);
if (debug){printf("saving grouplist: %lu groups..",(ulong)groups.size());fflush(stdout);}
t_group_availability_map::iterator gdi;
t_server_group_description_map::iterator sgdi;
c_group_availability::ptr gd;
f->putf(GLCACHE_VERSION"\n");
for (t_nntp_grouplist_server_info_map::iterator glsi=server_info.begin(); glsi!=server_info.end(); ++glsi) {
f->putf("%lu\t%s\n", glsi->first, glsi->second->date.c_str());
}
f->putf(".\n");
for (gdi=groups.begin(); gdi!=groups.end(); ++gdi){
gd=gdi->second;
char *sep="";
for (set<ulong>::iterator sii=gd->serverids.begin(); sii!=gd->serverids.end(); ++sii) {
f->putf("%s%lu", sep, *sii);
sep=",";
nums++;
}
f->putf("\t%s\n",gd->groupname.c_str());
for (sgdi=gd->servergroups.begin(); sgdi!=gd->servergroups.end(); ++sgdi) {
c_server_group_description::ptr sgd = sgdi->second;
char *sep="";
for (set<ulong>::iterator sii=sgd->serverids.begin(); sii!=sgd->serverids.end(); ++sii) {
f->putf("%s%lu", sep, *sii);
sep=",";
numsd++;
}
f->putf("\t%s\n",sgd->description.c_str());
numd++;
}
f->putf(".\n");
num++;
}
if (debug) printf(" (%i g, %i sg, %i gd, %i sgd) done.\n",num,nums,numd,numsd);
f->close();
}catch(FileEx &e){
if (unlink(tmpfn.c_str()))
perror("unlink:");
throw;
}
xxrename(tmpfn.c_str(), filename.c_str());
}
c_nntp_grouplist::~c_nntp_grouplist(){
try {
save();
} catch (FileEx &e) {
printCaughtEx(e);
fatal_exit();
}
}
void nntp_grouplist_printinfos(const t_grouplist_getinfo_list &getinfos) {
c_file *f=NULL;
string filename=ngcachehome+"newsgroups";
setfilenamegz(filename);
try {
f=dofileopen(filename.c_str(),"rb");
}catch(FileNOENTEx &e){
return;
}
auto_ptr<c_file> fcloser(f);
try{
t_nntp_grouplist_server_info_map server_info;
c_nntp_grouplist_reader reader(f, server_info);
c_group_availability::ptr ng;
ulong numgroups=0;
while ((ng=reader.read_group())) {
numgroups++;
nntp_grouplist_printinfo(getinfos, ng);
}
PDEBUG(DEBUG_MIN,"scanned %lu groups, %lu descriptions (%lu sgs, %lu sgds)",numgroups,reader.numd,reader.nums,reader.numsd);
reader.check_counts();
} catch (CacheEx &e) {
set_cache_warn_status();
printf("%s: %s\n", filename.c_str(), e.getExStr());
}
f->close();
}
syntax highlighted by Code2HTML, v. 0.9.1