1 /******************************************************************************
2  * HTICK --- FTN Ticker / Request Processor
3  ******************************************************************************
4  * report.c : htick reporting
5  *
6  * Copyright (C) 2002 by
7  *
8  * Max Chernogor
9  *
10  * Fido:      2:464/108@fidonet
11  * Internet:  <mihz@mail.ru>,<mihz@ua.fm>
12  *
13  * This file is part of HTICK
14  *
15  * This is free software; you can redistribute it and/or modify it
16  * under the terms of the GNU General Public License as published
17  * by the Free Software Foundation; either version 2, or (at your option)
18  * any later version.
19  *
20  * FIDOCONFIG library is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23  * General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with FIDOCONFIG library; see the file COPYING.  If not, write
27  * to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA
28  * or visit http://www.gnu.org
29  *****************************************************************************
30  * $Id$
31  */
32 
33 #include <stdlib.h>
34 #include <string.h>
35 
36 #include <huskylib/log.h>
37 #include <huskylib/xstr.h>
38 #include <fidoconf/common.h>
39 #include <huskylib/recode.h>
40 #include <fidoconf/afixcmd.h>
41 #include <huskylib/dirlayer.h>
42 #include <areafix/areafix.h>    /* for print_ch() */
43 
44 #include "toss.h"
45 #include "global.h"
46 #include "report.h"
47 
48 char *versionStr;
49 
50 typedef struct
51 {
52   s_area *farea;
53   UINT begin;
54   UINT32 fSize;
55   UINT fCount;
56 } s_FAreaRepInfo;
57 
58 s_ticfile *Report = NULL;
59 
60 /* Report uses field password for saving ticfilename */
61 /* Report uses field anzpath  for flag of deleting  ticfile*/
62 
63 UINT rCount = 0;
64 
65 s_FAreaRepInfo *aList = NULL;
66 UINT aCount = 0;
67 
68 /* Save cut-down ticket file into special directory for future reports
69  */
doSaveTic4Report(s_ticfile * tic)70 void doSaveTic4Report( s_ticfile * tic )
71 {
72   FILE *tichandle;
73   unsigned int i;
74 
75   char *rpTicName = NULL;
76   char *desc_line = NULL;
77 
78   rpTicName = makeUniqueDosFileName( config->announceSpool, "tic", config );
79 
80   tichandle = fopen( rpTicName, "wb" );
81 
82   if( tichandle == NULL )
83   {
84     w_log( LL_CRIT, "Can't create file %s for file %s", rpTicName, tic->file );
85     return;
86   }
87   else
88   {
89     w_log( LL_CREAT, "Report file %s created for file %s", rpTicName, tic->file );
90   }
91 
92   fprintf( tichandle, "File %s\r\n", tic->file );
93   if (tic->altfile != NULL) fprintf(tichandle,"AltFile %s\r\n",tic->altfile);
94   fprintf( tichandle, "Area %s\r\n", tic->area );
95 
96   if( tic->anzldesc > 0 )
97   {
98     for( i = 0; i < tic->anzldesc; i++ )
99     {
100       desc_line = sstrdup( tic->ldesc[i] );
101       if( config->intab != NULL )
102         recodeToInternalCharset( desc_line );
103       fprintf( tichandle, "LDesc %s\r\n", desc_line );
104       nfree( desc_line );
105     }
106   }
107   else
108   {
109     for( i = 0; i < tic->anzdesc; i++ )
110     {
111       desc_line = sstrdup( tic->desc[i] );
112       if( config->intab != NULL )
113         recodeToInternalCharset( desc_line );
114       fprintf( tichandle, "Desc %s\r\n", desc_line );
115       nfree( desc_line );
116     }
117   }
118   if( tic->origin.zone != 0 )
119     fprintf( tichandle, "Origin %s\r\n", aka2str( tic->origin ) );
120   if( tic->from.zone != 0 )
121     fprintf( tichandle, "From %s\r\n", aka2str( tic->from ) );
122   if( tic->size != 0 )
123     fprintf( tichandle, "Size %u\r\n", tic->size );
124 
125   fclose( tichandle );
126 }                               /* doSaveTic4Report() */
127 
cmp_reportEntry(const void * a,const void * b)128 static int cmp_reportEntry( const void *a, const void *b )
129 {
130   const s_ticfile *r1 = ( s_ticfile * ) a;
131   const s_ticfile *r2 = ( s_ticfile * ) b;
132 
133   if( stricmp( r1->area, r2->area ) > 0 )
134     return 1;
135   else if( stricmp( r1->area, r2->area ) < 0 )
136     return -1;
137   else if( stricmp( r1->file, r2->file ) > 0 )
138     return 1;
139   else if( stricmp( r1->file, r2->file ) < 0 )
140     return -1;
141   return 0;
142 }                               /* cmp_reportEntry() */
143 
144 
getReportInfo()145 static void getReportInfo(  )
146 {
147   husky_DIR *dir;
148   char *file;
149   s_ticfile tmptic;
150   char *fname = NULL;
151   UINT i = 0;
152 
153   dir = husky_opendir( config->announceSpool );
154 
155   while( ( file = husky_readdir( dir ) ) != NULL )
156   {
157     if( patimat( file, "*.TIC" ) == 0 )
158       continue;
159     xstrscat( &fname, config->announceSpool, file, NULL );
160     w_log( LL_DEBUG, "Parsing Report file %s", file );
161 
162     if( ( parseTic( fname, &tmptic ) != parseTic_error ) && tmptic.area && tmptic.file )
163     {
164       Report = srealloc( Report, ( rCount + 1 ) * sizeof( s_ticfile ) );
165       memset( &( Report[rCount] ), 0, sizeof( s_ticfile ) );
166       Report[rCount].size = tmptic.size;
167       Report[rCount].origin = tmptic.origin;
168       Report[rCount].from = tmptic.from;
169       Report[rCount].area = sstrdup( tmptic.area );
170       Report[rCount].file = sstrdup( tmptic.file );
171       Report[rCount].altfile = sstrdup(tmptic.altfile);
172       Report[rCount].anzdesc = tmptic.anzdesc;
173       Report[rCount].desc = scalloc( sizeof( char * ), tmptic.anzdesc );
174       for( i = 0; i < tmptic.anzdesc; i++ )
175       {
176         Report[rCount].desc[i] = sstrdup( tmptic.desc[i] );
177       }
178       Report[rCount].anzldesc = tmptic.anzldesc;
179       Report[rCount].ldesc = scalloc( sizeof( char * ), tmptic.anzldesc );
180       for( i = 0; i < tmptic.anzldesc; i++ )
181       {
182         Report[rCount].ldesc[i] = sstrdup( tmptic.ldesc[i] );
183       }
184       xstrscat( &( Report[rCount].password ), config->announceSpool, file, NULL );
185       rCount++;
186     }
187     disposeTic( &tmptic );
188     nfree( fname );
189   }
190   husky_closedir( dir );
191   w_log( LL_DEBUG, "Sorting report information. Number of entries: %d", rCount );
192   qsort( ( void * )Report, rCount, sizeof( s_ticfile ), cmp_reportEntry );
193 }                               /* getReportInfo() */
194 
buildAccessList()195 static void buildAccessList(  )
196 {
197   UINT i = 0;
198   s_area *currFArea = NULL;
199 
200   for( i = 0; i < rCount; i++ )
201   {
202     if( currFArea == NULL || stricmp( Report[i].area, currFArea->areaName ) )
203     {
204       if( getFileArea( Report[i].area ) == NULL )
205         continue;
206       aCount++;
207       aList = srealloc( aList, ( aCount ) * sizeof( s_FAreaRepInfo ) );
208       currFArea = getFileArea( Report[i].area );
209       aList[aCount - 1].farea = currFArea;
210       aList[aCount - 1].begin = i;
211       aList[aCount - 1].fCount = 0;
212       aList[aCount - 1].fSize = 0;
213     }
214     aList[aCount - 1].fCount++;
215     aList[aCount - 1].fSize += Report[i].size;
216   }
217 }                               /* buildAccessList() */
218 
parseRepMessAttr()219 static void parseRepMessAttr(  )
220 {
221   UINT i;
222   long attr;
223   ps_anndef RepDef;
224   char *flag = NULL;
225 
226   for( i = 0; i < config->ADCount; i++ )
227   {
228     RepDef = &( config->AnnDefs[i] );
229     if( !RepDef->annmessflags )
230       continue;
231     flag = strtok( RepDef->annmessflags, " \t" );
232     while( flag )
233     {
234       attr = str2attr( flag );
235       if( attr != -1L )
236         RepDef->attributes |= attr;
237       flag = strtok( NULL, " \t" );
238     }
239   }
240 }                               /* parseRepMessAttr() */
241 
242 /* report generation */
243 
244 
formDescStr(char * desc)245 static char *formDescStr( char *desc )
246 {
247   char *keepDesc, *newDesc, *tmp, *ch, *buff = NULL;
248 
249   keepDesc = sstrdup( desc );
250 
251   if( strlen( desc ) <= 50 )
252   {
253     return keepDesc;
254   }
255 
256   newDesc = ( char * )scalloc( 1, sizeof( char ) );
257 
258   tmp = keepDesc;
259 
260   ch = strtok( tmp, " \t\r\n" );
261   while( ch )
262   {
263     if( strlen( ch ) > 54 && !buff )
264     {
265       newDesc = ( char * )srealloc( newDesc, strlen( newDesc ) + 55 );
266       strncat( newDesc, ch, 54 );
267       xstrscat( &newDesc, "\r", print_ch( 24, ' ' ), NULL );
268       ch += 54;
269     }
270     else
271     {
272       if( buff && strlen( buff ) + strlen( ch ) > 54 )
273       {
274         xstrscat( &newDesc, buff, "\r", print_ch( 24, ' ' ), NULL );
275         nfree( buff );
276       }
277       else
278       {
279         xstrscat( &buff, ch, " ", NULL );
280         ch = strtok( NULL, " \t\r\n" );
281       }
282     }
283   }
284   if( buff && strlen( buff ) != 0 )
285   {
286     xstrcat( &newDesc, buff );
287   }
288   nfree( buff );
289 
290   nfree( keepDesc );
291 
292   return newDesc;
293 }                               /* formDescStr() */
294 
formDesc(char ** desc,int count)295 static char *formDesc( char **desc, int count )
296 {
297   char *buff = NULL, *tmp;
298   int i;
299 
300   for( i = 0; i < count; i++ )
301   {
302     tmp = formDescStr( desc[i] );
303     if( i == 0 )
304     {
305       xstrscat( &buff, tmp, "\r", NULL );
306     }
307     else
308     {
309       xstrscat( &buff, print_ch( 24, ' ' ), tmp, "\r", NULL );
310     }
311     nfree( tmp );
312   }
313   return buff;
314 }                               /* formDesc() */
315 
freeReportInfo()316 static void freeReportInfo(  )
317 {
318   unsigned i = 0;
319 
320   w_log( LL_INFO, "Deleting report files" );
321   for( i = 0; i < rCount; i++ )
322   {
323     if( Report[i].anzpath )
324     {
325       remove( Report[i].password );
326       w_log( LL_DELETE, "Removed file: %s", Report[i].password );
327       Report[i].anzpath = 0;
328     }
329     disposeTic( &( Report[i] ) );
330   }
331   nfree( Report );
332   nfree( aList );
333 }                               /* freeReportInfo() */
334 
MakeDefaultRepDef()335 static void MakeDefaultRepDef(  )
336 {
337   char *annArea;
338 
339   if( config->ADCount == 0 )
340   {
341     config->AnnDefs = scalloc( 1, sizeof( s_anndef ) );
342     if( config->ReportTo )
343     {
344       annArea = config->ReportTo;
345     }
346     else
347     {
348       annArea = config->netMailAreas[0].areaName;
349     }
350     config->ADCount = 1;
351     config->AnnDefs[0].annAreaTag = sstrdup( annArea );
352   }
353 }                               /* MakeDefaultRepDef() */
354 
MakeReportMessage(ps_anndef pRepDef)355 static s_message *MakeReportMessage( ps_anndef pRepDef )
356 {
357   s_message *msg = NULL;
358   enum
359   { dstfile = -1, dstechomail = 0, dstnetmail = 1
360   } reportDst = dstechomail;    /*  report destination  */
361 
362   if( pRepDef->annAreaTag[0] == '@' )
363     reportDst = dstfile;
364   else if( stricmp( pRepDef->annAreaTag, "netmail" ) == 0 )
365     reportDst = dstnetmail;
366   else if( getNetMailArea( config, pRepDef->annAreaTag ) != NULL )
367     reportDst = dstnetmail;
368 
369   if( reportDst > dstfile )     /* dstechomail or dstnetmail */
370   {
371     s_area *AreaToPost = getArea( config, pRepDef->annAreaTag );
372 
373     if( AreaToPost == &( config->badArea ) )
374       pRepDef->annaddrfrom = pRepDef->annaddrfrom ? pRepDef->annaddrfrom : &( config->addr[0] );
375     else
376       pRepDef->annaddrfrom = pRepDef->annaddrfrom ? pRepDef->annaddrfrom : AreaToPost->useAka;
377 
378     msg = makeMessage( pRepDef->annaddrfrom,
379                        pRepDef->annaddrto ? pRepDef->annaddrto : &( config->addr[0] ),
380                        pRepDef->annfrom ? pRepDef->annfrom : versionStr,
381                        pRepDef->annto ? pRepDef->annto : ( reportDst ? NULL : "All" ),  /* reportDst!=dstechomail ? */
382                        pRepDef->annsubj ? pRepDef->annsubj : "New Files",
383                        ( int )reportDst,
384                        robot->reportsAttr
385                      );
386 
387     msg->attributes = pRepDef->attributes;
388 
389     msg->text = createKludges( config, reportDst ? NULL : pRepDef->annAreaTag,  /* reportDst!=dstechomail ? */
390                                pRepDef->annaddrfrom,
391                                pRepDef->annaddrto ? pRepDef->annaddrto : &( config->addr[0] ),
392                                versionStr );
393     if( robot->reportsFlags )
394       xstrscat( &( msg->text ), "\001FLAGS ", robot->reportsFlags, "\r", NULL );
395   }
396   else                          /* report to file */
397   {
398     msg = ( s_message * ) scalloc( 1, sizeof( s_message ) );
399     msg->netMail = 2;
400     xstrcat( &( msg->subjectLine ), pRepDef->annAreaTag + 1 );
401   }
402   return msg;
403 }                               /* MakeReportMessage() */
404 
ReportOneFile(s_message * msg,ps_anndef pRepDef,s_ticfile * tic)405 static void ReportOneFile( s_message * msg, ps_anndef pRepDef, s_ticfile * tic )
406 {
407   char *tmp = NULL;
408   char *name;
409   static BigSize bs;
410 
411   memset( &bs, 0, sizeof( BigSize ) );
412   IncBigSize( &bs, ( ULONG ) tic->size );
413 
414   tic->anzpath = 1;             /* mark tic file as deleted */
415   if (tic->altfile != NULL) name = tic->altfile;
416   else name = tic->file;
417   if(strlen(name) > 12)
418     xscatprintf(&(msg->text)," %s\r% 23s ", name, PrintBigSize(&bs));
419   else
420     xscatprintf(&(msg->text)," %-12s % 9s ", name, PrintBigSize(&bs));
421 
422   if( tic->anzldesc > 0 )
423   {
424     tmp = formDesc( tic->ldesc, tic->anzldesc );
425   }
426   else
427   {
428     tmp = formDesc( tic->desc, tic->anzdesc );
429   }
430   xstrcat( &( msg->text ), tmp );
431   if( pRepDef->annforigin && tic->origin.zone != 0 )
432   {
433     xscatprintf( &( msg->text ), "%sOrig: %s\r", print_ch( 24, ' ' ), aka2str( tic->origin ) );
434   }
435   if( pRepDef->annfrfrom && tic->from.zone != 0 )
436   {
437     xscatprintf( &( msg->text ), "%sFrom: %s\r", print_ch( 24, ' ' ), aka2str( tic->from ) );
438   }
439   if (tic->altfile != NULL)
440   {
441     xscatprintf(&(msg->text), "%sTIC: %s\r", print_ch(24, ' '), tic->file);
442   }
443   if( tmp == NULL || tmp[0] == 0 )
444     xstrcat( &( msg->text ), "\r" );
445   nfree( tmp );
446 }                               /* ReportOneFile() */
447 
IsAreaMatched(char * areaname,ps_anndef pRepDef)448 static int IsAreaMatched( char *areaname, ps_anndef pRepDef )
449 {
450   int nRet = 0;
451   UINT i = 0;
452 
453   if( pRepDef->numbI == 0 )
454     nRet = 1;
455 
456   for( i = 0; i < pRepDef->numbI; i++ )
457   {
458     if( patimat( areaname, pRepDef->annInclude[i] ) == 1 )
459     {
460       nRet = 1;
461       break;
462     }
463   }
464   if( nRet == 1 )
465   {
466     for( i = 0; i < pRepDef->numbE; i++ )
467     {
468       if( patimat( areaname, pRepDef->annExclude[i] ) == 1 )
469       {
470         nRet = 0;
471         break;
472       }
473     }
474   }
475   return nRet;
476 }                               /* IsAreaMatched() */
477 
reportNewFiles()478 static void reportNewFiles(  )
479 {
480   UINT fileCountTotal = 0;
481   BigSize fileSizeTotal, bs;
482   UINT i, j, ii;
483   s_message *msg = NULL;
484   FILE *echotosslog;
485   FILE *rp;
486   ps_anndef RepDef;
487   char *cp = NULL;
488 
489   for( i = 0; i < config->ADCount; i++ )
490   {
491     RepDef = &( config->AnnDefs[i] );
492     fileCountTotal = 0;
493     memset( &fileSizeTotal, 0, sizeof( BigSize ) );
494     for( j = 0; j < aCount; j++ )
495     {
496       if( !IsAreaMatched( aList[j].farea->areaName, RepDef ) )
497       {
498         continue;
499       }
500       if( !msg )
501         msg = MakeReportMessage( RepDef );
502 
503       xscatprintf( &( msg->text ), "\r>Area : %s", strUpper( aList[j].farea->areaName ) );
504       if( aList[j].farea->description )
505       {
506         xscatprintf( &( msg->text ), " : %s", aList[j].farea->description );
507       }
508       xscatprintf( &( msg->text ), "\r %s\r", print_ch( 77, '-' ) );
509 
510       for( ii = aList[j].begin; ii < aList[j].begin + aList[j].fCount; ii++ )
511       {
512         ReportOneFile( msg, RepDef, &( Report[ii] ) );
513       }
514       memset( &bs, 0, sizeof( BigSize ) );
515       IncBigSize( &bs, ( ULONG ) aList[j].fSize );
516       xscatprintf( &( msg->text ), " %s\r", print_ch( 77, '-' ) );
517       xscatprintf( &( msg->text ), " %s bytes in %u file(s)\r", PrintBigSize( &bs ),
518                    aList[j].fCount );
519       fileCountTotal += aList[j].fCount;
520       IncBigSize( &fileSizeTotal, aList[j].fSize );
521     }
522     if( !msg )
523       continue;
524 
525     xscatprintf( &( msg->text ), "\r %s\r", print_ch( 77, '=' ) );
526     xscatprintf( &( msg->text ), ">Total %s bytes in %u file(s)\r\r",
527                  PrintBigSize( &fileSizeTotal ), fileCountTotal );
528 
529     if( msg->netMail > 1 )
530     {
531       if( NULL == ( rp = fopen( msg->subjectLine, "wt" ) ) )
532       {
533         w_log( LL_ERR, "Could not create report file: %s", msg->subjectLine );
534       }
535       else
536       {
537         /* if output to file:  \r ==> \r\n */
538         cp = msg->text;
539         while( ( cp = strchr( cp, '\r' ) ) != NULL )
540           *cp = '\n';
541         fprintf( rp, msg->text );       /* Automatic translate "\n" to "\015\012" */
542         w_log( LL_FLAG, "Created report file: %s", msg->subjectLine );
543         fclose( rp );
544       }
545     }
546     else
547     {
548       writeMsgToSysop( msg, RepDef->annAreaTag, RepDef->annorigin );
549       if( config->echotosslog != NULL )
550       {
551         echotosslog = fopen( config->echotosslog, "a" );
552         if( echotosslog != NULL )
553         {
554           fprintf( echotosslog, "%s\n", RepDef->annAreaTag );
555           fclose( echotosslog );
556         }
557       }
558     }
559     freeMsgBuffers( msg );
560     nfree( msg );
561   }
562 }                               /* reportNewFiles() */
563 
564 /* Make thick report
565  */
report()566 void report(  )
567 {
568   w_log( LL_INFO, "Start report generating..." );
569   getReportInfo(  );
570   if( Report )
571   {
572     buildAccessList(  );
573     MakeDefaultRepDef(  );
574     parseRepMessAttr(  );
575     reportNewFiles(  );
576     freeReportInfo(  );
577   }
578   w_log( LL_INFO, "End report generating" );
579 }                               /* report() */
580