mirror of
https://github.com/vincentmli/bpfire.git
synced 2026-04-11 03:25:54 +02:00
This disables the theme support and makes it impossible to use any other themes than the ipfire default theme. The only intention of this patch is to hardcode the theme to ipfire. To change any cgi we have is an ugly way, but the only way to do this fast. The colour handling needs certainly to be improved as well, but this will and should be done in other patches. Signed-off-by: Jonatan Schlag <jonatan.schlag@ipfire.org> Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
547 lines
14 KiB
Perl
547 lines
14 KiB
Perl
#!/usr/bin/perl
|
|
#
|
|
# SmoothWall CGIs
|
|
#
|
|
# This code is distributed under the terms of the GPL
|
|
#
|
|
# JC HERITIER
|
|
# page inspired from the initial firewalllog.dat
|
|
#
|
|
# Modified for IPFire by Christian Schmidt
|
|
# and Michael Tremer (www.ipfire.org)
|
|
|
|
use strict;
|
|
use Getopt::Std;
|
|
|
|
# enable only the following on debugging purpose
|
|
#use warnings;
|
|
#use CGI::Carp 'fatalsToBrowser';
|
|
|
|
require '/var/ipfire/general-functions.pl';
|
|
require "${General::swroot}/location-functions.pl";
|
|
require "${General::swroot}/lang.pl";
|
|
require "${General::swroot}/header.pl";
|
|
|
|
use POSIX();
|
|
|
|
my %cgiparams=();
|
|
my %settings=();
|
|
my $pienumber;
|
|
my $otherspie;
|
|
my $showpie;
|
|
my $sortcolumn;
|
|
my $errormessage = '';
|
|
|
|
$cgiparams{'pienumber'} = 10;
|
|
$cgiparams{'otherspie'} = 1;
|
|
$cgiparams{'showpie'} = 1;
|
|
$cgiparams{'sortcolumn'} = 1;
|
|
|
|
my @shortmonths = ( 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug',
|
|
'Sep', 'Oct', 'Nov', 'Dec' );
|
|
my @longmonths = ( $Lang::tr{'january'}, $Lang::tr{'february'}, $Lang::tr{'march'},
|
|
$Lang::tr{'april'}, $Lang::tr{'may'}, $Lang::tr{'june'}, $Lang::tr{'july'}, $Lang::tr{'august'},
|
|
$Lang::tr{'september'}, $Lang::tr{'october'}, $Lang::tr{'november'},
|
|
$Lang::tr{'december'} );
|
|
|
|
my @now = localtime();
|
|
my $dow = $now[6];
|
|
my $doy = $now[7];
|
|
my $tdoy = $now[7];
|
|
my $year = $now[5]+1900;
|
|
|
|
$cgiparams{'DAY'} = $now[3];
|
|
$cgiparams{'MONTH'} = $now[4];
|
|
$cgiparams{'ACTION'} = '';
|
|
|
|
&General::readhash("${General::swroot}/fwlogs/ipsettings", \%settings);
|
|
if ($settings{'pienumber'} != 0) { $cgiparams{'pienumber'} = $settings{'pienumber'} };
|
|
if ($settings{'otherspie'} != 0) { $cgiparams{'otherspie'} = $settings{'otherspie'} };
|
|
if ($settings{'showpie'} != 0) { $cgiparams{'showpie'} = $settings{'showpie'} };
|
|
if ($settings{'sortcolumn'} != 0) { $cgiparams{'sortcolumn'} = $settings{'sortcolumn'} };
|
|
|
|
&Header::getcgihash(\%cgiparams);
|
|
if ($cgiparams{'pienumber'} != 0) { $settings{'pienumber'} = $cgiparams{'pienumber'} };
|
|
if ($cgiparams{'otherspie'} != 0) { $settings{'otherspie'} = $cgiparams{'otherspie'} };
|
|
if ($cgiparams{'showpie'} != 0) { $settings{'showpie'} = $cgiparams{'showpie'} };
|
|
if ($cgiparams{'sortcolumn'} != 0) { $settings{'sortcolumn'} = $cgiparams{'sortcolumn'} };
|
|
|
|
if ($cgiparams{'ACTION'} eq $Lang::tr{'save'})
|
|
{
|
|
&General::writehash("${General::swroot}/fwlogs/ipsettings", \%settings);
|
|
}
|
|
|
|
my $start = -1;
|
|
if ($ENV{'QUERY_STRING'} && $cgiparams{'ACTION'} ne $Lang::tr{'update'})
|
|
{
|
|
my @temp = split(',',$ENV{'QUERY_STRING'});
|
|
$start = $temp[0];
|
|
$cgiparams{'MONTH'} = $temp[1];
|
|
$cgiparams{'DAY'} = $temp[2];
|
|
}
|
|
|
|
if (!($cgiparams{'MONTH'} =~ /^(0|1|2|3|4|5|6|7|8|9|10|11)$/) ||
|
|
!($cgiparams{'DAY'} =~ /^(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31)$/))
|
|
{
|
|
$cgiparams{'DAY'} = $now[3];
|
|
$cgiparams{'MONTH'} = $now[4];
|
|
}
|
|
elsif($cgiparams{'ACTION'} eq '>>')
|
|
{
|
|
my @temp_then=();
|
|
my @temp_now = localtime(time);
|
|
$temp_now[4] = $cgiparams{'MONTH'};
|
|
$temp_now[3] = $cgiparams{'DAY'};
|
|
@temp_then = localtime(POSIX::mktime(@temp_now) + 86400);
|
|
## Retrieve the same time on the next day -
|
|
## 86400 seconds in a day
|
|
$cgiparams{'MONTH'} = $temp_then[4];
|
|
$cgiparams{'DAY'} = $temp_then[3];
|
|
}
|
|
elsif($cgiparams{'ACTION'} eq '<<')
|
|
{
|
|
my @temp_then=();
|
|
my @temp_now = localtime(time);
|
|
$temp_now[4] = $cgiparams{'MONTH'};
|
|
$temp_now[3] = $cgiparams{'DAY'};
|
|
@temp_then = localtime(POSIX::mktime(@temp_now) - 86400);
|
|
## Retrieve the same time on the previous day -
|
|
## 86400 seconds in a day
|
|
$cgiparams{'MONTH'} = $temp_then[4];
|
|
$cgiparams{'DAY'} = $temp_then[3];
|
|
}
|
|
|
|
if (($cgiparams{'DAY'} ne $now[3]) || ($cgiparams{'MONTH'} ne $now[4]))
|
|
{
|
|
my @then = ();
|
|
if ( ( $cgiparams{'MONTH'} eq $now[4]) && ($cgiparams{'DAY'} > $now[3]) ||
|
|
( $cgiparams{'MONTH'} > $now[4] ) ) {
|
|
@then = localtime(POSIX::mktime( 0, 0, 0, $cgiparams{'DAY'}, $cgiparams{'MONTH'}, $year - 1901 ));
|
|
} else {
|
|
@then = localtime(POSIX::mktime( 0, 0, 0, $cgiparams{'DAY'}, $cgiparams{'MONTH'}, $year - 1900 ));
|
|
}
|
|
$tdoy = $then[7];
|
|
my $lastleap=($year-1)%4;
|
|
if ($tdoy>$doy) {
|
|
if ($lastleap == 0 && $tdoy < 60) {
|
|
$doy=$tdoy+366;
|
|
} else {
|
|
$doy=$doy+365;
|
|
}
|
|
}
|
|
}
|
|
|
|
my $datediff=0;
|
|
my $dowd=0;
|
|
my $multifile=0;
|
|
if ($tdoy ne $doy) {
|
|
$datediff=int(($doy-$tdoy)/7);
|
|
$dowd=($doy-$tdoy)%7;
|
|
if (($dow-$dowd)<1) {
|
|
$datediff=$datediff+1;
|
|
}
|
|
if (($dow-$dowd)==0) {
|
|
$multifile=1;
|
|
}
|
|
}
|
|
|
|
my $monthstr = $shortmonths[$cgiparams{'MONTH'}];
|
|
my $longmonthstr = $longmonths[$cgiparams{'MONTH'}];
|
|
my $day = $cgiparams{'DAY'};
|
|
my $daystr='';
|
|
if ($day <= 9) {
|
|
$daystr = " $day";
|
|
}
|
|
else {
|
|
$daystr = $day;
|
|
}
|
|
|
|
my $skip=0;
|
|
my $filestr='';
|
|
if ($datediff==0) {
|
|
$filestr="/var/log/messages";
|
|
} else {
|
|
$filestr="/var/log/messages.$datediff";
|
|
$filestr = "$filestr.gz" if -f "$filestr.gz";
|
|
}
|
|
|
|
if (!(open (FILE,($filestr =~ /.gz$/ ? "gzip -dc $filestr |" : $filestr)))) {
|
|
$errormessage = "$Lang::tr{'date not in logs'}: $filestr $Lang::tr{'could not be opened'}";
|
|
$skip=1;
|
|
# Note: This is in case the log does not exist for that date
|
|
}
|
|
my $lines = 0;
|
|
my @log=();
|
|
|
|
if (!$skip)
|
|
{
|
|
while (<FILE>)
|
|
{
|
|
if (/(^${monthstr} ${daystr} ..:..:..) [\w\-]+ kernel:.*(IN=.*)$/) {
|
|
$log[$lines] = $_;
|
|
$lines++;
|
|
}
|
|
}
|
|
close (FILE);
|
|
}
|
|
|
|
$skip=0;
|
|
if ($multifile) {
|
|
$datediff=$datediff-1;
|
|
if ($datediff==0) {
|
|
$filestr="/var/log/messages";
|
|
} else {
|
|
$filestr="/var/log/messages.$datediff";
|
|
$filestr = "$filestr.gz" if -f "$filestr.gz";
|
|
}
|
|
if (!(open (FILE,($filestr =~ /.gz$/ ? "gzip -dc $filestr |" : $filestr)))) {
|
|
$errormessage="$Lang::tr{'date not in logs'}: $filestr $Lang::tr{'could not be opened'}";
|
|
$skip=1;
|
|
}
|
|
if (!$skip) {
|
|
while (<FILE>) {
|
|
if (/(^${monthstr} ${daystr} ..:..:..) [\w\-]+ kernel:.*(IN=.*)$/) {
|
|
$log[$lines] = $_;
|
|
$lines++;
|
|
}
|
|
}
|
|
close (FILE);
|
|
}
|
|
}
|
|
|
|
my $MODNAME="fwlogs";
|
|
|
|
&Header::showhttpheaders();
|
|
&Header::openpage($Lang::tr{'firewall log country'}, 1, '');
|
|
&Header::openbigbox('100%', 'left', '', $errormessage);
|
|
|
|
|
|
if ($errormessage) {
|
|
&Header::openbox('100%', 'left', $Lang::tr{'error messages'});
|
|
print "<font class='base'>$errormessage </font>\n";
|
|
&Header::closebox();
|
|
}
|
|
|
|
&Header::openbox('100%', 'left', "$Lang::tr{'settings'}");
|
|
|
|
print <<END
|
|
<form method='post' action='$ENV{'SCRIPT_NAME'}'>
|
|
<table width='100%'>
|
|
<tr>
|
|
<td width='10%' class='base'>$Lang::tr{'month'}: </td>
|
|
<td width='10%'>
|
|
<select name='MONTH'>
|
|
END
|
|
;
|
|
my $month;
|
|
for ($month = 0; $month < 12; $month++)
|
|
{
|
|
print "\t<option ";
|
|
if ($month == $cgiparams{'MONTH'}) {
|
|
print "selected='selected' "; }
|
|
print "value='$month'>$longmonths[$month]</option>\n";
|
|
}
|
|
print <<END
|
|
</select>
|
|
</td>
|
|
<td width='10%' class='base' align='right'> $Lang::tr{'day'}: </td>
|
|
<td width='40%'>
|
|
<select name='DAY'>
|
|
END
|
|
;
|
|
for ($day = 1; $day <= 31; $day++)
|
|
{
|
|
print "\t<option ";
|
|
if ($day == $cgiparams{'DAY'}) {
|
|
print "selected='selected' "; }
|
|
print "value='$day'>$day</option>\n";
|
|
}
|
|
|
|
if( $cgiparams{'pienumber'} != 0){$pienumber=$cgiparams{'pienumber'};}
|
|
if( $cgiparams{'otherspie'} != 0){$otherspie=$cgiparams{'otherspie'};}
|
|
if( $cgiparams{'showpie'} != 0){$showpie=$cgiparams{'showpie'};}
|
|
if( $cgiparams{'sortcolumn'} != 0){$sortcolumn=$cgiparams{'sortcolumn'};}
|
|
print <<END
|
|
</select>
|
|
</td>
|
|
<td width='5%' align='center'><input type='submit' name='ACTION' title='$Lang::tr{'day before'}' value='<<' /></td>
|
|
<td width='5%' align='center'><input type='submit' name='ACTION' title='$Lang::tr{'day after'}' value='>>' /></td>
|
|
<td width='20%' align='right'><input type='submit' name='ACTION' value='$Lang::tr{'update'}' /></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan='3' align='left' valign="left">$Lang::tr{'Number of Countries for the pie chart'}:</td>
|
|
<td colspan='3' align='left' valign="center"><input type='text' name='pienumber' value='$pienumber' size='4'></td>
|
|
<td align='right'><input type='submit' name='ACTION' value='$Lang::tr{'save'}' /></td>
|
|
</tr>
|
|
</table>
|
|
</form>
|
|
END
|
|
;
|
|
|
|
&Header::closebox();
|
|
|
|
&Header::openbox('100%', 'left', $Lang::tr{'firewall graph country'});
|
|
print "<p><b>$Lang::tr{'firewall hits'} $longmonthstr $daystr: $lines</b></p>";
|
|
|
|
my $red_interface = &General::get_red_interface();
|
|
my $linesjc = 0;
|
|
my %tabjc;
|
|
|
|
if ($pienumber == -1 || $pienumber > $lines || $sortcolumn == 2) { $pienumber = $lines; };
|
|
$lines = 0;
|
|
|
|
foreach $_ (@log)
|
|
{
|
|
# If ipv6 uses bridge, use PHYSIN for iface, otherwise IN
|
|
if (/^... (..) (..:..:..) [\w\-]+ kernel:(.*)(PHYSIN=.*)$/) {}
|
|
elsif (/^... (..) (..:..:..) [\w\-]+ kernel:(.*)(IN=.*)$/) {}
|
|
my $packet = $4;
|
|
my $iface = '';
|
|
if ($packet =~ /PHYSIN=(\w+)/) { $iface = $1; } elsif ($packet =~ /IN=(\w+)/) { $iface = $1; }
|
|
if ( $1 =~ /2./ ) { $iface=''; }
|
|
my $srcaddr = '';
|
|
# Find ipv4 and ipv6 addresses
|
|
if ($packet =~ /SRC\=(([\d]{1,3})(\.([\d]{1,3})){3})/) { $srcaddr = $1; }
|
|
elsif ($packet =~ /SRC\=(([0-9a-fA-F]{0,4})(\:([0-9a-fA-F]{0,4})){2,7})/) { $srcaddr = $1; }
|
|
|
|
if($iface eq $red_interface) {
|
|
# Traffic from red
|
|
if($srcaddr ne '') {
|
|
# srcaddr is set
|
|
my $ccode = &Location::Functions::lookup_country_code($srcaddr);
|
|
if ($ccode eq '') {
|
|
$ccode = 'unknown';
|
|
}
|
|
$tabjc{$ccode} = $tabjc{$ccode} + 1;
|
|
if(($tabjc{$ccode} == 1) && ($lines < $pienumber)) { $lines = $lines + 1; }
|
|
$linesjc++;
|
|
}
|
|
}
|
|
else {
|
|
# Traffic not from red
|
|
if($iface ne '') {
|
|
$tabjc{$iface} = $tabjc{$iface} + 1 ;
|
|
if(($tabjc{$iface} == 1) && ($lines < $pienumber)) { $lines = $lines + 1; }
|
|
$linesjc++;
|
|
}
|
|
else {
|
|
# What to do with empty iface lines?
|
|
# This probably is traffic from ipfire itself (IN= OUT=XY)?
|
|
}
|
|
}
|
|
}
|
|
|
|
$pienumber = $lines;
|
|
|
|
my @keytabjc = keys %tabjc;
|
|
|
|
my @slice;
|
|
my $go;
|
|
my $nblinejc;
|
|
|
|
if( $cgiparams{'linejc'} eq 'all' ){ $nblinejc = $linesjc; $go=1; }
|
|
if( ($cgiparams{'linejc'} != 0) && ($cgiparams{'linejc'} ne 'all') ){ $nblinejc = $cgiparams{'linejc'}; $go=1;}
|
|
if( $go != 1){ $nblinejc = 1000; }
|
|
|
|
my @key;
|
|
my @value;
|
|
my $indice=0;
|
|
my @tabjc2;
|
|
|
|
if ($sortcolumn == 1) {
|
|
@tabjc2 = sort { $b <=> $a } values (%tabjc);
|
|
}
|
|
else {
|
|
@tabjc2 = sort { $a <=> $b } keys (%tabjc);
|
|
}
|
|
|
|
my $colour=1;
|
|
|
|
##############################################
|
|
#pie chart generation
|
|
use GD::Graph::pie;
|
|
use GD::Graph::colour;
|
|
#ips sort by hits number
|
|
my $v;
|
|
|
|
if ($sortcolumn == 1) {
|
|
for ($v=0;$v<$pienumber;$v++){
|
|
findkey($tabjc2[$v]);
|
|
}
|
|
}
|
|
else {
|
|
foreach $v (@tabjc2) {
|
|
$key[$indice] = $v;
|
|
$value[$indice] = $tabjc{$v};
|
|
$indice++;
|
|
}
|
|
}
|
|
|
|
my @ips;
|
|
my @numb;
|
|
|
|
@ips = @key;
|
|
@numb = @value;
|
|
|
|
my $o;
|
|
|
|
if($cgiparams{'otherspie'} == 2 ){}
|
|
else{
|
|
my $numothers;
|
|
for($o=0;$o<$pienumber;$o++){
|
|
$numothers = $numothers + $numb[$o];
|
|
}
|
|
$numothers = $linesjc - $numothers;
|
|
if ($numothers > 0) {
|
|
$ips[$pienumber]="$Lang::tr{'otherip'}";
|
|
$numb[$pienumber] = $numothers;
|
|
}
|
|
}
|
|
|
|
my @data = (\@ips,\@numb);
|
|
use GD::Graph::colour qw( :files );
|
|
|
|
my $color=0;
|
|
my %color = ();
|
|
my %mainsettings = ();
|
|
&General::readhash("${General::swroot}/main/settings", \%mainsettings);
|
|
&General::readhash("/srv/web/ipfire/html/themes/ipfire/include/colors.txt", \%color);
|
|
|
|
if ($showpie != 2 && $pienumber <= 50 && $pienumber != 0) {
|
|
my $mygraph = GD::Graph::pie->new(500, 350);
|
|
$mygraph->set(
|
|
'title' => '',
|
|
'pie_height' => 50,
|
|
'start_angle' => 89
|
|
) or warn $mygraph->error;
|
|
|
|
$mygraph->set_value_font(GD::gdMediumBoldFont);
|
|
$mygraph->set( dclrs => [ "$color{'color1'}" , "$color{'color2'}" , "$color{'color3'}" , "$color{'color4'}" , "$color{'color5'}" , "$color{'color6'}" , "$color{'color7'}" , "$color{'color8'}" , "$color{'color9'}" , "$color{'color10'}" ] );
|
|
my $myimage = $mygraph->plot(\@data) or die $mygraph->error;
|
|
|
|
my @filenames = glob("/srv/web/ipfire/html/graphs/fwlog-country*.png");
|
|
unlink(@filenames);
|
|
my $imagerandom = rand(1000000);
|
|
my $imagename = "/srv/web/ipfire/html/graphs/fwlog-country$imagerandom.png";
|
|
open(FILE,">$imagename");
|
|
print FILE $myimage->png;
|
|
close(FILE);
|
|
#####################################################
|
|
print "<div style='text-align:center;'>";
|
|
print "<img src='/graphs/fwlog-country$imagerandom.png'>";
|
|
print "</div>";
|
|
}
|
|
print <<END
|
|
<table width='100%' class='tbl'>
|
|
<tr>
|
|
<th width='10%' align='center' class='boldbase'></th>
|
|
<th width='30%' align='center' class='boldbase'><b>$Lang::tr{'country'}</b></th>
|
|
<th width='30%' align='center' class='boldbase'><b>Count</b></th>
|
|
<th width='30%' align='center' class='boldbase'><b>$Lang::tr{'percentage'}</b></th>
|
|
</tr>
|
|
END
|
|
;
|
|
|
|
my $total=0;
|
|
my $show=0;
|
|
|
|
my $s;
|
|
my $percent;
|
|
my $col="";
|
|
|
|
for($s=0;$s<$lines;$s++)
|
|
{
|
|
$show++;
|
|
$percent = $value[$s] * 100 / $linesjc;
|
|
$percent = sprintf("%.f", $percent);
|
|
$total = $total + $value[$s];
|
|
# colors are numbered 1 to 10
|
|
my $colorIndex = ($color % 10) + 1;
|
|
$col="bgcolor='$color{\"color$colorIndex\"}'";
|
|
$color++;
|
|
print "<tr>";
|
|
|
|
print "<td align='center' $col>";
|
|
|
|
# Dont show details button for "unknown" location.
|
|
if ($key[$s] ne 'unknown') {
|
|
print"<form method='post' action='showrequestfromcountry.dat'>";
|
|
print"<input type='hidden' name='MONTH' value='$cgiparams{'MONTH'}'>";
|
|
print"<input type='hidden' name='DAY' value='$cgiparams{'DAY'}'>";
|
|
print"<input type='hidden' name='country' value='$key[$s]'>";
|
|
print"<input type='submit' value='$Lang::tr{'details'}'></form>";
|
|
}
|
|
elsif ($key[$s] eq 'unknown') {
|
|
print "unknown";
|
|
}
|
|
# Looks dangerous to use hardcoded interface names here. Probably needs fixing.
|
|
if ($key[$s] eq 'blue0' || $key[$s] eq 'green0' || $key[$s] eq 'orange0' ) {
|
|
print "<td align='center' $col>$key[$s]</td>";
|
|
}
|
|
else {
|
|
my $fcode = lc($key[$s]);
|
|
|
|
# Get flag icon for of the country.
|
|
my $flag_icon = &Location::Functions::get_flag_icon($fcode);
|
|
|
|
if($flag_icon) {
|
|
print "<td align='center' $col><a href='/cgi-bin/country.cgi#$fcode'><img src='$flag_icon' border='0' align='absmiddle' alt='$key[$s]' title='$key[$s]'></a></td>";
|
|
} else {
|
|
print "<td align='center' $col>$key[$s]</td>";
|
|
}
|
|
}
|
|
print "<td align='center' $col>$value[$s]</td>";
|
|
print "<td align='center' $col>$percent</td>";
|
|
print "</tr>";
|
|
}
|
|
|
|
if($cgiparams{'otherspie'} == 2 ){}
|
|
else{
|
|
# colors are numbered 1 to 10
|
|
my $colorIndex = ($color % 10) + 1;
|
|
$col="bgcolor='$color{\"color$colorIndex\"}'";
|
|
print "<tr>";
|
|
|
|
if ( $linesjc ne "0")
|
|
{
|
|
my $dif;
|
|
$dif = $linesjc - $total;
|
|
$percent = $dif * 100 / $linesjc;
|
|
$percent = sprintf("%.f", $percent);
|
|
print <<END
|
|
<td align='center' $col></TD>
|
|
<td align='center' $col>$Lang::tr{'other countries'}</td>
|
|
<td align='center' $col>$dif</TD>
|
|
<td align='center' $col>$percent</TD>
|
|
</tr>
|
|
END
|
|
;
|
|
}
|
|
}
|
|
print <<END
|
|
</TABLE>
|
|
END
|
|
;
|
|
|
|
&Header::closebox();
|
|
&Header::closebigbox();
|
|
&Header::closepage();
|
|
|
|
sub findkey {
|
|
my $v;
|
|
foreach $v (@keytabjc) {
|
|
if ($tabjc{$v} eq $_[0]) {
|
|
delete $tabjc{$v};
|
|
$key[$indice] = "$v";
|
|
$value[$indice] = $_[0];
|
|
$indice++;
|
|
last;
|
|
}
|
|
}
|
|
}
|
|
sub checkversion {
|
|
#Automatic Updates is disabled
|
|
return "0","0";
|
|
}
|
|
|