#include #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #ifdef HAVE_DIRENT_H # include #else # define dirent direct # ifdef HAVE_SYS_NDIR_H # include # endif # ifdef HAVE_SYS_DIR_H # include # endif #endif #ifdef HAVE_LIMITS_H #include #endif #include "suck_config.h" #ifdef HAVE_REGEX_H #include #endif #ifdef DMALLOC #include #endif #include "both.h" #include "suck.h" #include "suckutils.h" #include "killfile.h" #include "phrases.h" #include "timer.h" #define GROUP_KEEP "keep" #define GROUP_DELETE "delete" /* word in param file parsed for on group line to signify keep or delete */ #define NEWSGROUP_HEADER "Newsgroups: " /* header for line to match group files to */ /* local function prototypes */ int get_chunk_mem(PMaster, unsigned long *len, int which, char **buf); int check_newsgroups(char *, const char *); void free_node(OneKill); int parse_a_file(const char *, const char *, POneKill, int, int, int); int pass_two(PKillStruct, int, const char *fname); int check_a_group(PMaster, POneKill, char *, char **); void print_debug(PKillStruct, const char *); void debug_one_kill(POneKill); void add_to_linkedlist(pmy_regex *, pmy_regex); pmy_regex regex_scan(char *, char, int, int, char); int regex_check(char *, pmy_regex, int); void initialize_one_kill(POneKill); #ifdef HAVE_REGEX_H const char regex_chars[] = "*[]()^$\\?."; /* characters which make it a regex */ #endif void display_skiparray(unsigned char [SIZEOF_SKIPARRAY]); int find_string(int, pmy_regex, char *); /*-------------------------------------------------------------------------*/ /* the enum must match the phrases in suckengl.c killf_reasons[] */ enum { REASON_NONE, REASON_TOOMANYLINES, REASON_NOTENUFLINES, REASON_NRGRPS, REASON_NOKEEP, REASON_TIE, REASON_HEADER, \ REASON_BODY, REASON_BODYBIG, REASON_BODYSMALL, REASON_NRXREF }; enum { CHECK_EXACT, CHECK_CHECK }; enum { CHUNK_HEADER, CHUNK_BODY }; const struct { int len; const char *name; int headerlen; const char *header; } Params[] = { {8, "HILINES=", 7, "Lines: " }, {9, "LOWLINES=", 7, "Lines: "}, {7, "NRGRPS=", 12, "Newsgroups: "}, {6, "GROUP=", 0, ""}, {6, "QUOTE=", 0, ""}, {8, "PROGRAM=", 0, ""}, {7, "HEADER:", 0, ""}, {5, "BODY:", 0, ""}, {9, "BODYSIZE>", 0, ""}, {9, "BODYSIZE<", 0, ""}, {21, "GROUP_OVERRIDE_MASTER", 0, ""}, /* string in masterfile to signify that group files override the master */ {17, "TIEBREAKER_DELETE", 0, ""}, /* what to do if match multi groups override default of keep */ {18, "USE_EXTENDED_REGEX", 0, ""}, /* do we use extended regular expressions */ {10, "NON_REGEX=", 0, ""}, {5, "PERL=", 0, ""}, {14, "XOVER_LOG_LONG", 0, ""}, /* make Xover killlog look like regular kill lo)*/ {7, "NRXREF=", 6, "Xref: "}, }; enum { PARAM_HILINE, PARAM_LOWLINE, PARAM_NRGRPS, PARAM_GROUP, PARAM_QUOTE, PARAM_PROGRAM,PARAM_HDRSCAN, PARAM_BODY, PARAM_BODYBIG, PARAM_BODYSMALL, PARAM_GRPOVERRIDE, PARAM_TIEDELETE, PARAM_E_REGEX, PARAM_NONREGEX, PARAM_PERL, PARAM_XOVER_LOG_LONG, PARAM_NRXREF}; #define NR_PARAMS ((int) (sizeof(Params)/sizeof(Params[0]))) /*--------------------------------------------------------------------------*/ PKillStruct parse_killfile(int which, int logfile_yn, int debug, int ignore_postfix) { FILE *fptr; char buf[MAXLINLEN+1]; int i, doprg = FALSE, mastergrp = RETVAL_OK, retval = TRUE; const char *filename; #ifdef PERL_EMBED int doperl = FALSE; #endif KillStruct *Masterkill = NULL; /* kill file is going to get three passes, 1st one to count how many group files to process */ /* so can allocate memory for all the group stuff. */ /* also check for group override of masterkillfile */ /* and process PROGRAM line if it exists. If we have one we don't do anything else */ /* 2nd pass will be to actually process the group files */ /* 3rd pass will be to process the master delete stuff */ /* first malloc our master structure */ if((Masterkill = malloc(sizeof(KillStruct))) == NULL) { error_log(ERRLOG_REPORT, killf_phrases[1], NULL); retval = FALSE; } else { /* now initialize everything */ Masterkill->logfp = NULL; Masterkill->grp_override = FALSE; Masterkill->tie_delete = FALSE; Masterkill->totgrps = 0; Masterkill->grps = NULL; Masterkill->killfunc = chk_msg_kill; Masterkill->pbody= NULL; Masterkill->bodylen = 0; Masterkill->use_extended_regex = FALSE; Masterkill->xover_log_long = FALSE; /* initialize the master struct */ Masterkill->child.Stdin = Masterkill->child.Stdout = -1; Masterkill->child.Pid = -1; initialize_one_kill(&(Masterkill->master)); #ifdef PERL_EMBED Masterkill->perl_int = NULL; #endif Masterkill->logyn = logfile_yn; /* which option to we use in call to full_path() ? Do we use the postfix or not? */ Masterkill->ignore_postfix = ( ignore_postfix == TRUE) ? FP_GET_NOPOSTFIX : FP_GET ; filename = ( which == KILL_XOVER ) ? N_XOVER : N_KILLFILE; if(debug == TRUE) { do_debug("Trying to read killfile: %s\n", full_path(Masterkill->ignore_postfix, FP_DATADIR, filename)); } /* FIRST PASS THRU MASTER KILLFILE - look for group delete/keeps and count em and check for PROGRAM call*/ if((fptr = fopen(full_path(Masterkill->ignore_postfix, FP_DATADIR, filename), "r")) == NULL) { /* this is not really an error, so don't report it as such */ retval = FALSE; } else { if(debug == TRUE) { do_debug("Pass 1 & 2 kill file: %s\n", full_path(Masterkill->ignore_postfix, FP_DATADIR, filename)); } while(fgets(buf, MAXLINLEN, fptr) != NULL && doprg == FALSE) { /* nuke nl, so file names are correct, etc */ i = strlen(buf); if(buf[i-1] == '\n') { buf[i-1] = '\0'; } if(debug == TRUE) { do_debug("Read kill file line: %s\n", buf); } if(strncmp(buf, Params[PARAM_GROUP].name, (size_t) Params[PARAM_GROUP].len) == 0) { Masterkill->totgrps++; /* how many group files must we process */ } if(strncmp(buf, Params[PARAM_PROGRAM].name, (size_t) Params[PARAM_PROGRAM].len) == 0) { #ifdef PERL_EMBED if(Masterkill->perl_int == NULL) { #endif doprg = killprg_forkit(Masterkill, &buf[Params[PARAM_PROGRAM].len], debug, which); #ifdef PERL_EMBED } #endif } if(strncmp(buf, Params[PARAM_GRPOVERRIDE].name, (size_t) Params[PARAM_GRPOVERRIDE].len) == 0) { Masterkill->grp_override = TRUE; } if(strncmp(buf, Params[PARAM_TIEDELETE].name, (size_t) Params[PARAM_TIEDELETE].len) == 0) { Masterkill->tie_delete = TRUE; } if(strncmp(buf, Params[PARAM_E_REGEX].name, (size_t) Params[PARAM_E_REGEX].len) == 0) { Masterkill->use_extended_regex = TRUE; } if(strncmp(buf, Params[PARAM_XOVER_LOG_LONG].name, (size_t) Params[PARAM_XOVER_LOG_LONG].len) == 0) { Masterkill->xover_log_long = TRUE; } #ifdef PERL_EMBED if(strncmp(buf, Params[PARAM_PERL].name, (size_t) Params[PARAM_PERL].len) == 0) { if(doprg == FALSE) { doperl = killperl_setup(Masterkill, &buf[Params[PARAM_PERL].len], debug, which); } } #endif } (void) fclose(fptr); #ifndef PERL_EMBED if(doprg == TRUE) { #else if(doprg == TRUE || Masterkill->perl_int != NULL) { #endif Masterkill->totgrps = 0; } else { /* SECOND PASS - call routine */ if(Masterkill->totgrps > 0) { if(pass_two(Masterkill, debug, filename) == RETVAL_ERROR) { retval = FALSE; } } /* THIRD PASS - process master delete stuff */ if(retval != FALSE && (mastergrp = parse_a_file(filename, "Master", &(Masterkill->master), debug, Masterkill->ignore_postfix, Masterkill->use_extended_regex)) == RETVAL_ERROR) { retval = FALSE; } } } /* do we have an error OR do we have empty killfiles */ if((retval == FALSE) || (mastergrp == RETVAL_EMPTYKILL && Masterkill->totgrps == 0 && doprg == FALSE)) { #ifdef PERL_EMBED if(Masterkill->perl_int == NULL) { #endif free_killfile(Masterkill); /* just in case any memory got allocated */ Masterkill = NULL; #ifdef PERL_EMBED } #endif } else if(debug == TRUE) { print_debug(Masterkill, filename); } } return Masterkill; } /*-------------------------------------------------------------------------------------------*/ int pass_two(PKillStruct killp, int debug, const char *filename ) { int retval = RETVAL_OK; FILE *fptr; char buf[MAXLINLEN]; int grpon = 0; size_t i; char *grpname, *grpfile, *delkeep; grpname = grpfile = delkeep = NULL; /* SECOND PASS - now that we know how many, we can allocate the space for em */ /* and then have parse_a_file read em in. */ if((killp->grps = calloc((size_t) killp->totgrps, sizeof(Group))) == NULL) { retval = RETVAL_ERROR; error_log(ERRLOG_REPORT, killf_phrases[1], NULL); } else if((fptr = fopen(full_path(killp->ignore_postfix, FP_DATADIR, filename), "r")) == NULL) { MyPerror(full_path(killp->ignore_postfix, FP_DATADIR, filename)); retval = RETVAL_ERROR; } else { while(retval == RETVAL_OK && fgets(buf, MAXLINLEN, fptr) != NULL) { if(strncmp(buf, Params[PARAM_GROUP].name, (size_t) Params[PARAM_GROUP].len) == 0 ) { /* now parse the line for the 3 required elements */ /* keep/delete group_name filename */ delkeep = &buf[Params[PARAM_GROUP].len]; if(strncmp(delkeep, GROUP_KEEP, (size_t) strlen(GROUP_KEEP)) == 0) { killp->grps[grpon].delkeep = DELKEEP_KEEP; } else if(strncmp(delkeep, GROUP_DELETE, (size_t) strlen(GROUP_DELETE)) == 0) { killp->grps[grpon].delkeep = DELKEEP_DELETE; } else { retval = RETVAL_ERROR; } if(retval == RETVAL_OK) { grpname = strchr(delkeep, ' '); /* find the space */ if(grpname == NULL) { retval = RETVAL_ERROR; } else { ++grpname; /* move past space */ grpfile = strchr(grpname, ' '); if(grpfile == NULL) { retval = RETVAL_ERROR; } else { *grpfile = '\0'; /* truncate the group name for easier copying later */ ++grpfile; /* nuke newline */ i = strlen(grpfile) - 1; if(grpfile[i] == '\n') { grpfile[i] = '\0'; } } } } if(retval == RETVAL_ERROR) { error_log(ERRLOG_REPORT, killf_phrases[2], buf, NULL); } else { /* have all three params, put them in place and parse the file */ /* +1 for newline */ if((killp->grps[grpon].group = malloc(strlen(grpname)+1)) == NULL) { error_log(ERRLOG_REPORT, killf_phrases[0], NULL); retval = RETVAL_ERROR; } else { strcpy(killp->grps[grpon].group, grpname); /* get, ignoring postfix, so use absolute filename on group line */ if(parse_a_file(grpfile, grpname, &(killp->grps[grpon].match), debug,FP_GET_NOPOSTFIX,killp->use_extended_regex) != RETVAL_OK) { /* whoops couldn't open file, ignore */ free(killp->grps[grpon].group); grpon--; /* so that we reuse this grp entry */ killp->totgrps--; } } } grpon++; /* finished with this group */ } } (void) fclose(fptr); } return retval; } /*--------------------------------------------------------------------------*/ void free_killfile(PKillStruct master) { int i; if(master != NULL) { /* first kill off killprg if its there */ if(master->killfunc==chk_msg_kill_fork || master->child.Pid != -1) { killprg_closeit(master); } #ifdef PERL_EMBED if(master->perl_int != NULL) { killperl_done(master); } #endif /* close off log, if opened */ if(master->logfp != NULL) { fclose(master->logfp); } free_node(master->master); if(master->totgrps > 0) { for(i=0;itotgrps;i++) { free_node(master->grps[i].match); free(master->grps[i].group); } free(master->grps); } free(master); } } /*--------------------------------------------------------------------*/ void free_node(OneKill node) { pmy_regex curr, next; curr = node.list; if (node.header != NULL) { #ifdef HAVE_REGEX_H if(node.header->ptrs != NULL) { regfree(node.header->ptrs); } #endif if(node.header->header != NULL) { free(node.header->header); } if(node.header->string != NULL) { free(node.header->string); } } while(curr != NULL) { #ifdef HAVE_REGEX_H if(curr->ptrs != NULL) { regfree(curr->ptrs); } #endif if(curr->header != NULL) { free(curr->header); } if(curr->string != NULL) { free(curr->string); } next = curr->next; free(curr); curr = next; } } /*--------------------------------------------------------------------------*/ int get_one_article_kill(PMaster master, int logcount, long itemon) { char buf[MAXLINLEN+1], *inbuf; const char *tname; char fname[PATH_MAX+1]; int retval, x; unsigned long len; FILE *fptr; PKillStruct killp; killp = master->killp; retval = RETVAL_OK; killp->pbody = NULL; /* since we haven't downloaded anything yet for this article */ killp->bodylen = 0; retval =get_chunk_mem(master, &len, CHUNK_HEADER, &inbuf); /* the killfunc pointer points to either chk_msg_kill(), chk_msg_kill_fork(), or chk_msg_kill_perl() */ /* do we have to download this sucker, is it mandatory? */ if(retval == RETVAL_OK && ((master->curr)->mandatory == MANDATORY_YES || (*killp->killfunc)(master, killp, inbuf, len) == FALSE)) { if(master->MultiFile == TRUE) { /* open file */ /* file name will be ####-#### ex 001-166 (nron,total) */ sprintf(buf,"%0*ld-%d", logcount, itemon, master->nritems); /* the strcpy to avoid wiping out fname in second call to full_path */ strcpy(fname, full_path(FP_GET, FP_MSGDIR, buf)); strcat(buf, N_TMP_EXTENSION); /* add temp file extension */ tname = full_path(FP_GET, FP_TMPDIR, buf); /* temp file name */ if(master->debug == TRUE) { do_debug("File name = \"%s\" temp = \"%s\"", fname, tname); } if((fptr = fopen(tname, "w")) == NULL) { MyPerror(tname); retval = RETVAL_ERROR; } else { /* write the header */ x = fwrite(inbuf,sizeof(inbuf[0]),len, fptr); fputs("\n", fptr); /* needed */ if(x != len) { retval = RETVAL_ERROR; error_log(ERRLOG_REPORT, killf_phrases[9], NULL); } if(retval == RETVAL_OK && master->header_only == FALSE) { /* have we already downloaded the body? */ if(killp->pbody != NULL) { inbuf = killp->pbody; len = killp->bodylen; } else { retval = get_chunk_mem(master, &len, CHUNK_BODY, &inbuf); } if(retval == RETVAL_OK) { x = fwrite(inbuf, sizeof(inbuf[0]), len, fptr); if(x != len) { retval = RETVAL_ERROR; error_log(ERRLOG_REPORT, killf_phrases[9], NULL); } } } (void) fclose(fptr); if(retval != RETVAL_OK) { unlink(tname); } /* now rename it to the permanent file name */ else { move_file(tname, fname); if((master->batch == BATCH_LIHAVE || master->batch == BATCH_INNFEED) && master->innfeed != NULL) { /* write path name and msgid to file */ fprintf(master->innfeed, "%s %s\n", fname, (master->curr)->msgnr); fflush(master->innfeed); /* so it gets written sooner */ } } } } else { fputs(inbuf, stdout); fputs("\n", stdout); if(master->header_only == FALSE) { retval = get_chunk_mem(master, &len, CHUNK_BODY, &inbuf); if(retval == RETVAL_OK) { fwrite(inbuf, sizeof(inbuf[0]), len, stdout); } } /* this is needed as a separator in stdout version */ fputs(".\n", stdout); } if(retval == RETVAL_OK) { master->nrgot++; } } if(retval == RETVAL_UNEXPECTEDANS) { retval = RETVAL_OK; /* so don't abort */ } return retval; } /*---------------------------------------------------------------*/ /* this routine gets the header or body into memory, keeping separate buffers for each */ int get_chunk_mem(PMaster master, unsigned long *size, int which, char **retbuf) { static char *header_buf = NULL; static char *body_buf = NULL; static int header_size = 8192; static unsigned long body_size = KILL_BODY_BUF_SIZE; int done, partial, len, i, retval; char *inbuf, *newbuf, *buf; const char *cmd; unsigned long temp, bufsize, currbuf; /* bufsize = alloced memory, currbuf = what retrieved so far */ done = FALSE; partial = FALSE; currbuf = 0; retval = RETVAL_OK; if(which == CHUNK_HEADER) { buf = header_buf; bufsize = header_size; } else { buf = body_buf; bufsize = body_size; } if(buf == NULL) { if((buf=malloc((size_t) bufsize)) == NULL) { error_log(ERRLOG_REPORT, killf_phrases[0], NULL); retval = RETVAL_ERROR; } } if(buf != NULL) { /* build command */ if(which == CHUNK_HEADER) { cmd = build_command(master, "head", master->curr); i = 221; } else { cmd = build_command(master, "body", master->curr); i = 222; } if((retval = send_command(master, cmd, NULL, i)) != RETVAL_OK) { free(buf); buf = NULL; } } while(buf != NULL && done == FALSE) { len=sgetline(master->sockfd, &inbuf, master->do_ssl, master->ssl_struct); (void) TimerFunc(TIMER_ADDBYTES, len, NULL); if(len < 0) { free(buf); buf = NULL; done = TRUE; retval = RETVAL_ERROR; } else if(partial == FALSE && inbuf[0] == '.') { if(len == 2 && inbuf[1] == '\n') { done = TRUE; } else { /* handle double dots IAW RFC977 2.4.1*/ inbuf++; /* move past first dot */ len--; } } if(done == FALSE) { while((len+currbuf) > bufsize && buf != NULL) { /* buffer not big enough realloc */ /* how much do we increase buf */ /* we do this test so in case KILL_CHUNK_BUF_INCREASE < len, we */ /* don't get a buffer overflow */ temp = (len > KILL_CHUNK_BUF_INCREASE) ? len : KILL_CHUNK_BUF_INCREASE; if(master->debug == TRUE) { do_debug("Re-allocing buffer from %lu to %lu\n", bufsize, bufsize+temp); } bufsize += temp; if((newbuf = realloc(buf, (size_t) bufsize)) == NULL) { free(buf); buf = NULL; currbuf = 0; error_log(ERRLOG_REPORT, killf_phrases[0], NULL); retval = RETVAL_ERROR; } else { buf = newbuf; } } if(buf != NULL) { /* put string in buffer, use memmove in case of nulls*/ memmove(buf+currbuf, inbuf, len); currbuf += len; partial= (len==MAXLINLEN&&inbuf[len-1]!='\n') ? TRUE : FALSE; } } } /* now save the values for next time */ if(which == CHUNK_HEADER) { header_buf = buf; header_size = bufsize; } else { body_buf = buf; body_size = bufsize; } /* return the length */ *size = currbuf; /* make sure buf is NULL terminated */ /* this is needed so logging is correct */ /* since using memmove() above, we don't copy the ending NULL */ if(buf != NULL) { buf[currbuf] = '\0'; } *retbuf = buf; return retval; } /*-------------------------------------------------------------------------*/ /* chk_msg_kill - return TRUE if kill article, FALSE if keep */ /* if kill article, add it to killlog */ int chk_msg_kill(PMaster master, PKillStruct killp, char *headerbuf, int buflen) { int killyn, i, del, keep, match, masterkill; const char *group = "Master"; char *why, *goodwhy; goodwhy = why = killf_reasons[REASON_NONE]; killyn = FALSE; /* first check against master delete */ masterkill = check_a_group(master, &(killp->master), headerbuf, &why); if(masterkill == TRUE && killp->grp_override == FALSE) { killyn = masterkill; } else { /* okay now have to parse group line */ /* then check to see if I have group keep/deletes for each group */ /* default actions */ keep = FALSE; del = FALSE; for(i=0;itotgrps;i++) { if(check_newsgroups(headerbuf, killp->grps[i].group) == TRUE) { /* bingo this article matches one of our group check it */ match = check_a_group(master, &(killp->grps[i].match), headerbuf, &why); if(killp->grps[i].delkeep == DELKEEP_KEEP) { /* matched keep group */ if(match == TRUE) { keep = TRUE; } else { del = TRUE; group = killp->grps[i].group; goodwhy = killf_reasons[REASON_NOKEEP]; } } else { if(match == TRUE) { del = TRUE; goodwhy = why; group = killp->grps[i].group; } else { keep = TRUE; } } } } /* now determine if we kill or keep this sucker */ if(keep == FALSE && del == FALSE) { /* no group matches, do what masterkill says to do */ killyn = masterkill; } else if(keep != del) { /* only matched one group, figure out which */ killyn = ( del == TRUE) ? TRUE : FALSE; why = goodwhy; } else { /* matched both, use TIEBREAKER */ why = killf_reasons[REASON_TIE]; killyn = killp->tie_delete; } } if(master->debug == TRUE && killyn == TRUE) { do_debug("killing: %s: %s: %s", group, why, headerbuf); } if(killyn == TRUE && killp->logyn != KILL_LOG_NONE) { /* log it */ /* only open this sucker once */ if(killp->logfp == NULL) { if((killp->logfp = fopen(full_path(FP_GET, FP_TMPDIR, master->kill_log_name), "a")) == NULL) { MyPerror(killf_phrases[3]); } } if(killp->logfp != NULL) { /* first print our one-line reason */ print_phrases(killp->logfp,killf_phrases[4], group ,why, (master->curr)->msgnr, NULL); if(killp->logyn == KILL_LOG_LONG) { /* print the header as well */ /* the nl so I get a blank line between em */ print_phrases(killp->logfp, "%v1%\n", headerbuf, NULL); } } } return killyn; } /*-----------------------------------------------------------------------*/ int check_a_group(PMaster master, POneKill killp, char *headerbuf, char **why) { int i, match = FALSE; char *startline, *tptr; pmy_regex curr; static char reason[MAXLINLEN]; PKillStruct temp; /* check hilines first */ if(killp->hilines > 0) { if((startline = strstr(headerbuf, Params[PARAM_HILINE].header)) != NULL) { i = 0; /* just in case */ sscanf(startline+Params[PARAM_HILINE].headerlen, "%d", &i); if(killp->hilines < i) { /* JACKPOT */ match = TRUE; *why = killf_reasons[REASON_TOOMANYLINES]; } } } /* now check low lines */ if(match == FALSE && killp->lowlines > 0) { if((startline = strstr(headerbuf, Params[PARAM_LOWLINE].header)) != NULL) { i = 0; /* just in case */ sscanf(startline+Params[PARAM_LOWLINE].headerlen, "%d", &i); if(i < killp->lowlines) { /* JACKPOT */ match = TRUE; *why = killf_reasons[REASON_NOTENUFLINES]; } } } /* now check nrgrps */ if(match == FALSE && killp->maxgrps > 0) { if((startline = strstr(headerbuf, Params[PARAM_NRGRPS].header)) != NULL) { /* count the nr of commas in the group line */ i = 1; /* have at least one group */ tptr = startline; while(i <= killp->maxgrps && *tptr != '\n' && *tptr != '\0' ) { /* some news server use space vice comma for separator */ if(*tptr == COMMA || *tptr == SPACE ) { i++; } tptr++; } if(i > killp->maxgrps) { match = TRUE; *why = killf_reasons[REASON_NRGRPS]; } } } /* now check nrxref */ if(match == FALSE && killp->maxxref > 0) { if((startline = strstr(headerbuf, Params[PARAM_NRXREF].header)) != NULL) { /* count the nr of colons in line, they separate the group/article nr */ i = 0; tptr = startline; while(i <= killp->maxxref && *tptr != '\n' && *tptr != '\0' ) { if(*tptr == COLON) { i++; } tptr++; } if(i > killp->maxxref) { match = TRUE; *why = killf_reasons[REASON_NRXREF]; } } } curr = killp->list; while(match == FALSE && curr != NULL) { if(regex_check(headerbuf, curr, master->debug) == TRUE) { match = TRUE; sprintf(reason, "%s %s %s ", killf_reasons[REASON_HEADER], curr->header, curr->string); *why = reason; } curr = curr->next; } curr = killp->header; while(match == FALSE && curr != NULL) { /* scan the entire header */ if(regex_block(headerbuf, curr, master->debug) == TRUE) { match = TRUE; sprintf(reason,"%s %s", killf_reasons[REASON_HEADER], curr->string); *why = reason; } curr = curr->next; } if(match == FALSE && (killp->bodybig > 0 || killp->bodysmall > 0 || killp->body != NULL)) { /* may have to download the header first to */ /* have a pointer to the master killstruct */ temp = master->killp; if(temp->pbody == NULL) { /* have to get the body first */ get_chunk_mem(master, &(temp->bodylen), CHUNK_BODY, &(temp->pbody)); } if(killp->bodybig > 0 && temp->bodylen > killp->bodybig) { match = TRUE; sprintf(reason, "%s %lu", killf_reasons[REASON_BODYBIG], temp->bodylen); *why = reason; } else if(killp->bodysmall > 0 && temp->bodylen < killp->bodysmall) { match = TRUE; sprintf(reason, "%s %lu", killf_reasons[REASON_BODYSMALL], temp->bodylen); *why = reason; } else { curr = killp->body; while(match == FALSE && curr != NULL) { /* scan the body */ if(regex_block(temp->pbody, curr, master->debug) == TRUE) { match = TRUE; sprintf(reason, "%s %s", killf_reasons[REASON_BODY], curr->string); *why = reason; } curr = curr->next; } } } return match; } /*--------------------------------------------------------------------------*/ int check_newsgroups(char *header, const char *whichgroup) { /* search Newsgroup headerline for whichgroup */ /* if in return TRUE else return FALSE; */ int match = FALSE; char *startline, *ptr; const char *ptr_group; if((startline = strstr(header, NEWSGROUP_HEADER)) != NULL) { ptr = startline + strlen(NEWSGROUP_HEADER); while( match == FALSE && *ptr != '\0' && *ptr != '\n' ) { ptr_group = whichgroup; while( *ptr == *ptr_group && *ptr != '\0') { ptr++; ptr_group++; } if(*ptr_group == '*') { /* wildcard match, they match so far, so they match */ match = TRUE; } else if((*ptr_group == '\0') && (*ptr == COMMA || *ptr == ' ' || *ptr == '\n' || *ptr == '\0')) { /* if we are at the end of both group names then we have a match */ /* we check for space even though its not in the standard, apparently */ /* some news server use it. */ match = TRUE; } else { /* advance to next group on line */ while(*ptr != COMMA && *ptr != '\n' && *ptr != '\0') { ptr++; } if(*ptr == COMMA) { /* advance past it */ ptr++; } } } } return match; } /*-------------------------------------------------------------------------------*/ void initialize_one_kill(POneKill mykill) { /* initialize the various elements*/ mykill->list = NULL; mykill->header = NULL; mykill->body = NULL; mykill->hilines = mykill->lowlines = mykill->maxgrps = mykill->maxxref = 0; mykill->bodybig = mykill->bodysmall = 0; mykill->quote = KILLFILE_QUOTE; mykill->non_regex = KILLFILE_NONREGEX; return; } /*-------------------------------------------------------------------------------*/ int parse_a_file(const char *fname, const char *group, POneKill mykill, int debug, int ignore_prefix, int use_e_regex) { FILE *fptr; char buf[MAXLINLEN+1]; int i, match; int retval = RETVAL_OK; pmy_regex curr; /* first initialize the killstruct */ initialize_one_kill(mykill); #ifdef HAVE_REGEX_H mykill->use_extended = use_e_regex; #endif /* now read in the killfile and parse it */ if((fptr = fopen(full_path(ignore_prefix, FP_DATADIR, fname), "r")) == NULL) { MyPerror(full_path(ignore_prefix, FP_DATADIR, fname)); retval = RETVAL_ERROR; } else { if(debug == TRUE) { do_debug("Pass 3 kill file: %s\n", full_path(ignore_prefix, FP_DATADIR, fname)); do_debug("Pass 3 options: group=%s, use_extended_regex=%s, ignore_prefix=%s\n", group, true_str(use_e_regex), true_str(ignore_prefix)); } while(fgets(buf, MAXLINLEN, fptr) != NULL) { buf[MAXLINLEN] = '\0'; /* just in case */ i = strlen(buf); /* strip nls off so they don't get added to regex scan, etc..*/ if(buf[i-1] == '\n') { buf[i-1] = '\0'; } if(debug == TRUE) { do_debug("pass 3 kill file line: %s\n", buf); } if(buf[0] != KILLFILE_COMMENT_CHAR) { /* skip any comment lines */ match = FALSE; for(i = 0 ; i < NR_PARAMS; i++) { if(strncmp(buf, Params[i].name, (size_t) Params[i].len) == 0) { match = TRUE; /* so don't try header field scan later */ switch(i) { case PARAM_HILINE: (void) sscanf(&buf[Params[PARAM_HILINE].len], "%d", &(mykill->hilines)); break; case PARAM_LOWLINE: (void) sscanf(&buf[Params[PARAM_LOWLINE].len], "%d", &(mykill->lowlines)); break; case PARAM_NRGRPS: (void) sscanf(&buf[Params[PARAM_NRGRPS].len], "%d", &(mykill->maxgrps)); break; case PARAM_NRXREF: (void) sscanf(&buf[Params[PARAM_NRXREF].len], "%d", &(mykill->maxxref)); break; case PARAM_QUOTE: if(buf[Params[PARAM_QUOTE].len] == '\0') { error_log(ERRLOG_REPORT, "%s\n", killf_phrases[6], NULL); } else { mykill->quote = buf[Params[PARAM_QUOTE].len]; } break; case PARAM_NONREGEX: if(buf[Params[PARAM_NONREGEX].len] == '\0') { error_log(ERRLOG_REPORT, "%s\n", killf_phrases[12], NULL); } else { mykill->non_regex = buf[Params[PARAM_NONREGEX].len]; } break; case PARAM_HDRSCAN: if(buf[Params[PARAM_HDRSCAN].len] == '\0') { error_log(ERRLOG_REPORT, "%s\n", killf_phrases[7], NULL); } else { if((curr = regex_scan(buf, mykill->quote, debug, use_e_regex, mykill->non_regex)) != NULL) { add_to_linkedlist(&(mykill->header), curr); } } break; case PARAM_BODY: if(buf[Params[PARAM_BODY].len] == '\0') { error_log(ERRLOG_REPORT, "%s\n", killf_phrases[10], NULL); } else { if((curr = regex_scan(buf, mykill->quote, debug, use_e_regex, mykill->non_regex)) != NULL) { add_to_linkedlist(&(mykill->body), curr); } } break; case PARAM_BODYBIG: (void) sscanf(&buf[Params[PARAM_BODYBIG].len], "%lu", &(mykill->bodybig)); break; case PARAM_BODYSMALL: (void) sscanf(&buf[Params[PARAM_BODYSMALL].len], "%lu", &(mykill->bodysmall)); break; case PARAM_E_REGEX: /* in case specified for this file only */ use_e_regex = TRUE; #ifdef HAVE_REGEX_H mykill->use_extended = TRUE; /* save for printing in debug */ #endif break; default: /* handle all other lines aka do nothing*/ break; } } } if(match == FALSE) { /* it's a different line in the header add to linked-list */ if((curr = regex_scan(buf, mykill->quote, debug, use_e_regex, mykill->non_regex)) != NULL) { add_to_linkedlist(&(mykill->list), curr); } } } /* comment line stuff */ } (void) fclose(fptr); /* check for an empty killfile */ if(mykill->hilines == 0 && mykill->lowlines == 0 && mykill->maxgrps == 0 && mykill->bodybig == 0 && mykill->bodysmall == 0 && mykill->header == NULL && mykill->list == NULL && mykill->body == NULL && mykill->maxxref == 0) { retval = RETVAL_EMPTYKILL; } } return retval; } /*---------------------------------------------------------------------------------------------*/ void add_to_linkedlist(pmy_regex *head_ptr, pmy_regex addon) { /* this routine adds a pointer the end of a linked list */ pmy_regex curr; if(*head_ptr == NULL) { /* put on top of list */ *head_ptr = addon; } else { curr = *head_ptr; while(curr->next != NULL) { curr = curr->next; } curr->next = addon; } } /*----------------------------------------------------------------------------------*/ /* scan a line and build the compiled regex pointers to call regexex() with */ /* ---------------------------------------------------------------------------------*/ pmy_regex regex_scan(char *linein, char quotechar, int debug, int use_e_regex, char non_regex) { char *tptr, *startline; int len,x,y, case_sens = FALSE, flags = FALSE; #ifdef HAVE_REGEX_H char errmsg[256]; int i,j, useregex = TRUE; #endif pmy_regex which = NULL; int err = FALSE; if(linein != NULL) { /* first find the : in the line to skip the parameter part */ tptr = linein; while(*tptr != '\0' && *tptr != ':') { tptr++; } startline = tptr+1; len = startline - linein; /* do we treat this string as a non-regex?? */ if(*startline == non_regex) { startline++; #ifdef HAVE_REGEX_H useregex = FALSE; #endif } /* case sensitive search?? */ if(*startline == quotechar) { startline++; case_sens = TRUE; } #ifdef HAVE_REGEX_H if(useregex == TRUE) { flags = REG_NOSUB; if(case_sens == FALSE) { flags |= REG_ICASE; } if(use_e_regex == TRUE) { flags |= REG_EXTENDED; } useregex = FALSE; /* test to see whether or not we need to use regex */ for(i = 0; i < strlen(regex_chars) && useregex != TRUE; i++) { for(j = 0 ; j < strlen(startline)&& useregex != TRUE; j++) { if(startline[j] == regex_chars[i]) { useregex = TRUE; } } } } #endif /* now count each of em, so that can alloc my array of regex_t */ if(*tptr != '\0') { /* now alloc memory */ if((which = malloc(sizeof (my_regex))) == NULL) { error_log(ERRLOG_REPORT, killf_phrases[5], NULL); } else { /* first save the string for later printing, if necessary */ if((which->string = malloc(strlen(startline)+1)) == NULL) { err = TRUE; error_log(ERRLOG_REPORT, killf_phrases[5], NULL); } else { strcpy(which->string,startline); which->next = NULL; /* initialize */ which->case_sensitive = case_sens; #ifdef HAVE_REGEX_H which->ptrs = NULL; /* just to be on safe side */ #endif if((which->header=calloc(sizeof(char), len+1)) == NULL) { error_log(ERRLOG_REPORT, killf_phrases[5], NULL); err = TRUE; } else { strncpy(which->header, linein, len); which->header[len] = '\0'; /* just in case */ #ifdef HAVE_REGEX_H if(useregex == TRUE) { if((which->ptrs = malloc(sizeof(regex_t))) == NULL) { error_log(ERRLOG_REPORT, killf_phrases[5], NULL); } else { if(debug == TRUE) { do_debug("Regcomping -%s-\n", startline); } if((err = regcomp(which->ptrs, startline, flags)) != 0) { /* whoops */ regerror(err, which->ptrs, errmsg, sizeof(errmsg)); error_log(ERRLOG_REPORT, killf_phrases[11], startline, errmsg, NULL); err= TRUE; } } } else { #endif /* not using regex, fill in skiparray */ /* first fill in max skip for all chars (len of string) */ /* See Algorithms/Sedgewick pg 287-289 for explanation */ y=strlen(which->string); for(x=0;xskiparray);x++) { which->skiparray[x] = y; } /* now go back and fill in those in our search string */ if(case_sens == FALSE) { /* case-insensitive search */ for(x=0;xstring[x]; which->skiparray[(unsigned char) toupper(xx)] = (y-1)-x; which->skiparray[(unsigned char) tolower(xx)] = (y-1)-x; } } else { for(x=1;xskiparray[(unsigned char) which->string[x]] = (y-1)-x; } } #ifdef HAVE_REGEX_H } #endif } } } } } if(which != NULL && err == TRUE) { /* error, free up everything */ #ifdef HAVE_REGEX_H if(which->ptrs != NULL) { free(which->ptrs); } #endif if(which->string != NULL) { free(which->string); } if(which->header != NULL) { free(which->header); } if(which != NULL) { free(which); which = NULL; } } return which; } /*------------------------------------------------------------------------------------*/ int regex_check(char *headerbuf, pmy_regex expr, int debug) { int match = FALSE; char *startline, *endline; if(expr->header != NULL) { if((startline = strstr(headerbuf, expr->header)) != NULL) { endline = strchr(startline, '\n'); /* end this line, so we only search the right header line */ if(endline != NULL) { *endline = '\0'; } /* the +strlen(expr->header) so the header field and the space */ /* immediately following it aren't included in the search */ if(debug == TRUE) { do_debug("checking -%s- for -%s-\n", startline+strlen(expr->header)+1, expr->string); } #ifdef HAVE_REGEX_H if(expr->ptrs != NULL) { if(regexec(expr->ptrs, startline+strlen(expr->header)+1, 0, NULL, 0) == 0) { match = TRUE; } } else { #endif match = find_string(debug, expr, startline+strlen(expr->header)+1); #ifdef HAVE_REGEX_H } #endif /* put the nl back on that we deleted */ if(endline != NULL) { *endline = '\n'; } } } if(debug== TRUE) { do_debug("Returning match=%s\n", (match == TRUE) ? "True" : "False"); } return match; } /*------------------------------------------------------------------------------------*/ int regex_block(char *what, pmy_regex where, int debug) { /* find a string in a block */ int match = FALSE; if(debug == TRUE && where->string != NULL) { do_debug("Checking for -%s-\n", where->string); } #ifdef HAVE_REGEX_H if(where->ptrs != NULL) { if(regexec(where->ptrs, what, 0, NULL, 0) == 0) { match = TRUE; } } else { #endif match = find_string(debug, where, what); #ifdef HAVE_REGEX_H } #endif return match; } /*---------------------------------------------------------------------------------------------*/ int find_string(int debug, pmy_regex expr, char *str) { /* the search mechanism is based on the Boyer-Moor Algorithms (pg 286- of Algorithms book) */ int match = FALSE; char *ptr; /* pointer to pattern */ int lenp; /* length of pattern */ int lens; /* length of string we are searching */ int i; /* current location in string */ int j; /* current location in pattern */ int t; /* value of skip array for string location */ ptr = expr->string; lenp = strlen(ptr); lens = strlen(str); if(expr->case_sensitive == TRUE ) { /* do case-sensitive search */ if(debug == TRUE) { do_debug("Case-sensitive search for %s\n", ptr); } i = j = lenp-1; do { if(str[i] == ptr[j]) { i--; j--; } else { t = expr->skiparray[(unsigned char) str[i]]; i += ( lenp -j > t) ? (lenp - j) : t; j = lenp - 1; } } while( j >= 0 && i < lens); if(j < 0) { match = TRUE; } } else { /* do case-insensitive search */ if(debug == TRUE ) { do_debug("Case-insensitive search for --%s--\n", ptr); } i = j = lenp-1; do { if(tolower(str[i]) == tolower(ptr[j])) { i--; j--; } else { t = expr->skiparray[(unsigned char) str[i]]; i += ( lenp -j > t) ? (lenp - j) : t; j = lenp - 1; } } while( j >= 0 && i < lens); if(j < 0) { match = TRUE; } } return match; } /*------------------------------------------------------------------------------------*/ void print_debug(PKillStruct master, const char *fname) { int i; /* print out status of master killstruct to debug file */ do_debug("--%s-- Master KillStruct\n",fname); do_debug("logyn=%s\n",true_str(master->logyn)); do_debug("grp_override=%s\n",true_str(master->grp_override)); do_debug("tie_delete=%s\n",true_str(master->tie_delete)); do_debug("totgrps=%d\n", master->totgrps); do_debug("ignore_postfix=%s\n",true_str(master->ignore_postfix)); do_debug("use_extended_regex=%s\n", true_str(master->use_extended_regex)); do_debug("xover_long_long=%s\n", true_str(master->xover_log_long)); do_debug("Master kill group"); debug_one_kill(&(master->master)); for(i = 0 ; i < master->totgrps ; i++ ) { do_debug("Group %d = %s\n", i, master->grps[i].group); do_debug("--delkeep =%s\n", (master->grps[i].delkeep == DELKEEP_KEEP) ? "keep" : "delete"); debug_one_kill(&(master->grps[i].match)); } do_debug("--%s-- End of Killstruct\n", fname); } /*------------------------------------------------------------------------*/ void debug_one_kill(POneKill ptr) { pmy_regex temp; do_debug("--hilines =%d\n", ptr->hilines); do_debug("--lowlines=%d\n", ptr->lowlines); do_debug("--maxgrps=%d\n", ptr->maxgrps); do_debug("--maxxref=%d\n", ptr->maxxref); do_debug("--bodysize>%lu\n", ptr->bodybig); do_debug("--bodysize<%lu\n", ptr->bodysmall); do_debug("--quote=%c\n", ptr->quote); do_debug("--non_regex=%c\n", ptr->non_regex); #ifdef HAVE_REGEX_H do_debug("--use_extended_regex=%s\n", true_str(ptr->use_extended)); #endif temp= ptr->header; while(temp != NULL) { #ifdef HAVE_REGEX_H do_debug("--header scan is %sregex\n", (temp->ptrs == NULL) ? "non-" : ""); #endif do_debug("--Scan is %scase-sensitive\n", (temp->case_sensitive == TRUE) ? "" : "non-"); if(temp->string != NULL) { do_debug("--header scan=%s\n", temp->string); } temp = temp->next; } temp = ptr->body; while(temp != NULL) { #ifdef HAVE_REGEX_H do_debug("--body scan is %sregex\n", (temp->ptrs == NULL) ? "non-" : ""); #endif do_debug("--Scan is %scase-sensitive\n", (temp->case_sensitive == TRUE) ? "" : "non-"); if(temp->string != NULL) { do_debug("--body scan=%s\n", temp->string); } temp = temp->next; } temp = ptr->list; while(temp != NULL) { #ifdef HAVE_REGEX_H do_debug("--header match is %sregex\n", (temp->ptrs == NULL) ? "non-" : ""); #endif do_debug("--Scan is %scase-sensitive\n", (temp->case_sensitive == TRUE) ? "" : "non-"); do_debug("--header match= %s %s\n", temp->header, temp->string); temp = temp->next; } do_debug("--end of Killfile Group--\n"); }