/* * This part was hacked by Harald Kipp * * Bug reports should be sent to * * harald@os2point.ping.de * harald@haport.sesam.com * Fido: 2:2448/434 * * This module contains routines to process an article file. * */ #include #include #include #include #include #include #include #include #include #include "config.h" #include "rnews.h" static void compressproc(FILE *fp, char *cmd, char *buf, size_t buflen, size_t cc); static void batchproc(FILE *fp, char *buf, size_t buflen, long art_size); static void singleproc(FILE *fp, char *buf, size_t buflen, long art_size); static int build_xref(char *xref_line, char **ngarray); static int copy_article(char *filename, char **ngarray); static int proc_control(char *cmd); /************************************************************************/ /* */ /* artfileproc */ /* */ /* Processes an article file. */ /* */ /* Parameter Description */ /* ------------ ------------------------------------------------------- */ /* fp Filepointer to the article file. */ /* ------------ ------------------------------------------------------- */ /* Return value None */ /* */ /************************************************************************/ void artfileproc(FILE *fp) { size_t buflen = PROCBUFSIZE; char *buf = malloc(buflen); /* * Read the first two byte to determine the type of this article file */ if(fread(buf, 1, 2, fp) == 2) { if(DOLOG(LOG_READ)) lprintf("Read 2 bytes from article: x'%02x x'%02x", (unsigned char)buf[0], (unsigned char)buf[1]); /* * If the first to bytes are "#!" then we found a batch */ if(*buf == '#' && *(buf + 1) == '!') { /* * Read the rest of the first line to distinguish between * compressed and non-compressed batches. */ if(fgets(buf, buflen, fp)) { if(DOLOG(LOG_READ)) lprintf("Read rest of the line from article: '%s'", buf); } else if(DOLOG(LOG_READ)) lprintf("Failed to read rest of the line from article"); if(strnicmp(buf + 2, "unbatch", 7) == 0) /* * We found a compressed batch. At this point * the first line of the file was read and the * rest contains the pure compressed batch. With * this we recursively call this procedure again. */ artfileproc(fp); else if(strnicmp(buf + 1, "rnews", 5) == 0) /* * We found a non-compressed batch if the first * line is "#! rnews ", where is the * size of the next article in bytes. More articles * might follow. */ batchproc(fp, buf, buflen, atol(buf + 6)); else lprintf("Article file has unknown format"); } else if(*buf == '\x1F') { switch(*(buf + 1)) { case '\x9D': if(DOLOG(LOG_READ)) lprintf("Batch is compressed"); compressproc(fp, cfg.uncompresscall, buf, buflen, 2); break; case '\x8B': if(DOLOG(LOG_READ)) lprintf("Batch is gzipped"); compressproc(fp, cfg.gunzipcall, buf, buflen, 2); break; default: lprintf("Compression method of article file is unknown"); break; } } else { *(buf + 2) = '\0'; singleproc(fp, buf, buflen, 0); } } else lprintf("Failed reading article file header"); free(buf); } /************************************************************************/ /* */ /* compressproc */ /* */ /* Processes a compressed article file. */ /* */ /* Parameter Description */ /* ------------ ------------------------------------------------------- */ /* fp Filepointer to the article file. */ /* */ /* cmd Points to a string that specifies command line to un- */ /* compress the file. */ /* */ /* buf This buffer will be used by this procedure to read */ /* the compressed file. It may already contain the */ /* first part of the file (see parameter cc). */ /* */ /* buflen Size of buf. */ /* */ /* cc Number of valid characters in buf. These characters */ /* may have been read from the file by the caller into */ /* buf. */ /* ------------ ------------------------------------------------------- */ /* Return value None */ /* */ /************************************************************************/ static void compressproc(FILE *fp, char *cmd, char *buf, size_t buflen, size_t cc) { char *tname = tempnam(NULL, "RNEWS"); char *cmdline = malloc(strlen(cmd) + strlen(tname) + 3); FILE *ft; int rc; strcat(strcat(strcpy(cmdline, cmd), " >"), tname); if(DOLOG(LOG_READ)) lprintf("Calling '%s'", cmdline); if((ft = _popen(cmdline, "wb")) != NULL) { do { fwrite(buf, 1, cc, ft); cc = fread(buf, 1, buflen, fp); if(cc && DOLOG(LOG_READ)) lprintf("Read another %u bytes from compressed batch", cc); } while(cc); if((rc = _pclose(ft)) == 0) { if((ft = xopen(tname, "rb")) != NULL) { artfileproc(ft); fclose(ft); if(unlink(tname)) lperror(tname); } else lperror(tname); } else lprintf("%s returned %d", cmd, rc); } free(tname); free(cmdline); } /************************************************************************/ /* */ /* batchproc */ /* */ /* Processes a batched article file. */ /* */ /* Parameter Description */ /* ------------ ------------------------------------------------------- */ /* fp Filepointer to the article file. */ /* */ /* buf This buffer will be used by this procedure to read */ /* the compressed file. */ /* */ /* buflen Size of buf. */ /* */ /* art_size Size of the first article in this batch. */ /* ------------ ------------------------------------------------------- */ /* Return value None */ /* */ /************************************************************************/ static void batchproc(FILE *fp, char *buf, size_t buflen, long art_size) { int skipped; while(art_size) { skipped = 0; *buf = '\0'; if(DOLOG(LOG_READ)) lprintf("Processing batched article with %ld bytes", art_size); singleproc(fp, buf, buflen, art_size); /* * Search the next article. */ art_size = 0; while(!art_size && fgets(buf, buflen, fp)) { if(strnicmp(buf + 3, "rnews", 5) == 0) art_size = atol(buf + 8); else { skipped++; } } if(skipped) lprintf("%u lines skipped in batch", skipped); } free(buf); } /************************************************************************/ /* */ /* singleproc */ /* */ /* Processes a single article. */ /* */ /* Parameter Description */ /* ------------ ------------------------------------------------------- */ /* fp File pointer to the article file. */ /* */ /* buf This buffer will be used by this procedure to read */ /* the compressed file. It may already contain the */ /* first part of the file (see parameter cc). */ /* */ /* buflen Size of buf. */ /* */ /* art_size Size of the article or zero if we should read the */ /* whole file in which case an EOF will mark the end */ /* of this article. */ /* ------------ ------------------------------------------------------- */ /* Return value None */ /* */ /************************************************************************/ static void singleproc(FILE *fp, char *buf, size_t buflen, long art_size) { long rv = 0; int cc = buflen; char **ngarray; long hdr_size = read_header(fp, buf, &cc); if(DOLOG(LOG_READ)) lprintf("Read %ld bytes of header lines", hdr_size); if((ngarray = validate_header(buf + cc, buflen - cc)) != NULL) { FILE *ofp; char *tname = tempnam(NULL, "RNEWS"); if(DOLOG(LOG_WRITE)) lprintf("Article to temporary file %s", tname); if((ofp = xopen(tname, "wb")) != NULL) { rv += write_xref(ofp, cfg.mynode, ngarray); if(rv && DOLOG(LOG_WRITE)) lprintf("Wrote %ld bytes xref line", rv); rv += write_header(ofp); if(DOLOG(LOG_WRITE)) lprintf("Wrote %ld bytes of header lines", rv); if(art_size) { if(art_size > hdr_size) rv += copy_body(ofp, fp, art_size - hdr_size); else { rv += fprintf(ofp, "Lines: 0\n\n"); if(DOLOG(LOG_READ | LOG_WRITE)) lprintf("Message body is empty"); } } else rv += copy_body(ofp, fp, 0); fclose(ofp); if(DOLOG(LOG_WRITE)) lprintf("Total size of written article is %ld bytes", rv); n_proc++; if(header[ID_Control].info) proc_control(header[ID_Control].info); else copy_article(tname, ngarray); if(unlink(tname)) lperror(tname); } free(tname); if(rv) { if(DOLOG(LOG_HISTORY)) lprintf("Add new history entry: '%s'", header[ID_Message_ID].info); addhistart(cfg.historyfile, header[ID_Message_ID].info, ngarray, rv); } free_ngarray(ngarray); } } /************************************************************************/ /* */ /* copy_article */ /* */ /* Copy artfile into dir for ng and update active file */ /* */ /************************************************************************/ static int copy_article(char *filename, char **ngarray) { int i; char snum[MAX_LONGSTRING]; FILE *fc; FILE *fp; char *cp; long himsg; char *artpath = malloc(_MAX_PATH); /* * Copy article to each group */ for(i = 0; ngarray[i] != NULL; i++) { if(find_active(NULL, ngarray[i], NULL, &himsg)) { if(DOLOG(LOG_READ | LOG_WRITE | LOG_ACTIVE)) lprintf("Deliver article to %s", ngarray[i]); /* * Make the path for a newsgroup */ strcpy(artpath, cfg.newsdir); strcat(artpath, "\\"); strcat(artpath, ngarray[i]); strcat(artpath, "\\"); strcat(artpath, ltoa(himsg + 1, snum, 10)); cp = artpath; while((cp = strchr(artpath, '.')) != NULL) *cp++ = '\\'; makepath(artpath); while(!access(artpath, 0)) { /* * Now we are in big trouble because the active * file has probably gotten out of sync. We try * to recover by requesting a new article number. * Since this is an unusual case we accept that * the xref line has already been created with * wrong article numbers. */ lprintf("Active file out of sync at %s", ngarray[i]); upd_active(ngarray[i], 0, 1); find_active(NULL, ngarray[i], NULL, &himsg); if(DOLOG(LOG_ACTIVE)) lprintf("Try article number %lu", himsg); strcpy(strrchr(artpath, '\\') + 1, ltoa(himsg + 1, snum, 10)); } /* * Copy copy article in textmode. * Someday we'll support compression, but * in that case all other utilities (like * expire etc.) must support this too. */ if(DOLOG(LOG_READ | LOG_WRITE)) lprintf("Copy '%s' to '%s'", filename, artpath); if((fc = xopen(artpath, "wt")) != NULL) { if((fp = xopen(filename, "rb")) != NULL) { int cc; char *xbuf = malloc(COPYBUFSIZE); while((cc = fread(xbuf, 1, COPYBUFSIZE, fp)) > 0) fwrite(xbuf, 1, cc, fc); fclose(fp); free(xbuf); } fclose(fc); if(DOLOG(LOG_ACTIVE)) lprintf("Increment last article number"); upd_active(ngarray[i], 0, 1); } else lperror(artpath); } } free(artpath); return(0); } /************************************************************************/ /* */ /* */ /************************************************************************/ static int proc_control(char *cmd) { char *cpl; char *cpc = cmd; char *line = malloc(512); lprintf("Control message: %s", cmd); strcpy(line, cfg.controlcall); cpl = strchr(line, '\0'); while(*cpc) { while(*cpc && (*cpc == ' ' || *cpc == '\t')) cpc++; if(*cpc) { *cpl++ = ' '; *cpl++ = '"'; while(*cpc && *cpc != ' ' && *cpc != '\t') *cpl++ = *cpc++; *cpl++ = '"'; } } *cpl = '\0'; save_active(cfg.activefile); if(system(line)) lprintf("Failed to run %s", line); else if(load_active(cfg.activefile) <= 0) lprintf("No active groups found in %s", cfg.activefile); free(line); return(0); }