1 /*****************************************************************************
2  * HPT --- FTN NetMail/EchoMail Tosser
3  *****************************************************************************
4  * Copyright (C) 1997-1999
5  *
6  * Matthias Tichy
7  *
8  * Fido:     2:2433/1245 2:2433/1247 2:2432/605.14
9  * Internet: mtt@tichy.de
10  *
11  * Grimmestr. 12         Buchholzer Weg 4
12  * 33098 Paderborn       40472 Duesseldorf
13  * Germany               Germany
14  *
15  * This file is part of HPT.
16  *
17  * HPT is free software; you can redistribute it and/or modify it
18  * under the terms of the GNU General Public License as published by the
19  * Free Software Foundation; either version 2, or (at your option) any
20  * later version.
21  *
22  * HPT is distributed in the hope that it will be useful, but
23  * WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
25  * General Public License for more details.
26  *
27  * You should have received a copy of the GNU General Public License
28  * along with HPT; see the file COPYING.  If not, write to the Free
29  * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
30  *****************************************************************************
31  * $Id$
32  */
33 #include <stdlib.h>
34 #include <string.h>
35 #include <stdio.h>
36 #include <ctype.h>
37 #include <assert.h>
38 #include <fcommon.h>
39 #include <areafix/areafix.h>
40 #include <global.h>
41 #include <seenby.h>
42 #include <huskylib/xstr.h>
43 #include <fidoconf/common.h>
44 
compare(const void * first,const void * second)45 int compare(const void *first, const void *second)
46 {
47    if ( ((s_seenBy*) first)->net < ((s_seenBy*) second)->net) return -1;
48    else
49       if ( ((s_seenBy*) first)->net > ((s_seenBy*) second)->net) return 1;
50       else if ( ((s_seenBy*) first)->node < ((s_seenBy*) second)->node) return -1;
51            else if ( ((s_seenBy*) first)->node > ((s_seenBy*) second)->node) return 1;
52    return 0;
53 }
54 
sortSeenBys(s_seenBy * seenBys,UINT count)55 void sortSeenBys(s_seenBy *seenBys, UINT count)
56 {
57 	assert(seenBys != NULL || count == 0);
58 		if(count)
59 			qsort(seenBys, count, sizeof(s_seenBy), compare);
60 }
61 
createControlText(s_seenBy seenBys[],UINT seenByCount,char * lineHeading)62 char *createControlText(s_seenBy seenBys[], UINT seenByCount, char *lineHeading)
63 {
64    #define size 81
65    #define addr2dSize 13
66    UINT  i;
67    char *text=NULL, *line = NULL, addr2d[addr2dSize];
68 
69    if (seenByCount==0) {              /* return empty control line */
70       xstrcat(&text, lineHeading);
71       /* reserve one byte for \r */
72          text = (char *) safe_realloc(text, strlen(text)+2);
73    } else {
74 
75       line = safe_malloc ((size_t) size);
76 
77       sprintf(addr2d, "%u/%u", seenBys[0].net, seenBys[0].node);
78       text = (char *) safe_malloc((size_t) size);
79       text[0]='\0';
80       strncpy(line, lineHeading, size);
81       strncat(line, addr2d, size);
82       for (i=1; i < seenByCount; i++) {
83 
84 /*  fix for double seen-by's (may be after ignoreSeen) */
85 /*  NOTE! fixed seen-by's hides shitty tossers! */
86 /*  it is not recommended to uncomment this. */
87 /* 		 if (config->ignoreSeenCount && */
88 /* 			 seenBys[i-1].net == seenBys[i].net && */
89 /* 			 seenBys[i-1].node == seenBys[i].node) continue; */
90 
91          if (seenBys[i-1].net == seenBys[i].net)
92             sprintf(addr2d, " %u", seenBys[i].node);
93          else
94             sprintf(addr2d, " %u/%u", seenBys[i].net, seenBys[i].node);
95 
96          if (strlen(line)+strlen(addr2d) > size-3) {
97             /* if line would be greater than 79 characters, make new line */
98             strcat(text, line);
99             strncat(text, "\r", size);
100             text = (char *) safe_realloc(text,strlen(text)+size);
101             strncpy(line, lineHeading, size);
102             /*  start new line with full 2d information */
103             sprintf(addr2d, "%u/%u", seenBys[i].net, seenBys[i].node);
104          }
105          strcat(line, addr2d);
106       }
107 	  /*  reserve only needed space + ending \r */
108 	  text = (char *) safe_realloc(text, strlen(text)+strlen(line)+2);
109 	  strcat(text,line);
110 	  nfree(line);
111    }
112 
113    strncat(text, "\r", size);
114 
115    return text;
116 }
117 
createSeenByArrayFromMsg(s_area * area,s_message * msg,s_seenBy ** seenBys,UINT * seenByCount)118 void createSeenByArrayFromMsg(s_area *area, s_message *msg, s_seenBy **seenBys, UINT *seenByCount)
119 {
120     char *seenByText=NULL, *start = NULL, *token = NULL;
121     unsigned long temp;
122     char *endptr = NULL;
123     UINT seenByAlloced;
124 
125     unused(area);
126 
127     *seenByCount = seenByAlloced = 0;
128 
129     start = strrstr(msg->text, " * Origin:"); /*  jump over Origin */
130     if (start == NULL) start = msg->text;
131 
132     /*  find beginning of seen-by lines */
133     do {
134 	start = strstr(start, "SEEN-BY:");
135 	if (start == NULL) return;
136 	start += 8; /*  jump over SEEN-BY: */
137 
138 	while (*start == ' ') start++; /*  find first word after SEEN-BY: */
139     } while (!isdigit(*start));
140 
141     /*  now that we have the start of the SEEN-BY's we can tokenize the lines and read them in */
142     xstrcat(&seenByText, start);
143 
144     token = strtok(seenByText, " \r\t\376");
145     for (; token != NULL; token = strtok(NULL, " \r\t\376")) {
146 	if (isdigit(*token)) {
147 	    /*  parse token */
148 	    temp = strtoul(token, &endptr, 10);
149 	    if (*endptr==':') {
150 		token = endptr+1;
151 		temp = strtoul(token, &endptr, 10);
152 	    }
153 	    if (*endptr && *endptr != '/')
154 		continue;
155 
156 	    /*  get new memory */
157 	    if ((*seenByCount)++ >= seenByAlloced)
158 		(*seenBys) = (s_seenBy*) safe_realloc(*seenBys, sizeof(s_seenBy) * (seenByAlloced+=32));
159 
160 	    if ((*endptr) == '\0') {
161 		/*  only node aka */
162 		(*seenBys)[*seenByCount-1].node = (UINT16) temp;
163 		/*  use net aka of last seenBy */
164 		if(*seenByCount >= 2)
165 		    (*seenBys)[*seenByCount-1].net = (*seenBys)[*seenByCount-2].net;
166 		else
167 		{ /* Shouldn't really happen. The best way out is unclear. */
168 		  /* I propose to drop incorrect seen-by's, as possible dupes seem to be lesser evil
169 		     compared to possible loss of mail if choose to propagate mail with buggy control
170 			 lines --Elfy 2010-03-18 */
171 			w_log(LL_ALERT, "Buggy SEEN-BY line encountered. Invalid node was removed from the line!"); /* FIXME: print msgid to pinpoint problem? */
172 			--*seenByCount;
173 		}
174 	    } else {
175 		/*  net and node aka */
176 		(*seenBys)[*seenByCount-1].net = (UINT16) temp;
177 		/*  eat up '/' */
178 		endptr++;
179 		(*seenBys)[*seenByCount-1].node = (UINT16) atol(endptr);
180 	    }
181 	} else if (strcmp(token,"SEEN-BY:")!=0) break; /*  not digit and not SEEN-BY */
182 
183     } /*  end while */
184 
185     if (*seenByCount != seenByAlloced)
186 	{
187 		if(*seenByCount > 0)
188 			(*seenBys) = (s_seenBy*) safe_realloc(*seenBys, sizeof(s_seenBy) * (*seenByCount));
189 		else
190 		{
191 			nfree(*seenBys);
192 			seenByAlloced = 0;
193 		}
194 	}
195     /* test output for reading of seenBys... */
196 #ifdef DEBUG_HPT
197     for (i=0; i < *seenByCount; i++) printf("%u/%u ", (*seenBys)[i].net, (*seenBys)[i].node);
198 #endif
199 /*    exit(2); */
200 
201     nfree(seenByText);
202 }
203 
createPathArrayFromMsg(s_message * msg,s_seenBy ** seenBys,UINT * seenByCount)204 void createPathArrayFromMsg(s_message *msg, s_seenBy **seenBys, UINT *seenByCount)
205 {
206 
207     /*  DON'T GET MESSED UP WITH THE VARIABLES NAMED SEENBY... */
208     /*  THIS FUNCTION READS PATH!!! */
209 
210     char *seenByText=NULL, *start = NULL, *token = NULL;
211     char *endptr = NULL;
212     unsigned long temp;
213     UINT seenByAlloced;
214 #ifdef DEBUG_HPT
215     int i;
216 #endif
217 
218     *seenByCount = seenByAlloced = 0;
219 
220     start = strrstr(msg->text, " * Origin:"); /*  jump over Origin */
221     if (start == NULL)
222 		start = msg->text;
223 
224     /*  find beginning of path lines */
225     do
226 	{
227 		start = strstr(start, "\001PATH:");
228 		if (start == NULL)
229 			return;
230 		for (endptr = strchr(start, '\r'); endptr; endptr = strchr(endptr, '\r'))
231 		{
232 			while (*endptr == '\r' || *endptr == '\n')
233 				endptr++;
234 			if (strncmp(endptr, "\001PATH:", 6))
235 				break; /* not path line */
236 		}
237 		if (endptr && strstr(endptr, "\001PATH:"))
238 		{
239 			start = endptr;
240 			continue; /* only last path lines are valid */
241 		}
242 		start += 7; /*  jump over PATH: */
243 
244 		while (*start == ' ')
245 			start++; /*  find first word after PATH: */
246     } while (!isdigit(*start));
247 
248     /*  now that we have the start of the PATH' so we can tokenize the lines and read them in */
249     xstrcat(&seenByText, start);
250 
251     token = strtok(seenByText, " \r\t\376");
252     for (; token != NULL; token = strtok(NULL, " \r\t\376"))
253 	{
254 		if (isdigit(*token))
255 		{
256 			/*  parse token */
257 			temp = strtoul(token, &endptr, 10);
258 			if (*endptr==':')
259 			{
260 				token = endptr+1;
261 				temp = strtoul(token, &endptr, 10);
262 			}
263 			if (*endptr && *endptr != '/')
264 				continue;
265 
266 			/*  get new memory */
267 			if ((*seenByCount)++ >= seenByAlloced)
268 				(*seenBys) = (s_seenBy*) safe_realloc(*seenBys, sizeof(s_seenBy) * (seenByAlloced+=32));
269 
270 			if ((*endptr) == '\0')
271 			{
272 				/*  only node aka */
273 				(*seenBys)[*seenByCount-1].node = (UINT16) temp;
274 				/*  use net aka of last seenBy */
275 				if(*seenByCount >= 2)
276 				{
277 					(*seenBys)[*seenByCount-1].net = (*seenBys)[*seenByCount-2].net;
278 				}
279 				else
280 				{
281 					w_log(LL_ALERT, "Buggy PATH line encountered. Invalid node was removed from the line!");
282 					--*seenByCount;
283 				}
284 			}
285 			else
286 			{
287 				/*  net and node aka */
288 				(*seenBys)[*seenByCount-1].net = (UINT16) temp;
289 				/*  eat up '/' */
290 				endptr++;
291 				(*seenBys)[*seenByCount-1].node = (UINT16) atol(endptr);
292 			}
293 		}
294 		else if (strcmp(token, "\001PATH:")!=0)
295 			break; /*  not digit and not PATH */
296 	}
297 
298     if (*seenByCount != seenByAlloced)
299 		(*seenBys) = (s_seenBy*) safe_realloc(*seenBys, sizeof(s_seenBy) * (*seenByCount));
300 
301     /*  test output for reading of paths... */
302 #ifdef DEBUG_HPT
303     for (i=0; i < *seenByCount; i++)
304 		printf("%u/%u ", (*seenBys)[i].net, (*seenBys)[i].node);
305 #endif
306     /* exit(2); */
307 
308     nfree(seenByText);
309 }
310 
311 /**
312   * This function returns 0 if the link is not in seenBy else it returns 1.
313   */
314 
checkLink(s_seenBy * seenBys,UINT seenByCount,s_link * link,hs_addr pktOrigAddr,s_area * area)315 int checkLink(s_seenBy *seenBys, UINT seenByCount, s_link *link,
316 	      hs_addr pktOrigAddr, s_area *area)
317 {
318     UINT i,j;
319 
320     /*  the link where we got the mail from */
321     if (addrComp(pktOrigAddr, link->hisAka) == 0) return 1;
322 
323     if (seenBys==NULL) return 0;
324 
325     /*  a point always gets the mail */
326     /*  if (link->hisAka.point != 0) return 0; */
327 
328     /*  send the mail to links within our node-system */
329     if ((link->hisAka.zone == area->useAka->zone) &&
330         (link->hisAka.net  == area->useAka->net) &&
331         (link->hisAka.node == area->useAka->node))
332 	return 0;
333 
334     for (i=0; i < seenByCount; i++) {
335 	if ((link->hisAka.net==seenBys[i].net) &&
336 	    (link->hisAka.node==seenBys[i].node)) {
337 
338 	    for (j=0; j < config->ignoreSeenCount; j++) {
339 		if (config->ignoreSeen[j].net == seenBys[i].net &&
340 		    config->ignoreSeen[j].node == seenBys[i].node) {
341 		    link->sb = 1; /*  fix for double seen-bys */
342 		    return 0;
343 		}
344 	    }
345 	    for (j=0; j < area->sbignCount; j++) {
346 		if (area->sbign[j].net == seenBys[i].net &&
347 		    area->sbign[j].node == seenBys[i].node) {
348 		    link->sb = 1; /*  fix for double seen-bys */
349 		    return 0;
350 		}
351 	    }
352 
353 	    return 1;
354 	}
355     }
356     return 0;
357 }
358 
359 /*
360   This function puts all the links of the echoarea in the newLink
361   array who does not have got the mail, zoneLinks - the links who
362   receive msg with stripped seen-by's.
363 */
364 
createNewLinkArray(s_seenBy * seenBys,UINT seenByCount,s_area * echo,s_arealink *** newLinks,s_arealink *** zoneLinks,s_arealink *** otherLinks,hs_addr pktOrigAddr)365 void createNewLinkArray(s_seenBy *seenBys, UINT seenByCount,
366 			           s_area *echo,
367                        s_arealink ***newLinks,
368 			           s_arealink ***zoneLinks,
369                        s_arealink ***otherLinks,
370                        hs_addr pktOrigAddr)
371 {
372     UINT i, lFound = 0, zFound = 0, oFound = 0;
373 
374     *newLinks =  (s_arealink **)safe_calloc(echo->downlinkCount,sizeof(s_arealink*));
375     *zoneLinks = (s_arealink **)safe_calloc(echo->downlinkCount,sizeof(s_arealink*));
376     *otherLinks =(s_arealink **)safe_calloc(echo->downlinkCount,sizeof(s_arealink*));
377 
378     for (i=0; i < echo->downlinkCount; i++) {
379         /*  is the link in SEEN-BYs? */
380         if ( checkLink(seenBys, seenByCount, echo->downlinks[i]->link,
381             pktOrigAddr, echo)!=0) continue;
382         /*  link with "export off" */
383         if (echo->downlinks[i]->aexport == 0) continue;
384 
385         if (pktOrigAddr.zone==echo->downlinks[i]->link->hisAka.zone) {
386             /*  links with same zone */
387             if(echo->downlinks[i]->link->reducedSeenBy)
388             {
389                 (*otherLinks)[oFound++] = echo->downlinks[i];
390             }
391             else
392             {
393                 (*newLinks)[lFound++] = echo->downlinks[i];
394             }
395         } else {
396             /*  links in different zones */
397             (*zoneLinks)[zFound++] = echo->downlinks[i];
398         }
399     }
400     if(lFound == 0)
401         nfree(*newLinks);
402     if(zFound == 0)
403         nfree(*zoneLinks);
404     if(oFound == 0)
405         nfree(*otherLinks);
406 }
407 
408 
409 /*
410    Create a new SEEN-BY array from another one for AKAs found in an address list.
411 */
412 
createFilteredSeenByArray(s_seenBy * seenBys,UINT seenByCount,s_seenBy ** newSeenBys,UINT * newSeenByCount,ps_addr addr,unsigned int addrCount)413 void createFilteredSeenByArray(s_seenBy *seenBys, UINT seenByCount,
414     s_seenBy **newSeenBys, UINT *newSeenByCount,
415     ps_addr addr, unsigned int addrCount)
416 {
417     unsigned int i, j;
418 
419     /* sanity checks */
420     if ((newSeenBys == NULL) || (newSeenByCount == NULL)) return;
421     if ((seenByCount > 0) && (seenBys == NULL)) return;
422     if ((addrCount > 0) && (addr == NULL)) return;
423 
424     *newSeenByCount = 0;
425 
426     /* get memory for array (required at maximum) */
427     (*newSeenBys) = (s_seenBy*)safe_calloc(addrCount, sizeof(s_seenBy));
428 
429     /* search for matches */
430     for (i = 0; i < addrCount; i++)          /* address array */
431     {
432         for (j = 0; j < seenByCount; j++)    /* SEEN-BY array */
433         {
434             if (((UINT16)addr[i].net == seenBys[j].net) &&
435                 ((UINT16)addr[i].node == seenBys[j].node))
436             {
437                 /* copy this one to new array */
438                 (*newSeenBys)[*newSeenByCount].net = (UINT16)addr[i].net;
439                 (*newSeenBys)[*newSeenByCount].node = (UINT16)addr[i].node;
440                 (*newSeenByCount)++;
441             }
442         }
443     }
444 }
445 
446 
447 /*
448    strip specific AKAs (address array) from SEEN-BY array
449 */
450 
stripSeenByArray(s_seenBy ** seenBys,UINT * seenByCount,ps_addr addr,unsigned int addrCount)451 void stripSeenByArray(s_seenBy **seenBys, UINT *seenByCount,
452     ps_addr addr, unsigned int addrCount)
453 {
454     unsigned int i, j, k;
455     unsigned int counter;
456 
457     /* sanity check */
458     if ((seenBys == NULL) || (seenByCount == NULL)) return;
459 
460     counter = *seenByCount;   /* local variable to speed up access */
461 
462     /* search for matches */
463     for (i = 0; i < addrCount; i++)          /* address array */
464     {
465         for (j = 0; j < counter; j++)        /* SEEN-BY array */
466         {
467             if (((UINT16)addr[i].net == (*seenBys)[j].net) &&
468                 ((UINT16)addr[i].node == (*seenBys)[j].node))
469             {
470                 /* remove this AKA by moving remaining SEEN-BYs one up */
471                 counter--;
472 
473                 for (k = j; k < counter; k++)
474                 {
475                     (*seenBys)[k].net = (*seenBys)[k + 1].net;
476                     (*seenBys)[k].node = (*seenBys)[k + 1].node;
477                 }
478             }
479         }
480     }
481 
482     *seenByCount = counter;   /* update counter */
483 }
484