/* * This program was written by Harald Kipp using code from * * nntpxfer * * written by * * Brian Kantor and Stan Barber * * Bug reports related to THIS modified version should be sent to * * harald@os2point.ping.de * harald@sesam.com * Fido: 2:2448/434 * */ #define OS2 #include #include #include #include #include #undef min #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "chanx.h" /* * Globals */ char buf[4096]; int omitupdate = 0; /* 1 = don't update nntp.hostname */ int keepids = 0; /* 1 = don't delete iwant.hostname */ /* * Locals */ static CFGITEM cfgitm[] = { { "access" , 0, 4, NULL }, { "actimes" , 0, 4, NULL }, { "active" , 0, 4, NULL }, { "control" , 0, 4, NULL }, { "dupegroup" , 0, 4, NULL }, { "gunzip" , 0, 4, NULL }, { "history" , 0, 4, cfg.historyfile }, { "inews" , 0, 4, NULL }, { "junkgroup" , 0, 4, NULL }, { "mydomain" , 0, 4, NULL }, { "mynode" , 0, 4, NULL }, { "newsdir" , 0, 4, NULL }, { "newsserver", 0, 4, NULL }, { "rnews" , 0, 4, cfg.rnewscall }, { "spooldir" , 0, 4, cfg.spooldir }, { "uncompress", 0, 4, NULL } }; static void usage(void); /************************************************************************/ /* CHANX */ /* */ /* Connect to a remote site, send news to and get news from it. */ /* */ /************************************************************************/ int main(int argc, char *argv[]) { int server; int option; FILE *dtfile; /* where last xfer date/time stored */ char dstrb[BUFSIZ]; char dtname[_MAX_PATH]; char tmpname[_MAX_PATH]; char iwant[_MAX_PATH]; char batch[_MAX_PATH]; FILE *fpid; FILE *fpbt = NULL; long ldate; long ltime; long newdate = 0, newtime = 0; struct tm *now; time_t t_proc = time(NULL); int n_proc = 0; char **artfiles; char *cp; int canpost; char *ngs = NULL; /* * Set up timer */ tzset(); now = gmtime(&t_proc); newdate = ((time_t)(now -> tm_year) * 10000) + (((time_t)(now -> tm_mon) + 1) * 100) + (time_t)(now -> tm_mday); newtime = ((time_t)(now -> tm_hour) * 10000) + ((time_t)(now -> tm_min) * 100) + (time_t)(now -> tm_sec); /* * Set up initial configuration */ init_cfg(); /* * Initialize exit proc */ initexit(); /* * Open log file */ lopen(cfg.logfile); lprintf("Chanx %s - %s", version, __TIMESTAMP__); /* * Process command line options */ while((option = getopt(argc, argv, "?c:d:k:m:p:r:s:t:y:")) != EOF) { switch(option) { case 'c': strcpy(cfg.configfile, optarg); break; case 'd': cp = optarg; while(*cp) { switch(*cp) { /* * -df enables logfile flushing */ case 'f': cfg.logflg |= LOG_FLUSH; lflush(1); break; /* * Log received telegrams */ case 'r': cfg.logflg |= LOG_RECV; so_setlog(ENABLE_RLOG); break; /* * Log sent telegrams */ case 's': cfg.logflg |= LOG_SEND; so_setlog(ENABLE_SLOG); break; default: lprintf("Unknown option -d%c ignored", *cp); break; } cp++; } break; case 'k': cfg.sizelimit = atol(optarg) * 1024L; break; case 'm': cp = optarg; while(*cp) { switch(*cp) { case 'b': cfg.modflg |= MOD_BATCH; break; case 'c': cfg.modflg |= MOD_CONT; break; case 'e': cfg.modflg |= MOD_ENHANCE; break; case 'i': cfg.modflg |= MOD_IGNORE; break; case 'l': cfg.modflg |= MOD_DTLOCAL; /* undocumented hack */ break; case 'n': cfg.modflg |= MOD_NGLIST; break; case 'o': cfg.modflg |= MOD_OMITUPD; break; case 'p': cfg.modflg |= MOD_POST; break; case 'q': cfg.modflg |= MOD_QUERY; break; case 'r': cfg.modflg |= MOD_READER; break; case 's': cfg.modflg |= MOD_SLAVE; break; default: lprintf("Unknown option -m%c", *cp); printf("Unknown option -m%c\n", *cp); usage(); return(2); } cp++; } break; case 'p': strcpy(cfg.remoteport, optarg); break; case 'r': strcpy(cfg.rnewscall, optarg); break; case 's': strcpy(cfg.spooldir, optarg); break; case 't': time(&cfg.timelimit); cfg.timelimit += atol(optarg) * 60L; break; case 'y': strcpy(cfg.historyfile, optarg); break; default: lprintf("Unknown option -%c", option); printf("Unknown option -%c\n", option); case '?': usage(); return(2); } } /* * All command line options are processed, check if one * argument is left to give us a list of newsgroups. */ argc -= optind; argv += optind; /* * Process remaining command line arguments */ if (argc != 1 && argc != 4 && argc != 5) { usage(); return (2); } strcpy(cfg.remotehost, argv[0]); sprintf(dtname, "%s\\nntp.%s", cfg.workdir, cfg.remotehost); if (argc > 1) { if((dtfile = xopen(dtname, "rt")) != NULL) { fclose(dtfile); omitupdate = 1; lprintf("Out-of-sequence connect to %s", cfg.remotehost); } else lprintf("Virgin connect to %s", cfg.remotehost); ngs = strdup(argv[1]); ldate = atol(argv[2]); ltime = atol(argv[3]); if (argc > 4) strcpy(dstrb, argv[4]); else dstrb[0] = '\0'; } else { if((dtfile = xopen(dtname, "rt")) == NULL) { lperror(dtname); ngs = "*"; ldate = newdate; ltime = 0; dstrb[0] = '\0'; lprintf("%s not found", dtname); } else { if (fscanf(dtfile, "%s %ld %ld %s", buf, &ldate, <ime, dstrb) < 3) { lprintf("%s has invalid format", dtname); ngs = "*"; ldate = newdate; ltime = 0; dstrb[0] = '\0'; } else ngs = strdup(buf); fclose(dtfile); } } /* * Read configuration file */ ReadCfg(cfg.configfile, cfgitm, sizeof(cfgitm) / sizeof(CFGITEM)); if(!validate_cfg()) return(2); /* * Connect to the nntp daemon and get the greeting */ sock_init(); printf("Connecting %s\n", cfg.remotehost); if ((server = get_tcp_conn(cfg.remotehost, cfg.remoteport)) < 0) { lprintf("Could not connect to %s", cfg.remotehost); return (4); } if(recv_bline(server, buf) <= 0) { soclose(server); return (5); } if (buf[0] != '2') { lprintf("Server error: %s", buf); soclose(server); return (5); } canpost = (atoi(buf) != 201); puts(buf + 4); /* * Tell them we're a slave/reader process */ if(DOMOD(MOD_SLAVE)) { if(so_printf(server, "SLAVE\r\n") < 0) { soclose(server); return (5); } if(recv_bline(server, buf) <= 0) { soclose(server); return (5); } if (buf[0] != '2') lprintf("Warning: %s", buf); } if(DOMOD(MOD_READER)) { if(so_printf(server, "MODE READER\r\n") < 0) { soclose(server); return (5); } if(recv_bline(server, buf) <= 0) { soclose(server); return (5); } if (buf[0] != '2') lprintf("Warning: %s", buf); } /* * Query server's gmt */ if(!DOMOD(MOD_DTLOCAL)) { if(so_printf(server, "DATE\r\n") < 0) { soclose(server); return (5); } if(recv_bline(server, buf) <= 0) { soclose(server); return (5); } if (buf[0] != '1') lprintf("Warning: %s", buf); else { newtime = atol(&buf[12]); buf[12] = '\0'; newdate = atol(&buf[6]); } } /* * Post articles */ if(!DOMOD(MOD_CONT) && !DOMOD(MOD_QUERY)) { if(canpost) { if((artfiles = scan_jobs(cfg.remotehost)) != NULL) { int i; char *txname = malloc(_MAX_PATH); printf("Posting...\n"); for(i = 0; artfiles[i]; i++) { sprintf(txname, "%s\\%s", cfg.spooldir, artfiles[i]); if(post(server, txname) == 1) { if(unlink(txname)) lperror(txname); } } free(txname); } else printf("Nothing to post\n"); } else printf("Server doesn't allow us to post\n"); } /* * Query lists */ if(DOMOD(MOD_NGLIST)) { int items; FILE *fp = fopen(cfg.newnwsgrps, "wt"); if(fp) { printf("Retrieving list of newsgroups"); items = query_list(server, fp, "LIST NEWSGROUPS\r\n"); fclose(fp); if(items > 0) { unlink(cfg.oldnwsgrps); rename(cfg.newsgroups, cfg.oldnwsgrps); rename(cfg.newnwsgrps, cfg.newsgroups); } else unlink(cfg.newnwsgrps); } else lperror(cfg.newnwsgrps); } /* * Query articles */ sprintf(iwant, "%s\\iwant.%s", cfg.workdir, cfg.remotehost); if(!DOMOD(MOD_CONT) && !DOMOD(MOD_POST)) { if((fpid = fopen(iwant, DOMOD(MOD_IGNORE) ? "wt" : "at")) == NULL) { lperror(iwant); return(3); } printf("Querying new articles since %06lu %06lu GMT\n", ldate, ltime); n_proc = query_ids(server, fpid, ngs, ldate, ltime, dstrb); fclose(fpid); } sprintf(batch, "%s\\batch.%s", cfg.workdir, cfg.remotehost); if(DOMOD(MOD_CONT) || n_proc > 0) { puts("Retrieving articles..."); if((fpid = fopen(iwant, "rt")) == NULL) { lperror(iwant); return(3); } if(!DOMOD(MOD_BATCH)) { if((fpbt = _popen(cfg.rnewscall, "wb")) == NULL) { lprintf("Failed to run %s, batch mode", cfg.rnewscall); cfg.modflg |= MOD_BATCH; } } if(DOMOD(MOD_BATCH)) { if((fpbt = fopen(batch, "ab")) == NULL) { fclose(fpid); lperror(batch); return(3); } } n_proc = query_arts(server, fpid, fpbt); if(!DOMOD(MOD_BATCH)) { int retcode = _pclose(fpbt); if(retcode) { omitupdate = 1; lprintf("%s returned error %d", cfg.rnewscall, retcode); } } else fclose(fpbt); if(keepids) { FILE *fptmp; sprintf(tmpname, "%s\\nwant.%s", cfg.workdir, cfg.remotehost); if((fptmp = fopen(tmpname, "wt")) != NULL) { while(fgets(buf, BUFSIZ, fpid)) fputs(buf, fptmp); fclose(fptmp); fclose(fpid); if(unlink(iwant)) lperror(iwant); rename(tmpname, iwant); } else { lperror(tmpname); fclose(fpid); } } else { fclose(fpid); unlink(iwant); } } /* * We're all done, so tell them goodbye */ so_printf(server, "QUIT\r\n"); soclose(server); puts("Connection closed"); /* * Spooling received batch */ if(DOMOD(MOD_BATCH) && n_proc > 0) { puts("Spooling..."); sprintf(buf, "%s < %s", cfg.rnewscall, batch); if (system(buf)) lperror(cfg.rnewscall); else if(unlink(batch)) lperror(batch); } /* * Update timestamp file */ if (!omitupdate && !DOMOD(MOD_OMITUPD)) { sprintf(buf, "%s %06lu %06lu %s\n", ngs, newdate, newtime, dstrb); dtfile = fopen(dtname, "wt"); if (dtfile == NULL) { lperror(dtname); return(3); } fputs(buf, dtfile); fclose(dtfile); } t_proc = time(NULL) - t_proc; lprintf("Chanx processed %d article(s) in %lu second(s)%c", n_proc, t_proc, '\n'); lclose(); return (n_proc ? 0 : 1); } /************************************************************************/ /* */ /************************************************************************/ static void usage(void) { puts("Usage: chanx [options] host [groups YYMMDD HHMMSS []]\n" "options:\n" " -c configuration file (changi.cfg)\n" " -d logfile flags\n" " -k stop after kBytes\n" " -m operation mode flags\n" " -p port number to connect (119)\n" " -r rnews program call (rnews)\n" " -s spool directory (spool)\n" " -t stop after minutes\n" " -y history file (history)\n\n" "mode flags: logfile flags:\n" " b batch mode f enable flushing\n" " c continuation mode r telegrams received\n" " e enhanced mode s telegrams sent\n" " i ignore previous ids\n" " n get newsgroups list\n" " o omit nntp.host update\n" " p posting only, no query\n" " q query only, no posting\n" " r send MODE READER\n" " s send SLAVE"); lprintf("Chanx usage displayed"); lclose(); }