pakfire: use correct tree on x86_64.
[ipfire-2.x.git] / src / squid-accounting / acct.pl
1 #!/usr/bin/perl
2 ###############################################################################
3 #                                                                             #
4 # IPFire.org - A linux based firewall                                         #
5 # Copyright (C) 2014  IPFire Team  <alexander.marx@ipfire.org>                #
6 #                                                                             #
7 # This program is free software: you can redistribute it and/or modify        #
8 # it under the terms of the GNU General Public License as published by        #
9 # the Free Software Foundation, either version 3 of the License, or           #
10 # (at your option) any later version.                                         #
11 #                                                                             #
12 # This program is distributed in the hope that it will be useful,             #
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of              #
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
15 # GNU General Public License for more details.                                #
16 #                                                                             #
17 # You should have received a copy of the GNU General Public License           #
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
19 #                                                                             #
20 ###############################################################################
21
22
23 ###########
24 # Modules #
25 ###########
26
27 use Time::Local;
28 use File::ReadBackwards;
29 use strict;
30 use MIME::Lite;
31
32 #use warnings;
33
34 require '/var/ipfire/general-functions.pl';
35 require "${General::swroot}/accounting/acct-lib.pl";
36 require "${General::swroot}/lang.pl";
37
38 #############
39 # Variables #
40 #############
41
42 my $count = 0;
43 my $dbh;
44 my $logfile = "/var/log/squid/access.log";
45 my $line = '';
46 my $checktime = 3600;  #1 hour = 3600 sec
47 my $starttime = time;
48 my ($time,$elapsed,$ip,$state,$bytes,$method,$url,$user,$peerstate,$type); #split logfileline into variables
49 my $name;
50 my $name1;
51 my $settingsfile = "${General::swroot}/accounting/settings.conf";
52 my $proxyenabled = "${General::swroot}/proxy/enable";
53 my %counter = ();
54 my %counterip = ();
55 my %settings = ();
56 my %toplist = ();
57 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$ydat,$isdst)=localtime();
58 my $skipurlcount=0;
59 my $skipurlsum=0;
60 &General::readhash("$settingsfile", \%settings);
61 my $skipurl=$settings{'SKIPURLS'};
62 $skipurl="'".$skipurl."'";
63 my ($mini,$max)=&ACCT::getminmax;
64 my $now = localtime;
65 my $proxylog;
66 my $proxysrv;
67 my $dmafile="${General::swroot}/dma/dma.conf";
68 my $authfile="${General::swroot}/dma/auth.conf";
69 my $mailfile="${General::swroot}/dma/mail.conf";
70 my %mail=();
71 my %dma=();
72
73 ########
74 # Main #
75 ########
76
77 &checkproxy;
78
79
80 #If we have a disabled file and the proxy is off, we don't need to check anything, exit!
81 if((! -f $proxyenabled || $proxylog eq $Lang::tr{'stopped'}) && -f "${General::swroot}/accounting/disabled"){
82         &ACCT::logger($settings{'LOG'}," Proxy or proxylogging disabled - exiting with no data collection\n");
83         exit 0;
84 }
85 #If proxy was turned off within last hour, we need to check missing minutes and write a disabled file
86 if ((! -f $proxyenabled || $proxylog eq $Lang::tr{'stopped'}) && ! -f "${General::swroot}/accounting/disabled"){
87         $checktime = (time-$max);
88         open (FH,">${General::swroot}/accounting/disabled");
89         close (FH);
90         &ACCT::logger($settings{'LOG'}," Proxy or proxylogging was disabled during last hour - just checking meantime and disabling data collection\n");
91 }
92
93 #If proxy is on, we are doing a normal run. maybe we had a disabled file, so delete it here
94 if (-f $proxyenabled && $proxylog eq $Lang::tr{'running'}){
95         #check if we are running again after the was shutdown and reenabled
96         if (-f "${General::swroot}/accounting/disabled"){
97                 unlink("${General::swroot}/accounting/disabled");
98         }
99         #Find out if the month changed
100         $dbh=&ACCT::connectdb;
101         my $m=sprintf("%d",(localtime((time-3600)))[4]+1);
102         &ACCT::logger($settings{'LOG'},"month before one hour $m, now is ".($mon+1)."\n");
103         if ($m = ($mon+1) || $m == '12' && ($mon+1) == '1'){
104                 #Logrotate
105                 my $year1=$year+1900;
106                 system ("tar", "cfz", "/var/log/accounting-$m-$year1.tar.gz", "/var/log/accounting.log");
107                 unlink ("/var/log/accounting.log");
108                 open (FH,">/var/log/accounting.log");
109                 close (FH);
110                 chmod 0755, "/var/log/accounting.log";
111                 #move all db entries older than this month to second table and cumulate them daily
112                 &ACCT::movedbdata;
113                 &ACCT::logger($settings{'LOG'},"New Month. Old trafficvalues moved to ACCT_HIST Table\n");
114                 #check if mail is enabled
115                 if ( -f $mailfile){
116                         &General::readhash($mailfile, \%mail);
117                 }
118                 if ($mail{'USEMAIL'} eq 'on'){
119                         &ACCT::logger($settings{'LOG'},"Mailserver is activated - Now sending bills via mail...\n");
120                         my $res=&ACCT::getbillgroups;
121                         foreach my $line (@$res){
122                                 my ($grp) = @$line;
123                                 open (FILE, "<", $dmafile) or die $!;
124                                 foreach my $line (<FILE>) {
125                                         $line =~ m/^([A-Z]+)\s+?(.*)?$/;
126                                         my $key = $1;
127                                         my $val = $2;
128                                         $dma{$key}=$val;
129                                 }
130                                 &sendbill($grp,$settings{'MWST'},$settings{'CURRENCY'});
131                         }
132                 }else{
133                         &ACCT::logger($settings{'LOG'},"Mailserver is deactivated - We are NOT sending bills via mail...\n");
134                 }
135         }
136
137         &ACCT::logger($settings{'LOG'},"Start reading last hour of access.log\n");
138         &readlog;
139         &fill_db;
140         &ACCT::closedb;
141         $skipurlsum=sprintf("%.2f",$skipurlsum/(1024*1024));
142         &ACCT::logger($settings{'LOG'},"skipped: $skipurlcount Adressen\n");
143         &ACCT::logger($settings{'LOG'},"skipped: $skipurlsum MB\n") if ($skipurl);
144 }
145 #############
146 # functions #
147 #############
148
149 sub checkproxy{
150         if(-f "${General::swroot}/proxy/enable"){
151                 $proxysrv=$Lang::tr{'running'};
152         }else{
153                 $proxysrv=$Lang::tr{'stopped'};
154         }
155         my $srce = "${General::swroot}/proxy/squid.conf";
156         my $string1 = 'access\.log';
157         open(FH, $srce);
158         while(my $line = <FH>) {
159                 if($line =~ m/$string1/) {
160                         $proxylog=$Lang::tr{'running'};
161                 }
162         }
163         close FH;
164         return;
165 }
166
167 sub readlog{
168         my $url1;
169         my $user1;
170         $count = 0;
171         my $urlcnt=0;
172         &ACCT::logger($settings{'LOG'},"Start: $now. Reading data back till: ".localtime(($starttime-$checktime)).".\n");
173         #Open Logfile and begin to read the file backwards
174         my $bw = File::ReadBackwards->new( $logfile ) or die "can't read $logfile $!" ;
175         while( defined( $line = $bw->readline ) ) {
176                 undef $url1;
177                 chomp $line;
178                 #Divide $line into single variables to get timestamp and check if we are within hte desired timerange
179                 ($time,$elapsed,$ip,$state,$bytes,$method,$url,$user,$peerstate,$type)=split(m/\s+/, $line);
180                 $count += $bytes;
181                 $time = substr($time, 0, -4);
182                 if (($time > ($starttime-$checktime))){
183                         #Skip DENIED stated lines (can be reactivated later)
184                         next if ($state =~ m/DENIED/);
185
186                         #extract site name
187                         if ($url =~ m/([a-z]+:\/\/)??([a-z0-9\-]+\.){1}(([a-z0-9\-]+\.){0,})([a-z0-9\-]+){1}(:[0-9]+)?\/(.*)/o) {
188                            $url=$2.$3.$5;
189                         } else {
190                            my ($a,$b)=split(":",$url);
191                            $url=$a;
192                         }
193
194                         #Skip special URLs like intranet and webservers from local network
195                         if ($url =~ m/$skipurl/o) {
196                           $skipurlcount++;
197                           $skipurlsum+=$bytes;
198                           next;
199                         };
200
201                         #Increase urlcounter
202                         $urlcnt++;
203
204                         #Get Data for accounting
205                         $counter{$user}{'bytes'} += $bytes if ($user ne '-');
206                         $counter{$ip}{'bytes'} += $bytes;
207                 }else{
208                         #If we are out of timewindow, break
209                         last;
210                 }
211         }
212         $count=sprintf("%.2f",$count/(1024*1024));
213         &ACCT::logger($settings{'LOG'},"got $count MB from $urlcnt URLs this run.\n");
214         $bw->close;
215 }
216 sub fill_db{
217         my $tim=time();
218         #Fill ACCT table with accounting information
219         foreach my $name (sort keys %counter){
220                 foreach my $bytes (keys %{ $counter{$name} }) {
221                         $dbh->do("insert into ACCT (TIME_RUN,NAME,BYTES) values ('$tim','$name','$counter{$name}{$bytes}');");
222                 }
223         }
224 }
225 sub sendbill {
226         my $rggrp=$_[0];
227         my $mwst=$_[1];
228         my $cur = $_[2];
229         my @now = localtime(time);
230         $now[5] = $now[5] + 1900;
231         my $actmonth = $now[4];
232         my $month;
233         $month = '0'.$actmonth if $actmonth < 10;
234         $month = '12' if $actmonth == 0;
235         my $actyear  = $now[5];
236         my ($from,$till)=&ACCT::getmonth($actmonth,$actyear);
237         my @billar = &ACCT::GetTaValues($from,$till,$rggrp);
238         my $address_cust = &ACCT::getTaAddress($rggrp,'CUST');
239         my $address_host = &ACCT::getTaAddress($rggrp,'HOST');
240         my $billpos             = &ACCT::getextrabillpos($rggrp);
241         my $no                  = &ACCT::getBillNr;
242         my $back = &ACCT::pdf2(\@billar,$actmonth,$actyear,$mwst,$address_cust,$address_host,$billpos,$rggrp,$cur);
243         my ($company_cust,$type_cust,$name1_cust,$str_cust,$str_nr_cust,$plz_cust,$city_cust,$bank,$iban,$bic,$blz,$kto,$email,$internet,$hrb,$stnr,$tel_host,$fax_host,$ccmail,$billgrp,$text,$host,$cust,$cent);
244
245         foreach my $addrline_cust (@$address_cust){
246                 ($company_cust,$type_cust,$name1_cust,$str_cust,$str_nr_cust,$plz_cust,$city_cust,$bank,$iban,$bic,$blz,$kto,$email,$internet,$hrb,$stnr,$tel_host,$fax_host,$ccmail,$billgrp,$text,$host,$cust,$cent)=@$addrline_cust;
247         }
248
249         if ($back eq '0'){
250                 &ACCT::logger($settings{'LOG'},"Bill for $company_cust successfully created.\n");
251                 my $file="/var/ipfire/accounting/bill/$rggrp/$month-$actyear-$no.pdf";
252                 $settings{'MAILTXT'} =~ tr/\|/\r\n/ ;
253
254                 #extract filename from path
255                 my ($filename) = $file =~ m{([^/]+)$};
256
257                 my $msg = MIME::Lite->new(
258                         From    => $mail{'SENDER'},
259                         To              => $email,
260                         Cc              => $ccmail,
261                         Subject => $settings{'MAILSUB'},
262                         Type    => 'multipart/mixed'
263                 );
264
265                 $msg->attach(
266                         Type    => 'TEXT',
267                         Data    => $settings{'MAILTXT'}
268                 );
269
270                 $msg->attach(
271                         Type            => 'application/pdf',
272                         Path            => $file,
273                         Filename        => $filename,
274                         Disposition     => 'attachment'
275                 );
276
277                 my $res=$msg->send_by_sendmail;
278
279                 if ($res == 0){
280                         &ACCT::logger($settings{'LOG'},"Bill for $company_cust successfully sent.\n");
281                 }elsif ($res > 0){
282                         &ACCT::logger($settings{'LOG'},"ERROR: Bill for $company_cust NOT sent.\n");
283                 }
284                 return 0;
285                 
286         }else{
287                 &ACCT::logger($settings{'LOG'},"ERROR Bill for $company_cust could not be created.\n");
288                 my $msg = MIME::Lite->new(
289                         From    => $mail{'SENDER'},
290                         To              => $mail{'RECIPIENT'},
291                         Subject => "ERROR Squid Accounting",
292                         Type    => 'multipart/mixed'
293                 );
294
295                 $msg->attach(
296                         Type    => 'TEXT',
297                         Data    => "The bill could not be created for customer $company_cust"
298                 );
299
300                 $msg->send_by_sendmail;
301                 return 0;
302         }
303 }