1 #include <config.h>
2 #include <stdlib.h>
3 #include <string.h>
4
5 #ifdef HAVE_LIMITS_H
6 #include <limits.h>
7 #endif
8
9 #ifdef DMALLOC
10 #include <dmalloc.h>
11 #endif
12
13 #include "suck_config.h"
14 #include "suck.h"
15 #include "both.h"
16 #include "xover.h"
17 #include "phrases.h"
18 #include "killfile.h"
19 #include "suckutils.h"
20
21 #ifdef EMX
22 #define strcasecmp(x,y) strcmp((x),(y))
23 #endif
24
25 typedef struct Grplist {
26 PGroup grp;
27 struct Grplist *next;
28 } GrpList, *PGrpList;
29
30 /*func prototypes */
31 int do_one_line(PMaster, char *, char *,PGrpList, int);
32 int chk_a_group(PMaster, POneKill, POverview, char **);
33 int match_group(char *, char *, int);
34 char *find_msgid(PMaster, char *);
35
36 /* these MUST match xover_reasons[] ! EXCEPT FOR THE last entry which MUST BE REASON_NONE */
37 enum { REASON_HIBYTES, REASON_LOWBYTES, REASON_HILINES, REASON_LOWLINES, REASON_HEADER, REASON_NOKEEP, REASON_TIE,\
38 REASON_XREF, REASON_PRG, REASON_PERL, REASON_NONE};
39
40 /*----------------------------------------------------------------------*/
do_group_xover(PMaster master,char * grpname,long startnr,long endnr)41 int do_group_xover(PMaster master, char *grpname, long startnr, long endnr) {
42
43 int done, len, nr, retval = RETVAL_OK;
44 char *resp, cmd[MAXLINLEN];
45 PKillStruct xoverkill;
46 PGroup grpkill = NULL;
47 PGrpList glist = NULL, gat = NULL, gptr;
48
49 sprintf(cmd, "xover %ld-%ld\r\n", startnr,endnr);
50 retval = send_command(master, cmd, &resp, 0);
51 if(retval == RETVAL_OK) {
52 number(resp, &nr);
53 switch(nr) {
54 default:
55 case 412:
56 case 502:
57 /* abort on these errors */
58 error_log(ERRLOG_REPORT, xover_phrases[0], resp, NULL);
59 retval = RETVAL_NOXOVER;
60 break;
61 case 420:
62 /* no articles available, no prob */
63 break;
64 case 224:
65 /* first, check to see if there are group kill file(s) for this group */
66 /* we do this here, so we only do it once per group, since we don't */
67 /* have all the crossposted groups to deal with */
68 xoverkill = master->xoverp;
69 if(xoverkill->totgrps > 0) {
70 for(nr = 0; xoverkill != NULL && nr < xoverkill->totgrps; nr++) {
71 if(match_group(xoverkill->grps[nr].group, grpname, master->debug) == TRUE) {
72 grpkill = &(xoverkill->grps[nr]);
73 if(master->debug == TRUE) {
74 do_debug("Using Group Xover killfile %s\n", grpkill->group);
75 }
76 /* now add it to our list to pass down to do_one_line()*/
77 if((gptr = calloc(1, sizeof(GrpList))) == NULL) {
78 error_log(ERRLOG_REPORT, xover_phrases[12], NULL);
79 retval = RETVAL_ERROR;
80 }
81 else {
82 gptr->grp = grpkill;
83 gptr->next = NULL;
84 /* add to list */
85 if(glist == NULL) {
86 /* head of list */
87 glist = gptr;
88 }
89 else {
90 gat->next = gptr;
91 }
92 gat = gptr;
93 }
94 }
95 }
96 }
97
98 /* okay, do it */
99 done = FALSE;
100 while((done == FALSE) && (retval == RETVAL_OK)) {
101 len = sgetline(master->sockfd, &resp, master->do_ssl, master->ssl_struct);
102 /* have we got the last one? */
103 if((len == 2) && (strcmp(resp, ".\n") == 0)) {
104 done = TRUE;
105 }
106 else if(len < 0) {
107 retval =RETVAL_ERROR;
108 }
109 else {
110 retval = do_one_line(master, grpname, resp, glist, xoverkill->tie_delete);
111 }
112 }
113 break;
114 }
115 }
116 while(glist != NULL) {
117 /* free up memory */
118 gptr = glist->next;
119 free(glist);
120 glist = gptr;
121 }
122
123 return retval;
124 }
125 /*-----------------------------------------------------------------------------------*/
do_one_line(PMaster master,char * group,char * linein,PGrpList grpskill,int tie_delete)126 int do_one_line(PMaster master, char *group, char *linein, PGrpList grpskill, int tie_delete) {
127
128 /* take in one line of xover, then run it thru the kill routines */
129
130 int why = REASON_NONE, len, msgidlen = 0, retval = RETVAL_OK, match = REASON_NONE, keep = FALSE, del = FALSE;
131 char *msgid = NULL, *ptr, *reason = NULL, tmp = '\0';
132 long msgnr;
133 POverview overv;
134 PGroup grpkill;
135
136 PKillStruct masterk = master->xoverp; /* our master killstruct */
137 overv = master->xoverview;
138 ptr = get_long(linein, &msgnr); /* get our message number */
139
140 /* first go thru and match up the header with the fields in the line, and fill */
141 /* in the pointers in the xoverview with pointers to start of field */
142 while( *ptr != '\0' && overv != NULL) {
143 /* we are just past a tab */
144 len = 0;
145 if(overv->full == TRUE) {
146 /* have to skip header name in the xoverview */
147 while(*ptr != COLON && *ptr != '\0' && *ptr != '\t') {
148 ptr++;
149 }
150 if(*ptr != '\0') {
151 ptr++; /* move past colon */
152 }
153 }
154 overv->field = ptr;
155 while(*ptr != '\t' && *ptr != '\0') {
156 len++;
157 ptr++;
158 }
159 overv->fieldlen = len;
160
161 /* save Message-ID location for later passing to subroutines */
162 if(strcasecmp("Message-ID:", overv->header) == 0) {
163 msgid = overv->field;
164 msgidlen = overv->fieldlen;
165 }
166 overv = overv->next;
167 if(*ptr == '\t') {
168 ptr++; /* advance past tab */
169 }
170 }
171 while(overv != NULL) {
172 /* in case we got a short xoverview */
173 overv->field = NULL;
174 overv->fieldlen = 0;
175 overv = overv->next;
176 }
177 if(msgid == NULL) {
178 error_log(ERRLOG_REPORT, xover_phrases[13], linein, NULL);
179 }
180 else {
181 if(masterk->child.Pid != -1) {
182 /* send to child program */
183 match = (killprg_sendxover(master, linein) == TRUE) ? REASON_PRG : REASON_NONE;
184 }
185 #ifdef PERL_EMBED
186 else if(masterk->perl_int != NULL) {
187 /* send to perl subroutine */
188 match = (killperl_sendxover(master, linein) == TRUE) ? REASON_PERL : REASON_NONE;
189 }
190 #endif
191 else {
192 /* we have to check against master killfile and group kill file */
193 match = chk_a_group( master, &(masterk->master), master->xoverview, &reason);
194
195 if((grpskill != NULL) && (match == REASON_NONE || masterk->grp_override == TRUE)) {
196 /* we have to check against group */
197 while ( grpskill != NULL) {
198 grpkill = grpskill->grp;
199 match = chk_a_group( master, &(grpkill->match), master->xoverview, &reason);
200 if(grpkill->delkeep == DELKEEP_KEEP) {
201 if(match != REASON_NONE) {
202 keep = TRUE;
203 }
204 else {
205 del = TRUE;
206 why = REASON_NOKEEP;
207 }
208 }
209 else {
210 if(match != REASON_NONE) {
211 del = TRUE;
212 why = match;
213 }
214 else {
215 keep = TRUE;
216 }
217 }
218 grpskill = grpskill->next;
219 }
220
221 /* now do tie-breaking */
222 if(del == FALSE && keep == FALSE) {
223 /* match nothing */
224 match = REASON_NONE;
225 }
226 else if(del != keep) {
227 /* either keep or del */
228 match = ( del == TRUE) ? why : REASON_NONE;
229 }
230 else {
231 /* do tie-breaking */
232 match = (tie_delete == TRUE) ? REASON_TIE : REASON_NONE;
233 }
234 }
235 }
236
237 /* now we need to null terminate the msgid, for allocing or printing */
238
239 if(msgid != NULL) {
240 tmp = msgid[msgidlen];
241 msgid[msgidlen] = '\0';
242 }
243
244 if(match == REASON_NONE) {
245 /* we keep it */
246 retval= allocnode(master, msgid, MANDATORY_OPTIONAL, group, msgnr);
247 }
248 else if(masterk->logyn != KILL_LOG_NONE) {
249 /* only open this once */
250 if(masterk->logfp == NULL) {
251 if((masterk->logfp = fopen(full_path(FP_GET, FP_TMPDIR, master->kill_log_name), "a")) == NULL) {
252 MyPerror(xover_phrases[11]);
253 }
254 }
255
256 if(masterk->logfp != NULL) {
257 /* Log it */
258 if(match == REASON_HEADER) {
259 print_phrases(masterk->logfp, xover_phrases[9], group, reason, msgid, NULL);
260 }
261 else {
262 print_phrases(masterk->logfp, xover_phrases[9], group, xover_reasons[match], msgid, NULL);
263 }
264 /* restore the message-id end of string so entire line prints */
265 if(msgid != NULL) {
266 msgid[msgidlen] = tmp;
267 }
268 if(masterk->xover_log_long == TRUE) {
269 /* print the xover formatted to look like a message header */
270 overv = master->xoverview;
271 while(overv != NULL ) {
272 tmp = overv->field[overv->fieldlen]; /* null terminate it */
273 overv->field[overv->fieldlen] = '\0';
274 print_phrases(masterk->logfp, "%v1% %v2%\n", overv->header, overv->field);
275 overv->field[overv->fieldlen] = tmp; /* restore it */
276 overv = overv->next ;
277 }
278 }
279 else if(masterk->logyn == KILL_LOG_LONG) {
280 /* print the xover as well */
281 print_phrases(masterk->logfp, xover_phrases[10],linein, NULL);
282 }
283 }
284 }
285 }
286
287 return retval;
288 }
289 /*----------------------------------------------------------------------------------------*/
get_xoverview(PMaster master)290 void get_xoverview(PMaster master) {
291 /* get in the xoverview.fmt list, so we can parse what xover returns later */
292 /* we'll put em in a linked list */
293
294 int done, retval, len, full;
295 char *resp;
296 POverview tmp, tmp2, curptr;
297
298 retval = RETVAL_OK;
299 curptr = NULL; /* where we are currently at in the linked list */
300
301 if(send_command(master, "list overview.fmt\r\n", &resp, 215) == RETVAL_OK) {
302 done = FALSE;
303 /* now get em in, until we hit .\n which signifies end of response */
304 while(done != TRUE) {
305 sgetline(master->sockfd, &resp, master->do_ssl, master->ssl_struct);
306 if(master->debug == TRUE) {
307 do_debug("Got line--%s", resp);
308 }
309 if(strcmp(resp, ".\n") == 0) {
310 done = TRUE;
311 }
312 else if(retval == RETVAL_OK) {
313 /* now save em if we haven't run out of memory */
314 len = strlen(resp);
315 /* does this have a full flag on it, which means the field name */
316 /* will be in the xover string */
317 full = (strstr(resp, ":full") == NULL) ? FALSE : TRUE;
318 /* now get rid of everything back to : */
319 while(resp[len] != ':') {
320 resp[len--] = '\0';
321 }
322 len++; /* so we point past colon */
323
324 if((tmp = malloc(sizeof(Overview))) == NULL) {
325 error_log(ERRLOG_REPORT, xover_phrases[12], NULL);
326 retval = RETVAL_ERROR;
327 }
328 else if((tmp->header = calloc(sizeof(char), len+1)) == NULL) {
329 error_log(ERRLOG_REPORT, xover_phrases[12], NULL);
330 retval = RETVAL_ERROR;
331 }
332 else {
333 /* initialize the structure */
334
335 /* do this first so we don't wipe out with null termination */
336 strncpy(tmp->header, resp, len); /* so we get the colon */
337 tmp->header[len] = '\0';
338 tmp->next = NULL;
339 tmp->field = NULL;
340 tmp->fieldlen = 0;
341 tmp->full = full;
342
343
344 if(curptr == NULL) {
345 /* at head of list */
346 curptr = tmp;
347 master->xoverview = tmp;
348 }
349 else {
350 /* add to linked list */
351 curptr->next = tmp;
352 curptr = tmp;
353 }
354 }
355 }
356 }
357 }
358 if(retval != RETVAL_OK) {
359 /* free up whatever alloced */
360 tmp = master->xoverview;
361 while (tmp != NULL) {
362 tmp2 = tmp;
363 if(tmp->header != NULL) {
364 free(tmp->header);
365 }
366 free(tmp);
367 tmp = tmp2;
368 }
369 master->xoverview = NULL;
370 }
371 if(master->debug == TRUE && (tmp = master->xoverview) != NULL) {
372 do_debug("--Xoverview.fmt list\n");
373 while(tmp != NULL) {
374 if(tmp->header != NULL) {
375 do_debug("item = %s -- full = %s\n", tmp->header, true_str(tmp->full));
376 }
377 tmp = tmp->next;
378 }
379 do_debug("--End Xoverview.fmt list\n");
380 }
381 }
382 /*-------------------------------------------------------------------------------------*/
chk_a_group(PMaster master,POneKill grp,POverview overv,char ** reason)383 int chk_a_group(PMaster master, POneKill grp, POverview overv, char **reason) {
384 /* return REASON_ if xover matches group */
385 /* linein should be at tab after the Message Number */
386
387 int i, match = REASON_NONE;
388 unsigned long bytes;
389 int lines;
390 char tchar, *tptr;
391 static char reasonstr[MAXLINLEN];
392
393 pmy_regex ptr;
394 POverview tmp;
395 *reason = reasonstr;
396
397 tmp = overv;
398 /* go thru each header field, and see if we must test against it */
399 while ( tmp != NULL && match == REASON_NONE) {
400 /* only do the test if we have a valid header & field to test against */
401 if(tmp->field != NULL && tmp->header != NULL) {
402 /* test the size of the body of the article */
403 if(grp->bodybig > 0 || grp->bodysmall > 0) {
404 if(strcasecmp(tmp->header, "Bytes:") == 0) {
405 sscanf(tmp->field, "%lu", &bytes); /* convert ascii to long */
406 if((grp->bodybig > 0) && (bytes > grp->bodybig)) {
407 match = REASON_HIBYTES;
408 }
409 else if((grp->bodysmall > 0) && (bytes < grp->bodysmall)) {
410 match = REASON_LOWBYTES;
411 }
412 }
413 }
414 /* test the number of lines in the article */
415 if((match == REASON_NONE) && (grp->hilines > 0 || grp->lowlines > 0)) {
416 if(strcasecmp(tmp->header, "Lines:") == 0) {
417 sscanf(tmp->field, "%d", &lines);
418 if((grp->hilines > 0) && (lines > grp->hilines)) {
419 match = REASON_HILINES;
420 }
421 else if((grp->lowlines > 0) && (lines < grp->lowlines)) {
422 match = REASON_LOWLINES;
423 }
424 }
425 }
426 /* check the number of xrefs */
427 if(match == REASON_NONE && grp->maxxref > 0) {
428 if(strcasecmp(tmp->header, "Xref:") == 0) {
429 i = 0;
430 tptr = tmp->field;
431 while(i <= grp->maxxref && *tptr != '\0' ) {
432 if(*tptr == COLON ) {
433 i++;
434 }
435 tptr++;
436 }
437 if(i > grp->maxxref) {
438 match = REASON_XREF;
439 }
440 }
441 }
442 /* match against any of the headers */
443 if(match == REASON_NONE) {
444 ptr = grp->list;
445 while(match == REASON_NONE && ptr != NULL) {
446 if(strcasecmp(tmp->header, ptr->header) == 0) {
447 /* we need to null terminate the field and restore it later */
448 tchar = tmp->field[tmp->fieldlen];
449 tmp->field[tmp->fieldlen] = '\0';
450 if(regex_block(tmp->field, ptr, master->debug) == TRUE) {
451 match = REASON_HEADER;
452 sprintf(reasonstr, "%s-%s%s", xover_reasons[REASON_HEADER], tmp->header, tmp->field);
453 }
454 tmp->field[tmp->fieldlen] = tchar;
455 }
456 ptr = ptr->next;
457 }
458 }
459 }
460 tmp = tmp->next;
461 }
462 return match;
463 }
464 /*---------------------------------------------------------------------------*/
match_group(char * match_grp,char * group,int debug)465 int match_group(char *match_grp, char *group, int debug) {
466 /* does match match group? match may contain wildcards */
467 int match = FALSE;
468 if(match_grp != NULL && group != NULL) {
469 if(debug == TRUE) {
470 do_debug("Xover - matching %s against %s\n", match_grp, group);
471 }
472
473 while( *group == *match_grp && *group != '\0') {
474 group++;
475 match_grp++;
476 }
477 if(*match_grp == '\0' || *match_grp == '*') {
478 /* wildcard match or end of string, they match so far, so they match */
479 match = TRUE;
480 }
481 if(debug == TRUE) {
482 do_debug("match = %s\n", true_str(match));
483 }
484
485 }
486 return match;
487 }
488 /*-------------------------------------------------------------------------------*/
get_xover(PMaster master,char * group,long startnr,long endnr)489 int get_xover(PMaster master, char *group, long startnr, long endnr) {
490
491 int len, nr, done, retval = RETVAL_OK;
492 char cmd[MAXLINLEN], *resp, *ptr, *msgid;
493 long msgnr;
494
495 sprintf(cmd, "xover %ld-%ld\r\n", startnr,endnr);
496 retval = send_command(master, cmd, &resp, 0);
497 if(retval == RETVAL_OK) {
498 number(resp, &nr);
499 switch(nr) {
500 default:
501 case 412:
502 case 502:
503 /* abort on these errors */
504 error_log(ERRLOG_REPORT, xover_phrases[0], resp, NULL);
505 retval = RETVAL_NOXOVER;
506 break;
507 case 420:
508 /* no articles available, no prob */
509 break;
510 case 224:
511 /* got a list coming at us */
512 done = FALSE;
513 while((done == FALSE) && (retval == RETVAL_OK)) {
514 len = sgetline(master->sockfd, &resp, master->do_ssl, master->ssl_struct);
515 if(master->debug == TRUE) {
516 do_debug("Got xover line: %s", resp);
517 }
518
519 /* have we got the last one? */
520 if((len == 2) && (strcmp(resp, ".\n") == 0)) {
521 done = TRUE;
522 }
523 else if(len < 0) {
524 retval =RETVAL_ERROR;
525 }
526 else {
527 ptr = get_long(resp, &msgnr); /* get our message number */
528 msgid = find_msgid(master, ptr); /* find the msg-id */
529 if(msgid == NULL) {
530 error_log(ERRLOG_REPORT, xover_phrases[13], resp);
531 }
532 else {
533 /* alloc the memory for it */
534 retval= allocnode(master, msgid, MANDATORY_OPTIONAL, group, msgnr);
535 }
536
537 }
538 }
539 break;
540 }
541 }
542 return retval;
543 }
544 /*-----------------------------------------------------------------------------------*/
545 /* find the msgid in a xover, and return it null-terminated, so can alloc memory, etc*/
546 /*-----------------------------------------------------------------------------------*/
find_msgid(PMaster master,char * xover)547 char *find_msgid(PMaster master, char *xover) {
548
549 char *ptr, *ptr2, *retval = NULL;
550 POverview pov;
551
552 pov = master->xoverview;
553 ptr = xover;
554
555 /* now go thru and find the Message-ID in the xoverview, using the overview.fmt */
556 /* to tell us which field is which */
557
558 if(ptr != NULL) {
559 while ( *ptr != '\0' && pov != NULL && strcmp(pov->header, "Message-ID:") != 0) {
560 /* go to the next field, past the tab */
561 pov = pov->next;
562 while(*ptr != '\t' && *ptr != '\0') {
563 ptr++;
564 }
565 if(*ptr != '\0') {
566 ptr++; /* past the tab */
567 }
568 }
569 if(*ptr != '\0' && pov != NULL) {
570 /* bingo, found it */
571 /* find the start of the msgid */
572 while(*ptr != '\0' && *ptr != '<') {
573 ptr++;
574 }
575 if(ptr != NULL) {
576 ptr2 = ptr;
577 /* NULL terminate the msgid */
578 while(*ptr2 != '\0' && *ptr2 != '>') {
579 ptr2++;
580 }
581 /* ONLY if we find a valid start and end to the MessageId */
582 /* do we set a valid return value */
583 if(*ptr2 != '\0') {
584 *(ptr2+1) = '\0';
585 retval = ptr;
586 }
587 }
588 }
589 }
590
591 return retval;
592 }
593
594
595
596
597
598