Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | File Members | Related Pages

RServer Class Reference

Provides an interface to access news on a remote site. More...

#include <NServer.h>

Inheritance diagram for RServer:

NServer CServer List of all members.

Public Member Functions

 RServer (MPList *serverlist)
virtual ~RServer ()
void setserverlist (MPList *serverlist)
MPListgetserverlist (void)
virtual ActiveDBactive ()
virtual GroupInfogroupinfo (const char *name)
virtual Newsgroupgetgroup (const char *name)
virtual int post (Article *article)
virtual void listgroup (const char *gname, char *lstgrp, unsigned int f, unsigned int l)
virtual void overviewdb (Newsgroup *ng, unsigned int fst, unsigned int lst)
virtual void article (const char *gname, unsigned int nb, Article *artr)
virtual void article (const char *id, Article *art)

Protected Member Functions

 RServer ()
void init (MPList *serverlist)
void connect ()
void disconnect ()
 Disconnect from the current news server.

std::string issue (const char *command, const char *expresp=NULL)
void setserver (MPListEntry *server)
int is_connected ()
void selectgroup (const char *name, int force=0)

Protected Attributes

MPList_ServerList
MPListEntry_CurrentServer
GroupInfo _CurrentGroup
sstream_pServerStream

Private Member Functions

void post (MPListEntry *srvr, Article *article)

Detailed Description

Provides an interface to access news on a remote site.

Author:
Thomas Gschwind

Definition at line 205 of file NServer.h.


Constructor & Destructor Documentation

RServer::RServer  )  [inline, protected]
 

Definition at line 243 of file NServer.h.

References _CurrentServer, _pServerStream, and _ServerList.

00243                   :NServer() {
00244                 _ServerList = NULL;
00245                 _CurrentServer = NULL;
00246                 _pServerStream = NULL;
00247         } void init(MPList * serverlist);

RServer::RServer MPList serverlist  ) 
 

Initialize the new RServer class. Sets up the server list and the default number of retries.

Parameters:
serverlist list of news server and their newsgroups

Definition at line 197 of file NServer.cc.

References init(), Logger::p(), slog, and VERB.

00198 {
00199         VERB(slog.p(Logger::Debug) << "RServer::RServer/1\n");
00200         init(serverlist);
00201 }

RServer::~RServer  )  [virtual]
 

Definition at line 203 of file NServer.cc.

References Logger::p(), setserverlist(), slog, and VERB.

00204 {
00205         VERB(slog.p(Logger::Debug) << "RServer::~RServer()\n");
00206         setserverlist(NULL);
00207 }


Member Function Documentation

ActiveDB * RServer::active  )  [virtual]
 

Retrieves the active database from the news servers and returns a pointer to it. If an error occurs while retrieving the database from a server, the active database will not be retrieved from this server.

Note:
The offline stuff does not belong to the RServer. Thus it should be moved into the CServer. Setting up all of this filter expressions can be done at the server-list's creation time. However, at the moment this is neglectable. The active database is retrieved rather seldom and takes a long time nevertheless.
Returns:
Pointer to the active database.

Implements NServer.

Reimplemented in CServer.

Definition at line 575 of file NServer.cc.

References _CurrentServer, _pServerStream, _ServerList, ASSERT, connect(), Logger::Error, ERROR_LOCATION, issue(), MAXGROUPNAMELEN, MPListEntry::nntpflags, Logger::p(), Logger::print(), ActiveDB::read(), MPListEntry::read, setserver(), slog, and VERB.

Referenced by CServer::active().

00576 {
00577         VERB(slog.p(Logger::Debug) << "RServer::active()\n");
00578         unsigned int i, flags;
00579         char cgroup[MAXGROUPNAMELEN + 1], *cgp, buf[1024];
00580         const char *sp;
00581         char c;
00582 
00583         ASSERT(if (!_ServerList) {
00584                throw
00585                AssertionError
00586                ("RServer::active: _ServerList is a null-pointer",
00587                 ERROR_LOCATION);}
00588         );
00589 
00590         for (i = 0; i < _ServerList->entries.size(); i++) {
00591                 if (!_ServerList->entries[i].hostname[0])
00592                         continue;
00593                 // Connect to ith news server
00594                 try {
00595                         setserver(&(_ServerList->entries[i]));
00596                         if (_CurrentServer->
00597                             flags & (MPListEntry::F_OFFLINE | MPListEntry::
00598                                      F_SEMIOFFLINE))
00599                                 continue;
00600                         flags =
00601                             ((_CurrentServer->
00602                               nntpflags & MPListEntry::F_POST)
00603                              || !(_CurrentServer->
00604                                   flags & MPListEntry::
00605                                   F_SETPOSTFLAG)) ? 0 : ActiveDB::
00606                             F_STORE_READONLY;
00607 
00608                         connect();
00609                         if (_CurrentServer->
00610                             nntpflags & MPListEntry::
00611                             F_LIST_ACTIVE_WILDMAT) {
00612                                 try {
00613                                         sp = _CurrentServer->read;
00614                                         do {
00615                                                 // Extract a newsgroup-expression
00616                                                 cgp = cgroup;
00617                                                 while ((c = *sp++) != ','
00618                                                        && c)
00619                                                         *cgp++ = c;
00620                                                 *cgp = '\0';
00621                                                 sprintf(buf,
00622                                                         "list active %s\r\n",
00623                                                         cgroup);
00624                                                 issue(buf, "215");
00625                                                 // read active database and filter unwanted groups
00626                                                 _ActiveDB->
00627                                                     read(*_pServerStream,
00628                                                          _ServerList->
00629                                                          makeFilter(i,
00630                                                                     cgroup),
00631                                                          flags);
00632                                         } while (c);
00633                                 }
00634                                 catch(ResponseError & re) {
00635                                         slog.p(Logger::Notice)
00636                                             <<
00637                                             "list active [wildmat] failed for "
00638                                             << _ServerList->entries[i].
00639                                             hostname << ":" <<
00640                                             _ServerList->entries[i].
00641                                             servicename << "\n";
00642                                         _CurrentServer->nntpflags &=
00643                                             ~MPListEntry::
00644                                             F_LIST_ACTIVE_WILDMAT;
00645                                 }
00646                         }
00647                         if (!
00648                             (_CurrentServer->
00649                              nntpflags & MPListEntry::
00650                              F_LIST_ACTIVE_WILDMAT)) {
00651                                 issue("list active\r\n", "215");
00652                                 _ActiveDB->read(*_pServerStream,
00653                                                 _ServerList->makeFilter(i,
00654                                                                         "*"),
00655                                                 flags);
00656                         }
00657                 }
00658                 catch(ResponseError & re) {
00659                         slog.p(Logger::Error)
00660                             << "retrieval of activedb failed for "
00661                             << _ServerList->entries[i].hostname << ":"
00662                             << _ServerList->entries[i].servicename << "\n";
00663                 }
00664                 catch(SystemError & se) {
00665                         slog.p(Logger::Warning)
00666                             << "connection to "
00667                             << _ServerList->entries[i].hostname << ":"
00668                             << _ServerList->entries[i].
00669                             servicename << " failed\n";
00670                 }
00671                 catch(Error & e) {
00672                         slog.p(Logger::Alert)
00673                             <<
00674                             "UNEXPECTED EXCEPTION WHILE RETRIEVING ACTIVEDB FROM "
00675                             << _ServerList->entries[i].
00676                             hostname << ":" << _ServerList->entries[i].
00677                             servicename <<
00678                             "\nPLEASE REPORT TO h.straub@aon.at\n";
00679                         e.print();
00680                 }
00681         }
00682         return _ActiveDB;
00683 }

void RServer::article const char *  id,
Article art
[virtual]
 

Return a given article of a given newsgroup

Note:
Article nbr:
  • 220 success
  • 412 no group sel
  • 420 no art sel
  • 423 art nbr not found in grp
  • 430 no such art found
Returns:
Pointer to an article, this has to be freed by the user.
Exceptions:
SystemError If the connection to the news server fails.
NoNewsServerError If no news server is configured for this group.
NoSuchGroupError If the group does not exist.
NoSuchArticleError If the requested article does not exist.
ResponseError If the news server replies an invalid response code.
Bug:
Description and Code (Parameters) not synchronized.

Todo:
Synchronize Description and Code (Parameters).

Reimplemented in CServer.

Definition at line 834 of file NServer.cc.

References _CurrentServer, _pServerStream, _ServerList, connect(), MPList::entries, ERROR_LOCATION, issue(), Logger::p(), Article::read(), Article::setnbr(), setserver(), slog, and VERB.

00835 {
00836         VERB(slog.p(Logger::Debug) << "RServer::article(" << id << ")\n");
00837         char buf[1024];
00838         const char *p;
00839         string resp;
00840 
00841         sprintf(buf, "article %s\r\n", id);
00842         if (_CurrentServer) {
00843                 resp = issue(buf, NULL);
00844                 p = resp.c_str();
00845                 if (strncmp(p, "220", 3) == 0) {
00846                         art->read(*_pServerStream);
00847                         art->setnbr(-1);
00848                         return;
00849                 }
00850                 // 412 cannot happen since we specified the article id
00851                 // 420 -"- since we specified the article id
00852                 // 423 -"- since we specified the article id, not the art nbr
00853                 if (strncmp(p, "430", 3) != 0) {
00854                         slog.p(Logger::Notice)
00855                             <<
00856                             "illegal response code to <artcile <id>> request\n"
00857                             << p;
00858                 }
00859         }
00860         // check whether the article can be found at one of the other
00861         // news servers
00862         MPListEntry *cs = _CurrentServer;
00863 
00864         for (unsigned int i = 0; i < _ServerList->entries.size(); i++) {
00865                 if (cs == &(_ServerList->entries[i]) ||
00866                     !_ServerList->entries[i].hostname[0])
00867                         continue;
00868                 setserver(&(_ServerList->entries[i]));
00869                 connect();
00870                 resp = issue(buf, NULL);
00871                 p = resp.c_str();
00872                 if (strncmp(p, "220", 3) == 0) {
00873                         art->read(*_pServerStream);
00874                         art->setnbr(-1);
00875                         return;
00876                 }
00877                 if (strncmp(p, "430", 3) != 0) {
00878                         slog.p(Logger::Notice)
00879                             <<
00880                             "illegal response code to <artcile <id>> request\n"
00881                             << p;
00882                 }
00883         }
00884         throw NoSuchArticleError(resp, ERROR_LOCATION);
00885 }

void RServer::article const char *  gname,
unsigned int  nb,
Article artr
[virtual]
 

Return a given article of a given newsgroup

Note:
Article nbr:
  • 220 success
  • 412 no group sel
  • 420 no art sel
  • 423 art nbr not found in grp
  • 430 no such art found
Parameters:
gname Name of the newsgroup.
nb Number of the article.
Returns:
Pointer to an article, this has to be freed by the user.
Exceptions:
SystemError If the connection to the news server fails.
NoNewsServerError If no news server is configured for this group.
NoSuchGroupError If the group does not exist.
NoSuchArticleError If the requested article does not exist.
ResponseError If the news server replies an invalid response code.
Bug:
Description and Code (Parameters) not synchronized.

Todo:
Synchronize Description and Code (Parameters).

Reimplemented in CServer.

Definition at line 809 of file NServer.cc.

References _pServerStream, ERROR_LOCATION, issue(), Logger::p(), Article::read(), selectgroup(), Article::setnbr(), slog, and VERB.

Referenced by CServer::article(), RNewsgroup::getarticle(), RNewsgroup::printarticle(), and CNewsgroup::retrievearticle().

00810 {
00811         VERB(slog.p(Logger::Debug) << "RServer::article(" << nbr << ")\n");
00812         char buf[513];
00813         const char *p;
00814         string resp;
00815 
00816         selectgroup(gname);
00817 
00818         sprintf(buf, "article %u\r\n", nbr);
00819         resp = issue(buf, NULL);
00820         p = resp.c_str();
00821         if (strncmp(p, "220", 3) != 0) {
00822                 // 412 cannot happen since we have already selected the newsgroup
00823                 // 420 -"- since we specified the article nbr
00824                 // 430 -"- since we specified the article nbr, not the art-id
00825                 if (strncmp(p, "423", 3) == 0)
00826                         throw NoSuchArticleError(resp, ERROR_LOCATION);
00827                 string c(buf), e("220");
00828                 throw ResponseError(c, e, resp);
00829         }
00830         art->read(*_pServerStream);
00831         art->setnbr(nbr);
00832 }

void RServer::connect  )  [protected]
 

Connect to the news server. Stores whether posting is allowed in _CurrentServer->flags.

Exceptions:
SystemError ... Thrown, if a system error (eg. cannot connect) occurs after _Retries successive connection-failures.
ResponseError ... Thrown, if the news server returns a response code different from the allowed connection setup return codes.
  • 200 ... reading and posting allowed
  • 201 ... reading allowed
NoNewsServerError If no news server is configured for this group (in theory, this should be impossible)
NoSuchGroupError If the group does not exist
Todo:
RECONNECT/RETRY (see source code)

Definition at line 233 of file NServer.cc.

References _CurrentGroup, _CurrentServer, _pServerStream, MPListEntry::bindFrom, sstream::connectTo(), ERROR_LOCATION, MPListEntry::hostname, is_connected(), issue(), GroupInfo::name(), nlreadline(), MPListEntry::nntpflags, Logger::p(), MPListEntry::passwd, OverviewFmt::readxoin(), MPListEntry::retries, RSERVER_CONNECT_CHECK_CONNECTION, selectgroup(), MPListEntry::servicename, slog, MPListEntry::user, and VERB.

Referenced by active(), CServer::article(), article(), issue(), and post().

00234 {
00235         VERB(slog.p(Logger::Debug) << "RServer::connect()\n");
00236         if (is_connected())
00237                 return;
00238 
00239         string grt, resp;
00240         char buf[1024];
00241         int i = _CurrentServer->retries;
00242         _pServerStream = new sstream;
00243 
00244         for (;;) {
00245                 // Open connection
00246                 if (strcmp(_CurrentServer->bindFrom, "") == 0) {
00247                         slog.
00248                             p(Logger::
00249                               Debug) << "RServer::connect: Connecting to "
00250                             << _CurrentServer->
00251                             hostname <<
00252                             " from DEFAULT interface to servicename " <<
00253                             _CurrentServer->servicename << "\n";
00254                         _pServerStream->connectTo(_CurrentServer->hostname,
00255                                                   _CurrentServer->
00256                                                   servicename);
00257                 } else {
00258                         slog.
00259                             p(Logger::
00260                               Debug) << "RServer::connect: Connecting to "
00261                             << _CurrentServer->
00262                             hostname << " from interface " <<
00263                             _CurrentServer->
00264                             bindFrom << " to servicename " <<
00265                             _CurrentServer->servicename << "\n";
00266                         _pServerStream->connectTo(_CurrentServer->hostname,
00267                                                   _CurrentServer->
00268                                                   servicename,
00269                                                   _CurrentServer->
00270                                                   bindFrom);
00271                 }
00272                 if (!_pServerStream->good()) {
00273                         delete _pServerStream;
00274                         _pServerStream = NULL;
00275                         throw
00276                             SystemError
00277                             ("RServer::connect: cannot connect to news server",
00278                              errno, ERROR_LOCATION);
00279                 }
00280                 _pServerStream->unsetf(ios::skipws);
00281 
00282                 //FIXME! repl nlreadline by nntpreadline that transparently
00283                 //FIXME! provides auth? use a authenticator callback class, that 
00284                 //FIXME! supplies all the negotiation between client/server. Will
00285                 //FIXME! get a server and a client handle as well as server and 
00286                 //FIXME! groupname to send correct passwd to correct server/group
00287                 //FIXME! RECONNECT/RETRY? HOW IMPLEMENT? What happens if module 
00288                 //FIXME! cannot handle this?!?
00289                 nlreadline(*_pServerStream, grt, 0);
00290                 slog.p(Logger::Info) << grt << "\n";
00291 
00292                 RSERVER_CONNECT_CHECK_CONNECTION
00293                     ("Connection to %s:%s failed");
00294                 if (grt[0] != '2' || grt[1] != '0'
00295                     || (grt[2] != '0' && grt[2] != '1')) {
00296                         string c("_connect_"), e("20[01]");
00297                         delete _pServerStream;
00298                         _pServerStream = NULL;
00299                         throw ResponseError(c, e, grt);
00300                 }
00301 
00302                 if (_CurrentServer->user[0]) {
00303                         sprintf(buf, "authinfo user %s\r\n",
00304                                 _CurrentServer->user);
00305                         resp = issue(buf, NULL);
00306                         if (resp[0] == '3') {
00307                                 sprintf(buf, "authinfo pass %s\r\n",
00308                                         _CurrentServer->passwd);
00309                                 resp = issue(buf, NULL);
00310                         }
00311                         if (resp[0] != '2') {
00312                                 string c("_authenticate_"), e("[23]..");
00313                                 delete _pServerStream;
00314                                 _pServerStream = NULL;
00315                                 throw ResponseError(c, e, resp);
00316                         }
00317                 }
00318 
00319                 if (_CurrentServer->nntpflags & MPListEntry::F_MODE_READER) {
00320                         // mode reader
00321                         resp = issue("mode reader\r\n", NULL);
00322                         RSERVER_CONNECT_CHECK_CONNECTION
00323                             ("Cannot read data from %s:%s");
00324                         if (resp[0] != '2' || resp[1] != '0'
00325                             || (resp[2] != '0' && resp[2] != '1')) {
00326                                 slog.
00327                                     p(Logger::
00328                                       Warning) <<
00329                                     "mode reader failed, ignored\n";
00330                                 _CurrentServer->nntpflags &=
00331                                     ~MPListEntry::F_MODE_READER;
00332                         }
00333                         grt = resp;
00334                 }
00335                 if (grt[2] == '1') {
00336                         _CurrentServer->nntpflags &= ~MPListEntry::F_POST;
00337                 }
00338 
00339                 if (_CurrentServer->
00340                     nntpflags & MPListEntry::F_LIST_OVERVIEW_FMT) {
00341                         resp = issue("list overview.fmt\r\n", "215");
00342                         _OverviewFormat->readxoin(*_pServerStream);
00343                         if (resp[0] != '2' || resp[1] != '1'
00344                             || resp[2] != '5') {
00345                                 slog.
00346                                     p(Logger::
00347                                       Warning) <<
00348                                     "list overview.fmt failed, ignored\n";
00349                                 _CurrentServer->nntpflags &=
00350                                     ~MPListEntry::F_LIST_OVERVIEW_FMT;
00351                                 goto rserver__connect_no_list_overview_fmt;
00352                         }
00353                 } else {
00354                       rserver__connect_no_list_overview_fmt:
00355 #ifdef HAVE_SSTREAM
00356                         istringstream standardOverviewFmt
00357 #else
00358                         istrstream standardOverviewFmt
00359 #endif
00360                             ("Subject:\nFrom:\nDate:\nMessage-ID:\nReferences:\nBytes:\nLines:\n");
00361                         _OverviewFormat->readxoin(standardOverviewFmt);
00362                 }
00363 
00364                 if (_CurrentGroup.name()[0] != '\0') {
00365                         selectgroup(_CurrentGroup.name(), 1);
00366                 }
00367                 return;
00368         }
00369 }

void RServer::disconnect  )  [protected]
 

Disconnect from the current news server.

Definition at line 373 of file NServer.cc.

References _pServerStream, Logger::p(), slog, and VERB.

Referenced by issue(), post(), setserver(), and setserverlist().

00374 {
00375         VERB(slog.p(Logger::Debug) << "RServer::disconnect()\n");
00376         if (_pServerStream != NULL) {
00377                 *_pServerStream << "quit\r\n";
00378                 // Who is interested in this return code? 
00379                 delete _pServerStream;
00380                 _pServerStream = NULL;
00381         }
00382 }

Newsgroup * RServer::getgroup const char *  name  )  [virtual]
 

Return the newsgroup with name >name<. If the newsgroup does not exist, NULL will be returned.

Parameters:
name Name of the newsgroup.
Returns:
Pointer to a Newsgroup structure. Free it by calling the freegroup method. The newsgroup must destructed by the same Server-instance it was constructed from.
Exceptions:
NoNewsServerError If no news server is configured for this group.
NoSuchGroupError If the group does not exist.
ResponseError If the news server replies an invalid response code.
SystemError ... if the connection to the news server fails.

Implements NServer.

Reimplemented in CServer.

Definition at line 707 of file NServer.cc.

References _CurrentGroup, GroupInfo::first(), GroupInfo::last(), selectgroup(), RNewsgroup::setsize(), slog, and VERB.

00708 {
00709         VERB(slog.
00710              p(Logger::Debug) << "RServer::getgroup(" << name << ")\n");
00711 
00712         selectgroup(name);
00713         RNewsgroup *grp = new RNewsgroup(this, _OverviewFormat, name);
00714         grp->setsize(_CurrentGroup.first(), _CurrentGroup.last());
00715         return grp;
00716 }

MPList* RServer::getserverlist void   )  [inline]
 

Return the currently installed list of news servers.

Returns:
The server-list.

Definition at line 336 of file NServer.h.

References _ServerList.

00336                                     {
00337                 return _ServerList;
00338         }

GroupInfo * RServer::groupinfo const char *  name  )  [virtual]
 

Returns the GroupInfo object of a given newsgroup.

Parameters:
name The name of newsgroup.
Returns:
Returns informations of the newsgroup in a statically allocated GroupInfo structure.
Exceptions:
SystemError If the connection to the news server fails
NoSuchGroupError If the group does not exist, or no news server has been configured for this group.
ResponseError If the news server replies an invalid response code.

Implements NServer.

Reimplemented in CServer.

Definition at line 685 of file NServer.cc.

References _CurrentGroup, ASSERT, ERROR_LOCATION, ActiveDB::get(), selectgroup(), slog, and VERB.

Referenced by CNewsgroup::sUpdateGroupInfo().

00686 {
00687         VERB(slog.
00688              p(Logger::Debug) << "RServer::groupinfo(" << name << ")\n");
00689         static GroupInfo agroup;
00690 
00691         ASSERT(if (strlen(name) > 510) {
00692                throw
00693                AssertionError
00694                ("RServer::groupinfo: name of newsgroup too long\n",
00695                 ERROR_LOCATION);}
00696         );
00697 
00698         // If the newsgroup cannot be found within the active database
00699         // reload the database from the news server and try again.
00700         if (_ActiveDB->get(name, &agroup) < 0) {
00701                 selectgroup(name, 1);
00702                 agroup = _CurrentGroup;
00703         }
00704         return &agroup;
00705 }

void RServer::init MPList serverlist  )  [protected]
 

Definition at line 209 of file NServer.cc.

References _CurrentServer, _ServerList, Logger::p(), slog, and VERB.

Referenced by CServer::CServer(), and RServer().

00210 {
00211         VERB(slog.p(Logger::Debug) << "RServer::init/1\n");
00212 
00213         _ServerList = serverlist;
00214         _CurrentServer = NULL;
00215 
00216         if (!_OverviewFormat)
00217                 _OverviewFormat = new OverviewFmt();
00218         if (!_ActiveDB)
00219                 _ActiveDB = NULL;
00220 }

int RServer::is_connected  )  [inline, protected]
 

Definition at line 296 of file NServer.h.

References _pServerStream.

Referenced by connect(), and issue().

00296                            {
00297                 return (_pServerStream != NULL) ? 1 : 0;
00298         }

string RServer::issue const char *  command,
const char *  expresp = NULL
[protected]
 

Send a command to the remote news server and return its response.

Parameters:
command Command that should be sent to the news server
expresp ... Expected response code. If omitted, any response code will do. Should be checked by the caller in this case.
Returns:
static string holding the news server's response.
Exceptions:
SystemError If the connection fails
ResponseError If the server returns an illegal response code
NoNewsServerError ... if no news server is configured for the current group.
NoSuchGroupError If the group does not exist.

Definition at line 384 of file NServer.cc.

References _CurrentServer, _pServerStream, connect(), disconnect(), ERROR_LOCATION, is_connected(), nlreadline(), Logger::p(), MPListEntry::retries, and slog.

Referenced by active(), CServer::article(), article(), connect(), listgroup(), overviewdb(), post(), and selectgroup().

00385 {
00386         string req, resp;
00387         int rs;
00388         int i = _CurrentServer->retries + 1;
00389 
00390         req = "issue ";
00391         req.append(command, strlen(command) - 2);
00392         req.append("\n");
00393         slog.p(Logger::Info) << "RServer::issue: " << req.c_str();
00394         if (!is_connected())
00395                 connect();
00396         // Send command
00397         for (;;) {
00398                 try {
00399                         *_pServerStream << command << flush;
00400                 }
00401                 catch(...) {
00402                         slog.
00403                             p(Logger::
00404                               Error) <<
00405                             "RServer::issue: error in command sending, good: "
00406                             << _pServerStream->good() << "\n";
00407                 }
00408                 try {
00409                         rs = nlreadline(*_pServerStream, resp, 0);
00410                 }
00411                 catch(...) {
00412                         slog.
00413                             p(Logger::
00414                               Error) <<
00415                             "RServer::issue: error in command readline\n";
00416                 }
00417                 // If the remote server closed the connection (400), reconnect
00418                 if (!_pServerStream->good()) {
00419                         slog.
00420                             p(Logger::
00421                               Warning) <<
00422                             "RServer::issue: no connection to news server\n";
00423                 } else if (strncmp(resp.c_str(), "400", 3) == 0
00424                            || strncmp(resp.c_str(), "503", 3) == 0) {
00425                         slog.
00426                             p(Logger::
00427                               Warning) <<
00428                             "RServer::issue: server closed connection---reconnecting\n";
00429                 }
00430                 /*
00431                    else if(strncmp(resp.c_str(),"480",3)==0) {
00432                    slog.p(Logger::Warning) << "server requests authentication\n";
00433                    req="authinfo user ";
00434                    req.append(_CurrentServer->Username);
00435                    slog.p(Logger::Debug) << req.c_str();
00436                    req.append("\r\n");
00437                    _ServerStream << req;
00438                    rs=nlreadline(_ServerStream,resp,0);
00439                    } */
00440                 else {
00441                         break;
00442                 }
00443 
00444                 // If we have lost the connection to the news server, we try
00445                 // to reconnect to the server and reissue the command.
00446                 for (;;) {
00447                         i--;
00448                         if (i == 0)
00449                                 throw
00450                                     SystemError
00451                                     ("maximum number of retries reached",
00452                                      -1, ERROR_LOCATION);
00453                         disconnect();
00454                         try {
00455                                 connect();
00456                                 break;
00457                         }
00458                         catch(SystemError & se) {
00459                         }
00460                 }
00461         }
00462 
00463         // Check the response code
00464         if ((rs < 3)
00465             || (expresp
00466                 && strncmp(expresp, resp.data(), strlen(expresp)))) {
00467                 string c(command), e(expresp);
00468                 throw ResponseError(c, e, resp);
00469         }
00470 
00471         return resp;
00472 }

void RServer::listgroup const char *  gname,
char *  lstgrp,
unsigned int  f,
unsigned int  l
[virtual]
 

Return a list of article available in a newsgroup.

Parameters:
gname Name of the newsgroup.
lstgrp Pointer to a preallocated array, where the list is stored.
lstgrpsz Size of the array above.
Exceptions:
SystemError If the connection to the news server fails.
NoNewsServerError If no news server is configured for this group.
NoSuchGroupError If the group does not exist.
ResponseError If the news server replies an invalid response code.

Reimplemented in CServer.

Definition at line 718 of file NServer.cc.

References _pServerStream, ERROR_LOCATION, issue(), nlreadline(), NNTP_ISCODE, selectgroup(), slog, and VERB.

Referenced by CServer::listgroup(), and CNewsgroup::listgroup().

00720 {
00721         VERB(slog.
00722              p(Logger::
00723                Debug) << "RServer::listgroup(" << gname << ",...)\n");
00724         const char *p;
00725         unsigned int i;
00726         string resp, line;
00727 
00728         selectgroup(gname);
00729         resp = issue("listgroup\r\n", NULL);
00730         p = resp.c_str();
00731         if (!NNTP_ISCODE(p, "211")) {
00732                 string c("listgroup\r\n"), e("211");
00733                 throw ResponseError(c, e, resp);
00734         }
00735 
00736         for (;;) {
00737                 nlreadline(*_pServerStream, line);
00738                 if (line == ".\r\n")
00739                         break;
00740                 if (!_pServerStream->good())
00741                         throw
00742                             SystemError("error while reading from server",
00743                                         errno, ERROR_LOCATION);
00744                 i = atoi(line.data());
00745                 if (f <= i && i <= l)
00746                         lstgrp[i - f] = 1;
00747         }
00748 }

void RServer::overviewdb Newsgroup ng,
unsigned int  fst,
unsigned int  lst
[virtual]
 

Return the overviewdatabase of the newsgroup >ng<. This function should not be called by the user. Instead it should be called via a Newsgroup object.

Parameters:
name Name of the newsgroup.
Exceptions:
SystemError If the connection to the news server fails.
NoNewsServerError If no news server is configured for this group.
NoSuchGroupError If the group does not exist.
ResponseError If the news server replies an invalid response code.
NotAllowedError If retrieving the overviewdb is prohibited.
NSError If news server does not implement over and xover.

Reimplemented in CServer.

Definition at line 750 of file NServer.cc.

References _CurrentServer, _pServerStream, ASSERT, ERROR_LOCATION, issue(), NNTP_ISCODE, MPListEntry::nntpflags, Logger::p(), selectgroup(), slog, and VERB.

Referenced by CServer::overviewdb(), RNewsgroup::setsize(), and CNewsgroup::sUpdateOverview().

00752 {
00753         VERB(slog.p(Logger::Debug) << "RServer::overviewdb(*ng,"
00754              << fst << "-" << lst << ")\n");
00755         ASSERT(if (!ng) {
00756                throw
00757                AssertionError
00758                ("RServer::overviewdb: ng parameter is a null-pointer",
00759                 ERROR_LOCATION);}
00760         );
00761 
00762         char buf[513];
00763         string resp;
00764         const char *p;
00765         const char *fmt = "over %d-%d\r\n";
00766 
00767         selectgroup(ng->name());
00768         for (;;) {
00769                 if (!(_CurrentServer->nntpflags & MPListEntry::F_OVER))
00770                         fmt = "xover %d-%d\r\n";
00771                 if (!(_CurrentServer->nntpflags & MPListEntry::F_XOVER)) {
00772                         slog.
00773                             p(Logger::
00774                               Critical) <<
00775                             "remote news server neither implements OVER nor XOVER\n";
00776                         throw
00777                             NSError
00778                             ("news server neither implements OVER nor XOVER",
00779                              ERROR_LOCATION);
00780                 }
00781 
00782                 sprintf(buf, fmt, fst, lst);
00783                 resp = issue(buf, NULL);
00784                 p = resp.c_str();
00785                 if (NNTP_ISCODE(p, "224")) {
00786                         ng->readoverdb(*_pServerStream);
00787                         return;
00788                 }
00789                 if (NNTP_ISCODE(p, "502")) {
00790                         throw NotAllowedError(resp, ERROR_LOCATION);
00791                 }
00792                 if (NNTP_ISCODE(p, "412")) {
00793                         slog.
00794                             p(Logger::
00795                               Warning) <<
00796                             "newsgroup disappeared while working on it\n";
00797                         throw NoSuchGroupError("no such group",
00798                                                ERROR_LOCATION);
00799                 }
00800                 if (NNTP_ISCODE(p, "420"))
00801                         throw ResponseError(buf, "224", resp);
00802                 if (fmt[0] == 'o')
00803                         _CurrentServer->nntpflags &= ~MPListEntry::F_OVER;
00804                 else
00805                         _CurrentServer->nntpflags &= ~MPListEntry::F_XOVER;
00806         }
00807 }

int RServer::post Article article  )  [virtual]
 

Post an article.

Todo:
A per server result code should be returned.

We should use the path field here, or introduce a new field that shows the cache-posting-chain (see SourceCode).

Parameters:
article The article to be posted
Returns:
  • 0 if the article has been posted successfully
  • -1 spooling of article is necessary
  • -2 article could be posted to some of the news servers
Exceptions:
InvalidArticleError If the article is not correctly formated.

Implements NServer.

Reimplemented in CServer.

Definition at line 976 of file NServer.cc.

References _ServerList, MPList::entries, ERROR_LOCATION, MPListEntry::flags, Article::getfield(), Article::has_field(), newsgroups, nntp_hostname, nntp_posting_host, Logger::p(), post(), MPList::postserver(), Article::setfield(), slog, TOBASE36, and VERB.

00977 {
00978         VERB(slog.p(Logger::Debug) << "RServer::post(Article)\n");
00979 
00980         // do some sanity checks
00981         string newsgroups;
00982         try {
00983                 if (!article->has_field("from:"))
00984                         throw InvalidArticleError("no from field",
00985                                                   ERROR_LOCATION);
00986                 if (!article->has_field("subject:"))
00987                         throw InvalidArticleError("no subject field",
00988                                                   ERROR_LOCATION);
00989                 newsgroups = article->getfield("newsgroups:");
00990         }
00991         catch(NoSuchFieldError & n) {
00992                 throw InvalidArticleError("no newsgroups field",
00993                                           ERROR_LOCATION);
00994         }
00995 
00996         static int pc = 1;
00997         const char *p, *q;
00998         string cgroup;
00999         char buf[MAXHOSTNAMELEN + 256];
01000         char msgid[MAXHOSTNAMELEN + 256];
01001         MPListEntry **mpe;
01002         MPListEntry *c;
01003         int sc = 0, i;
01004 
01005         //FIXME! We should use the path field here, or introduce a 
01006         //FIXME! new field that shows the cache-posting-chain
01007         if (nntp_posting_host[0]) {
01008                 sprintf(buf, "X-NNTP-Posting-Host: %s\r\n",
01009                         nntp_posting_host);
01010                 article->setfield("X-NNTP-Posting-Host:", buf);
01011         }
01012 
01013         if (!article->has_field("message-id:")) {
01014                 struct timeval tv;
01015                 int j;
01016                 char mid[768];
01017                 char *p;
01018                 p = mid;
01019                 gettimeofday(&tv, NULL);
01020                 TOBASE36(i, tv.tv_sec, p);
01021                 *p++ = '$';
01022 
01023                 j = getpid();
01024                 TOBASE36(i, j, p);
01025                 *p++ = '$';
01026 
01027                 TOBASE36(i, pc, p);
01028                 *p++ = '@';
01029                 strcpy(p, nntp_hostname);
01030                 pc++;
01031 
01032                 sprintf(msgid, "Message-ID: <newscache$%s>\r\n", mid);
01033                 article->setfield("Message-ID:", msgid);
01034         }
01035 
01036         if ((mpe =
01037              (MPListEntry **) malloc(_ServerList->entries.size() *
01038                                      sizeof(MPListEntry *))) == NULL) {
01039                 throw
01040                     SystemError("cannot allocate buffer for post servers",
01041                                 errno, ERROR_LOCATION);
01042         }
01043 
01044         q = newsgroups.c_str();
01045         while (isspace(*q) || *q == ',')
01046                 q++;
01047         do {
01048                 p = q;
01049                 while (*q && *q != ',' && !isspace(*q))
01050                         q++;
01051                 cgroup.assign(p, q - p);
01052                 c = _ServerList->postserver(cgroup.c_str());
01053                 if (c) {
01054                         i = 0;
01055                         while (i < sc && c != mpe[i])
01056                                 i++;
01057                         if (i == sc) {
01058                                 mpe[sc] = c;
01059                                 sc++;
01060                         }
01061                 } else {
01062                         slog.p(Logger::Info)
01063                             << "posting to unknown newsgroup: " << cgroup
01064                             << "\n";
01065                 }
01066                 while (isspace(*q) || *q == ',')
01067                         q++;
01068         } while (*q);
01069 
01070         if (!sc) {
01071                 slog.p(Logger::Info) << "no news server configured for "
01072                     << msgid << "\n";
01073                 free(mpe);
01074                 throw InvalidArticleError("no valid newsgroup",
01075                                           ERROR_LOCATION);
01076         }
01077 
01078         int posts = 0, spool = 0, err = 0;
01079         i = 0;
01080         // Error classification:
01081         // * Duplicate Articles ... Since we use our hostname and people should 
01082         //   be forbidden to use wrong hostnames, we assume that a duplicate 
01083         //   article means that the article arrived already somehow on that 
01084         //   server.
01085         // * NotAllowed ... user wants to post to a private newsgroup
01086         // * PostingFailed ... user submitted badly formatted article
01087         // * Response, System ... problem on remote site occurred
01088         //
01089         //FIX! If this library should be used for other purposes than just 
01090         //FIX! for NewsCache, errors should be resolved using a callback
01091         //FIX! class!
01092         do {
01093                 try {
01094                         if (mpe[i]->flags & MPListEntry::F_OFFLINE) {
01095                                 ++spool;
01096                         } else {
01097                                 post(mpe[i], article);
01098                                 ++posts;
01099                         }
01100                 }
01101                 catch(DuplicateArticleError & dae) {
01102                         ++posts;
01103                 }
01104                 catch(NotAllowedError & nae) {
01105                         err |= 1;
01106                 }
01107                 catch(PostingFailedError & pfe) {
01108                         err |= 2;
01109                 }
01110                 catch(ResponseError & re) {
01111                         ++spool;
01112                 }
01113                 catch(SystemError & se) {
01114                         ++spool;
01115                 }
01116                 catch(Error & e) {
01117                         err |= 4;
01118                 }
01119                 ++i;
01120         } while (i < sc);
01121 
01122         free(mpe);
01123         if (!err)
01124                 return spool ? -1 : 0;
01125         if (err && !posts) {
01126                 throw
01127                     InvalidArticleError
01128                     ("illegaly formatted article or no post permission",
01129                      ERROR_LOCATION);
01130         }
01131         // Hm, on some news servers posting succeeded on others not. 
01132         // What should we do now? Spool it to the error-log?
01133         //FIXME! Report posting as OK and send mail to posting indicating the 
01134         //FIXME! failure.
01135         return -2;
01136 }

void RServer::post MPListEntry srvr,
Article article
[private]
 

Note:
The news server, where the article should be posted, should be taken from the article's newsgroup field. An article posted to comp.os.linux should be posted to the news server from where we receive this newsgroup. POST:
  • 240 post ok
  • 340 send art
  • 440 not allowed
  • 441 post failed
Parameters:
srvr News server the article should be posted to.
article The article to be posted.
Exceptions:
SystemError If the connection to the news server fails.
ResponseError If the news server replies an invalid response code.
NotAllowedError If posting is prohibited.
NoNewsServerError If no news server is configured for this group (in theory, this should be impossible).
DuplicateArticleError The message id exists already on the news server.
PostingFailedError The server returned 441 ...
NoSuchGroupError If the group does not exist (no group selected). Semantically Impossible Exceptions.

Definition at line 887 of file NServer.cc.

References _CurrentServer, _pServerStream, connect(), disconnect(), ERROR_LOCATION, Article::getfield(), MPListEntry::hostname, issue(), nlreadline(), Logger::p(), MPListEntry::retries, MPListEntry::servicename, setserver(), slog, and VERB.

Referenced by CServer::post(), post(), and CServer::postspooled().

00888 {
00889         string resp;
00890         const char *p;
00891         char buf[513];
00892 
00893         VERB(slog.p(Logger::Debug) << "RServer::post({"
00894              << srvr->hostname << "," << srvr->servicename << "}\n");
00895 
00896         setserver(srvr);        // clears current group selection
00897         connect();
00898 
00899         // Before posting an article, we should check whether the article
00900         // exists already on the upstream news server. (Since the post
00901         // command does not indicate duplicates, we have to check this
00902         // manually beforehand.
00903 
00904         try {
00905                 resp = article->getfield("message-id:");
00906                 sprintf(buf, "stat %s\r\n", resp.c_str());
00907                 resp = issue(buf, NULL);
00908                 p = resp.c_str();
00909                 if (strncmp(p, "223", 3) == 0)
00910                         throw DuplicateArticleError("Response 223",
00911                                                     ERROR_LOCATION);
00912                 else
00913         if (strncmp(p, "430", 3) != 0) {
00914                 string c(buf), e("223|430");
00915                 throw ResponseError(c, e, resp);
00916         }
00917         }
00918         catch(NoSuchFieldError e) {
00919                 VERB(slog.
00920                      p(Logger::
00921                        Debug) << "RServer::post message-id not found\n");
00922         }
00923 
00924         for (int i = _CurrentServer->retries + 1;;) {
00925                 resp = issue("post\r\n", NULL);
00926                 p = resp.c_str();
00927                 if (strncmp(p, "340", 3) != 0) {
00928                         if (strncmp(p, "440", 3) == 0)
00929                                 throw NotAllowedError(resp,
00930                                                       ERROR_LOCATION);
00931                         throw ResponseError("post\r\n", "[34]40", resp);
00932                 }
00933                 *_pServerStream << *article << ".\r\n" << flush;
00934                 nlreadline(*_pServerStream, resp);
00935                 if (_pServerStream->good())
00936                         break;
00937 
00938                 // If we have lost the connection to the news server, we try
00939                 // to reconnect to the server and reissue the command.
00940                 slog.
00941                     p(Logger::
00942                       Warning) <<
00943                     "lost connection to news server unexpectedly\n";
00944                 for (;;) {
00945                         i--;
00946                         if (i == 0)
00947                                 throw
00948                                     SystemError
00949                                     ("maximum number of retries reached while trying to post an article",
00950                                      -1, ERROR_LOCATION);
00951                         disconnect();
00952                         try {
00953                                 connect();
00954                                 break;
00955                         }
00956                         catch(SystemError & se) {
00957                         }
00958                 }
00959         }
00960 
00961         p = resp.c_str();
00962         if (strncmp("240", p, 3) != 0) {
00963                 if (strncmp("440", p, 3) == 0)
00964                         throw NotAllowedError(resp, ERROR_LOCATION);
00965                 if (strncmp("441", p, 3) == 0)
00966                         throw PostingFailedError(resp, ERROR_LOCATION);
00967                 throw ResponseError("post\r\n", "[23]40,44[01]", resp);
00968         }
00969 }

void RServer::selectgroup const char *  name,
int  force = 0
[protected]
 

Select a newsgroup on the remote news server. Remarks: group: 211 nflsg selected 411 no group

Parameters:
name Name of the newsgroup
Exceptions:
SystemError If the connection to the news server fails
NoSuchGroupError ... if the group does not exist, or no news server has been configured for this group.
ResponseError If the news server replies an invalid response code

Definition at line 489 of file NServer.cc.

References _CurrentGroup, _ServerList, ASSERT, Logger::Error, ERROR_LOCATION, ActiveDB::get(), issue(), GroupInfo::name(), NNTP_ISCODE, Logger::p(), MPList::server(), ActiveDB::set(), GroupInfo::set(), setserver(), slog, and VERB.

Referenced by article(), connect(), getgroup(), CServer::groupinfo(), groupinfo(), listgroup(), and overviewdb().

00490 {
00491         VERB(slog.
00492              p(Logger::Debug) << "RServer::selectgroup(" << name << ")\n");
00493         ASSERT(if (strlen(name) > 512) {
00494                throw
00495                AssertionError
00496                ("RServer::selectgroup: name of newsgroup too long\n",
00497                 ERROR_LOCATION);}
00498         );
00499 
00500         if (!force && strcmp(_CurrentGroup.name(), name) == 0)
00501                 return;
00502 
00503         char buf[MAXPATHLEN];
00504         const char *ep, *p;
00505         int nbr, fst, lst;
00506         MPListEntry *mpe;
00507         string resp;
00508 
00509         // Find the server responsible for this newsgroup
00510         if ((mpe = _ServerList->server(name)) == NULL) {
00511                 throw NoSuchGroupError("no such group", ERROR_LOCATION);
00512         }
00513 
00514         setserver(mpe);
00515         sprintf(buf, "group %s\r\n", name);
00516         resp = issue(buf, NULL);
00517 
00518         p = resp.c_str();
00519         if (!NNTP_ISCODE(p, "211")) {
00520                 if (NNTP_ISCODE(p, "411"))
00521                         throw NoSuchGroupError("no such group",
00522                                                ERROR_LOCATION);
00523                 string c(buf), e("211");
00524                 throw ResponseError(c, e, resp);
00525         }
00526 
00527         p += 3;
00528         nbr = strtol(p, (char **) &p, 10);
00529         fst = strtol(p, (char **) &p, 10);
00530         lst = strtol(p, (char **) &ep, 10);
00531 
00532         if (ep == resp) {
00533                 //THROW!?! _Response_should_be_
00534                 // 211 $n $f $l $g selected
00535                 // $n Nbr. of articles in group
00536                 // $f Nbr. of first article
00537                 // $l Nbr. of last article
00538                 // $g Name of newsgroup
00539                 slog.p(Logger::Error)
00540                     <<
00541                     "response from news server not in >>211 n f l g ...<< format\n";
00542                 // If we cannot parse the response code, use the first/last number 
00543                 // and number of articles from the active database
00544                 if (_ActiveDB->get(name, &_CurrentGroup) < 0) {
00545                         // If the above call fails, _ActiveDB will be updated with a
00546                         // probably inconsistent posting flag ('y')
00547                         _CurrentGroup.set(name, 0, 0, 0);
00548                         _ActiveDB->set(_CurrentGroup);
00549                 }
00550         } else {
00551                 _ActiveDB->get(name, &_CurrentGroup);
00552                 // If the above call fails, _ActiveDB will be updated with a
00553                 // probably inconsistent posting flag ('y')
00554                 _CurrentGroup.set(name, fst, lst, nbr);
00555                 _ActiveDB->set(_CurrentGroup);
00556         }
00557 }

void RServer::setserver MPListEntry server  )  [protected]
 

Disconnect from the current news server and set a new news server as the current one, if the new one is different to the current.

Parameters:
server A multiplexinglist entry pointing to the new news server. This parameter must not be freed as long as RServer uses this news server, since only the pointer is stored. As soon as RServer connects to this news server, it will store whether posting is allowed in server->flags.

Definition at line 475 of file NServer.cc.

References _CurrentGroup, _CurrentServer, disconnect(), MPListEntry::hostname, GroupInfo::init(), Logger::p(), MPListEntry::servicename, slog, and VERB.

Referenced by active(), CServer::article(), article(), post(), and selectgroup().

00476 {
00477         VERB(slog.p(Logger::Debug) << "RServer::setserver({"
00478              << server->hostname << "," << server->servicename << "})\n");
00479 
00480         if (_CurrentServer == server)
00481                 return;
00482 
00483         if (_CurrentServer)
00484                 disconnect();
00485         _CurrentGroup.init();
00486         _CurrentServer = server;
00487 }

void RServer::setserverlist MPList serverlist  ) 
 

Delete all previously allocated data and install a new list of news servers and newsgroups.

Parameters:
serverlist The list of news servers. This parameter must not be freed as long as RServer uses this server-list, since only the pointer is stored.

Definition at line 559 of file NServer.cc.

References _CurrentGroup, _CurrentServer, _ServerList, disconnect(), GroupInfo::init(), Logger::p(), slog, and VERB.

Referenced by ~RServer().

00560 {
00561         VERB(slog.p(Logger::Debug) << "RServer::setserverlist()\n");
00562         // Disconnect
00563         disconnect();
00564 
00565         // Clean up
00566         _CurrentGroup.init();
00567         if (_ActiveDB) {
00568                 delete _ActiveDB;
00569                 _ActiveDB = NULL;
00570         }
00571         _CurrentServer = NULL;
00572         _ServerList = serverlist;
00573 }


Member Data Documentation

GroupInfo RServer::_CurrentGroup [protected]
 

Definition at line 239 of file NServer.h.

Referenced by connect(), getgroup(), groupinfo(), selectgroup(), setserver(), and setserverlist().

MPListEntry* RServer::_CurrentServer [protected]
 

Definition at line 238 of file NServer.h.

Referenced by active(), article(), connect(), init(), issue(), overviewdb(), post(), RServer(), setserver(), and setserverlist().

sstream* RServer::_pServerStream [protected]
 

Definition at line 241 of file NServer.h.

Referenced by active(), article(), connect(), disconnect(), is_connected(), issue(), listgroup(), overviewdb(), post(), and RServer().

MPList* RServer::_ServerList [protected]
 

Definition at line 237 of file NServer.h.

Referenced by active(), article(), getserverlist(), init(), post(), RServer(), selectgroup(), and setserverlist().


The documentation for this class was generated from the following files:
Generated on Fri Aug 20 10:58:12 2004 for NewsCache by doxygen 1.3.6-20040222