1 /*****************************************************************************
2 * HTICK --- FTN Ticker / Request Processor
3 *****************************************************************************
4 * Copyright (C) 1999 by
5 *
6 * Gabriel Plutzar
7 *
8 * Fido: 2:31/1
9 * Internet: gabriel@hit.priv.at
10 *
11 * Vienna, Austria, Europe
12 *
13 * This file is part of HTICK, which is based on HPT by Matthias Tichy,
14 * 2:2432/605.14 2:2433/1245, mtt@tichy.de
15 *
16 * HTICK is free software; you can redistribute it and/or modify it
17 * under the terms of the GNU General Public License as published by the
18 * Free Software Foundation; either version 2, or (at your option) any
19 * later version.
20 *
21 * HTICK is distributed in the hope that it will be useful, but
22 * WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 * General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with HTICK; see the file COPYING. If not, write to the Free
28 * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
29 *****************************************************************************
30 * $Id$
31 *****************************************************************************/
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <ctype.h>
37 #include <sys/types.h>
38 #include <signal.h>
39 #include <fcntl.h>
40 #include <sys/stat.h>
41
42 #if (defined(__EMX__) || defined(__MINGW32__)) && defined(__NT__)
43 /* we can't include windows.h for prevent conflicts: send() & other */
44 # define CharToOem CharToOemA
45 #endif
46
47 /* compiler.h */
48 #include <huskylib/compiler.h>
49
50 #ifdef HAS_UNISTD_H
51 # include <unistd.h>
52 #endif
53
54 #ifdef HAS_IO_H
55 # include <io.h>
56 #endif
57
58 #ifdef __OS2__
59 # define INCL_DOSPROCESS
60 # define INCL_DOSERRORS
61 # include <os2.h>
62 #endif
63
64 /* smapi */
65 #include <smapi/msgapi.h>
66 #include <huskylib/huskylib.h>
67
68 /* fidoconf */
69 #include <fidoconf/fidoconf.h>
70 #include <fidoconf/common.h>
71 #include <huskylib/log.h>
72 #include <huskylib/recode.h>
73 #include <huskylib/xstr.h>
74 #include <huskylib/ftnaddr.h>
75
76 /* areafix library */
77 #include <areafix/areafix.h>
78 #include <areafix/afglobal.h>
79
80 /* htick */
81 #include <htick.h>
82 #include <global.h>
83 #include "version.h"
84 #include <toss.h>
85 #include <scan.h>
86 #include <hatch.h>
87 #include <filelist.h>
88 #include <htickafix.h>
89 #include "report.h"
90
processHatchParams(int i,int argc,char ** argv)91 int processHatchParams( int i, int argc, char **argv )
92 {
93
94 char *basename, *extdelim;
95
96 if( argc - i < 2 )
97 {
98 fprintf( stderr, "Insufficient number of arguments\n" );
99 return 0;
100 }
101
102 cmHatch = 1;
103 hatchInfo = scalloc( sizeof( s_ticfile ), 1 );
104 basename = ( argv[i] != NULL ) ? argv[i++] : "";
105 hatchInfo->file = sstrdup( basename );
106 hatchInfo->anzdesc = 1;
107 hatchInfo->desc =
108 srealloc( hatchInfo->desc, ( hatchInfo->anzdesc ) * sizeof( &hatchInfo->desc ) );
109 hatchInfo->desc[0] = sstrdup( "-- description missing --" );
110
111 /* Check filename for 8.3, warn if not */
112 basename = strrchr( hatchInfo->file, PATH_DELIM );
113 if( basename == NULL )
114 basename = hatchInfo->file;
115 else
116 basename++;
117 if( ( extdelim = strchr( basename, '.' ) ) == NULL )
118 extdelim = basename + strlen( basename );
119
120 if( extdelim - basename > 8 || strlen( extdelim ) > 4 )
121 {
122 if( !quiet )
123 fprintf( stderr, "Warning: hatching file with non-8.3 name!\n" );
124 }
125 basename = ( argv[i] != NULL ) ? argv[i++] : "";
126 hatchInfo->area = sstrdup( basename );
127
128 if( argc - i == 0 )
129 {
130 return 1;
131 }
132 if( stricmp( argv[i], "replace" ) == 0 )
133 {
134 if( ( i < argc - 1 ) && ( stricmp( argv[i + 1], "desc" ) != 0 ) )
135 {
136 i++;
137 hatchInfo->replaces = sstrdup( argv[i] );
138 }
139 else
140 {
141 basename = strrchr( hatchInfo->file, PATH_DELIM ) ?
142 strrchr( hatchInfo->file, PATH_DELIM ) + 1 : hatchInfo->file;
143
144 hatchInfo->replaces = sstrdup( basename );
145 }
146 if(i < argc - 1)
147 {
148 i++;
149 }
150 }
151 if( stricmp( argv[i], "desc" ) != 0 )
152 return 1;
153 else if( argc - i < 2 )
154 return 1;
155 i++;
156 nfree( hatchInfo->desc[0] );
157 hatchInfo->desc[0] = sstrdup( argv[i] );
158 #ifdef __NT__
159 CharToOem( hatchInfo->desc[0], hatchInfo->desc[0] );
160 #endif
161 i++;
162 if( argc - i != 0 )
163 {
164 hatchInfo->anzldesc = 1;
165 hatchInfo->ldesc =
166 srealloc( hatchInfo->ldesc, ( hatchInfo->anzldesc ) * sizeof( &hatchInfo->ldesc ) );
167 hatchInfo->ldesc[0] = sstrdup( argv[i] );
168
169 #ifdef __NT__
170 CharToOem( hatchInfo->ldesc[0], hatchInfo->ldesc[0] );
171 #endif
172
173 }
174 return 1;
175 }
176
start_help(void)177 void start_help( void )
178 {
179 printf( "%s\n", versionStr );
180 printf( "\nUsage: htick [options] <command>\n"
181 "Options: -q Quiet mode (display only urgent messages to console)\n"
182 " -c <conf-file> Specify an alternative configuration file\n"
183 "Commands:\n"
184 " toss [annfecho <file>] Do tossing. [Announce new fileechos in text file]\n"
185 " toss -b Toss bad tics\n"
186 " scan Scan netmail area for mails to filefix and process\n"
187 " filefix commands contained inside the mails\n"
188 " ffix <addr> <command> Process filefix command for address from command line\n"
189 " ffix! <addr> <command> The same as 'ffix' and notify the link of the \n"
190 " processed filefix command\n"
191 " relink <pattern> <addr> Refresh subscription for fileareas matching pattern\n"
192 " relink -f [file] <addr> Refresh subscription for fileareas listed in the file\n"
193 " resubscribe <pattern> <fromaddr> <toaddr> - move subscription of areas\n"
194 " matching the pattern from one link to another\n"
195 " resubscribe -f [file] <fromaddr> <toaddr> - move subscription of areas\n"
196 " matching area patterns listed in this file with one\n"
197 " pattern on a line from one link to another\n"
198 " clean Clean passthrough dir and old files in fileechos\n"
199 " announce Announce new files\n"
200 " send <file> <filearea> <address> Send file from filearea to address\n"
201 " hatch <file> <area> [replace [<filemask>]] [desc <desc> [<ldesc>]]\n"
202 " Hatch file into area using description for file,\n"
203 " if \"replace\" is present, add replace field to TIC;\n"
204 " if <filemask> is not present then put <file> in field;\n"
205 " if \"desc\" is present, add description(s);\n"
206 " a short one line <desc> and a long <ldesc>.\n"
207 " One may use the following as <desc>:\n"
208 " @@BBS to use first line from files.bbs\n"
209 " @@DIZ to use 1st line from the contained file_id.diz\n"
210 " @@<file> to use first line from <file>\n"
211 " @BBS to load from files.bbs\n"
212 " @DIZ to load from the contained file_id.diz\n"
213 " @<file> to load from <file>\n"
214 " The last three items may be used as <ldesc>.\n"
215 " filelist <file> [<dirlist>] Generate filelist which includes all files in base\n"
216 " <dirlist> - list of paths to files from filelist\n" );
217 }
218
processCommandLine(int argc,char ** argv)219 e_exitCode processCommandLine( int argc, char **argv )
220 {
221 int i = 0;
222 e_exitCode rc = ex_OK;
223
224 if( argc == 1 )
225 {
226 start_help( );
227 return ex_Help;
228 }
229
230 while( i < argc - 1 )
231 {
232 i++;
233 if( !strcmp( argv[i], "-h" ) )
234 {
235 start_help( );
236 return ex_Help;
237 }
238 if( !strcmp( argv[i], "-v" ) )
239 {
240 printf( "%s", versionStr );
241 return ex_Help;
242 }
243 if( !strcmp( argv[i], "-q" ) )
244 {
245 quiet = 1;
246 continue;
247 }
248 if( !strcmp( argv[i], "-c" ) )
249 {
250 i++;
251 if( argv[i] != NULL )
252 xstrcat( &cfgFile, argv[i] );
253 else
254 {
255 fprintf(stderr, "Parameter missing after \"%s\"!\n", argv[i - 1]);
256 rc = ex_Error;
257 }
258 continue;
259 }
260 if( stricmp( argv[i], "toss" ) == 0 )
261 {
262 cmToss = 1;
263 if( i < argc - 1 )
264 {
265 if( stricmp( argv[i + 1], "-b" ) == 0 )
266 {
267 cmToss = 2;
268 i++;
269 }
270 }
271 continue;
272 }
273 else if( stricmp( argv[i], "scan" ) == 0 )
274 {
275 cmScan = 1;
276 continue;
277 }
278 else if( stricmp( argv[i], "hatch" ) == 0 )
279 {
280 processHatchParams( i + 1, argc, argv );
281 break;
282 }
283 else if( stricmp( argv[i], "ffix" ) == 0 )
284 {
285 if( i < argc - 1 )
286 {
287 i++;
288 parseFtnAddrZS( argv[i], &afixAddr );
289 if( i < argc - 1 )
290 {
291 i++;
292 xstrcat( &afixCmd, argv[i] );
293 }
294 else
295 {
296 fprintf(stderr, "Parameter missing after \"%s\"!\n", argv[i]);
297 rc = ex_Error;
298 }
299 }
300 cmAfix = 1;
301 continue;
302 }
303 else if( stricmp( argv[i], "ffix!" ) == 0 )
304 {
305 if( i < argc - 1 )
306 {
307 i++;
308 parseFtnAddrZS( argv[i], &afixAddr );
309 if( i < argc - 1 )
310 {
311 i++;
312 xstrcat( &afixCmd, argv[i] );
313 }
314 else
315 {
316 fprintf(stderr, "Parameter missing after \"%s\"!\n", argv[i]);
317 rc = ex_Error;
318 }
319 }
320 cmNotifyLink = 1;
321 cmAfix = 1;
322 continue;
323 }
324 else if( stricmp( argv[i], "relink" ) == 0 )
325 {
326 if( i < argc - 1 )
327 {
328 i++;
329 xstrcat( &relinkPattern, argv[i] );
330 if( i < argc - 1 )
331 {
332 i++;
333 parseFtnAddrZS( argv[i], &relinkFromAddr );
334 cmRelink = modeRelink;
335 }
336 else
337 {
338 fprintf(stderr, "Address missing after \"%s\"!\n", argv[i]);
339 rc = ex_Error;
340 }
341 }
342 else
343 {
344 fprintf(stderr, "Pattern missing after \"%s\"!\n", argv[i]);
345 rc = ex_Error;
346 }
347 continue;
348 }
349 else if( stricmp( argv[i], "resubscribe" ) == 0 )
350 {
351 if (i < argc-1)
352 {
353 i++;
354 if (stricmp(argv[i], "-f") == 0)
355 {
356 if (i < argc-1)
357 {
358 i++;
359 if (fexist(argv[i]))
360 {
361 if (fsize(argv[i]) == 0)
362 {
363 fprintf(stderr, "File \"%s\" is empty\n", argv[i]);
364 rc = ex_Error;
365 }
366 xstrcat(&resubscribePatternFile, argv[i]);
367 cmRelink = modeResubsribeWithFile;
368 }
369 else
370 {
371 fprintf(stderr, "File \"%s\" does not exist\n", argv[i]);
372 rc = ex_Error;
373 }
374 }
375 else
376 {
377 fprintf(stderr, "Path missing after -f\n");
378 rc = ex_Error;
379 }
380 }
381 else
382 {
383 xstrcat(&relinkPattern, argv[i]);
384 cmRelink = modeResubsribeWithPattern;
385 }
386 if (i < argc-1)
387 {
388 i++;
389 parseFtnAddrZS(argv[i], &relinkFromAddr);
390 if (i < argc-1)
391 {
392 i++;
393 parseFtnAddrZS(argv[i], &relinkToAddr);
394 }
395 else
396 {
397 fprintf(stderr, "Address missing after \"%s\"!\n", argv[i]);
398 rc = ex_Error;
399 }
400 }
401 else
402 {
403 fprintf(stderr, "Address missing after \"%s\"!\n", argv[i]);
404 rc = ex_Error;
405 }
406 }
407 else
408 {
409 fprintf(stderr, "Pattern missing after \"%s\"!\n", argv[i]);
410 rc = ex_Error;
411 }
412 }
413 else if( stricmp( argv[i], "send" ) == 0 )
414 {
415 cmSend = 1;
416 i++;
417 strcpy( sendfile, ( argv[i] != NULL ) ? argv[i++] : "" );
418 strcpy( sendarea, ( argv[i] != NULL ) ? argv[i++] : "" );
419 strcpy( sendaddr, ( argv[i] != NULL ) ? argv[i] : "" );
420 continue;
421 }
422 else if( stricmp( argv[i], "filelist" ) == 0 )
423 {
424 cmFlist = 1;
425 if( i < argc - 1 )
426 {
427 i++;
428 flistfile = sstrdup( argv[i] );
429 if( i < argc - 1 )
430 {
431 i++;
432 dlistfile = sstrdup( argv[i] );
433 }
434 }
435 continue;
436 }
437 else if( stricmp( argv[i], "announce" ) == 0 )
438 {
439 cmAnnounce = 1;
440 continue;
441 }
442 else if( stricmp( argv[i], "annfecho" ) == 0 )
443 {
444 if( i < argc - 1 )
445 {
446 i++;
447 cmAnnNewFileecho = 1;
448 strcpy( announcenewfileecho, argv[i] );
449 }
450 else
451 {
452 fprintf(stderr, "Insufficient number of arguments\n");
453 rc = ex_Error;
454 }
455 continue;
456 }
457 else if( stricmp( argv[i], "clean" ) == 0 )
458 {
459 cmClean = 1;
460 continue;
461 }
462 else
463 {
464 fprintf(stderr, "Unrecognized command line option %s!\n", argv[i]);
465 rc = ex_Error;
466 }
467
468 } /* endwhile */
469 return rc;
470 }
471
processConfig()472 void processConfig( )
473 {
474 char *buff = NULL;
475 int exitflag = 0;
476
477 setvar( "module", "htick" );
478 SetAppModule( M_HTICK );
479 config = readConfig( cfgFile );
480 if( NULL == config )
481 {
482 nfree( cfgFile );
483 fprintf( stderr, "Config file not found\n" );
484 exit( 1 );
485 };
486
487
488 /* open Logfile */
489
490 initLog( config->logFileDir, config->logEchoToScreen, config->loglevels,
491 config->screenloglevels );
492 setLogDateFormat( config->logDateFormat );
493 htick_log = openLog( LogFileName, versionStr ); /* if failed: openLog() prints a message to stderr */
494 if( htick_log && quiet )
495 htick_log->logEcho = 0;
496
497 if( !sstrlen( config->inbound ) && !sstrlen( config->localInbound )
498 && !sstrlen( config->protInbound ) )
499 {
500 w_log( LL_CRIT, "You must set inbound, protInbound or localInbound in fidoconfig first" );
501 exitflag = 1;
502 }
503 if( !sstrlen( config->outbound ) )
504 {
505 w_log( LL_CRIT, "You must set outbound in fidoconfig first" );
506 exitflag = 1;
507 }
508 if( config->addrCount == 0 )
509 {
510 w_log( LL_CRIT, "At least one addr must be defined" );
511 exitflag = 1;
512 }
513 if( config->linkCount == 0 )
514 {
515 w_log( LL_CRIT, "At least one link must be specified" );
516 exitflag = 1;
517 }
518 if( config->fileAreaBaseDir == NULL )
519 {
520 w_log( LL_CRIT, "You must set FileAreaBaseDir in fidoconfig first" );
521 exitflag = 1;
522 }
523 if( config->passFileAreaDir == NULL )
524 {
525 w_log( LL_CRIT, "You must set PassFileAreaDir in fidoconfig first" );
526 exitflag = 1;
527 }
528 if( cmAnnounce && config->announceSpool == NULL )
529 {
530 w_log( LL_CRIT, "You must set AnnounceSpool in fidoconfig first" );
531 exitflag = 1;
532 }
533 if( config->MaxTicLineLength && config->MaxTicLineLength < 80 )
534 {
535 w_log( LL_CRIT, "Parameter MaxTicLineLength (%d) in fidoconfig must be 0 or >80\n",
536 config->MaxTicLineLength );
537 exitflag = 1;
538 }
539
540 if( exitflag )
541 {
542 exit_htick( "Error(s) is found in config file, please run tparser and analize it's output.",
543 1 );
544 }
545
546
547 if( config->lockfile )
548 {
549 lock_fd = lockFile( config->lockfile, config->advisoryLock );
550 if( lock_fd < 0 )
551 {
552 disposeConfig( config );
553 exit( EX_CANTCREAT );
554 }
555 }
556
557 w_log( LL_START, "Start" );
558
559 nfree( buff );
560
561 if( config->busyFileDir == NULL )
562 {
563 config->busyFileDir = ( char * )smalloc( strlen( config->outbound ) + 10 );
564 strcpy( config->busyFileDir, config->outbound );
565 sprintf( config->busyFileDir + strlen( config->outbound ), "busy.htk%c", PATH_DELIM );
566 }
567 if( config->ticOutbound == NULL )
568 config->ticOutbound = sstrdup( config->passFileAreaDir );
569 }
570
main(int argc,char ** argv)571 int main( int argc, char **argv )
572 {
573 struct _minf m;
574 int rc = 0;
575 e_exitCode res;
576
577 versionStr = GenVersionStr( "htick", VER_MAJOR, VER_MINOR, VER_PATCH, VER_BRANCH, cvs_date );
578
579 res = processCommandLine( argc, argv );
580 if( res == ex_Error )
581 return EX_USAGE;
582 else if( res == ex_Help )
583 return EX_OK;
584 processConfig( );
585
586 if( !( config->netMailAreas ) )
587 w_log( LL_CRIT, "Netmailarea not defined, filefix not allowed!" );
588
589 /* init areafix */
590 if( !init_htickafix( ) )
591 exit_htick( "Can't init Areafix library", 1 );
592
593 /* init SMAPI */
594 m.req_version = 0;
595 m.def_zone = config->addr[0].zone;
596 if( MsgOpenApi( &m ) != 0 )
597 {
598 exit_htick( "MsgApiOpen Error", 1 );
599 } /*endif */
600
601 /* load recoding tables */
602 initCharsets( );
603 getctabs( config->intab, config->outtab );
604 #ifdef __NT__
605 SetFileApisToOEM( );
606 #endif
607
608 checkTmpDir( );
609
610 if( cmScan )
611 scan( );
612 if( cmToss )
613 toss( );
614 if( cmHatch )
615 rc = hatch( );
616 if( cmSend )
617 send( sendfile, sendarea, sendaddr );
618 if( cmFlist )
619 filelist( );
620 if( cmClean )
621 Purge( );
622 if( cmAfix && config->netMailAreas )
623 ffix( afixAddr, afixCmd );
624 if( cmAnnounce && config->announceSpool != NULL )
625 report( );
626
627 if (cmRelink != modeNone)
628 {
629 int ret;
630 FILE *f;
631 char *line, *fromCmd = NULL, *toCmd = NULL, *toPrint = NULL;
632 unsigned int count = 0;
633
634 w_log(LL_START, "%s has started", cmRelink == modeRelink ? "Relinking" : "Resubscribing");
635 if (cmRelink != modeResubsribeWithFile)
636 {
637 /* modeRelink or modeResubsribeWithPattern */
638 ret = relink(cmRelink, relinkPattern, relinkFromAddr, relinkToAddr, &fromCmd, &toCmd, &count);
639 nfree(relinkPattern);
640 if (ret)
641 return 1;
642 }
643 else
644 {
645 /* modeResubsribeWithFile */
646 f = fopen(resubscribePatternFile, "r");
647 if (f == NULL)
648 {
649 fprintf(stderr, "Cannot open file \"%s\"!\n", resubscribePatternFile);
650 nfree(resubscribePatternFile);
651 return 1;
652 }
653 else
654 {
655 while ((line = readLine(f)) != NULL)
656 {
657 xstrcat(&relinkPattern, line);
658 ret = relink(cmRelink, relinkPattern, relinkFromAddr, relinkToAddr, &fromCmd, &toCmd, &count);
659 nfree(relinkPattern);
660 if (ret)
661 {
662 nfree(resubscribePatternFile);
663 return 1;
664 }
665 }
666 nfree(resubscribePatternFile);
667 }
668 }
669 if (fromCmd)
670 {
671 if (cmRelink == modeRelink)
672 sendRelinkMsg(cmRelink, relinkFromAddr, fromCmd, smodeSubscribe);
673 else
674 sendRelinkMsg(cmRelink, relinkFromAddr, fromCmd, smodeUnsubscribe);
675 nfree(fromCmd);
676 }
677 if (toCmd)
678 {
679 sendRelinkMsg(cmRelink, relinkToAddr, toCmd, smodeSubscribe);
680 nfree(toCmd);
681 }
682 xscatprintf(&toPrint, "%i ", count);
683 count == 1 ? xscatprintf(&toPrint, "%s has been ", af_robot->strA) : xscatprintf(&toPrint, "%ses have been", af_robot->strA);
684 w_log(LL_AREAFIX, "%s %s", toPrint, cmRelink == modeRelink ? "relinked" : "resubscribed");
685 nfree(toPrint);
686 }
687
688 nfree( afixCmd );
689 nfree( flistfile );
690 nfree( dlistfile );
691 if( hatchInfo )
692 {
693 disposeTic( hatchInfo );
694 nfree( hatchInfo );
695 }
696 /* deinit SMAPI */
697 MsgCloseApi( );
698
699 doneCharsets( );
700 w_log( LL_STOP, "End" );
701 closeLog( );
702 if( config->lockfile )
703 {
704 FreelockFile( config->lockfile, lock_fd );
705 }
706 disposeConfig( config );
707 nfree( versionStr );
708
709 return rc;
710 }
711