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

NewsCache.cc

Go to the documentation of this file.
00001 /* NewsCache.cc 
00002  * -- (c) 1996-1998 by Thomas GSCHWIND <tom@infosys.tuwien.ac.at>
00003  * -- implements the cache server in combination with the NServer library
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation; either version 2 of the License, or
00008  * (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00018  */
00019 /*
00020  * Portions Copyright (C) 2001-2003 Herbert Straub
00021  * Herbert Straub: modify ns_stat: bug fix stat by messageId
00022  * Herbert Straub: adding nice values for master and client
00023  * Herbert Straub: debug output in selectgroup
00024  */
00025 #include "config.h"
00026 
00027 #if !(defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__))
00028 #include <crypt.h>
00029 #endif
00030 #include <ctype.h>
00031 #include <unistd.h>
00032 #include <string.h>
00033 #include <errno.h>
00034 #include <getopt.h>
00035 #include <netinet/in.h>
00036 #include <arpa/inet.h>
00037 #include <netdb.h>
00038 #include <signal.h>
00039 #include <sys/socket.h>
00040 #ifdef TIME_WITH_SYS_TIME
00041 #include <sys/time.h>
00042 #endif
00043 #ifdef HAVE_SETPRIORITY
00044 #include <sys/resource.h>
00045 #endif
00046 #include <sys/types.h>
00047 #include <sys/wait.h>
00048 #include <time.h>
00049 #include <limits.h>
00050 #include <socket++/sockstream.h>
00051 
00052 char *ConfigurationOptions[] = {
00053 #ifdef MD5_CRYPT
00054         "--with-md5",
00055 #endif
00056 #ifdef MD5_AUTO
00057         "--with-md5auto",
00058 #endif
00059 #ifdef SHADOW_PASSWORD
00060         "--with-shadow",
00061 #endif
00062 #ifdef PAM_AUTH
00063         "--with-pam="PAM_DEFAULT_SERVICENAME,
00064 #endif
00065 #ifdef WITH_SYSLOG
00066         "--with-syslog",
00067 #endif
00068 #ifdef ENABLE_HEADERLOG
00069         "--enable_headerlog",
00070 #endif
00071 #ifdef ENABLE_DEBUG
00072         "--enable-debug",
00073 #endif
00074 #ifdef PLAIN_TEXT_PASSWORDS
00075         "--enable-plainpass",
00076 #endif
00077 #ifdef ENABLE_ASSERTIONS
00078         "--enable-assertions",
00079 #endif
00080 #ifdef WITH_UNIQUE_PACKAGE_NAME
00081         "--with-uniquenames",
00082 #endif
00083         NULL
00084 };
00085 
00086 using namespace std;
00087 
00088 #ifdef HAVE_LIBWRAP
00089 #include <tcpd.h>
00090 extern "C" int hosts_ctl(char *daemon, char *client_name,
00091                          char *client_addr, char *client_user);
00092 #endif
00093 
00094 #if (defined (MD5_AUTO) && !defined (MD5_CRYPT))
00095 #define MD5_CRYPT
00096 #endif                          /*MD5_AUTO */
00097 
00098 #include <unistd.h>
00099 #include <pwd.h>
00100 #ifdef SHADOW_PASSWORD
00101 #include <shadow.h>
00102 #endif                          /*SHADOW_PASSWORD */
00103 #ifdef MD5_CRYPT
00104 #include "md5crypt.h"
00105 #endif                          /*MD5_CRYPT */
00106 #ifdef PAM_AUTH                 /*PAM_AUTH */
00107 #include <security/pam_appl.h>
00108 #include <grp.h>
00109 #endif                          /*PAM_AUTH */
00110 
00111 #include <iostream>
00112 #include <algorithm>
00113 #include <map>
00114 
00115 #include "NServer.h"
00116 #include "Newsgroup.h"
00117 
00118 #include "util.h"
00119 #include "setugid.h"
00120 #include "Config.h"
00121 #include "Logger.h"
00122 
00123 using namespace std;
00124 
00125 
00126 /* CONF_MultiClient
00127  * Allow several simultaneous clients in standalone mode.
00128  * This switch is turned off for debug purposes mainly.
00129  */
00130 #define CONF_MultiClient
00131 
00132 //FIXME: since porting to socket++, this had to connent out
00133 /* If you are very performance concerned, you might try to comment this
00134 #define NO_STREAM_BUFFERING
00135  */
00136 
00137 #ifndef SOCKLEN_TYPE
00138 #define SOCKLEN_TYPE size_t
00139 #endif
00140 
00141 int clientTimeoutReached=0;
00142 Logger slog;
00143 const char *cmnd;
00144 class ClientData;
00145 // nntp helper functions
00146 int ns_authinfo(ClientData * clt, int argc, char *argv[]);
00147 int ns_article(ClientData * clt, int argc, char *argv[]);
00148 int ns_stat(ClientData * clt, int argc, char *argv[]);
00149 int ns_date(ClientData * clt, int argc, char *argv[]);
00150 int ns_group(ClientData * clt, int argc, char *argv[]);
00151 int ns_help(ClientData * clt, int argc, char *argv[]);
00152 int ns_lastnext(ClientData * clt, int argc, char *argv[]);
00153 int ns_list(ClientData * clt, int argc, char *argv[]);
00154 int ns_listgroup(ClientData * clt, int argc, char *argv[]);
00155 int ns_mode(ClientData * clt, int argc, char *argv[]);
00156 int ns_newgroups(ClientData * clt, int argc, char *argv[]);
00157 int ns_post(ClientData * clt, int argc, char *argv[]);
00158 int ns_quit(ClientData * clt, int argc, char *argv[]);
00159 int ns_xmotd(ClientData * clt, int argc, char *argv[]);
00160 int ns_xover(ClientData * clt, int argc, char *argv[]);
00161 int ns_xdebug(ClientData * clt, int argc, char *argv[]);
00162 
00163 typedef struct nnrp_command_t {
00164         const char *name;
00165         const char *desc;
00166         int (*func) (ClientData *, int, char *[]);
00167 } nnrp_command_t;
00168 
00169 static nnrp_command_t all_nnrp_commands[] = {
00170         {"authinfo", "[user username|pass password]", ns_authinfo}
00171         ,
00172         {"article", "[number|<id>]", ns_article}
00173         ,
00174         {"head", "[number|<id>]", ns_article}
00175         ,
00176         {"body", "[number|<id>]", ns_article}
00177         ,
00178         {"stat", "[number|<id>]", ns_stat}
00179         ,
00180         {"date", "", ns_date}
00181         ,
00182         {"group", "newsgroup", ns_group}
00183         ,
00184         {"help", "", ns_help}
00185         ,
00186         {"last", "", ns_lastnext}
00187         ,
00188         {"list",
00189          "[active [wildmat]|active.times [time]|extensions|newsgroups|overview.fmt]",
00190          ns_list}
00191         ,
00192         {"listgroup", "[newsgroup]", ns_listgroup}
00193         ,
00194         {"mode", "reader", ns_mode}
00195         ,
00196         {"newgroups", "date time [GMT] [wildmat]", ns_newgroups}
00197         ,
00198         {"next", "", ns_lastnext}
00199         ,
00200         {"over", "[range]", ns_xover}
00201         ,
00202         {"post", "", ns_post}
00203         ,
00204         {"quit", "", ns_quit}
00205         ,
00206         {"xdebug", "xdebug help", ns_xdebug}
00207         ,
00208         {"xhdr", "header [range]", ns_xover}
00209         ,
00210         {"xover", "[range]", ns_xover}
00211         ,
00212 
00213         {NULL, NULL, NULL}
00214 };
00215 
00216 
00217 /*
00218  * \class NNRPCommandMap
00219  * \author Thomas Gschwind, Herbert Straub
00220  * \brief manage the commands
00221  */
00222 class NNRPCommandMap:public map < string, nnrp_command_t * > {
00223       public:
00224          /*
00225           * Constructor enable all commands.
00226           */
00227          NNRPCommandMap();
00228 
00229          /* 
00230           * Enable all commands.
00231           */
00232          void enableAll (void);
00233 
00234          /*
00235           * Disable reader commands
00236           * \author Herbert Straub
00237           */
00238          void disableRead (void);
00239 
00240          /*
00241           * Disable Post command.
00242           * \author Herbert Straub
00243           */
00244          void disablePost (void);
00245 
00246          /* 
00247           * Disable Debug command.
00248           * \author Herbert Straub
00249           */
00250          void disableDebug (void);
00251 };
00252 void set_client_command_table (ClientData &clt);
00253 
00254 void NNRPCommandMap::enableAll (void)
00255 {
00256         nnrp_command_t *p = all_nnrp_commands;
00257         while (p->name) {
00258                 (*this)[p->name] = p;
00259                 p++;
00260         }
00261 }
00262 
00263 NNRPCommandMap::NNRPCommandMap ()
00264 {
00265         enableAll();
00266 }
00267 
00268 void NNRPCommandMap::disableRead (void)
00269 {
00270         erase("article");
00271         erase("head");
00272         erase("body");
00273         erase("stat");
00274         erase("group");
00275         erase("last");
00276         erase("listgroup");
00277         erase("next");
00278         erase("over");
00279         erase("xhdr");
00280         erase("xover");
00281         erase("list");
00282         erase("mode");
00283         erase("newgroups");
00284 }
00285 
00286 void NNRPCommandMap::disablePost (void)
00287 {
00288         erase("post");
00289 }
00290 
00291 void NNRPCommandMap::disableDebug (void)
00292 {
00293         erase("xdebug");
00294 }
00295 
00296 static NNRPCommandMap nnrp_commands;
00297 
00298 char config_file[MAXPATHLEN];
00299 Config Cfg;
00300 
00301 int Xsignal;
00302 int nntp_connections;
00303 
00304 enum {
00305         AUTH_REQUIRED,
00306         AUTH_OK,
00307         AUTH_FAILED,
00308         AUTH_NOT_REQUIRED
00309 };
00310 
00317 class ClientData {
00318       public:
00319         struct sockaddr_in sock;
00320         SOCKLEN_TYPE socklen;
00321         char client_name[MAXHOSTNAMELEN];
00322         string client_logname;
00323         istream *ci;
00324         ostream *co;
00325 
00326         CServer *srvr;
00327         char groupname[MAXGROUPNAMELEN];
00328         Newsgroup *grp;
00329         int nbr;
00330 
00331         AccessEntry *access_entry;
00332         char auth_user[512];
00333         char auth_pass[512];
00334         int auth_state;
00335         int auth_failures;
00336         int auth_max_failures;
00337         int auth_failure_sleep_time;
00338 
00339 #ifdef PAM_AUTH
00340         pam_handle_t *pamh;
00341         struct pam_conv conv;
00342 #endif
00343 
00344         int stat_groups;
00345         int stat_artingrp;
00346         int stat_articles;
00347         
00348         NNRPCommandMap client_command_map;
00349 
00350          ClientData()
00351         :client_logname(""),
00352             grp(NULL), nbr(-1),
00353             access_entry(NULL), auth_state(AUTH_OK), auth_failures(0),
00354             auth_max_failures(3), auth_failure_sleep_time(15),
00355             stat_groups(0), stat_artingrp(0), stat_articles(0) {
00356                 auth_user[0] = auth_pass[0] = '\0';
00357                 groupname[0] = '\0';
00358 }};
00359 
00360 #ifdef PAM_AUTH
00361 /* pam_conv_func by Tuomo Pyhala <tuomo.pyhala@iki.fi> */
00362 /* Dirty kludge */
00363 int pam_conv_func(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr)
00364 {
00365   int i;
00366   struct pam_response *resp_array = (struct pam_response*) calloc(num_msg, sizeof(struct pam_response));
00367   class ClientData *clnt = (ClientData *) appdata_ptr;
00368   
00369   slog.p(Logger::Debug) << "in pam_conf_func\n";
00370   if(resp_array == NULL) { 
00371     return PAM_CONV_ERR; /* Out of memory */
00372   }
00373   for(i=0; i<num_msg; i++) {
00374     resp_array[i].resp_retcode = 0;
00375     resp_array[i].resp = NULL;
00376     switch(msg[i]->msg_style) {
00377     case PAM_PROMPT_ECHO_OFF: /* Assume this is password */
00378       resp_array[i].resp = strdup(clnt->auth_pass);
00379       break;
00380     case PAM_PROMPT_ECHO_ON: /* Assume this is username */
00381       resp_array[i].resp = strdup(clnt->auth_user);
00382       break;
00383     case PAM_ERROR_MSG:
00384       break;
00385     case PAM_TEXT_INFO:
00386       break;
00387     default:
00388       /* Error of some sort */
00389       free(resp_array);
00390       return PAM_CONV_ERR;
00391     }
00392   }
00393   *resp=resp_array;
00394   return PAM_SUCCESS;
00395 }
00396 #endif /* PAM_AUTH */
00397 
00398 char **seekfile(const char *filename, const char *username)
00399 {
00400         FILE *F;
00401         static char buf[1024];
00402         char *p;
00403         int i;
00404 
00405         static char *fields[8];
00406         int fields_n = sizeof(fields) / sizeof(fields[0]);
00407 
00408         if ((F = fopen(filename, "r")) == NULL) {
00409                 slog.
00410                     p(Logger::
00411                       Error) << "cannot fopen " << filename << "\n";
00412                 return NULL;
00413         }
00414 
00415         while (fgets(buf, sizeof(buf), F) != NULL) {
00416                 p = buf + strlen(buf) - 1;
00417                 while (p > buf && isspace(*p))
00418                         *p-- = '\0';
00419                 p = buf;
00420                 while (isspace(*p))
00421                         p++;
00422                 if (*p == '#' || *p == '\0')
00423                         continue;
00424 
00425                 fields[0] = p;
00426                 for (i = 1; *p && i < fields_n - 1; p++) {
00427                         if (*p == ':') {
00428                                 *p = '\0';
00429                                 fields[i++] = p + 1;
00430                         }
00431                 }
00432                 fields[i] = NULL;
00433 
00434                 if (strcmp(username, fields[0]) == 0) {
00435                         (void) fclose(F);
00436                         return fields;
00437                 }
00438         }
00439         (void) fclose(F);
00440         return NULL;
00441 }
00442 
00443 int auto_cryptcheck(const char *key, const char *pass)
00444 {
00445 #ifdef MD5_AUTO
00446         if (strlen(pass) == 24 && pass[23] == '=' && pass[22] == '=') {
00447                 // looks like an MD5 hash
00448 #endif
00449 #ifdef MD5_CRYPT
00450                 return strcmp(pass, (const char *) md5crypt(key));
00451 #endif
00452 #ifdef MD5_AUTO
00453         } else {
00454 #endif
00455                 return strcmp(pass, (const char *) crypt(key, pass));
00456 #ifdef MD5_AUTO
00457         }
00458 #endif                          /*MD5_AUTO */
00459         return 0;
00460 }
00461 
00462 int check_auth_unix (ClientData *clt)
00463 {
00464         AccessEntry *access_entry = clt->access_entry;
00465         string type("unix:");
00466 
00467         if (access_entry->authentication.typeEqual (type))
00468                 return -1;
00469 
00470         const char *pass;
00471         struct passwd *pw = NULL;
00472         pw = getpwnam(clt->auth_user);
00473         if (!pw || !(pw->pw_name) || !(pw->pw_passwd)
00474             || !(pw->pw_passwd[0]))
00475                 return -1;
00476         pass = pw->pw_passwd;
00477 #ifdef SHADOW_PASSWORD
00478         if ((pass[0] == 'x') && (pass[1] == '\0')) {
00479                 struct spwd *spw = NULL;
00480                 spw = getspnam(pw->pw_name);
00481                 if (!(spw) || !(spw->sp_namp) || !(spw->sp_pwdp))
00482                         return -1;
00483                 pass = spw->sp_pwdp;
00484         }
00485 #endif
00486         if (auto_cryptcheck(clt->auth_pass, pass))
00487                 return -1;
00488 
00489         access_entry->list |= NewsgroupFilter(
00490                         (access_entry->authentication.getField(1))
00491                         .c_str());
00492         access_entry->read |= NewsgroupFilter(
00493                         (access_entry->authentication.getField(2))
00494                         .c_str());
00495         access_entry->postTo |= NewsgroupFilter(
00496                         (access_entry->authentication.getField(3)).
00497                         c_str());
00498         access_entry->modifyAccessFlags (access_entry->authentication.
00499                         getField(4));
00500         slog.p(Logger::Debug) << "check_auth_unix::NewsgroupFilter set to: "
00501                 << access_entry->list.getRulelist() << ":"
00502                 << access_entry->read.getRulelist() << ":"
00503                 << access_entry->postTo.getRulelist() << "\n";
00504 
00505         return 0;
00506 }
00507 
00508 int check_auth_file (ClientData *clt)
00509 {
00510         AccessEntry *access_entry = clt->access_entry;
00511         string type("file:");
00512 
00513         if (access_entry->authentication.typeEqual (type))
00514                 return -1;
00515 
00516         string authfile = access_entry->authentication.getField(1);
00517         char **authv = seekfile(authfile.c_str(), clt->auth_user);
00518         int authc = 0;
00519         int passf=-1;
00520 
00521         if (!authv)
00522                 return -1;
00523 
00524         while (authv[authc])
00525                 authc++;
00526         if (authc < 3)
00527                 return -1;
00528 
00529 
00530 #ifdef PLAIN_TEXT_PASSWORDS
00531         passf=strcmp(clt->auth_pass, authv[1]);
00532 #endif
00533         if (passf)
00534                 passf=auto_cryptcheck(clt->auth_pass, authv[1]);
00535         if (passf)
00536                 return -1;
00537 
00538         // Now we build a authentication string like the 
00539         // unix:::: authentication string and set authentication
00540         // Herbert Straub
00541         access_entry->authentication.set (type+authfile);
00542         for (authc=2; authv[authc]; authc++) {
00543                 access_entry->authentication.appendField(authv[authc]);
00544         }
00545         access_entry->list |= NewsgroupFilter(
00546                         (access_entry->authentication.getField(2))
00547                         .c_str());
00548         access_entry->read |= NewsgroupFilter(
00549                         (access_entry->authentication.getField(3))
00550                         .c_str());
00551         access_entry->postTo |= NewsgroupFilter(
00552                         (access_entry->authentication.getField(4)).
00553                         c_str());
00554         access_entry->modifyAccessFlags (access_entry->authentication.
00555                         getField(5));
00556         slog.p(Logger::Debug) << "check_auth_file::NewsgroupFilter set to: "
00557                 << access_entry->list.getRulelist() << ":"
00558                 << access_entry->read.getRulelist() << ":"
00559                 << access_entry->postTo.getRulelist() << "\n";
00560         
00561         return 0;
00562 }
00563 
00564 #ifdef PAM_AUTH
00565 int check_pam_user_pass (ClientData *clt)
00566 {
00567         int retval;
00568 
00569         clt->conv.appdata_ptr = clt;
00570         clt->conv.conv = pam_conv_func;
00571         clt->pamh = NULL;
00572         retval =
00573             pam_start(clt->access_entry->PAMServicename.c_str(), clt->auth_user,
00574                       &(clt->conv), &(clt->pamh));
00575         slog.p(Logger::Debug) << "check_pam_user_pass: pam_start returns " 
00576                 << retval << "\n";
00577         if (retval == PAM_SUCCESS) {
00578                 retval = pam_authenticate(clt->pamh, 0);        /* is user really user? */
00579                 slog.p(Logger::Debug) << "check_pam_user_pass: pam_authenticate returns " 
00580                         << retval << "\n";
00581         }
00582         if (retval == PAM_SUCCESS) {
00583                 retval = pam_acct_mgmt(clt->pamh, 0);   /* permitted access? */
00584                 slog.p(Logger::Debug) << "check_pam_user_pass: pam_acct_mgmt returns " 
00585                         << retval << "\n";
00586         }
00587         if (retval != PAM_SUCCESS) {
00588                 pam_end(clt->pamh, retval);
00589                 return -1;
00590         }
00591         pam_end(clt->pamh, retval);
00592 
00593         return 0;
00594 }
00595 int check_auth_pam_file (ClientData *clt)
00596 {
00597 /*
00598  * check_auth_pam_file code by Tuomo Pyhala <tuomo.pyhala@iki.fi>
00599  * Modify by Herbert Straub <h.straub@aon.at>:
00600  *      using class Authentication
00601  *      restructure source code
00602  */
00603         slog.p(Logger::Debug) << "in check_out_pam_file\n";
00604         AccessEntry *access_entry = clt->access_entry;
00605         string type("pam+file:");
00606 
00607         if (access_entry->authentication.typeEqual (type))
00608                 return -1;
00609 
00610         if (check_pam_user_pass (clt))
00611                 return -1;
00612 
00613         Authentication userAuth, defAuth;
00614         string authfile = access_entry->authentication.getField(1);
00615         char **authv = seekfile(authfile.c_str(), clt->auth_user);
00616         if (authv == NULL)
00617                 return -1;
00618 
00619         int authc, j;
00620         struct group *dummy_group;
00621         char group_entry[1024];
00622 
00623         userAuth.set (type+authfile);
00624         for (authc=1; authv[authc]; authc++) {
00625                 userAuth.appendField (authv[authc]);
00626         }
00627         
00628         /* apply rights of user '*' to everyone :) */
00629         authv = seekfile(authfile.c_str(), "*");
00630         if (authv != NULL) {
00631                 defAuth.set (type+authfile);
00632                 for (authc=1; authv[authc]; authc++)
00633                         defAuth.appendField (authv[authc]);
00634         }
00635 
00636         /* Gather rights given to user supplementary groups */
00637         vector<Authentication> groupAuths;
00638         setgrent();
00639         while ((dummy_group = getgrent()) != NULL) {
00640                 for (j = 0; dummy_group->gr_mem[j]; j++) {
00641                         if (!strcmp (dummy_group->gr_mem[j], clt->auth_user)) {
00642                                 group_entry[0] = '@';
00643                                 strcpy(group_entry + 1,
00644                                        dummy_group->gr_name);
00645                                 authv = seekfile(authfile.c_str(),
00646                                              group_entry);
00647                                 if (authv != NULL) {
00648                                         Authentication a;
00649                                         a.set(type+authfile);
00650                                         for (authc = 1; authv[authc]; authc++) {
00651                                                 a.appendField(authv[authc]);
00652                                         }
00653                                         groupAuths.push_back(a);
00654                                 }
00655                                 break;
00656                         }
00657                 }
00658         }
00659         // Now assemble the various authentication informations.
00660         access_entry->authentication = userAuth;
00661         access_entry->authentication.modify (2, defAuth);
00662         vector<Authentication>::iterator ap, ae;
00663         ap = groupAuths.begin();
00664         ae = groupAuths.end();
00665         for (; ap!=ae; ap++) {
00666                 access_entry->authentication.modify (2, *ap);
00667         }
00668         // and set the new access_entry values
00669         access_entry->list |= NewsgroupFilter(
00670                         (access_entry->authentication.getField(2))
00671                         .c_str());
00672         access_entry->read |= NewsgroupFilter(
00673                         (access_entry->authentication.getField(3))
00674                         .c_str());
00675         access_entry->postTo |= NewsgroupFilter(
00676                         (access_entry->authentication.getField(4)).
00677                         c_str());
00678         access_entry->modifyAccessFlags (access_entry->authentication.
00679                         getField(5));
00680         slog.p(Logger::Debug) << "check_auth_pam_file::NewsgroupFilter set to: "
00681                 << access_entry->list.getRulelist() << ":"
00682                 << access_entry->read.getRulelist() << ":"
00683                 << access_entry->postTo.getRulelist() << "\n";
00684         
00685         return 0;
00686 }
00687 
00688 int check_auth_pam (ClientData *clt)
00689 {
00690         AccessEntry *access_entry = clt->access_entry;
00691         string type("pam:");
00692 
00693         if (access_entry->authentication.typeEqual(type))
00694                 return -1;
00695 
00696         if (check_pam_user_pass (clt))
00697                 return -1;
00698 
00699         access_entry->list |= NewsgroupFilter(
00700                         (access_entry->authentication.getField(1))
00701                         .c_str());
00702         access_entry->read |= NewsgroupFilter(
00703                         (access_entry->authentication.getField(2))
00704                         .c_str());
00705         access_entry->postTo |= NewsgroupFilter(
00706                         (access_entry->authentication.getField(3)).
00707                         c_str());
00708         access_entry->modifyAccessFlags (access_entry->authentication.
00709                         getField(4));
00710         slog.p(Logger::Debug) << "check_auth_pam::NewsgroupFilter set to: "
00711                 << access_entry->list.getRulelist() << ":"
00712                 << access_entry->read.getRulelist() << ":"
00713                 << access_entry->postTo.getRulelist() << "\n";
00714 
00715         return 0;
00716 }
00717 #endif /* PAM_AUTH */
00718 
00719 /* the following formats may be specified (file/unix)
00720  * list:read:postto:allow
00721  */
00722 int check_authentication(ClientData * clt)
00723 {
00724         AccessEntry *access_entry = clt->access_entry;
00725 
00726         if (access_entry->authentication.getType() == "none") {
00727                 return -1;
00728         }
00729 
00730         if (!strncmp((access_entry->authentication.getType()).c_str()
00731                                 , "unix", 4)) {
00732                 return check_auth_unix (clt);
00733         } else
00734             if (!strncmp((access_entry->authentication.getType()).c_str(),
00735                                     "file", 4))
00736         {
00737                 return check_auth_file (clt);
00738         }
00739 #ifdef PAM_AUTH
00740         else if (!strncmp
00741                  ((access_entry->authentication.getType()).c_str(),
00742                   "pam+file", 8)) {
00743                 return check_auth_pam_file (clt);
00744         }
00745         else if (!strncmp
00746                  ((access_entry->authentication.getType()).c_str(),
00747                   "pam", 3)) {
00748                 return check_auth_pam (clt);
00749         }
00750 #endif                          /* #ifndef PAM_AUTH */
00751         // auth_{deny,none} is handled in nnrpd()
00752         return -1;
00753 }
00754 
00755 // fills sin_addr.s and sin_port in addr
00756 // return -1 if an error occurred
00757 int fillHostStruct(struct sockaddr_in *addr, char *pHost)
00758 {
00759         char fname[] = "getHost";
00760         char *p;
00761         struct servent *cport;
00762         struct hostent *host;
00763         int portFlg = 0;
00764 
00765         addr->sin_family = AF_INET;
00766 
00767         if ((p = strchr(pHost, ':')) != NULL) {
00768                 portFlg = 1;
00769                 *p = '\0';
00770         }
00771 
00772         if (strcmp(pHost, "DEFAULT") == 0) {
00773                 addr->sin_addr.s_addr = INADDR_ANY;
00774         } else if ((addr->sin_addr.s_addr = inet_addr(pHost)) ==
00775                    INADDR_NONE) {
00776                 if ((host =
00777                      gethostbyname(pHost)) == NULL | host->h_addrtype !=
00778                     AF_INET) {
00779                         if (portFlg)
00780                                 *p = ':';
00781                         slog.
00782                             p(Logger::
00783                               Error) << fname << ": gethostbyname (" <<
00784                             pHost << ") error: " << strerror(errno) <<
00785                             "\n";
00786                         return -1;
00787                 } else {
00788                         if (host->h_addr_list[0]) {
00789                                 memcpy(&addr->sin_addr.s_addr,
00790                                        host->h_addr_list[0],
00791                                        host->h_length);
00792                         } else {
00793                                 if (portFlg)
00794                                         *p = ':';
00795                                 slog.
00796                                     p(Logger::
00797                                       Error) << fname <<
00798                                     ":host->h_addr_list[0] of " << pHost <<
00799                                     "not valid\n";
00800                                 return -1;
00801                         }
00802                 }
00803         }
00804         if (portFlg)
00805                 *p = ':';
00806 
00807         if (portFlg && isdigit(*(p + 1))) {
00808                 addr->sin_port = htons(atoi(p + 1));
00809         } else
00810             if ((cport =
00811                  getservbyname((portFlg) ? (p + 1) : ("nntp"),
00812                                "tcp")) == NULL) {
00813 
00814                 slog.p(Logger::Error) << fname << ": getservbyname ("
00815                     << ((portFlg) ? ((const char *) (p + 1))
00816                         : ((const char *) "nntp")) << "/tcp error \n";
00817                 return -1;
00818         } else {
00819                 addr->sin_port = cport->s_port;
00820         }
00821 
00822         return 1;
00823 }
00824 
00825 
00826 GroupInfo *selectgroup(ClientData * clt, const char *group)
00827 {
00828         GroupInfo *gi;
00829         VERB(slog.
00830              p(Logger::
00831                Debug) << "NewsCache selectgroup (" << group << ")\n");
00832 
00833         try {
00834                 gi = clt->srvr->groupinfo(group);
00835         } catch(NSError & nse) {
00836                 return NULL;
00837         }
00838         catch(...) {
00839                 return NULL;
00840         }
00841         // Reset Article Pointer
00842         if (gi->first() <= gi->last())
00843                 clt->nbr = gi->first();
00844         else
00845                 clt->nbr = -1;
00846         if (strcmp(clt->groupname, group) != 0) {
00847                 if (clt->grp)
00848                         clt->srvr->freegroup(clt->grp);
00849                 clt->grp = NULL;
00850                 clt->stat_groups++;
00851                 if (clt->stat_artingrp) {
00852                         slog.p(Logger::Notice) << clt->
00853                             client_logname << " group " << clt->
00854                             groupname << " " << clt->stat_artingrp << "\n";
00855                         clt->stat_artingrp = 0;
00856                 }
00857                 strcpy(clt->groupname, group);
00858         } else {
00859                 //FIXME! NServer has to maintain a list of all the allocated
00860                 //FIXME! newsgroups and should call this function itself!!!
00861                 if (clt->grp)
00862                         clt->grp->setsize(gi->first(), gi->last());
00863         }
00864         return gi;
00865 }
00866 
00867 void nsh_particle(ClientData * clt, char *cmnd, int nbr, Article * a)
00868 {
00869         string aid = a->getfield("Message-ID:");
00870 
00871         switch (cmnd[0]) {
00872         case 'a':
00873                 (*clt->co) << "220 " << nbr << " " << aid
00874                     << " article retrieved - head and body follow\r\n";
00875                 (*clt->co) << *a << ".\r\n";
00876                 slog.p(Logger::Info) << "articlesize " << clt->
00877                     groupname << ":" << nbr << " " << a->length() << "\n";
00878                 break;
00879         case 'h':
00880                 (*clt->co) << "221 " << nbr << " " << aid
00881                     << " article retrieved - head follows\r\n";
00882                 a->write(*clt->co, Article::Head);
00883                 (*clt->co) << ".\r\n";
00884                 break;
00885         case 'b':
00886                 (*clt->co) << "222 " << nbr << " " << aid
00887                     << " article retrieved - body follows\r\n";
00888                 a->write(*clt->co, Article::Body);
00889                 (*clt->co) << ".\r\n";
00890                 slog.p(Logger::Info) << "articlesize " << clt->
00891                     groupname << ":" << nbr << " " << a->length() << "\n";
00892                 break;
00893         }
00894 }
00895 
00896 
00897 #define REJECT_SEQUENCE                 "502"
00898 #define PASS_NEEDED_SEQUENCE            "381"
00899 #define OK_SEQUENCE                     "281"
00900 #define INCORRECT_ORDER_SEQUENCE        "482"
00901 
00902 //Authinfo
00903 int ns_authinfo(ClientData * clt, int argc, char *argv[])
00904 {
00905         // check arguments
00906         if (argc != 3)
00907                 goto ns_authinfo_error;
00908         if (strlen(argv[2]) > sizeof(clt->auth_user) - 1) {
00909                 (*clt->co) << REJECT_SEQUENCE " argument too long\r\n";
00910                 return -1;
00911         }
00912 
00913         if (!strcasecmp(argv[1], "user")) {
00914                 strcpy(clt->auth_user, argv[2]);
00915                 (*clt->co) << PASS_NEEDED_SEQUENCE " password please\r\n";
00916                 return 0;
00917         }
00918 
00919         if (!strcasecmp(argv[1], "pass")) {
00920                 if (clt->auth_user[0] == '\0') {
00921                         (*clt->
00922                          co) << INCORRECT_ORDER_SEQUENCE
00923                       " issue authinfo user first\r\n";
00924                         return -1;
00925                 }
00926                 strcpy(clt->auth_pass, argv[2]);
00927 
00928                 if (check_authentication(clt) < 0) {
00929                         sleep (clt->auth_failure_sleep_time*
00930                                         (++clt->auth_failures));
00931                         (*clt->
00932                          co) << REJECT_SEQUENCE << " Bad authinfo.\r\n";
00933                         return -1;
00934                 }
00935 
00936                 (*clt->co) << OK_SEQUENCE << " OK.\r\n";
00937                 clt->auth_state = AUTH_OK;
00938                 set_client_command_table(*clt);
00939 
00940                 return 0;
00941         }
00942         // error
00943       ns_authinfo_error:
00944         if (argc > 1) {
00945                 switch (tolower(argv[1][0])) {
00946                 case 'u':
00947                         (*clt->
00948                          co) << "501 Syntax: authinfo user username\r\n";
00949                         return -1;
00950                 case 'p':
00951                         (*clt->
00952                          co) << "501 Syntax: authinfo pass password\r\n";
00953                         return -1;
00954                 }
00955         }
00956         (*clt->
00957          co) << "501 Syntax: authinfo [user username|pass password]\r\n";
00958         return -1;
00959 }
00960 
00961 int ns_article(ClientData * clt, int argc, char *argv[])
00962 {
00963         if (argc > 2)
00964                 goto ns_article_error;
00965 
00966         // retrieve article|body|head by number?
00967         if (argc == 1 || isdigit(argv[1][0])) {
00968                 if (clt->groupname[0] == '\0') {
00969                         (*clt->
00970                          co) << "412 no newsgroup has been selected\r\n";
00971                         return -1;
00972                 }
00973                 try {
00974                         if (!clt->grp)
00975                                 clt->grp =
00976                                     clt->srvr->getgroup(clt->groupname);
00977                 }
00978                 catch(NoSuchGroupError & nsge) {
00979                         (*clt->co) << "411 no such newsgroup\r\n";
00980                         return -1;
00981                 }
00982                 catch(Error & e) {
00983                         (*clt->co) << "412 operation failed\r\n";
00984                         return -1;
00985                 }
00986 
00987                 if (argc == 1 && clt->nbr < 0) {
00988                         (*clt->
00989                          co) <<
00990                       "420 no current article has been selected\r\n";
00991                         return -1;
00992                 }
00993 
00994                 int nbr = (argc == 2) ? atoi(argv[1]) : clt->nbr;
00995 
00996                 ASSERT2(clt->grp->testdb());
00997                 Article *a;
00998                 
00999                 if ((a = clt->grp->getarticle(nbr)) == NULL) {
01000                         (*clt->
01001                          co) <<
01002                       "423 no such article number in this group\r\n";
01003                         return -1;
01004                 }
01005                 nsh_particle(clt, argv[0], nbr, a);
01006                 clt->grp->freearticle(a);
01007                 clt->nbr = nbr;
01008                 clt->stat_artingrp++;
01009                 clt->stat_articles++;
01010                 return 0;
01011         }
01012         // retrieve article|body|head by id?
01013         if (argv[1][0] == '<') {
01014                 try {
01015                         Article a;
01016                         clt->srvr->article(argv[1], &a);
01017                         nsh_particle(clt, argv[0], 0, &a);
01018                 }
01019                 catch(NoSuchArticleError nsae) {
01020                         (*clt->co) << "430 no such article id found\r\n";
01021                         return -1;
01022                 }
01023                 catch(ResponseError e) {
01024                         // error
01025                         (*clt->co) << e._got << "\r\n";
01026                         return -1;
01027                 }
01028                 catch(Error e) {
01029                         // error
01030                         (*clt->co) << "520 ???\r\n";
01031                         slog.
01032                             p(Logger::
01033                               Error) <<
01034                             "Internal Error? Please report to h.straub@aon.at\r\n";
01035                         e.print();
01036                         return -1;
01037                 }
01038                 clt->stat_artingrp++;
01039                 clt->stat_articles++;
01040                 return 0;
01041         }
01042         // error
01043       ns_article_error:
01044         switch (argv[0][0]) {
01045         case 'a':
01046                 (*clt->co) << "501 Syntax: article [nbr|<id>]\r\n";
01047                 return -1;
01048         case 'h':
01049                 (*clt->co) << "501 Syntax: head [nbr|<id>]\r\n";
01050                 return -1;
01051         case 'b':
01052                 (*clt->co) << "501 Syntax: body [nbr|<id>]\r\n";
01053                 return -1;
01054         }
01055         return -1;
01056 }
01057 
01058 int ns_stat(ClientData * clt, int argc, char *argv[])
01059 {
01060         /* Offene Punkte:
01061            ** stat auf nummer die nicht vorhanden ist, ergibt:
01062            ** 223 351  status
01063            ** das ist falsch, sollte sein:
01064            ** 423 No Such Article In Group
01065            **
01066          */
01067         if (argc > 2)
01068                 goto ns_stat_error;
01069 
01070         // stat by number?
01071         if (argc == 1 || isdigit(argv[1][0])) {
01072                 // fetch article by number
01073                 if (clt->groupname[0] == '\0') {
01074                         (*clt->
01075                          co) << "412 no newsgroup has been selected\r\n";
01076                         return -1;
01077                 }
01078                 try {
01079                         if (!clt->grp)
01080                                 clt->grp =
01081                                     clt->srvr->getgroup(clt->groupname);
01082                 }
01083                 catch(NoSuchGroupError & nsge) {
01084                         (*clt->co) << "411 no such newsgroup\r\n";
01085                         return -1;
01086                 }
01087                 catch(Error & e) {
01088                         (*clt->co) << "412 operation failed\r\n";
01089                         return -1;
01090                 }
01091 
01092                 if (argc == 1 && clt->nbr < 0) {
01093                         (*clt->
01094                          co) <<
01095                       "420 no current article has been selected\r\n";
01096                         return -1;
01097                 }
01098 
01099                 int nbr = (argc == 2) ? atoi(argv[1]) : clt->nbr;
01100 
01101                 ASSERT2(clt->grp->testdb());
01102                 OverviewFmt *of = clt->srvr->overviewfmt();
01103                 const char *o = clt->grp->getover(nbr);
01104                 (*clt->co) << "223 " << nbr << " " << of->getfield(o,
01105                                                                    "Message-ID:",
01106                                                                    0)
01107                     << " status \r\n";
01108                 return 0;
01109         }
01110         // stat by id?
01111         if (argv[1][0] == '<') {
01112                 //(*clt->co) << "223 0 " << argv[1] << " status\r\n";
01113                 Article a;
01114                 try {
01115                         clt->srvr->article(argv[1], &a);
01116                         //nsh_particle(clt, argv[0], 0, &a);
01117                 }
01118                 catch(NoSuchArticleError nsae) {
01119                         (*clt->co) << "430 no such article id found\r\n";
01120                         return -1;
01121                 }
01122                 catch(ResponseError e) {
01123                         // error
01124                         (*clt->co) << e._got << "\r\n";
01125                         return -1;
01126                 }
01127                 catch(Error e) {
01128                         // error
01129                         (*clt->co) << "520 ???\r\n";
01130                         slog.
01131                             p(Logger::
01132                               Error) <<
01133                             "Internal Error? Please report to h.straub@aon.at\r\n";
01134                         e.print();
01135                         return -1;
01136                 }
01137                 string mid = a.getfield("Message-ID:");
01138                 (*clt->co) << "223 0 " << mid << " status\r\n";
01139                 return 0;
01140         }
01141         // error
01142       ns_stat_error:
01143         (*clt->co) << "501 Syntax: stat [nbr|<id>]\r\n";
01144         return -1;
01145 }
01146 
01152 int ns_date(ClientData * clt, int argc, char *argv[])
01153 {
01154         struct tm *ltm;
01155         time_t conv;
01156         char buf[256];
01157 
01158         if (argc != 1) {
01159                 (*clt->co) << "501 Syntax: group Newsgroup\r\n";
01160                 return -1;
01161         }
01162         time(&conv);
01163         if ((ltm = gmtime(&conv)) == NULL) {
01164                 (*clt->co) << "500 gmtime failed\r\n";
01165                 slog.p(Logger::Error) << "ns_date() gmtime failed\n";
01166                 return -1;
01167         }
01168         sprintf(buf, "111 %04d%02d%02d%02d%02d%02d\r\n",
01169                 1900 + ltm->tm_year, ltm->tm_mon + 1, ltm->tm_mday,
01170                 ltm->tm_hour, ltm->tm_min, ltm->tm_sec);
01171         (*clt->co) << buf;
01172 
01173         return 0;
01174 }
01175 
01181 int ns_group(ClientData * clt, int argc, char *argv[])
01182 {
01183         if (argc != 2) {
01184                 (*clt->co) << "501 Syntax: group Newsgroup\r\n";
01185                 return -1;
01186         }
01187 
01188         if (clt->access_entry->read.matches(argv[1]) <= 0) {
01189                 (*clt->co) << "480 authentication required\r\n";
01190                 return 0;
01191         }
01192 
01193         GroupInfo *gi;
01194         if ((gi = selectgroup(clt, argv[1])) == NULL) {
01195                 (*clt->co) << "411 no such news group\r\n";
01196                 return -1;
01197         }
01198 
01199         (*clt->co) << "211 "
01200             << gi->n() << " "
01201             << gi->first() << " "
01202             << gi->last() << " " << gi->name() << " group selected\r\n";
01203 
01204         slog.p(Logger::Info) << "groupsize [" << gi->first() << ","
01205             << gi->last() << "]\n";
01206 
01207         return 0;
01208 }
01209 
01214 int ns_help(ClientData * clt, int argc, char *argv[])
01215 {
01216         map < string, nnrp_command_t * >::iterator begin =
01217             clt->client_command_map.begin(), end = clt->client_command_map.end();
01218 
01219         VERB(slog.p(Logger::Debug) << "NewsCache::ns_help:\n");
01220         (*clt->co) << "100 Legal commands\r\n";
01221         while (begin != end) {
01222                 if (begin->second->func) {
01223                         (*clt->co) << "  " << begin->
01224                             first << " " << begin->second->desc << "\r\n";
01225                 }
01226                 ++begin;
01227         }
01228         (*clt->co) << "Report problems to " << Cfg.Admin << "\r\n.\r\n";
01229         return 0;
01230 }
01231 
01232 /* last,next */
01233 int ns_lastnext(ClientData * clt, int argc, char *argv[])
01234 {
01235         if (argc != 1) {
01236                 switch (argv[0][0]) {
01237                 case 'l':
01238                         (*clt->co) << "501 Syntax: last\r\n";
01239                         return -1;
01240                 case 'n':
01241                         (*clt->co) << "501 Syntax: next\r\n";
01242                         return -1;
01243                 }
01244         }
01245         if (clt->groupname[0] == '\0') {
01246                 (*clt->co) << "412 No newsgroup currently selected\r\n";
01247                 return -1;
01248         }
01249         if (clt->nbr < 0) {
01250                 (*clt->
01251                  co) << "420 no current article has been selected\r\n";
01252                 return -1;
01253         }
01254         try {
01255                 if (!clt->grp)
01256                         clt->grp = clt->srvr->getgroup(clt->groupname);
01257         }
01258         catch(NoSuchGroupError & nsge) {
01259                 (*clt->co) << "411 no such news group\r\n";
01260                 return -1;
01261         }
01262         catch(Error & e) {
01263                 (*clt->co) << "412 operation failed\r\n";
01264                 return -1;
01265         }
01266 
01267         unsigned int f, i = clt->nbr, l;
01268         const char *o;
01269         string aid;
01270         OverviewFmt *ofmt = clt->srvr->overviewfmt();
01271         clt->grp->getsize(&f, &l);
01272         switch (argv[0][0]) {
01273         case 'l':
01274                 for (;;) {
01275                         i--;
01276                         if (i < f) {
01277                                 (*clt->
01278                                  co) << "422 No previous to retrieve\r\n";
01279                                 return -1;
01280                         }
01281                         o = clt->grp->getover(i);
01282                         if (o[0]) {
01283                                 aid = ofmt->getfield(o, "Message-ID:", 0);
01284                                 break;
01285                         }
01286                 }
01287                 break;
01288         case 'n':
01289                 for (;;) {
01290                         ++i;
01291                         if (i > l) {
01292                                 (*clt->
01293                                  co) << "421 No next to retrieve\r\n";
01294                                 return -1;
01295                         }
01296                         o = clt->grp->getover(i);
01297                         if (o[0]) {
01298                                 aid = ofmt->getfield(o, "Message-ID:", 0);
01299                                 break;
01300                         }
01301                 }
01302         }
01303         clt->nbr = i;
01304 
01305         (*clt->co) << "223 " << clt->nbr << " " << aid
01306             << " article retrieved - request text separately\r\n";
01307         return 0;
01308 }
01309 
01310 class print_list_active {
01311         ostream & os;
01312       public:
01313         print_list_active(ostream & os):os(os) {
01314         } void operator() (const GroupInfo & gd) {
01315                 os << gd << "\r\n";
01316                 if (!os.good())
01317                         throw SystemError("cannot write to clientstream",
01318                                           errno, ERROR_LOCATION);
01319         }
01320 };
01321 
01322 class print_list_active_times {
01323         ostream & os;
01324       public:
01325         print_list_active_times(ostream & os):os(os) {
01326         } void operator() (const GroupInfo & gd) {
01327                 os << gd.name() << " " << gd.ctime() << " news\r\n";
01328                 if (!os.good())
01329                         throw SystemError("cannot write to clientstream",
01330                                           errno, ERROR_LOCATION);
01331         }
01332 };
01333 
01334 template < class Format > class active_filter_group {
01335         Format fmt;
01336         const NewsgroupFilter & filter;
01337       public:
01338         active_filter_group(ostream & os, const NewsgroupFilter & filter)
01339         :fmt(os), filter(filter) {
01340         }
01341         void operator() (const GroupInfo & gd) {
01342                 if (filter.matches(gd.name()) > 0)
01343                         fmt(gd);
01344         }
01345 };
01346 
01347 template < class Format > class active_filter_group_time {
01348         Format fmt;
01349         const NewsgroupFilter & filter;
01350         nvtime_t ctime;
01351       public:
01352         active_filter_group_time(ostream & os,
01353                                  const NewsgroupFilter & filter,
01354                                  nvtime_t ctime)
01355         :fmt(os), filter(filter), ctime(ctime) {
01356         }
01357         void operator() (const GroupInfo & gd) {
01358                 if (gd.ctime() > ctime && filter.matches(gd.name()) > 0)
01359                         fmt(gd);
01360         }
01361 };
01362 
01363 template < class Format > class active_filter_time {
01364         Format fmt;
01365         nvtime_t ctime;
01366       public:
01367         active_filter_time(ostream & os, nvtime_t ctime)
01368       :    fmt(os), ctime(ctime) {
01369         }
01370         void operator() (const GroupInfo & gd) {
01371                 if (gd.ctime() > ctime)
01372                         fmt(gd);
01373         }
01374 };
01375 
01376 /* List [active|newsgroups|overview.fmt]
01377  * List the specified data
01378  */
01379 int ns_list(ClientData * clt, int argc, char *argv[])
01380 {
01381         if (argc <= 3) {
01382                 if (argc == 1 || strcasecmp(argv[1], "active") == 0) {
01383                         ActiveDB *active;
01384                         active = clt->srvr->active();
01385                         (*clt->
01386                          co) <<
01387                       "215 List of newsgroups (Group High Low Flags) follows\r\n";
01388                         try {
01389                                 NewsgroupFilter filter(clt->access_entry->
01390                                                        list);
01391                                 if (argc == 3)
01392                                         filter.setWildmat(argv[2]);
01393                                 for_each(active->begin(),
01394                                          active->end(),
01395                                          active_filter_group <
01396                                          print_list_active > (*clt->co,
01397                                                               filter));
01398                         }
01399                         catch(SystemError & se) {
01400                                 return -2;
01401                         }
01402                         (*clt->co) << ".\r\n";
01403                         return 0;
01404                 } else if (strcasecmp(argv[1], "active.times") == 0) {
01405                         ActiveDB *active;
01406                         active = clt->srvr->active();
01407                         (*clt->co) << "215 information follows\r\n";
01408                         try {
01409                                 NewsgroupFilter filter(clt->access_entry->
01410                                                        list);
01411                                 if (argc == 3)
01412                                         filter &= NewsgroupFilter(argv[2]);
01413 
01414                                 for_each(active->begin(),
01415                                          active->end(),
01416                                          active_filter_group <
01417                                          print_list_active_times >
01418                                          (*clt->co, filter));
01419                         }
01420                         catch(SystemError & se) {
01421                                 return -2;
01422                         }
01423                         (*clt->co) << ".\r\n";
01424                         return 0;
01425                 } else if (argc == 2) {
01426                         if (strcasecmp(argv[1], "extensions") == 0) {
01427                                 (*clt->
01428                                  co) << "202 extensions supported\r\n";
01429                                 (*clt->co) << " over\r\n";
01430                                 (*clt->co) << " authinfo user\r\n";
01431                                 (*clt->co) << ".\r\n";
01432                                 return 0;
01433                         } else if (strcasecmp(argv[1], "newsgroups") == 0) {
01434                                 char buf[MAXPATHLEN];
01435                                 string strg;
01436                                 sprintf(buf, "%s/.newsgroups",
01437                                         Cfg.SpoolDirectory);
01438                                 ifstream ifs(buf);
01439                                 (*clt->
01440                                  co) << "215 Description follows\r\n";
01441                                 for (;;) {
01442                                         nlreadline(ifs, strg);
01443                                         if (!ifs.good())
01444                                                 break;
01445                                         (*clt->co) << strg;
01446                                 }
01447                                 (*clt->co) << ".\r\n";
01448                                 return 0;
01449                         } else if (strcasecmp(argv[1], "overview.fmt") ==
01450                                    0) {
01451                                 (*clt->
01452                                  co) <<
01453                               "215 Order of fields in overview database\r\n"
01454                               << *(clt->srvr->overviewfmt()) << ".\r\n";
01455                                 return 0;
01456                         }
01457                 }
01458         }
01459 
01460         (*clt->
01461          co) <<
01462       "501 Syntax: list [active [wildmat]|active.times [time]|extensions|newsgroups|overview.fmt]\r\n";
01463         return -1;
01464 }
01465 
01466 /* listgroup
01467  * Listgroup only works for groups, where the overview
01468  * database has already been cached!
01469  */
01470 int ns_listgroup(ClientData * clt, int argc, char *argv[])
01471 {
01472         if (argc > 2) {
01473                 (*clt->co) << "501 Syntax: listgroup [Newsgroup]\r\n";
01474                 return -1;
01475         }
01476         if (argc == 1 && clt->groupname[0] == '\0') {
01477                 (*clt->co) << "412 Not currently in a newsgroup\r\n";
01478                 return -1;
01479         }
01480         if (argc == 2) {
01481                 if (selectgroup(clt, argv[1]) == NULL) {
01482                         (*clt->co) << "411 no such news group\r\n";
01483                         return -1;
01484                 }
01485                 if (clt->access_entry->read.matches(argv[1]) <= 0) {
01486                         (*clt->co) << "480 authentication required\r\n";
01487                         return 0;
01488                 }
01489         }
01490 
01491         try {
01492                 if (!clt->grp)
01493                         clt->grp = clt->srvr->getgroup(clt->groupname);
01494         }
01495         catch(NoSuchGroupError & nsge) {
01496                 (*clt->co) << "411 no such news group\r\n";
01497                 return -1;
01498         }
01499         catch(Error & e) {
01500                 (*clt->co) << "412 operation failed\r\n";
01501                 e.print();
01502                 return -1;
01503         }
01504 
01505         (*clt->co) << "211 list of article numbers follow\r\n";
01506         clt->grp->printlistgroup(*clt->co);
01507         (*clt->co) << ".\r\n";
01508         return 0;
01509 }
01510 
01511 int ns_mode(ClientData * clt, int argc, char *argv[])
01512 {
01513         if (argc != 2 ||
01514             (strcasecmp(argv[1], "reader") != 0 &&
01515              strcasecmp(argv[1], "query") != 0)) {
01516                 (*clt->co) << "501 Syntax: mode (reader|query)\r\n";
01517                 return -1;
01518         }
01519         (*clt->
01520          co) << "200 " PACKAGE " " VERSION ", accepting NNRP commands\r\n";
01521         return 0;
01522 }
01523 
01524 /* 
01525  * NEWGROUPS date time [GMT|UTC] [<distributions>]
01526  */
01527 #define DD2INT(x) ((x)[0]-'0')*10+((x)[1]-'0')
01528 int ns_newgroups(ClientData * clt, int argc, char *argv[])
01529 {
01530         if (argc < 3 || argc > 5) {
01531               ns_newgroups_error:
01532                 (*clt->
01533                  co) <<
01534               "501 Syntax:  NEWGROUPS date time [GMT] [<wildmat>]\r\n";
01535                 return -1;
01536         }
01537 
01538         int i, y4, is_gmt = 0;
01539         struct tm ltm;
01540         time_t lt;
01541         char *p;
01542 
01543         // check first two arguments, y4 indicates whether year has 4 digits
01544         for (i = 0; isdigit(argv[2][i]); i++);
01545         if (i != 6 || argv[2][i])
01546                 goto ns_newgroups_error;
01547         for (y4 = 0; isdigit(argv[1][y4]); y4++);
01548         if ((y4 != 8 && y4 != 6) || argv[1][y4])
01549                 goto ns_newgroups_error;
01550         y4 -= 4;
01551 
01552         // check whether time/date is specified in GMT
01553         if (argc >= 4) {
01554                 if (strcasecmp(argv[3], "gmt") != 0 &&
01555                     strcasecmp(argv[3], "utc") != 0) {
01556                         goto ns_newgroups_error;
01557                 }
01558                 is_gmt = 1;
01559         }
01560         // fill in ltm with supplied values
01561         p = argv[1];
01562         if (y4 > 2) {
01563                 i = DD2INT(p);
01564                 p += 2;
01565                 ltm.tm_year = i * 100 + DD2INT(p) - 1900;
01566                 p += 2;
01567         } else {
01568                 struct tm *now;
01569                 int yr;
01570 
01571                 // draft-ietf-nntpext-base-04.txt:
01572                 // If the first two digits of the year are not specified,
01573                 // the year is to be taken from the current century if YY
01574                 // is smaller than or equal to the current year,
01575                 // otherwise the year is from the previous century.
01576                 time(&lt);
01577                 now = is_gmt ? gmtime(&lt) : localtime(&lt);
01578                 yr = now->tm_year % 100;
01579 
01580                 ltm.tm_year = DD2INT(p);
01581                 p += 2;
01582                 if (ltm.tm_year > yr)
01583                         ltm.tm_year += now->tm_year - yr - 100;
01584                 else
01585                         ltm.tm_year += now->tm_year - yr;
01586         }
01587         ltm.tm_mon = DD2INT(p) - 1;
01588         p += 2;                 // tm_mon has a range of 0(Jan)--11(Dec)!!!
01589         ltm.tm_mday = DD2INT(p);
01590 
01591         p = argv[2];
01592         ltm.tm_hour = DD2INT(p);
01593         p += 2;
01594         ltm.tm_min = DD2INT(p);
01595         p += 2;
01596         ltm.tm_sec = DD2INT(p);
01597 
01598 #ifdef HAVE_TZNAME
01599         ltm.tm_isdst = daylight;
01600 
01601         lt = mktime(&ltm);
01602         if (lt == -1)
01603                 goto ns_newgroups_error;
01604 
01605         // correct timezone if time/date is given in GMT
01606         if (is_gmt) {
01607                 lt -= timezone;
01608         }
01609 #else
01610         struct tm *plt;
01611 
01612         time(&lt);
01613         plt = localtime(&lt);
01614 
01615         ltm.tm_isdst = plt->tm_isdst;
01616         ltm.tm_gmtoff = plt->tm_gmtoff;
01617 
01618         lt = mktime(&ltm);
01619         if (lt == -1)
01620                 goto ns_newgroups_error;
01621 #endif
01622 
01623         ActiveDB *active;
01624         try {
01625                 active = clt->srvr->active();
01626         }
01627         catch(Error & e) {
01628                 (*clt->co) << "410 operation failed\r\n";
01629                 return -1;
01630         }
01631         (*clt->co) << "231 list of new groups follows " << lt << "\r\n";
01632         try {
01633                 NewsgroupFilter filter();
01634 
01635                 for_each(active->begin(),
01636                          active->end(),
01637                          active_filter_group_time <
01638                          print_list_active_times > (*clt->co,
01639                                                     clt->access_entry->
01640                                                     list, lt));
01641         }
01642         catch(SystemError & se) {
01643                 return -2;
01644         }
01645         (*clt->co) << ".\r\n";
01646         return 0;
01647 }
01648 
01649 #undef DD2INT
01650 
01651 int ns_post(ClientData * clt, int argc, char *argv[])
01652 {
01653         VERB(slog.p(Logger::Debug) << "NewsCache::ns_post\n");
01654         if (argc != 1) {
01655                 (*clt->co) << "501 Syntax: post\r\n";
01656                 return -1;
01657         }
01658 
01659         Article art;
01660 
01661         try {
01662                 // request article to be posted
01663                 (*clt->co) << "340 send article to be posted.\r\n";
01664                 clt->co->flush();
01665                 art.read(*clt->ci);
01666 
01667                 // check whether client is authorized to post this article.
01668                 string newsgroups = art.getfield("newsgroups:");
01669                 unsigned int i1 = 0, i2 = newsgroups.find(",");
01670                 for (;;) {
01671                         string group = newsgroups.substr(i1, i2);
01672                         if (clt->access_entry->postTo.
01673                             matches(group.c_str()) <= 0) {
01674                                 (*clt->
01675                                  co) << "480 authentication required\r\n";
01676                                 return 0;
01677                         }
01678                         if (i2 == string::npos)
01679                                 break;
01680                         i1 = i2 + 1;
01681                         i2 = newsgroups.find(",", i1);
01682                 }
01683         }
01684         catch(InvalidArticleError & iae) {
01685                 (*clt->co) << "441 invalid article\r\n";
01686                 return -1;
01687         }
01688         catch(NoSuchFieldError & nsfe) {
01689                 (*clt->co) << "441 invalid article\r\n";
01690                 return -1;
01691         }
01692 
01693         try {
01694                 // client is authorized to post the article
01695                 clt->srvr->post(&art);
01696                 (*clt->co) << "240 Article posted\r\n";
01697                 return 0;
01698         }
01699         catch(InvalidArticleError & iae) {
01700                 (*clt->co) << "441 invalid article\r\n";
01701                 return -1;
01702         }
01703         catch(NotAllowedError & nae) {
01704                 (*clt->co) << "440 posting not allowed\r\n";
01705                 return -1;
01706         }
01707         catch(Error & e) {
01708                 (*clt->co) << "449 operation failed\r\n";
01709                 return -1;
01710         }
01711 }
01712 
01713 int ns_quit(ClientData * clt, int argc, char *argv[])
01714 {
01715         (*clt->co) << "205 Good bye\r\n";
01716         return 1;
01717 }
01718 
01719 int ns_xmotd(ClientData * clt, int argc, char *argv[])
01720 {
01721         (*clt->co) << "500 not yet supported\r\n";
01722         return -1;
01723 }
01724 
01725 
01726 /*
01727  * 221 Header follows
01728  * 412 No news group current selected
01729  * 420 No current article selected
01730  * 430 no such article
01731  * 502 no permission
01732  */
01733 int ns_xover(ClientData * clt, int argc, char *argv[])
01734 {
01735         int i = 1, fst, lst;
01736         char *p;
01737 
01738         if (clt->groupname[0] == '\0') {
01739                 (*clt->co) << "412 No newsgroup currently selected\r\n";
01740                 return -1;
01741         }
01742         if (strcmp(argv[0], "xhdr") == 0) {
01743                 i = 2;
01744         }
01745         switch (argc - i) {
01746         case 0:
01747                 fst = lst = clt->nbr;
01748                 break;
01749         case 1:
01750                 p = argv[i];
01751                 if (isdigit(*p))
01752                         fst = lst = strtol(argv[i], &p, 10);
01753                 if ((*p) == '\0')
01754                         break;
01755                 if ((*p) == '-') {
01756                         lst = UINT_MAX;
01757                         p++;
01758                         if (isdigit(*p))
01759                                 lst = strtol(p, &p, 10);
01760                         if ((*p) == '\0')
01761                                 break;
01762                 }
01763         default:
01764                 (*clt->co) << "501 Syntax: xover [range]\r\n";
01765                 return -1;
01766         }
01767         try {
01768                 if (!clt->grp)
01769                         clt->grp = clt->srvr->getgroup(clt->groupname);
01770         }
01771         catch(NoSuchGroupError & nsge) {
01772                 (*clt->co) << "411 no such news group\r\n";
01773                 return -1;
01774         }
01775         catch(Error & e) {
01776                 e.print();
01777                 (*clt->co) << "412 operation failed\r\n";
01778                 return -1;
01779         }
01780 
01781         ASSERT2(clt->grp->testdb());
01782         switch (i) {
01783         case 1:
01784                 (*clt->co) << "224 Overview information follows\r\n";
01785                 clt->grp->printoverdb(*clt->co, fst, lst);
01786                 break;
01787         case 2:
01788                 (*clt->
01789                  co) << "221 " << p << "[" << fst << "-" << lst << "]\r\n";
01790                 clt->grp->printheaderdb(*clt->co, argv[1], fst, lst);
01791                 break;
01792         }
01793         (*clt->co) << ".\r\n";
01794         return 0;
01795 }
01796 
01797 int ns_xdebug(ClientData * clt, int argc, char *argv[])
01798 {
01799         if (argc == 3) {
01800                 if (strcmp(argv[1], "dump") == 0) {
01801                         if (strcmp(argv[2], "authorization") == 0) {
01802                                 (*clt->
01803                                  co) <<
01804                               "xxx authorization data follows\r\n";
01805                                 (*clt->co) << *clt->access_entry << "\r\n";
01806                                 (*clt->co) << ".\r\n";
01807                         }
01808                         if (strcmp(argv[2], "configuration") == 0) {
01809                                 (*clt->
01810                                  co) <<
01811                               "xxx configuration data follows\r\n";
01812                                 Cfg.printParameters (clt->co);
01813                                 (*clt->co) << ".\r\n";
01814                         }
01815                 }
01816                 return 0;
01817         }
01818 
01819         if (argc == 2) {
01820                 if (strcmp(argv[1], "help") == 0) {
01821                         (*clt->co) << "Valid commands:\r\n";
01822                         (*clt->co) << "xdebug dump authorization:\r\n";
01823                         (*clt->co) << "xdebug dump configuration:\r\n";
01824                         (*clt->co) << ".\r\n";
01825                         return 0;
01826                 }
01827         }
01828 
01829         (*clt->co) << "501 Syntax: xdebug help\r\n";
01830         return -1;
01831 }
01832 
01833 
01834 #ifdef HAVE_LIBWRAP
01835 #ifndef WITH_SYSLOG
01836 /* This is from my syslog.h */
01837 #define LOG_EMERG       0       /* system is unusable */
01838 #define LOG_ALERT       1       /* action must be taken immediately */
01839 #define LOG_CRIT        2       /* critical conditions */
01840 #define LOG_ERR         3       /* error conditions */
01841 #define LOG_WARNING     4       /* warning conditions */
01842 #define LOG_NOTICE      5       /* normal but significant condition */
01843 #define LOG_INFO        6       /* informational */
01844 #define LOG_DEBUG       7       /* debug-level messages */
01845 #endif
01846 
01847 /* needed for TCP wrappers */
01848 int allow_severity = LOG_INFO;
01849 int deny_severity = LOG_NOTICE;
01850 #endif
01851 void set_client_command_table (ClientData &clt)
01852 {
01853         clt.client_command_map.enableAll();
01854         if (!(clt.access_entry->access_flags & AccessEntry::af_read)) {
01855                 clt.client_command_map.disableRead();
01856         }
01857 
01858         if (!(clt.access_entry->access_flags & AccessEntry::af_post)) {
01859                 clt.client_command_map.disablePost ();
01860         }
01861 
01862         if (!(clt.access_entry->access_flags & AccessEntry::af_debug)) {
01863                 clt.client_command_map.disableDebug ();
01864         }
01865 }
01866 
01867 /* nnrpd(fd)
01868  */
01869 void nnrpd(int fd)
01870 {
01871         struct hostent *he;
01872         ClientData clt;
01873         char req[1024], oreq[1024], *rp;
01874         char *argv[256];
01875         int argc;
01876         const char *cmd;
01877         map < string, nnrp_command_t * >::iterator cmdp, end =
01878             nnrp_commands.end();
01879         int errc = 0;
01880         sockbuf *psock_buff = NULL;
01881         iosockstream *psock_stream = NULL;
01882         int nice;
01883 
01884         /* set nice value for master server */
01885 #ifdef HAVE_SETPRIORITY
01886         errno = 0;
01887         nice = Cfg.NiceServer;
01888         nice += getpriority(PRIO_PROCESS, 0);
01889         if (nice == -1 && errno != 0) {
01890                 slog.
01891                     p(Logger::
01892                       Error) << "getpriority failed: " << strerror(errno)
01893                     << "\n";
01894         } else {
01895                 if (setpriority(PRIO_PROCESS, 0, nice) == -1)
01896                         slog.
01897                             p(Logger::
01898                               Error) << "setpriority failed: " <<
01899                             strerror(errno) << "\n";
01900         }
01901 #endif
01902 
01903         if (fd >= 0) {
01904                 // FIXME: check NULL Pointer and throw exception
01905                 psock_buff = new sockbuf(fd);
01906                 psock_buff->setname ("nntp client socket");
01907                 psock_stream = new iosockstream(psock_buff);
01908                 psock_buff->recvtimeout(-1);
01909                 psock_buff->sendtimeout(-1);
01910                 psock_stream->unsetf(ios::skipws);
01911                 clt.co = psock_stream;
01912                 clt.ci = psock_stream;
01913 
01914                 // get network address and name of client
01915                 clt.socklen = sizeof(clt.sock);
01916                 if (getpeername
01917                     (fd, (struct sockaddr *) &clt.sock,
01918                      &clt.socklen) < 0) {
01919                         // Cannot get socket --- connection from stdin?
01920                         sprintf(clt.client_name, "fd#%d", fd);
01921                         clt.client_logname = clt.client_name;
01922                 } else {
01923                         // get the name of the client and store it in clt.client_name.
01924                         // if the client does not have a name, use the ip-address instead
01925                         // store it in nntp_posting_host as used by libnserver
01926                         he = gethostbyaddr((const char *)
01927                                            &(clt.sock.sin_addr),
01928                                            sizeof(clt.sock.sin_addr),
01929                                            AF_INET);
01930                         if (he) {
01931                                 strncpy(clt.client_name, he->h_name,
01932                                         sizeof(clt.client_name));
01933                                 clt.client_name[sizeof(clt.client_name) -
01934                                                 1] = '\0';
01935                         } else {
01936                                 strncpy(clt.client_name,
01937                                         inet_ntoa(clt.sock.sin_addr),
01938                                         sizeof(clt.client_name));
01939                                 clt.client_name[sizeof(clt.client_name) -
01940                                                 1] = '\0';
01941                         }
01942                         strcpy(nntp_posting_host, clt.client_name);
01943 
01944                         // add the client's name to its logging name (clt.client_logname),
01945                         // if names should be logged
01946                         if (Cfg.LogStyle & Config::LogName) {
01947                                 clt.client_logname = clt.client_name;
01948                         }
01949 
01950                         if (Cfg.LogStyle & Config::LogAddr) {
01951                                 if (clt.client_logname.length() > 0)
01952                                         clt.client_logname += " [";
01953                                 else
01954                                         clt.client_logname += '[';
01955                                 clt.client_logname +=
01956                                     inet_ntoa(clt.sock.sin_addr);
01957                                 clt.client_logname += ']';
01958                         }
01959                         // check whether the client is allowed access according to
01960                         // libwrap
01961 #ifdef HAVE_LIBWRAP
01962                         // Check the hosts_access configuration; emulate INN error message
01963                         if (!hosts_ctl(PACKAGE,
01964                                        clt.client_name,
01965                                        inet_ntoa(clt.sock.sin_addr),
01966                                        STRING_UNKNOWN)) {
01967                                 slog.p(Logger::Notice) << clt.
01968                                     client_logname << " denied - hosts.allow\n";
01969                                 (*clt.
01970                                  co) <<
01971                                "502 You have no permission to talk.  Goodbye.\n";
01972                                 clt.co->flush ();
01973                                 return;
01974                         }
01975 #endif
01976                 }
01977         } else {
01978                 cin.unsetf(ios::skipws);
01979                 clt.ci = &cin;
01980                 clt.co = &cout;
01981                 strcpy(clt.client_name, "stdin");
01982                 clt.client_logname = clt.client_name;
01983                 fd = 0;
01984         }
01985 
01986 #ifdef NO_STREAM_BUFFERING
01987         {
01988                 streambuf *b;
01989                 b = clt.ci->rdbuf();
01990                 if (b)
01991                         b->setbuf(0, 0);
01992                 b = clt.co->rdbuf();
01993                 if (b)
01994                         b->setbuf(0, 0);
01995         }
01996 #endif
01997 
01998         // check whether the client is allowed access according to
01999         // our own access configuration
02000         clt.access_entry =
02001             Cfg.clnts.client(clt.client_name, clt.sock.sin_addr);
02002         if (!clt.access_entry || !clt.access_entry->access_flags) {
02003 //   nnrpd_deny:
02004                 slog.p(Logger::Notice) << clt.
02005                     client_logname << " denied\n";
02006                 (*clt.
02007                  co) << "502 You have no permission to talk.  Goodbye.\n";
02008                 clt.co->flush();
02009                 return;
02010         } else {
02011                 slog.p(Logger::Debug) << "nnrpd: access_entry name matched: "
02012                         << clt.access_entry->hostname << "\n";
02013         }
02014 
02015         slog.p(Logger::Notice) << clt.client_logname << " connect\n";
02016 
02017         if (clt.access_entry->authentication.getType() != "none") {
02018                 clt.auth_state = AUTH_REQUIRED;
02019         }
02020         if (clt.access_entry->authentication.getType() == "unix" &&
02021             clt.access_entry->authentication.getNrOfFields() > 1) {
02022                 // authentication optional, not required
02023                 clt.auth_state = AUTH_NOT_REQUIRED;
02024         }
02025         if (clt.access_entry->authentication.getType() == "file") {
02026                 // authentication optional, not required
02027                 clt.auth_state = AUTH_NOT_REQUIRED;
02028         }
02029         if (clt.access_entry->authentication.getType() == "pam") {
02030                 // authentication optional, not required
02031                 clt.auth_state = AUTH_NOT_REQUIRED;
02032         }
02033         if (clt.access_entry->authentication.getType() == "pam+file") {
02034                 // authentication optional, not required
02035                 clt.auth_state = AUTH_NOT_REQUIRED;
02036         }
02037 
02038         // Set client command table, can be changed after successfully
02039         // authentication!
02040         set_client_command_table (clt);
02041 
02042         try {
02043                 clt.srvr = new CServer(Cfg.SpoolDirectory, &(Cfg.srvrs));
02044                 clt.srvr->setttl(Cfg.ttl_list, Cfg.ttl_desc);
02045         }
02046         catch(SystemError & se) {
02047                 slog.
02048                     p(Logger::
02049                       Alert) << "CServer failed, check permissions\n";
02050                 se.print();
02051                 (*clt.
02052                  co) << "400 " PACKAGE " " VERSION
02053                ", service not available\r\n";
02054                 exit(1);
02055         }
02056 
02057         (*clt.
02058          co) << "200 " PACKAGE " " VERSION ", accepting NNRP commands\r\n";
02059         do {
02060                 flush(*clt.co);
02061 
02062                 alarm (Cfg.ClientTimeout);
02063                 clt.ci->getline(req, sizeof(req), '\n');
02064                 alarm (0);
02065                 if (clientTimeoutReached) {
02066                         (*clt.co) << "400 " PACKAGE " " VERSION ", service timed out!\r\n";
02067                         slog.p(Logger::Notice) << clt.client_logname << " ClientTimeout reached\n";
02068                         goto client_exit;
02069                 }
02070                 if (Xsignal >= 0) {
02071                         (*clt.co) << "400 " PACKAGE " " VERSION ", service discontinued\r\n";
02072                         slog.p(Logger::Notice) << clt.client_logname << " discontinued due to Signal\n";
02073                         goto client_exit;
02074                 }
02075                 if (clt.ci->eof()) {
02076                         slog.p(Logger::Debug) << "NewsCache.cc input stream eof!\n";
02077                         break;
02078                 }
02079                 if (!clt.ci->good()) {
02080                         slog.p(Logger::Debug) << "NewsCache.cc input stream not good!\n";
02081                         break;
02082                 }
02083                 rp = req + strlen(req);
02084                 while (rp > req) {
02085                         rp--;
02086                         if (!isspace(*rp))
02087                                 break;
02088                         else
02089                                 *rp = '\0';
02090                 }
02091                 strcpy(oreq, req);
02092                 // FIXME: better solution?
02093                 if (strncasecmp(oreq, "authinfo", 8))
02094                         slog.p(Logger::Info) << clt.
02095                             client_logname << " " << oreq << "\n";
02096                 else
02097                         slog.p(Logger::Info) << clt.
02098                             client_logname << " authinfo\n";
02099 
02100                 // Split command into arguments
02101                 for (rp = req, argc = 0; *rp && argc < 256; rp++) {
02102                         if (isspace(*rp)) {
02103                                 *rp = '\0';
02104                         } else {
02105                                 if (rp == req || *(rp - 1) == '\0')
02106                                         argv[argc++] = rp;
02107                                 if (argc == 1)
02108                                         *rp = tolower(*rp);
02109                         }
02110                 }
02111                 if (!argc)
02112                         continue;
02113                 if (argc == 256) {
02114                         (*clt.co) << "500 Line too long\r\n";
02115                         continue;
02116                 }
02117                 // Call function for command
02118                 cmd = argv[0];
02119                 if (clt.auth_state == AUTH_REQUIRED &&
02120                     strcasecmp(cmd, "authinfo") != 0 &&
02121                     strcasecmp(cmd, "quit") != 0 &&
02122                     strcasecmp(cmd, "help") != 0) {
02123                         (*clt.
02124                          co) << "480 Authentication required (" << cmd <<
02125                        ")\r\n";
02126                         continue;
02127                 }
02128 
02129                 cmdp = clt.client_command_map.find(argv[0]);
02130                 if (cmdp == clt.client_command_map.end() || !cmdp->second->func) {
02131                         slog.p(Logger::Notice) << clt.client_logname
02132                             << " unrecognized " << oreq << "\n";
02133                         (*clt.co) << "500 What?\r\n";
02134                         continue;
02135                 }
02136 
02137                 errc = cmdp->second->func(&clt, argc, argv);
02138                 if (!(Cfg.LogStyle & Config::LogINN)) {
02139                         if (errc < 0) {
02140                                 if (strncasecmp(oreq, "authinfo", 8))
02141                                         slog.p(Logger::Notice) << clt.
02142                                                 client_logname << " failed " << oreq <<
02143                                                 "\n";
02144                                 else 
02145                                         slog.p(Logger::Notice) << clt.
02146                                                 client_logname << " failed authinfo for user  " <<
02147                                                 clt.auth_user << "\n";
02148                         }
02149                 if (clt.auth_failures >= clt.auth_max_failures) {
02150 
02151                         (*clt.  co) << "400 " PACKAGE " " VERSION
02152                        ", service discontinued (max passwords retries)\r\n";
02153                         goto client_exit;
02154                 }
02155                 }
02156         } while (errc == 0 || errc == -1);
02157 
02158       client_exit:
02159         if (clt.stat_artingrp) {
02160                 slog.p(Logger::Notice) << clt.client_logname
02161                     << " group " << clt.groupname
02162                     << " " << clt.stat_artingrp << "\n";
02163                 clt.stat_artingrp = 0;
02164         }
02165         slog.p(Logger::Notice) << clt.client_logname
02166             << " exit articles " << clt.stat_articles
02167             << " groups " << clt.stat_groups << "\n";
02168         clt.co->flush();
02169         delete clt.srvr;
02170 }
02171 
02172 void nntpd()
02173 {
02174         int sock;
02175         struct sockaddr_in nproxy;
02176         struct servent *cport;
02177 
02178         struct sockaddr_in clt_sa;
02179         SOCKLEN_TYPE clt_salen;
02180 
02181         int clt_fd;
02182         int clt_pid = 0;
02183         int nice;
02184 
02185         slog.p(Logger::Notice) << "NewsCache Server Start\n";
02186 
02187         /* set nice value for master server */
02188 #ifdef HAVE_SETPRIORITY
02189         errno = 0;
02190         nice = Cfg.NiceServer;
02191         nice += getpriority(PRIO_PROCESS, 0);
02192         if (nice == -1 && errno != 0) {
02193                 slog.
02194                     p(Logger::
02195                       Error) << "getpriority failed: " << strerror(errno)
02196                     << "\n";
02197         } else {
02198                 if (setpriority(PRIO_PROCESS, 0, nice) == -1)
02199                         slog.
02200                             p(Logger::
02201                               Error) << "setpriority failed: " <<
02202                             strerror(errno) << "\n";
02203         }
02204 #endif
02205 
02206         /* nobody is connecting to NewsCache currently */
02207         nntp_connections = 0;
02208 
02209         {                       /* create socket and set some socket options */
02210                 int one = 1;
02211                 if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
02212                         slog.
02213                             p(Logger::
02214                               Error) << "socket failed: " <<
02215                             strerror(errno) << "\n";
02216                         exit(1);
02217                 }
02218 
02219                 if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
02220                                (char *) &one, sizeof(int)) < 0) {
02221                         slog.
02222                             p(Logger::
02223                               Error) << "setsockopt failed: " <<
02224                             strerror(errno) << "\n";
02225                 }
02226 
02227                 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
02228                                (char *) &one, sizeof(int)) < 0) {
02229                         slog.
02230                             p(Logger::
02231                               Error) << "setsockopt failed: " <<
02232                             strerror(errno) << "\n";
02233                 }
02234 
02235                 /* fill the sockaddr with the port we should listen too */
02236                 if (Cfg.CachePort[0] != '\0') {
02237                         // old variant
02238                         const char *cp = Cfg.CachePort;
02239                         nproxy.sin_family = AF_INET;
02240                         nproxy.sin_addr.s_addr = INADDR_ANY;
02241                         if (cp[0] != '#') {
02242                                 if ((cport =
02243                                      getservbyname(cp, "tcp")) == NULL) {
02244                                         slog.
02245                                             p(Logger::
02246                                               Error) << cmnd <<
02247                                             ": Can't resolve service " <<
02248                                             cp << "/tcp\n";
02249                                         exit(1);
02250                                 }
02251                                 nproxy.sin_port = cport->s_port;
02252                         } else {
02253                                 nproxy.sin_port = htons(atoi(cp + 1));
02254                         }
02255                 } else if (fillHostStruct(&nproxy, Cfg.ListenTo) == -1) {
02256                         slog.
02257                             p(Logger::
02258                               Error) << cmnd <<
02259                             ": Can't resolve parameter ListenTo: " << Cfg.
02260                             ListenTo << "\n";
02261                         exit(1);
02262                 }
02263         }                       /* end create socket and set some socket options */
02264 
02265         if (bind(sock, (struct sockaddr *) &nproxy, sizeof(nproxy)) < 0) {
02266                 slog.
02267                     p(Logger::
02268                       Error) << "can't bind socket: " << strerror(errno) <<
02269                     "\n";
02270                 exit(1);
02271         }
02272 
02273         {                       /* store pid in Cfg.PidFile */
02274                 ofstream pid(Cfg.PidFile);
02275                 pid << getpid() << endl;
02276                 if (!pid.good()) {
02277                         slog.
02278                             p(Logger::Warning) << "cannot open pid file\n";
02279                 }
02280         }
02281 
02282         setugid(Cfg.Username, Cfg.Groupname);
02283         listen(sock, 4);
02284         for (;;) {
02285                 /* Accept connection */
02286                 clt_salen = sizeof(clt_sa);
02287                 do {
02288                         errno = 0;
02289                         clt_fd =
02290                             accept(sock, (struct sockaddr *) &clt_sa,
02291                                    &clt_salen);
02292                         if (Xsignal >= 0) {
02293                                 close(sock);
02294                                 exit(0);
02295                         }
02296                 } while (errno == EINTR);
02297                 if (clt_fd < 0) {
02298                         slog.
02299                             p(Logger::
02300                               Warning) << "accept failed: " <<
02301                             strerror(errno) << "\n";
02302                         continue;
02303                 }
02304 #ifdef CONF_MultiClient
02305                 if (nntp_connections >= Cfg.MaxConnections
02306                     || (clt_pid = fork()) < 0) {
02307                         if (clt_pid < 0) {
02308                                 slog.
02309                                     p(Logger::
02310                                       Error) << "fork failed: " <<
02311                                     strerror(errno) << "\n";
02312                         }
02313                         write(clt_fd, "400 too many users\r\n", 21);
02314                         close(clt_fd);
02315                 } else {
02316                         // success
02317                         if (clt_pid == 0) {
02318                                 // child
02319                                 int one = 1;
02320                                 close(sock);
02321                                 if (setsockopt
02322                                     (clt_fd, SOL_SOCKET, SO_KEEPALIVE,
02323                                      (char *) &one, sizeof(int)) < 0) {
02324                                         slog.
02325                                             p(Logger::
02326                                               Error) <<
02327                                             "client setsockopt failed: " <<
02328                                             strerror(errno) << "\n";
02329                                 }
02330                                 try {
02331                                         nnrpd(clt_fd);
02332                                 } catch (sockerr e) {
02333                                         slog.p(Logger::Error) << "nnrpd caught "
02334                                                 << "sockbuf " << e.operation ()
02335                                                 << e.serrno ()
02336                                                 << " " << e.errstr () << "\n";
02337                                 } catch (UsageError e) {
02338                                         slog.p(Logger::Error) << "nnrpd caught "
02339                                                 << "UsageError ";
02340                                         e.print();
02341                                 } catch (NotAllowedError e) {
02342                                         slog.p(Logger::Error) << "nnrpd caught "
02343                                                 << "NotAllowdError ";
02344                                         e.print();
02345                                 } catch (NoSuchArticleError e) {
02346                                         slog.p(Logger::Error) << "nnrpd caught "
02347                                                 << "NoSuchArticleError ";
02348                                         e.print();
02349                                 } catch (DuplicateArticleError e) {
02350                                         slog.p(Logger::Error) << "nnrpd caught "
02351                                                 << "DuplicateArticleError ";
02352                                         e.print();
02353                                 } catch (NoSuchGroupError e) {
02354                                         slog.p(Logger::Error) << "nnrpd caught "
02355                                                 << "NoSuchGroupError ";
02356                                         e.print();
02357                                 } catch (NoNewsServerError e) {
02358                                         slog.p(Logger::Error) << "nnrpd caught "
02359                                                 << "NoNewsServerError ";
02360                                         e.print();
02361                                 } catch (NoSuchFieldError e) {
02362                                         slog.p(Logger::Error) << "nnrpd caught "
02363                                                 << "NoSuchFieldError ";
02364                                         e.print();
02365                                 } catch (NSError e) {
02366                                         slog.p(Logger::Error) << "nnrpd caught "
02367                                                 << "NSError ";
02368                                         e.print();
02369                                 } catch (AssertionError e) {
02370                                         slog.p(Logger::Error) << "nnrpd caught "
02371                                                 << "AssertionError ";
02372                                         e.print();
02373                                 } catch (IOError e) {
02374                                         slog.p(Logger::Error) << "nnrpd caught "
02375                                                 << "IOError ";
02376                                         e.print();
02377                                 } catch (SystemError e) {
02378                                         slog.p(Logger::Error) << "nnrpd caught "
02379                                                 << "SystemError ";
02380                                         e.print();
02381                                 } catch (Error e) {
02382                                         slog.p(Logger::Error) << "nnrpd caught "
02383                                                 << "Error ";
02384                                         e.print();
02385                                 } catch ( ... ) {
02386                                         slog.p(Logger::Error) << "nnrpd caught "
02387                                                 << "unknown!!"  << "\n";
02388                                 }
02389                                 close(clt_fd);
02390                                 exit(0);
02391                         }
02392                         //Parent
02393                         close(clt_fd);
02394                         nntp_connections++;
02395                 }
02396 #else
02397                 nnrpd(clt_fd);
02398                 close(clt_fd);
02399 #endif
02400         }
02401 
02402         close(sock);
02403 }
02404 
02405 void sigchld(int num)
02406 {
02407         int pid;
02408         int st;
02409 
02410         slog.p(Logger::Debug) << "receiving signal SIGCHLD: " << num << "\n";
02411         /* Reinstall the signal handler */
02412 #ifdef HAVE_SIGACTION
02413         struct sigaction action;
02414         action.sa_handler = sigchld;
02415         sigemptyset(&action.sa_mask);
02416         action.sa_flags = 0;
02417         sigaction(num, &action, NULL);
02418 #else
02419         signal(num, sigchld);
02420 #endif
02421 
02422         while ((pid = waitpid(0, &st, WNOHANG)) > 0) {
02423                 if (WIFEXITED(st) || WIFSIGNALED(st)) {
02424                         // child terminated
02425                         nntp_connections--;
02426                         if (st) {
02427                                 if (WIFEXITED(st)) {
02428                                         slog.
02429                                             p(Logger::
02430                                               Error) << pid << " returned "
02431                                             << WEXITSTATUS(st) << "\n";
02432                                 } else if (WIFSIGNALED(st)) {
02433                                         slog.
02434                                             p(Logger::
02435                                               Error) << pid <<
02436                                             " caught signal " <<
02437                                             WTERMSIG(st) << "\n";
02438                                 }
02439                         }
02440                 }
02441         }
02442 }
02443 
02444 void catchsigalarm(int num)
02445 {
02446         slog.p(Logger::Debug) << "receiving signal SIGALRM: " << num << "\n";
02447         clientTimeoutReached=1;
02448 #ifdef HAVE_SIGACTION
02449         /* Reinstall the signal handler */
02450         struct sigaction action;
02451         action.sa_handler = catchsigalarm;
02452         sigemptyset(&action.sa_mask);
02453         action.sa_flags = 0;
02454         sigaction(num, &action, NULL);
02455 #else
02456         signal(num, catchsigalarm);
02457 #endif
02458 }
02459 
02460 void catchsighup(int num)
02461 {
02462         slog.p(Logger::Debug) << "receiving signal SIGHUP: " << num << "\n";
02463         Cfg.init();
02464         Cfg.read(config_file);
02465 #ifdef HAVE_SIGACTION
02466         /* Reinstall the signal handler */
02467         struct sigaction action;
02468         action.sa_handler = catchsighup;
02469         sigemptyset(&action.sa_mask);
02470         action.sa_flags = 0;
02471         sigaction(num, &action, NULL);
02472 #else
02473         signal(num, catchsighup);
02474 #endif
02475 }
02476 
02477 void catchsignal(int num)
02478 {
02479         slog.p(Logger::Debug) << "receiving signal: " << num << "\n";
02480         Xsignal = num;
02481 #ifdef HAVE_SIGACTION
02482         /* Reinstall the signal handler */
02483         struct sigaction action;
02484         action.sa_handler = catchsignal;
02485         sigemptyset(&action.sa_mask);
02486         action.sa_flags = 0;
02487         sigaction(num, &action, NULL);
02488 #else
02489         signal(num, catchsignal);
02490 #endif
02491 }
02492 
02493 #define USAGE \
02494 "       -h, --help\n"\
02495 "              Show summary of options.\n"\
02496 "\n"\
02497 "       -v, --version\n"\
02498 "              Show version of program.\n"\
02499 "\n"\
02500 "       -f --fqdn\n"\
02501 "              Print what newscache thinks is the fully qualified domain  name.\n"\
02502 "\n"\
02503 "       -c --configuration config-file\n"\
02504 "\n"\
02505 "       -i --inetd\n"\
02506 "              newscache is used with inetd and read from stdin.\n"\
02507 "\n"\
02508 "       -d --debug\n"\
02509 "              Do not detach.\n"\
02510 "\n"\
02511 "       -p --print-parameter\n"\
02512 "              Print current parameter settings.\n"\
02513 "\n"\
02514 "       -o --configurtion-options\n"\
02515 "              Print all options specified in the configure phase.\n"
02516 
02517 int main(int argc, char **argv)
02518 {
02519 #ifndef WITH_SYSLOG
02520         char logfile[MAXPATHLEN];
02521         time_t t;
02522         pid_t p;
02523 #endif
02524         int opt_config = 0, opt_inetd = 0, opt_debug = 0, opt_print_para = 0;
02525         int opt_config_options = 0;
02526         int aerr = 0, c;
02527 
02528         sprintf(config_file, "%s/newscache.conf", SYSCONFDIR);
02529 
02530         cmnd = argv[0];
02531         while (1) {
02532                 int option_index = 0;
02533                 static struct option long_options[] = {
02534                         {"version", 0, 0, 'v'},
02535                         {"fqdn", 0, 0, 'f'},
02536                         {"help", 0, 0, 'h'},
02537                         {"configuration", 1, 0, 'c'},
02538                         {"inetd", 0, 0, 'i'},
02539                         {"debug", 0, 0, 'd'},
02540                         {"print-parameter", 0, 0, 'p'},
02541                         {"configuration-options", 0, 0, 'o'},
02542                         {0, 0, 0, 0}
02543                 };
02544 
02545                 c = getopt_long (argc, argv, "vfhc:idpo", long_options,
02546                                 &option_index);
02547                 if (c == -1) 
02548                         break;
02549 
02550                 switch (c) {
02551                         case 'v':
02552                                 cout << PACKAGE << " " VERSION << endl;
02553                                 exit (0);
02554                                 break;
02555                         
02556                         case 'f':
02557                                 cout << getfqdn () << endl;
02558                                 exit (0);
02559                                 break;
02560                         
02561                         case 'h':
02562                                 cout << "Usage: " << cmnd << " [options]\n"
02563                                         << USAGE;
02564                                 exit (0);
02565                                 break;
02566                         
02567                         case 'c':
02568                                 strcpy (config_file, optarg);
02569                                 ++opt_config;
02570                                 break;
02571 
02572                         case 'i':
02573                                 ++opt_inetd;
02574                                 break;
02575 
02576                         case 'd':
02577                                 ++opt_debug;
02578                                 break;
02579                         case 'p':
02580                                 ++opt_print_para;
02581                                 break;
02582                         case 'o':
02583                                 ++opt_config_options;
02584                                 break;
02585                         default:
02586                                 aerr = 1;
02587                                 break;
02588                 }
02589         }
02590 
02591         if (aerr || optind != argc) {
02592                 cerr << "Usage: " << cmnd << " [options]\n" << USAGE;
02593                 exit(1);
02594         }
02595 
02596         try {
02597                 Cfg.read(config_file);
02598                 strcpy(nntp_hostname, Cfg.Hostname);
02599         }
02600         catch(IOError & io) {
02601                 cerr << "unexpected EOF in " << config_file << "\n";
02602                 exit(2);
02603         }
02604         catch(SyntaxError & se) {
02605                 cerr << se._errtext << "\n";
02606                 exit(2);
02607         }
02608 
02609         if (opt_print_para) {
02610                 cout << "# This output is generated with newscache -p" << endl;
02611                 Cfg.printParameters (&cout);
02612                 exit (0);
02613         }
02614 
02615         if (opt_config_options) {
02616                 for (int i=0; ConfigurationOptions[i] != NULL; i++) {
02617                         cout << ConfigurationOptions[i] << " ";
02618                 }
02619                 cout << endl;
02620                 exit (0);
02621         }
02622 
02623 #ifdef WITH_SYSLOG
02624         slog.open(PACKAGE, LOG_NDELAY | LOG_PID, LOG_NEWS);
02625 #else
02626         sprintf (logfile, "%s/newscache.log", Cfg.LogDirectory);
02627         slog.open (logfile);
02628 #endif
02629 
02630         // signal handling
02631         Xsignal = -1;
02632 
02633 #ifdef HAVE_SIGACTION
02634         struct sigaction action;
02635 
02636         // alarm - client timeout signal
02637         action.sa_handler = catchsigalarm;
02638         sigemptyset(&action.sa_mask);
02639         action.sa_flags = 0;
02640         sigaction(SIGALRM, &action, NULL);
02641 
02642         // reread config file
02643         action.sa_handler = catchsighup;
02644         sigemptyset(&action.sa_mask);
02645         action.sa_flags = 0;
02646         sigaction(SIGHUP, &action, NULL);
02647 
02648         // signals indicating to terminate NewsCache
02649         action.sa_handler = catchsignal;
02650         sigaction(SIGINT, &action, NULL);
02651         sigaction(SIGPIPE, &action, NULL);
02652         sigaction(SIGTERM, &action, NULL);
02653 
02654         // ignored signals
02655         action.sa_handler = SIG_IGN;
02656         sigaction(SIGUSR1, &action, NULL);
02657         sigaction(SIGUSR2, &action, NULL);
02658 
02659         // clean zombies
02660         action.sa_handler = sigchld;
02661         sigaction(SIGCHLD, &action, NULL);
02662 #else
02663         signal(SIGALRM, catchsigalarm);
02664         signal(SIGHUP, catchsighup);
02665         signal(SIGINT, catchsignal);
02666         signal(SIGPIPE, catchsignal);
02667         signal(SIGTERM, catchsignal);
02668 
02669         signal(SIGUSR1, SIG_IGN);
02670         signal(SIGUSR2, SIG_IGN);
02671 
02672         signal(SIGCHLD, sigchld);
02673 #endif
02674 
02675         if (Cfg.ServerType == Config::inetd)
02676                 opt_inetd = 1;
02677 
02678         if (opt_inetd) {
02679                 if (getuid() == CONF_UIDROOT)
02680                         setugid(Cfg.Username, Cfg.Groupname);
02681                 nnrpd(-1);
02682         } else {
02683                 if (opt_debug == 0) {
02684                         switch (fork()) {
02685                         case -1:
02686                                 cerr << "cannot fork\n";
02687                                 slog.p(Logger::Error) << "cannot fork\n";
02688                                 break;
02689                         case 0:
02690                                 if (setsid() == -1) {
02691                                         cerr << "setsid failed\n";
02692                                         slog.
02693                                             p(Logger::
02694                                               Error) << "setsid failed\n";
02695                                         exit(1);
02696                                 }
02697                                 close(STDIN_FILENO);
02698                                 close(STDOUT_FILENO);
02699                                 close(STDERR_FILENO);
02700                                 break;
02701                         default:
02702                                 return 0;
02703                         }
02704                 }
02705                 nntpd();
02706         }
02707 
02708         return 0;
02709 }

Generated on Fri Aug 20 10:58:07 2004 for NewsCache by doxygen 1.3.6-20040222