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