00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
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
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
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
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191 return -1;
00192 }
00193
00194
00195
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
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
00283
00284
00285
00286
00287
00288
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
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
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
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
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
00432
00433
00434
00435
00436
00437
00438
00439
00440 else {
00441 break;
00442 }
00443
00444
00445
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
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
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
00534
00535
00536
00537
00538
00539 slog.p(Logger::Error)
00540 <<
00541 "response from news server not in >>211 n f l g ...<< format\n";
00542
00543
00544 if (_ActiveDB->get(name, &_CurrentGroup) < 0) {
00545
00546
00547 _CurrentGroup.set(name, 0, 0, 0);
00548 _ActiveDB->set(_CurrentGroup);
00549 }
00550 } else {
00551 _ActiveDB->get(name, &_CurrentGroup);
00552
00553
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
00563 disconnect();
00564
00565
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
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
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
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
00699
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
00823
00824
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
00851
00852
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
00861
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);
00897 connect();
00898
00899
00900
00901
00902
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
00939
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
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
01006
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
01081
01082
01083
01084
01085
01086
01087
01088
01089
01090
01091
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
01132
01133
01134
01135 return -2;
01136 }
01137
01138 #undef TOBASE36
01139
01140
01141
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
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
01203
01204
01205
01206
01207
01208
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
01269 if (errc < 0)
01270 throw NoSuchGroupError("no such group",
01271 ERROR_LOCATION);
01272 }
01273 catch(SystemError & sr) {
01274
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
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
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
01456
01457
01458
01459
01460
01461
01462
01463
01464
01465
01466
01467
01468
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
01501
01502
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
01511
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 }