1 /******************************************************************************
2  * HPT --- FTN NetMail/EchoMail Tosser
3  ******************************************************************************
4  * carbon.c : functions for making carbon copy
5  *
6  * by Max Chernogor <mihz@ua.fm>, 2:464/108@fidonet
7  *
8  * This file is part of HPT.
9  *
10  * HPT is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU General Public License as published by the
12  * Free Software Foundation; either version 2, or (at your option) any
13  * later version.
14  *
15  * HPT is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with HPT; see the file COPYING.  If not, write to the Free
22  * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23  *****************************************************************************
24  * $Id$
25  */
26 
27 
28 #include <stdlib.h>
29 #include <string.h>
30 
31 #include <huskylib/compiler.h>
32 
33 #ifdef HAS_PROCESS_H
34 #  include <process.h>
35 #endif
36 
37 #ifdef HAS_IO_H
38 #include <io.h>
39 #endif
40 
41 #ifdef HAS_UNISTD_H
42 #include <unistd.h>
43 #endif
44 
45 #ifdef HAS_DOS_H
46 #include <dos.h>
47 #endif
48 
49 #include <fidoconf/fidoconf.h>
50 #include <fidoconf/common.h>
51 #include <huskylib/recode.h>
52 #include <huskylib/temp.h>
53 #include <huskylib/xstr.h>
54 #include <smapi/msgapi.h>
55 
56 #include <areafix/areafix.h>
57 #include "global.h"
58 #include "toss.h"
59 
60 extern s_statToss statToss;
61 
MessForCC(s_message * msg)62 s_message* MessForCC(s_message *msg)
63 {
64     s_message* CCmsg;
65 
66     if(config->carbonCount == 0)
67         return NULL;
68 
69     CCmsg = (s_message*) safe_calloc(1,sizeof(s_message));
70 
71     CCmsg->origAddr.zone  = msg->origAddr.zone;
72     CCmsg->origAddr.net   = msg->origAddr.net;
73     CCmsg->origAddr.node  = msg->origAddr.node;
74     CCmsg->origAddr.point = msg->origAddr.point;
75 
76     CCmsg->destAddr.zone  = msg->destAddr.zone;
77     CCmsg->destAddr.net   = msg->destAddr.net ;
78     CCmsg->destAddr.node  = msg->destAddr.node;
79     CCmsg->destAddr.point = msg->destAddr.point;
80 
81     xstrcat(&(CCmsg->fromUserName), msg->fromUserName);
82     xstrcat(&(CCmsg->toUserName), msg->toUserName);
83     xstrcat(&(CCmsg->subjectLine), msg->subjectLine);
84     xstrcat(&(CCmsg->text), msg->text);
85 
86     strcpy( (char*)CCmsg->datetime, (char*)msg->datetime );
87     CCmsg->attributes = msg->attributes;
88     CCmsg->textLength = msg->textLength;
89     CCmsg->netMail    = msg->netMail;
90     CCmsg->recode     = msg->recode;
91 
92     return CCmsg;
93 }
94 
processExternal(s_area * echo,s_message * msg,s_carbon carbon)95 int processExternal (s_area *echo, s_message *msg,s_carbon carbon)
96 {
97     FILE *msgfp = NULL;
98     char *fname = NULL;
99     char *progname = carbon.areaName, *execstr = NULL, *p = NULL;
100     int  rc;
101 
102     w_log(LL_CARBON, "Carbon external from area %s to program \"%s\"%s%s%s: msg from \"%s\" %s to \"%s\"%s%s",
103                       echo->areaName? echo->areaName:"netmail",
104                       progname? progname:"",
105                       carbon.reason? " at reason \"":"", carbon.reason? carbon.reason:"", carbon.reason? "\"":"",
106                       msg->fromUserName, aka2str(msg->origAddr), msg->toUserName,
107                       msg->netMail? " ":"", msg->netMail? aka2str(msg->destAddr):""
108          );
109 
110 #ifdef HAS_popen_close
111     if (*progname == '|') {
112 	msgfp = popen(progname + 1, "w");
113     } else
114 #endif
115 	msgfp = createTempTextFile(config->tempDir, &fname);
116 
117     if (!msgfp) {
118 	w_log(LL_ERR, "external process %s: cannot create file", progname);
119 	return 1;
120     } else
121         w_log(LL_FILE,"toss.c:processExternal() opened %s %s", fname?"file":"pipe", fname?fname:progname);
122     /* Output header info */
123     if (!msg->netMail) fprintf(msgfp, "Area: %s\n", echo->areaName);
124     fprintf(msgfp, "From: \"%s\" %s\n", msg->fromUserName, aka2str(msg->origAddr));
125     fprintf(msgfp, "To:   \"%s\" %s\n", msg->toUserName, aka2str(msg->destAddr));
126     fprintf(msgfp, "Date: \"%s\"\n", msg->datetime);
127     fprintf(msgfp, "Subject: \"%s\"\n\n", msg->subjectLine);
128     /* Output msg text */
129     for (p = msg->text; *p ; p++)
130       if (*p == '\r')
131         fputc('\n', msgfp);
132       else
133         fputc(*p, msgfp);
134     fputc('\n', msgfp);
135 #ifdef HAS_popen_close
136     if (*progname == '|') {
137       pclose(msgfp);
138       rc = 0;
139     } else
140 #endif
141     {
142       /* Execute external program */
143       fclose(msgfp);
144       execstr = safe_malloc(strlen(progname)+strlen(fname)+3);
145       if (*progname == '|')
146               sprintf(execstr, "%s < %s", progname+1, fname);
147       else    sprintf(execstr, "%s %s", progname, fname);
148 #ifdef __NT__
149       CharToOem(execstr, execstr); /*  this is really need? */
150 #endif
151       rc = cmdcall(execstr);
152       nfree(execstr);
153       unlink(fname);
154       nfree(fname);
155     }
156 /*    if (rc == -1 || rc == 127) */
157     if (rc)  /* system() return exit status returned by shell */
158 	w_log(LL_ERR, "Execution of external program failed. Cmd is: \"%s\", return code %d", execstr, rc);
159     return 0;
160 
161 }
162 
163 /* area - area to carbon messages, echo - original echo area */
processCarbonCopy(s_area * area,s_area * echo,s_message * msg,s_carbon carbon)164 int processCarbonCopy (s_area *area, s_area *echo, s_message *msg, s_carbon carbon)
165 {
166     char *p, *text, *line, *old_text, *reason = carbon.reason;
167     int i, old_textLength, aexport = carbon.aexport, rc = 0;
168 
169     statToss.CC++;
170 
171     old_textLength = msg->textLength;
172     old_text = msg->text;
173     i = old_textLength;
174 
175     /*  recoding from internal to transport charSet if needed */
176 /* this must be jub of putMsgInArea.
177     if (config->outtab) {
178 	if (msg->recode & REC_TXT) {
179 	    recodeToTransportCharset((char*)msg->text);
180 	    msg->recode &= ~REC_TXT;
181 	}
182 	if (msg->recode & REC_HDR) {
183 	    recodeToTransportCharset((char*)msg->fromUserName);
184 	    recodeToTransportCharset((char*)msg->toUserName);
185 	    recodeToTransportCharset((char*)msg->subjectLine);
186 	    msg->recode &= ~REC_HDR;
187 	}
188 	if (reason) recodeToTransportCharset((char*)reason);
189     }
190 */
191     msg->text = NULL;
192     msg->textLength = 0;
193 
194     line = old_text;
195 
196     if (!msg->netMail) {
197         xstrscat(&msg->text,
198                  (aexport) ? "AREA:" : "",
199                  (aexport) ? area->areaName : "",
200                  (aexport) ? "\r" : "",
201                  "\001AREA:", echo->areaName,
202                  "\r" , NULLP);
203     }
204     if (strncmp(line, "AREA:", 5) == 0) {
205         /*  jump over AREA:xxxxx\r */
206         line+=5;
207         while (*line && *line != '\r') line++;
208         if (*line) line++;
209     }
210 
211     while(*line == '\001')
212     {
213         p = strchr(line, '\r');
214         if(!p)
215             break;
216         /* Temporary make it \0 terminated string */
217         *p = '\0';
218         xstrcat(&msg->text,line);
219         /* and then we *must* put '\r' back. */
220         xstrcat(&msg->text, "\r");
221         *p = '\r';
222         line = p+1;
223     }
224 
225     text = line; /* may be old_test or old_text w/o begining kluges */
226 
227     if (!msg->netMail) {
228         if ((!config->carbonKeepSb) && (!area->keepsb)) {
229             line = strrstr(text, " * Origin:");
230             if (NULL != (p = strstr(line ? line : text,"\rSEEN-BY:")))
231                 i = (size_t) (p - text) + 1;
232         }
233         xstrscat(&msg->text,
234             msg->text ? (msg->text[strlen(msg->text)-1] == '\r' ?"":"\r") : "" ,
235             (config->carbonExcludeFwdFrom) ? "" : " * Forwarded from area '",
236             (config->carbonExcludeFwdFrom) ? "" : echo->areaName,
237             (config->carbonExcludeFwdFrom) ? "" : "'\r",
238             (reason) ? reason : "",
239             (reason) ? "\r" : "", NULLP);
240         msg->textLength = strlen(msg->text);
241     }
242 
243     xstralloc(&msg->text,i); /*  add i bytes */
244     strncat(msg->text,text,i); /*  copy rest of msg */
245     msg->textLength += i;
246 
247     if (!aexport) {
248 	if (msg->netMail) rc = putMsgInArea(area,msg,0,MSGSENT);
249 	else rc = putMsgInArea(area,msg,0,0);
250 	area->imported++;  /*  area has got new messages */
251     }
252     else if (!msg->netMail) {
253 	rc = processEMMsg(msg, *area->useAka, 1, 0);
254     } else
255 	rc = processNMMsg(msg, NULL, area, 1, 0);
256 
257     w_log(LL_CARBON, "Carbon %s from %s to %s%s%s%s: msg from \"%s\" %s to \"%s\"%s%s. Result code is %d", carbon.move? "move":"copy",
258                       echo->areaName? echo->areaName:"netmail",
259                       area->areaName? area->areaName:"netmail",
260                       reason? " at reason \"":"", reason? reason:"", reason? "\"":"",
261                       msg->fromUserName, aka2str(msg->origAddr), msg->toUserName,
262                       msg->netMail? " ":"", msg->netMail? aka2str(msg->destAddr):"",
263                       rc
264          );
265 
266     nfree(msg->text);
267     msg->textLength = old_textLength;
268     msg->text = old_text;
269     msg->recode &= ~REC_TXT; /*  old text is always in Transport Charset */
270     if (config->intab && reason) recodeToInternalCharset((char*)reason);
271 
272     return rc;
273 }
274 
275 
276 /* Does carbon copying */
277 /* Return value: 0 if nothing happend, 1 if there was a carbon copy,
278    > 1 if there was a carbon move or carbon delete*/
carbonCopy(s_message * msg,XMSG * xmsg,s_area * echo)279 int carbonCopy(s_message *msg, XMSG *xmsg, s_area *echo)
280 {
281     unsigned int i, rc = 0, result=0;
282     char *testptr = NULL, *testptr2 = NULL, *pattern = NULL;
283     s_area *area = NULL;
284     s_carbon *cb=&(config->carbons[0]);
285     s_area **copiedTo = NULL;
286     int copiedToCount = 0;
287     int ncop;
288 
289     w_log( LL_FUNC, "carbonCopy() begin");
290 
291 	if(!msg)
292         return 0;
293     if (echo->ccoff==1)
294         return 0;
295     if (echo->msgbType==MSGTYPE_PASSTHROUGH && config->exclPassCC)
296         return 0;
297 
298     for (i=0; i<config->carbonCount; i++,++cb) {
299         /* Dont come to use netmail on echomail and vise verse */
300         if (!(cb->rule & CC_AND) /* move and netmail have meaning only in last carbon in group */
301 				&& cb->move != 2 /* type of mail doesn't matter for CarbonDelete */
302 				&& !msg->netMail != !cb->netMail) continue; /* if types differ the rule doesn't apply */
303 
304         area = cb->area;
305 
306         if(!(cb->rule&CC_AND))  /* not AND & not AND-NOT */
307         {
308             if (!cb->extspawn && /*  fix for extspawn */
309                 cb->areaName != NULL && /*  fix for carbonDelete */
310                 /*  dont CC to the echo the mail comes from */
311                 !sstricmp(echo->areaName,area->areaName)
312                 )
313                 continue;
314         }
315         switch (cb->ctype) {
316         case ct_to:
317             result=patimat(msg->toUserName,cb->str);
318             break;
319 
320         case ct_from:
321             result=patimat(msg->fromUserName,cb->str);
322             break;
323 
324         case ct_kludge:
325         case ct_msgtext:
326             testptr=msg->text;
327             /* skip area: kludge */
328             if (strncmp(testptr, "AREA:", 5) == 0)
329             {
330                 if ((testptr = strchr(testptr, '\r')) != NULL)
331                     testptr++;
332             }
333             /* cb->str is substring, so pattern must be "*str*" */
334             pattern=safe_malloc(strlen(cb->str)+3);
335             *pattern='*';
336             sstrcpy(pattern+1, cb->str);
337             strcat(pattern, "*");
338             result=0;
339 
340             /* check the message line by line */
341             while (testptr) {
342                 testptr2 = strchr(testptr, '\r');
343                 if ((*testptr == '\001' && cb->ctype == ct_kludge) ||
344                     (*testptr != '\001' && cb->ctype == ct_msgtext)) {
345                     if (testptr2) *testptr2 = '\0';
346                     result = patimat(testptr, pattern);
347                     if (testptr2) *testptr2 = '\r';
348                     if (result) break;
349                 }
350                 if (testptr2)
351                     testptr = testptr2+1;
352                 else
353                     break;
354             }
355             nfree(pattern);
356             break;
357 
358         case ct_subject:
359             result=patimat(msg->subjectLine,cb->str);
360             break;
361 
362         case ct_addr:
363             result=!addrComp(msg->origAddr, cb->addr);
364             break;
365 
366         case ct_fromarea:
367             result=patimat(echo->areaName,cb->str);
368             break;
369 
370         case ct_group:
371             if(echo->group!=NULL){
372                 /* cb->str for example Fido,xxx,.. */
373                 testptr=cb->str;
374                 do{
375                     if(NULL==(testptr=fc_stristr(echo->group,testptr)))
376                         break;
377                     testptr+=strlen(echo->group);
378                     result=(*testptr==',' || *testptr==' ' || !*testptr);
379                     testptr-=strlen(echo->group);
380                     ++testptr;
381                 }while(!result);
382             }
383             break;
384         }
385 
386         if(cb->rule&CC_NOT) /* NOT on/off */
387             result=!result;
388 
389         switch(cb->rule&CC_AND){ /* what operation with next result */
390         case CC_OR: /* OR */
391             if (result && area && cb->move!=2 && !config->carbonAndQuit) {
392                 /* check if we've done cc to dest area already */
393                 for (ncop=0; ncop < copiedToCount && result; ncop++)
394                     if (area == copiedTo[ncop]) result = 0;
395                     if (result) {
396                         copiedTo = safe_realloc (copiedTo, (copiedToCount+1) * sizeof (s_area *));
397                         copiedTo[copiedToCount] = area;
398                         copiedToCount++;
399                     }
400             }
401 
402             if(result){
403                 /* make cc */
404                 /* Set value: 1 if copy 3 if move */
405                 rc = cb->move ? 3 : 1;
406                 if(cb->extspawn)
407                     processExternal(echo,msg,*cb);
408                 else
409                     if (cb->areaName && cb->move!=2)
410                     {
411                         if (!processCarbonCopy(area,echo,msg,*cb))
412                             rc &= 1;
413                     }
414                     /*  delete CarbonMove and CarbonDelete messages */
415                     if (cb->move && xmsg) xmsg->attr |= MSGKILL;
416 					if (cb->move == 2) {
417 						/* FIXME: Is there any reason in this case?  */
418                       w_log( LL_CARBON, "Carbon delete from %s %s%s%s: msg from \"%s\" %s to \"%s\"%s%s.",
419                              echo->areaName? echo->areaName:"netmail",
420                              cb->reason? " at reason \"":"", cb->reason? cb->reason:"", cb->reason? "\"":"",
421                              msg->fromUserName, aka2str(msg->origAddr), msg->toUserName,
422                              msg->netMail? " ":"", msg->netMail? aka2str(msg->destAddr):""
423                            );
424                     }
425 					if (config->carbonAndQuit)
426                         /* not skip quit or delete */
427                         if ((cb->areaName && *cb->areaName!='*') ||	cb->move==2) {
428                             return rc;
429                         }
430             }
431             break;
432         case CC_AND: /* AND & AND-NOT */
433             if(!result){
434                 /* following expressions can be skipped until OR */
435                 for (++i,++cb; i<config->carbonCount; i++,++cb)
436                     if(!(cb->rule&CC_AND))  /* AND & AND-NOT */
437                         break; /* this is the last in the AND expr. chain */
438             }
439             /* else result==TRUE, so continue with next expr. */
440             break;
441         }
442     } /* end for() */
443 
444     if (copiedTo) nfree (copiedTo);
445 	w_log( LL_FUNC, "carbonCopy() rc=%d", rc);
446     return rc;
447 }
448 
449 
450 
451