summaryrefslogblamecommitdiffstats
path: root/innbbsd/bbsnnrp.c
blob: 544e3336aff9f681642d9e868801d9533810e48f (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
  









                                                                             



                       
                      



                   
                  

                
                    

                 
                      







                     

                                      

                         




                                                         

                       









                                             

                       


                           
 

                               









                                              
                        
 


                      
 




                                                  

 

                               

                
                   

      



                                
 
                                       
 


                                                



                     




                                               

          
                        
 





















                                                                                                             

 




                                            
                

                         
 



                                          







                                            







































































                                                                             
                      

                       

                                                          

                       

                                     








                                                                                                                          
 



                                                                                
 
































                                                                                                   



                                    








                                       
                         

                         
          




                                                    



                                



                                                      



                 
                           
 








                                            


                      
                            
 





























































































































































































                                                                                                               

 

                                 

                    
                            
 
















                                                                        
               





                                                                     
     








                                                                                           
      





































































                                                                                                                                                   


                                  


                            
 























                                                                                           
                           

                                                                                                                    
         










                                                                   
         
















                                                               



              





                              


                            


                           
 



                                         

 


                                                                    
   

                              
 





                            


                
                            
 







                                                                            
               
                                                                                                                                                                                            
     

                                                                                                                                                                
      


                                                              
                                                                          


                                                                                                                                                           
                
                          

                                         





                          


                
                            
 

                              
               



                                                              
      


                                                   

 


                                   
 
                
                         


                           
 








                                                         

 




                             
 

                         
 


















                                                             



                                        



                              
 

                                                
 

                                                                       
                   
                                                    
      





                                                               

 




                                  



                                                                
                 
          

















                                               


     




                            
 








                                                                   

 




                                
 












                                                                                 
                  

                                                
                   
                                           

      

















                                                                 
                   
                                        
      




                                                                               
                   
                                               
      










                                                                           
                       





                                                                                       
                          
                                                        



                                                       




                                                          
                    















                                                                            



                                


                               
 


















                                                                         


 


                            
 









                                                    
                   
                         
      









                                                               
                   
                         
      












                                                        
                   
                         
      















                                                                  

                   
                                                          
      













                                                                                                       


                                                                   



























                                                                                           
            
                                                            
      














































                                                                                                  
             















                                                                           




             
/*
 * Usage: bbsnnrp [options] nntpserver activefile -h|? (help) -v (verbose
 * protocol transactions) -c (reset active files only; don't receive
 * articles) -r remotehost(send articles to remotehost, default=local) -p
 * port|(send articles to remotehost at port, default=7777) path(send
 * articles to local at path, default=~bbs/innd/.innbbsd) -n (don't ask
 * innbbsd server and stat articles) -w seconds (wait for seconds and run
 * infinitely, default=once)  -a max_art (maximum number of articles received
 * for a group each time) -s max_stat(maximum number of articles stated for a
 * group each time) -t stdin|nntp (default=nntp)
 */

#include "innbbsconf.h"
#include <sys/mman.h>
#ifndef AIX
#include <sys/fcntl.h>
#endif
#include "bbslib.h"
#include "daemon.h"
#include "nntp.h"
#include "osdep.h"

#ifndef MAX_ARTS
#define MAX_ARTS 100
#endif
#ifndef MAX_STATS
#define MAX_STATS 1000
#endif

#if  defined(__linux)
#define NO_USE_MMAP
#else
#define USE_MMAP
#endif

int             Max_Arts = MAX_ARTS;
int             Max_Stats = MAX_STATS;

typedef struct NEWSRC_T {
    char           *nameptr, *lowptr, *highptr, *modeptr;
    int             namelen, lowlen, highlen;
    ULONG           low, high;
    int             mode, subscribe;
}               newsrc_t;

typedef struct NNRP_T {
    int             nnrpfd;
    int             innbbsfd;
    FILE           *nnrpin, *nnrpout;
    FILE           *innbbsin, *innbbsout;
    char            activefile[MAXPATHLEN];
    char            rcfile[MAXPATHLEN];
    newsrc_t       *newsrc;
    char           *actpointer, *actend;
    int             actsize, actfd, actdirty;
}               nnrp_t;

typedef struct XHDR_T {
    char           *header;
    ULONG           artno;
}               xhdr_t;

xhdr_t          XHDR[MAX_ARTS];
char            LockFile[1024];

#define NNRPGroupOK NNTP_GROUPOK_VAL
#define NNRPXhdrOK NNTP_HEAD_FOLLOWS_VAL
#define NNRParticleOK NNTP_ARTICLE_FOLLOWS_VAL
#define INNBBSstatOK NNTP_NOTHING_FOLLOWS_VAL
#define INNBBSihaveOK NNTP_SENDIT_VAL
#define NNRPconnectOK NNTP_POSTOK_VAL
#define NNRPstatOK NNTP_NOTHING_FOLLOWS_VAL
#define INNBBSconnectOK NNTP_POSTOK_VAL

nnrp_t          BBSNNRP;

void 
doterm(s)
    int             s;
{
    printf("bbsnnrp terminated.  Signal %d\n", s);
    writerc(&BBSNNRP);
    if (isfile(LockFile))
    unlink(LockFile);
    exit(1);
}

extern char    *optarg;
extern int      opterr, optind;

#ifndef MIN_WAIT
#define MIN_WAIT 60
#endif

int             ResetActive = 0;
int             StatHistory = 1;
int             AskLocal = 1;
int             RunOnce = 1;

int             DefaultWait = MIN_WAIT;

char           *DefaultPort = DefaultINNBBSPort;
char           *DefaultPath = LOCALDAEMON;
char           *DefaultRemoteHost;

#ifndef MAXBUFLEN
#define MAXBUFLEN 256
#endif
char            DefaultNewsgroups[MAXBUFLEN];
char            DefaultOrganization[MAXBUFLEN];
char            DefaultModerator[MAXBUFLEN];
char            DefaultTrustfrom[MAXBUFLEN];
char            DefaultTrustFrom[MAXBUFLEN];

usage(arg)
    char           *arg;
{
    fprintf(stderr, "Usage: %s [options] nntpserver activefile\n", arg);
    fprintf(stderr, "       -h|? (help) \n");
    fprintf(stderr, "       -v (verbose protocol transactions)\n");
    fprintf(stderr, "       -c (reset active files only; don't receive articles)\n");
    fprintf(stderr, "       -r [proto:]remotehost\n");
    fprintf(stderr, "          (send articles to remotehost, default=ihave:local)\n");
    fprintf(stderr, "       -p port|(send articles to remotehost at port, default=%s)\n", DefaultINNBBSPort);
    fprintf(stderr, "          path(send articles to local at path, default=~bbs/innd/.innbbsd)\n");
    fprintf(stderr, "       -w seconds ( > 1 wait for seconds and run infinitely, default=once)\n");
    fprintf(stderr, "       -n (don't ask innbbsd server and stat articles)\n");
    fprintf(stderr, "       -a max_art(maximum number of articles received for a group each time)\n");
    fprintf(stderr, "          default=%d\n", MAX_ARTS);
    fprintf(stderr, "       -s max_stat(maximum number of articles stated for a group each time)\n");
    fprintf(stderr, "          default=%d\n", MAX_STATS);
    fprintf(stderr, "       -t stdin|nntp (default=nntp)\n");
    fprintf(stderr, "       -g newsgroups\n");
    fprintf(stderr, "       -m moderator\n");
    fprintf(stderr, "       -o organization\n");
    fprintf(stderr, "       -f trust_user (From: trust_user)\n");
    fprintf(stderr, "       -F trust_user (From trust_user)\n");
    fprintf(stderr, " Please E-mail bug to skhuang@csie.nctu.edu.tw or\n");
    fprintf(stderr, " post to tw.bbs.admin.installbbs\n");
}

static char    *StdinInputType = "stdin";
static char    *NntpInputType = "nntp";
static char    *NntpIhaveProtocol = "ihave";
static char    *NntpPostProtocol = "post";
static char    *DefaultNntpProtocol;
main(argc, argv)
    int             argc;
    char          **argv;
{
    char           *ptr, *server, *active;
    int             c, errflag = 0;
    int             lockfd;
    char           *inputtype;

    DefaultNntpProtocol = NntpIhaveProtocol;
    *DefaultNewsgroups = '\0';
    *DefaultModerator = '\0';
    *DefaultOrganization = '\0';
    *DefaultTrustFrom = '\0';
    *DefaultTrustfrom = '\0';
    inputtype = NntpInputType;
    while ((c = getopt(argc, argv, "f:F:m:o:g:w:r:p:a:s:t:h?ncv")) != -1)
    switch (c) {
    case 'v':
        verboseon("bbsnnrp.log");
        break;
    case 'c':
        ResetActive = 1;
        break;
    case 'g':
        strncpy(DefaultNewsgroups, optarg, sizeof DefaultNewsgroups);
        break;
    case 'm':
        strncpy(DefaultModerator, optarg, sizeof DefaultModerator);
        break;
    case 'o':
        strncpy(DefaultOrganization, optarg, sizeof DefaultOrganization);
        break;
    case 'f':
        strncpy(DefaultTrustfrom, optarg, sizeof DefaultTrustfrom);
        break;
    case 'F':
        strncpy(DefaultTrustFrom, optarg, sizeof DefaultTrustFrom);
        break;
    case 'r':{
        char           *hostptr;
        AskLocal = 0;
        DefaultRemoteHost = optarg;
        if ((hostptr = strchr(optarg, ':')) != NULL) {
            *hostptr = '\0';
            DefaultRemoteHost = hostptr + 1;
            if (strcasecmp(optarg, "post") == 0)
            DefaultNntpProtocol = NntpPostProtocol;
            *hostptr = ':';
        }
        break;
        }
    case 'w':
        RunOnce = 0;
        DefaultWait = atoi(optarg);
        if (DefaultWait < MIN_WAIT)
        DefaultWait = MIN_WAIT;
        break;
    case 'p':
        if (AskLocal == 0) {
        DefaultPort = optarg;
        } else {
        DefaultPath = optarg;
        }
        break;
    case 'n':
        StatHistory = 0;
        break;
    case 'a':
        Max_Arts = atol(optarg);
        if (Max_Arts < 0)
        Max_Arts = 0;
        break;
    case 's':
        Max_Stats = atol(optarg);
        if (Max_Stats < 0)
        Max_Stats = 0;
        break;
    case 't':
        if (strcasecmp(optarg, StdinInputType) == 0) {
        inputtype = StdinInputType;
        }
        break;
    case 'h':
    case '?':
    default:
        errflag++;
    }
    if (errflag > 0) {
    usage(argv[0]);
    return (1);
    }
    if (inputtype == NntpInputType && argc - optind < 2) {
    usage(argv[0]);
    exit(1);
    }
    if (inputtype == NntpInputType) {
    server = argv[optind];
    active = argv[optind + 1];
    if (isfile(active)) {
        strncpy(BBSNNRP.activefile, active, sizeof BBSNNRP.activefile);
    } else if (strchr(active, '/') == NULL) {
        sprintf(BBSNNRP.activefile, "%s/innd/%.*s", BBSHOME, sizeof BBSNNRP.activefile - 7 - strlen(BBSHOME), active);
    } else {
        strncpy(BBSNNRP.activefile, active, sizeof BBSNNRP.activefile);
    }

    strncpy(LockFile, (char *)fileglue("%s.lock", active), sizeof LockFile);
    if ((lockfd = open(LockFile, O_RDONLY)) >= 0) {
        char            buf[10];
        int             pid;

        if (read(lockfd, buf, sizeof buf) > 0 && (pid = atoi(buf)) > 0 && kill(pid, 0) == 0) {
        fprintf(stderr, "another process [%d] running\n", pid);
        exit(1);
        } else {
        fprintf(stderr, "no process [%d] running, but lock file existed, unlinked\n", pid);
        unlink(LockFile);
        }
        close(lockfd);
    }
    if ((lockfd = open(LockFile, O_RDWR | O_CREAT | O_EXCL, 0644)) < 0) {
        fprintf(stderr, "maybe another %s process running\n", argv[0]);
        exit(1);
    } else {
        char            buf[10];
        int             pid;
        sprintf(buf, "%-.8d\n", getpid());
        write(lockfd, buf, strlen(buf));
        close(lockfd);
    }
    for (;;) {
        if (!initial_bbs(NULL)) {
        fprintf(stderr, "Initial BBS failed\n");
        exit(1);
        }
        initsockets(server, &BBSNNRP, inputtype);
        ptr = (char *)strrchr(active, '/');
        if (ptr != NULL)
        ptr++;
        else
        ptr = active;
        sprintf(BBSNNRP.rcfile, "%s/.newsrc.%s.%s", INNDHOME, server, ptr);
        initrcfiles(&BBSNNRP);

        Signal(SIGTERM, doterm);
        Signal(SIGKILL, doterm);
        Signal(SIGHUP, doterm);
        Signal(SIGPIPE, doterm);

        readnews(server, &BBSNNRP);
        writerc(&BBSNNRP);
        closesockets();

        if (RunOnce)
        break;
        sleep(DefaultWait);
    }
    unlink(LockFile);
    }
     /* NntpInputType */ 
    else {
    if (!initial_bbs(NULL)) {
        fprintf(stderr, "Initial BBS failed\n");
        exit(1);
    }
    initsockets(server, &BBSNNRP, inputtype);
    Signal(SIGTERM, doterm);
    Signal(SIGKILL, doterm);
    Signal(SIGHUP, doterm);
    Signal(SIGPIPE, doterm);

    stdinreadnews(&BBSNNRP);
    closesockets();
    }               /* stdin input type */
    return 0;
}

headbegin(buffer)
    char           *buffer;
{
    if (strncmp(buffer, "Path: ", 6) == 0) {
    if (strchr(buffer + 6, '!') != NULL)
        return 1;
    }
    if (strncmp(buffer, "From ", 5) == 0) {
    if (strchr(buffer + 5, ':') != NULL)
        return 1;
    }
    return 0;
}

stdinreadnews(bbsnnrp)
    nnrp_t         *bbsnnrp;
{
    int             i;
    char            buffer[4096];
    ULONG           low, high;
    char            tmpfilename[MAXPATHLEN];
    FILE           *tmpfp = NULL;
    char            mid[1024];
    int             pathagain;
    int             ngmet, submet, midmet, pathmet, orgmet, approvedmet;
    int             discard;
    char            sending_path[MAXPATHLEN];
    int             sending_path_len = 0;

    strncpy(tmpfilename, (char *)fileglue("/tmp/bbsnnrp-stdin-%d-%d", getuid(), getpid()), sizeof tmpfilename);
    fgets(buffer, sizeof buffer, bbsnnrp->innbbsin);
    verboselog("innbbsGet: %s", buffer);
    if (atoi(buffer) != INNBBSconnectOK) {
    fprintf(stderr, "INNBBS server not OK\n");
    return;
    }
    if (DefaultNntpProtocol == NntpPostProtocol) {
    fputs("MODE READER\r\n", bbsnnrp->innbbsout);
    fflush(bbsnnrp->innbbsout);
    verboselog("innbbsPut: MODE READER\n");
    fgets(buffer, sizeof buffer, bbsnnrp->innbbsin);
    verboselog("innbbsGet: %s", buffer);
    }
    if (StatHistory == 0) {
    fputs("MIDCHECK OFF\r\n", bbsnnrp->innbbsout);
    fflush(bbsnnrp->innbbsout);
    verboselog("innbbsPut: MIDCHECK OFF\n");
    fgets(buffer, sizeof buffer, bbsnnrp->innbbsin);
    verboselog("innbbsGet: %s", buffer);
    }
    tmpfp = fopen(tmpfilename, "w");
    if (tmpfp == NULL)
    return;
    *mid = '\0';
    for (;;) {
    fprintf(stderr, "Try to read from stdin ...\n");
    ngmet = 0, submet = 0, midmet = 0, pathmet = 0, orgmet = 0, approvedmet = 0;
    discard = 0;
    while (fgets(buffer, sizeof buffer, stdin) != NULL) {
        char           *tmpptr;
        tmpptr = strchr(buffer, '\n');
        if (tmpptr != NULL)
        *tmpptr = '\0';
        if (strncasecmp(buffer, "Message-ID: ", 12) == 0) {
        strncpy(mid, buffer + 12, sizeof mid);
        midmet = 1;
        } else if (strncmp(buffer, "Subject: ", 9) == 0) {
        submet = 1;
        } else if (strncmp(buffer, "Path: ", 6) == 0) {
        pathmet = 1;
        } else if (strncmp(buffer, "Organization: ", 14) == 0) {
        orgmet = 1;
        } else if (strncmp(buffer, "Approved: ", 10) == 0) {
        approvedmet = 1;
        } else if (strncmp(buffer, "From: ", 6) == 0 && *DefaultTrustfrom) {
        if (strstr(buffer + 6, DefaultTrustfrom) == NULL) {
            discard = 1;
            verboselog("Discard: %s for %s", buffer, DefaultTrustfrom);
        }
        } else if (strncmp(buffer, "From ", 5) == 0 && *DefaultTrustFrom) {
        if (strstr(buffer + 5, DefaultTrustFrom) == NULL) {
            discard = 1;
            verboselog("Discard: %s for %s", buffer, DefaultTrustFrom);
        }
        } else if (strncmp(buffer, "Received: ", 10) == 0) {
        char           *rptr = buffer + 10, *rrptr;
        int             savech, len;
        if (strncmp(buffer + 10, "from ", 5) == 0) {
            rptr += 5;
            rrptr = strchr(rptr, '(');
            if (rrptr != NULL)
            rptr = rrptr + 1;
            rrptr = strchr(rptr, ' ');
            savech = *rrptr;
            if (rrptr != NULL)
            *rrptr = '\0';
        } else if (strncmp(buffer + 10, "(from ", 6) == 0) {
            rptr += 6;
            rrptr = strchr(rptr, ')');
            savech = *rrptr;
            if (rrptr != NULL)
            *rrptr = '\0';
        }
        len = strlen(rptr) + 1;
        if (*rptr && sending_path_len + len < sizeof(sending_path)) {
            if (*sending_path)
            strcat(sending_path, "!");
            strcat(sending_path, rptr);
            sending_path_len += len;
        }
        if (rrptr != NULL)
            *rrptr = savech;
        }
        if (strncmp(buffer, "Newsgroups: ", 12) == 0) {
        if (*DefaultNewsgroups) {
            fprintf(tmpfp, "Newsgroups: %s\r\n", DefaultNewsgroups);
        } else {
            fprintf(tmpfp, "%s\r\n", buffer);
        }
        ngmet = 1;
        } else {
        if (buffer[0] == '\0') {
            if (!ngmet && *DefaultNewsgroups) {
            fprintf(tmpfp, "Newsgroups: %s\r\n", DefaultNewsgroups);
            }
            if (!submet) {
            fprintf(tmpfp, "Subject: (no subject)\r\n");
            }
            if (!pathmet) {
            fprintf(tmpfp, "Path: from-mail\r\n");
            }
            if (!midmet) {
            static int      seed;
            time_t          now;
            time(&now);
            fprintf(tmpfp, "Message-ID: <%d@%d.%d.%d>\r\n", now, getpid(), getuid(), seed);
            sprintf(mid, "<%d@%d.%d.%d>", now, getpid(), getuid(), seed);
            seed++;
            }
            if (!orgmet && *DefaultOrganization) {
            fprintf(tmpfp, "Organization: %s\r\n", DefaultOrganization);
            }
            if (!approvedmet && *DefaultModerator) {
            fprintf(tmpfp, "Approved: %s\r\n", DefaultModerator);
            }
        }
        if (strncmp(buffer, "From ", 5) != 0 && strncmp(buffer, "To: ", 4) != 0) {
            if (buffer[0] == '\0') {
            if (*sending_path) {
                fprintf(tmpfp, "X-Sending-Path: %s\r\n", sending_path);
            }
            }
            fprintf(tmpfp, "%s\r\n", buffer);
        }
        }
        if (buffer[0] == '\0')
        break;
    }
    fprintf(stderr, "Article Body begin ...\n");
    pathagain = 0;
    while (fgets(buffer, sizeof buffer, stdin) != NULL) {
        char           *tmpptr;
        tmpptr = strchr(buffer, '\n');
        if (tmpptr != NULL)
        *tmpptr = '\0';
        if (headbegin(buffer)) {
        FILE           *oldfp = bbsnnrp->nnrpin;
        pathagain = 1;
        fputs(".\r\n", tmpfp);
        fclose(tmpfp);
        fprintf(stderr, "Try to post ...\n");
        tmpfp = fopen(tmpfilename, "r");
        bbsnnrp->nnrpin = tmpfp;
        if (!discard)
            if (INNBBSihave(bbsnnrp, -1, mid) == -1) {
            fprintf(stderr, "post failed\n");
            }
        bbsnnrp->nnrpin = oldfp;
        fclose(tmpfp);
        *mid = '\0';
        tmpfp = fopen(tmpfilename, "w");
        fprintf(tmpfp, "%s\r\n", buffer);
        break;
        } else {
        fprintf(tmpfp, "%s\r\n", buffer);
        }
    }
    if (!pathagain)
        break;
    }
    if (!pathagain && tmpfp) {
    FILE           *oldfp = bbsnnrp->nnrpin;
    fputs(".\r\n", tmpfp);
    fclose(tmpfp);
    fprintf(stderr, "Try to post ...\n");
    tmpfp = fopen(tmpfilename, "r");
    bbsnnrp->nnrpin = tmpfp;
    if (!discard)
        if (INNBBSihave(bbsnnrp, -1, mid) == -1) {
        fprintf(stderr, "post failed\n");
        }
    bbsnnrp->nnrpin = oldfp;
    fclose(tmpfp);
    }
    if (isfile(tmpfilename)) {
    unlink(tmpfilename);
    }
}

static char    *ACT_BUF, *RC_BUF;
int             ACT_COUNT;

initrcfiles(bbsnnrp)
    nnrp_t         *bbsnnrp;
{
    FILE           *actfp, *rcfp;
    char            buff[1024];
    int             actfd, i, count, actcount = 0, rcount = 0, maxcount;
    struct stat     st;
    char           *actlistptr, *ptr;

    actfd = open(bbsnnrp->activefile, O_RDWR);
    if (actfd < 0) {
    fprintf(stderr, "can't read/write %s\n", bbsnnrp->activefile);
    exit(1);
    }
    if (fstat(actfd, &st) != 0) {
    fprintf(stderr, "can't stat %s\n", bbsnnrp->activefile);
    exit(1);
    }
    bbsnnrp->actfd = actfd;
    bbsnnrp->actsize = st.st_size;
#ifdef USE_MMAP
    bbsnnrp->actpointer = mmap(0, st.st_size, PROT_WRITE | PROT_READ,
                   MAP_SHARED, actfd, 0);
    if (bbsnnrp->actpointer == (char *)-1) {
    fprintf(stderr, "mmap error \n");
    exit(1);
    }
#else
    if (bbsnnrp->actpointer == NULL) {
    bbsnnrp->actpointer = (char *)mymalloc(st.st_size);
    } else {
    bbsnnrp->actpointer = (char *)myrealloc(bbsnnrp->actpointer, st.st_size);
    }
    if (bbsnnrp->actpointer == NULL || read(actfd, bbsnnrp->actpointer, st.st_size) <= 0) {
    fprintf(stderr, "read act error \n");
    exit(1);
    }
#endif
    bbsnnrp->actend = bbsnnrp->actpointer + st.st_size;
    i = 0, count = 0;
    for (ptr = bbsnnrp->actpointer; ptr < bbsnnrp->actend && (actlistptr = (char *)strchr(ptr, '\n')) != NULL; ptr = actlistptr + 1, ACT_COUNT++) {
    if (*ptr == '\n')
        continue;
    if (*ptr == '#')
        continue;
    count++;
    }
    bbsnnrp->newsrc = (newsrc_t *) mymalloc(sizeof(newsrc_t) * count);
    ACT_COUNT = 0;
    for (ptr = bbsnnrp->actpointer; ptr < bbsnnrp->actend && (actlistptr = (char *)strchr(ptr, '\n')) != NULL; ptr = actlistptr + 1) {
    register newsrc_t *rcptr;
    char           *nptr;
    /**actlistptr = '\0';*/
    if (*ptr == '\n')
        continue;
    if (*ptr == '#')
        continue;
    rcptr = &bbsnnrp->newsrc[ACT_COUNT];
    rcptr->nameptr = NULL;
    rcptr->namelen = 0;
    rcptr->lowptr = NULL;
    rcptr->lowlen = 0;
    rcptr->highptr = NULL;
    rcptr->highlen = 0;
    rcptr->modeptr = NULL;
    rcptr->low = 0;
    rcptr->high = 0;
    rcptr->mode = 'y';
    for (nptr = ptr; *nptr && isspace(*nptr);)
        nptr++;
    if (nptr == actlistptr)
        continue;
    rcptr->nameptr = nptr;
    for (nptr++; *nptr && !isspace(*nptr);)
        nptr++;
    rcptr->namelen = (int)(nptr - rcptr->nameptr);
    if (nptr == actlistptr)
        continue;
    for (nptr++; *nptr && isspace(*nptr);)
        nptr++;
    if (nptr == actlistptr)
        continue;
    rcptr->highptr = nptr;
    rcptr->high = atol(nptr);
    for (nptr++; *nptr && !isspace(*nptr);)
        nptr++;
    rcptr->highlen = (int)(nptr - rcptr->highptr);
    if (nptr == actlistptr)
        continue;
    for (nptr++; *nptr && isspace(*nptr);)
        nptr++;
    if (nptr == actlistptr)
        continue;
    rcptr->lowptr = nptr;
    rcptr->low = atol(nptr);
    for (nptr++; *nptr && !isspace(*nptr);)
        nptr++;
    rcptr->lowlen = (int)(nptr - rcptr->lowptr);
    if (nptr == actlistptr)
        continue;
    for (nptr++; *nptr && isspace(*nptr);)
        nptr++;
    if (nptr == actlistptr)
        continue;
    rcptr->mode = *nptr;
    rcptr->modeptr = nptr;
    ACT_COUNT++;
    }
}

initsockets(server, bbsnnrp, type)
    char           *server;
    nnrp_t         *bbsnnrp;
    char           *type;
{
    int             nnrpfd;
    int             innbbsfd;
    if (AskLocal) {
    innbbsfd = unixclient(DefaultPath, "tcp");
    if (innbbsfd < 0) {
        fprintf(stderr, "Connect to %s error. You may not run innbbsd\n", LOCALDAEMON);
        /*
         * unix connect fail, may run by inetd, try to connect to local
         * once
         */
        innbbsfd = inetclient("localhost", DefaultPort, "tcp");
        if (innbbsfd < 0) {
        exit(2);
        }
        close(innbbsfd);
        /* try again */
        innbbsfd = unixclient(DefaultPath, "tcp");
        if (innbbsfd < 0) {
        exit(3);
        }
    }
    verboselog("INNBBS connect to %s\n", DefaultPath);
    } else {
    innbbsfd = inetclient(DefaultRemoteHost, DefaultPort, "tcp");
    if (innbbsfd < 0) {
        fprintf(stderr, "Connect to %s at %s error. Remote Server not Ready\n", DefaultRemoteHost, DefaultPort);
        exit(2);
    }
    verboselog("INNBBS connect to %s\n", DefaultRemoteHost);
    }
    if (type == StdinInputType) {
    bbsnnrp->nnrpfd = 0;
    bbsnnrp->innbbsfd = innbbsfd;
    if ((bbsnnrp->nnrpin = fdopen(0, "r")) == NULL ||
        (bbsnnrp->nnrpout = fdopen(1, "w")) == NULL ||
        (bbsnnrp->innbbsin = fdopen(innbbsfd, "r")) == NULL ||
        (bbsnnrp->innbbsout = fdopen(innbbsfd, "w")) == NULL) {
        fprintf(stderr, "fdopen error\n");
        exit(3);
    }
    return;
    }
    nnrpfd = inetclient(server, "nntp", "tcp");
    if (nnrpfd < 0) {
    fprintf(stderr, " connect to %s error \n", server);
    exit(2);
    }
    verboselog("NNRP connect to %s\n", server);
    bbsnnrp->nnrpfd = nnrpfd;
    bbsnnrp->innbbsfd = innbbsfd;
    if ((bbsnnrp->nnrpin = fdopen(nnrpfd, "r")) == NULL ||
    (bbsnnrp->nnrpout = fdopen(nnrpfd, "w")) == NULL ||
    (bbsnnrp->innbbsin = fdopen(innbbsfd, "r")) == NULL ||
    (bbsnnrp->innbbsout = fdopen(innbbsfd, "w")) == NULL) {
    fprintf(stderr, "fdopen error\n");
    exit(3);
    }
}

closesockets()
{
    fclose(BBSNNRP.nnrpin);
    fclose(BBSNNRP.nnrpout);
    fclose(BBSNNRP.innbbsin);
    fclose(BBSNNRP.innbbsout);
    close(BBSNNRP.nnrpfd);
    close(BBSNNRP.innbbsfd);
}

updaterc(actptr, len, value)
    char           *actptr;
    int             len;
    ULONG           value;
{
    for (actptr += len - 1; len-- > 0;) {
    *actptr-- = value % 10 + '0';
    value /= 10;
    }
}

/*
 * if old file is empty, don't need to update prevent from disk full
 */
int
myrename(old, new)
    char           *old, *new;
{
    struct stat     st;
    if (stat(old, &st) != 0)
    return -1;
    if (st.st_size <= 0)
    return -1;
    return rename(old, new);
}

flushrc(bbsnnrp)
    nnrp_t         *bbsnnrp;
{
    int             backfd;
    char           *bak1;
    if (bbsnnrp->actdirty == 0)
    return;
    bak1 = (char *)strdup((char *)fileglue("%s.BAK", bbsnnrp->activefile));
    if (isfile(bak1)) {
    myrename(bak1, (char *)fileglue("%s.BAK.OLD", bbsnnrp->activefile));
    }
#ifdef USE_MMAP
    if ((backfd = open((char *)fileglue("%s.BAK", bbsnnrp->activefile), O_WRONLY | O_TRUNC | O_CREAT, 0664)) < 0 || write(backfd, bbsnnrp->actpointer, bbsnnrp->actsize) < bbsnnrp->actsize)
#else
    myrename(bbsnnrp->activefile, bak1);
    if ((backfd = open(bbsnnrp->activefile, O_WRONLY | O_TRUNC | O_CREAT, 0664)) < 0 || write(backfd, bbsnnrp->actpointer, bbsnnrp->actsize) < bbsnnrp->actsize)
#endif
    {
    char            emergent[128];
    sprintf(emergent, "/tmp/bbsnnrp.%d.active", getpid());
    fprintf(stderr, "write to backup active fail. Maybe disk full\n");
    fprintf(stderr, "try to write in %s\n", emergent);
    if ((backfd = open(emergent, O_WRONLY | O_TRUNC | O_CREAT, 0644)) < 0 || write(backfd, bbsnnrp->actpointer, bbsnnrp->actsize) < bbsnnrp->actsize) {
        fprintf(stderr, "write to %sfail.\n", emergent);
    } else {
        close(backfd);
    }
    /* if write fail, should leave */
    /* exit(1); */
    } else {
    close(backfd);
    }
    free(bak1);
    bbsnnrp->actdirty = 0;
}

writerc(bbsnnrp)
    nnrp_t         *bbsnnrp;
{
    if (bbsnnrp->actpointer) {
    flushrc(bbsnnrp);
#ifdef USE_MMAP
    if (munmap(bbsnnrp->actpointer, bbsnnrp->actsize) < 0)
        fprintf(stderr, "can't unmap\n");
    /* free(bbsnnrp->actpointer); */
    bbsnnrp->actpointer = NULL;
#endif
    if (close(bbsnnrp->actfd) < 0)
        fprintf(stderr, "can't close actfd\n");
    }
}

static FILE    *Xhdrfp;
static char     NNRPbuffer[4096];
static char     INNBBSbuffer[4096];

char           *
NNRPgets(string, len, fp)
    char           *string;
    int             len;
    FILE           *fp;
{
    char           *re = fgets(string, len, fp);
    char           *ptr;
    if (re != NULL) {
    if ((ptr = (char *)strchr(string, '\r')) != NULL)
        *ptr = '\0';
    if ((ptr = (char *)strchr(string, '\n')) != NULL)
        *ptr = '\0';
    }
    return re;
}

int 
NNRPstat(bbsnnrp, artno, mid)
    nnrp_t         *bbsnnrp;
    ULONG           artno;
    char          **mid;
{
    char           *ptr;
    int             code;

    *mid = NULL;
    fprintf(bbsnnrp->nnrpout, "STAT %d\r\n", artno);
    fflush(bbsnnrp->nnrpout);
    verboselog("nnrpPut: STAT %d\n", artno);
    NNRPgets(NNRPbuffer, sizeof NNRPbuffer, bbsnnrp->nnrpin);
    verboselog("nnrpGet: %s\n", NNRPbuffer);

    ptr = (char *)strchr(NNRPbuffer, ' ');
    if (ptr != NULL)
    *ptr++ = '\0';
    code = atoi(NNRPbuffer);
    ptr = (char *)strchr(ptr, ' ');
    if (ptr != NULL)
    *ptr++ = '\0';
    *mid = ptr;
    ptr = (char *)strchr(ptr, ' ');
    if (ptr != NULL)
    *ptr++ = '\0';
    return code;
}

int
NNRPxhdr(pattern, bbsnnrp, i, low, high)
    char           *pattern;
    nnrp_t         *bbsnnrp;
    int             i;
    ULONG           low, high;
{
    newsrc_t       *rcptr = &bbsnnrp->newsrc[i];
    int             size, code;

    Xhdrfp = bbsnnrp->nnrpin;
    fprintf(bbsnnrp->nnrpout, "XHDR %s %d-%d\r\n", pattern, low, high);
#ifdef BBSNNRPDEBUG
    printf("XHDR %s %d-%d\r\n", pattern, low, high);
#endif
    fflush(bbsnnrp->nnrpout);
    verboselog("nnrpPut: XHDR %s %d-%d\n", pattern, low, high);
    NNRPgets(NNRPbuffer, sizeof NNRPbuffer, bbsnnrp->nnrpin);
    verboselog("nnrpGet: %s\n", NNRPbuffer);
    code = atoi(NNRPbuffer);
    return code;
}

int 
NNRPxhdrget(artno, mid, iscontrol)
    int            *artno;
    char          **mid;
    int             iscontrol;
{
    *mid = NULL;
    *artno = 0;
    if (NNRPgets(NNRPbuffer, sizeof NNRPbuffer, Xhdrfp) == NULL)
    return 0;
    else {
    char           *ptr, *s;
    if (strcmp(NNRPbuffer, ".") == 0)
        return 0;
    ptr = (char *)strchr(NNRPbuffer, ' ');
    if (!ptr)
        return 1;
    *ptr++ = '\0';
    *artno = atol(NNRPbuffer);
    if (iscontrol) {
        ptr = (char *)strchr(s = ptr, ' ');
        if (!ptr)
        return 1;
        *ptr++ = '\0';
        if (strcmp(s, "cancel") != 0)
        return 1;
    }
    *mid = ptr;
    return 1;
    }
}

int 
INNBBSstat(bbsnnrp, i, mid)
    nnrp_t         *bbsnnrp;
    int             i;
    char           *mid;
{
    newsrc_t       *rcptr = &bbsnnrp->newsrc[i];
    int             size, code;

    fprintf(bbsnnrp->innbbsout, "STAT %s\r\n", mid);
    fflush(bbsnnrp->innbbsout);
    verboselog("innbbsPut: STAT %s\n", mid);
    NNRPgets(INNBBSbuffer, sizeof INNBBSbuffer, bbsnnrp->innbbsin);
    verboselog("innbbsGet: %s\n", INNBBSbuffer);
    return atol(INNBBSbuffer);
}

int 
INNBBSihave(bbsnnrp, artno, mid)
    nnrp_t         *bbsnnrp;
    ULONG           artno;
    char           *mid;
{
    int             size, code;
    int             header = 1;

    if (DefaultNntpProtocol == NntpPostProtocol) {
    fprintf(bbsnnrp->innbbsout, "POST\r\n");
    fflush(bbsnnrp->innbbsout);
    verboselog("innbbsPut: POST %s\n", mid);
    } else {
    fprintf(bbsnnrp->innbbsout, "IHAVE %s\r\n", mid);
    fflush(bbsnnrp->innbbsout);
    verboselog("innbbsPut: IHAVE %s\n", mid);
    }
    if (NNRPgets(INNBBSbuffer, sizeof INNBBSbuffer, bbsnnrp->innbbsin) == NULL) {
    return -1;
    }
    verboselog("innbbsGet: %s\n", INNBBSbuffer);
#ifdef BBSNNRPDEBUG
    printf("ihave got %s\n", INNBBSbuffer);
#endif

    if (DefaultNntpProtocol == NntpPostProtocol) {
    if ((code = atol(INNBBSbuffer)) != NNTP_START_POST_VAL) {
        if (code == NNTP_POSTFAIL_VAL)
        return 0;
        else
        return -1;
    }
    } else {
    if ((code = atol(INNBBSbuffer)) != INNBBSihaveOK) {
        if (code == 435 || code == 437)
        return 0;
        else
        return -1;
    }
    }
    if (artno != -1) {
    fprintf(bbsnnrp->nnrpout, "ARTICLE %d\r\n", artno);
    verboselog("nnrpPut: ARTICLE %d\n", artno);
#ifdef BBSNNRPDEBUG
    printf("ARTICLE %d\r\n", artno);
#endif
    fflush(bbsnnrp->nnrpout);
    if (NNRPgets(NNRPbuffer, sizeof NNRPbuffer, bbsnnrp->nnrpin) == NULL) {
        return -1;
    }
    verboselog("nnrpGet: %s\n", NNRPbuffer);
#ifdef BBSNNRPDEBUG
    printf("article got %s\n", NNRPbuffer);
#endif
    if (atol(NNRPbuffer) != NNRParticleOK) {
        fputs(".\r\n", bbsnnrp->innbbsout);
        fflush(bbsnnrp->innbbsout);
        NNRPgets(INNBBSbuffer, sizeof INNBBSbuffer, bbsnnrp->innbbsin);
        verboselog("innbbsGet: %s\n", INNBBSbuffer);
        return 0;
    }
    }
    header = 1;
    while (fgets(NNRPbuffer, sizeof NNRPbuffer, bbsnnrp->nnrpin) != NULL) {
    if (strcmp(NNRPbuffer, "\r\n") == 0)
        header = 0;
    if (strcmp(NNRPbuffer, ".\r\n") == 0) {
        verboselog("nnrpGet: .\n");
        fputs(NNRPbuffer, bbsnnrp->innbbsout);
        fflush(bbsnnrp->innbbsout);
        verboselog("innbbsPut: .\n");
        if (NNRPgets(INNBBSbuffer, sizeof INNBBSbuffer, bbsnnrp->innbbsin) == NULL)
        return -1;
        verboselog("innbbsGet: %s\n", INNBBSbuffer);
#ifdef BBSNNRPDEBUG
        printf("end ihave got %s\n", INNBBSbuffer);
#endif
        code = atol(INNBBSbuffer);
        if (DefaultNntpProtocol == NntpPostProtocol) {
        if (code == NNTP_POSTEDOK_VAL)
            return 1;
        if (code == NNTP_POSTFAIL_VAL)
            return 0;
        } else {
        if (code == 235)
            return 1;
        if (code == 437 || code == 435)
            return 0;
        }
        break;
    }
    if (DefaultNntpProtocol == NntpPostProtocol &&
    header && strncasecmp(NNRPbuffer, "NNTP-Posting-Host: ", 19) == 0) {
        fprintf(bbsnnrp->innbbsout, "X-%s", NNRPbuffer);
    } else {
        fputs(NNRPbuffer, bbsnnrp->innbbsout);
    }
    }
    fflush(bbsnnrp->innbbsout);
    return -1;
}

int
NNRPgroup(bbsnnrp, i, low, high)
    nnrp_t         *bbsnnrp;
    int             i;
    ULONG          *low, *high;
{
    newsrc_t       *rcptr = &bbsnnrp->newsrc[i];
    int             size, code;
    ULONG           tmp;

    fprintf(bbsnnrp->nnrpout, "GROUP %-.*s\r\n",
        rcptr->namelen, rcptr->nameptr);
    printf("GROUP %-.*s\r\n", rcptr->namelen, rcptr->nameptr);
    verboselog("nnrpPut: GROUP %-.*s\n", rcptr->namelen, rcptr->nameptr);
    fflush(bbsnnrp->nnrpout);
    NNRPgets(NNRPbuffer, sizeof NNRPbuffer, bbsnnrp->nnrpin);
    verboselog("nnrpGet: %s\n", NNRPbuffer);
    printf("%s\n", NNRPbuffer);
    sscanf(NNRPbuffer, "%d %d %ld %ld", &code, &size, low, high);
    if (*low > *high) {
    tmp = *low;
    *low = *high;
    *high = tmp;
    }
    return code;
}


readnews(server, bbsnnrp)
    char           *server;
    nnrp_t         *bbsnnrp;
{
    int             i;
    char            buffer[4096];
    ULONG           low, high;

    fgets(buffer, sizeof buffer, bbsnnrp->innbbsin);
    verboselog("innbbsGet: %s", buffer);
    if (atoi(buffer) != INNBBSconnectOK) {
    fprintf(stderr, "INNBBS server not OK\n");
    return;
    }
#ifdef BBSNNRPDEBUG
    printf("%s", buffer);
#endif
    fgets(buffer, sizeof buffer, bbsnnrp->nnrpin);
    verboselog("nnrpGet: %s", buffer);
    if (buffer[0] != '2') {
    /*
     * if (atoi(buffer) != NNRPconnectOK && atoi(buffer) !=
     * NNTP_NOPOSTOK_VAL) {
     */
    fprintf(stderr, "NNRP server not OK\n");
    return;
    }
#ifdef BBSNNRPDEBUG
    printf("%s", buffer);
#endif
    fputs("MODE READER\r\n", bbsnnrp->nnrpout);
    fflush(bbsnnrp->nnrpout);
    verboselog("nnrpPut: MODE READER\n");
    fgets(buffer, sizeof buffer, bbsnnrp->nnrpin);
    verboselog("nnrpGet: %s", buffer);

    if (DefaultNntpProtocol == NntpPostProtocol) {
    fputs("MODE READER\r\n", bbsnnrp->innbbsout);
    fflush(bbsnnrp->innbbsout);
    verboselog("innbbsPut: MODE READER\n");
    fgets(buffer, sizeof buffer, bbsnnrp->innbbsin);
    verboselog("innbbsGet: %s", buffer);
    }
#ifdef BBSNNRPDEBUG
    printf("%s", buffer);
#endif

    if (StatHistory == 0) {
    fputs("MIDCHECK OFF\r\n", bbsnnrp->innbbsout);
    fflush(bbsnnrp->innbbsout);
    verboselog("innbbsPut: MIDCHECK OFF\n");
    fgets(buffer, sizeof buffer, bbsnnrp->innbbsin);
    verboselog("innbbsGet: %s", buffer);
    }
    bbsnnrp->actdirty = 0;
    for (i = 0; i < ACT_COUNT; i++) {
    int             code = NNRPgroup(bbsnnrp, i, &low, &high);
    newsrc_t       *rcptr = &bbsnnrp->newsrc[i];
    int             j;
    ULONG           artno;
    char           *mid;
    int             artcount;

#ifdef BBSNNRPDEBUG
    printf("got reply %d %ld %ld\n", code, low, high);
#endif
    artcount = 0;
    if (code == 411) {
        FILE           *ff = fopen(BBSHOME "/innd/log/badgroup.log", "a");
        fprintf(ff, "%s\t%-.*s\r\n", server, rcptr->namelen, rcptr->nameptr);
        fclose(ff);
    } else if (code == NNRPGroupOK) {
        int             xcount;
        ULONG           maxartno = rcptr->high;
        int             isCancelControl = (strncmp(rcptr->nameptr, "control", rcptr->namelen) == 0)
        ||
        (strncmp(rcptr->nameptr, "control.cancel", rcptr->namelen) == 0);

        /* less than or equal to high, for server renumber */
        if (rcptr->low != low) {
        bbsnnrp->actdirty = 1;
        rcptr->low = low;
        updaterc(rcptr->lowptr, rcptr->lowlen, rcptr->low);
        }
        if (ResetActive) {
        if (rcptr->high != high) {
            bbsnnrp->actdirty = 1;
            rcptr->high = high;
            updaterc(rcptr->highptr, rcptr->highlen, rcptr->high);
        }
        } else if (rcptr->high < high) {
        int             xhdrcode;
        ULONG           maxget = high;
        int             exception = 0;
        if (rcptr->high < low) {
            bbsnnrp->actdirty = 1;
            rcptr->high = low;
            updaterc(rcptr->highptr, rcptr->highlen, low);
        }
        if (high > rcptr->high + Max_Stats) {
            maxget = rcptr->high + Max_Stats;
        }
        if (isCancelControl)
            xhdrcode = NNRPxhdr("Control", bbsnnrp, i, rcptr->high + 1, maxget);
        else
            xhdrcode = NNRPxhdr("Message-ID", bbsnnrp, i, rcptr->high + 1, maxget);

        maxartno = maxget;
        if (xhdrcode == NNRPXhdrOK) {
            while (NNRPxhdrget(&artno, &mid, isCancelControl)) {
            /* #define DEBUG */
#ifdef DEBUG
            printf("no %d id %s\n", artno, mid);
#endif
            if (artcount < Max_Arts) {
                if (mid != NULL && !isCancelControl) {
                if (!StatHistory || INNBBSstat(bbsnnrp, i, mid) != INNBBSstatOK) {
                    printf("** %d ** %d need it %s\n", artcount, artno, mid);
                    XHDR[artcount].artno = artno;
                    XHDR[artcount].header = restrdup(XHDR[artcount].header, mid);
                    /* INNBBSihave(bbsnnrp,i,artno,mid); */
                    /* to get it */
                    artcount++;
                }
                } else if (mid != NULL) {
                if (INNBBSstat(bbsnnrp, i, mid) == INNBBSstatOK) {
                    printf("** %d ** %d need cancel %s\n", artcount, artno, mid);
                    XHDR[artcount].artno = artno;
                    XHDR[artcount].header = restrdup(XHDR[artcount].header, mid);
                    artcount++;
                }
                }
                maxartno = artno;
            }
            }
        }       /* while xhdr OK */
        exception = 0;
        for (xcount = 0; xcount < artcount; xcount++) {
            ULONG           artno;
            char           *mid;
            artno = XHDR[xcount].artno;
            mid = XHDR[xcount].header;
            if (isCancelControl) {
            if (NNRPstat(bbsnnrp, artno, &mid) == NNRPstatOK) {
            }
            }
            printf("** %d ** %d i have it %s\n", xcount, artno, mid);
            if (!ResetActive && mid != NULL)
            exception = INNBBSihave(bbsnnrp, artno, mid);
            if (exception == -1)
            break;
            if (rcptr->high != artno) {
            rcptr->high = artno;
            updaterc(rcptr->highptr, rcptr->highlen, rcptr->high);
            }
        }
        if (rcptr->high != maxartno && exception != -1) {
            bbsnnrp->actdirty = 1;
            rcptr->high = maxartno;
            updaterc(rcptr->highptr, rcptr->highlen, maxartno);
        }
        }
    }
    /*
     * flushrc(bbsnnrp);
     */
    }
    fprintf(bbsnnrp->innbbsout, "quit\r\n");
    fprintf(bbsnnrp->nnrpout, "quit\r\n");
    fflush(bbsnnrp->innbbsout);
    fflush(bbsnnrp->nnrpout);
    fgets(NNRPbuffer, sizeof NNRPbuffer, bbsnnrp->nnrpin);
    fgets(INNBBSbuffer, sizeof INNBBSbuffer, bbsnnrp->innbbsin);

    /*
     * bbsnnrp->newsrc[0].high = 1900; updaterc(bbsnnrp->newsrc[0].highptr,
     * bbsnnrp->newsrc[0].highlen, bbsnnrp->newsrc[0].high);
     */
}

INNBBSDhalt()
{
}