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