#include #include #include #ifdef HAVE_LIMITS_H #include #endif #ifdef DMALLOC #include #endif #include "suck_config.h" #include "suck.h" #include "both.h" #include "xover.h" #include "phrases.h" #include "killfile.h" #include "suckutils.h" #ifdef EMX #define strcasecmp(x,y) strcmp((x),(y)) #endif typedef struct Grplist { PGroup grp; struct Grplist *next; } GrpList, *PGrpList; /*func prototypes */ int do_one_line(PMaster, char *, char *,PGrpList, int); int chk_a_group(PMaster, POneKill, POverview, char **); int match_group(char *, char *, int); char *find_msgid(PMaster, char *); /* these MUST match xover_reasons[] ! EXCEPT FOR THE last entry which MUST BE REASON_NONE */ enum { REASON_HIBYTES, REASON_LOWBYTES, REASON_HILINES, REASON_LOWLINES, REASON_HEADER, REASON_NOKEEP, REASON_TIE,\ REASON_XREF, REASON_PRG, REASON_PERL, REASON_NONE}; /*----------------------------------------------------------------------*/ int do_group_xover(PMaster master, char *grpname, long startnr, long endnr) { int done, len, nr, retval = RETVAL_OK; char *resp, cmd[MAXLINLEN]; PKillStruct xoverkill; PGroup grpkill = NULL; PGrpList glist = NULL, gat = NULL, gptr; sprintf(cmd, "xover %ld-%ld\r\n", startnr,endnr); retval = send_command(master, cmd, &resp, 0); if(retval == RETVAL_OK) { number(resp, &nr); switch(nr) { default: case 412: case 502: /* abort on these errors */ error_log(ERRLOG_REPORT, xover_phrases[0], resp, NULL); retval = RETVAL_NOXOVER; break; case 420: /* no articles available, no prob */ break; case 224: /* first, check to see if there are group kill file(s) for this group */ /* we do this here, so we only do it once per group, since we don't */ /* have all the crossposted groups to deal with */ xoverkill = master->xoverp; if(xoverkill->totgrps > 0) { for(nr = 0; xoverkill != NULL && nr < xoverkill->totgrps; nr++) { if(match_group(xoverkill->grps[nr].group, grpname, master->debug) == TRUE) { grpkill = &(xoverkill->grps[nr]); if(master->debug == TRUE) { do_debug("Using Group Xover killfile %s\n", grpkill->group); } /* now add it to our list to pass down to do_one_line()*/ if((gptr = calloc(1, sizeof(GrpList))) == NULL) { error_log(ERRLOG_REPORT, xover_phrases[12], NULL); retval = RETVAL_ERROR; } else { gptr->grp = grpkill; gptr->next = NULL; /* add to list */ if(glist == NULL) { /* head of list */ glist = gptr; } else { gat->next = gptr; } gat = gptr; } } } } /* okay, do it */ done = FALSE; while((done == FALSE) && (retval == RETVAL_OK)) { len = sgetline(master->sockfd, &resp, master->do_ssl, master->ssl_struct); /* have we got the last one? */ if((len == 2) && (strcmp(resp, ".\n") == 0)) { done = TRUE; } else if(len < 0) { retval =RETVAL_ERROR; } else { retval = do_one_line(master, grpname, resp, glist, xoverkill->tie_delete); } } break; } } while(glist != NULL) { /* free up memory */ gptr = glist->next; free(glist); glist = gptr; } return retval; } /*-----------------------------------------------------------------------------------*/ int do_one_line(PMaster master, char *group, char *linein, PGrpList grpskill, int tie_delete) { /* take in one line of xover, then run it thru the kill routines */ int why = REASON_NONE, len, msgidlen = 0, retval = RETVAL_OK, match = REASON_NONE, keep = FALSE, del = FALSE; char *msgid = NULL, *ptr, *reason = NULL, tmp = '\0'; long msgnr; POverview overv; PGroup grpkill; PKillStruct masterk = master->xoverp; /* our master killstruct */ overv = master->xoverview; ptr = get_long(linein, &msgnr); /* get our message number */ /* first go thru and match up the header with the fields in the line, and fill */ /* in the pointers in the xoverview with pointers to start of field */ while( *ptr != '\0' && overv != NULL) { /* we are just past a tab */ len = 0; if(overv->full == TRUE) { /* have to skip header name in the xoverview */ while(*ptr != COLON && *ptr != '\0' && *ptr != '\t') { ptr++; } if(*ptr != '\0') { ptr++; /* move past colon */ } } overv->field = ptr; while(*ptr != '\t' && *ptr != '\0') { len++; ptr++; } overv->fieldlen = len; /* save Message-ID location for later passing to subroutines */ if(strcasecmp("Message-ID:", overv->header) == 0) { msgid = overv->field; msgidlen = overv->fieldlen; } overv = overv->next; if(*ptr == '\t') { ptr++; /* advance past tab */ } } while(overv != NULL) { /* in case we got a short xoverview */ overv->field = NULL; overv->fieldlen = 0; overv = overv->next; } if(msgid == NULL) { error_log(ERRLOG_REPORT, xover_phrases[13], linein, NULL); } else { if(masterk->child.Pid != -1) { /* send to child program */ match = (killprg_sendxover(master, linein) == TRUE) ? REASON_PRG : REASON_NONE; } #ifdef PERL_EMBED else if(masterk->perl_int != NULL) { /* send to perl subroutine */ match = (killperl_sendxover(master, linein) == TRUE) ? REASON_PERL : REASON_NONE; } #endif else { /* we have to check against master killfile and group kill file */ match = chk_a_group( master, &(masterk->master), master->xoverview, &reason); if((grpskill != NULL) && (match == REASON_NONE || masterk->grp_override == TRUE)) { /* we have to check against group */ while ( grpskill != NULL) { grpkill = grpskill->grp; match = chk_a_group( master, &(grpkill->match), master->xoverview, &reason); if(grpkill->delkeep == DELKEEP_KEEP) { if(match != REASON_NONE) { keep = TRUE; } else { del = TRUE; why = REASON_NOKEEP; } } else { if(match != REASON_NONE) { del = TRUE; why = match; } else { keep = TRUE; } } grpskill = grpskill->next; } /* now do tie-breaking */ if(del == FALSE && keep == FALSE) { /* match nothing */ match = REASON_NONE; } else if(del != keep) { /* either keep or del */ match = ( del == TRUE) ? why : REASON_NONE; } else { /* do tie-breaking */ match = (tie_delete == TRUE) ? REASON_TIE : REASON_NONE; } } } /* now we need to null terminate the msgid, for allocing or printing */ if(msgid != NULL) { tmp = msgid[msgidlen]; msgid[msgidlen] = '\0'; } if(match == REASON_NONE) { /* we keep it */ retval= allocnode(master, msgid, MANDATORY_OPTIONAL, group, msgnr); } else if(masterk->logyn != KILL_LOG_NONE) { /* only open this once */ if(masterk->logfp == NULL) { if((masterk->logfp = fopen(full_path(FP_GET, FP_TMPDIR, master->kill_log_name), "a")) == NULL) { MyPerror(xover_phrases[11]); } } if(masterk->logfp != NULL) { /* Log it */ if(match == REASON_HEADER) { print_phrases(masterk->logfp, xover_phrases[9], group, reason, msgid, NULL); } else { print_phrases(masterk->logfp, xover_phrases[9], group, xover_reasons[match], msgid, NULL); } /* restore the message-id end of string so entire line prints */ if(msgid != NULL) { msgid[msgidlen] = tmp; } if(masterk->xover_log_long == TRUE) { /* print the xover formatted to look like a message header */ overv = master->xoverview; while(overv != NULL ) { tmp = overv->field[overv->fieldlen]; /* null terminate it */ overv->field[overv->fieldlen] = '\0'; print_phrases(masterk->logfp, "%v1% %v2%\n", overv->header, overv->field); overv->field[overv->fieldlen] = tmp; /* restore it */ overv = overv->next ; } } else if(masterk->logyn == KILL_LOG_LONG) { /* print the xover as well */ print_phrases(masterk->logfp, xover_phrases[10],linein, NULL); } } } } return retval; } /*----------------------------------------------------------------------------------------*/ void get_xoverview(PMaster master) { /* get in the xoverview.fmt list, so we can parse what xover returns later */ /* we'll put em in a linked list */ int done, retval, len, full; char *resp; POverview tmp, tmp2, curptr; retval = RETVAL_OK; curptr = NULL; /* where we are currently at in the linked list */ if(send_command(master, "list overview.fmt\r\n", &resp, 215) == RETVAL_OK) { done = FALSE; /* now get em in, until we hit .\n which signifies end of response */ while(done != TRUE) { sgetline(master->sockfd, &resp, master->do_ssl, master->ssl_struct); if(master->debug == TRUE) { do_debug("Got line--%s", resp); } if(strcmp(resp, ".\n") == 0) { done = TRUE; } else if(retval == RETVAL_OK) { /* now save em if we haven't run out of memory */ len = strlen(resp); /* does this have a full flag on it, which means the field name */ /* will be in the xover string */ full = (strstr(resp, ":full") == NULL) ? FALSE : TRUE; /* now get rid of everything back to : */ while(resp[len] != ':') { resp[len--] = '\0'; } len++; /* so we point past colon */ if((tmp = malloc(sizeof(Overview))) == NULL) { error_log(ERRLOG_REPORT, xover_phrases[12], NULL); retval = RETVAL_ERROR; } else if((tmp->header = calloc(sizeof(char), len+1)) == NULL) { error_log(ERRLOG_REPORT, xover_phrases[12], NULL); retval = RETVAL_ERROR; } else { /* initialize the structure */ /* do this first so we don't wipe out with null termination */ strncpy(tmp->header, resp, len); /* so we get the colon */ tmp->header[len] = '\0'; tmp->next = NULL; tmp->field = NULL; tmp->fieldlen = 0; tmp->full = full; if(curptr == NULL) { /* at head of list */ curptr = tmp; master->xoverview = tmp; } else { /* add to linked list */ curptr->next = tmp; curptr = tmp; } } } } } if(retval != RETVAL_OK) { /* free up whatever alloced */ tmp = master->xoverview; while (tmp != NULL) { tmp2 = tmp; if(tmp->header != NULL) { free(tmp->header); } free(tmp); tmp = tmp2; } master->xoverview = NULL; } if(master->debug == TRUE && (tmp = master->xoverview) != NULL) { do_debug("--Xoverview.fmt list\n"); while(tmp != NULL) { if(tmp->header != NULL) { do_debug("item = %s -- full = %s\n", tmp->header, true_str(tmp->full)); } tmp = tmp->next; } do_debug("--End Xoverview.fmt list\n"); } } /*-------------------------------------------------------------------------------------*/ int chk_a_group(PMaster master, POneKill grp, POverview overv, char **reason) { /* return REASON_ if xover matches group */ /* linein should be at tab after the Message Number */ int i, match = REASON_NONE; unsigned long bytes; int lines; char tchar, *tptr; static char reasonstr[MAXLINLEN]; pmy_regex ptr; POverview tmp; *reason = reasonstr; tmp = overv; /* go thru each header field, and see if we must test against it */ while ( tmp != NULL && match == REASON_NONE) { /* only do the test if we have a valid header & field to test against */ if(tmp->field != NULL && tmp->header != NULL) { /* test the size of the body of the article */ if(grp->bodybig > 0 || grp->bodysmall > 0) { if(strcasecmp(tmp->header, "Bytes:") == 0) { sscanf(tmp->field, "%lu", &bytes); /* convert ascii to long */ if((grp->bodybig > 0) && (bytes > grp->bodybig)) { match = REASON_HIBYTES; } else if((grp->bodysmall > 0) && (bytes < grp->bodysmall)) { match = REASON_LOWBYTES; } } } /* test the number of lines in the article */ if((match == REASON_NONE) && (grp->hilines > 0 || grp->lowlines > 0)) { if(strcasecmp(tmp->header, "Lines:") == 0) { sscanf(tmp->field, "%d", &lines); if((grp->hilines > 0) && (lines > grp->hilines)) { match = REASON_HILINES; } else if((grp->lowlines > 0) && (lines < grp->lowlines)) { match = REASON_LOWLINES; } } } /* check the number of xrefs */ if(match == REASON_NONE && grp->maxxref > 0) { if(strcasecmp(tmp->header, "Xref:") == 0) { i = 0; tptr = tmp->field; while(i <= grp->maxxref && *tptr != '\0' ) { if(*tptr == COLON ) { i++; } tptr++; } if(i > grp->maxxref) { match = REASON_XREF; } } } /* match against any of the headers */ if(match == REASON_NONE) { ptr = grp->list; while(match == REASON_NONE && ptr != NULL) { if(strcasecmp(tmp->header, ptr->header) == 0) { /* we need to null terminate the field and restore it later */ tchar = tmp->field[tmp->fieldlen]; tmp->field[tmp->fieldlen] = '\0'; if(regex_block(tmp->field, ptr, master->debug) == TRUE) { match = REASON_HEADER; sprintf(reasonstr, "%s-%s%s", xover_reasons[REASON_HEADER], tmp->header, tmp->field); } tmp->field[tmp->fieldlen] = tchar; } ptr = ptr->next; } } } tmp = tmp->next; } return match; } /*---------------------------------------------------------------------------*/ int match_group(char *match_grp, char *group, int debug) { /* does match match group? match may contain wildcards */ int match = FALSE; if(match_grp != NULL && group != NULL) { if(debug == TRUE) { do_debug("Xover - matching %s against %s\n", match_grp, group); } while( *group == *match_grp && *group != '\0') { group++; match_grp++; } if(*match_grp == '\0' || *match_grp == '*') { /* wildcard match or end of string, they match so far, so they match */ match = TRUE; } if(debug == TRUE) { do_debug("match = %s\n", true_str(match)); } } return match; } /*-------------------------------------------------------------------------------*/ int get_xover(PMaster master, char *group, long startnr, long endnr) { int len, nr, done, retval = RETVAL_OK; char cmd[MAXLINLEN], *resp, *ptr, *msgid; long msgnr; sprintf(cmd, "xover %ld-%ld\r\n", startnr,endnr); retval = send_command(master, cmd, &resp, 0); if(retval == RETVAL_OK) { number(resp, &nr); switch(nr) { default: case 412: case 502: /* abort on these errors */ error_log(ERRLOG_REPORT, xover_phrases[0], resp, NULL); retval = RETVAL_NOXOVER; break; case 420: /* no articles available, no prob */ break; case 224: /* got a list coming at us */ done = FALSE; while((done == FALSE) && (retval == RETVAL_OK)) { len = sgetline(master->sockfd, &resp, master->do_ssl, master->ssl_struct); if(master->debug == TRUE) { do_debug("Got xover line: %s", resp); } /* have we got the last one? */ if((len == 2) && (strcmp(resp, ".\n") == 0)) { done = TRUE; } else if(len < 0) { retval =RETVAL_ERROR; } else { ptr = get_long(resp, &msgnr); /* get our message number */ msgid = find_msgid(master, ptr); /* find the msg-id */ if(msgid == NULL) { error_log(ERRLOG_REPORT, xover_phrases[13], resp); } else { /* alloc the memory for it */ retval= allocnode(master, msgid, MANDATORY_OPTIONAL, group, msgnr); } } } break; } } return retval; } /*-----------------------------------------------------------------------------------*/ /* find the msgid in a xover, and return it null-terminated, so can alloc memory, etc*/ /*-----------------------------------------------------------------------------------*/ char *find_msgid(PMaster master, char *xover) { char *ptr, *ptr2, *retval = NULL; POverview pov; pov = master->xoverview; ptr = xover; /* now go thru and find the Message-ID in the xoverview, using the overview.fmt */ /* to tell us which field is which */ if(ptr != NULL) { while ( *ptr != '\0' && pov != NULL && strcmp(pov->header, "Message-ID:") != 0) { /* go to the next field, past the tab */ pov = pov->next; while(*ptr != '\t' && *ptr != '\0') { ptr++; } if(*ptr != '\0') { ptr++; /* past the tab */ } } if(*ptr != '\0' && pov != NULL) { /* bingo, found it */ /* find the start of the msgid */ while(*ptr != '\0' && *ptr != '<') { ptr++; } if(ptr != '\0') { ptr2 = ptr; /* NULL terminate the msgid */ while(*ptr2 != '\0' && *ptr2 != '>') { ptr2++; } /* ONLY if we find a valid start and end to the MessageId */ /* do we set a valid return value */ if(*ptr2 != '\0') { *(ptr2+1) = '\0'; retval = ptr; } } } } return retval; }