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

NServer.cc

Go to the documentation of this file.
00001 /* This program is free software; you can redistribute it and/or modify
00002  * it under the terms of the GNU General Public License as published by
00003  * the Free Software Foundation; either version 2 of the License, or
00004  * (at your option) any later version.
00005  *
00006  * This program is distributed in the hope that it will be useful,
00007  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00008  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00009  * GNU General Public License for more details.
00010  *
00011  * You should have received a copy of the GNU General Public License
00012  * along with this program; if not, write to the Free Software
00013  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00014  */
00015 /*
00016  * Portions Copyright (C) 2002 Herbert Straub
00017  *      post error in a cascaded configuration fixed
00018  * Portions Copyright (C) 2002 Herbert Straub
00019  *      convert ServerStram to *_pServerStream and handle
00020  *      connect/disconnect with new/delete
00021  */
00022 #include "config.h"
00023 
00024 #include <sys/types.h>
00025 #include <sys/stat.h>
00026 #include <sys/param.h>
00027 #ifdef TIME_WITH_SYS_TIME
00028 #include <sys/time.h>
00029 #endif
00030 #include <fcntl.h>
00031 #include <unistd.h>
00032 #include <netdb.h>
00033 #include <stdlib.h>
00034 #include <stdio.h>
00035 #include <errno.h>
00036 
00037 #include <iostream>
00038 #include <fstream>
00039 #include <string>
00040 #ifdef HAVE_SSTREAM
00041 #include <sstream>
00042 #else
00043 #include <strstream>
00044 #endif
00045 
00046 #include "NServer.h"
00047 #include "RNewsgroup.h"
00048 #include "readline.h"
00049 
00050 using namespace std;
00051 
00052 #define NNTP_ISCODE(reply,code) ((reply)[0]==(code)[0] && \
00053                                  (reply)[1]==(code)[1] && \
00054                                  (reply)[2]==(code)[2])
00055 
00056 char nntp_hostname[MAXHOSTNAMELEN];
00057 char nntp_posting_host[MAXHOSTNAMELEN];
00058 
00059 //********************
00060 //***   NServer
00061 //********************
00062 NServer::NServer()
00063 {
00064         VERB(slog.p(Logger::Debug) << "NServer::NServer()\n");
00065         if (nntp_hostname[0] == '\0') {
00066                 strcpy(nntp_hostname, getfqdn());
00067         }
00068         slog.p(Logger::Debug) << "NServer::NServer() hostname set to: "
00069             << nntp_hostname << "\n";
00070         _OverviewFormat = NULL;
00071         _ActiveDB = NULL;
00072 }
00073 
00074 NServer::~NServer()
00075 {
00076         VERB(slog.p(Logger::Debug) << "NServer::~NServer()\n");
00077         if (_OverviewFormat)
00078                 delete _OverviewFormat;
00079         if (_ActiveDB)
00080                 delete _ActiveDB;
00081         _OverviewFormat = NULL;
00082         _ActiveDB = NULL;
00083 }
00084 
00085 void NServer::freegroup(Newsgroup * group)
00086 {
00087         VERB(slog.p(Logger::Debug) << "NServer::freegroup(*group)\n");
00088         delete group;
00089 }
00090 
00091 //********************
00092 //***   LServer
00093 //********************
00094 LServer::LServer(const char *spooldir)
00095 {
00096         VERB(slog.
00097              p(Logger::Debug) << "LServer::LServer(" << spooldir << ")\n");
00098         init(spooldir);
00099 }
00100 
00101 LServer::~LServer()
00102 {
00103         VERB(slog.p(Logger::Debug) << "LServer::~LServer()\n");
00104 
00105         delete pSpool;
00106         pSpool = NULL;
00107 }
00108 
00109 void LServer::init(const char *spooldir)
00110 {
00111         VERB(slog.
00112              p(Logger::Debug) << "LServer::init(" << spooldir << ")\n");
00113 
00114         ASSERT(if
00115                (strlen(spooldir) >
00116                 sizeof(_SpoolDirectory) - MAXGROUPNAMELEN - 16) {
00117                throw
00118                AssertionError
00119                ("name of spooldirectory too long. longest groupname+spooldirlength does not fit into maxpathlen",
00120                 ERROR_LOCATION);}
00121         );
00122         strcpy(_SpoolDirectory, spooldir);
00123         pSpool = new ArtSpooler(spooldir);
00124 
00125         if (!_OverviewFormat)
00126                 _OverviewFormat = new OverviewFmt();
00127         if (!_ActiveDB)
00128                 _ActiveDB = NULL;
00129 }
00130 
00131 ActiveDB *LServer::active()
00132 {
00133         return _ActiveDB;
00134 }
00135 
00136 GroupInfo *LServer::groupinfo(const char *name)
00137 {
00138         VERB(slog.
00139              p(Logger::Debug) << "LServer::groupinfo(" << name << ")\n");
00140         static GroupInfo agroup;
00141 
00142         ASSERT(if (strlen(name) > 512) {
00143                throw
00144                AssertionError
00145                ("LServer::groupinfo: name of newsgroup too long\n",
00146                 ERROR_LOCATION);}
00147         );
00148 
00149         if (_ActiveDB->get(name, &agroup) < 0)
00150                 throw NoSuchGroupError("no such group", ERROR_LOCATION);
00151         return &agroup;
00152 }
00153 
00154 Newsgroup *LServer::getgroup(const char *name)
00155 {
00156         VERB(slog.
00157              p(Logger::Debug) << "LServer::getgroup(" << name << ")\n");
00158         if (!_ActiveDB->hasgroup(name))
00159                 throw NoSuchGroupError("no such group", ERROR_LOCATION);
00160         return new NVNewsgroup(_OverviewFormat, _SpoolDirectory, name);
00161 }
00162 
00163 int LServer::post(Article * article)
00164 {
00165 //   TRACE(ctrace << "LServer::post(Article*)\n");
00166 //   char fn[1024];
00167 //   fstream fs;
00168 //   int qnbr;
00169 
00170 //   sprintf(fn,".outgoing/.qnbr");
00171 //   fs.open(fn,ios::in|ios::out);
00172 //   fs >> qnbr;
00173 //   if(!fs.good()) {
00174 //     qnbr=0;
00175 //     fs.clear();
00176 //   }
00177 //   fs.seekp(0,ios::beg);
00178 //   fs << qnbr+1 << endl;
00179 //   if(!fs.good()) {
00180 //     DEBUG(cdbg << "Failure on .outgoing/.qnbr\n");
00181 //   }
00182 //   fs.close();
00183 
00184 //   DEBUG(cdbg << "LServer: qnbr=" << qnbr << endl << article << endl);
00185 
00186 //   sprintf(fn,".outgoing/a%d",qnbr);
00187 //   fs.open(fn,ios::out);
00188 //   fs.unsetf(ios::skipws);
00189 //   fs << article;
00190 //   fs.close();
00191         return -1;
00192 }
00193 
00194 //********************
00195 //***   RServer
00196 //********************
00197 RServer::RServer(MPList * serverlist)
00198 {
00199         VERB(slog.p(Logger::Debug) << "RServer::RServer/1\n");
00200         init(serverlist);
00201 }
00202 
00203 RServer::~RServer()
00204 {
00205         VERB(slog.p(Logger::Debug) << "RServer::~RServer()\n");
00206         setserverlist(NULL);
00207 }
00208 
00209 void RServer::init(MPList * serverlist)
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 }
00221 
00222 #define RSERVER_CONNECT_CHECK_CONNECTION(errstrg) \
00223     if(!_pServerStream->good()) { \
00224       if(i) { \
00225         i--; \
00226         sleep(1); \
00227         continue; \
00228       } \
00229       sprintf(buf,"Connection to %s:%s failed", \
00230               _CurrentServer->hostname,_CurrentServer->servicename); \
00231       throw SystemError(buf,errno, ERROR_LOCATION); \
00232     }
00233 void RServer::connect()
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 }
00370 
00371 #undef RSERVER_CONNECT_CHECK_CONNECTION
00372 
00373 void RServer::disconnect()
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 }
00383 
00384 string RServer::issue(const char *command, const char *expresp)
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 }
00473 
00474 
00475 void RServer::setserver(MPListEntry * server)
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 }
00488 
00489 void RServer::selectgroup(const char *name, int force)
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 }
00558 
00559 void RServer::setserverlist(MPList * serverlist)
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 }
00574 
00575 ActiveDB *RServer::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 }
00684 
00685 GroupInfo *RServer::groupinfo(const char *name)
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 }
00706 
00707 Newsgroup *RServer::getgroup(const char *name)
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 }
00717 
00718 void RServer::listgroup(const char *gname, char *lstgrp,
00719                         unsigned int f, unsigned int l)
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 }
00749 
00750 void RServer::overviewdb(Newsgroup * ng, unsigned int fst,
00751                          unsigned int lst)
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 }
00808 
00809 void RServer::article(const char *gname, unsigned int nbr, Article * art)
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 }
00833 
00834 void RServer::article(const char *id, Article * art)
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 }
00886 
00887 void RServer::post(MPListEntry * srvr, Article * article)
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 }
00970 
00971 #define TOBASE36(i,j,p) while(j) {\
00972         (i)=(j)%36; (j)/=36;\
00973         if((i)<10) *(p)++='0'+(i);\
00974         else *(p)++='a'+(i)-10;\
00975 }
00976 int RServer::post(Article * article)
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 }
01137 
01138 #undef TOBASE36
01139 
01140 //********************
01141 //***   CServer
01142 //********************
01143 CServer::CServer(const char *spooldir, MPList * serverlist)
01144 {
01145         VERB(slog.
01146              p(Logger::
01147                Debug) << "CServer::CServer(" << spooldir <<
01148              ",*serverlist)\n");
01149         char buf[MAXPATHLEN];
01150 
01151         ASSERT(if (!serverlist) {
01152                slog.
01153                p(Logger::
01154                  Notice) <<
01155                "CServer::CServer: serverlist is a null-pointer\n";}
01156         );
01157         if (!_OverviewFormat) {
01158                 _OverviewFormat = new OverviewFmt;
01159         }
01160         if (!_ActiveDB) {
01161                 sprintf(buf, "%s/.active", spooldir);
01162                 _ActiveDB = _NVActiveDB = new NVActiveDB(buf);
01163         }
01164 
01165         ASSERT(if (!serverlist) {
01166                slog.
01167                p(Logger::
01168                  Notice) <<
01169                "CServer::CServer: serverlist is a null-pointer\n";}
01170         );
01171         // other constructors
01172         LServer::init(spooldir);
01173         RServer::init(serverlist);
01174 
01175         ASSERT(if (!_ServerList) {
01176                slog.
01177                p(Logger::
01178                  Notice) <<
01179                "CServer::CServer: serverlist is a null-pointer\n";
01180                exit(9);}
01181         );
01182         _TTLActive = 300;
01183         _TTLDesc = 500;
01184 }
01185 
01186 CServer::~CServer()
01187 {
01188         VERB(slog.p(Logger::Debug) << "CServer::~CServer()\n");
01189         if (_OverviewFormat) {
01190                 delete _OverviewFormat;
01191                 _OverviewFormat = NULL;
01192         }
01193         if (_NVActiveDB) {
01194                 delete _NVActiveDB;
01195                 _NVActiveDB = NULL;
01196                 _ActiveDB = NULL;
01197         }
01198 }
01199 
01200 ActiveDB *CServer::active()
01201 {
01202         // The validity of the active database has to be tested twice,
01203         // because it is possible that several processes can reach
01204         // this point and in this case all these processes would
01205         // transfer the active database
01206         // Locking the database before the first active_valid call
01207         // is no good idea, since this requires to set a lock
01208         // even in cases where this is not necessary at all
01209 
01210         if (!active_valid()) {
01211                 slog.p(Logger::Info) << "CServer::active: active database timed out\n";
01212                 _NVActiveDB->lock(NVcontainer::ExclLock);
01213                 try {
01214                         if (!active_valid()) {
01215                                 RServer::active();
01216                         }
01217                 }
01218                 catch(Error & e) {
01219                         slog.p(Logger::Alert)
01220                             <<
01221                             "CServer::active: UNEXPECTED EXCEPTION CAUGHT, WHILE IN CRITICAL REGION!\n"
01222                             <<
01223                             "CServer::active: PLEASE REPORT TO h.straub@aon.at\n";
01224                         e.print();
01225                 }
01226                 catch(...) {
01227                         slog.p(Logger::Alert)
01228                             <<
01229                             "CServer::active: UNEXPECTED EXCEPTION CAUGHT, WHILE IN CRITICAL REGION!\n"
01230                             <<
01231                             "CServer::active: PLEASE REPORT TO h.straub@aon.at\n";
01232                 }
01233                 _NVActiveDB->lock(NVcontainer::UnLock);
01234         }
01235         return _NVActiveDB;
01236 }
01237 
01238 GroupInfo *CServer::groupinfo(const char *name)
01239 {
01240         VERB(slog.
01241              p(Logger::Debug) << "CServer::groupinfo(" << name << ")\n");
01242         ASSERT(if (strlen(name) > 512) {
01243                throw
01244                AssertionError
01245                ("CServer::groupinfo: Name of newsgroup too long\n",
01246                 ERROR_LOCATION);}
01247         );
01248 
01249         static GroupInfo agroup;
01250         int errc;
01251         MPListEntry *mpe;
01252 
01253         if ((mpe = _ServerList->server(name)) == NULL) {
01254                 throw NoSuchGroupError("no such group", ERROR_LOCATION);
01255         }
01256         if (mpe->flags & MPListEntry::F_OFFLINE)
01257                 return LServer::groupinfo(name);
01258 
01259         if ((errc = _NVActiveDB->get(name, &agroup)) < 0 ||
01260             agroup.mtime() + mpe->groupTimeout < nvtime(NULL)) {
01261                 VERB(slog.p(Logger::Debug) << "CServer::groupinfo " 
01262                                 << "groupinfo timed out\n");
01263                 try {
01264                         RServer::selectgroup(name, 1);
01265                         agroup = _CurrentGroup;
01266                 }
01267                 catch(ResponseError & re) {
01268                         // in case of a response error, use cached values
01269                         if (errc < 0)
01270                                 throw NoSuchGroupError("no such group",
01271                                                        ERROR_LOCATION);
01272                 }
01273                 catch(SystemError & sr) {
01274                         // in case of a system error, use cached values
01275                         if (errc < 0)
01276                                 throw NoSuchGroupError("no such group",
01277                                                        ERROR_LOCATION);
01278                 }
01279         }
01280         return &agroup;
01281 }
01282 
01283 Newsgroup *CServer::getgroup(const char *name)
01284 {
01285         VERB(slog.
01286              p(Logger::Debug) << "CServer::getgroup(" << name << ")\n");
01287         CNewsgroup *grp;
01288         MPListEntry *mpe;
01289 
01290         if ((mpe = _ServerList->server(name)) == NULL) {
01291                 throw NoSuchGroupError("no such group", ERROR_LOCATION);
01292         }
01293 
01294         if (mpe->flags & MPListEntry::F_OFFLINE) {
01295                 // offline => use NVNewsgroup
01296                 GroupInfo grpinfo;
01297                 if (_NVActiveDB->get(name, &grpinfo) < 0)
01298                         throw NoSuchGroupError("no such group",
01299                                                ERROR_LOCATION);
01300                 NVNewsgroup *grp =
01301                     new NVNewsgroup(_OverviewFormat, _SpoolDirectory,
01302                                     name);
01303                 grp->setsize(grpinfo.first(), grpinfo.last());
01304                 return grp;
01305         }
01306 
01307         if (!(mpe->flags & MPListEntry::F_CACHED)) {
01308                 // not-cached => use RNewsgroup
01309                 GroupInfo *grpinfo = groupinfo(name);
01310                 if (_NVActiveDB->get(name, grpinfo) < 0)
01311                         throw NoSuchGroupError("no such group",
01312                                                ERROR_LOCATION);
01313                 RNewsgroup *grp =
01314                     new RNewsgroup(this, _OverviewFormat, name);
01315                 grp->setsize(grpinfo->first(), grpinfo->last());
01316                 return grp;
01317         }
01318 
01319         GroupInfo *grpinfo = groupinfo(name);
01320         grp = new CNewsgroup(this, _OverviewFormat, _SpoolDirectory, name);
01321         grp->setsize(grpinfo->first(), grpinfo->last());
01322         grp->setttl(mpe->groupTimeout);
01323         return grp;
01324 }
01325 
01326 int CServer::post(Article * article)
01327 {
01328         VERB(slog.p(Logger::Debug) << "CServer::post(Article*)\n");
01329         int r;
01330 
01331         if ((r = RServer::post(article)) < 0) {
01332                 spoolarticle(article);
01333                 return 1;
01334         }
01335 #ifdef ENABLE_HEADERLOG
01336         char fn[MAXPATHLEN];
01337         fstream fs;
01338         struct timeval tv;
01339 
01340         gettimeofday(&tv, NULL);
01341 
01342         sprintf(fn, "%s/.headerlog", _SpoolDirectory);
01343         fs.open(fn, ios::app);
01344         fs << getpid() << " " << tv.tv_sec << endl;
01345         article->write(fs, Article::Head);
01346         fs.close();
01347 #endif
01348         return 0;
01349 }
01350 
01351 void CServer::spoolarticle(Article * article)
01352 {
01353         VERB(slog.p(Logger::Debug) << "CServer::spoolarticle\n");
01354 
01355         try {
01356                 pSpool->spoolArt(*article);
01357         } catch(SystemError se) {
01358                 slog.p(Logger::Error) << "CServer::spoolarticle: catched\
01359                 SystemError while spooling article\n";
01360                 throw se;
01361         }
01362         catch(...) {
01363                 slog.p(Logger::Error) << "CServer::spoolarticle: catched\
01364                 unknown error while spooling article";
01365                 throw;
01366         }
01367 }
01368 
01369 void CServer::postspooled(void)
01370 {
01371         VERB(slog.p(Logger::Debug) << "CServer::postspooled()\n");
01372 
01373         Article *pA;
01374         int s;
01375 
01376         try {
01377                 for (pA = pSpool->getSpooledArt(); pA != NULL;
01378                      pA = pSpool->getSpooledArt()) {
01379                         try {
01380                                 s = RServer::post(pA);
01381                                 if (s == -1) {
01382                                         slog.p(Logger::Debug)
01383                                             << "CServer::postspooled():\
01384                                         RServer::post returns Spooling\
01385                                         necessary; ID:" << pA->getfield("message-id") << "\n";
01386                                         pSpool->storeBadArt(*pA);
01387                                 } else if (s == -2) {
01388                                         slog.p(Logger::Error)
01389                                             << "CServer::postspooled():\
01390                                         RServer::post returns -2; ID:" << pA->getfield("message-id") << "\n";
01391                                         pSpool->storeBadArt(*pA);
01392                                 }
01393                         }
01394                         catch(InvalidArticleError iae) {
01395                                 slog.p(Logger::Error)
01396                                     << "CServer::postspooled(): RServer::post\
01397                                 throw InvalidArticleError; ID: "
01398                                     << pA->getfield("message-id") << "\n";
01399                                 pSpool->storeBadArt(*pA);
01400                         }
01401                         delete pA;
01402                 }
01403         }
01404         catch(SystemError se) {
01405                 slog.p(Logger::Error) << "CServer::postspooled():\
01406                         Error posting articles from spool\n";
01407                 throw se;
01408         }
01409         catch(...) {
01410                 slog.p(Logger::Error) << "CServer::postspooled();\
01411                         Unknown error while posting articles\n";
01412                 throw;
01413         }
01414 }
01415 
01416 void CServer::listgroup(const char *gname, char *lstgrp,
01417                         unsigned int f, unsigned int l)
01418 {
01419         VERB(slog.
01420              p(Logger::
01421                Debug) << "CServer::listgroup(" << gname << ",...)\n");
01422         ASSERT(if (!gname) {
01423                throw
01424                AssertionError
01425                ("CServer::listgroup: gname parameter is a null-pointer",
01426                 ERROR_LOCATION);}
01427                if (!lstgrp) {
01428                throw
01429                AssertionError
01430                ("CServer::listgroup: lstgrp parameter is a null-pointer",
01431                 ERROR_LOCATION);}
01432         );
01433         MPListEntry *mpe;
01434 
01435         mpe = _ServerList->server(gname);
01436         if (mpe->flags & MPListEntry::F_OFFLINE)
01437                 throw NSError("cannot listgroup in offline mode",
01438                               ERROR_LOCATION);
01439 
01440         RServer::listgroup(gname, lstgrp, f, l);
01441 }
01442 
01443 void CServer::overviewdb(Newsgroup * ng, unsigned int fst,
01444                          unsigned int lst)
01445 {
01446         VERB(slog.p(Logger::Debug) << "CServer::overviewdb(*ng,"
01447              << fst << "-" << lst << ")\n");
01448         MPListEntry *mpe;
01449 
01450         mpe = _ServerList->server(ng->name());
01451         if (!(mpe->flags & MPListEntry::F_OFFLINE))
01452                 RServer::overviewdb(ng, fst, lst);
01453 }
01454 
01455 /* CServer::article
01456  * Description:
01457  *   Return a given article of a given newsgroup
01458  * Parameters:
01459  *   gname ... Name of the newsgroup
01460  *   nbr ... Number of the article
01461  * Return:
01462  *   Pointer to a statically preallocated article
01463  * Exceptions:
01464  *   SystemError ... if the connection to the news server fails
01465  *   NoNewsServerError ... if no news server is configured for this group
01466  *   NoSuchGroupError ... if the group does not exist
01467  *   NoSuchArticleError ... if the requested article does not exist
01468  *   NSError ... if in offline mode
01469  */
01470 void CServer::article(const char *gname, unsigned int nbr, Article * art)
01471 {
01472         VERB(slog.p(Logger::Debug) << "CServer::article(" << nbr << ")\n");
01473         MPListEntry *mpe;
01474 
01475         mpe = _ServerList->server(gname);
01476         if (mpe->flags & MPListEntry::F_OFFLINE)
01477                 throw NSError("cannot retrieve articles in offline mode",
01478                               ERROR_LOCATION);
01479 
01480         RServer::article(gname, nbr, art);
01481 }
01482 
01483 void CServer::article(const char *id, Article * art)
01484 {
01485         VERB(slog.p(Logger::Debug) << "CServer::article(" << id << ")\n");
01486         char buf[1024];
01487         const char *p;
01488         string resp;
01489 
01490         sprintf(buf, "article %s\r\n", id);
01491         if (_CurrentServer
01492             && !(_CurrentServer->flags & MPListEntry::F_OFFLINE)) {
01493                 resp = issue(buf, NULL);
01494                 p = resp.c_str();
01495                 if (strncmp(p, "220", 3) == 0) {
01496                         art->read(*_pServerStream);
01497                         art->setnbr(-1);
01498                         return;
01499                 }
01500                 // 412 cannot happen since we specified the article id
01501                 // 420 -"- since we specified the article id
01502                 // 423 -"- since we specified the article id, not the art nbr
01503                 if (strncmp(p, "430", 3) != 0) {
01504                         slog.p(Logger::Notice)
01505                             <<
01506                             "illegal response code to <artcile <id>> request\n"
01507                             << p;
01508                 }
01509         }
01510         // check whether the article can be found at one of the other
01511         // news servers
01512         MPListEntry *cs = _CurrentServer;
01513         for (unsigned int i = 0; i < _ServerList->entries.size(); i++) {
01514                 if (cs == &(_ServerList->entries[i]) ||
01515                     !_ServerList->entries[i].hostname[0] ||
01516                     _ServerList->entries[i].flags & MPListEntry::F_OFFLINE)
01517                         continue;
01518                 setserver(&(_ServerList->entries[i]));
01519                 connect();
01520                 resp = issue(buf, NULL);
01521                 p = resp.c_str();
01522                 if (strncmp(p, "220", 3) == 0) {
01523                         art->read(*_pServerStream);
01524                         art->setnbr(-1);
01525                         return;
01526                 }
01527                 if (strncmp(p, "430", 3) != 0) {
01528                         slog.p(Logger::Notice)
01529                             <<
01530                             "illegal response code to <artcile <id>> request\n"
01531                             << p;
01532                 }
01533         }
01534         throw NoSuchArticleError(resp, ERROR_LOCATION);
01535 }

Generated on Sun Oct 24 21:08:18 2004 for NewsCache by doxygen 1.3.6-20040222