1#!/usr/bin/perl
2
3# Point string processor
4# (c) Stas Degteff 2:5080/102
5#
6# Only "Boss+Point" pointlist format is supported
7
8# $Id$
9#
10#
11#
12
13$helpmsg = <<HELP ;
14USAGE:
15	$0
16
17Pntstr scan secure inbound directory for files pointstr.* (* = point number)
18and parse its.
19
20Point string file format (with line numbers):
211: Session_password_for_point
222: Pointlist string
23
24HELP
25
26#-----------------------------------------------------------------------------#
27#
28# $Log$
29# Revision 1.4  2002/12/14 16:21:20  stas_degteff
30# Use full point address
31#
32# Revision 1.3  2002/10/28 08:25:50  stas_degteff
33# Fix shebang
34#
35# Revision 1.2  2002/06/04 11:37:05  stas
36# Some bugs fixed.
37#
38# Revision 1.6  2002/06/03 18:48:27  User
39# Many, many fixes.
40#
41# Revision 1.5  2002/06/02 14:20:30  User
42# First release.
43# Fidoconfig parsing implement (include not supported).
44# Add log support.
45#
46# Revision 1.4  2002/06/01 14:31:21  User
47# RCS tag 'log' inserted
48#
49# Revision 1.3  2002/06/01 14:30:22  User
50# Reading fidoconfig implemented
51#
52# Revision 1.2  2002/06/01 12:52:37  User
53# .bak file create
54#
55# Revision 1.1  2002/06/01 11:53:34  User
56# First beta
57#
58#
59#=============================================================================#
60
61# Setup section --------------------------------------------------------------#
62
63#DOS/WIN
64$fidoconfig = "\\ftn\\config";
65#UNIX
66#$fidoconfig = "/usr/local/etc/fido/links";
67
68$PntStrFileMask   = "PNTSTR.*";
69$PointSegmentFile = "segNNNN.ptn";  # NNNN replace to node number in future
70
71# read from fidoconfig
72$address = "2:5080/102";
73$logfiledir  = "\\ftn\\log";
74#$logfiledir = ".";
75$nodelistdir = "\\ftn\\nodelist";
76$protinbound = "\\ftn\\inbound";
77
78# Variables (& constants) section
79
80%password = ();
81%pointName = ();
82
83@validflags = ( V34, V32, V32B, VFC, HST, X2C, X2S, V90C, V90S, HST, H14, H16,
84                ZYX, Z19, V32T, CSP, PEP, MAX, H96, MNP, V42B, V42, V29, V22,
85                V110L, V110H, V120L, V120H, X75, ISDN, # ISDN
86                IBN, IFT, IFC, ITN, IVM, IP, # IP-based
87                IMI, IUC, ITX, ISE, IEM, # SMTP-based
88                EVY, EMA,                # email-based
89#                RPK, NPK, NEC, REC, NC, SMH # coordinators & secure mail hub
90                CM, MN, MO, LO,
91                PING, # auto-reply
92                UUCP, # internet email gate (official)
93                "G[A-Z1-90]+", # gateway to other FTN domain
94                "#01", "#02", "#08", "#09", "#18", "#20", # MH (bell-212)
95                "!01", "!02", "!08", "!09", "!18", "!20", # MH (CCITT)
96                "T[A-Xa-x][A-Xa-x]", # Answer time
97                XA, XB, XC, XP, XR, XW, XX, # FREQ
98                K12, ENC, CDP, SDS );
99
100$pntnumFieldNo = 1;
101$phoneFieldNo=5;
102$flagFieldNo=7;
103
104@fields = ();
105
106# Program section
107
108if( $ENV{FIDOCONFIG} ){
109   $fidoconfig = $ENV{FIDOCONFIG};
110}elsif( $ENV{fidoconfig} ){
111   $fidoconfig = $ENV{fidoconfig};
112}
113
114# $DIRSEP - directory char (slash in unix and backslash in DOS-like OS
115if( $fidoconfig =~ /\\/ ){
116  $DIRSEP = "\\";
117}else{
118  $DIRSEP = "/";
119}
120
121
122&readconfig($fidoconfig);
123
124
125open(LOG, ">>$logfiledir" . "pntstr.log") || print STDERR "Can't open log file (", $logfiledir, "pntstr.log)\n";
126print LOG "\n---Starting at $curdate\n";
127print LOG "Fidoconfig \"$fidoconfig\" readed\n";
128
129
130($a = $address) =~ s|\d+:\d+/(\d+)|$1|;
131if( $a >0 ){
132  $a = sprintf "%04u", $a ;
133  $PointSegmentFile =~ s/NNNN/$a/;
134}elsif( $a == "0" ){
135  die "Hmmmmmmm! Host with points is BAD idea!\n";
136}else{
137  die "\n";
138}
139
140@lt = localtime(time); $lt[5]=$lt[5]+1900; $lt[4]++;
141$curdate = "$lt[3]-$lt[4]-$lt[5] $lt[2]:$lt[1]:$lt[0]";
142@lt=();
143
144@inbound = glob("$protinbound$PntStrFileMask");
145
146for $ff ( @inbound ){
147   @fields = ();
148   if( $ff =~ /\.([0-9]+)/ ){
149#     $pointNum = $1;
150     $pointNum = "$address.$1";
151     if( !open( FF, $ff) ){  print "can't open \"$ff\", skip file\n"; next; }
152     print LOG "Process $ff (point number $pointNum)...\n";
153     $pass = <FF>;
154#     chomp($pass);
155     $pass =~ s/[\n\r]$//g;  # chomp is platform-dependent
156     if( !defined($password{$pointNum}) ){
157       $pass = $ff . ".bad";
158       print LOG "Point $pointNum not found\n",
159                 "Rename $ff to $pass\n";
160       print "Point $pointNum not found\n";
161       rename $ff, $pass;
162     }elsif( length($password{$pointNum})==0 ){
163       $pass = $ff . ".bad";
164       print LOG "Empty password for point $pointNum\n",
165                 "Rename $ff to $pass\n";
166       print "Empty password for point '$pointNum' ($password{$pointNum})\n";
167       rename $ff, $pass;
168     }elsif( $pass =~ /^$password{$pointNum}$/ ){
169       print LOG "... $ff : password OK\n";
170       $pointString = <FF>;
171       chomp($pointString);
172       $pointString = parsePointString($pointString);
173       if( length($pointString)>0 ){
174         writePointString($pointString);
175         print LOG "Remove input file \"$ff\"...";
176         close FF;
177         unlink $ff || die "Error!\n";
178         print LOG "OK\n";
179       }else{
180         ($bb = $ff) =~ s/...\.$pointNum$/ERR.$pointNum/;
181         print LOG "Rename $ff to $bb ...";
182         close FF;
183         rename $ff, $bb || die "Error!\n";
184         print LOG "OK\n";
185       }
186     }else{
187       print "Error: Invalid password \"$pass\" for point \"$pointNum\"\n";
188       print LOG "...Invalid password \"$pass\" for point \"$pointNum\"\n";
189       ($bb = $ff) =~ s/...\.$pointNum$/SEC.$pointNum/;
190       print LOG "Rename $ff to $bb ...";
191       close FF;
192       rename $ff, $bb || die "Error!\n";
193       print LOG "OK\n";
194     }
195     close FF;
196   }
197}
198
199print LOG "---End---\n";
200close LOG;
201
202#END
203
204sub readconfig{
205# Read husky configuration: pathnames, points passwords.
206
207  my $config = $_[0]; # fidoconfig file name
208  my $pointNo=0, $pointName="";
209  my @line,$line;
210  my $t, $a=1;
211
212  open FIDOCONFIG, "$config" || die "Can't open $config: $!";
213
214  print LOG "\nReading fidoconfig \"$config\"...";
215  while( ($line = <FIDOCONFIG>) ){
216    chomp($line);
217    $line =~ s/\t/ /g;
218    $line =~ s/ +/ /g;
219    @line = split / /, $line;
220#    if( $line[0] =~ /^include/i ){
221#       readconfig($line[1]);
222#    }
223    if( $line[0] =~ /^address$/i ){
224       $address = $line[1] if($a);
225       $a=0;
226    }elsif( $line[0] =~ /^protinbound$/i ){
227       $protinbound = $line[1];
228    }elsif( $line[0] =~ /^logfiledir$/i ){
229       $logfiledir  = $line[1];
230    }elsif( $line[0] =~ /^nodelistdir$/i ){
231       $nodelistdir = $line[1];
232    }elsif( $line[0] =~ /^PntStrFileMask$/i ){
233       $PntStrFileMask = $line[1];
234    }elsif( $line[0] =~ /^PointSegmentFile$/i ){
235       $PointSegmentFile = $line[1];
236#    }elsif( $line[0] =~ /^$/i ){
237    }elsif( $line[0] =~ /^link$/i ){
238       # Write previous item if point
239       if( $pointNo ){
240          $password{$pointNo} = $password;
241          $pointName{$pointNo} = $pointName;
242          if( !$password ){
243            print LOG "\nEmpty password for point $address.$pointNo, ignored\n";
244          }
245       }
246       # clear vars
247       $pointNo = 0;
248       $pointName = "";
249       $password = "";
250       # store new pointname
251       shift @line;
252       $pointName = join " ", @line;
253    }elsif( $line[0] =~ /^aka$/i ){
254#       if( $address && ($line[1] =~ /$address.([1-9]\d*)/) ){
255#         $pointNo = $1;
256       if( $address && ($line[1] =~ /$address.[1-9][0-9]*/) ){
257         $pointNo = $line[1];
258       }
259    }elsif( !$pointNo ){
260       next;
261    }elsif( $line[0] =~ /^sessionpwd$/i ){
262       $password = $line[1];
263    }elsif( ($line[0] =~ /^password$/i) ){
264       $password = $line[1] if( length($password)==0 );
265    }
266#    if( $line[0] =~ //i ){
267#       $ = $line[1];
268#    }
269  }
270  if( $pointNo ){
271    $password{$pointNo} = $password;
272    $pointName{$pointNo} = $pointName;
273    if( !$password ){
274      print LOG "\nEmpty password for point $address.$pointNo, ignored\n";
275    }
276  }
277  close FIDOCONFIG;
278  print LOG "OK\n";
279
280  # Add slash (or backslash) if omitted
281  $nodelistdir .= $DIRSEP if( length($nodelistdir)>0 && ($nodelistdir !~ /$DIRSEP$/) );
282  $protinbound .= $DIRSEP  if( length($protinbound)>0 && ($protinbound !~ /$DIRSEP$/) );
283  $logfiledir  .= $DIRSEP  if( length($logfiledir)>0 && ($logfiledir !~ /$DIRSEP$/) );
284
285# debug output #
286#for $pointNo (keys %password){
287#  print "$pointNo:$password{$pointNo}:$pointName{$pointNo}\n";
288#}
289
290}
291
292
293sub parsePointString{
294  my $i, $ii;
295  my $line = $_[0];
296
297  # check for spaces & tabs
298  if( $line =~ / / ){
299     print "Error: space in line! Replaced to \"_\"\n";
300     print LOG "Error: space in line! Replaced to \"_\"\n";
301  }
302  if( $line =~ /\t/ ){
303     print "Error: tab stop in line! Replaced to \"_\"\n";
304     print LOG "Error: tab stop in line! Replaced to \"_\"\n";
305  }
306  $line =~ s/[ \t]/_/g;
307
308  @fields = split /,/, $line;
309
310  if($pointNum != $fields[$pntnumFieldNo]){
311    print LOG "Can't match point number: \"$fields[$pntnumFieldNo]\", not $pointNum\n";
312    print "Can't match point number: \"$fields[$pntnumFieldNo]\", not $pointNum\n";
313    return "";
314  }
315
316  # print point string fields (log)
317  print <<FIELDS;
318
319Point:   $fields[$pntnumFieldNo]
320Station: $fields[2]
321City:    $fields[3]
322Sysop:   $fields[4]
323Phone:   $fields[$phoneFieldNo]
324Speed:   $fields[6]
325FIELDS
326  print "Flags:   " ;
327  print "$fields[$flagFieldNo]";
328  for( $i=$flagFieldNo+1; $i<=$#fields; $i++ ){ print ",$fields[$i]"; }
329  print "\n\n";
330
331  # validate "Point" (1st) field
332  if( $fields[0] !~ /^Point$/ ){
333    print LOG "Error: Invalid first field \"$fields[0]\", valid value is \"Point\". Process failed.\n";
334    print "Error: Invalid first field \"$fields[0]\", valid value is \"Point\". Process failed.\n";
335  }else{
336    if( $fields[0] != "Point" ){
337      $fields[0] = "Point";
338      print LOG "Warning: Invalid first field \"$fields[0]\", replace to valid value \"Point\"\n";
339      print  "Warning: Invalid first field \"$fields[0]\", replace to valid value \"Point\"\n";
340    }
341  }
342
343  # validate phone
344  if( $fields[$phoneFieldNo] =~ /^-Unpublished-$/i ){
345    if( $fields[$phoneFieldNo] != "-Unpublished-" ){
346     $fields[$phoneFieldNo] = "-Unpublished-";
347     print LOG "Warning: Invalid \"magic\" phone number \"$fields[$phoneFieldNo]\": it's case-cencitivity, replace to valid value \"-Unpublished-\"\n";
348     print "Warning: Invalid \"magic\" phone number \"$fields[$phoneFieldNo]\": it's case-cencitivity, replace to valid value \"-Unpublished-\"\n";
349    }
350  }elsif( $fields[$phoneFieldNo] !~ /^[0-9-]+$/i ){
351     print LOG "Invalid phone number \"$fields[$phoneFieldNo]\" (only digits & dash is legal, also "-Unpublished-")\n";
352     print "Invalid phone number \"$fields[$phoneFieldNo]\" (only digits & dash is legal, also "-Unpublished-")\n";
353     return "";
354  }
355  # validate flags
356  for( $i=$#fields; $i>=$flagFieldNo; $i-- ){
357    for( $ii=$#validflags; $ii>=0 && $fields[$i] !~ /$validflags[$ii]/ ; $ii-- ){
358       if( uc($fields[$i]) =~ /$validflags[$ii]/ ){
359         $fields[$i] = uc($fields[$i]);
360         print LOG "Warning: flag \"$fields[$i]\" converted to uppercase\n";
361         print "Warning: flag \"$fields[$i]\" converted to uppercase\n";
362       }
363    }
364    if( $fields[$i] !~ /$validflags[$ii]/ ){
365      return "";
366      print LOG "Illegal flag \"$fields[$i]\"\n";
367      print "Illegal flag \"$fields[$i]\"\n";
368    }
369  }
370  return join(",",@fields);     # return valid point string
371}
372
373sub writePointString(){
374  my @fields = split /,/, $_[0];
375  my @aflds = (), $tmpname;
376  my $doit=1;
377
378  if( !open(SEGMENT, "$nodelistdir$PointSegmentFile") ){
379    print LOG "Can't open $PointSegmentFile ($!), abort.\n";
380    die "Can't open $PointSegmentFile, abort. (See log for details.)\n";
381  }
382  ($tmpname = $PointSegmentFile ) =~ s/\.[\w\d_-]*$//;
383  $tmpname .= ".tmp";
384  if( !open(TMP, ">$tmpname") ){
385    print LOG "Can't open temporary file \"$tmpname\" ($!), abort.\n";
386    die "Can't open temporary file \"$tmpname\", abort. (See log for details.)\n";
387  }
388  print LOG "Write to $tmpname...";
389  while( $line = <SEGMENT> ){
390    chomp($line);
391    @aflds = split /,/, $line;
392    if( "$aflds[$pntnumFieldNo]" == "$fields[$pntnumFieldNo]" ){
393       print TMP join(",",@fields), "\n"; # replace point string
394       $doit=0;
395    }else{
396       print TMP "$line\n";
397    }
398  }
399  print TMP (join(",",@fields), "\n") if($doit); # add point string
400
401  print LOG "OK\n";
402  close SEGMENT;
403  close TMP;
404
405  ($line = $PointSegmentFile ) =~ s/\.[\w\d_-]*$//;
406  $line .= ".bak";
407  print LOG "Backup $PointSegmentFile to $line...";
408  unlink $line if( -f $line );
409  rename $PointSegmentFile, $line || die "Error!\n";
410  print LOG "OK\n";
411  print LOG "Rename $tmpname to $PointSegmentFile...";
412  rename $tmpname, $PointSegmentFile || die "Error!\n";
413  print LOG "OK\n";
414}
415