#!/usr/bin/perl

###############################################################################
#   shell-  Firewall'  Linux.            
# Firewall    -,     
#        .                   
#  
#      General Public License v2.0
# :  
#        e-mail: robin@altlinux.ru
###############################################################################

$ProgramName="fwcreator";
$VersionNum="0.11";
$MinorVersionNum="5";

$LocalShapeDir="$ENV{HOME}/.$ProgramName-$VersionNum/shapes/";
$SystemShapeDir="/usr/share/$ProgramName/shapes/";


if(@ARGV == 0)
{
  print(STDERR "Firewall Script Creator V$VersionNum.$MinorVersionNum\n");
  print(STDERR "\n$ProgramName: at least 1 argument expected!\n\n");
  print(STDERR "Usage: $ProgramName project-file [output-file]\n\n");
  exit(2);
}

$INPUTFNAME=$ARGV[0];
$OUTPUTFNAME=$ARGV[1];

%order=();     #-,        .  
	       #       firewall'
%aliases=();   #-,        ,  
%ResultScript=(); #-,      firewall',
		  #   ( ) ,  
		  #   


open(PROJECT, "<", $INPUTFNAME) || clean_and_exit("Can't open project file"); 

@ParametersList=(); #    
$CurrentLine=0; #     
$CUR_DEVICE=""; #   
$CUR_ADDR="";   #IP-   


LINE: while(<PROJECT>)
{
  $CurrentLine++;
  
  #        
  next LINE if /^\s*#/;
  next LINE if /^\s*\n*$/;
  
  chomp();
  @ParametersList=split(/\s+/,trim($_));

  if($ParametersList[0] eq "alias")
  {
    if(@ParametersList>2)
    {
      my $AliasName=$ParametersList[1];
      #        
      #    

  
      for(my $i=2; $i<@ParametersList; $i++)
      {
        $ParametersList[$i]=~ s/,$//;
      }
      my $AliasVal=join(",",splice(@ParametersList,2));
      $aliases{$AliasName}=$AliasVal;
      
      next LINE;
    }
    else
    {
      clean_and_exit("Keyword \'alias\' must have 2 or more parameters");
    }
  }
  
  if($ParametersList[0] eq "interface")
  {
    if(@ParametersList==3)
    {
      $CUR_DEVICE=$ParametersList[1];
      $CUR_ADDR=$ParametersList[2];
      next LINE;
    }
    else
    {
      clean_and_exit("Keyword \'interface\' must have 2 parameters");
    }
  }

  if($CUR_DEVICE ne "")
  {
    #   -    (    -) 
    $FNAME=$ParametersList[0]; 
    
    # ""   
    for (my $i=1; $i<@ParametersList; $i++)
    {
      $ParametersList[$i]=unalias($ParametersList[$i]);
    }
	
    if ($ParametersList[1] =~ /^\%(\S+).*/ )
    {
      #      '%',    
      #  ,     % -   
      gen_incl_module($FNAME,$CUR_DEVICE,$CUR_ADDR,unmask_spaces($1));
    }
    else
    {
      #      
      gen_list();
    }
  }
  else
  {
    clean_and_exit("Keyword \'interface\' must be defined before");  
  }
}

compile_firewall();

exit();

###################################################################
#    
###################################################################
sub clean_and_exit
{
   print(STDERR "Error at line $CurrentLine: ",$_[0],"\n");
   exit(1);
}


##################################################################################
# ,       "$SOMETHING" 		 #
#                                                    #
##################################################################################
sub unalias
{
  my @List=split(/\s*,\s*/,$_[0]);
  my $Result;
  for(my $i=0; $i<@List; $i++)
  {
    if ($List[$i] =~ /^\$(.*)/ )
    { 
      if ( $aliases{$1} ne "" )
      {
        if($Result eq "")
        { $Result=unalias($aliases{$1}); }
        else
        { $Result=join(",",$Result,unalias($aliases{$1})); }
      }
      else
      {
        clean_and_exit ("Unknown alias: @_[0]");
      }
    }
    else
    {
      if($Result eq "")
      { $Result=$List[$i]; }
      else
      { $Result=join(",",$Result,$List[$i]); }
    }
  }
  return $Result;  
}

##################################################################################
# ,        "$SOMETHING",
#  ,    
##################################################################################

sub gen_incl_module
{
   my $FILENAME=$_[0];
   my $INTERFACE=$_[1];
   my $ADDR=$_[2];
   my $LISTFNAME=$_[3];
  
   my $service=$FILENAME;

   if (!check_service_name($FILENAME))
   {
     clean_and_exit("Impermissible characters at rule's name: $FILENAME");
   }
   
   open(LFILE, "<", $LISTFNAME) || clean_and_exit("Can't open source file: $LISTFNAME");
   
   while (<LFILE>)
   {
     chomp();
     my @PARAM=split(/\s+/,trim($_));
     
     open(MFILE, "<", "$LocalShapeDir$FILENAME") ||
       open(MFILE, "<", "$SystemShapeDir$FILENAME") || clean_and_exit("Can't open source file: $FILENAME");
     while (<MFILE>)
     {
       chomp();
       if (/^#\/(\d+) *$/)
       {
         $order{$1}=$FILENAME;
       }
       else
       {
         s/(\$INTERFACE)/$INTERFACE/;
         s/(\$ADDR)/$ADDR/;
	 
	 for(my $i=0, my $j=1; $i<@PARAM; $i++,$j++)
	 {
           s/(\$PARAM$j)/$PARAM[$i]/g;
	 }  
	 $ResultScript{$service}=join('',$ResultScript{$service},$_,"\n");
       }
     }
     close(MFILE);
   }
   close(LFILE);
   $ResultScript{$service}=join('',$ResultScript{$service},"\n");
}

##################################################################################
# ,        "$SOMETHING",
#                                               
##################################################################################

sub gen_module
{
   my $FILENAME=$_[0];
   my $INTERFACE=$_[1];
   my $ADDR=$_[2];

   if (!check_service_name($FILENAME))
   {
     clean_and_exit("Impermissible characters at rule's name: $FILENAME");
   }

   my $service=$FILENAME;
   
   my @PARAM=splice(@_,3);
   
   open(MFILE, "<", "$LocalShapeDir$FILENAME") ||
     open(MFILE, "<", "$SystemShapeDir$FILENAME") || clean_and_exit("Can't open source file: $FILENAME");
   while (<MFILE>)
   {
     chomp();
     if (/^#\/(\d+) *$/)
     {
       $order{$1}=$FILENAME;
     }
     else
     {
       s/(\$INTERFACE)/$INTERFACE/;
       s/(\$ADDR)/$ADDR/;
       
       for(my $i=0, my $j=1; $i<@PARAM; $i++,$j++)
       {
         s/(\$PARAM$j)/$PARAM[$i]/g;
       }
       $ResultScript{$service}=join('',$ResultScript{$service},$_,"\n");
     }
   }
   close(MFILE);
   $ResultScript{$service}=join('',$ResultScript{$service},"\n");
}

#########################################################################
#  ,   ,   
#  .
#   -       
#      @ParametersList
#       
# $FNAME,$CUR_DEVICE,$CUR_ADDR
#########################################################################
sub gen_list
{
  my $Depth=$_[0]+0; #  / 
  my @CParam;        #  .    
		     #     .   
		     #   - ,  
		     #    
  if ($Depth!=0)
  {
    shift();
    @CParam=@_;
  }

  my @List=split(/\s*,\s*/,$ParametersList[$Depth+1]);
  for (my $j=0; $j<@List; $j++)
  {
    $CParam[$Depth]=$List[$j];
    if($Depth==(@ParametersList-2))
    {
      gen_module($FNAME,$CUR_DEVICE,$CUR_ADDR,@CParam);
    }
    else
    {
      gen_list($Depth+1,@CParam);
    }  
  }
}

#########################################################################
#   ""  ""
#       
#    "\_" (     )
#        
#########################################################################

sub unmask_spaces
{
  $_=$_[0];
  my $Result="";
  my $i=0;
  my $OrigLength=length($_);
  while($i<$OrigLength)
  {
    my $char=substr($_,$i,1);
    if($char eq "\\")
    {
      if(($i+1)<$OrigLength)
      {
        if(substr($_,$i+1,1) eq "_") 
        {
          $Result=join('',$Result," ");
        }
        else
        {
          $Result=join('',$Result,substr($_,$i+1,1));
        }
        $i++;
      }
    }
    else
    {
      $Result=join('',$Result,$char);
    }
    $i++;
  }
  return $Result;    
}

####################################################################################
# ,     / 
####################################################################################
sub trim
{
  $_=$_[0];
  s/^\s*//;
  s/\s*$//;
  return $_;
}

#####################################################################################
#         "/"
#####################################################################################

sub check_service_name
{
  if($_[0] =~ /[\/]/)
  {
    return 0;
  }
  else
  {
    return 1;
  }
}

#####################################################################################
# ,        Firewall'  
#      (#/servie_order_num)
#####################################################################################
sub compile_firewall
{
  if(@ARGV==1)
  {
    $OUTFILE=STDOUT;
  }
  else
  {
    open(OUTFILE, ">", $OUTPUTFNAME) || clean_and_exit("Can't open/create destination file: $OUTPUTFNAME"); 
    $OUTFILE=OUTFILE;    
  }
  print($OUTFILE "#!/bin/sh\n\n");
  print($OUTFILE "# This firewall script was generated by $ProgramName V$VersionNum.$MinorVersionNum\n\n");

  open(SFILE, "<", join('', $LocalShapeDir, "_iptables")) || 
    open(SFILE, "<", join('', $SystemShapeDir, "_iptables")) ||clean_and_exit("Can't open source file: _iptables");
  while (<SFILE>) {  print($OUTFILE $_); }
  close(SFILE);

  open(SFILE, "<", join('', $LocalShapeDir, "_default_policy")) || 
    open(SFILE, "<", join('', $SystemShapeDir, "_default_policy")) ||clean_and_exit("Can't open source file: _default_policy");
  while (<SFILE>) {  print($OUTFILE $_); }
  close(SFILE);
  
  open(SFILE, "<", join('', $LocalShapeDir, "_maindefs")) ||
    open(SFILE, "<", join('', $SystemShapeDir, "_maindefs")) || clean_and_exit("Can't open source file: _maindefs");
  while (<SFILE>) { print($OUTFILE $_); }
  close(SFILE);
  
  open(SFILE, "<", join('', $LocalShapeDir, "_init")) || 
    open(SFILE, "<", join('', $SystemShapeDir, "_init")) || clean_and_exit("Can't open source file: _init");
  while (<SFILE>) { print($OUTFILE $_); }
  close(SFILE);

  foreach $service_order_num (sort keys %order)
  {
    print($OUTFILE $ResultScript{$order{$service_order_num}});
  }
  
  if(@ARGV>1)
  {
    close($OUTFILE);
  }
}
