\n";
+print "\n";
+print "";
+print "[Home ]";
+my $path = "";
+my $cnt = 1;
+my @path = split(/\//,$form{p});
+foreach my $dir (@path) {
+ if ($dir ne "" and ($dir ne "/")) {
+ if ($cnt == @path) {
+ print "/$dir";
+ } else {
+ print "/$dir ";
+ }
+ $path .= "/$dir";
+ $cnt++;
+ }
+}
+print " \n";
+print "";
+print "Search results for [$form{words}] (".uc($form{type})." words) \n";
+
+my @skipath = split(/\//,$webpath);
+my $class = "tdshade2";
+if (@dirmatches > 0) {
+ foreach my $match (@dirmatches) {
+ my ($score,$dir) = split(/\,/,$match);
+ my $view = "";
+ @splitme = split(/\//,$dir);
+ for (my $x = @skipath + 1;$x < (@splitme - 1);$x++) {
+ $view .= "/$splitme[$x]";
+ }
+ $view .= "/$splitme[-1] ";
+ if ($class eq "tdshade2") {$class = "tdshade1"} else {$class = "tdshade2"}
+ print "Score: $score Directory: [Home]$view \n";
+ }
+} else {
+ print "No directory matches found \n";
+}
+print " \n";
+$class = "tdshade2";
+if (@filematches > 0) {
+ foreach my $match (@filematches) {
+ my ($score,$file) = split(/\,/,$match);
+ my $view = "";
+ @splitme = split(/\//,$file);
+ if ((@skipath + 2) > @splitme) {
+ $view = "[Home ]/$splitme[-1] ";
+ } else {
+ for (my $x = @skipath + 1;$x < (@splitme - 2);$x++) {
+ $view .= "/$splitme[$x]";
+ }
+ $view = "[Home]$view/$splitme[-2] /$splitme[-1] ";
+ }
+ if ($class eq "tdshade2") {$class = "tdshade1"} else {$class = "tdshade2"}
+ print "Score: $score File: $view \n";
+ }
+} else {
+ print "No file matches found \n";
+}
+
+print "
\n";
+return;
+}
+# end search
+###############################################################################
+# start process_form
+sub process_form {
+
+my $buffer = $ENV{'QUERY_STRING'};
+if ($buffer eq "") {
+ binmode (STDIN);
+ read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
+ if (($buffer =~ /Content-Disposition: form-data/) and ($buffer =~ /^--/)) {
+ &upload($buffer);
+ exit;
+ }
+}
+
+my @pairs = split(/&/, $buffer);
+foreach my $pair (@pairs) {
+ my ($name, $value) = split(/=/, $pair);
+ $value =~ tr/+/ /;
+ $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
+ $value =~ s/\|//g;
+ $value =~ s/\`//g;
+ $value =~ s/\0//g;
+ $value =~ s/\.\.//g;
+ $value =~ s/~!/ ~!/g;
+ $form{$name} = $value;
+}
+
+return;
+}
+# end process_form
+###############################################################################
+# start uploadfile
+sub uploadfile {
+
+$form{p} =~ s/\r//g;
+$form{p} =~ s/\n//g;
+$form{p} = &validate_upload_vars($form{p});
+$form{type} =~ s/\r//g;
+$form{type} =~ s/\n//g;
+$form{type} = &validate_upload_vars($form{type});
+$form{c} =~ s/\r//g;
+$form{c} =~ s/\n//g;
+$form{c} = &validate_upload_vars($form{c});
+$form{m} =~ s/\r//g;
+$form{m} =~ s/\n//g;
+$form{m} = &validate_upload_vars($form{m});
+$form{caller} =~ s/\r//g;
+$form{caller} =~ s/\n//g;
+
+my @filenames;
+my @filebodies;
+foreach my $key (keys %form) {
+ if (($form{$key} =~ /\/tmp\/cpanel3upload\./) or ($form{$key} =~ /\/tmp\/cpanel\.TMP/) or ($form{$key} =~ /\/tmp\/Cpanel_Form_file\.upload\./)) {
+ push (@filenames, $key);
+ push (@filebodies, $form{$key});
+ }
+}
+
+if ($demo) {
+ $message = "This action is disabled in the demonstration";
+} else {
+ for (my $x = 0;$x < @filenames ;$x++) {
+ $filenames[$x] = &validate_upload_vars($filenames[$x]);
+ $filenames[$x] =~ s/\r//g;
+ $filenames[$x] =~ s/\n//g;
+ $filenames[$x] =~ s/^file-//g;
+ $filenames[$x] = (split (/\\/,$filenames[$x]))[-1];
+ $filenames[$x] = (split (/\//,$filenames[$x]))[-1];
+ if ($form{type} eq "ascii") {$filebodies[$x] =~ s/\r//g}
+ if (-e "$webpath$form{p}/$filenames[$x]") {
+ $extramessage .= " $filenames[$x] - Already exists, delete the original first";
+ $fileno--;
+ next;
+ }
+ my $openok = 1;
+ &printcmd("mv -f $filebodies[$x] $webpath$form{p}/$filenames[$x]");
+ $extramessage .= " $filenames[$x] - Uploaded";
+ }
+
+ $message = "$fileno File(s) Uploaded".$extramessage;
+}
+
+&browse;
+return;
+}
+# end upload
+###############################################################################
+# start validate_upload_vars
+sub validate_upload_vars {
+
+my $value = shift;
+return $value;
+
+}
+# end validate_upload_vars
+###############################################################################
+# start countfiles
+sub countfiles {
+
+if (-d $File::Find::name) {push (@dirs, $File::Find::name)} else {push (@files, $File::Find::name)}
+
+return;
+}
+# end countfiles
+###############################################################################
+# start wantedfiles
+sub wantedfiles {
+
+unless (-f $File::Find::name) {return}
+if ($File::Find::name =~ /\_vti\_/) {return}
+if ($File::Find::dir =~ /^$wwwpath/) {$dir = $';} else {return}
+if ($dir =~ /^\//) {$dir = $';}
+if ($dir ne "") {$dir = $dir."/";}
+push (@userfiles,$dir.$_);
+
+return;
+}
+# end wantedfiles
+###############################################################################
+# start upgrade
+sub upgrade {
+
+$| = 1;
+
+print "";
+
+if (-e "/usr/src/cse.tgz") {unlink ("/usr/src/cse.tgz") or die $!}
+print "Retrieving new cse package...\n";
+
+my ($status, $text) = &urlget("https://$downloadserver/cse.tgz","/usr/src/cse.tgz");
+if ($status) {print "Oops: $text\n"}
+
+if (! -z "/usr/src/cse.tgz") {
+ print "Unpacking new cse package...\n";
+ print "";
+ &printcmd("cd /usr/src ; tar -xzf cse.tgz ; cd cse ; sh install.sh 2>&1");
+ print " ";
+ print "Tidying up...\n";
+ print "";
+ &printcmd("rm -Rfv /usr/src/cse*");
+ print " ";
+ print "...All done.\n";
+}
+print " ";
+
+open (IN, "<", $versionfile) or die $!;
+$myv = ;
+close (IN);
+chomp $myv;
+
+print "
\n";
+
+return;
+}
+# end upgrade
+###############################################################################
+
+###############################################################################
+# start urlget (v1.3)
+#
+# Examples:
+#my ($status, $text) = &urlget("http://prdownloads.sourceforge.net/clamav/clamav-0.92.tar.gz","/tmp/clam.tgz");
+#if ($status) {print "Oops: $text\n"}
+#
+#my ($status, $text) = &urlget("http://www.configserver.com/free/msfeversion.txt");
+#if ($status) {print "Oops: $text\n"} else {print "Version: $text\n"}
+#
+sub urlget {
+ my $url = shift;
+ my $file = shift;
+ my $status = 0;
+ my $timeout = 1200;
+ local $SIG{PIPE} = 'IGNORE';
+
+ use LWP::UserAgent;
+ my $ua = LWP::UserAgent->new;
+ $ua->timeout(30);
+ my $req = HTTP::Request->new(GET => $url);
+ my $res;
+ my $text;
+
+ ($status, $text) = eval {
+ local $SIG{__DIE__} = undef;
+ local $SIG{'ALRM'} = sub {die "Download timeout after $timeout seconds"};
+ alarm($timeout);
+ if ($file) {
+ $|=1;
+ my $expected_length;
+ my $bytes_received = 0;
+ my $per = 0;
+ my $oldper = 0;
+ open (OUT, ">", "$file\.tmp") or return (1, "Unable to open $file\.tmp: $!");
+ binmode (OUT);
+ print "...0\%\n";
+ $res = $ua->request($req,
+ sub {
+ my($chunk, $res) = @_;
+ $bytes_received += length($chunk);
+ unless (defined $expected_length) {$expected_length = $res->content_length || 0}
+ if ($expected_length) {
+ my $per = int(100 * $bytes_received / $expected_length);
+ if ((int($per / 5) == $per / 5) and ($per != $oldper)) {
+ print "...$per\%\n";
+ $oldper = $per;
+ }
+ } else {
+ print ".";
+ }
+ print OUT $chunk;
+ });
+ close (OUT);
+ print "\n";
+ } else {
+ $res = $ua->request($req);
+ }
+ alarm(0);
+ if ($res->is_success) {
+ if ($file) {
+ rename ("$file\.tmp","$file") or return (1, "Unable to rename $file\.tmp to $file: $!");
+ return (0, $file);
+ } else {
+ return (0, $res->content);
+ }
+ } else {
+ return (1, "Unable to download: ".$res->message);
+ }
+ };
+ alarm(0);
+ if ($@) {
+ return (1, $@);
+ }
+ if ($text) {
+ return ($status,$text);
+ } else {
+ return (1, "Download timeout after $timeout seconds");
+ }
+}
+# end urlget
+###############################################################################
+## start printcmd
+sub printcmd {
+ my @command = @_;
+ my ($childin, $childout);
+ my $pid = open3($childin, $childout, $childout, @command);
+ while (<$childout>) {print $_}
+ waitpid ($pid, 0);
+ return;
+}
+## end printcmd
+###############################################################################
+## start getdownloadserver
+sub getdownloadserver {
+ my @servers;
+ my $downloadservers = "/usr/local/cpanel/whostmgr/docroot/cgi/configserver/cse/downloadservers";
+ my $chosen;
+ if (-e $downloadservers) {
+ open (my $DOWNLOAD, "<", $downloadservers);
+ flock ($DOWNLOAD, LOCK_SH);
+ my @data = <$DOWNLOAD>;
+ close ($DOWNLOAD);
+ chomp @data;
+ foreach my $line (@data) {
+ if ($line =~ /^download/) {push @servers, $line}
+ }
+## foreach my $line (slurp($downloadservers)) {
+## $line =~ s/$cleanreg//g;
+## if ($line =~ /^download/) {push @servers, $line}
+## }
+ $chosen = $servers[rand @servers];
+ }
+ if ($chosen eq "") {$chosen = "download.configserver.com"}
+ return $chosen;
+}
+## end getdownloadserver
+###############################################################################
+
+1;
diff --git a/cse/cse.conf b/cse/cse.conf
new file mode 100644
index 0000000..dd5b2a5
--- /dev/null
+++ b/cse/cse.conf
@@ -0,0 +1,27 @@
+# name
+name=cse
+
+# Service that will serve this app
+service=whostmgr
+
+# Physical path: /usr/local/cpanel/3rdparty/Foo.php
+# Literal URL path: $server:$port/$cpsession/3rdparty/Foo.php
+url=/cgi/configserver/cse.cgi
+
+# System user to run process as
+user=root
+
+# Required acls
+acls=all
+
+# Display name as show in the service ui
+displayname=ConfigServer Explorer
+
+# Url to show in the service ui (relative to install path for whm this is cgi/)
+entryurl=configserver/cse.cgi
+
+upgradecall=/usr/local/cpanel/whostmgr/docroot/cgi/configserver/cse/upgrade.sh
+
+icon=cse.png
+
+target=_self
diff --git a/cse/cse.conf.old b/cse/cse.conf.old
new file mode 100644
index 0000000..99fa1ff
--- /dev/null
+++ b/cse/cse.conf.old
@@ -0,0 +1,23 @@
+# name
+name=cse
+
+# Service that will serve this app
+service=whostmgr
+
+# Physical path: /usr/local/cpanel/3rdparty/Foo.php
+# Literal URL path: $server:$port/$cpsession/3rdparty/Foo.php
+url=/cgi/addon_cse.cgi
+
+# System user to run process as
+#user=root
+
+# Required acls
+acls=all
+
+# Display name as show in the service ui
+displayname=ConfigServer Explorer
+
+# Url to show in the service ui (relative to install path for whm this is cgi/)
+#entryurl=addon_cse.cgi
+
+upgradecall=/usr/local/cpanel/whostmgr/docroot/cgi/configserver/cse/upgrade.sh
diff --git a/cse/cse.tmpl b/cse/cse.tmpl
new file mode 100644
index 0000000..7214326
--- /dev/null
+++ b/cse/cse.tmpl
@@ -0,0 +1,29 @@
+[%
+USE Whostmgr;
+USE JSON;
+
+WRAPPER 'master_templates/master.tmpl'
+ header = 'ConfigServer Explorer'
+ skipsupport = 1
+ skipheader = 1
+ hide_license_warnings = 1
+ theme='bootstrap'
+ breadcrumbdata = {
+ previous = [
+ {
+ name = "Home",
+ url = "/scripts/command?PFILE=main",
+ },
+ {
+ name = "Plugins",
+ url = "/scripts/command?PFILE=Plugins",
+ }
+ ],
+ name = 'ConfigServer Explorer',
+ url = '/cgi/configserver/cse.cgi',
+ };
+%]
+
+[% cse_output %]
+
+[% END %]
diff --git a/cse/cse/bootstrap/css/bootstrap.min.css b/cse/cse/bootstrap/css/bootstrap.min.css
new file mode 100644
index 0000000..ed3905e
--- /dev/null
+++ b/cse/cse/bootstrap/css/bootstrap.min.css
@@ -0,0 +1,6 @@
+/*!
+ * Bootstrap v3.3.7 (http://getbootstrap.com)
+ * Copyright 2011-2016 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}}
+/*# sourceMappingURL=bootstrap.min.css.map */
\ No newline at end of file
diff --git a/cse/cse/bootstrap/fonts/glyphicons-halflings-regular.eot b/cse/cse/bootstrap/fonts/glyphicons-halflings-regular.eot
new file mode 100644
index 0000000..b93a495
Binary files /dev/null and b/cse/cse/bootstrap/fonts/glyphicons-halflings-regular.eot differ
diff --git a/cse/cse/bootstrap/fonts/glyphicons-halflings-regular.svg b/cse/cse/bootstrap/fonts/glyphicons-halflings-regular.svg
new file mode 100644
index 0000000..94fb549
--- /dev/null
+++ b/cse/cse/bootstrap/fonts/glyphicons-halflings-regular.svg
@@ -0,0 +1,288 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/cse/cse/bootstrap/fonts/glyphicons-halflings-regular.ttf b/cse/cse/bootstrap/fonts/glyphicons-halflings-regular.ttf
new file mode 100644
index 0000000..1413fc6
Binary files /dev/null and b/cse/cse/bootstrap/fonts/glyphicons-halflings-regular.ttf differ
diff --git a/cse/cse/bootstrap/fonts/glyphicons-halflings-regular.woff b/cse/cse/bootstrap/fonts/glyphicons-halflings-regular.woff
new file mode 100644
index 0000000..9e61285
Binary files /dev/null and b/cse/cse/bootstrap/fonts/glyphicons-halflings-regular.woff differ
diff --git a/cse/cse/bootstrap/fonts/glyphicons-halflings-regular.woff2 b/cse/cse/bootstrap/fonts/glyphicons-halflings-regular.woff2
new file mode 100644
index 0000000..64539b5
Binary files /dev/null and b/cse/cse/bootstrap/fonts/glyphicons-halflings-regular.woff2 differ
diff --git a/cse/cse/bootstrap/js/bootstrap.min.js b/cse/cse/bootstrap/js/bootstrap.min.js
new file mode 100644
index 0000000..9bcd2fc
--- /dev/null
+++ b/cse/cse/bootstrap/js/bootstrap.min.js
@@ -0,0 +1,7 @@
+/*!
+ * Bootstrap v3.3.7 (http://getbootstrap.com)
+ * Copyright 2011-2016 Twitter, Inc.
+ * Licensed under the MIT license
+ */
+if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1||b[0]>3)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){if(a(b.target).is(this))return b.handleObj.handler.apply(this,arguments)}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.7",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a("#"===f?[]:f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.7",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c).prop(c,!0)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c).prop(c,!1))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target).closest(".btn");b.call(d,"toggle"),a(c.target).is('input[type="radio"], input[type="checkbox"]')||(c.preventDefault(),d.is("input,button")?d.trigger("focus"):d.find("input:visible,button:visible").first().trigger("focus"))}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.7",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));if(!(a>this.$items.length-1||a<0))return this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){if(!this.sliding)return this.slide("next")},c.prototype.prev=function(){if(!this.sliding)return this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.7",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger(a.Event("hidden.bs.dropdown",f)))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.7",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger(a.Event("shown.bs.dropdown",h))}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);if(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),!c.isInStateTrue())return clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null,a.$element=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;!e&&/destroy|hide/.test(b)||(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.7",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.7",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.7",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return e=a-d&&"bottom"},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery);
\ No newline at end of file
diff --git a/cse/cse/configserver.css b/cse/cse/configserver.css
new file mode 100644
index 0000000..ec8984a
--- /dev/null
+++ b/cse/cse/configserver.css
@@ -0,0 +1,128 @@
+.icon-configserver {
+ color: #990000;
+}
+.btn-default:active,
+.btn-default:visited,
+.btn-default:focus,
+.btn-default {
+ background:#FFFFFF;
+ border-radius:3px;
+ border:1px solid #A6C150;
+ color:#990000 !important;
+}
+.btn-default:hover {
+ border:1px solid #A6C150;
+ background: #F5F5F5;
+}
+input[type=text], select {
+ -webkit-transition: all 0.30s ease-in-out;
+ -moz-transition: all 0.30s ease-in-out;
+ -ms-transition: all 0.30s ease-in-out;
+ -o-transition: all 0.30s ease-in-out;
+ transition: all 0.30s ease-in-out;
+ border-radius:3px;
+ outline: none;
+ padding: 3px 0px 3px 3px;
+ margin: 5px 1px 3px 0px;
+ border: 1px solid #990000;
+}
+input[type=text]:focus, select:focus {
+ box-shadow: 0 0 5px #CC0000;
+ padding: 3px 0px 3px 3px;
+ margin: 5px 1px 3px 0px;
+ border: 1px solid #990000;
+}
+.td-btn {
+ width: 200px;
+}
+.td-text {
+}
+th {
+ background: #F4F4EA;
+}
+.table tbody>tr>td {
+ vertical-align: middle;
+}
+.panel-default > .panel-heading-cxs {
+ font-weight: bold;
+ background: #F4F4EA;
+}
+.panel-default > .panel-footer-cxs {
+ font-weight: bold;
+ background: #F4F4EA;
+}
+#loader {
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ z-index: 1;
+ margin: -75px 0 0 -75px;
+ border: 16px solid #F4F4EA;
+ border-radius: 50%;
+ border-top: 16px solid #990000;
+ border-bottom: 16px solid #990000;
+ width: 120px;
+ height: 120px;
+ -webkit-animation: spin 2s linear infinite;
+ animation: spin 2s linear infinite;
+}
+@-webkit-keyframes spin {
+ 0% { -webkit-transform: rotate(0deg); }
+ 100% { -webkit-transform: rotate(360deg); }
+}
+@keyframes spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
+.bs-callout {
+ padding: 20px;
+ margin: 20px 0;
+ border: 1px solid #eee;
+ border-left-width: 5px;
+ border-radius: 3px;
+}
+.bs-callout h4 {
+ margin-top: 0;
+ margin-bottom: 5px;
+}
+.bs-callout p:last-child {
+ margin-bottom: 0;
+}
+.bs-callout code {
+ border-radius: 3px;
+}
+.bs-callout+.bs-callout {
+ margin-top: -5px;
+}
+.bs-callout-success {
+ border-left-color: #5cb85c;
+ background-color: #edf7ed;
+}
+.bs-callout-success h4 {
+ color: #5cb85c;
+}.bs-callout-info {
+ border-left-color: #5bc0de;
+ background-color: #eaf7fb;
+}
+.bs-callout-info h4 {
+ color: #5bc0de;
+}
+.bs-callout-warning {
+ border-left-color: #f0ad4e;
+ background-color: #fdf4e8;
+}
+.bs-callout-warning h4 {
+ color: #f0ad4e;
+}
+.bs-callout-danger {
+ border-left-color: #d9534f;
+ background-color: #faebea;
+}
+.bs-callout-danger h4 {
+ color: #d9534f;
+}
+.label-pill {
+ padding-right: .6em;
+ padding-left: .6em;
+ border-radius: 10rem;
+}
\ No newline at end of file
diff --git a/cse/cse/cse.png b/cse/cse/cse.png
new file mode 100644
index 0000000..8aa1f03
Binary files /dev/null and b/cse/cse/cse.png differ
diff --git a/cse/cse/cse.svg b/cse/cse/cse.svg
new file mode 100644
index 0000000..07b7465
--- /dev/null
+++ b/cse/cse/cse.svg
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/cse/cse/jquery.min.js b/cse/cse/jquery.min.js
new file mode 100644
index 0000000..e836475
--- /dev/null
+++ b/cse/cse/jquery.min.js
@@ -0,0 +1,5 @@
+/*! jQuery v1.12.4 | (c) jQuery Foundation | jquery.org/license */
+!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=a.document,e=c.slice,f=c.concat,g=c.push,h=c.indexOf,i={},j=i.toString,k=i.hasOwnProperty,l={},m="1.12.4",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return e.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:e.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a){return n.each(this,a)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(e.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:g,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(n.isPlainObject(c)||(b=n.isArray(c)))?(b?(b=!1,f=a&&n.isArray(a)?a:[]):f=a&&n.isPlainObject(a)?a:{},g[d]=n.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray||function(a){return"array"===n.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){var b=a&&a.toString();return!n.isArray(a)&&b-parseFloat(b)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==n.type(a)||a.nodeType||n.isWindow(a))return!1;try{if(a.constructor&&!k.call(a,"constructor")&&!k.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(!l.ownFirst)for(b in a)return k.call(a,b);for(b in a);return void 0===b||k.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?i[j.call(a)]||"object":typeof a},globalEval:function(b){b&&n.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b){var c,d=0;if(s(a)){for(c=a.length;c>d;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):g.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(h)return h.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,g=0,h=[];if(s(a))for(d=a.length;d>g;g++)e=b(a[g],g,c),null!=e&&h.push(e);else for(g in a)e=b(a[g],g,c),null!=e&&h.push(e);return f.apply([],h)},guid:1,proxy:function(a,b){var c,d,f;return"string"==typeof b&&(f=a[b],b=a,a=f),n.isFunction(a)?(c=e.call(arguments,2),d=function(){return a.apply(b||this,c.concat(e.call(arguments)))},d.guid=a.guid=a.guid||n.guid++,d):void 0},now:function(){return+new Date},support:l}),"function"==typeof Symbol&&(n.fn[Symbol.iterator]=c[Symbol.iterator]),n.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){i["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=!!a&&"length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ga(),z=ga(),A=ga(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+M+"))|)"+L+"*\\]",O=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+N+")*)|.*)\\)|)",P=new RegExp(L+"+","g"),Q=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),R=new RegExp("^"+L+"*,"+L+"*"),S=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),T=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),U=new RegExp(O),V=new RegExp("^"+M+"$"),W={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M+"|[*])"),ATTR:new RegExp("^"+N),PSEUDO:new RegExp("^"+O),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},X=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Z=/^[^{]+\{\s*\[native \w/,$=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,_=/[+~]/,aa=/'|\\/g,ba=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),ca=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},da=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(ea){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fa(a,b,d,e){var f,h,j,k,l,o,r,s,w=b&&b.ownerDocument,x=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==x&&9!==x&&11!==x)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==x&&(o=$.exec(a)))if(f=o[1]){if(9===x){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(w&&(j=w.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(o[2])return H.apply(d,b.getElementsByTagName(a)),d;if((f=o[3])&&c.getElementsByClassName&&b.getElementsByClassName)return H.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==x)w=b,s=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(aa,"\\$&"):b.setAttribute("id",k=u),r=g(a),h=r.length,l=V.test(k)?"#"+k:"[id='"+k+"']";while(h--)r[h]=l+" "+qa(r[h]);s=r.join(","),w=_.test(a)&&oa(b.parentNode)||b}if(s)try{return H.apply(d,w.querySelectorAll(s)),d}catch(y){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(Q,"$1"),b,d,e)}function ga(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ha(a){return a[u]=!0,a}function ia(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ja(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function ka(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function la(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function na(a){return ha(function(b){return b=+b,ha(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function oa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=fa.support={},f=fa.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fa.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ia(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ia(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Z.test(n.getElementsByClassName),c.getById=ia(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return"undefined"!=typeof b.getElementsByClassName&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=Z.test(n.querySelectorAll))&&(ia(function(a){o.appendChild(a).innerHTML=" ",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ia(function(a){var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Z.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ia(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",O)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Z.test(o.compareDocumentPosition),t=b||Z.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return ka(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?ka(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},fa.matches=function(a,b){return fa(a,null,null,b)},fa.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(T,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fa(b,n,null,[a]).length>0},fa.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fa.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fa.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fa.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fa.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fa.selectors={cacheLength:50,createPseudo:ha,match:W,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ba,ca),a[3]=(a[3]||a[4]||a[5]||"").replace(ba,ca),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fa.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fa.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return W.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&U.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ba,ca).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fa.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(P," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fa.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ha(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ha(function(a){var b=[],c=[],d=h(a.replace(Q,"$1"));return d[u]?ha(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ha(function(a){return function(b){return fa(a,b).length>0}}),contains:ha(function(a){return a=a.replace(ba,ca),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ha(function(a){return V.test(a||"")||fa.error("unsupported lang: "+a),a=a.replace(ba,ca).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Y.test(a.nodeName)},input:function(a){return X.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:na(function(){return[0]}),last:na(function(a,b){return[b-1]}),eq:na(function(a,b,c){return[0>c?c+b:c]}),even:na(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:na(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:na(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:na(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function ra(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j,k=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(j=b[u]||(b[u]={}),i=j[b.uniqueID]||(j[b.uniqueID]={}),(h=i[d])&&h[0]===w&&h[1]===f)return k[2]=h[2];if(i[d]=k,k[2]=a(b,c,g))return!0}}}function sa(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ta(a,b,c){for(var d=0,e=b.length;e>d;d++)fa(a,b[d],c);return c}function ua(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function va(a,b,c,d,e,f){return d&&!d[u]&&(d=va(d)),e&&!e[u]&&(e=va(e,f)),ha(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ta(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ua(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ua(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ua(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function wa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ra(function(a){return a===b},h,!0),l=ra(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[ra(sa(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return va(i>1&&sa(m),i>1&&qa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(Q,"$1"),c,e>i&&wa(a.slice(i,e)),f>e&&wa(a=a.slice(e)),f>e&&qa(a))}m.push(c)}return sa(m)}function xa(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=F.call(i));u=ua(u)}H.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&fa.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ha(f):f}return h=fa.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xa(e,d)),f.selector=a}return f},i=fa.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ba,ca),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=W.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ba,ca),_.test(j[0].type)&&oa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qa(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,!b||_.test(a)&&oa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ia(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ia(function(a){return a.innerHTML=" ","#"===a.firstChild.getAttribute("href")})||ja("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ia(function(a){return a.innerHTML=" ",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ja("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ia(function(a){return null==a.getAttribute("disabled")})||ja(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fa}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.uniqueSort=n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},v=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},w=n.expr.match.needsContext,x=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,y=/^.[^:#\[\.,]*$/;function z(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(y.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return n.inArray(a,b)>-1!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;e>b;b++)if(n.contains(d[b],this))return!0}));for(b=0;e>b;b++)n.find(a,d[b],c);return c=this.pushStack(e>1?n.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(z(this,a||[],!1))},not:function(a){return this.pushStack(z(this,a||[],!0))},is:function(a){return!!z(this,"string"==typeof a&&w.test(a)?n(a):a||[],!1).length}});var A,B=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=n.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||A,"string"==typeof a){if(e="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:B.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),x.test(e[1])&&n.isPlainObject(b))for(e in b)n.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}if(f=d.getElementById(e[2]),f&&f.parentNode){if(f.id!==e[2])return A.find(a);this.length=1,this[0]=f}return this.context=d,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof c.ready?c.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};C.prototype=n.fn,A=n(d);var D=/^(?:parents|prev(?:Until|All))/,E={children:!0,contents:!0,next:!0,prev:!0};n.fn.extend({has:function(a){var b,c=n(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(n.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=w.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?n.inArray(this[0],n(a)):n.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.uniqueSort(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function F(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return u(a,"parentNode")},parentsUntil:function(a,b,c){return u(a,"parentNode",c)},next:function(a){return F(a,"nextSibling")},prev:function(a){return F(a,"previousSibling")},nextAll:function(a){return u(a,"nextSibling")},prevAll:function(a){return u(a,"previousSibling")},nextUntil:function(a,b,c){return u(a,"nextSibling",c)},prevUntil:function(a,b,c){return u(a,"previousSibling",c)},siblings:function(a){return v((a.parentNode||{}).firstChild,a)},children:function(a){return v(a.firstChild)},contents:function(a){return n.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(E[a]||(e=n.uniqueSort(e)),D.test(a)&&(e=e.reverse())),this.pushStack(e)}});var G=/\S+/g;function H(a){var b={};return n.each(a.match(G)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?H(a):n.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),h>=c&&h--}),this},has:function(a){return a?n.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=!0,c||j.disable(),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().progress(c.notify).done(c.resolve).fail(c.reject):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=e.call(arguments),d=c.length,f=1!==d||a&&n.isFunction(a.promise)?d:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?e.call(arguments):d,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(d>1)for(i=new Array(d),j=new Array(d),k=new Array(d);d>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().progress(h(b,j,i)).done(h(b,k,c)).fail(g.reject):--f;return f||g.resolveWith(k,c),g.promise()}});var I;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(I.resolveWith(d,[n]),n.fn.triggerHandler&&(n(d).triggerHandler("ready"),n(d).off("ready"))))}});function J(){d.addEventListener?(d.removeEventListener("DOMContentLoaded",K),a.removeEventListener("load",K)):(d.detachEvent("onreadystatechange",K),a.detachEvent("onload",K))}function K(){(d.addEventListener||"load"===a.event.type||"complete"===d.readyState)&&(J(),n.ready())}n.ready.promise=function(b){if(!I)if(I=n.Deferred(),"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll)a.setTimeout(n.ready);else if(d.addEventListener)d.addEventListener("DOMContentLoaded",K),a.addEventListener("load",K);else{d.attachEvent("onreadystatechange",K),a.attachEvent("onload",K);var c=!1;try{c=null==a.frameElement&&d.documentElement}catch(e){}c&&c.doScroll&&!function f(){if(!n.isReady){try{c.doScroll("left")}catch(b){return a.setTimeout(f,50)}J(),n.ready()}}()}return I.promise(b)},n.ready.promise();var L;for(L in n(l))break;l.ownFirst="0"===L,l.inlineBlockNeedsLayout=!1,n(function(){var a,b,c,e;c=d.getElementsByTagName("body")[0],c&&c.style&&(b=d.createElement("div"),e=d.createElement("div"),e.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(e).appendChild(b),"undefined"!=typeof b.style.zoom&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",l.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(e))}),function(){var a=d.createElement("div");l.deleteExpando=!0;try{delete a.test}catch(b){l.deleteExpando=!1}a=null}();var M=function(a){var b=n.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b},N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(O,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}n.data(a,b,c)}else c=void 0;
+}return c}function Q(a){var b;for(b in a)if(("data"!==b||!n.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function R(a,b,d,e){if(M(a)){var f,g,h=n.expando,i=a.nodeType,j=i?n.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||n.guid++:h),j[k]||(j[k]=i?{}:{toJSON:n.noop}),"object"!=typeof b&&"function"!=typeof b||(e?j[k]=n.extend(j[k],b):j[k].data=n.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[n.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[n.camelCase(b)])):f=g,f}}function S(a,b,c){if(M(a)){var d,e,f=a.nodeType,g=f?n.cache:a,h=f?a[n.expando]:n.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){n.isArray(b)?b=b.concat(n.map(b,n.camelCase)):b in d?b=[b]:(b=n.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!Q(d):!n.isEmptyObject(d))return}(c||(delete g[h].data,Q(g[h])))&&(f?n.cleanData([a],!0):l.deleteExpando||g!=g.window?delete g[h]:g[h]=void 0)}}}n.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?n.cache[a[n.expando]]:a[n.expando],!!a&&!Q(a)},data:function(a,b,c){return R(a,b,c)},removeData:function(a,b){return S(a,b)},_data:function(a,b,c){return R(a,b,c,!0)},_removeData:function(a,b){return S(a,b,!0)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=n.data(f),1===f.nodeType&&!n._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));n._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){n.data(this,a)}):arguments.length>1?this.each(function(){n.data(this,a,b)}):f?P(f,a,n.data(f,a)):void 0},removeData:function(a){return this.each(function(){n.removeData(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=n._data(a,b),c&&(!d||n.isArray(c)?d=n._data(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return n._data(a,c)||n._data(a,c,{empty:n.Callbacks("once memory").add(function(){n._removeData(a,b+"queue"),n._removeData(a,c)})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},Z=/^(?:checkbox|radio)$/i,$=/<([\w:-]+)/,_=/^$|\/(?:java|ecma)script/i,aa=/^\s+/,ba="abbr|article|aside|audio|bdi|canvas|data|datalist|details|dialog|figcaption|figure|footer|header|hgroup|main|mark|meter|nav|output|picture|progress|section|summary|template|time|video";function ca(a){var b=ba.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}!function(){var a=d.createElement("div"),b=d.createDocumentFragment(),c=d.createElement("input");a.innerHTML=" a ",l.leadingWhitespace=3===a.firstChild.nodeType,l.tbody=!a.getElementsByTagName("tbody").length,l.htmlSerialize=!!a.getElementsByTagName("link").length,l.html5Clone="<:nav>"!==d.createElement("nav").cloneNode(!0).outerHTML,c.type="checkbox",c.checked=!0,b.appendChild(c),l.appendChecked=c.checked,a.innerHTML="",l.noCloneChecked=!!a.cloneNode(!0).lastChild.defaultValue,b.appendChild(a),c=d.createElement("input"),c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),a.appendChild(c),l.checkClone=a.cloneNode(!0).cloneNode(!0).lastChild.checked,l.noCloneEvent=!!a.addEventListener,a[n.expando]=1,l.attributes=!a.getAttribute(n.expando)}();var da={option:[1,""," "],legend:[1,""," "],area:[1,""," "],param:[1,""," "],thead:[1,""],tr:[2,""],col:[2,""],td:[3,""],_default:l.htmlSerialize?[0,"",""]:[1,"X","
"]};da.optgroup=da.option,da.tbody=da.tfoot=da.colgroup=da.caption=da.thead,da.th=da.td;function ea(a,b){var c,d,e=0,f="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||n.nodeName(d,b)?f.push(d):n.merge(f,ea(d,b));return void 0===b||b&&n.nodeName(a,b)?n.merge([a],f):f}function fa(a,b){for(var c,d=0;null!=(c=a[d]);d++)n._data(c,"globalEval",!b||n._data(b[d],"globalEval"))}var ga=/<|?\w+;/,ha=/r;r++)if(g=a[r],g||0===g)if("object"===n.type(g))n.merge(q,g.nodeType?[g]:g);else if(ga.test(g)){i=i||p.appendChild(b.createElement("div")),j=($.exec(g)||["",""])[1].toLowerCase(),m=da[j]||da._default,i.innerHTML=m[1]+n.htmlPrefilter(g)+m[2],f=m[0];while(f--)i=i.lastChild;if(!l.leadingWhitespace&&aa.test(g)&&q.push(b.createTextNode(aa.exec(g)[0])),!l.tbody){g="table"!==j||ha.test(g)?""!==m[1]||ha.test(g)?0:i:i.firstChild,f=g&&g.childNodes.length;while(f--)n.nodeName(k=g.childNodes[f],"tbody")&&!k.childNodes.length&&g.removeChild(k)}n.merge(q,i.childNodes),i.textContent="";while(i.firstChild)i.removeChild(i.firstChild);i=p.lastChild}else q.push(b.createTextNode(g));i&&p.removeChild(i),l.appendChecked||n.grep(ea(q,"input"),ia),r=0;while(g=q[r++])if(d&&n.inArray(g,d)>-1)e&&e.push(g);else if(h=n.contains(g.ownerDocument,g),i=ea(p.appendChild(g),"script"),h&&fa(i),c){f=0;while(g=i[f++])_.test(g.type||"")&&c.push(g)}return i=null,p}!function(){var b,c,e=d.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(l[b]=c in a)||(e.setAttribute(c,"t"),l[b]=e.attributes[c].expando===!1);e=null}();var ka=/^(?:input|select|textarea)$/i,la=/^key/,ma=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,na=/^(?:focusinfocus|focusoutblur)$/,oa=/^([^.]*)(?:\.(.+)|)/;function pa(){return!0}function qa(){return!1}function ra(){try{return d.activeElement}catch(a){}}function sa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)sa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=qa;else if(!e)return a;return 1===f&&(g=e,e=function(a){return n().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=n.guid++)),a.each(function(){n.event.add(this,b,e,d,c)})}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=n._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=n.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return"undefined"==typeof n||a&&n.event.triggered===a.type?void 0:n.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(G)||[""],h=b.length;while(h--)f=oa.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=n.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=n.event.special[o]||{},l=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},i),(m=g[o])||(m=g[o]=[],m.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,l):m.push(l),n.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=n.hasData(a)&&n._data(a);if(r&&(k=r.events)){b=(b||"").match(G)||[""],j=b.length;while(j--)if(h=oa.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=m.length;while(f--)g=m[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(m.splice(f,1),g.selector&&m.delegateCount--,l.remove&&l.remove.call(a,g));i&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(k)&&(delete r.handle,n._removeData(a,"events"))}},trigger:function(b,c,e,f){var g,h,i,j,l,m,o,p=[e||d],q=k.call(b,"type")?b.type:b,r=k.call(b,"namespace")?b.namespace.split("."):[];if(i=m=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!na.test(q+n.event.triggered)&&(q.indexOf(".")>-1&&(r=q.split("."),q=r.shift(),r.sort()),h=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=r.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:n.makeArray(c,[b]),l=n.event.special[q]||{},f||!l.trigger||l.trigger.apply(e,c)!==!1)){if(!f&&!l.noBubble&&!n.isWindow(e)){for(j=l.delegateType||q,na.test(j+q)||(i=i.parentNode);i;i=i.parentNode)p.push(i),m=i;m===(e.ownerDocument||d)&&p.push(m.defaultView||m.parentWindow||a)}o=0;while((i=p[o++])&&!b.isPropagationStopped())b.type=o>1?j:l.bindType||q,g=(n._data(i,"events")||{})[b.type]&&n._data(i,"handle"),g&&g.apply(i,c),g=h&&i[h],g&&g.apply&&M(i)&&(b.result=g.apply(i,c),b.result===!1&&b.preventDefault());if(b.type=q,!f&&!b.isDefaultPrevented()&&(!l._default||l._default.apply(p.pop(),c)===!1)&&M(e)&&h&&e[q]&&!n.isWindow(e)){m=e[h],m&&(e[h]=null),n.event.triggered=q;try{e[q]()}catch(s){}n.event.triggered=void 0,m&&(e[h]=m)}return b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,d,f,g,h=[],i=e.call(arguments),j=(n._data(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())a.rnamespace&&!a.rnamespace.test(g.namespace)||(a.handleObj=g,a.data=g.data,d=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==d&&(a.result=d)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&("click"!==a.type||isNaN(a.button)||a.button<1))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>-1:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h ]","i"),va=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,wa=/\n";
+ print <$options Lines: Refresh Now
+Refresh in
0 Pause
+a
+A
+
+
+
+EOF
+ if ($config{DIRECTADMIN}) {$script = $script_safe}
+ &printreturn;
+ }
+ elsif ($FORM{action} eq "logtailcmd") {
+ $FORM{lines} =~ s/\D//g;
+ if ($FORM{lines} eq "" or $FORM{lines} == 0) {$FORM{lines} = 30}
+
+ my @data = slurp("/etc/csf/csf.syslogs");
+ foreach my $line (@data) {
+ if ($line =~ /^Include\s*(.*)$/) {
+ my @incfile = slurp($1);
+ push @data,@incfile;
+ }
+ }
+ @data = sort @data;
+ my $cnt = 0;
+ my $logfile = "/var/log/lfd.log";
+ my $hit = 0;
+ foreach my $file (@data) {
+ $file =~ s/$cleanreg//g;
+ if ($file eq "") {next}
+ if ($file =~ /^\s*\#|Include/) {next}
+ my @globfiles;
+ if ($file =~ /\*|\?|\[/) {
+ foreach my $log (glob $file) {push @globfiles, $log}
+ } else {push @globfiles, $file}
+
+ foreach my $globfile (@globfiles) {
+ if (-f $globfile) {
+ if ($FORM{lognum} == $cnt) {
+ $logfile = $globfile;
+ $hit = 1;
+ last;
+ }
+ $cnt++;
+ }
+ }
+ if ($hit) {last}
+ }
+ if (-z $logfile) {
+ print "<---- $logfile is currently empty ---->";
+ } else {
+ if (-x $config{TAIL}) {
+ my $timeout = 30;
+ eval {
+ local $SIG{__DIE__} = undef;
+ local $SIG{'ALRM'} = sub {die};
+ alarm($timeout);
+ my ($childin, $childout);
+ my $pid = open3($childin, $childout, $childout,$config{TAIL},"-$FORM{lines}",$logfile);
+ while (<$childout>) {
+ my $line = $_;
+ $line =~ s/&/&/g;
+ $line =~ s/</g;
+ $line =~ s/>/>/g;
+ print $line;
+ }
+ waitpid ($pid, 0);
+ alarm(0);
+ };
+ alarm(0);
+ if ($@) {print "TIMEOUT: tail command took too long. Timed out after $timeout seconds\n"}
+ } else {
+ print "Executable [$config{TAIL}] invalid";
+ }
+ }
+ }
+ elsif ($FORM{action} eq "loggrep") {
+ $FORM{lines} =~ s/\D//g;
+ if ($FORM{lines} eq "" or $FORM{lines} == 0) {$FORM{lines} = 30}
+ my $script_safe = $script;
+ my $CSFfrombot = 120;
+ my $CSFfromright = 10;
+ if ($config{DIRECTADMIN}) {
+ $script = $script_da;
+ $CSFfrombot = 400;
+ $CSFfromright = 150;
+ }
+ my @data = slurp("/etc/csf/csf.syslogs");
+ foreach my $line (@data) {
+ if ($line =~ /^Include\s*(.*)$/) {
+ my @incfile = slurp($1);
+ push @data,@incfile;
+ }
+ }
+ @data = sort @data;
+ my $options = "\n";
+ my $cnt = 0;
+ foreach my $file (@data) {
+ $file =~ s/$cleanreg//g;
+ if ($file eq "") {next}
+ if ($file =~ /^\s*\#|Include/) {next}
+ my @globfiles;
+ if ($file =~ /\*|\?|\[/) {
+ foreach my $log (glob $file) {push @globfiles, $log}
+ } else {push @globfiles, $file}
+
+ foreach my $globfile (@globfiles) {
+ if (-f $globfile) {
+ my $size = int((stat($globfile))[7]/1024);
+ $options .= ";
+ close ($AJAX);
+ print "\n";
+ print <Log: $options
+
+a
+A
+
+
+
+EOF
+ if ($config{DIRECTADMIN}) {$script = $script_safe}
+ &printreturn;
+ }
+ elsif ($FORM{action} eq "loggrepcmd") {
+ my @data = slurp("/etc/csf/csf.syslogs");
+ foreach my $line (@data) {
+ if ($line =~ /^Include\s*(.*)$/) {
+ my @incfile = slurp($1);
+ push @data,@incfile;
+ }
+ }
+ @data = sort @data;
+ my $cnt = 0;
+ my $logfile = "/var/log/lfd.log";
+ my $hit = 0;
+ foreach my $file (@data) {
+ $file =~ s/$cleanreg//g;
+ if ($file eq "") {next}
+ if ($file =~ /^\s*\#|Include/) {next}
+ my @globfiles;
+ if ($file =~ /\*|\?|\[/) {
+ foreach my $log (glob $file) {push @globfiles, $log}
+ } else {push @globfiles, $file}
+
+ foreach my $globfile (@globfiles) {
+ if (-f $globfile) {
+ if ($FORM{lognum} == $cnt) {
+ $logfile = $globfile;
+ $hit = 1;
+ last;
+ }
+ $cnt++;
+ }
+ }
+ if ($hit) {last}
+ }
+ my @cmd;
+ my $grepbin = $config{GREP};
+ if ($FORM{grepZ}) {$grepbin = $config{ZGREP}}
+ if ($FORM{grepi}) {push @cmd, "-i"}
+ if ($FORM{grepE}) {push @cmd, "-E"}
+ push @cmd, $FORM{grep};
+
+ if (-z $logfile) {
+ print "<---- $logfile is currently empty ---->";
+ } else {
+ if (-x $grepbin) {
+ my $timeout = 30;
+ eval {
+ local $SIG{__DIE__} = undef;
+ local $SIG{'ALRM'} = sub {die};
+ my $total;
+ if ($FORM{grepZ}) {
+ foreach my $file (glob $logfile."\*") {
+ print "\nSearching $file:\n";
+ alarm($timeout);
+ my ($childin, $childout);
+ my $pid = open3($childin, $childout, $childout,$grepbin,@cmd,$file);
+ while (<$childout>) {
+ my $line = $_;
+ $line =~ s/&/&/g;
+ $line =~ s/</g;
+ $line =~ s/>/>/g;
+ if ($FORM{grep} ne "") {
+ eval {
+ local $SIG{__DIE__} = undef;
+ if ($FORM{grepi}) {
+ $line =~ s/$FORM{grep}/$&<\/mark>/ig;
+ } else {
+ $line =~ s/$FORM{grep}/$&<\/mark>/g;
+ }
+ };
+ }
+ print $line;
+ $total += length $line;
+ }
+ waitpid ($pid, 0);
+ unless ($total) {print "<---- No matches found for \"$FORM{grep}\" in $file ---->\n"}
+ alarm(0);
+ }
+ } else {
+ alarm($timeout);
+ my ($childin, $childout);
+ my $pid = open3($childin, $childout, $childout,$grepbin,@cmd,$logfile);
+ while (<$childout>) {
+ my $line = $_;
+ $line =~ s/&/&/g;
+ $line =~ s/</g;
+ $line =~ s/>/>/g;
+ if ($FORM{grep} ne "") {
+ eval {
+ local $SIG{__DIE__} = undef;
+ if ($FORM{grepi}) {
+ $line =~ s/$FORM{grep}/$&<\/mark>/ig;
+ } else {
+ $line =~ s/$FORM{grep}/$&<\/mark>/g;
+ }
+ };
+ }
+ print $line;
+ $total += length $line;
+ }
+ waitpid ($pid, 0);
+ unless ($total) {print "<---- No matches found for \"$FORM{grep}\" in $logfile ---->\n"}
+ alarm(0);
+ }
+ };
+ alarm(0);
+ if ($@) {print "TIMEOUT: grep command took too long. Timed out after $timeout seconds\n"}
+ } else {
+ print "Executable [$grepbin] invalid";
+ }
+ }
+ }
+ elsif ($FORM{action} eq "readme") {
+ &resize("top");
+ print "\n";
+ &resize("bot",0);
+ &printreturn;
+ }
+ elsif ($FORM{action} eq "servercheck") {
+ print ConfigServer::ServerCheck::report($FORM{verbose});
+
+ open (my $IN, "<", "/etc/cron.d/csf-cron");
+ flock ($IN, LOCK_SH);
+ my @data = <$IN>;
+ close ($IN);
+ chomp @data;
+ my $optionselected = "never";
+ my $email;
+ if (my @ls = grep {$_ =~ /csf \-m/} @data) {
+ if ($ls[0] =~ /\@(\w+)\s+root\s+\/usr\/sbin\/csf \-m (.*)/) {$optionselected = $1; $email = $2}
+ }
+ print "
\n";
+
+ print "
\n";
+ print "
\n";
+ &printreturn;
+ }
+ elsif ($FORM{action} eq "serverchecksave") {
+ my $extra = "";
+ my $freq = "daily";
+ my $email;
+ if ($FORM{email} ne "") {$email = "root"}
+ if ($FORM{email} =~ /^[a-zA-Z0-9\-\_\.\@\+]+$/) {$email = $FORM{email}}
+ foreach my $option ("never","hourly","daily","weekly","monthly") {if ($FORM{freq} eq $option) {$freq = $option}}
+ unless ($email) {$freq = "never"; $extra = "(no valid email address supplied)";}
+ sysopen (my $CRON, "/etc/cron.d/csf-cron", O_RDWR | O_CREAT) or die "Unable to open file: $!";
+ flock ($CRON, LOCK_EX);
+ my @data = <$CRON>;
+ chomp @data;
+ seek ($CRON, 0, 0);
+ truncate ($CRON, 0);
+ my $done = 0;
+ foreach my $line (@data) {
+ if ($line =~ /csf \-m/) {
+ if ($freq and ($freq ne "never") and !$done) {
+ print $CRON "\@$freq root /usr/sbin/csf -m $email\n";
+ $done = 1;
+ }
+ } else {
+ print $CRON "$line\n";
+ }
+ }
+ if (!$done and ($freq ne "never")) {
+ print $CRON "\@$freq root /usr/sbin/csf -m $email\n";
+ }
+ close ($CRON);
+
+ if ($freq and $freq ne "never") {
+ print "Report scheduled to be emailed to $email $freq
\n";
+ } else {
+ print "Report schedule cancelled $extra
\n";
+ }
+ print "
\n";
+ }
+ elsif ($FORM{action} eq "rblcheck") {
+ my ($status, undef) = ConfigServer::RBLCheck::report($FORM{verbose},$images,1);
+
+ print "These options can take a long time to run (several minutes) depending on the number of IP addresses to check and the response speed of the DNS requests:
\n";
+ print "
\n";
+ print "
\n";
+ print "
\n";
+
+ open (my $IN, "<", "/etc/cron.d/csf-cron");
+ flock ($IN, LOCK_SH);
+ my @data = <$IN>;
+ close ($IN);
+ chomp @data;
+ my $optionselected = "never";
+ my $email;
+ if (my @ls = grep {$_ =~ /csf \-\-rbl/} @data) {
+ if ($ls[0] =~ /\@(\w+)\s+root\s+\/usr\/sbin\/csf \-\-rbl (.*)/) {$optionselected = $1; $email = $2}
+ }
+ print "
\n";
+ &printreturn;
+ }
+ elsif ($FORM{action} eq "rblchecksave") {
+ my $extra = "";
+ my $freq = "daily";
+ my $email;
+ if ($FORM{email} ne "") {$email = "root"}
+ if ($FORM{email} =~ /^[a-zA-Z0-9\-\_\.\@\+]+$/) {$email = $FORM{email}}
+ foreach my $option ("never","hourly","daily","weekly","monthly") {if ($FORM{freq} eq $option) {$freq = $option}}
+ unless ($email) {$freq = "never"; $extra = "(no valid email address supplied)";}
+ sysopen (my $CRON, "/etc/cron.d/csf-cron", O_RDWR | O_CREAT) or die "Unable to open file: $!";
+ flock ($CRON, LOCK_EX);
+ my @data = <$CRON>;
+ chomp @data;
+ seek ($CRON, 0, 0);
+ truncate ($CRON, 0);
+ my $done = 0;
+ foreach my $line (@data) {
+ if ($line =~ /csf \-\-rbl/) {
+ if ($freq and ($freq ne "never") and !$done) {
+ print $CRON "\@$freq root /usr/sbin/csf --rbl $email\n";
+ $done = 1;
+ }
+ } else {
+ print $CRON "$line\n";
+ }
+ }
+ if (!$done and ($freq ne "never")) {
+ print $CRON "\@$freq root /usr/sbin/csf --rbl $email\n";
+ }
+ close ($CRON);
+
+ if ($freq and $freq ne "never") {
+ print "Report scheduled to be emailed to $email $freq
\n";
+ } else {
+ print "Report schedule cancelled $extra
\n";
+ }
+ print "
\n";
+ }
+ elsif ($FORM{action} eq "rblcheckedit") {
+ &editfile("/etc/csf/csf.rblconf","saverblcheckedit");
+ print "
\n";
+ }
+ elsif ($FORM{action} eq "saverblcheckedit") {
+ &savefile("/etc/csf/csf.rblconf","");
+ print "
\n";
+ }
+ elsif ($FORM{action} eq "cloudflareedit") {
+ &editfile("/etc/csf/csf.cloudflare","savecloudflareedit");
+ &printreturn;
+ }
+ elsif ($FORM{action} eq "savecloudflareedit") {
+ &savefile("/etc/csf/csf.cloudflare","");
+ &printreturn;
+ }
+ elsif ($FORM{action} eq "restartboth") {
+ print "Restarting csf...
\n";
+ &resize("top");
+ print "\n
...Done .
\n";
+ if ($config{THIS_UI}) {
+ print "Signal lfd to restart ...
\n
\n"}
+ print "\n
";
+ print "$1
\n";
+ $first = 0;
+ next;
+ }
+ if ($line =~ /^\# / and $comment == 0) {
+ $comment = 1;
+ print "
\n";
+ print "
\n";
+ print <
+var pagecontent=new virtualpaginate({
+ piececlass: "virtualpage", //class of container for each piece of content
+ piececontainer: "div", //container element type (ie: "div", "p" etc)
+ pieces_per_page: 1, //Pieces of content to show per page (1=1 piece, 2=2 pieces etc)
+ defaultpage: 0, //Default page selected (0=1st page, 1=2nd page etc). Persistence if enabled overrides this setting.
+ wraparound: false,
+ persist: false //Remember last viewed page and recall it when user returns within a browser session?
+});
+EOD
+ print "pagecontent.buildpagination(['paginatediv','paginatediv2'],[";
+ foreach my $line (@divnames) {print "'$line',"}
+ print "''])\npagecontent.showall();\n\n";
+ print "
\n";
+ print "\n";
+ &printreturn;
+ }
+ elsif ($FORM{action} eq "saveconf") {
+ sysopen (my $IN, "/etc/csf/csf.conf", O_RDWR | O_CREAT) or die "Unable to open file: $!";
+ flock ($IN, LOCK_SH);
+ my @confdata = <$IN>;
+ close ($IN);
+ chomp @confdata;
+
+ my %restricted;
+ if ($config{RESTRICT_UI}) {
+ sysopen (my $IN, "/usr/local/csf/lib/restricted.txt", O_RDWR | O_CREAT) or die "Unable to open file: $!";
+ flock ($IN, LOCK_SH);
+ while (my $entry = <$IN>) {
+ chomp $entry;
+ $restricted{$entry} = 1;
+ }
+ close ($IN);
+ }
+
+ sysopen (my $OUT, "/etc/csf/csf.conf", O_WRONLY | O_CREAT) or die "Unable to open file: $!";
+ flock ($OUT, LOCK_EX);
+ seek ($OUT, 0, 0);
+ truncate ($OUT, 0);
+ for (my $x = 0; $x < @confdata;$x++) {
+ if (($confdata[$x] !~ /^\#/) and ($confdata[$x] =~ /=/)) {
+ my ($start,$end) = split (/=/,$confdata[$x],2);
+ if ($end =~ /\"(.*)\"/) {$end = $1}
+ my $name = $start;
+ my $sanity_name = $start;
+ $name =~ s/\s/\_/g;
+ $sanity_name =~ s/\s//g;
+ if ($restricted{$sanity_name}) {
+ print $OUT "$confdata[$x]\n";
+ } else {
+ print $OUT "$start= \"$FORM{$name}\"\n";
+ $end = $FORM{$name};
+ }
+ } else {
+ print $OUT "$confdata[$x]\n";
+ }
+ }
+ close ($OUT);
+ ConfigServer::Config::resetconfig();
+ my $newconfig = ConfigServer::Config->loadconfig();
+ my %newconfig = $config->config;
+ foreach my $key (keys %newconfig) {
+ my ($insane,$range,$default) = sanity($key,$newconfig{$key});
+ if ($insane) {print " WARNING: $key sanity check. $key = \"$newconfig{$key}\". Recommended range: $range (Default: $default)\n"}
+ }
+
+ print "Changes saved. You should restart both csf and lfd.
\n";
+ print "
\n";
+ &printreturn;
+ }
+ elsif ($FORM{action} eq "viewlogs") {
+ if (-e "/var/lib/csf/stats/iptables_log") {
+ open (my $IN, "<", "/var/lib/csf/stats/iptables_log") or die "Unable to open file: $!";
+ flock ($IN, LOCK_SH);
+ my @iptables = <$IN>;
+ close ($IN);
+ chomp @iptables;
+ @iptables = reverse @iptables;
+ my $from;
+ my $to;
+ my $divcnt = 0;
+ my $expcnt = @iptables;
+
+ if ($iptables[0] =~ /\|(\S+\s+\d+\s+\S+)/) {$from = $1}
+ if ($iptables[-1] =~ /\|(\S+\s+\d+\s+\S+)/) {$to = $1}
+
+ print " \n";
+ print "
\n";
+ print "Last $config{ST_IPTABLES} iptables logs*, latest:$from oldest:$to \n";
+ print "\n";
+ print "Time From Port I/O To Port Proto \n";
+ my $size = scalar @iptables;
+ if ($size > $config{ST_IPTABLES}) {$size = $config{ST_IPTABLES}}
+ for (my $x = 0 ;$x < $size ;$x++) {
+ my $line = $iptables[$x];
+ $divcnt++;
+ my ($text,$log) = split(/\|/,$line);
+ my ($time,$desc,$in,$out,$src,$dst,$spt,$dpt,$proto,$inout);
+ if ($log =~ /IN=(\S+)/) {$in = $1}
+ if ($log =~ /OUT=(\S+)/) {$out = $1}
+ if ($log =~ /SRC=(\S+)/) {$src = $1}
+ if ($log =~ /DST=(\S+)/) {$dst = $1}
+ if ($log =~ /SPT=(\d+)/) {$spt = $1}
+ if ($log =~ /DPT=(\d+)/) {$dpt = $1}
+ if ($log =~ /PROTO=(\S+)/) {$proto = $1}
+
+ if ($text ne "") {
+ $text =~ s/\(/\ \(/g;
+ if ($in and $src) {$src = $text ; $dst .= " (server)"}
+ elsif ($out and $dst) {$dst = $text ; $src .= " (server)"}
+ }
+ if ($log =~ /^(\S+\s+\d+\s+\S+)/) {$time = $1}
+
+ $inout = "n/a";
+ if ($in) {$inout = "in"}
+ elsif ($out) {$inout = "out"}
+
+ print " $time$src $spt $inout $dst $dpt $proto \n";
+
+ $log =~ s/\&/\&\;/g;
+ $log =~ s/>/\>\;/g;
+ $log =~ s/\<\;/g;
+ print "\n";
+ }
+ print "
\n";
+ print "* These iptables logs taken from $config{IPTABLES_LOG} will not necessarily show all packets blocked by iptables. For example, ports listed in DROP_NOLOG or the settings for DROP_LOGGING/DROP_IP_LOGGING/DROP_ONLYRES/DROP_PF_LOGGING will affect what is logged. Additionally, there is rate limiting on all iptables log rules to prevent log file flooding
\n";
+ } else {
+ print " No logs entries found
\n";
+ }
+ &printreturn;
+ }
+ elsif ($FORM{action} eq "sips") {
+ sysopen (my $IN, "/etc/csf/csf.sips", O_RDWR | O_CREAT) or die "Unable to open file: $!";
+ flock ($IN, LOCK_SH);
+ my @confdata = <$IN>;
+ close ($IN);
+ chomp @confdata;
+
+ print "\n";
+ &printreturn;
+ }
+ elsif ($FORM{action} eq "sipsave") {
+ open (my $IN,"<","/etc/csf/csf.sips");
+ flock ($IN, LOCK_SH);
+ my @data = <$IN>;
+ close ($IN);
+ chomp @data;
+
+ open (my $OUT,">","/etc/csf/csf.sips");
+ flock ($OUT, LOCK_EX);
+ foreach my $line (@data) {
+ if ($line =~ /^\#/) {print $OUT "$line\n"} else {last}
+ }
+ foreach my $key (keys %FORM) {
+ if ($key =~ /^ip_(.*)/) {
+ my $ip = $1;
+ $ip =~ s/\_/\./g;
+ print $OUT "$ip\n";
+ }
+ }
+ close($OUT);
+
+ print "Changes saved. You should restart csf.
\n";
+ print "
\n";
+ &printreturn;
+ }
+ elsif ($FORM{action} eq "upgrade") {
+ if ($config{THIS_UI}) {
+ print "You cannot upgrade through the UI as restarting lfd will interrupt this session. You must login to the root shell to upgrade csf using:\n
csf -u
\n";
+ } else {
+ print "Upgrading csf...
\n";
+ &resize("top");
+ print "\n
...Done .
\n";
+ &resize("bot",1);
+
+ open (my $IN, "<", "/etc/csf/version.txt") or die $!;
+ flock ($IN, LOCK_SH);
+ $myv = <$IN>;
+ close ($IN);
+ chomp $myv;
+ }
+
+ &printreturn;
+ }
+ elsif ($FORM{action} eq "denyf") {
+ print "Removing all entries from csf.deny...
\n";
+ &resize("top");
+ print "\n
...Done .
\n";
+ &resize("bot",1);
+ &printreturn;
+ }
+ elsif ($FORM{action} eq "csftest") {
+ print "Testing iptables...
\n\n
...Done .
\n";
+ print "You should restart csf after having run this test.
\n";
+ print "
\n";
+ &printreturn;
+ }
+ elsif ($FORM{action} eq "profiles") {
+ my @profiles = sort glob("/usr/local/csf/profiles/*");
+ my @backups = reverse glob("/var/lib/csf/backup/*");
+
+ print "\n";
+
+ print " \n";
+
+ print " \n";
+
+ print " \n";
+
+ &printreturn;
+ }
+ elsif ($FORM{action} eq "profileapply") {
+ my $profile = $FORM{profile};
+ $profile =~ s/\W/_/g;
+ print "Applying profile ($profile)...
\n\n
...Done .
\n";
+ print "You should restart both csf and lfd.
\n";
+ print "
\n";
+ &printreturn;
+ }
+ elsif ($FORM{action} eq "profilebackup") {
+ my $profile = $FORM{backup};
+ $profile =~ s/\W/_/g;
+ print "Creating backup...
\n\n
...Done .
\n";
+ &printreturn;
+ }
+ elsif ($FORM{action} eq "profilerestore") {
+ my $profile = $FORM{backup};
+ $profile =~ s/\W/_/g;
+ print "Restoring backup ($profile)...
\n\n
...Done .
\n";
+ print "You should restart both csf and lfd.
\n";
+ print "
\n";
+ &printreturn;
+ }
+ elsif ($FORM{action} eq "profilediff") {
+ my $profile1 = $FORM{profile1};
+ my $profile2 = $FORM{profile2};
+ $profile2 =~ s/\W/_/g;
+ $profile2 =~ s/\W/_/g;
+
+ print "\n";
+ my ($childin, $childout);
+ my $pid = open3($childin, $childout, $childout, "/usr/sbin/csf","--profile","diff",$profile1,$profile2);
+ while (<$childout>) {
+ $_ =~ s/\[|\]//g;
+ my ($var,$p1,$p2) = split(/\s+/,$_);
+ if ($var eq "") {
+ next;
+ }
+ elsif ($var eq "SETTING") {
+ print "$var $p1 $p2 \n";
+ }
+ else {
+ print "$var $p1 $p2 \n";
+ }
+ }
+ waitpid ($pid, 0);
+ print "
\n";
+
+ &printreturn;
+ }
+ elsif ($FORM{action} eq "viewports") {
+ print "
Ports listening for external connections and the executables running behind them: \n";
+ print "\n";
+ print "Port Proto Open Conns PID User Command Line Executable \n";
+ my %listen = ConfigServer::Ports->listening;
+ my %ports = ConfigServer::Ports->openports;
+ foreach my $protocol (sort keys %listen) {
+ foreach my $port (sort {$a <=> $b} keys %{$listen{$protocol}}) {
+ foreach my $pid (sort {$a <=> $b} keys %{$listen{$protocol}{$port}}) {
+ my $fopen;
+ if ($ports{$protocol}{$port}) {$fopen = "4"} else {$fopen = "-"}
+ if ($config{IPV6} and $ports{$protocol."6"}{$port}) {$fopen .= "/6"} else {$fopen .= "/-"}
+
+ my $fcmd = ($listen{$protocol}{$port}{$pid}{cmd});
+ $fcmd =~ s/\\</g;
+ $fcmd =~ s/\&/\&/g;
+
+ my $fexe = $listen{$protocol}{$port}{$pid}{exe};
+ $fexe =~ s/\\</g;
+ $fexe =~ s/\&/\&/g;
+
+ my $fconn = $listen{$protocol}{$port}{$pid}{conn};
+ print "$port $protocol $fopen $fconn $pid $listen{$protocol}{$port}{$pid}{user} $fcmd $fexe \n";
+ }
+ }
+ }
+ print "
\n";
+
+ &printreturn;
+ }
+ elsif ($mobile) {
+ print "\n";
+ }
+ elsif ($FORM{action} eq "fix") {
+ print "These options should only be used as a last resort as most of them will reduce the effectiveness of csf and lfd to protect the server
\n";
+
+ print "\n";
+ print "Fix Common Problems ";
+
+ if ($config{LF_SPI} == 0) {
+ print "Disable SPI \n";
+ } else {
+ print "Disable SPI \n";
+ }
+ print "If you find that ports listed in TCP_IN/UDP_IN are being blocked by iptables (e.g. port 80) as seen in /var/log/messages and users can only connect to the server if entered in csf.allow, then it could be that the kernel (usually on virtual servers) is broken and cannot perform connection tracking. In this case, disabling the Stateful Packet Inspection functionality of csf (LF_SPI) may help\n";
+ if ($config{LF_SPI} == 0) {
+ print "Note: LF_SPI is already disabled ";
+ }
+ print " \n";
+
+ if ($config{TCP_IN} =~ /30000:35000/) {
+ print "Open PASV FTP Hole \n";
+ } else {
+ print "Open PASV FTP Hole \n";
+ }
+ print "If the kernel (usually on virtual servers) is broken and cannot perform ftp connection tracking, or if you are trying to use FTP over SSL, this option will open a hole in the firewall to allow PASV connections through\n";
+ if ($config{TCP_IN} =~ /30000:35000/) {
+ print "Note: The port range 30000 to 35000 is already open in csf \n";
+ }
+ print " \n";
+
+ if ($config{PT_USERKILL} == 0) {
+ print "Disable PT_USERKILL \n";
+ } else {
+ print "Disable PT_USERKILL \n";
+ }
+ print "If lfd is killing running processes and you have PT_USERKILL enabled, then we recommend that you disable this feature\n";
+ if ($config{PT_USERKILL} == 0) {
+ print "Note: PT_USERKILL is already disabled ";
+ }
+ print " \n";
+
+ if ($config{SMTP_BLOCK} == 0) {
+ print "Disable SMTP_BLOCK \n";
+ } else {
+ print "Disable SMTP_BLOCK \n";
+ }
+ print "If scripts on the server are unable to send out email via external SMTP connections and you have SMTP_BLOCK enabled then those scripts should be configured to send email either through /usr/sbin/sendmail or localhost on the server. If this is not possible then disabling SMTP_BLOCK can fix this\n";
+ if ($config{SMTP_BLOCK} == 0) {
+ print "Note: SMTP_BLOCK is already disabled ";
+ }
+ print " \n";
+
+ print "Disable All Alerts \n";
+ print "If you really want to disable all alerts in lfd you can do so here. This is not recommended in any situation - you should go through the csf configuration and only disable those you do not want. As new features are added to csf you may find that you have to go into the csf configuration and disable them manually as this procedure only disables the ones that it is aware of when applied\n";
+ print " \n";
+
+ print "Reinstall csf \n";
+ print "If all else fails this option will completely uninstall csf and install it again with completely default options (including TESTING mode). The previous configuration will be lost including all modifications\n";
+ print " \n";
+
+ print "
\n";
+ &printreturn;
+ &confirmmodal;
+ }
+ elsif ($FORM{action} eq "fixpasvftp") {
+ print "\n";
+ print "
Enabling pure-ftpd PASV hole:
\n";
+ print "
";
+ &resize("top");
+ print "
\n";
+ &resize("bot",1);
+ print "\n";
+ print "
\n";
+ print "You MUST now restart both csf and lfd:
\n";
+ print "
\n";
+ &printreturn;
+ }
+ elsif ($FORM{action} eq "fixspi") {
+ print "\n";
+ print "
Disabling LF_SPI:
\n";
+ print "
";
+
+ copy("/etc/csf/csf.conf","/var/lib/csf/backup/".time."_prefixspi");
+ sysopen (my $CSFCONF,"/etc/csf/csf.conf", O_RDWR | O_CREAT);
+ flock ($CSFCONF, LOCK_EX);
+ my @csf = <$CSFCONF>;
+ chomp @csf;
+ seek ($CSFCONF, 0, 0);
+ truncate ($CSFCONF, 0);
+ foreach my $line (@csf) {
+ if ($line =~ /^LF_SPI /) {
+ print $CSFCONF "LF_SPI = \"0\"\n";
+ print "*** LF_SPI disabled ***\n";
+ } else {
+ print $CSFCONF $line."\n";
+ }
+ }
+ close ($CSFCONF);
+
+ print "
\n";
+ print "\n";
+ print "
\n";
+ print "You MUST now restart both csf and lfd:
\n";
+ print "
\n";
+ &printreturn;
+ }
+ elsif ($FORM{action} eq "fixkill") {
+ print "\n";
+ print "
Disabling PT_USERKILL:
\n";
+ print "
";
+
+ copy("/etc/csf/csf.conf","/var/lib/csf/backup/".time."_prefixkill");
+ sysopen (my $CSFCONF,"/etc/csf/csf.conf", O_RDWR | O_CREAT);
+ flock ($CSFCONF, LOCK_EX);
+ my @csf = <$CSFCONF>;
+ chomp @csf;
+ seek ($CSFCONF, 0, 0);
+ truncate ($CSFCONF, 0);
+ foreach my $line (@csf) {
+ if ($line =~ /^PT_USERKILL /) {
+ print $CSFCONF "PT_USERKILL = \"0\"\n";
+ print "*** PT_USERKILL disabled ***\n";
+ } else {
+ print $CSFCONF $line."\n";
+ }
+ }
+ close ($CSFCONF);
+
+ print "
\n";
+ print "\n";
+ print "
\n";
+ print "You MUST now restart both csf and lfd:
\n";
+ print "
\n";
+ &printreturn;
+ }
+ elsif ($FORM{action} eq "fixsmtp") {
+ print "\n";
+ print "
Disabling SMTP_BLOCK:
\n";
+ print "
";
+
+ copy("/etc/csf/csf.conf","/var/lib/csf/backup/".time."_prefixsmtp");
+ sysopen (my $CSFCONF,"/etc/csf/csf.conf", O_RDWR | O_CREAT);
+ flock ($CSFCONF, LOCK_EX);
+ my @csf = <$CSFCONF>;
+ chomp @csf;
+ seek ($CSFCONF, 0, 0);
+ truncate ($CSFCONF, 0);
+ foreach my $line (@csf) {
+ if ($line =~ /^SMTP_BLOCK /) {
+ print $CSFCONF "SMTP_BLOCK = \"0\"\n";
+ print "*** SMTP_BLOCK disabled ***\n";
+ } else {
+ print $CSFCONF $line."\n";
+ }
+ }
+ close ($CSFCONF);
+
+ print "
\n";
+ print "\n";
+ print "
\n";
+ print "You MUST now restart both csf and lfd:
\n";
+ print "
\n";
+ &printreturn;
+ }
+ elsif ($FORM{action} eq "fixalerts") {
+ print "\n";
+ print "
Disabling All Alerts:
\n";
+ print "
";
+
+ &resize("top");
+ print "\n";
+ &resize("bot",1);
+ print "
\n";
+ print "\n";
+ print "
\n";
+ print "You MUST now restart both csf and lfd:
\n";
+ print "
\n";
+ &printreturn;
+ }
+ elsif ($FORM{action} eq "fixnuclear") {
+ print "\n";
+ print "
Nuclear Option:
\n";
+ print "
";
+
+ my $time = time;
+ sysopen (my $REINSTALL, "/usr/src/reinstall_$time.sh", O_WRONLY | O_CREAT | O_TRUNC);
+ flock ($REINSTALL, LOCK_EX);
+ print $REINSTALL <
\n";
+ print "\n";
+ print "
\n";
+ &printreturn;
+ }
+ else {
+ if (defined $ENV{WEBMIN_VAR} and defined $ENV{WEBMIN_CONFIG} and -e "module.info") {
+ my @data = slurp("module.info");
+ foreach my $line (@data) {
+ if ($line =~ /^name=csf$/) {
+ unless (-l "index.cgi") {
+ unlink "index.cgi";
+ my $status = symlink ("/usr/local/csf/lib/webmin/csf/index.cgi","index.cgi");
+ if ($status and -l "index.cgi") {
+ symlink ("/usr/local/csf/lib/webmin/csf/images","csfimages");
+ print "csf updated to symlink webmin module to /usr/local/csf/lib/webmin/csf/. Click here to continue
\n";
+ exit;
+ } else {
+ print "
Failed to symlink to /usr/local/csf/lib/webmin/csf/
\n";
+ }
+ }
+ last;
+ }
+ }
+ }
+
+ &getethdev;
+ my ($childin, $childout);
+ my $pid = open3($childin, $childout, $childout, "$config{IPTABLES} $config{IPTABLESWAIT} -L LOCALINPUT -n");
+ my @iptstatus = <$childout>;
+ waitpid ($pid, 0);
+ chomp @iptstatus;
+ if ($iptstatus[0] =~ /# Warning: iptables-legacy tables present/) {shift @iptstatus}
+ my $status = "
Firewall Status: Enabled and Running ";
+
+ if (-e "/etc/csf/csf.disable") {
+ $status = "
\n"
+ }
+ elsif ($config{TESTING}) {
+ $status = "
";
+ }
+ elsif ($iptstatus[0] !~ /^Chain LOCALINPUT/) {
+ $status = "
"
+ }
+ if (-e "/var/lib/csf/lfd.restart") {$status .= "
lfd restart request pending "}
+ unless ($config{RESTRICT_SYSLOG}) {$status .= "
WARNING: RESTRICT_SYSLOG is disabled. See SECURITY WARNING in Firewall Configuration \n"}
+
+ my $tempcnt = 0;
+ if (! -z "/var/lib/csf/csf.tempban") {
+ sysopen (my $IN, "/var/lib/csf/csf.tempban", O_RDWR);
+ flock ($IN, LOCK_EX);
+ my @data = <$IN>;
+ close ($IN);
+ chomp @data;
+ $tempcnt = scalar @data;
+ }
+ my $tempbans = "(Currently: $tempcnt temp IP bans, ";
+ $tempcnt = 0;
+ if (! -z "/var/lib/csf/csf.tempallow") {
+ sysopen (my $IN, "/var/lib/csf/csf.tempallow", O_RDWR);
+ flock ($IN, LOCK_EX);
+ my @data = <$IN>;
+ close ($IN);
+ chomp @data;
+ $tempcnt = scalar @data;
+ }
+ $tempbans .= "$tempcnt temp IP allows)";
+
+ my $permcnt = 0;
+ if (! -z "/etc/csf/csf.deny") {
+ sysopen (my $IN, "/etc/csf/csf.deny", O_RDWR);
+ flock ($IN, LOCK_SH);
+ while (my $line = <$IN>) {
+ chomp $line;
+ if ($line =~ /^(\#|\n|\r)/) {next}
+ if ($line =~ /$ipv4reg|$ipv6reg/) {$permcnt++}
+ }
+ close ($IN);
+ }
+ my $permbans = "(Currently: $permcnt permanent IP bans)";
+
+ $permcnt = 0;
+ if (! -z "/etc/csf/csf.allow") {
+ sysopen (my $IN, "/etc/csf/csf.allow", O_RDWR);
+ flock ($IN, LOCK_SH);
+ while (my $line = <$IN>) {
+ chomp $line;
+ if ($line =~ /^(\#|\n|\r)/) {next}
+ if ($line =~ /$ipv4reg|$ipv6reg/) {$permcnt++}
+ }
+ close ($IN);
+ }
+ my $permallows = "(Currently: $permcnt permanent IP allows)";
+
+ print $status;
+
+ print "\n";
+ print "
A new version of csf is available ";
+
+ print "
\n";
+ print "All \n";
+ print "Info \n";
+ print "csf \n";
+ print "lfd \n";
+ if ($config{CLUSTER_SENDTO}) {
+ print "Cluster \n";
+ }
+ print "Other \n";
+ print " \n";
+
+ print "
\n";
+ print "
\n";
+ print "
\n";
+
+ print "
\n";
+ if ($upgrade) {print "\n"}
+ print "
\n";
+
+ print "
\n";
+ print "
\n";
+ print "csf - Quick Actions ";
+ print "Quick Allow \n";
+ print "Quick Deny \n";
+ print "Quick Ignore \n";
+ print "Quick Unblock \n";
+ print "
\n";
+
+ print "
\n";
+ print "csf - ConfigServer Firewall ";
+ print "Edit the configuration file for the csf firewall and lfd \n";
+ print "Apply pre-configured csf.conf profiles and backup/restore csf.conf \n";
+ print "Display the active iptables rules \n";
+ print "Search for IP \n";
+ print "Edit csf.allow, the IP address allow file $permallows \n";
+ print "Edit csf.deny, the IP address deny file $permbans \n";
+ print "Enables csf and lfd if previously Disabled \n";
+ print "Completely disables csf and lfd \n";
+ print "Restart the csf iptables firewall \n";
+ print "Have lfd restart the csf iptables firewall \n";
+ print "Temporary Allow/Deny \n";
+ print "View/Remove the temporary IP entries $tempbans \n";
+ print "Deny access to and from specific IP addresses configured on the server (csf.sips) \n";
+ print "Removes and unblocks all entries in csf.deny (excluding those marked \"do not delete\") and all temporary IP entries (blocks and allows) \n";
+ print "Redirect connections to this server to other ports/IP addresses \n";
+ print "Offers solutions to some common problems when using an SPI firewall \n";
+ print "
\n";
+ print "\n";
+ print "
\n";
+
+ print "
\n";
+ print "
\n";
+ print "lfd - Login Failure Daemon ";
+ print "Display lfd status \n";
+ print "Restart lfd \n";
+ print "Edit lfd ignore file \n";
+ print "Edit the Directory File Watching file (csf.dirwatch) - all listed files and directories will be watched for changes by lfd \n";
+ print "Edit the Dynamic DNS file (csf.dyndns) - all listed domains will be resolved and allowed through the firewall \n";
+ print "Edit email alert templates. See Firewall Information for details of each file \n";
+ print "Edit the Log Scanner file (csf.logfiles) - Scan listed log files for log lines and periodically send a report \n";
+ print "Edit the Blocklists configuration file (csf.blocklists) \n";
+ print "Edit the syslog/rsyslog allowed users file (csf.syslogusers) \n";
+ print "
\n";
+ print "
\n";
+
+ if ($config{CLUSTER_SENDTO}) {
+ print "
\n";
+ print "
\n";
+ print "csf - ConfigServer lfd Cluster ";
+
+ print "Ping each member of the cluster (logged in lfd.log) \n";
+ print "Cluster Allow \n";
+ print "Cluster Deny \n";
+ print "Cluster Ignore \n";
+ print "Search the Cluster for IP \n";
+ print "Cluster Temp Allow/Deny \n";
+ print "Cluster Remove Deny \n";
+ print "Cluster Remove Allow \n";
+ print "Cluster Remove Ignore \n";
+
+ if ($config{CLUSTER_CONFIG}) {
+ if ($ips{$config{CLUSTER_MASTER}} or $ipscidr6->find($config{CLUSTER_MASTER}) or ($config{CLUSTER_MASTER} eq $config{CLUSTER_NAT})) {
+ my $options;
+ my %restricted;
+ if ($config{RESTRICT_UI}) {
+ sysopen (my $IN, "/usr/local/csf/lib/restricted.txt", O_RDWR | O_CREAT) or die "Unable to open file: $!";
+ flock ($IN, LOCK_SH);
+ while (my $entry = <$IN>) {
+ chomp $entry;
+ $restricted{$entry} = 1;
+ }
+ close ($IN);
+ }
+ foreach my $key (sort keys %config) {
+ unless ($restricted{$key}) {$options .= "$key "}
+ }
+ print "Cluster Config \n";
+ print "Restart csf and lfd on Cluster members \n";
+ }
+ }
+ print "
\n";
+ print "
\n";
+ }
+
+ print "
\n";
+ if ($config{CF_ENABLE}) {
+ print "
\n";
+ print "CloudFlare Firewall ";
+ print "Access CloudFlare firewall functionality \n";
+ print "Edit the CloudFlare Configuration file (csf.cloudflare) \n";
+ print "
\n";
+ }
+ if ($config{SMTPAUTH_RESTRICT}) {
+ print "
\n";
+ print "cPanel SMTP AUTH Restrictions ";
+ print "Edit the file that allows SMTP AUTH to be advertised to listed IP addresses (csf.smtpauth) \n";
+ print "
\n";
+ }
+
+ if (-e "/usr/local/cpanel/version" or $config{DIRECTADMIN} or $config{INTERWORX}) {
+ my $resellers = "cPanel Resellers";
+ if ($config{DIRECTADMIN}) {$resellers = "DirectAdmin Resellers"}
+ elsif ($config{INTERWORX}) {$resellers = "InterWorx Resellers"}
+ print "
\n";
+ print "$resellers ";
+ print "Privileges can be assigned to $resellers accounts by editing this file (csf.resellers) \n";
+ print "
\n";
+ }
+
+ print "
\n";
+ print "Extra ";
+ print "Check that iptables has the required modules to run csf \n";
+ print "
\n";
+# if ($config{DIRECTADMIN} and !$config{THIS_UI}) {
+# print "
DirectAdmin Main Page\n";
+# }
+ print "
\n
\n";
+
+ if ($config{STYLE_MOBILE}) {
+ if (-e "/usr/local/cpanel/version" and !$config{THIS_UI}) {
+ require Cpanel::Version::Tiny;
+ if ($Cpanel::Version::Tiny::major_version < 65) {
+ print "
cPanel Main Page\n";
+ }
+ }
+ if (defined $ENV{WEBMIN_VAR} and defined $ENV{WEBMIN_CONFIG} and !$config{THIS_UI}) {
+ print "
Webmin Main Page\n";
+ }
+ print "
Shows a subset of functions suitable for viewing on mobile devices
\n";
+ print "
\n";
+
+ print "
\n\n";
+
+ print "
\n";
+
+ if (-e "/usr/local/cpanel/version" and !$config{THIS_UI}) {
+ if ($Cpanel::Version::Tiny::major_version < 65) {
+ print "
cPanel Main Page
\n";
+ }
+ }
+# if ($config{DIRECTADMIN} and !$config{THIS_UI}) {
+# print "
DirectAdmin Main Page
\n";
+# }
+ if (defined $ENV{WEBMIN_VAR} and defined $ENV{WEBMIN_CONFIG} and !$config{THIS_UI}) {
+ print "
Webmin Main Page
\n";
+ }
+
+ print "
Desktop View
\n";
+ print "
\n\n";
+ }
+
+ print "
\n";
+ print "
Development Contribution
";
+ print "
We are very happy to be able to provide this and other products for free. However, it does take time for us to develop and maintain them. If you would like to help with their development by providing a PayPal contribution, please
contact us for details
\n";
+ print "
\n";
+
+ }
+
+ unless ($FORM{action} eq "tailcmd" or $FORM{action} =~ /^cf/ or $FORM{action} eq "logtailcmd" or $FORM{action} eq "loggrepcmd") {
+ print "
\n";
+ print "
csf: v$myv
";
+ print "
©2006-2023, ConfigServer Services (Jonathan Michaelson)
\n";
+ print "
\n";
+ }
+
+ return;
+}
+# end main
+###############################################################################
+# start printcmd
+sub printcmd {
+ my @command = @_;
+
+ my ($childin, $childout);
+ my $pid = open3($childin, $childout, $childout, @command);
+ while (<$childout>) {print $_}
+ waitpid ($pid, 0);
+
+ return;
+}
+# end printcmd
+###############################################################################
+# start getethdev
+sub getethdev {
+ my $ethdev = ConfigServer::GetEthDev->new();
+ my %g_ipv4 = $ethdev->ipv4;
+ my %g_ipv6 = $ethdev->ipv6;
+ foreach my $key (keys %g_ipv4) {
+ $ips{$key} = 1;
+ }
+ if ($config{IPV6}) {
+ foreach my $key (keys %g_ipv6) {
+ eval {
+ local $SIG{__DIE__} = undef;
+ $ipscidr6->add($key);
+ };
+ }
+ }
+
+ return;
+}
+# end getethdev
+###############################################################################
+# start chart
+sub chart {
+ my $img;
+ my $imgdir = "";
+ my $imghddir = "";
+ if (-e "/usr/local/cpanel/version") {
+ $imgdir = "/";
+ $imghddir = "";
+ }
+ elsif (-e "/usr/local/directadmin/conf/directadmin.conf") {
+ $imgdir = "/CMD_PLUGINS_ADMIN/csf/images/";
+ $imghddir = "plugins/csf/images/";
+ umask(0133);
+ }
+ elsif (-e "/usr/local/interworx") {
+ $imgdir = "/configserver/csf/";
+ $imghddir = "/usr/local/interworx/html/configserver/csf/";
+ umask(0133);
+ }
+ elsif (-e "/usr/local/CyberCP/") {
+ $imgdir = "/static/configservercsf/";
+ $imghddir = "/usr/local/CyberCP/public/static/configservercsf/";
+ umask(0133);
+ }
+ if ($config{THIS_UI}) {
+ $imgdir = "$images/";
+ $imghddir = "/etc/csf/ui/images/";
+ }
+
+ my $STATS;
+ if (-e "/var/lib/csf/stats/lfdstats") {
+ sysopen ($STATS,"/var/lib/csf/stats/lfdstats", O_RDWR | O_CREAT);
+ }
+ elsif (-e "/var/lib/csf/stats/lfdmain") {
+ sysopen (my $OLDSTATS,"/var/lib/csf/stats/lfdmain", O_RDWR | O_CREAT);
+ flock ($OLDSTATS, LOCK_EX);
+ my @stats = <$OLDSTATS>;
+ chomp @stats;
+
+ my @newstats;
+ my $cnt = 0;
+ foreach my $line (@stats) {
+ if ($cnt == 55) {push @newstats,""}
+ push @newstats,$line;
+ $cnt++;
+ }
+ sysopen ($STATS,"/var/lib/csf/stats/lfdstats", O_RDWR | O_CREAT);
+ flock ($STATS, LOCK_EX);
+ seek ($STATS, 0, 0);
+ truncate ($STATS, 0);
+ foreach my $line (@newstats) {
+ print $STATS "$line\n";
+ }
+ close ($STATS);
+
+ rename "/var/lib/csf/stats/lfdmain", "/var/lib/csf/stats/lfdmain.".time;
+ close ($OLDSTATS);
+ sysopen ($STATS,"/var/lib/csf/stats/lfdstats", O_RDWR | O_CREAT);
+ } else {
+ sysopen ($STATS,"/var/lib/csf/stats/lfdstats", O_RDWR | O_CREAT);
+ }
+ flock ($STATS, LOCK_SH);
+ my @stats = <$STATS>;
+ chomp @stats;
+ close ($STATS);
+
+ if (@stats) {
+ ConfigServer::ServerStats::charts($config{CC_LOOKUPS},$imghddir);
+ print ConfigServer::ServerStats::charts_html($config{CC_LOOKUPS},$imgdir);
+ } else {
+ print "\n";
+ print "No statistical data has been collected yet
\n";
+ }
+ &printreturn;
+
+ return;
+}
+# end chart
+###############################################################################
+# start systemstats
+sub systemstats {
+ my $type = shift;
+ if ($type eq "") {$type = "load"}
+ my $img;
+ my $imgdir = "";
+ my $imghddir = "";
+ if (-e "/usr/local/cpanel/version") {
+ if (-e "/usr/local/cpanel/bin/register_appconfig") {
+ $imgdir = "csf/";
+ $imghddir = "cgi/configserver/csf/";
+ } else {
+ $imgdir = "/";
+ $imghddir = "";
+ }
+ }
+ elsif (-e "/usr/local/directadmin/conf/directadmin.conf") {
+ $imgdir = "/CMD_PLUGINS_ADMIN/csf/images/";
+ $imghddir = "plugins/csf/images/";
+ umask(0133);
+ }
+ elsif (-e "/usr/local/interworx") {
+ $imgdir = "/configserver/csf/";
+ $imghddir = "/usr/local/interworx/html/configserver/csf/";
+ umask(0133);
+ }
+ elsif (-e "/usr/local/CyberCP/") {
+ $imgdir = "/static/configservercsf/";
+ $imghddir = "/usr/local/CyberCP/public/static/configservercsf/";
+ umask(0133);
+ }
+ if ($config{THIS_UI}) {
+ $imgdir = "$images/";
+ $imghddir = "/etc/csf/ui/images/";
+ }
+ if (defined $ENV{WEBMIN_VAR} and defined $ENV{WEBMIN_CONFIG}) {
+ $imgdir = "/csf/";
+ $imghddir = "";
+ }
+
+ sysopen (my $STATS,"/var/lib/csf/stats/system", O_RDWR | O_CREAT);
+ flock ($STATS, LOCK_SH);
+ my @stats = <$STATS>;
+ chomp @stats;
+ close ($STATS);
+
+ if (@stats > 1) {
+ ConfigServer::ServerStats::graphs($type,$config{ST_SYSTEM_MAXDAYS},$imghddir);
+
+ print "
\n";
+
+ print ConfigServer::ServerStats::graphs_html($imgdir);
+
+ unless ($config{ST_MYSQL} and $config{ST_APACHE}) {
+ print " \n\n";
+ print "You may be able to collect more statistics by enabling ST_MYSQL or ST_APACHE in the csf configuration
\n";
+ }
+ } else {
+ print "\n";
+ print "No statistical data has been collected yet
\n";
+ }
+ &printreturn;
+
+ return;
+}
+# end systemstats
+###############################################################################
+# start editfile
+sub editfile {
+ my $file = shift;
+ my $save = shift;
+ my $extra = shift;
+ my $ace = 0;
+
+ sysopen (my $IN, $file, O_RDWR | O_CREAT) or die "Unable to open file: $!";
+ flock ($IN, LOCK_SH);
+ my @confdata = <$IN>;
+ close ($IN);
+ chomp @confdata;
+
+ if (-e "/usr/local/cpanel/3rdparty/share/ace-editor/optimized/src-min-noconflict/ace.js") {$ace = 1}
+
+ if (-e "/usr/local/cpanel/version" and $ace and !$config{THIS_UI}) {
+ print "\n";
+ print "Edit $file \n";
+ print "Toggle Editor/Textarea \n";
+ print " a \n";
+ print "A
\n";
+ print "\n";
+ print <
+ var myFont = 14;
+ var textarea = \$('#formdata');
+ var editordiv = \$('#editor');
+ var editor = ace.edit("editor");
+ editor.setTheme("ace/theme/tomorrow");
+ editor.setShowPrintMargin(false);
+ editor.setOptions({
+ fontFamily: "Courier New, Courier",
+ fontSize: "14px"
+ });
+ editor.getSession().setMode("ace/mode/space");
+
+ editor.getSession().on('change', function () {
+ textarea.val(editor.getSession().getValue());
+ });
+
+ textarea.on('change', function () {
+ editor.getSession().setValue(textarea.val());
+ });
+
+ editor.getSession().setValue(textarea.val());
+ \$('#textarea').hide();
+ editordiv.show();
+
+ \$("#toggletextarea-btn").on('click', function () {
+ \$('#textarea').toggle();
+ editordiv.toggle();
+ });
+ \$("#fontplus-btn").on('click', function () {
+ myFont++;
+ if (myFont > 20) {myFont = 20}
+ editor.setFontSize(myFont)
+ textarea.css("font-size",myFont+"px");
+ });
+ \$("#fontminus-btn").on('click', function () {
+ myFont--;
+ if (myFont < 12) {myFont = 12}
+ editor.setFontSize(myFont)
+ textarea.css("font-size",myFont+"px");
+ });
+
+EOF
+ } else {
+ if ($config{DIRECTADMIN}) {
+ print "\n\n";
+ } else {
+ print "
\n\n";
+ }
+ print "
Edit $file
\n";
+ print "
\n";
+ print " \n";
+ if ($extra) {print " \n";}
+ print "";
+ foreach my $line (@confdata) {
+ $line =~ s/\\<\;/g;
+ $line =~ s/\>/\>\;/g;
+ print $line."\n";
+ }
+ print "
\n";
+ print "\n";
+ print "
\n";
+ }
+
+ return;
+}
+# end editfile
+###############################################################################
+# start savefile
+sub savefile {
+ my $file = shift;
+ my $restart = shift;
+
+ $FORM{formdata} =~ s/\r//g;
+ if ($FORM{ace} == "1") {
+ if ($FORM{formdata} !~ /^# Do not remove or change this line as it is a safeguard for the UI editor\n/) {
+ print "
UI editor safeguard missing, changes have not been saved.
\n";
+ return;
+ }
+ $FORM{formdata} =~ s/^# Do not remove or change this line as it is a safeguard for the UI editor\n//g;
+ }
+
+ sysopen (my $OUT, $file, O_WRONLY | O_CREAT) or die "Unable to open file: $!";
+ flock ($OUT, LOCK_EX);
+ seek ($OUT, 0, 0);
+ truncate ($OUT, 0);
+ if ($FORM{formdata} !~ /\n$/) {$FORM{formdata} .= "\n"}
+ print $OUT $FORM{formdata};
+ close ($OUT);
+
+ if ($restart eq "csf") {
+ print "
Changes saved. You should restart csf.
\n";
+ print "
\n";
+ }
+ elsif ($restart eq "lfd") {
+ print "
Changes saved. You should restart lfd.
\n";
+ print "
\n";
+ }
+ elsif ($restart eq "both") {
+ print "
Changes saved. You should restart csf and lfd.\n";
+ print "
\n";
+ }
+ else {
+ print "
Changes saved.
\n";
+ }
+
+ return;
+}
+# end cloudflare
+###############################################################################
+# start cloudflare
+sub cloudflare {
+ my $scope = &ConfigServer::CloudFlare::getscope();
+ print "
\n";
+ print "\n";
+ print "\n";
+
+ print "
\n";
+ print "
\n";
+ print "
Note:\n
\n";
+ print "target can be one of:An IP address \n2 letter Country Code \nIP range CIDR \n \n";
+ print "Only Enterprise customers can block a Country Code, but all can allow and challenge \n";
+ print " \nIP range CIDR is limited to /16 and /24 \n";
+ print "\n";
+ &printreturn;
+ return;
+}
+# end cloudflare
+###############################################################################
+# start resize
+sub resize {
+ my $part = shift;
+ my $scroll = shift;
+ if ($part eq "top") {
+ print "
a \n";
+ print "A
\n";
+ } else {
+ print "
+EOF
+ }
+ return;
+}
+# end resize
+###############################################################################
+# start printreturn
+sub printreturn {
+ print "
\n";
+
+ return;
+}
+# end printreturn
+###############################################################################
+# start confirmmodal
+# print "
Submit \n";
+# &confirmmodal;
+sub confirmmodal {
+ print "
\n";
+ print "
\n";
+ print "
\n";
+ print "
\n";
+ print "
text \n";
+ print "\n";
+ print "\n";
+ print "
\n";
+ print "
\n";
+ print "
\n";
+ print "\n";
+ return;
+}
+# end confirmmodal
+###############################################################################
+# start csgetversion
+sub csgetversion {
+ my $product = shift;
+ my $current = shift;
+ my $upgrade = 0;
+ my $newversion;
+ if (-e "/var/lib/configserver/".$product.".txt.error") {
+ open (my $VERSION, "<", "/var/lib/configserver/".$product.".txt.error");
+ flock ($VERSION, LOCK_SH);
+ $newversion = <$VERSION>;
+ close ($VERSION);
+ chomp $newversion;
+ if ($newversion eq "") {
+ $newversion = "Failed to retrieve latest version from ConfigServer";
+ } else {
+ $newversion = "Failed to retrieve latest version from ConfigServer: $newversion";
+ }
+ }
+ elsif (-e "/var/lib/configserver/".$product.".txt") {
+ open (my $VERSION, "<", "/var/lib/configserver/".$product.".txt");
+ flock ($VERSION, LOCK_SH);
+ $newversion = <$VERSION>;
+ close ($VERSION);
+ chomp $newversion;
+ if ($newversion eq "") {
+ $newversion = "Failed to retrieve latest version from ConfigServer";
+ } else {
+ if ($newversion =~ /^[\d\.]*$/) {
+ if ($newversion > $current) {$upgrade = 1} else {$newversion = ""}
+ } else {$newversion = ""}
+ }
+ }
+ elsif (-e "/var/lib/configserver/error") {
+ open (my $VERSION, "<", "/var/lib/configserver/error");
+ flock ($VERSION, LOCK_SH);
+ $newversion = <$VERSION>;
+ close ($VERSION);
+ chomp $newversion;
+ if ($newversion eq "") {
+ $newversion = "Failed to retrieve latest version from ConfigServer";
+ } else {
+ $newversion = "Failed to retrieve latest version from ConfigServer: $newversion";
+ }
+ } else {
+ $newversion = "Failed to retrieve latest version from ConfigServer";
+ }
+ return ($upgrade, $newversion);
+}
+# end csgetversion
+###############################################################################
+# start manualversion
+sub manualversion {
+ my $current = shift;
+ my $upgrade = 0;
+ my $url = "https://$config{DOWNLOADSERVER}/csf/version.txt";
+ if ($config{URLGET} == 1) {$url = "http://$config{DOWNLOADSERVER}/csf/version.txt";}
+ my ($status, $newversion) = $urlget->urlget($url);
+ if (!$status and $newversion ne "" and $newversion =~ /^[\d\.]*$/ and $newversion > $current) {$upgrade = 1} else {$newversion = ""}
+ return ($upgrade, $newversion);
+}
+# end manualversion
+###############################################################################
+
+1;
diff --git a/csf/ConfigServer/GetEthDev.pm b/csf/ConfigServer/GetEthDev.pm
new file mode 100644
index 0000000..6d2f591
--- /dev/null
+++ b/csf/ConfigServer/GetEthDev.pm
@@ -0,0 +1,170 @@
+###############################################################################
+# Copyright (C) 2006-2025 Jonathan Michaelson
+#
+# https://github.com/waytotheweb/scripts
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, see
.
+###############################################################################
+## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
+# start main
+package ConfigServer::GetEthDev;
+
+use strict;
+use lib '/usr/local/csf/lib';
+use Carp;
+use Fcntl qw(:DEFAULT :flock);
+use IPC::Open3;
+use POSIX qw(locale_h);
+use ConfigServer::Config;
+use ConfigServer::CheckIP qw(checkip);
+use ConfigServer::Logger;
+
+use Exporter qw(import);
+our $VERSION = 1.01;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw();
+
+my (%ifaces, %ipv4, %ipv6, %brd);
+
+# end main
+###############################################################################
+# start new
+sub new {
+ my $class = shift;
+ my $self = {};
+ bless $self,$class;
+
+ my $status;
+ my $config = ConfigServer::Config->loadconfig();
+ my %config = $config->config();
+ my $ipv4reg = $config->ipv4reg;
+ my $ipv6reg = $config->ipv6reg;
+ $brd{"255.255.255.255"} = 1;
+ setlocale(LC_ALL, "POSIX");
+
+ if (-e $config{IP}) {
+ my ($childin, $childout);
+ my $pid = open3($childin, $childout, $childout, $config{IP}, "-oneline", "addr");
+ my @ifconfig = <$childout>;
+ waitpid ($pid, 0);
+ chomp @ifconfig;
+
+ foreach my $line (@ifconfig) {
+ if ($line =~ /^\d+:\s+([\w\.\-]+)/ ) {
+ $ifaces{$1} = 1;
+ }
+ if ($line =~ /inet.*?($ipv4reg)/) {
+ my ($ip,undef) = split(/\//,$1);
+ if (checkip(\$ip)) {
+ $ipv4{$ip} = 1;
+ }
+ }
+ if ($line =~ /brd\s+($ipv4reg)/) {
+ my ($ip,undef) = split(/\//,$1);
+ if (checkip(\$ip)) {
+ $brd{$ip} = 1;
+ }
+ }
+ if ($line =~ /inet6.*?($ipv6reg)/) {
+ my ($ip,undef) = split(/\//,$1);
+ $ip .= "/128";
+ if (checkip(\$ip)) {
+ $ipv6{$ip} = 1;
+ }
+ }
+ }
+ $status = 0;
+ }
+ elsif (-e $config{IFCONFIG}) {
+ my ($childin, $childout);
+ my $pid = open3($childin, $childout, $childout, $config{IFCONFIG});
+ my @ifconfig = <$childout>;
+ waitpid ($pid, 0);
+ chomp @ifconfig;
+
+ foreach my $line (@ifconfig) {
+ if ($line =~ /^([\w\.\-]+)/ ) {
+ $ifaces{$1} = 1;
+ }
+ if ($line =~ /inet.*?($ipv4reg)/) {
+ my ($ip,undef) = split(/\//,$1);
+ if (checkip(\$ip)) {
+ $ipv4{$ip} = 1;
+ }
+ }
+ if ($line =~ /Bcast:($ipv4reg)/) {
+ my ($ip,undef) = split(/\//,$1);
+ if (checkip(\$ip)) {
+ $brd{$ip} = 1;
+ }
+ }
+ if ($line =~ /inet6.*?($ipv6reg)/) {
+ my ($ip,undef) = split(/\//,$1);
+ $ip .= "/128";
+ if (checkip(\$ip)) {
+ $ipv6{$ip} = 1;
+ }
+ }
+ }
+ $status = 0;
+ }
+ else {
+ $status = 1;
+ }
+
+ if (-e "/var/cpanel/cpnat") {
+ open (my $NAT, "<", "/var/cpanel/cpnat");
+ flock ($NAT, LOCK_SH);
+ while (my $line = <$NAT>) {
+ chomp $line;
+ if ($line =~ /^(\#|\n|\r)/) {next}
+ my ($internal,$external) = split(/\s+/,$line);
+ if (checkip(\$internal) and checkip(\$external)) {
+ $ipv4{$external} = 1;
+ }
+ }
+ close ($NAT);
+ }
+
+ $self->{status} = $status;
+ return $self;
+}
+# end main
+###############################################################################
+# start ifaces
+sub ifaces {
+ return %ifaces;
+}
+# end ifaces
+###############################################################################
+# start ipv4
+sub ipv4 {
+ return %ipv4;
+}
+# end ipv4
+###############################################################################
+# start ipv6
+sub ipv6 {
+ return %ipv6;
+}
+# end ipv6
+###############################################################################
+# start brd
+sub brd {
+ return %brd;
+}
+# end brd
+###############################################################################
+
+1;
\ No newline at end of file
diff --git a/csf/ConfigServer/GetIPs.pm b/csf/ConfigServer/GetIPs.pm
new file mode 100644
index 0000000..bb57096
--- /dev/null
+++ b/csf/ConfigServer/GetIPs.pm
@@ -0,0 +1,95 @@
+###############################################################################
+# Copyright (C) 2006-2025 Jonathan Michaelson
+#
+# https://github.com/waytotheweb/scripts
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, see .
+###############################################################################
+## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
+# start main
+package ConfigServer::GetIPs;
+
+use strict;
+use lib '/usr/local/csf/lib';
+use Carp;
+use Socket;
+use IPC::Open3;
+use ConfigServer::Config;
+
+use Exporter qw(import);
+our $VERSION = 1.03;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw(getips);
+
+my $config = ConfigServer::Config->loadconfig();
+my %config = $config->config();
+my $ipv4reg = ConfigServer::Config->ipv4reg;
+my $ipv6reg = ConfigServer::Config->ipv6reg;
+
+# end main
+###############################################################################
+# start getips
+sub getips {
+ my $hostname = shift;
+ my @ips;
+
+ if (-e $config{HOST} and -x $config{HOST}) {
+ my $cmdpid;
+ eval {
+ local $SIG{__DIE__} = undef;
+ local $SIG{'ALRM'} = sub {die};
+ alarm(10);
+ my ($childin, $childout);
+ $cmdpid = open3($childin, $childout, $childout, $config{HOST},"-W","5",$hostname);
+ close $childin;
+ my @results = <$childout>;
+ waitpid ($cmdpid, 0);
+ chomp @results;
+
+ foreach my $line (@results) {
+ if ($line =~ /($ipv4reg|$ipv6reg)/) {push @ips, $1}
+ }
+ alarm(0);
+ };
+ alarm(0);
+ if ($cmdpid =~ /\d+/ and $cmdpid > 1 and kill(0,$cmdpid)) {kill(9,$cmdpid)}
+ } else {
+ local $SIG{__DIE__} = undef;
+ eval ('use Socket6;');
+ if ($@) {
+ my @iplist;
+ my (undef, undef, undef, undef, @addrs) = gethostbyname($hostname);
+ foreach (@addrs) {push(@iplist,join(".",unpack("C4", $_)))}
+ push @ips,$_ foreach(@iplist);
+ } else {
+ eval ('
+ use Socket6;
+ my @res = getaddrinfo($hostname, undef, AF_UNSPEC, SOCK_STREAM);
+ while(scalar(@res)>=5){
+ my $saddr;
+ (undef, undef, undef, $saddr, undef, @res) = @res;
+ my ($host, undef) = getnameinfo($saddr,NI_NUMERICHOST | NI_NUMERICSERV);
+ push @ips,$host;
+
+ }
+ ');
+ }
+ }
+
+ return @ips;
+}
+# end getips
+###############################################################################
+
+1;
\ No newline at end of file
diff --git a/csf/ConfigServer/KillSSH.pm b/csf/ConfigServer/KillSSH.pm
new file mode 100644
index 0000000..41bc5ed
--- /dev/null
+++ b/csf/ConfigServer/KillSSH.pm
@@ -0,0 +1,107 @@
+###############################################################################
+# Copyright (C) 2006-2025 Jonathan Michaelson
+#
+# https://github.com/waytotheweb/scripts
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, see .
+###############################################################################
+## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
+# start main
+package ConfigServer::KillSSH;
+
+use strict;
+use lib '/usr/local/csf/lib';
+use Fcntl qw(:DEFAULT :flock);
+use ConfigServer::Logger;
+
+use Exporter qw(import);
+our $VERSION = 1.00;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw();
+
+# end main
+###############################################################################
+# start iplookup
+sub find {
+ my $ip = shift;
+ my $ports = shift;
+
+ my %inodes;
+
+ if ($ports eq "" or $ip eq "") {return}
+
+ foreach my $proto ("tcp","tcp6") {
+ open (my $IN, "<", "/proc/net/$proto");
+ flock ($IN, LOCK_SH);
+ while (<$IN>) {
+ my @rec = split();
+ if ($rec[9] =~ /uid/) {next}
+
+ my ($dip,$dport) = split(/:/,$rec[2]);
+ $dport = hex($dport);
+
+ my ($sip,$sport) = split(/:/,$rec[1]);
+ $sport = hex($sport);
+
+ $dip = &hex2ip($dip);
+ $sip = &hex2ip($sip);
+
+ if ($sip eq '0.0.0.1') {next}
+ if ($dip eq $ip) {
+ foreach my $port (split(/\,/, $ports)) {
+ if ($port eq $sport) {
+ $inodes{$rec[9]} = 1;
+ }
+ }
+ }
+ }
+ close ($IN);
+ }
+
+ opendir (my $PROCDIR, "/proc");
+ while (my $pid = readdir($PROCDIR)) {
+ if ($pid !~ /^\d+$/) {next}
+ opendir (DIR, "/proc/$pid/fd") or next;
+ while (my $file = readdir (DIR)) {
+ if ($file =~ /^\./) {next}
+ my $fd = readlink("/proc/$pid/fd/$file");
+ if ($fd =~ /^socket:\[?([0-9]+)\]?$/) {
+ if ($inodes{$1} and readlink("/proc/$pid/exe") =~ /sshd/) {
+ kill (9,$pid);
+ ConfigServer::Logger::logfile("*PT_SSHDKILL*: Process PID:[$pid] killed for blocked IP:[$ip]");
+ }
+ }
+ }
+ closedir (DIR);
+ }
+ closedir ($PROCDIR);
+ return;
+}
+# end find
+###############################################################################
+## start hex2ip
+sub hex2ip {
+ my $bin = pack "C*" => map hex, $_[0] =~ /../g;
+ my @l = unpack "L*", $bin;
+ if (@l == 4) {
+ return join ':', map { sprintf "%x:%x", $_ >> 16, $_ & 0xffff } @l;
+ }
+ elsif (@l == 1) {
+ return join '.', map { $_ >> 24, ($_ >> 16 ) & 0xff, ($_ >> 8) & 0xff, $_ & 0xff } @l;
+ }
+}
+## end hex2ip
+###############################################################################
+
+1;
\ No newline at end of file
diff --git a/csf/ConfigServer/Logger.pm b/csf/ConfigServer/Logger.pm
new file mode 100644
index 0000000..d275da4
--- /dev/null
+++ b/csf/ConfigServer/Logger.pm
@@ -0,0 +1,83 @@
+###############################################################################
+# Copyright (C) 2006-2025 Jonathan Michaelson
+#
+# https://github.com/waytotheweb/scripts
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, see .
+###############################################################################
+## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
+# start main
+package ConfigServer::Logger;
+
+use strict;
+use lib '/usr/local/csf/lib';
+use Carp;
+use Fcntl qw(:DEFAULT :flock);
+use ConfigServer::Config;
+
+use Exporter qw(import);
+our $VERSION = 1.02;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw(logfile);
+
+my $config = ConfigServer::Config->loadconfig();
+my %config = $config->config();
+my $hostname;
+if (-e "/proc/sys/kernel/hostname") {
+ open (my $IN, "<", "/proc/sys/kernel/hostname");
+ flock ($IN, LOCK_SH);
+ $hostname = <$IN>;
+ chomp $hostname;
+ close ($IN);
+} else {
+ $hostname = "unknown";
+}
+my $hostshort = (split(/\./,$hostname))[0];
+
+my $sys_syslog;
+if ($config{SYSLOG}) {
+ eval('use Sys::Syslog;'); ##no critic
+ unless ($@) {$sys_syslog = 1}
+}
+
+# end main
+###############################################################################
+# start logfile
+sub logfile {
+ my $line = shift;
+ my @ts = split(/\s+/,scalar localtime);
+ if ($ts[2] < 10) {$ts[2] = " ".$ts[2]}
+
+ my $logfile = "/var/log/lfd.log";
+ if ($< != 0) {$logfile = "/var/log/lfd_messenger.log"}
+
+ sysopen (my $LOGFILE, $logfile, O_WRONLY | O_APPEND | O_CREAT);
+ flock ($LOGFILE, LOCK_EX);
+ print $LOGFILE "$ts[1] $ts[2] $ts[3] $hostshort lfd[$$]: $line\n";
+ close ($LOGFILE);
+
+ if ($config{SYSLOG} and $sys_syslog) {
+ eval {
+ local $SIG{__DIE__} = undef;
+ openlog('lfd', 'ndelay,pid', 'user');
+ syslog('info', $line);
+ closelog();
+ }
+ }
+ return;
+}
+# end logfile
+###############################################################################
+
+1;
\ No newline at end of file
diff --git a/csf/ConfigServer/LookUpIP.pm b/csf/ConfigServer/LookUpIP.pm
new file mode 100644
index 0000000..5c293c4
--- /dev/null
+++ b/csf/ConfigServer/LookUpIP.pm
@@ -0,0 +1,439 @@
+###############################################################################
+# Copyright (C) 2006-2025 Jonathan Michaelson
+#
+# https://github.com/waytotheweb/scripts
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, see .
+###############################################################################
+## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
+# start main
+package ConfigServer::LookUpIP;
+
+use strict;
+use lib '/usr/local/csf/lib';
+use Carp;
+use Fcntl qw(:DEFAULT :flock);
+use IPC::Open3;
+use JSON::Tiny;
+use Net::IP;
+use Socket;
+use ConfigServer::CheckIP qw(checkip);
+use ConfigServer::Config;
+use ConfigServer::URLGet;
+
+use Exporter qw(import);
+our $VERSION = 2.00;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw(iplookup);
+
+my $config = ConfigServer::Config->loadconfig();
+my %config = $config->config();
+
+my $urlget;
+if ($config{CC_LOOKUPS} == 4) {
+ $urlget = ConfigServer::URLGet->new($config{URLGET}, "", $config{URLPROXY});
+ unless (defined $urlget) {
+ $config{URLGET} = 1;
+ $urlget = ConfigServer::URLGet->new($config{URLGET}, "", $config{URLPROXY});
+ }
+}
+
+# end main
+###############################################################################
+# start iplookup
+sub iplookup {
+ my $ip = shift;
+ my $cconly = shift;
+ my $host = "-";
+ my $iptype = checkip(\$ip);
+
+ if ($config{LF_LOOKUPS} and !$cconly) {
+ my $dnsip;
+ my $dnsrip;
+ my $dnshost;
+ my $cachehit;
+ open (my $DNS, "<", "/var/lib/csf/csf.dnscache");
+ flock ($DNS, LOCK_SH);
+ while (my $line = <$DNS>) {
+ chomp $line;
+ ($dnsip,$dnsrip,$dnshost) = split(/\|/,$line);
+ if ($ip eq $dnsip) {
+ $cachehit = 1;
+ last;
+ }
+ }
+ close ($DNS);
+ if ($cachehit) {
+ $host = $dnshost;
+ } else {
+ if (-e $config{HOST} and -x $config{HOST}) {
+ my $cmdpid;
+ eval {
+ local $SIG{__DIE__} = undef;
+ local $SIG{'ALRM'} = sub {die};
+ alarm(10);
+ my ($childin, $childout);
+ $cmdpid = open3($childin, $childout, $childout, $config{HOST},"-W","5",$ip);
+ close $childin;
+ my @results = <$childout>;
+ waitpid ($cmdpid, 0);
+ chomp @results;
+ if ($results[0] =~ /(\S+)\.$/) {$host = $1}
+ alarm(0);
+ };
+ alarm(0);
+ if ($cmdpid =~ /\d+/ and $cmdpid > 1 and kill(0,$cmdpid)) {kill(9,$cmdpid)}
+ } else {
+ if ($iptype == 4) {
+ eval {
+ local $SIG{__DIE__} = undef;
+ local $SIG{'ALRM'} = sub {die};
+ alarm(10);
+ my $ipaddr = inet_aton($ip);
+ $host = gethostbyaddr($ipaddr, AF_INET);
+ alarm(0);
+ };
+ alarm(0);
+ }
+ elsif ($iptype == 6) {
+ eval {
+ local $SIG{__DIE__} = undef;
+ local $SIG{'ALRM'} = sub {die};
+ alarm(10);
+ eval('use Socket6;'); ##no critic
+ my $ipaddr = inet_pton(AF_INET6, $ip);
+ $host = gethostbyaddr($ipaddr, AF_INET6);
+ alarm(0);
+ };
+ alarm(0);
+ }
+ }
+ sysopen (DNS, "/var/lib/csf/csf.dnscache", O_WRONLY | O_APPEND | O_CREAT);
+ flock (DNS, LOCK_EX);
+ print DNS "$ip|$ip|$host\n";
+ close (DNS);
+ }
+ if ($host eq "") {$host = "-"}
+ }
+
+ if (($config{CC_LOOKUPS} and $iptype == 4) or ($config{CC_LOOKUPS} and $config{CC6_LOOKUPS} and $iptype == 6)) {
+ my @result;
+ eval {
+ local $SIG{__DIE__} = undef;
+ @result = &geo_binary($ip,$iptype);
+ };
+ my $asn = $result[4];
+ if ($result[0] eq "") {$result[0] = "-"}
+ if ($result[1] eq "") {$result[1] = "-"}
+ if ($result[2] eq "") {$result[2] = "-"}
+ if ($result[3] eq "") {$result[3] = "-"}
+ if ($result[4] eq "") {$result[4] = "-"} else {$result[4] = "[$result[4]]"}
+ if ($config{CC_LOOKUPS} == 3) {
+ if ($cconly) {return ($result[0],$asn)}
+ my $return = "$ip ($result[0]/$result[1]/$result[2]/$result[3]/$host/$result[4])";
+ if ($result[0] eq "-") {$return = "$ip ($host)"}
+ $return =~ s/'|"//g;
+ return $return;
+ }
+ elsif ($config{CC_LOOKUPS} == 2 or $config{CC_LOOKUPS} == 4) {
+ if ($cconly) {return $result[0]}
+ my $return = "$ip ($result[0]/$result[1]/$result[2]/$result[3]/$host)";
+ if ($result[0] eq "-") {$return = "$ip ($host)"}
+ $return =~ s/'|"//g;
+ return $return;
+ }
+ else {
+ if ($cconly) {return $result[0]}
+ my $return = "$ip ($result[0]/$result[1]/$host)";
+ if ($result[0] eq "-") {$return = "$ip ($host)"}
+ $return =~ s/'|"//g;
+ return $return;
+ }
+ }
+
+ if ($config{LF_LOOKUPS}) {
+ if ($host eq "-") {$host = "Unknown"}
+ my $return = "$ip ($host)";
+ $return =~ s/'//g;
+ return $return;
+ } else {
+ return $ip;
+ }
+}
+# end iplookup
+###############################################################################
+# start geo_binary
+sub geo_binary {
+ my $myip = shift;
+ my $ipv = shift;
+ my @return;
+
+ my $netip = Net::IP->new($myip);
+ my $ip = $netip->binip();
+ my $type = $netip->iptype();
+ if ($type eq "PRIVATE") {return}
+
+ if ($config{CC_LOOKUPS} == 4) {
+ my ($status, $text) = $urlget->urlget("http://api.db-ip.com/v2/free/$myip");
+ if ($status) {$text = ""}
+ if ($text ne "") {
+ my $json = JSON::Tiny::decode_json($text);
+ return ($json->{countryCode},$json->{countryName},$json->{stateProv},$json->{city});
+ } else {
+ return;
+ }
+ return;
+ }
+
+ if ($config{CC_SRC} eq "" or $config{CC_SRC} eq "1") {
+ my $file = "/var/lib/csf/Geo/GeoLite2-Country-Blocks-IPv${ipv}.csv";
+ if ($config{CC_LOOKUPS} == 2 or $config{CC_LOOKUPS} == 3) {
+ $file = "/var/lib/csf/Geo/GeoLite2-City-Blocks-IPv${ipv}.csv";
+ }
+ my $start = 0;
+ my $end = -s $file;
+ $end += 4;
+ my $cnt = 0;
+ my $last;
+ my $range;
+ my $geoid;
+ open (my $CSV, "<", $file);
+ flock ($CSV, LOCK_SH);
+ while (1) {
+ my $mid = int (($end + $start) / 2);
+ seek ($CSV, $mid, 0);
+ my $a = <$CSV>;
+ my $b = <$CSV>;
+ chomp $b;
+ ($range,$geoid,undef) = split(/\,/,$b);
+ if ($range !~ /^\d/ or $range eq $last or $range eq "") {return}
+ $last = $range;
+ my $netip = Net::IP->new($range);
+ my $lastip = $netip->last_ip();
+ $lastip = Net::IP::ip_iptobin($lastip,$ipv);
+ my $firstip = $netip->ip();
+ $firstip = Net::IP::ip_iptobin($firstip,$ipv);
+ if (Net::IP::ip_bincomp($ip,'lt',$firstip) == 1) {
+ $end = $mid;
+ }
+ elsif (Net::IP::ip_bincomp($ip,'gt',$lastip) == 1) {
+ $start = $mid;
+ } else {
+ last;
+ }
+ $cnt++;
+ if ($cnt > 200) {return}
+ }
+ close ($CSV);
+
+ if ($geoid > 0) {
+ my $file = "/var/lib/csf/Geo/GeoLite2-Country-Locations-en.csv";
+ if ($config{CC_LOOKUPS} == 2 or $config{CC_LOOKUPS} == 3) {
+ $file = "/var/lib/csf/Geo/GeoLite2-City-Locations-en.csv";
+ }
+ my $start = 0;
+ my $end = -s $file;
+ $end += 4;
+ my $cnt = 0;
+ my $last;
+ open (my $CSV, "<", $file);
+ flock ($CSV, LOCK_SH);
+ while (1) {
+ my $mid = int (($end + $start) / 2);
+ seek ($CSV, $mid, 0);
+ my $a = <$CSV>;
+ my $b = <$CSV>;
+ chomp $b;
+ my @bits = split(/\,/,$b);
+ if ($range !~ /^\d/ or $bits[0] eq $last or $bits[0] eq "") {last}
+ $last = $bits[0];
+ if ($geoid < $bits[0]) {
+ $end = $mid;
+ }
+ elsif ($geoid > $bits[0]) {
+ $start = $mid + 1;
+ } else {
+ $b =~ s/\"//g;
+ my ($geoname_id, $locale_code, $continent_code, $continent_name, $country_iso_code, $country_name, $subdivision_1_iso_code, $subdivision_1_name, $subdivision_2_iso_code, $subdivision_2_name, $city_name, $metro_code, $time_zone) = split(/\,/,$b);
+ my $region = $subdivision_2_name;
+ if ($region eq "" or $region eq $city_name) {$region = $subdivision_1_name}
+ $return[0] = $country_iso_code;
+ $return[1] = $country_name;
+ $return[2] = $region;
+ $return[3] = $city_name;
+ last;
+ }
+ $cnt++;
+ if ($cnt > 200) {return}
+ }
+ close ($CSV);
+ }
+
+ if ($config{CC_LOOKUPS} == 3) {
+ my $file = "/var/lib/csf/Geo/GeoLite2-ASN-Blocks-IPv${ipv}.csv";
+ my $start = 0;
+ my $end = -s $file;
+ $end += 4;
+ my $cnt = 0;
+ my $last;
+ my $range;
+ my $asn;
+ my $asnorg;
+ open (my $CSV, "<", $file);
+ flock ($CSV, LOCK_SH);
+ while (1) {
+ my $mid = int (($end + $start) / 2);
+ seek ($CSV, $mid, 0);
+ my $a = <$CSV>;
+ my $b = <$CSV>;
+ chomp $b;
+ ($range,$asn,$asnorg) = split(/\,/,$b,3);
+ if ($range !~ /^\d/ or $range eq $last or $range eq "") {last}
+ $last = $range;
+ my $netip = Net::IP->new($range);
+ my $lastip = $netip->last_ip();
+ $lastip = Net::IP::ip_iptobin($lastip,$ipv);
+ my $firstip = $netip->ip();
+ $firstip = Net::IP::ip_iptobin($firstip,$ipv);
+ if (Net::IP::ip_bincomp($ip,'lt',$firstip) == 1) {
+ $end = $mid;
+ }
+ elsif (Net::IP::ip_bincomp($ip,'gt',$lastip) == 1) {
+ $start = $mid + 1;
+ } else {
+ $return[4] = "AS$asn $asnorg";
+ last;
+ }
+ $cnt++;
+ if ($cnt > 200) {last}
+ }
+ close ($CSV);
+ }
+ } elsif ($config{CC_SRC} eq "2") {
+ my %country_name;
+ open (my $CC, "<", "/var/lib/csf/Geo/countryInfo.txt");
+ flock ($CC, LOCK_SH);
+ foreach my $line (<$CC>) {
+ if ($line eq "" or $line =~ /^\#/ or $line =~ /^\s/) {next}
+ my ($cc,undef,undef,undef,$country,undef) = split(/\t/, $line);
+ if ($cc ne "" and $country ne "") {$country_name{$cc} = $country}
+ }
+ close ($CC);
+
+ my $file = "/var/lib/csf/Geo/dbip-country-lite.csv";
+ if ($config{CC_LOOKUPS} == 2 or $config{CC_LOOKUPS} == 3) {
+ $file = "/var/lib/csf/Geo/dbip-city-lite.csv";
+ }
+ my $start = 0;
+ my $end = -s $file;
+ $end += 4;
+ my $cnt = 0;
+ my $last;
+ my $range;
+ my $geoid;
+ open (my $CSV, "<", $file);
+ flock ($CSV, LOCK_SH);
+ while (1) {
+ my $mid = int (($end + $start) / 2);
+ seek ($CSV, $mid, 0);
+ my $a = <$CSV>;
+ my $b = <$CSV>;
+ chomp $b;
+ my ($firstip,$lastip,$cc_lookups1,$country_iso_code,$region,$city_name,undef) = split(/\,/,$b);
+ if ($firstip eq $lastip or $firstip eq "") {return}
+ if (checkip(\$firstip) ne $ipv) {
+ if ($ipv eq "6") {
+ $start = $mid;
+ } else {
+ $end = $mid;
+ }
+ } else {
+ my $netfirstip = Net::IP->new($firstip);
+ my $firstip = $netfirstip->binip();
+ my $netlastip = Net::IP->new($lastip);
+ my $lastip = $netlastip->binip();
+ if (Net::IP::ip_bincomp($ip,'lt',$firstip) == 1) {
+ $end = $mid;
+ }
+ elsif (Net::IP::ip_bincomp($ip,'gt',$lastip) == 1) {
+ $start = $mid + 1;
+ } else {
+ if ($config{CC_LOOKUPS} == 1) {$country_iso_code = $cc_lookups1}
+ if ($country_iso_code eq "ZZ") {last}
+ $return[0] = $country_iso_code;
+ $return[1] = $country_name{$country_iso_code};
+ $return[2] = $region;
+ $return[3] = $city_name;
+ last;
+ }
+ }
+ $cnt++;
+ if ($cnt > 200) {return}
+ }
+ close ($CSV);
+
+ if ($config{CC_LOOKUPS} == 3) {
+ my $file = "/var/lib/csf/Geo/ip2asn-combined.tsv";
+ my $start = 0;
+ my $end = -s $file;
+ $end += 4;
+ my $cnt = 0;
+ my $last;
+ my $range;
+ my $asn;
+ my $asnorg;
+ open (my $CSV, "<", $file);
+ flock ($CSV, LOCK_SH);
+ while (1) {
+ my $mid = int (($end + $start) / 2);
+ seek ($CSV, $mid, 0);
+ my $a = <$CSV>;
+ my $b = <$CSV>;
+ chomp $b;
+ my ($firstip,$lastip,$asn,undef,$asnorg) = split(/\t/,$b);
+ if ($firstip eq $lastip or $firstip eq "") {last}
+ if (checkip(\$firstip) ne $ipv) {
+ if ($ipv eq "6") {
+ $start = $mid;
+ } else {
+ $end = $mid;
+ }
+ } else {
+ my $netfirstip = Net::IP->new($firstip);
+ my $firstip = $netfirstip->binip();
+ my $netlastip = Net::IP->new($lastip);
+ my $lastip = $netlastip->binip();
+ if (Net::IP::ip_bincomp($ip,'lt',$firstip) == 1) {
+ $end = $mid;
+ }
+ elsif (Net::IP::ip_bincomp($ip,'gt',$lastip) == 1) {
+ $start = $mid + 1;
+ } else {
+ if ($asn eq "0") {last}
+ $return[4] = "AS$asn $asnorg";
+ last;
+ }
+ }
+ $cnt++;
+ if ($cnt > 200) {last}
+ }
+ close ($CSV);
+ }
+ }
+ return @return;
+}
+# end geo_binary
+###############################################################################
+
+1;
\ No newline at end of file
diff --git a/csf/ConfigServer/Messenger.pm b/csf/ConfigServer/Messenger.pm
new file mode 100644
index 0000000..bb59fd6
--- /dev/null
+++ b/csf/ConfigServer/Messenger.pm
@@ -0,0 +1,1290 @@
+###############################################################################
+# Copyright (C) 2006-2025 Jonathan Michaelson
+#
+# https://github.com/waytotheweb/scripts
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, see .
+###############################################################################
+## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
+# start main
+package ConfigServer::Messenger;
+
+use strict;
+use lib '/usr/local/csf/lib';
+use Fcntl qw(:DEFAULT :flock);
+use File::Copy;
+use JSON::Tiny;
+use IO::Socket::INET;
+use Net::CIDR::Lite;
+use Net::IP;
+use IPC::Open3;
+use ConfigServer::Config;
+use ConfigServer::CheckIP qw(checkip);
+use ConfigServer::Logger qw(logfile);
+use ConfigServer::URLGet;
+use ConfigServer::Slurp qw(slurp);
+use ConfigServer::GetIPs qw(getips);
+use ConfigServer::GetEthDev;
+
+use Exporter qw(import);
+our $VERSION = 3.00;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw();
+
+my $slurpreg = ConfigServer::Slurp->slurpreg;
+my $cleanreg = ConfigServer::Slurp->cleanreg;
+
+my $config = ConfigServer::Config->loadconfig();
+my %config = $config->config();
+my $ipv4reg = ConfigServer::Config->ipv4reg;
+my $ipv6reg = ConfigServer::Config->ipv6reg;
+
+my $childproc;
+my $hostname;
+
+my %ips;
+my $ipscidr6;
+my %sslcerts;
+my %sslkeys;
+my %ssldomains;
+my @ssldomainkeys;
+my $webserver = "apache";
+my $sslhost;
+my $sslcert;
+my $sslkey;
+my $sslca;
+my $osslcert;
+my $osslkey;
+my $osslca;
+my $sslaliases;
+my $litestart = 0;
+my $ssldir = "/var/lib/csf/ssl/";
+my $phphandler;
+my $version = 1;
+my $serverroot;
+
+# end main
+###############################################################################
+# start init
+sub init {
+ my $class = shift;
+ $version = shift;
+ my $self = {};
+ bless $self,$class;
+
+ if (-e "/proc/sys/kernel/hostname") {
+ open (my $IN, "<", "/proc/sys/kernel/hostname");
+ flock ($IN, LOCK_SH);
+ $hostname = <$IN>;
+ chomp $hostname;
+ close ($IN);
+ } else {
+ $hostname = "unknown";
+ }
+ if ($version == 1) {
+ if ($config{MESSENGER6}) {
+ eval('use IO::Socket::INET6;'); ##no critic
+ if ($@) {$config{MESSENGER6} = "0"}
+ }
+ $ipscidr6 = Net::CIDR::Lite->new;
+ &getethdev;
+ foreach my $ip (split(/,/,$config{RECAPTCHA_NAT})) {
+ $ip =~ s/\s*//g;
+ $ips{$ip} = 1;
+ }
+ }
+ elsif ($version == 2) {
+ }
+ elsif ($version == 3) {
+ mkdir $ssldir;
+ mkdir $ssldir."certs/";
+ mkdir $ssldir."keys/";
+ mkdir $ssldir."ca/";
+ }
+
+ return $self;
+}
+# end init
+###############################################################################
+# start start
+sub start {
+ my $self = shift;
+ my $port = shift;
+ my $user = shift;
+ my $type = shift;
+ my $status;
+ my $reason;
+ if ($version == 1) {
+ ($status,$reason) = &messenger($port, $user, $type);
+ }
+ elsif ($version == 2) {
+ ($status,$reason) = &messengerv2();
+ }
+ elsif ($version == 3) {
+ ($status,$reason) = &messengerv3();
+ }
+
+ return ($status,$reason);
+}
+# end start
+###############################################################################
+# start messenger
+sub messenger {
+ my $port = shift;
+ my $user = shift;
+ my $type = shift;
+ my $oldtype = $type;
+ my $server;
+ my %sslcerts;
+ my %sslkeys;
+
+ $SIG{CHLD} = 'IGNORE';
+ $SIG{INT} = \&childcleanup;
+ $SIG{TERM} = \&childcleanup;
+ $SIG{HUP} = \&childcleanup;
+ $SIG{__DIE__} = sub {&childcleanup(@_);};
+ $0 = "lfd $type messenger";
+ $childproc = "Messenger ($type)";
+
+ if ($type eq "HTTPS") {
+ eval {
+ local $SIG{__DIE__} = undef;
+ require IO::Socket::SSL;
+ import IO::Socket::SSL;
+ };
+
+ my $start = 0;
+ my $sslhost;
+ my $sslcert;
+ my $sslkey;
+ my $sslaliases;
+ my %messengerports;
+ foreach my $serverports (split(/\,/,$config{MESSENGER_HTTPS_IN})) {$messengerports{$serverports} = 1}
+ foreach my $file (glob($config{MESSENGER_HTTPS_CONF})) {
+ if (-e $file) {
+ foreach my $line (slurp($file)) {
+ $line =~ s/\'|\"//g;
+ if ($line =~ /^\s*]+>/) {
+ $start = 1;
+ }
+ if ($webserver eq "apache" and $start) {
+ if ($line =~ /\s*ServerName\s+(\w+:\/\/)?([a-zA-Z0-9\.\-]+)(:\d+)?/) {$sslhost = $2}
+ if ($line =~ /\s*ServerAlias\s+(.*)/) {$sslaliases .= " ".$1}
+ if ($line =~ /\s*SSLCertificateFile\s+(\S+)/) {
+ my $match = $1;
+ if (-e $match) {$sslcert = $match}
+ }
+ if ($line =~ /\s*SSLCertificateKeyFile\s+(\S+)/) {
+ my $match = $1;
+ if (-e $match) {$sslkey = $match}
+ }
+ }
+
+ if (($webserver eq "apache" and $line =~ /^\s*<\/VirtualHost\s*>/)) {
+ $start = 0;
+ if ($sslhost ne "" and !checkip($sslhost) and $sslcert ne "") {
+ $sslcerts{$sslhost} = $sslcert;
+ if ($sslkey eq "") {$sslkey = $sslcert}
+ $sslkeys{$sslhost} = $sslkey;
+ foreach my $alias (split(/\s+/,$sslaliases)) {
+ if ($alias eq "") {next}
+ if (checkip($alias)) {next}
+ if ($alias =~ /^[a-zA-Z0-9\.\-]+$/) {
+ if ($config{MESSENGER_HTTPS_SKIPMAIL} and $alias =~ /^mail\./) {next}
+ $sslcerts{$alias} = $sslcert;
+ $sslkeys{$alias} = $sslkey;
+ }
+ }
+ }
+ $sslhost = "";
+ $sslcert = "";
+ $sslkey = "";
+ $sslaliases = "";
+ }
+ }
+ }
+ }
+ if (scalar(keys %sslcerts < 1)) {
+ return (1, "No SSL certs found in MESSENGER_HTTPS_CONF location");
+ }
+ if (-e $config{MESSENGER_HTTPS_KEY}) {
+ $sslkeys{''} = $config{MESSENGER_HTTPS_KEY};
+ }
+ if (-e $config{MESSENGER_HTTPS_CRT}) {
+ $sslcerts{''} = $config{MESSENGER_HTTPS_CRT};
+ }
+ if ($config{DEBUG} >= 1) {
+ foreach my $key (keys %sslcerts) {
+ logfile("SSL: [$key] [$sslcerts{$key}] [$sslkeys{$key}]");
+ }
+ }
+ eval {
+ local $SIG{__DIE__} = undef;
+ if ($config{MESSENGER6}) {
+ $server = IO::Socket::SSL->new(
+ Domain => AF_INET6,
+ LocalPort => $port,
+ Type => SOCK_STREAM,
+ ReuseAddr => 1,
+ Listen => $config{MESSENGER_CHILDREN},
+ SSL_server => 1,
+ SSL_use_cert => 1,
+ SSL_cert_file => \%sslcerts,
+ SSL_key_file => \%sslkeys,
+ ) or &error("MESSENGER: *Error* cannot open server on port $port: ".IO::Socket::SSL->errstr);
+ } else {
+ $server = IO::Socket::SSL->new(
+ Domain => AF_INET,
+ LocalPort => $port,
+ Type => SOCK_STREAM,
+ ReuseAddr => 1,
+ Listen => $config{MESSENGER_CHILDREN},
+ SSL_server => 1,
+ SSL_use_cert => 1,
+ SSL_cert_file => \%sslcerts,
+ SSL_key_file => \%sslkeys,
+ ) or &error("MESSENGER: *Error* cannot open server on port $port: ".IO::Socket::SSL->errstr);
+ }
+ &logfile("Messenger HTTPS Service started for ".scalar(keys %sslcerts)." domains");
+ $type = "HTML";
+ };
+ if ($@) {
+ return (1, $@);
+ }
+ }
+ elsif ($config{MESSENGER6}) {
+ $server = IO::Socket::INET6->new(
+ LocalPort => $port,
+ Type => SOCK_STREAM,
+ ReuseAddr => 1,
+ Listen => $config{MESSENGER_CHILDREN}) or &childcleanup(__LINE__,"*Error* cannot open server on port $port: $!");
+ } else {
+ $server = IO::Socket::INET->new(
+ LocalPort => $port,
+ Type => SOCK_STREAM,
+ ReuseAddr => 1,
+ Listen => $config{MESSENGER_CHILDREN}) or &childcleanup(__LINE__,"*Error* cannot open server on port $port: $!");
+ }
+
+ my $index;
+ if ($type eq "HTML" and $config{RECAPTCHA_SITEKEY} ne "") {$index = "/etc/csf/messenger/index.recaptcha.html"}
+ elsif ($type eq "HTML") {$index = "/etc/csf/messenger/index.html"}
+ else {$index = "/etc/csf/messenger/index.text"}
+ open (my $IN, "<", $index);
+ flock ($IN, LOCK_SH);
+ my @message = <$IN>;
+ close ($IN);
+ chomp @message;
+
+ my %images;
+ if ($type eq "HTML") {
+ opendir (DIR, "/etc/csf/messenger");
+ foreach my $file (readdir(DIR)) {
+ if ($file =~ /\.(gif|png|jpg)$/) {
+ open (my $IN, "<", "/etc/csf/messenger/$file");
+ flock ($IN, LOCK_SH);
+ my @data = <$IN>;
+ close ($IN);
+ chomp @data;
+ foreach my $line (@data) {
+ $images{$file} .= "$line\n";
+ }
+ }
+ }
+ closedir (DIR);
+ }
+ my $chldallow = $config{MESSENGER_CHILDREN};
+
+ if ($oldtype eq "HTTPS") {
+ open (my $STATUS,"<", "/proc/$$/status") or next;
+ flock ($STATUS, LOCK_SH);
+ my @status = <$STATUS>;
+ close ($STATUS);
+ chomp @status;
+ my $vmsize = 0;
+ my $vmrss = 0;
+ foreach my $line (@status) {
+ if ($line =~ /^VmSize:\s+(\d+) kB$/) {$vmsize = $1}
+ if ($line =~ /^VmRSS:\s+(\d+) kB$/) {$vmrss = $1}
+ }
+
+ logfile("lfd $oldtype messenger using $vmrss kB of RSS memory at startup, adding up to $config{MESSENGER_CHILDREN} children = ".(($config{MESSENGER_CHILDREN} + 1) * $vmrss)." kB");
+ logfile("lfd $oldtype messenger using $vmsize kB of VIRT memory at startup, adding up to $config{MESSENGER_CHILDREN} children = ".(($config{MESSENGER_CHILDREN} + 1) * $vmsize)." kB");
+ }
+
+ if ($user ne "") {
+ my (undef,undef,$uid,$gid,undef,undef,undef,$homedir) = getpwnam($user);
+ if (($uid > 0) and ($gid > 0)) {
+ local $( = $gid;
+ local $) = "$gid $gid";
+ local $> = local $< = $uid;
+ if (($) != $gid) or ($> != $uid) or ($( != $gid) or ($< != $uid)) {
+ logfile("MESSENGER_USER unable to drop privileges - stopping $oldtype Messenger");
+ exit;
+ }
+ my %children;
+ while (1) {
+ while (my $client = $server->accept()) {
+ while (scalar (keys %children) >= $chldallow) {
+ sleep 1;
+ foreach my $pid (keys %children) {
+ unless (kill(0,$pid)) {delete $children{$pid}}
+ }
+ $0 = "lfd $oldtype messenger (busy)";
+ }
+ $0 = "lfd $oldtype messenger";
+
+ $SIG{CHLD} = 'IGNORE';
+ my $pid = fork;
+ $children{$pid} = 1;
+ if ($pid == 0) {
+ eval {
+ local $SIG{__DIE__} = undef;
+ local $SIG{'ALRM'} = sub {die};
+ alarm(10);
+ close $server;
+
+ $0 = "lfd $oldtype messenger client";
+
+ binmode $client;
+ $| = 1;
+ my $firstline;
+
+ my $hostaddress = $client->sockhost();
+ my $peeraddress = $client->peerhost();
+ $peeraddress =~ s/^::ffff://;
+ $hostaddress =~ s/^::ffff://;
+
+ if ($type eq "HTML") {
+ while ($firstline !~ /\n$/) {
+ my $char;
+ $client->read($char,1);
+ $firstline .= $char;
+ if ($char eq "") {exit}
+ if (length $firstline > 2048) {last}
+ }
+ chomp $firstline;
+ if ($firstline =~ /\r$/) {chop $firstline}
+ }
+
+ &messengerlog($homedir,"Client connection [$peeraddress] [$firstline]");
+ my $error;
+ my $success;
+ my $failure;
+ if (($type eq "HTML") and ($firstline =~ /^GET \/unblk\?g-recaptcha-response=(\S+)/i)) {
+ my $recv = $1;
+ my $status = 1;
+ my $text;
+ eval {
+ local $SIG{__DIE__} = undef;
+ eval("no lib '/usr/local/csf/lib'");
+ my $urlget = ConfigServer::URLGet->new(2, "", $config{URLPROXY});
+ my $url = "https://www.google.com/recaptcha/api/siteverify?secret=$config{RECAPTCHA_SECRET}&response=$recv";
+ ($status, $text) = $urlget->urlget($url);
+ };
+ if ($status) {
+ &messengerlog($homedir,"*Error*, ReCaptcha ($peeraddress): $text");
+ if ($config{DEBUG} >= 1) {
+ if ($@) {$error .= "Error:".$@}
+ if ($!) {$error .= "Error:".$!}
+ $error .= " Error Status: $status";
+ }
+ $error .= "Unable to verify with Google reCAPTCHA";
+ } else {
+ my $resp = JSON::Tiny::decode_json($text);
+ if ($resp->{success}) {
+ my $ip = $resp->{hostname};
+ unless ($ip =~ /^($ipv4reg|$ipv6reg)$/) {$ip = (getips($ip))[0]}
+ if ($ips{$ip} or $ip eq $hostaddress or $ipscidr6->find($ip)) {
+ sysopen (my $UNBLOCK, "$homedir/unblock.txt", O_WRONLY | O_APPEND | O_CREAT) or $error .= "Unable to write to [$homedir/unblock.txt] (make sure that MESSENGER_USER has a home directory)";
+ flock($UNBLOCK, LOCK_EX);
+ print $UNBLOCK "$peeraddress;$resp->{hostname};$ip\n";
+ close ($UNBLOCK);
+ $success = 1;
+ &messengerlog($homedir,"*Success*, ReCaptcha ($peeraddress): [$resp->{hostname} ($ip)] requested unblock");
+ } else {
+ $error .= "Failed, [$resp->{hostname} ($ip)] does not appear to be hosted on this server.";
+ &messengerlog($homedir,"*Failed*, ReCaptcha ($peeraddress): [$resp->{hostname} ($ip)] does not appear to be hosted on this server");
+ }
+ } else {
+ $failure = 1;
+ my @codes = @{$resp->{'error-codes'}};
+ &messengerlog($homedir,"*Failure*, ReCaptcha ($peeraddress): [$codes[0]]");
+ }
+ }
+ }
+ if (($type eq "HTML") and ($firstline =~ /^GET\s+(\S*\/)?(\S*\.(gif|png|jpg))\s+/i)) {
+ my $type = $3;
+ if ($type eq "jpg") {$type = "jpeg"}
+ print $client "HTTP/1.1 200 OK\r\n";
+ print $client "Content-type: image/$type\r\n";
+ print $client "\r\n";
+ print $client $images{$2};
+ } else {
+ if ($type eq "HTML") {
+ print $client "HTTP/1.1 403 OK\r\n";
+ print $client "Content-type: text/html\r\n";
+ print $client "\r\n";
+ foreach my $line (@message) {
+ if ($line =~ /\[IPADDRESS\]/) {$line =~ s/\[IPADDRESS\]/$peeraddress/}
+ if ($line =~ /\[HOSTNAME\]/) {$line =~ s/\[HOSTNAME\]/$hostname/}
+ if ($line =~ /\[RECAPTCHA_SITEKEY\]/) {$line =~ s/\[RECAPTCHA_SITEKEY\]/$config{RECAPTCHA_SITEKEY}/}
+ if ($line =~ /\[RECAPTCHA_ERROR=\"([^\"]+)\"\]/) {
+ my $text = $1;
+ if ($error ne "") {$line =~ s/\[RECAPTCHA_ERROR=\"([^\"]+)\"\]/$text $error/} else {$line =~ s/\[RECAPTCHA_ERROR=\"([^\"]+)\"\]//}
+ }
+ if ($line =~ /\[RECAPTCHA_SUCCESS=\"([^\"]+)\"\]/) {
+ my $text = $1;
+ if ($success) {$line =~ s/\[RECAPTCHA_SUCCESS=\"([^\"]+)\"\]/$text/} else {$line =~ s/\[RECAPTCHA_SUCCESS=\"([^\"]+)\"\]//}
+ }
+ if ($line =~ /\[RECAPTCHA_FAILURE=\"([^\"]+)\"\]/) {
+ my $text = $1;
+ if ($failure) {$line =~ s/\[RECAPTCHA_FAILURE=\"([^\"]+)\"\]/$text/} else {$line =~ s/\[RECAPTCHA_FAILURE=\"([^\"]+)\"\]//}
+ }
+ print $client "$line\r\n";
+ }
+ print $client "\r\n";
+ } else {
+ foreach my $line (@message) {
+ if ($line =~ /\[IPADDRESS\]/) {$line =~ s/\[IPADDRESS\]/$peeraddress/}
+ if ($line =~ /\[HOSTNAME\]/) {$line =~ s/\[HOSTNAME\]/$hostname/}
+ print $client "$line ";
+ }
+ print $client "\n";
+ }
+ }
+ alarm(0);
+ };
+ shutdown ($client,2);
+ $client->close();
+ alarm(0);
+ exit;
+ }
+ if ($oldtype eq "HTTPS") {
+ $client->close(SSL_no_shutdown => 1);
+ } else {
+ $client->close();
+ }
+ }
+ }
+ } else {
+ logfile("MESSENGER_USER invalid - stopping $oldtype Messenger");
+ }
+ } else {
+ logfile("MESSENGER_USER not set - stopping $oldtype Messenger");
+ }
+ return;
+}
+# end messenger
+###############################################################################
+# start messengerv2
+sub messengerv2 {
+ my (undef,undef,$uid,$gid,undef,undef,undef,$homedir) = getpwnam($config{MESSENGER_USER});
+ if ($homedir eq "" or $homedir eq "/" or $homedir =~ m[/etc/csf]) {
+ return (1, "The home directory for $config{MESSENGER_USER} is not valid [$homedir]");
+ }
+ if (! -e $homedir) {
+ return (1, "The home directory for $config{MESSENGER_USER} does not exist [$homedir]");
+ }
+ system("chmod","711",$homedir);
+ my $public_html = $homedir."/public_html";
+ unless (-e $public_html) {
+ system("mkdir","-p",$public_html);
+ system("chown","$config{MESSENGER_USER}:nobody",$public_html);
+ system("chmod","711",$public_html);
+ }
+ unless (-e $public_html."/.htaccess") {
+ open (my $HTACCESS, ">", $public_html."/.htaccess");
+ flock ($HTACCESS, LOCK_EX);
+ print $HTACCESS "Require all granted\n";
+ print $HTACCESS "DirectoryIndex index.php index.cgi index.html index.htm\n";
+ print $HTACCESS "Options +FollowSymLinks +ExecCGI\n";
+ print $HTACCESS "RewriteEngine On\n";
+ print $HTACCESS "RewriteCond \%{REQUEST_FILENAME} !-f\n";
+ print $HTACCESS "RewriteCond \%{REQUEST_FILENAME} !-d\n";
+ print $HTACCESS "RewriteRule ^ /index.php [L,QSA]\n";
+ system("chown","$config{MESSENGER_USER}:$config{MESSENGER_USER}",$public_html."/.htaccess");
+ system("chmod","644",$public_html."/.htaccess");
+ }
+ unless (-e $public_html."/index.php") {
+ if ($config{RECAPTCHA_SITEKEY}) {
+ system("cp","/etc/csf/messenger/index.recaptcha.php",$public_html."/index.php");
+ } else {
+ system("cp","/etc/csf/messenger/index.php",$public_html."/index.php");
+ }
+ system("chown","$config{MESSENGER_USER}:$config{MESSENGER_USER}",$public_html."/index.php");
+ system("chmod","644",$public_html."/index.php");
+ }
+ unless (-e $homedir."/en.php") {
+ system("cp","/etc/csf/messenger/en.php",$homedir."/en.php");
+ system("chown","$config{MESSENGER_USER}:$config{MESSENGER_USER}",$homedir."/en.php");
+ system("chmod","644",$homedir."/en.php");
+ }
+ open (my $CONF, ">", $homedir."/recaptcha.php");
+ flock ($CONF, LOCK_EX);
+ print $CONF "\n";
+ system("chown","$config{MESSENGER_USER}:$config{MESSENGER_USER}",$homedir."/recaptcha.php");
+ system("chmod","644",$homedir."/recaptcha.php");
+
+
+ open (my $OUT, ">", "/var/lib/csf/csf.conf");
+ flock ($OUT, LOCK_EX);
+
+ if ($config{MESSENGER_HTML_IN} ne "") {
+ print $OUT "Listen 0.0.0.0:$config{MESSENGER_HTML}\n";
+ if ($config{IPV6}) {print $OUT "Listen [::]:$config{MESSENGER_HTML}\n"}
+ print $OUT "\n";
+ print $OUT " ServerName $hostname\n";
+ print $OUT " DocumentRoot $public_html\n";
+ print $OUT " \n";
+ print $OUT " AllowOverride All\n";
+ print $OUT " \n";
+ print $OUT " \n";
+ print $OUT " suPHP_UserGroup $config{MESSENGER_USER} $config{MESSENGER_USER}\n";
+ print $OUT " \n";
+ print $OUT " \n";
+ print $OUT " \n";
+ print $OUT " SuexecUserGroup $config{MESSENGER_USER} $config{MESSENGER_USER}\n";
+ print $OUT " \n";
+ print $OUT " \n";
+ print $OUT " \n";
+ print $OUT " RMode config\n";
+ print $OUT " RUidGid $config{MESSENGER_USER} $config{MESSENGER_USER}\n";
+ print $OUT " \n";
+ print $OUT " \n";
+ print $OUT " AssignUserID $config{MESSENGER_USER} $config{MESSENGER_USER}\n";
+ print $OUT " \n";
+ print $OUT " KeepAlive Off\n";
+ print $OUT " \n";
+ }
+
+ if ($config{MESSENGER_HTTPS_IN} ne "") {
+ my %sslcerts;
+ my %sslkeys;
+ my %ssldomains;
+ my $start = 0;
+ my $sslhost;
+ my $sslcert;
+ my $sslkey;
+ my $sslaliases;
+ my $ssldir = "/var/lib/csf/ssl/";
+ unless (-d $ssldir) {
+ mkdir $ssldir;
+ mkdir $ssldir."certs/";
+ mkdir $ssldir."keys/";
+ }
+ foreach my $file (glob($config{MESSENGER_HTTPS_CONF})) {
+ if (-e $file) {
+ foreach my $line (slurp($file)) {
+ $line =~ s/\'|\"//g;
+ if ($line =~ /^\s*]+>/) {
+ $start = 1;
+ }
+ if ($webserver eq "apache" and $start) {
+ if ($line =~ /\s*ServerName\s+(\w+:\/\/)?([a-zA-Z0-9\.\-]+)(:\d+)?/) {$sslhost = $2}
+ if ($line =~ /\s*ServerAlias\s+(.*)/) {$sslaliases .= " ".$1}
+ if ($line =~ /\s*SSLCertificateFile\s+(\S+)/) {
+ my $match = $1;
+ if (-e $match) {
+ copy($match, $ssldir."certs/".$sslhost."\.crt");
+ $sslcert = $ssldir."certs/".$sslhost."\.crt";
+ }
+ }
+ if ($line =~ /\s*SSLCertificateKeyFile\s+(\S+)/) {
+ my $match = $1;
+ if (-e $match) {
+ copy($match, $ssldir."keys/".$sslhost."\.key");
+ $sslkey = $ssldir."keys/".$sslhost."\.key";
+ }
+ }
+ }
+
+ if (($webserver eq "apache" and $line =~ /^\s*<\/VirtualHost\s*>/)) {
+ $start = 0;
+ if ($sslhost ne "" and !checkip($sslhost) and $sslcert ne "") {
+ $ssldomains{$sslhost}{key} = $sslkey;
+ $ssldomains{$sslhost}{aliases} = $sslaliases;
+ $ssldomains{$sslhost}{cert} = $sslcert;
+ }
+ $sslhost = "";
+ $sslcert = "";
+ $sslkey = "";
+ $sslaliases = "";
+ }
+ }
+ }
+ }
+ if (scalar(keys %ssldomains < 1)) {
+ return (1, "No SSL domains found in MESSENGER_HTTPS_CONF location");
+ }
+
+ print $OUT "Listen 0.0.0.0:$config{MESSENGER_HTTPS}\n";
+ if ($config{IPV6}) {print $OUT "Listen [::]:$config{MESSENGER_HTTPS}\n"}
+ if (-e $config{MESSENGER_HTTPS_KEY}) {
+ print $OUT "\n";
+ print $OUT " ServerName $hostname\n";
+ print $OUT " DocumentRoot $public_html\n";
+ print $OUT " UseCanonicalName Off\n";
+ print $OUT " \n";
+ print $OUT " AllowOverride All\n";
+ print $OUT " \n";
+ print $OUT " \n";
+ print $OUT " suPHP_UserGroup $config{MESSENGER_USER} $config{MESSENGER_USER}\n";
+ print $OUT " \n";
+ print $OUT " \n";
+ print $OUT " \n";
+ print $OUT " SuexecUserGroup $config{MESSENGER_USER} $config{MESSENGER_USER}\n";
+ print $OUT " \n";
+ print $OUT " \n";
+ print $OUT " \n";
+ print $OUT " RMode config\n";
+ print $OUT " RUidGid $config{MESSENGER_USER} $config{MESSENGER_USER}\n";
+ print $OUT " \n";
+ print $OUT " \n";
+ print $OUT " AssignUserID $config{MESSENGER_USER} $config{MESSENGER_USER}\n";
+ print $OUT " \n";
+ print $OUT " SSLEngine on\n";
+ if (-e $config{MESSENGER_HTTPS_KEY}) {
+ copy($config{MESSENGER_HTTPS_KEY}, $ssldir."keys/".$hostname."\.key");
+ print $OUT " SSLCertificateKeyFile ".$ssldir."keys/".$hostname."\.key\n";
+ }
+ if (-e $config{MESSENGER_HTTPS_CRT}) {
+ copy($config{MESSENGER_HTTPS_CRT}, $ssldir."certs/".$hostname."\.crt");
+ print $OUT " SSLCertificateFile ".$ssldir."certs/".$hostname."\.crt\n";
+ }
+ print $OUT " SSLUseStapling off\n";
+ print $OUT " KeepAlive Off\n";
+ print $OUT " \n";
+ }
+ foreach my $key (keys %ssldomains) {
+ if ($key eq "") {next}
+ if ($key =~ /^\s+$/) {next}
+ if (-e $ssldomains{$key}{cert}) {
+ print $OUT "\n";
+ print $OUT " ServerName $key\n";
+ print $OUT " ServerAlias $ssldomains{$key}{aliases}\n";
+ print $OUT " DocumentRoot $public_html\n";
+ print $OUT " UseCanonicalName Off\n";
+ print $OUT " \n";
+ print $OUT " AllowOverride All\n";
+ print $OUT " \n";
+ print $OUT " \n";
+ print $OUT " suPHP_UserGroup $config{MESSENGER_USER} $config{MESSENGER_USER}\n";
+ print $OUT " \n";
+ print $OUT " \n";
+ print $OUT " \n";
+ print $OUT " SuexecUserGroup $config{MESSENGER_USER} $config{MESSENGER_USER}\n";
+ print $OUT " \n";
+ print $OUT " \n";
+ print $OUT " \n";
+ print $OUT " RMode config\n";
+ print $OUT " RUidGid $config{MESSENGER_USER} $config{MESSENGER_USER}\n";
+ print $OUT " \n";
+ print $OUT " \n";
+ print $OUT " AssignUserID $config{MESSENGER_USER} $config{MESSENGER_USER}\n";
+ print $OUT " \n";
+ print $OUT " SSLEngine on\n";
+ if (-e $ssldomains{$key}{cert}) {print $OUT " SSLCertificateFile $ssldomains{$key}{cert}\n"}
+ if (-e $ssldomains{$key}{key}) {print $OUT " SSLCertificateKeyFile $ssldomains{$key}{key}\n"}
+ print $OUT " SSLUseStapling off\n";
+ print $OUT " KeepAlive Off\n";
+ print $OUT " \n";
+ }
+ }
+ }
+ close ($OUT);
+
+ system("cp","-f","/var/lib/csf/csf.conf","/etc/apache2/conf.d/csf.messenger.conf");
+
+ my ($childin, $childout);
+ my $cmdpid = open3($childin, $childout, $childout, "/usr/sbin/apachectl", "configtest");
+ my @data = <$childout>;
+ waitpid ($cmdpid, 0);
+
+ if (-e "/var/lib/csf/apachectl.error") {unlink("/var/lib/csf/apachectl.error")}
+ my $ok = 0;
+ foreach (@data) {
+ if ($_ =~ /^Syntax OK/) {$ok = 1}
+ }
+ if ($ok) {
+ system("/scripts/restartsrv_httpd");
+ logfile("MESSENGERV2: Started Apache MESSENGERV2 service using /etc/apache2/conf.d/csf.messenger.conf");
+ } else {
+ logfile("*MESSENGERV2*: Unable to generate a valid Apache configuration, see /var/lib/csf/apachectl.error");
+ if (-e "/etc/apache2/conf.d/csf.messenger.conf") {unlink("/etc/apache2/conf.d/csf.messenger.conf")}
+ system("/scripts/restartsrv_httpd");
+
+ open (my $ERROR, ">", "/var/lib/csf/apachectl.error");
+ flock ($ERROR, LOCK_EX);
+ foreach (@data) {print $ERROR $_}
+ close ($ERROR);
+ }
+ return;
+}
+# end messengerv2
+###############################################################################
+# start messengerv3
+sub messengerv3 {
+ my (undef,undef,$uid,$gid,undef,undef,undef,$homedir) = getpwnam($config{MESSENGER_USER});
+ if ($homedir eq "" or $homedir eq "/" or $homedir =~ m[/etc/csf]) {
+ return (1, "The home directory for $config{MESSENGER_USER} is not valid [$homedir]");
+ }
+ if (! -e $homedir) {
+ return (1, "The home directory for $config{MESSENGER_USER} does not exist [$homedir]");
+ }
+ my $public_html = $homedir."/public_html";
+ unless (-e $public_html) {
+ system("mkdir","-p",$public_html);
+ system("chown","$config{MESSENGER_USER}:$config{MESSENGERV3GROUP}",$public_html);
+ system("chmod",$config{MESSENGERV3PERMS},$public_html);
+ }
+ unless (-e $public_html."/.htaccess") {
+ open (my $HTACCESS, ">", $public_html."/.htaccess");
+ flock ($HTACCESS, LOCK_EX);
+ print $HTACCESS <", $homedir."/recaptcha.php");
+ flock ($CONF, LOCK_EX);
+ print $CONF "\n";
+ system("chown","$config{MESSENGER_USER}:$config{MESSENGER_USER}",$homedir."/recaptcha.php");
+ system("chmod","644",$homedir."/recaptcha.php");
+
+ if ($config{MESSENGERV3WEBSERVER} eq "apache") {
+ $webserver = "apache";
+ }
+ elsif ($config{MESSENGERV3WEBSERVER} eq "litespeed") {
+ $webserver = "litespeed";
+ }
+
+ open (my $OUT, ">", "/var/lib/csf/csf.conf");
+ flock ($OUT, LOCK_EX);
+
+ if ($config{MESSENGERV3PHPHANDLER} ne "") {
+ $phphandler = $config{MESSENGERV3PHPHANDLER};
+ } else {
+ my $file = "/etc/httpd/conf/extra/httpd-hostname.conf";
+ if (-e $file) {
+ foreach my $line (slurp($file)) {
+ if ($line =~ /^\s*AddHandler\s+.+\s+\.php/) {
+ $phphandler = $line;
+ if ($config{DEBUG} >= 1) {logfile("SSL: PHP Handler found in [$file]")}
+ }
+ }
+ }
+ }
+
+ foreach my $line (slurp("/usr/local/csf/tpl/$webserver.main.txt")) {
+ $line =~ s/\[PORT\]/$config{MESSENGER_HTML}/g;
+ if ($line =~ /Listen \[::\]:/ and !$config{IPV6}) {next}
+ $line =~ s/\[SERVERNAME\]/$hostname/g;
+ $line =~ s/\[DOCUMENTROOT\]/$public_html/g;
+ $line =~ s/\[DIRECTORY\]/$homedir/g;
+ $line =~ s/\[USER\]/$config{MESSENGER_USER}/g;
+ $line =~ s/\[PHPHANDLER\]/$phphandler/g;
+ print $OUT $line."\n";
+ }
+
+ if ($config{MESSENGER_HTML_IN} ne "") {
+ foreach my $line (slurp("/usr/local/csf/tpl/$webserver.http.txt")) {
+ $line =~ s/\[PORT\]/$config{MESSENGER_HTML}/g;
+ if ($line =~ /Listen \[::\]:/ and !$config{IPV6}) {next}
+ $line =~ s/\[SERVERNAME\]/$hostname/g;
+ $line =~ s/\[DOCUMENTROOT\]/$public_html/g;
+ $line =~ s/\[DIRECTORY\]/$homedir/g;
+ $line =~ s/\[USER\]/$config{MESSENGER_USER}/g;
+ $line =~ s/\[PHPHANDLER\]/$phphandler/g;
+ print $OUT $line."\n";
+ }
+ }
+
+ if ($config{MESSENGER_HTTPS_IN} ne "") {
+ if ($webserver eq "litespeed") {
+ if ($config{MESSENGERV3HTTPS_CONF} =~ /(.*\/lsws\/)/) {
+ $serverroot = $1;
+ }
+ }
+ &conftree($config{MESSENGERV3HTTPS_CONF});
+ if ($webserver eq "litespeed") {
+ if ($sslhost ne "" and $osslcert ne "" and $ssldomains{$sslhost}{cert} eq "") {
+ if (-e $osslcert) {
+ $sslcert = $ssldir."certs/".$sslhost."\.crt";
+ copy($osslcert, $ssldir."certs/".$sslhost."\.crt");
+ }
+ if (-e $osslkey) {
+ $sslkey = $ssldir."keys/".$sslhost."\.key";
+ copy($osslkey, $ssldir."keys/".$sslhost."\.key");
+ }
+ if (-e $osslca) {
+ $sslca = $ssldir."ca/".$sslhost."\.ca";
+ copy($osslca, $ssldir."ca/".$sslhost."\.ca");
+ }
+ $sslaliases =~ s/\$VH_NAME/$sslhost/;
+ $ssldomains{$sslhost}{key} = $sslkey;
+ $ssldomains{$sslhost}{aliases} = $sslaliases;
+ $ssldomains{$sslhost}{cert} = $sslcert;
+ $ssldomains{$sslhost}{ca} = $sslca;
+ push @ssldomainkeys, $sslhost;
+
+ $sslhost = "";
+ $sslcert = "";
+ $sslkey = "";
+ $sslca = "";
+ $osslcert = "";
+ $osslkey = "";
+ $osslca = "";
+ $sslaliases = "";
+ }
+ }
+
+ if (scalar(keys %ssldomains < 1)) {
+ return (1, "No SSL domains found in MESSENGERV3HTTPS_CONF location [$config{MESSENGERV3HTTPS_CONF}] for $webserver web server");
+ }
+
+ my @virtualhost;
+ my $start = 0;
+ my $key = $ssldomainkeys[0];
+ foreach my $line (slurp("/usr/local/csf/tpl/$webserver.https.txt")) {
+ if ($line =~ /^\# Virtualhost start/) {$start = 1}
+ if ($start) {
+ if ($line =~ /^\# Virtualhost end/) {$start = 0}
+ push @virtualhost, $line;
+ next;
+ }
+ $line =~ s/\[SSLPORT\]/$config{MESSENGER_HTTPS}/g;
+ if ($line =~ /Listen \[::\]:/ and !$config{IPV6}) {next}
+ $line =~ s/\[SERVERNAME\]/$hostname/g;
+ $line =~ s/\[DOCUMENTROOT\]/$public_html/g;
+ $line =~ s/\[DIRECTORY\]/$homedir/g;
+ $line =~ s/\[USER\]/$config{MESSENGER_USER}/g;
+ $line =~ s/\[PHPHANDLER\]/$phphandler/g;
+ if ($line =~ /[MAPS]/) {
+ my $mapping;
+ foreach my $map (@ssldomainkeys) {
+ if (-e $ssldomains{$map}{cert}) {
+ $mapping .= "map csfssl.${map} ${map}\n\t";
+ }
+ }
+ $line =~ s/\[MAPS\]/$mapping/g;
+ }
+ if ($line =~ /\[SSLCERTIFICATEFILE\]/) {
+ if ( -e $ssldomains{$key}{cert}) {
+ $line =~ s/\[SSLCERTIFICATEFILE\]/$ssldomains{$key}{cert}/g;
+ } else {next}
+ }
+
+ if ($line =~ /\[SSLCERTIFICATEKEYFILE\]/) {
+ if (-e $ssldomains{$key}{key}) {
+ $line =~ s/\[SSLCERTIFICATEKEYFILE\]/$ssldomains{$key}{key}/g;
+ } else {next}
+ }
+
+ if ($line =~ /\[SSLCACERTIFICATEFILE\]/) {
+ if (-e $ssldomains{$key}{ca}) {
+ $line =~ s/\[SSLCACERTIFICATEFILE\]/$ssldomains{$key}{ca}/g;
+ } else {next}
+ }
+
+ print $OUT $line."\n";
+ }
+
+ foreach my $key (@ssldomainkeys) {
+ if ($key eq "") {next}
+ if ($key =~ /^\s+$/) {next}
+ if ($config{DEBUG} >= 1) {logfile("SSL: Processing [$key]")}
+
+ if (-e $ssldomains{$key}{cert}) {
+ foreach (@virtualhost) {
+ my $line = $_;
+ $line =~ s/\[SSLPORT\]/$config{MESSENGER_HTTPS}/g;
+ $line =~ s/\[SERVERNAME\]/$key/g;
+ $line =~ s/\[SERVERALIAS\]/$ssldomains{$key}{aliases}/g;
+ $line =~ s/\[DOCUMENTROOT\]/$public_html/g;
+ $line =~ s/\[DIRECTORY\]/$homedir/g;
+ $line =~ s/\[USER\]/$config{MESSENGER_USER}/g;
+ $line =~ s/\[PHPHANDLER\]/$phphandler/g;
+
+ if ($line =~ /\[SSLCERTIFICATEFILE\]/) {
+ if ( -e $ssldomains{$key}{cert}) {
+ $line =~ s/\[SSLCERTIFICATEFILE\]/$ssldomains{$key}{cert}/g;
+ } else {next}
+ }
+
+ if ($line =~ /\[SSLCERTIFICATEKEYFILE\]/) {
+ if (-e $ssldomains{$key}{key}) {
+ $line =~ s/\[SSLCERTIFICATEKEYFILE\]/$ssldomains{$key}{key}/g;
+ } else {next}
+ }
+
+ if ($line =~ /\[SSLCACERTIFICATEFILE\]/) {
+ if (-e $ssldomains{$key}{ca}) {
+ $line =~ s/\[SSLCACERTIFICATEFILE\]/$ssldomains{$key}{ca}/g;
+ } else {next}
+ }
+
+ print $OUT $line."\n";
+ }
+ }
+ }
+ }
+ close ($OUT);
+
+ my $location;
+ if (-d $config{MESSENGERV3LOCATION}) {
+ system("cp","-f","/var/lib/csf/csf.conf",$config{MESSENGERV3LOCATION}."/csf.messenger.conf");
+ $location = $config{MESSENGERV3LOCATION}."/csf.messenger.conf";
+ }
+ elsif (-f $config{MESSENGERV3LOCATION}) {
+ my @conf = slurp($config{MESSENGERV3LOCATION});
+ unless (grep {$_ =~ m[^Include /var/lib/csf/csf.conf]i} @conf) {
+ sysopen (my $FILE, $config{MESSENGERV3LOCATION}, O_WRONLY | O_APPEND | O_CREAT);
+ flock ($FILE, LOCK_EX);
+ if ($webserver eq "apache") {
+ print $FILE "Include /var/lib/csf/csf.conf\n";
+ }
+ elsif ($webserver eq "litespeed") {
+ print $FILE "include /var/lib/csf/csf.conf\n";
+ }
+ close ($FILE);
+ }
+ $location = $config{MESSENGERV3LOCATION};
+ }
+ else {
+ logfile("MESSENGERV3: [$config{MESSENGERV3LOCATION}] is neither a directory nor a file. You must manually include /var/lib/csf/csf.conf into the $webserver configuration");
+ return;
+ }
+
+ if ($config{MESSENGERV3TEST} ne "") {
+ my ($childin, $childout);
+ my $cmdpid = open3($childin, $childout, $childout, $config{MESSENGERV3TEST});
+ my @data = <$childout>;
+ waitpid ($cmdpid, 0);
+
+ if (-e "/var/lib/csf/messenger.error") {unlink("/var/lib/csf/messenger.error")}
+ my $ok = 0;
+ foreach (@data) {
+ if ($_ =~ /^Syntax OK/) {$ok = 1}
+ }
+ if ($ok) {
+ system($config{MESSENGERV3RESTART});
+ logfile("MESSENGERV3: Restarted $webserver MESSENGERV3 service using $location");
+ } else {
+ open (my $ERROR, ">", "/var/lib/csf/messenger.error");
+ flock ($ERROR, LOCK_EX);
+ foreach (@data) {print $ERROR $_}
+ close ($ERROR);
+
+ if (-d $config{MESSENGERV3LOCATION}) {
+ unlink ($config{MESSENGERV3LOCATION}."/csf.messenger.conf");
+ }
+ elsif (-f $config{MESSENGERV3LOCATION}) {
+ my @conf = slurp($config{MESSENGERV3LOCATION});
+ if (grep {$_ =~ m[^Include /var/lib/csf/csf.conf]i} @conf) {
+ sysopen (my $FILE, $config{MESSENGERV3LOCATION}, O_WRONLY | O_CREAT | O_TRUNC);
+ flock ($FILE, LOCK_EX);
+ foreach my $line (@conf) {
+ $line =~ s/$cleanreg//g;
+ if ($line =~ m[^Include /var/lib/csf/csf.conf]i) {next}
+ print $FILE $line."\n";
+ }
+ close ($FILE);
+ }
+ }
+
+ system($config{MESSENGERV3RESTART});
+
+ logfile("*MESSENGERV3*: Unable to generate a valid $webserver configuration, see /var/lib/csf/messenger.error");
+ }
+ } else {
+ system($config{MESSENGERV3RESTART});
+ logfile("MESSENGERV3: Restarted $webserver MESSENGERV3 service using $location");
+ }
+ return;
+}
+# end messengerv3
+###############################################################################
+# start messengerlog
+sub messengerlog {
+ my $homedir = shift;
+ my $message = shift;
+ if ($config{DEBUG}) {
+ sysopen (my $LOG, "/var/log/lfd_messenger.log", O_WRONLY | O_APPEND | O_CREAT);
+ print $LOG "[$$]: ".$message."\n";
+ close ($LOG);
+ }
+ return;
+}
+# end messengerlog
+###############################################################################
+# start childcleanup
+sub childcleanup {
+ $SIG{INT} = 'IGNORE';
+ $SIG{TERM} = 'IGNORE';
+ $SIG{HUP} = 'IGNORE';
+ my $line = shift;
+ my $message = shift;
+
+ if (($message eq "") and $line) {
+ $message = "Child $childproc: $line";
+ $line = "";
+ }
+
+ $0 = "child - aborting";
+
+ if ($message) {
+ if ($line ne "") {$message .= ", at line $line"}
+ logfile("$message");
+ }
+ exit;
+}
+# end childcleanup
+###############################################################################
+# start getethdev
+sub getethdev {
+ my $ethdev = ConfigServer::GetEthDev->new();
+ my %g_ipv4 = $ethdev->ipv4;
+ my %g_ipv6 = $ethdev->ipv6;
+ foreach my $key (keys %g_ipv4) {
+ my $netip = Net::IP->new($key);
+ my $type = $netip->iptype();
+ if ($type eq "PUBLIC") {$ips{$key} = 1}
+ }
+ if ($config{IPV6}) {
+ foreach my $key (keys %g_ipv6) {
+ if ($key !~ m[::1/128]) {
+ eval {
+ local $SIG{__DIE__} = undef;
+ $ipscidr6->add($key);
+ };
+ }
+ }
+ }
+ return;
+}
+# end getethdev
+###############################################################################
+# start error
+sub error {
+ my $error = shift;
+ logfile($error);
+ exit;
+}
+# end error
+###############################################################################
+# start conftree
+sub conftree {
+ my $fileglob = shift;
+ foreach my $file (glob($fileglob)) {
+ if ($file =~ /csf\.messenger\.conf$/) {next}
+ if ($file =~ /\/var\/lib\/csf\/csf.conf$/) {next}
+ if (-e $file) {
+ if ($config{DEBUG} >= 1) {logfile("SSL: Processing [$file]")}
+ my $start = 0;
+ foreach my $line (slurp($file)) {
+ if ($webserver eq "apache") {
+ $line =~ s/\'|\"//g;
+ if ($line =~ /^\s*ServerRoot\s+\"?(\S+)\"?/) {
+ $serverroot = $1;
+ unless (-d $serverroot) {$serverroot = ""}
+ }
+ if ($serverroot eq "" and -d "/etc/apache2") {$serverroot = "/etc/apache2"}
+ if ($line =~ /^\s*Include\s+(\S+)/) {
+ my $include = $1;
+ if ($include !~ /^\//) {$include = "$serverroot/$include"}
+ if ($config{DEBUG} >= 1) {logfile("SSL: Including [$include]")}
+ &conftree($include);
+ }
+ if ($line =~ /^\s*IncludeOptional\s+(\S+)/) {
+ my $include = $1;
+ if ($include !~ /^\//) {$include = "$serverroot/$include"}
+ if ($config{DEBUG} >= 1) {logfile("SSL: IncludeOptional [$include]")}
+ &conftree($include);
+ }
+ if ($line =~ /^\s*]+>/) {
+ $start = 1;
+ }
+ if ($start) {
+ if ($line =~ /\s*ServerName\s+(\w+:\/\/)?([a-zA-Z0-9\.\-]+)(:\d+)?/) {$sslhost = $2}
+ if ($line =~ /\s*ServerAlias\s+(.*)/) {$sslaliases .= " ".$1}
+ if ($line =~ /\s*SSLCertificateFile\s+(\S+)/) {
+ my $match = $1;
+ if (-e $match) {
+ $osslcert = $match;
+ logfile("SSL: Found [$sslhost] certificate in [$file]");
+ }
+ }
+ if ($line =~ /\s*SSLCertificateKeyFile\s+(\S+)/) {
+ my $match = $1;
+ if (-e $match) {
+ $osslkey = $match;
+ logfile("SSL: Found [$sslhost] key in [$file]");
+ }
+ }
+ if ($line =~ /\s*SSLCACertificateFile\s+(\S+)/) {
+ my $match = $1;
+ if (-e $match) {
+ $osslca = $match;
+ logfile("SSL: Found [$sslhost] ca bundle in [$file]");
+ }
+ }
+ }
+
+ if ($line =~ /^\s*<\/VirtualHost\s*>/) {
+ $start = 0;
+ if ($sslhost ne "" and !checkip($sslhost) and $osslcert ne "") {
+ if (-e $osslcert) {
+ $sslcert = $ssldir."certs/".$sslhost."\.crt";
+ copy($osslcert, $ssldir."certs/".$sslhost."\.crt");
+ }
+ if (-e $osslkey) {
+ $sslkey = $ssldir."keys/".$sslhost."\.key";
+ copy($osslkey, $ssldir."keys/".$sslhost."\.key");
+ }
+ if (-e $osslca) {
+ $sslca = $ssldir."ca/".$sslhost."\.ca";
+ copy($osslca, $ssldir."ca/".$sslhost."\.ca");
+ }
+ $ssldomains{$sslhost}{key} = $sslkey;
+ $ssldomains{$sslhost}{aliases} = $sslaliases;
+ $ssldomains{$sslhost}{cert} = $sslcert;
+ $ssldomains{$sslhost}{ca} = $sslca;
+ push @ssldomainkeys, $sslhost;
+ if ($config{DEBUG} >= 1) {logfile("SSL: Found [$sslhost] in [$file]")}
+ }
+ $sslhost = "";
+ $sslcert = "";
+ $sslkey = "";
+ $sslca = "";
+ $osslcert = "";
+ $osslkey = "";
+ $osslca = "";
+ $sslaliases = "";
+ }
+ }
+ elsif ($webserver eq "litespeed") {
+ $line =~ s/\'|\"//g;
+ if ($line =~ /^\s*include\s+(\S+)/) {
+ my $include = $1;
+ $include =~ s/\$SERVER_ROOT/$serverroot/;
+ $include =~ s/\$VH_NAME/$sslhost/;
+ if ($include !~ /^\//) {$include = "$serverroot/$include"}
+ if ($config{DEBUG} >= 1) {logfile("SSL: include [$include]")}
+ &conftree($include);
+ }
+ if ($line =~ /^\s*configFile\s+(\S+)/) {
+ my $include = $1;
+ $include =~ s/\$SERVER_ROOT/$serverroot/;
+ $include =~ s/\$VH_NAME/$sslhost/;
+ if ($include !~ /^\//) {$include = "$serverroot/$include"}
+ if ($config{DEBUG} >= 1) {logfile("SSL: configFile [$include]")}
+ &conftree($include);
+ }
+ if ($line =~ /^\s*virtualHost\s+([^\{]+)\s+\{/) {
+ my $newsslhost = $1;
+ if ($newsslhost ne "" and $config{DEBUG} >= 1) {logfile("SSL: Found [$newsslhost] in [$file]")}
+ if ($litestart == 1) {
+ if ($sslhost ne "" and $osslcert ne "") {
+ if (-e $osslcert) {
+ $sslcert = $ssldir."certs/".$sslhost."\.crt";
+ copy($osslcert, $ssldir."certs/".$sslhost."\.crt");
+ }
+ if (-e $osslkey) {
+ $sslkey = $ssldir."keys/".$sslhost."\.key";
+ copy($osslkey, $ssldir."keys/".$sslhost."\.key");
+ }
+ if (-e $osslca) {
+ $sslca = $ssldir."ca/".$sslhost."\.ca";
+ copy($osslca, $ssldir."ca/".$sslhost."\.ca");
+ }
+ $sslaliases =~ s/\$VH_NAME/$sslhost/;
+ $ssldomains{$sslhost}{key} = $sslkey;
+ $ssldomains{$sslhost}{aliases} = $sslaliases;
+ $ssldomains{$sslhost}{cert} = $sslcert;
+ $ssldomains{$sslhost}{ca} = $sslca;
+ push @ssldomainkeys, $sslhost;
+
+ $sslhost = "";
+ $sslcert = "";
+ $sslkey = "";
+ $sslca = "";
+ $osslcert = "";
+ $osslkey = "";
+ $osslca = "";
+ $sslaliases = "";
+ }
+ }
+ $litestart = 1;
+ $sslhost = $newsslhost;
+ }
+ if ($litestart) {
+ if ($line =~ /\s*vhDomain\s+(\w+:\/\/)?([a-zA-Z0-9\.\-]+)(:\d+)?/) {$sslhost = $2}
+ if ($line =~ /\s*vhAliases\s+(.*)/) {$sslaliases .= " ".$1}
+ if ($line =~ /\s*certFile\s+(\S+)/) {
+ my $match = $1;
+ if (-e $match) {
+ $osslcert = $match;
+ logfile("SSL: Found [$sslhost] certificate in [$file]");
+ }
+ }
+ if ($line =~ /\s*keyFile\s+(\S+)/) {
+ my $match = $1;
+ if (-e $match) {
+ $osslkey = $match;
+ logfile("SSL: Found [$sslhost] key in [$file]");
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return;
+}
+# end conftree
+###############################################################################
+
+1;
diff --git a/csf/ConfigServer/Ports.pm b/csf/ConfigServer/Ports.pm
new file mode 100644
index 0000000..2bacaaf
--- /dev/null
+++ b/csf/ConfigServer/Ports.pm
@@ -0,0 +1,226 @@
+###############################################################################
+# Copyright (C) 2006-2025 Jonathan Michaelson
+#
+# https://github.com/waytotheweb/scripts
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, see .
+###############################################################################
+## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
+# start main
+package ConfigServer::Ports;
+
+use strict;
+use lib '/usr/local/csf/lib';
+use Fcntl qw(:DEFAULT :flock);
+use ConfigServer::Config;
+
+use Exporter qw(import);
+our $VERSION = 1.02;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw();
+
+my %printable = ( ( map { chr($_), unpack('H2', chr($_)) } (0..255) ), "\\"=>'\\', "\r"=>'r', "\n"=>'n', "\t"=>'t', "\""=>'"' ); ##no critic
+my %tcpstates = ("01" => "ESTABLISHED",
+ "02" => "SYN_SENT",
+ "03" => "SYN_RECV",
+ "04" => "FIN_WAIT1",
+ "05" => "FIN_WAIT2",
+ "06" => "TIME_WAIT",
+ "07" => "CLOSE",
+ "08" => "CLOSE_WAIT",
+ "09" => "LAST_ACK",
+ "0A" => "LISTEN",
+ "0B" => "CLOSING");
+# end main
+###############################################################################
+# start listening
+sub listening {
+ my %net;
+ my %conn;
+ my %listen;
+
+ foreach my $proto ("tcp","udp","tcp6","udp6") {
+ open (my $IN, "<","/proc/net/$proto");
+ flock ($IN, LOCK_SH);
+ while (<$IN>) {
+ my @rec = split();
+ if ($rec[9] =~ /uid/) {next}
+
+ my ($dip,$dport) = split(/:/,$rec[1]);
+ $dport = hex($dport);
+
+ my ($sip,$sport) = split(/:/,$rec[2]);
+ $sport = hex($sport);
+
+ $dip = &hex2ip($dip);
+ $sip = &hex2ip($sip);
+
+ my $inode = $rec[9];
+ my $state = $tcpstates{$rec[3]};
+ my $protocol = $proto;
+ $protocol =~ s/6//;
+ if ($protocol eq "udp" and $state eq "CLOSE") {$state = "LISTEN"}
+
+ if ($state eq "ESTABLISHED") {$conn{$dport}{$protocol}++}
+
+ if ($dip =~ /^127\./) {next}
+ if ($dip =~ /^0\.0\.0\.1/) {next}
+ if ($state eq "LISTEN") {$net{$inode}{$protocol} = $dport}
+ }
+ close ($IN);
+ }
+
+ opendir (PROCDIR, "/proc");
+ while (my $pid = readdir(PROCDIR)) {
+ if ($pid !~ /^\d+$/) {next}
+ my $exe = readlink("/proc/$pid/exe") || "";
+ my $cwd = readlink("/proc/$pid/cwd") || "";
+ my $uid;
+ my $user;
+
+ if (defined $exe) {$exe =~ s/([\r\n\t\"\\\x00-\x1f\x7F-\xFF])/\\$printable{$1}/sg}
+ open (my $CMDLINE,"<","/proc/$pid/cmdline");
+ flock ($CMDLINE, LOCK_SH);
+ my $cmdline = <$CMDLINE>;
+ close ($CMDLINE);
+ if (defined $cmdline) {
+ chomp $cmdline;
+ $cmdline =~ s/\0$//g;
+ $cmdline =~ s/\0/ /g;
+ $cmdline =~ s/([\r\n\t\"\\\x00-\x1f\x7F-\xFF])/\\$printable{$1}/sg;
+ $cmdline =~ s/\s+$//;
+ $cmdline =~ s/^\s+//;
+ }
+ if ($exe eq "") {next}
+ my @fd;
+ opendir (DIR, "/proc/$pid/fd") or next;
+ while (my $file = readdir (DIR)) {
+ if ($file =~ /^\./) {next}
+ push (@fd, readlink("/proc/$pid/fd/$file"));
+ }
+ closedir (DIR);
+ open (my $STATUS,"<", "/proc/$pid/status") or next;
+ flock ($STATUS, LOCK_SH);
+ my @status = <$STATUS>;
+ close ($STATUS);
+ chomp @status;
+ foreach my $line (@status) {
+ if ($line =~ /^Uid:(.*)/) {
+ my $uidline = $1;
+ my @uids;
+ foreach my $bit (split(/\s/,$uidline)) {
+ if ($bit =~ /^(\d*)$/) {push @uids, $1}
+ }
+ $uid = $uids[-1];
+ $user = getpwuid($uid);
+ if ($user eq "") {$user = $uid}
+ }
+ }
+
+ my $files;
+ my $sockets;
+ foreach my $file (@fd) {
+ if ($file =~ /^socket:\[?([0-9]+)\]?$/) {
+ my $ino = $1;
+ if ($net{$ino}) {
+ foreach my $protocol (keys %{$net{$ino}}) {
+ $listen{$protocol}{$net{$ino}{$protocol}}{$pid}{user} = $user;
+ $listen{$protocol}{$net{$ino}{$protocol}}{$pid}{exe} = $exe;
+ $listen{$protocol}{$net{$ino}{$protocol}}{$pid}{cmd} = $cmdline;
+ $listen{$protocol}{$net{$ino}{$protocol}}{$pid}{cmd} = $cmdline;
+ $listen{$protocol}{$net{$ino}{$protocol}}{$pid}{conn} = $conn{$net{$ino}{$protocol}}{$protocol} | "-";
+ }
+ }
+ }
+ }
+
+ }
+ closedir (PROCDIR);
+ return %listen;
+}
+# end listening
+###############################################################################
+# start openports
+sub openports {
+ my $config = ConfigServer::Config->loadconfig();
+ my %config = $config->config();
+ my %ports;
+
+ $config{TCP_IN} =~ s/\s//g;
+ foreach my $entry (split(/,/,$config{TCP_IN})) {
+ if ($entry =~ /^(\d+):(\d+)$/) {
+ my $from = $1;
+ my $to = $2;
+ for (my $port = $from; $port < $to ; $port++) {
+ $ports{tcp}{$port} = 1;
+ }
+ } else {
+ $ports{tcp}{$entry} = 1;
+ }
+ }
+ $config{TCP6_IN} =~ s/\s//g;
+ foreach my $entry (split(/,/,$config{TCP6_IN})) {
+ if ($entry =~ /^(\d+):(\d+)$/) {
+ my $from = $1;
+ my $to = $2;
+ for (my $port = $from; $port < $to ; $port++) {
+ $ports{tcp6}{$port} = 1;
+ }
+ } else {
+ $ports{tcp6}{$entry} = 1;
+ }
+ }
+ $config{UDP_IN} =~ s/\s//g;
+ foreach my $entry (split(/,/,$config{UDP_IN})) {
+ if ($entry =~ /^(\d+):(\d+)$/) {
+ my $from = $1;
+ my $to = $2;
+ for (my $port = $from; $port < $to ; $port++) {
+ $ports{udp}{$port} = 1;
+ }
+ } else {
+ $ports{udp}{$entry} = 1;
+ }
+ }
+ $config{UDP6_IN} =~ s/\s//g;
+ foreach my $entry (split(/,/,$config{UDP6_IN})) {
+ if ($entry =~ /^(\d+):(\d+)$/) {
+ my $from = $1;
+ my $to = $2;
+ for (my $port = $from; $port < $to ; $port++) {
+ $ports{udp6}{$port} = 1;
+ }
+ } else {
+ $ports{udp6}{$entry} = 1;
+ }
+ }
+ return %ports;
+}
+# end openports
+###############################################################################
+## start hex2ip
+sub hex2ip {
+ my $bin = pack "C*" => map hex, $_[0] =~ /../g;
+ my @l = unpack "L*", $bin;
+ if (@l == 4) {
+ return join ':', map { sprintf "%x:%x", $_ >> 16, $_ & 0xffff } @l;
+ }
+ elsif (@l == 1) {
+ return join '.', map { $_ >> 24, ($_ >> 16 ) & 0xff, ($_ >> 8) & 0xff, $_ & 0xff } @l;
+ }
+}
+## end hex2ip
+###############################################################################
+
+1;
\ No newline at end of file
diff --git a/csf/ConfigServer/RBLCheck.pm b/csf/ConfigServer/RBLCheck.pm
new file mode 100644
index 0000000..6034f88
--- /dev/null
+++ b/csf/ConfigServer/RBLCheck.pm
@@ -0,0 +1,255 @@
+###############################################################################
+# Copyright (C) 2006-2025 Jonathan Michaelson
+#
+# https://github.com/waytotheweb/scripts
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, see .
+###############################################################################
+## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
+# start main
+package ConfigServer::RBLCheck;
+
+use strict;
+use lib '/usr/local/csf/lib';
+use Fcntl qw(:DEFAULT :flock);
+use ConfigServer::Config;
+use ConfigServer::CheckIP qw(checkip);
+use ConfigServer::Slurp qw(slurp);
+use ConfigServer::GetIPs qw(getips);
+use ConfigServer::RBLLookup qw(rbllookup);
+use IPC::Open3;
+use Net::IP;
+use ConfigServer::GetEthDev;
+
+use Exporter qw(import);
+our $VERSION = 1.01;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw();
+
+my ($ui, $failures, $verbose, $cleanreg, $status, %ips, $images, %config,
+ $ipresult, $output);
+
+my $ipv4reg = ConfigServer::Config->ipv4reg;
+my $ipv6reg = ConfigServer::Config->ipv6reg;
+
+# end main
+###############################################################################
+# start report
+sub report {
+ $verbose = shift;
+ $images = shift;
+ $ui = shift;
+ my $config = ConfigServer::Config->loadconfig();
+ %config = $config->config();
+ $cleanreg = ConfigServer::Slurp->cleanreg;
+ $failures = 0;
+
+ $| = 1;
+
+ &startoutput;
+
+ &getethdev;
+
+ my @RBLS = slurp("/usr/local/csf/lib/csf.rbls");
+
+ if (-e "/etc/csf/csf.rblconf") {
+ my @entries = slurp("/etc/csf/csf.rblconf");
+ foreach my $line (@entries) {
+ if ($line =~ /^Include\s*(.*)$/) {
+ my @incfile = slurp($1);
+ push @entries,@incfile;
+ }
+ }
+ foreach my $line (@entries) {
+ $line =~ s/$cleanreg//g;
+ if ($line eq "") {next}
+ if ($line =~ /^\s*\#|Include/) {next}
+ if ($line =~ /^enablerbl:(.*)$/) {
+ push @RBLS, $1;
+ }
+ elsif ($line =~ /^disablerbl:(.*)$/) {
+ my $hit = $1;
+ for (0..@RBLS) {
+ my $x = $_;
+ my ($rbl,$rblurl) = split(/:/,$RBLS[$x],2);
+ if ($rbl eq $hit) {$RBLS[$x] = ""}
+ }
+ }
+ if ($line =~ /^enableip:(.*)$/) {
+ if (checkip(\$1)) {$ips{$1} = 1}
+ }
+ elsif ($line =~ /^disableip:(.*)$/) {
+ if (checkip(\$1)) {delete $ips{$1}}
+ }
+ }
+ }
+ @RBLS = sort @RBLS;
+
+ foreach my $ip (sort keys %ips) {
+ my $netip = Net::IP->new($ip);
+ my $type = $netip->iptype();
+ if ($type eq "PUBLIC") {
+
+ if ($verbose and -e "/var/lib/csf/${ip}.rbls") {
+ unlink "/var/lib/csf/${ip}.rbls";
+ }
+
+ if (-e "/var/lib/csf/${ip}.rbls") {
+ my $text = join("\n",slurp("/var/lib/csf/${ip}.rbls"));
+ if ($ui) {print $text} else {$output .= $text}
+ } else {
+ if ($verbose) {
+ $ipresult = "";
+ my $hits = 0;
+ &addtitle("Checked $ip ($type) on ".localtime());
+
+ foreach my $line (@RBLS) {
+ my ($rbl,$rblurl) = split(/:/,$line,2);
+ if ($rbl eq "") {next}
+
+ my ($rblhit,$rbltxt) = rbllookup($ip,$rbl);
+ my @tmptxt = $rbltxt;
+ $rbltxt = "";
+ foreach my $line (@tmptxt) {
+ $line =~ s/(http(\S+))/$1<\/a>/g;
+ $rbltxt .= "${line}\n";
+ }
+ $rbltxt =~ s/\n/ \n/g;
+
+ if ($rblhit eq "timeout") {
+ &addline(0,$rbl,$rblurl,"TIMEOUT");
+ }
+ elsif ($rblhit eq "") {
+ if ($verbose == 2) {
+ &addline(0,$rbl,$rblurl,"OK");
+ }
+ }
+ else {
+ &addline(1,$rbl,$rblurl,$rbltxt);
+ $hits++;
+ }
+ }
+ unless ($hits) {
+ my $text;
+ $text .= "OK
\n";
+ if ($ui) {print $text} else {$output .= $text}
+ $ipresult .= $text;
+ }
+ sysopen (my $OUT, "/var/lib/csf/${ip}.rbls", O_WRONLY | O_CREAT);
+ flock($OUT, LOCK_EX);
+ print $OUT $ipresult;
+ close ($OUT);
+ } else {
+ &addtitle("New $ip ($type)");
+ my $text;
+ $text .= "Not Checked
\n";
+ if ($ui) {print $text} else {$output .= $text}
+ }
+ }
+ } else {
+ if ($verbose == 2) {
+ &addtitle("Skipping $ip ($type)");
+ my $text;
+ $text .= "OK
\n";
+ if ($ui) {print $text} else {$output .= $text}
+ }
+ }
+ }
+ &endoutput;
+
+ return ($failures,$output);
+}
+# end report
+###############################################################################
+# start startoutput
+sub startoutput {
+ return;
+}
+# end startoutput
+###############################################################################
+# start addline
+sub addline {
+ my $status = shift;
+ my $rbl = shift;
+ my $rblurl = shift;
+ my $comment = shift;
+ my $text;
+ my $check = $rbl;
+ if ($rblurl ne "") {$check = " $rbl "}
+
+ if ($status) {
+ $text .= "\n";
+ $text .= "
$check
\n";
+ $text .= "
$comment
\n";
+ $text .= "
\n";
+ $failures ++;
+ $ipresult .= $text;
+ }
+ elsif ($verbose) {
+ $text .= "\n";
+ $text .= "
$check
\n";
+ $text .= "
$comment
\n";
+ $text .= "
\n";
+ }
+ if ($ui) {print $text} else {$output .= $text}
+
+ return;
+}
+# end addline
+###############################################################################
+# start addtitle
+sub addtitle {
+ my $title = shift;
+ my $text;
+
+ $text .= "$title
\n";
+
+ $ipresult .= $text;
+ if ($ui) {print $text} else {$output .= $text}
+
+ return;
+}
+# end addtitle
+###############################################################################
+# start endoutput
+sub endoutput {
+ if ($ui) {print " \n"} else {$output .= " \n"}
+
+ return;
+}
+# end endoutput
+###############################################################################
+# start getethdev
+sub getethdev {
+ my $ethdev = ConfigServer::GetEthDev->new();
+ my %g_ipv4 = $ethdev->ipv4;
+ my %g_ipv6 = $ethdev->ipv6;
+ foreach my $key (keys %g_ipv4) {
+ $ips{$key} = 1;
+ }
+# if ($config{IPV6}) {
+# foreach my $key (keys %g_ipv6) {
+# eval {
+# local $SIG{__DIE__} = undef;
+# $ipscidr6->add($key);
+# };
+# }
+# }
+
+ return;
+}
+# end getethdev
+###############################################################################
+
+1;
diff --git a/csf/ConfigServer/RBLLookup.pm b/csf/ConfigServer/RBLLookup.pm
new file mode 100644
index 0000000..16de33f
--- /dev/null
+++ b/csf/ConfigServer/RBLLookup.pm
@@ -0,0 +1,116 @@
+###############################################################################
+# Copyright (C) 2006-2025 Jonathan Michaelson
+#
+# https://github.com/waytotheweb/scripts
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, see .
+###############################################################################
+## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
+# start main
+package ConfigServer::RBLLookup;
+
+use strict;
+use lib '/usr/local/csf/lib';
+use Fcntl qw(:DEFAULT :flock);
+use IPC::Open3;
+use Net::IP;
+use ConfigServer::Config;
+use ConfigServer::CheckIP qw(checkip);
+
+use Exporter qw(import);
+our $VERSION = 1.01;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw(rbllookup);
+
+my $config = ConfigServer::Config->loadconfig();
+my %config = $config->config();
+my $ipv4reg = ConfigServer::Config->ipv4reg;
+my $ipv6reg = ConfigServer::Config->ipv6reg;
+
+# end main
+###############################################################################
+# start rbllookup
+sub rbllookup {
+ my $ip = shift;
+ my $rbl = shift;
+ my %rblhits;
+ my $netip;
+ my $reversed_ip;
+ my $timeout = 4;
+ my $rblhit;
+ my $rblhittxt;
+
+ if (checkip(\$ip)) {
+ eval {
+ local $SIG{__DIE__} = undef;
+ $netip = Net::IP->new($ip);
+ $reversed_ip = $netip->reverse_ip();
+ };
+
+ if ($reversed_ip =~ /^(\S+)\.in-addr\.arpa/) {$reversed_ip = $1}
+ if ($reversed_ip =~ /^(\S+)\s+(\S+)\.in-addr\.arpa/) {$reversed_ip = $2}
+ if ($reversed_ip =~ /^(\S+)\.ip6\.arpa/) {$reversed_ip = $1}
+ if ($reversed_ip =~ /^(\S+)\s+(\S+)\.ip6\.arpa/) {$reversed_ip = $2}
+
+ if ($reversed_ip ne "") {
+ my $lookup_ip = $reversed_ip.".".$rbl;
+
+ my $cmdpid;
+ eval {
+ local $SIG{__DIE__} = undef;
+ local $SIG{'ALRM'} = sub {die};
+ alarm($timeout);
+ my ($childin, $childout);
+ $cmdpid = open3($childin, $childout, $childout, $config{HOST},"-t","A",$lookup_ip);
+ close $childin;
+ my @results = <$childout>;
+ waitpid ($cmdpid, 0);
+ chomp @results;
+ if ($results[0] =~ /^${reversed_ip}.+ ($ipv4reg|$ipv6reg)$/) {$rblhit = $1}
+ alarm(0);
+ };
+ alarm(0);
+ if ($@) {$rblhit = "timeout"}
+ if ($cmdpid =~ /\d+/ and $cmdpid > 1 and kill(0,$cmdpid)) {kill(9,$cmdpid)}
+
+ if ($rblhit ne "") {
+ if ($rblhit ne "timeout") {
+ my $cmdpid;
+ eval {
+ local $SIG{__DIE__} = undef;
+ local $SIG{'ALRM'} = sub {die};
+ alarm($timeout);
+ my ($childin, $childout);
+ $cmdpid = open3($childin, $childout, $childout, $config{HOST},"-t","TXT",$lookup_ip);
+ close $childin;
+ my @results = <$childout>;
+ waitpid ($cmdpid, 0);
+ chomp @results;
+ foreach my $line (@results) {
+ if ($line =~ /^${reversed_ip}.+ "([^\"]+)"$/) {$rblhittxt .= "$1\n"}
+ }
+ alarm(0);
+ };
+ alarm(0);
+ if ($cmdpid =~ /\d+/ and $cmdpid > 1 and kill(0,$cmdpid)) {kill(9,$cmdpid)}
+ }
+ }
+ }
+ }
+ return ($rblhit,$rblhittxt);
+}
+# end rbllookup
+###############################################################################
+
+1;
diff --git a/csf/ConfigServer/RegexMain.pm b/csf/ConfigServer/RegexMain.pm
new file mode 100644
index 0000000..483cf6e
--- /dev/null
+++ b/csf/ConfigServer/RegexMain.pm
@@ -0,0 +1,1028 @@
+###############################################################################
+# Copyright (C) 2006-2025 Jonathan Michaelson
+#
+# https://github.com/waytotheweb/scripts
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, see .
+###############################################################################
+## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
+# start main
+package ConfigServer::RegexMain;
+
+use strict;
+use lib '/usr/local/csf/lib';
+use IPC::Open3;
+use ConfigServer::Config;
+use ConfigServer::CheckIP qw(checkip);
+use ConfigServer::Slurp qw(slurp);
+use ConfigServer::Logger qw(logfile);
+use ConfigServer::GetEthDev;
+
+use Exporter qw(import);
+our $VERSION = 1.03;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw();
+
+our (%config, %cpconfig, $slurpreg, $cleanreg, %globlogs, %brd, %ips);
+
+my $config = ConfigServer::Config->loadconfig();
+%config = $config->config;
+
+$slurpreg = ConfigServer::Slurp->slurpreg;
+$cleanreg = ConfigServer::Slurp->cleanreg;
+
+if (-e "/etc/wwwacct.conf") {
+ foreach my $line (slurp("/etc/wwwacct.conf")) {
+ $line =~ s/$cleanreg//g;
+ if ($line =~ /^(\s|\#|$)/) {next}
+ my ($name,$value) = split (/ /,$line,2);
+ $cpconfig{$name} = $value;
+ }
+}
+if (-e "/usr/local/cpanel/version") {
+ foreach my $line (slurp("/usr/local/cpanel/version")) {
+ $line =~ s/$cleanreg//g;
+ if ($line =~ /\d/) {$cpconfig{version} = $line}
+ }
+}
+
+if ($config{LF_APACHE_ERRPORT} == 0) {
+ my $apachebin = "";
+ if (-e "/usr/local/apache/bin/httpd") {$apachebin = "/usr/local/apache/bin/httpd"}
+ elsif (-e "/usr/sbin/httpd") {$apachebin = "/usr/sbin/httpd"}
+ elsif (-e "/usr/sbin/apache2") {$apachebin = "/usr/sbin/apache2"}
+ elsif (-e "/usr/sbin/httpd2") {$apachebin = "/usr/sbin/httpd2"}
+ if (-e $apachebin) {
+ my ($childin, $childout);
+ my $mypid = open3($childin, $childout, $childout, $apachebin,"-v");
+ my @version = <$childout>;
+ waitpid ($mypid, 0);
+ chomp @version;
+ $version[0] =~ /Apache\/(\d+)\.(\d+)\.(\d+)/;
+ my $mas = $1;
+ my $maj = $2;
+ my $min = $3;
+ if ("$mas.$maj" < 2.4) {$config{LF_APACHE_ERRPORT} = 1}
+ }
+}
+unless ($config{LF_APACHE_ERRPORT} == 1) {$config{LF_APACHE_ERRPORT} = 2}
+ConfigServer::Logger::logfile("LF_APACHE_ERRPORT: Set to [$config{LF_APACHE_ERRPORT}]");
+
+my $ethdev = ConfigServer::GetEthDev->new();
+%brd = $ethdev->brd;
+%ips = $ethdev->ipv4;
+
+if (-e "/usr/local/csf/bin/regex.custom.pm") {require "/usr/local/csf/bin/regex.custom.pm"} ##no critic
+
+# end main
+###############################################################################
+# start processline
+sub processline {
+ my $line = shift;
+ my $lgfile = shift;
+ my $globlogs_ref = shift;
+ %globlogs = %{$globlogs_ref};
+ $line =~ s/\n//g;
+ $line =~ s/\r//g;
+
+ if (-e "/usr/local/csf/bin/regex.custom.pm") {
+ my ($text,$ip,$app,$trigger,$ports,$temp,$cf) = &custom_line($line,$lgfile);
+ if ($text) {
+ return ($text,$ip,$app,$trigger,$ports,$temp,$cf);
+ }
+ }
+
+#openSSH
+#RH
+ if (($config{LF_SSHD}) and (($lgfile eq "/var/log/messages") or ($lgfile eq "/var/log/secure") or ($globlogs{SSHD_LOG}{$lgfile})) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) (\S+ )?sshd\[\d+\]: pam_unix\(sshd:auth\): authentication failure; logname=\S* uid=\S* euid=\S* tty=\S* ruser=\S* rhost=(\S+)\s+(user=(\S+))?/)) {
+ my $ip = $3;
+ my $acc = $5;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed SSH login from","$ip|$acc","sshd")} else {return}
+ }
+ if (($config{LF_SSHD}) and (($lgfile eq "/var/log/messages") or ($lgfile eq "/var/log/secure") or ($globlogs{SSHD_LOG}{$lgfile})) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) (\S+ )?sshd\[\d+\]: Failed none for (\S*) from (\S+) port \S+/)) {
+ my $ip = $4;
+ my $acc = $3;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed SSH login from","$ip|$acc","sshd")} else {return}
+ }
+ if (($config{LF_SSHD}) and (($lgfile eq "/var/log/messages") or ($lgfile eq "/var/log/secure") or ($globlogs{SSHD_LOG}{$lgfile})) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) (\S+ )?sshd\[\d+\]: Failed password for (invalid user |illegal user )?(\S*) from (\S+)( port \S+ \S+\s*)?/)) {
+ my $ip = $5;
+ my $acc = $4;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed SSH login from","$ip|$acc","sshd")} else {return}
+ }
+ if (($config{LF_SSHD}) and (($lgfile eq "/var/log/messages") or ($lgfile eq "/var/log/secure") or ($globlogs{SSHD_LOG}{$lgfile})) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) (\S+ )?sshd\[\d+\]: Failed keyboard-interactive(\/pam)? for (invalid user )?(\S*) from (\S+) port \S+/)) {
+ my $ip = $6;
+ my $acc = $4;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed SSH login from","$ip|$acc","sshd")} else {return}
+ }
+ if (($config{LF_SSHD}) and (($lgfile eq "/var/log/messages") or ($lgfile eq "/var/log/secure") or ($globlogs{SSHD_LOG}{$lgfile})) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) (\S+ )?sshd\[\d+\]: Invalid user (\S*) from (\S+)/)) {
+ my $ip = $4;
+ my $acc = $3;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed SSH login from","$ip|$acc","sshd")} else {return}
+ }
+ if (($config{LF_SSHD}) and (($lgfile eq "/var/log/messages") or ($lgfile eq "/var/log/secure") or ($globlogs{SSHD_LOG}{$lgfile})) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) (\S+ )?sshd\[\d+\]: User (\S*) from (\S+)\s* not allowed because not listed in AllowUsers/)) {
+ my $ip = $4;
+ my $acc = $3;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed SSH login from","$ip|$acc","sshd")} else {return}
+ }
+ if (($config{LF_SSHD}) and (($lgfile eq "/var/log/messages") or ($lgfile eq "/var/log/secure") or ($globlogs{SSHD_LOG}{$lgfile})) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) (\S+ )?sshd\[\d+\]: Did not receive identification string from (\S+)/)) {
+ my $ip = $3;
+ my $acc = "";
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed SSH login from","$ip|$acc","sshd")} else {return}
+ }
+ if (($config{LF_SSHD}) and (($lgfile eq "/var/log/messages") or ($lgfile eq "/var/log/secure") or ($globlogs{SSHD_LOG}{$lgfile})) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) (\S+ )?sshd\[\d+\]: refused connect from (\S+)/)) {
+ my $ip = $3;
+ my $acc = "";
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed SSH login from","$ip|$acc","sshd")} else {return}
+ }
+ if (($config{LF_SSHD}) and (($lgfile eq "/var/log/messages") or ($lgfile eq "/var/log/secure") or ($globlogs{SSHD_LOG}{$lgfile})) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) (\S+ )?sshd\[\d+\]: error: maximum authentication attempts exceeded for (\S*) from (\S+)/)) {
+ my $ip = $4;
+ my $acc = "";
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed SSH login from","$ip|$acc","sshd")} else {return}
+ }
+
+#Debian/Ubuntu
+ if (($config{LF_SSHD}) and (($lgfile eq "/var/log/messages") or ($lgfile eq "/var/log/secure") or ($globlogs{SSHD_LOG}{$lgfile})) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) (\S+ )?sshd\[\d+\]: Illegal user (\S*) from (\S+)/)) {
+ my $ip = $4;
+ my $acc = $3;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed SSH login from","$ip|$acc","sshd")} else {return}
+ }
+
+#Gentoo
+ if (($config{LF_SSHD}) and (($lgfile eq "/var/log/messages") or ($lgfile eq "/var/log/secure") or ($globlogs{SSHD_LOG}{$lgfile})) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) (\S+ )?sshd\[\d+\]: error: PAM: Authentication failure for (\S*) from (\S+)/)) {
+ my $ip = $4;
+ my $acc = $3;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed SSH login from","$ip|$acc","sshd")} else {return}
+ }
+
+#courier-imap
+ if (($config{LF_POP3D}) and ($globlogs{POP3D_LOG}{$lgfile}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ pop3d(-ssl)?: LOGIN FAILED, user=(\S*), ip=\[(\S+)\]\s*$/)) {
+ my $ip = $4;
+ my $acc = $3;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed POP3 login from","$ip|$acc","pop3d")} else {return}
+ }
+ if (($config{LF_IMAPD}) and ($globlogs{IMAPD_LOG}{$lgfile}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ imapd(-ssl)?: LOGIN FAILED, user=(\S*), ip=\[(\S+)\]\s*$/)) {
+ my $ip = $4;
+ my $acc = $3;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed IMAP login from","$ip|$acc","imapd")} else {return}
+ }
+
+#uw-imap
+ if (($config{LF_POP3D}) and ($globlogs{POP3D_LOG}{$lgfile}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ ipop3d\[\d+\]: Login failed user=(\S*) auth=\S+ host=\S+ \[(\S+)\]\s*$/)) {
+ my $ip = $3;
+ my $acc = $2;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed POP3 login from","$ip|$acc","pop3d")} else {return}
+ }
+ if (($config{LF_IMAPD}) and ($globlogs{IMAPD_LOG}{$lgfile}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ imapd\[\d+\]: Login failed user=(\S*) auth=\S+ host=\S+ \[(\S+)\]\s*$/)) {
+ my $ip = $3;
+ my $acc = $2;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed IMAP login from","$ip|$acc","imapd")} else {return}
+ }
+
+#dovecot
+ if (($config{LF_POP3D}) and ($globlogs{POP3D_LOG}{$lgfile}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ dovecot(\[\d+\])?: pop3-login: (Disconnected: )?(Aborted login( by logging out)?|Connection closed|Disconnected|Disconnected: Inactivity)(:\s*\S+\sfailed: Connection reset by peer)?(\s*\(auth failed, \d+ attempts( in \d+ secs)?\))?: (user=(<\S*>)?, )?(method=\S+, )?rip=(\S+), lip=/)) {
+ my $ip = $12;
+ my $acc = $10;
+ $ip =~ s/^::ffff://;
+ $acc =~ s/^<|>$//g;
+ if (checkip(\$ip)) {return ("Failed POP3 login from","$ip|$acc","pop3d")} else {return}
+ }
+ if (($config{LF_IMAPD}) and ($globlogs{IMAPD_LOG}{$lgfile}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ dovecot(\[\d+\])?: imap-login: (Disconnected: )?(Aborted login( by logging out)?|Connection closed|Disconnected|Disconnected: Inactivity)(:\s*\S+\sfailed: Connection reset by peer)?(\s*\(auth failed, \d+ attempts( in \d+ secs)?\))?: (user=(<\S*>)?, )?(method=\S+, )?rip=(\S+), lip=/)) {
+ my $ip = $12;
+ my $acc = $10;
+ $ip =~ s/^::ffff://;
+ $acc =~ s/^<|>$//g;
+ if (checkip(\$ip)) {return ("Failed IMAP login from","$ip|$acc","imapd")} else {return}
+ }
+ if (($config{LF_POP3D}) and ($globlogs{POP3D_LOG}{$lgfile}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) pop3-login(\[\d+\])?: Info: (Aborted login( by logging out)?|Connection closed|Disconnected|Disconnected: Inactivity)(\s*\(auth failed, \d+ attempts( in \d+ secs)?\))?: (user=(<\S*>)?, )?(method=\S+, )?rip=(\S+), lip=/)) {
+ my $ip = $10;
+ my $acc = $8;
+ $ip =~ s/^::ffff://;
+ $acc =~ s/^<|>$//g;
+ if (checkip(\$ip)) {return ("Failed POP3 login from","$ip|$acc","pop3d")} else {return}
+ }
+ if (($config{LF_IMAPD}) and ($globlogs{IMAPD_LOG}{$lgfile}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) imap-login(\[\d+\])?: Info: (Aborted login( by logging out)?|Connection closed|Disconnected|Disconnected: Inactivity)(\s*\(auth failed, \d+ attempts( in \d+ secs)?\))?: (user=(<\S*>)?, )?(method=\S+, )?rip=(\S+), lip=/)) {
+ my $ip = $10;
+ my $acc = $8;
+ $ip =~ s/^::ffff://;
+ $acc =~ s/^<|>$//g;
+ if (checkip(\$ip)) {return ("Failed IMAP login from","$ip|$acc","imapd")} else {return}
+ }
+
+#Kerio Mailserver
+ if (($config{LF_POP3D}) and ($globlogs{POP3D_LOG}{$lgfile}) and ($line =~ /^\S+ \S+ POP3(\[\d+\])?: User (\S*) doesn\'t exist\. Attempt from IP address (\S+)\s*$/)) {
+ my $ip = $3;
+ my $acc = $2;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed POP3 login from","$ip|$acc","pop3d")} else {return}
+ }
+ if (($config{LF_POP3D}) and ($globlogs{POP3D_LOG}{$lgfile}) and ($line =~ /^\S+ \S+ POP3(\[\d+\])?: Invalid password for user (\S*)\. Attempt from IP address (\S+)\s*$/)) {
+ my $ip = $3;
+ my $acc = $2;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed POP3 login from","$ip|$acc","pop3d")} else {return}
+ }
+ if (($config{LF_IMAPD}) and ($globlogs{IMAPD_LOG}{$lgfile}) and ($line =~ /^\S+ \S+ IMAP(\[\d+\])?: User (\S*) doesn\'t exist\. Attempt from IP address (\S+)\s*$/)) {
+ my $ip = $3;
+ my $acc = $2;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed IMAP login from","$ip|$acc","imapd")} else {return}
+ }
+ if (($config{LF_IMAPD}) and ($globlogs{IMAPD_LOG}{$lgfile}) and ($line =~ /^\S+ \S+ IMAP(\[\d+\])?: Invalid password for user (\S*)\. Attempt from IP address (\S+)\s*$/)) {
+ my $ip = $3;
+ my $acc = $2;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed IMAP login from","$ip|$acc","imapd")} else {return}
+ }
+ if (($config{LF_SMTPAUTH}) and ($globlogs{SMTPAUTH_LOG}{$lgfile}) and ($line =~ /^\S+ \S+ smtp(\[\d+\])?: User (\S*) doesn\'t exist\. Attempt from IP address (\S+)\s*$/)) {
+ my $ip = $3;
+ my $acc = $2;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed IMAP login from","$ip|$acc","imapd")} else {return}
+ }
+
+#pure-ftpd
+#Nov 10 04:28:04 w212 pure-ftpd[3269638]: (?@152.57.198.52) [WARNING] Authentication failed for user [www]
+ if (($config{LF_FTPD}) and ($globlogs{FTPD_LOG}{$lgfile}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ pure-ftpd(\[\d+\])?: \(\?\@(\S+)\) \[WARNING\] Authentication failed for user \[(\S*)\]/)) {
+ my $ip = $3;
+ my $acc = $4;
+ $ip =~ s/^::ffff://;
+ $ip =~ s/\_/\:/g;
+ if (checkip(\$ip)) {return ("Failed FTP login from","$ip|$acc","ftpd")} else {return}
+ }
+
+#proftpd
+ if (($config{LF_FTPD}) and ($globlogs{FTPD_LOG}{$lgfile}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ proftpd\[\d+\]:? \S+ \([^\[]+\[(\S+)\]\)( -)?:? - no such user \'(\S*)\'/)) {
+ my $ip = $2;
+ my $acc = $4;
+ $ip =~ s/^::ffff://;
+ $acc =~ s/:$//g;
+ if (checkip(\$ip)) {return ("Failed FTP login from","$ip|$acc","ftpd")} else {return}
+ }
+ if (($config{LF_FTPD}) and ($globlogs{FTPD_LOG}{$lgfile}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ proftpd\[\d+\]:? \S+ \([^\[]+\[(\S+)\]\)( -)?:? USER (\S*) no such user found from/)) {
+ my $ip = $2;
+ my $acc = $4;
+ $ip =~ s/^::ffff://;
+ $acc =~ s/:$//g;
+ if (checkip(\$ip)) {return ("Failed FTP login from","$ip|$acc","ftpd")} else {return}
+ }
+ if (($config{LF_FTPD}) and ($globlogs{FTPD_LOG}{$lgfile}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ proftpd\[\d+\]:? \S+ \([^\[]+\[(\S+)\]\)( -)?:? - SECURITY VIOLATION/)) {
+ my $ip = $2;
+ my $acc = "";
+ $ip =~ s/^::ffff://;
+ $acc =~ s/:$//g;
+ if (checkip(\$ip)) {return ("Failed FTP login from","$ip|$acc","ftpd")} else {return}
+ }
+ if (($config{LF_FTPD}) and ($globlogs{FTPD_LOG}{$lgfile}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ proftpd\[\d+\]:? \S+ \([^\[]+\[(\S+)\]\)( -)?:? - USER (\S*) \(Login failed\): Incorrect password/)) {
+ my $ip = $2;
+ my $acc = $4;
+ $ip =~ s/^::ffff://;
+ $acc =~ s/:$//g;
+ if (checkip(\$ip)) {return ("Failed FTP login from","$ip|$acc","ftpd")} else {return}
+ }
+
+#vsftpd
+ if (($config{LF_FTPD}) and ($globlogs{FTPD_LOG}{$lgfile}) and ($line =~ /^\S+\s+\S+\s+\d+\s+\S+\s+\d+ \[pid \d+] \[(\S+)\] FAIL LOGIN: Client "(\S+)"/)) {
+ my $ip = $2;
+ my $acc = $1;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed FTP login from","$ip|$acc","ftpd")} else {return}
+ }
+ if (($config{LF_FTPD}) and ($globlogs{FTPD_LOG}{$lgfile}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ vsftpd\[\d+\]: pam_unix\(\S+\): authentication failure; logname=\S*\s+\S+\s+\S+\s+\S+\s+ruser=\S*\s+rhost=(\S+)(\s+user=(\S*))?/)) {
+ my $ip = $2;
+ my $acc = $4;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed FTP login from","$ip|$acc","ftpd")} else {return}
+ }
+ if (($config{LF_FTPD}) and ($globlogs{FTPD_LOG}{$lgfile}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ vsftpd\(pam_unix\)\[\d+\]: authentication failure; logname=\S*\s+\S+\s+\S+\s+\S+\s+ruser=\S*\s+rhost=(\S+)(\s+user=(\S*))?/)) {
+ my $ip = $2;
+ my $acc = $4;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed FTP login from","$ip|$acc","ftpd")} else {return}
+ }
+
+#apache htaccess
+ if (($config{LF_HTACCESS}) and ($globlogs{HTACCESS_LOG}{$lgfile}) and ($line =~ /^\[\S+\s+\S+\s+\S+\s+\S+\s+\S+\] \[(\S*:)?error\] (\[pid \d+(:tid \d+)?\] )?\[(client|remote) (\S+)\] (\w+: )?user (\S*)(( not found:)|(: authentication failure for))/)) {
+ my $ip = $5;
+ my $acc = $7;
+ $ip =~ s/^::ffff://;
+ if ($config{LF_APACHE_ERRPORT} == 2 and $ip =~ /(.*):\d+$/) {$ip = $1}
+ if (checkip(\$ip)) {return ("Failed web page login from","$ip|$acc","htpasswd")} else {return}
+ }
+#nginx
+ if (($config{LF_HTACCESS}) and ($globlogs{HTACCESS_LOG}{$lgfile}) and ($line =~ /^\S+ \S+ \[error\] \S+ \*\S+ no user\/password was provided for basic authentication, client: (\S+),/)) {
+ my $ip = $1;
+ my $acc = "";
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed web page login from","$ip|$acc","htpasswd")} else {return}
+ }
+ if (($config{LF_HTACCESS}) and ($globlogs{HTACCESS_LOG}{$lgfile}) and ($line =~ /^\S+ \S+ \[error\] \S+ \*\S+ user \"(\S*)\": password mismatch, client: (\S+),/)) {
+ my $ip = $2;
+ my $acc = $1;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed web page login from","$ip|$acc","htpasswd")} else {return}
+ }
+ if (($config{LF_HTACCESS}) and ($globlogs{HTACCESS_LOG}{$lgfile}) and ($line =~ /^\S+ \S+ \[error\] \S+ \*\S+ user \"(\S*)\" was not found in \".*?\", client: (\S+),/)) {
+ my $ip = $2;
+ my $acc = $1;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed web page login from","$ip|$acc","htpasswd")} else {return}
+ }
+
+#cxs Apache
+ if (($config{LF_CXS}) and ($globlogs{MODSEC_LOG}{$lgfile}) and ($line =~ /^\[\S+\s+\S+\s+\S+\s+\S+\s+\S+\] \[(\S*:)?error\] (\[pid \d+(:tid \d+)?\] )?\[(client|remote) (\S+)\]( \[client \S+\])? (\w+: )?ModSecurity:(( \[[^]]+\])*)? Access denied with code \d\d\d \(phase 2\)\. File \"[^\"]*\" rejected by the approver script \"\/etc\/cxs\/cxscgi\.sh\"/)) {
+ my $ip = $5;
+ my $acc = "";
+ my $domain = "";
+ if ($line =~ /\] \[hostname "([^\"]+)"\] \[/) {$domain = $1}
+ $ip =~ s/^::ffff://;
+ if ($config{LF_APACHE_ERRPORT} == 2 and $ip =~ /(.*):\d+$/) {$ip = $1}
+ if (checkip(\$ip)) {return ("cxs mod_security triggered by","$ip|$acc|$domain","cxs")} else {return}
+ }
+#cxs Litespeed
+ if (($config{LF_CXS}) and ($globlogs{MODSEC_LOG}{$lgfile}) and ($line =~ /^\[\S+\s+\S+\s+\S+\s+\S+\s+\S+\] \[(\S*:)?error\] (\[pid \d+(:tid \d+)?\] )?\[(client|remote) (\S+)\]( \[client \S+\])? (\w+: )?ModSecurity:(( \[[^]]+\])*)? Access denied with code \d\d\d, \[Rule: 'FILES_TMPNAMES' '\@inspectFile \/etc\/cxs\/cxscgi\.sh'\] \[id "1010101"\]/)) {
+ my $ip = $5;
+ my $acc = "";
+ my $domain = "";
+ if ($line =~ /\] \[hostname "([^\"]+)"\] \[/) {$domain = $1}
+ $ip =~ s/^::ffff://;
+ if ($config{LF_APACHE_ERRPORT} == 2 and $ip =~ /(.*):\d+$/) {$ip = $1}
+ if (checkip(\$ip)) {return ("cxs mod_security triggered by","$ip|$acc|$domain","cxs")} else {return}
+ }
+
+#mod_security v1
+ if (($config{LF_MODSEC}) and ($globlogs{MODSEC_LOG}{$lgfile}) and ($line =~ /^\[\S+\s+\S+\s+\S+\s+\S+\s+\S+\] \[error\] \[(client|remote) (\S+)\] mod_security: Access denied/)) {
+ my $ip = $2;
+ my $acc = "";
+ my $domain = "";
+ if ($line =~ /\] \[hostname "([^\"]+)"\] \[/) {$domain = $1}
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("mod_security triggered by","$ip|$acc|$domain","mod_security")} else {return}
+ }
+
+#mod_security v2 (apache)
+ if (($config{LF_MODSEC}) and ($globlogs{MODSEC_LOG}{$lgfile}) and ($line =~ /^\[\S+\s+\S+\s+\S+\s+\S+\s+\S+\] \[(\S*:)?error\] (\[pid \d+(:tid \d+)?\] )?\[(client|remote) (\S+)\]( \[client \S+\])? (\w+: )?ModSecurity:(( \[[^]]+\])*)? Access denied/)) {
+ my $ip = $5;
+ my $acc = "";
+ my $domain = "";
+ if ($line =~ /\] \[hostname "([^\"]+)"\] \[/) {$domain = $1}
+ $ip =~ s/^::ffff://;
+ if ($config{LF_APACHE_ERRPORT} == 2 and $ip =~ /(.*):\d+$/) {$ip = $1}
+ my $ruleid = "unknown";
+ if ($line =~ /\[id "(\d+)"\]/) {$ruleid = $1}
+ if (checkip(\$ip)) {return ("mod_security (id:$ruleid) triggered by","$ip|$acc|$domain","mod_security")} else {return}
+ }
+#mod_security v2 (nginx)
+ if (($config{LF_MODSEC}) and ($globlogs{MODSEC_LOG}{$lgfile}) and ($line =~ /^\S+ \S+ \[\S+\] \S+ \[(client|remote) (\S+)\] ModSecurity:(( \[[^]]+\])*)? Access denied/)) {
+ my $ip = $2;
+ my $acc = "";
+ my $domain = "";
+ if ($line =~ /\] \[hostname "([^\"]+)"\] \[/) {$domain = $1}
+ $ip =~ s/^::ffff://;
+ my $ruleid = "unknown";
+ if ($line =~ /\[id "(\d+)"\]/) {$ruleid = $1}
+ if (checkip(\$ip)) {return ("mod_security (id:$ruleid) triggered by","$ip|$acc|$domain","mod_security")} else {return}
+ }
+
+#BIND
+ if (($config{LF_BIND}) and ($globlogs{BIND_LOG}{$lgfile}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ named\[\d+\]: client( \S+)? (\S+)\#\d+(\s\(\S+\))?\:( view external\:)? (update|zone transfer|query \(cache\)) \'[^\']*\' denied$/)) {
+ my $ip = $3;
+ my $acc = "";
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("bind triggered by","$ip|$acc","bind")} else {return}
+ }
+
+#suhosin
+ if (($config{LF_SUHOSIN}) and ($globlogs{SUHOSIN_LOG}{$lgfile})and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ suhosin\[\d+\]: ALERT - .* \(attacker \'(\S+)\'/)) {
+ my $ip = $2;
+ my $acc = "";
+ $ip =~ s/^::ffff://;
+ if ($line !~ /script tried to increase memory_limit/) {
+ if (checkip(\$ip)) {return ("Suhosin triggered by","$ip|$acc","suhosin")} else {return}
+ }
+ }
+
+#cPanel/WHM
+ if (($config{LF_CPANEL}) and ($globlogs{CPANEL_LOG}{$lgfile}) and ($line =~ /^\[\S+\s+\S+\s+\S+\] \w+ \[\w+] (\S+) - (\S+) \"[^\"]+\" FAILED LOGIN/)) {
+ my $ip = $1;
+ my $acc = $2;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed cPanel login from","$ip|$acc","cpanel")} else {return}
+ }
+ if (($config{LF_CPANEL}) and ($globlogs{CPANEL_LOG}{$lgfile}) and ($line =~ /^(\S+) - (\S+)? \[\S+ \S+\] \"[^\"]*\" FAILED LOGIN/)) {
+ my $ip = $1;
+ my $acc = $2;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed cPanel login from","$ip|$acc","cpanel")} else {return}
+ }
+
+#webmin
+ if (($config{LF_WEBMIN}) and ($globlogs{WEBMIN_LOG}{$lgfile}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ webmin\[\d+\]: Invalid login as (\S+) from (\S+)/)) {
+ my $ip = $3;
+ my $acc = $2;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed Webmin login from","$ip|$acc","webmin")} else {return}
+ }
+
+#DirectAdmin
+ if (($config{LF_DIRECTADMIN}) and ($globlogs{DIRECTADMIN_LOG}{$lgfile}) and ($line =~ /^\S+ \'(\S+)\' \d+ (failed login attempts\. Account|failed login attempt on account) \'(\S+)\'/)) {
+ my $ip = $1;
+ my $acc = $3;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed DirectAdmin login from","$ip|$acc","directadmin")} else {return}
+ }
+ if (($config{LF_DIRECTADMIN}) and ($globlogs{DIRECTADMIN_LOG_R}{$lgfile}) and ($line =~ /^\[\S+\s+\S+\s+\S+\]: (<\S+> )?IMAP Error: Login failed for (\S+) (against \S+ )?from (\S+)\. AUTHENTICATE PLAIN: Authentication failed\. in \/var\/www\/html\/roundcubemail/)) {
+ my $ip = $4;
+ my $acc = $2;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed DirectAdmin Roundcube login from","$ip|$acc","directadmin")} else {return}
+ }
+ if (($config{LF_DIRECTADMIN}) and ($globlogs{DIRECTADMIN_LOG_S}{$lgfile}) and ($line =~ /^\S+\s+\S+ \[LOGIN_ERROR\] (\S+)( \(\S+\))? from (\S+): Unknown user or password incorrect\.\s*$/)) {
+ my $ip = $3;
+ my $acc = $1;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed DirectAdmin SquirrelMail login from","$ip|$acc","directadmin")} else {return}
+ }
+#Jun 12 10:58:00 phpmyadmin: user denied: bill (mysql-denied) from 192.168.254.10
+ if (($config{LF_DIRECTADMIN}) and ($globlogs{DIRECTADMIN_LOG_P}{$lgfile}) and ($line =~ /^\S+\s+\S+\s+\S+: pma auth user='(\S+)' status='mysql-denied' ip='(\S+)'\s*$/)) {
+ my $ip = $2;
+ my $acc = $1;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed DirectAdmin phpMyAdmin login from","$ip|$acc","directadmin")} else {return}
+ }
+ if (($config{LF_DIRECTADMIN}) and ($globlogs{DIRECTADMIN_LOG_P}{$lgfile}) and ($line =~ /^\S+\s+\S+\s+\S+ phpmyadmin: user denied: (\S+) \(mysql-denied\) from (\S+)\s*$/)) {
+ my $ip = $2;
+ my $acc = $1;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed DirectAdmin phpMyAdmin login from","$ip|$acc","directadmin")} else {return}
+ }
+
+#Exim SMTP AUTH
+ if (($config{LF_SMTPAUTH}) and ($globlogs{SMTPAUTH_LOG}{$lgfile}) and ($line =~ /^\S+\s+\S+\s+(\[\d+\] )?(\S+) authenticator failed for \S+ (\S+ )?\[(\S+)\](:\S*:?)?( I=\S+| \d+\:)? 535 Incorrect authentication data( \(set_id=(\S+)\))?/)) {
+ my $ip = $4;
+ my $acc = $8;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed SMTP AUTH login from","$ip|$acc","smtpauth")} else {return}
+ }
+
+#Exim Syntax Errors
+ if (($config{LF_EXIMSYNTAX}) and ($globlogs{SMTPAUTH_LOG}{$lgfile}) and ($line =~ /^\S+\s+\S+\s+(\[\d+\] )?SMTP call from (\S+ )?\[(\S+)\](:\S*:?)?( I=\S+)? dropped: too many syntax or protocol errors/)) {
+ my $ip = $3;
+ my $acc = "";
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Exim syntax errors from","$ip|$acc","eximsyntax")} else {return}
+ }
+ if (($config{LF_EXIMSYNTAX}) and ($globlogs{SMTPAUTH_LOG}{$lgfile}) and ($line =~ /^\S+\s+\S+\s+(\[\d+\] )?SMTP protocol error in \"[^\"]+\" H=\S+ (\S+ )?\[(\S+)\](:\S*:?)?( I=\S+)? AUTH command used when not advertised/)) {
+ my $ip = $3;
+ my $acc = "";
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Exim syntax errors from","$ip|$acc","eximsyntax")} else {return}
+ }
+
+#mod_qos
+ if (($config{LF_QOS}) and ($globlogs{HTACCESS_LOG}{$lgfile}) and ($line =~ /^\[\S+\s+\S+\s+\S+\s+\S+\s+\S+\] \[(\S*:)?error\] (\[pid \d+(:tid \d+)?\] )?\[(client|remote) (\S+)\] (\w+: )?mod_qos\(\d+\): access denied,/)) {
+ my $ip = $5;
+ my $acc = "";
+ $ip =~ s/^::ffff://;
+ if ($config{LF_APACHE_ERRPORT} == 2 and $ip =~ /(.*):\d+$/) {$ip = $1}
+ if (checkip(\$ip)) {return ("mod_qos triggered by","$ip|$acc","mod_qos")} else {return}
+ }
+
+#Apache symlink race condition
+ if (($config{LF_SYMLINK}) and ($globlogs{MODSEC_LOG}{$lgfile}) and ($line =~ /^\[\S+\s+\S+\s+\S+\s+\S+\s+\S+\] \[(\S*:)?error\] (\[pid \d+(:tid \d+)?\] )?\[(client|remote) (\S+)\] (\w+: )?Caught race condition abuser/)) {
+ my $ip = $5;
+ my $acc = "";
+ $ip =~ s/^::ffff://;
+ if ($config{LF_APACHE_ERRPORT} == 2 and $ip =~ /(.*):\d+$/) {$ip = $1}
+ if ($line !~ /\/cgi-sys\/suspendedpage\.cgi$/) {
+ if (checkip(\$ip)) {return ("symlink race condition triggered by","$ip|$acc","symlink")} else {return}
+ }
+ }
+
+#courier-imap (Plesk)
+ if (($config{LF_POP3D}) and ($globlogs{POP3D_LOG}{$lgfile}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ (courier-)?pop3(?:d|s)(-ssl)?(\[\d+\])?: LOGIN FAILED, user=(\S*), ip=\[(\S+)\]\s*$/)) {
+ my $ip = $6;
+ my $acc = $5;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed POP3 login from","$ip|$acc","pop3d")} else {return}
+ }
+ if (($config{LF_IMAPD}) and ($globlogs{IMAPD_LOG}{$lgfile}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ (courier-)?imap(?:d|s)(-ssl)?(\[\d+\])?: LOGIN FAILED, user=(\S*), ip=\[(\S+)\]\s*$/)) {
+ my $ip = $6;
+ my $acc = $5;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed IMAP login from","$ip|$acc","imapd")} else {return}
+ }
+
+#Qmail SMTP AUTH (Plesk)
+ if (($config{LF_SMTPAUTH}) and ($globlogs{SMTPAUTH_LOG}{$lgfile}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ smtp_auth(?:\[\d+\])?: FAILED: (\S*) - password incorrect from \S+ \[(\S+)\]\s*$/)) {
+ my $ip = $3;
+ my $acc = $2;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed SMTP AUTH login from","$ip|$acc","smtpauth")} else {return}
+ }
+
+#Postfix SMTP AUTH (Plesk)
+ if (($config{LF_SMTPAUTH}) and ($globlogs{SMTPAUTH_LOG}{$lgfile}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ postfix\/(submission\/)?smtpd(?:\[\d+\])?: warning: \S+\[(\S+)\]: SASL (?:(?i)LOGIN|PLAIN|(?:CRAM|DIGEST)-MD5) authentication failed/)) {
+ my $ip = $3;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed SMTP AUTH login from","$ip","smtpauth")} else {return}
+ }
+
+#InterWorx (dovecot, proftpd, qmail)
+ if (($config{LF_POP3D}) and ($globlogs{POP3D_LOG}{$lgfile}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) pop3-login(\[\d+\])?: Info: (Aborted login|Disconnected|Disconnected: Inactivity)( \(auth failed, \d+ attempts( in \d+ secs)?\))?: (user=(<\S*>)?, )?(method=\S+, )?rip=(\S+), lip=/)) {
+ my $ip = $9;
+ my $acc = $7;
+ $ip =~ s/^::ffff://;
+ $acc =~ s/^<|>$//g;
+ if (checkip(\$ip)) {return ("Failed POP3 login from","$ip|$acc","pop3d")} else {return}
+ }
+ if (($config{LF_IMAPD}) and ($globlogs{IMAPD_LOG}{$lgfile}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) imap-login(\[\d+\])?: Info: (Aborted login|Disconnected|Disconnected: Inactivity)( \(auth failed, \d+ attempts( in \d+ secs)?\))?: (user=(<\S*>)?, )?(method=\S+, )?rip=(\S+), lip=/)) {
+ my $ip = $9;
+ my $acc = $7;
+ $ip =~ s/^::ffff://;
+ $acc =~ s/^<|>$//g;
+ if (checkip(\$ip)) {return ("Failed IMAP login from","$ip|$acc","imapd")} else {return}
+ }
+ if (($config{LF_FTPD}) and ($globlogs{FTPD_LOG}{$lgfile}) and ($line =~ /^\S+ \S+ \S+ proftpd\[\d+\]:? \S+ \(\S+?[^\[]+\[(\S+)\]\)( -)?:? USER (\S*): no such user found from/)) {
+ my $ip = $1;
+ my $acc = $3;
+ $ip =~ s/^::ffff://;
+ $acc =~ s/:$//g;
+ if (checkip(\$ip)) {return ("Failed FTP login from","$ip|$acc","ftpd")} else {return}
+ }
+ if (($config{LF_FTPD}) and ($globlogs{FTPD_LOG}{$lgfile}) and ($line =~ /^\S+ \S+ \S+ proftpd\[\d+\]:? \S+ \(\S+?[^\[]+\[(\S+)\]\)( -)?:? USER (\S*) \(Login failed\): Incorrect password/)) {
+ my $ip = $1;
+ my $acc = $3;
+ $ip =~ s/^::ffff://;
+ $acc =~ s/:$//g;
+ if (checkip(\$ip)) {return ("Failed FTP login from","$ip|$acc","ftpd")} else {return}
+ }
+ if (($config{LF_SMTPAUTH}) and ($globlogs{SMTPAUTH_LOG}{$lgfile}) and ($line =~ /^\S+ qmail-smtpd\[\d+\]: AUTH failed \[(\S+)\] (\S+)/)) {
+ my $ip = $1;
+ my $acc = $2;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed SMTP AUTH login from","$ip|$acc","smtpauth")} else {return}
+ }
+ if (($config{LF_INTERWORX}) and ($globlogs{INTERWORX_LOG}{$lgfile}) and ($line =~ /^\S+ \S+ (\S+) (\S+) (\S+)/)) {
+ my $iw = "SiteWorx";
+ if ($1 eq "NW") {$iw = "NodeWorx"}
+ my $ip = $2;
+ my $acc = $3;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed $iw login from","$ip|$acc","interworx")} else {return}
+ }
+
+# CWP
+ if (($config{LF_CWP}) and ($globlogs{CWP_LOG}{$lgfile}) and ($line =~ /^\S+\s+\S+\s+(\S+)\s+Failed Login from:\s+(\S+) on:/)) {
+ my $ip = $2;
+ my $acc = $1;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed CWP login from","$ip|$acc","cwp")} else {return}
+ }
+# VestaCP
+ if (($config{LF_VESTA}) and ($globlogs{VESTA_LOG}{$lgfile}) and ($line =~ /^\S+\s+\S+\s+(\S+)\s+(\S+) failed to login/)) {
+ my $ip = $2;
+ my $acc = $1;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("Failed VestaCP login from","$ip|$acc","vesta")} else {return}
+ }
+
+}
+# end processline
+###############################################################################
+# start processloginline
+sub processloginline {
+ my $line = shift;
+
+#courier-imap
+ if (($config{LT_POP3D}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ pop3d(-ssl)?: LOGIN, user=(\S*), ip=\[(\S+)\], port=\S+/)) {
+ my $ip = $4;
+ my $acc = $3;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("pop3d",$acc,$ip)} else {return}
+ }
+ if (($config{LT_IMAPD}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ imapd(-ssl)?: LOGIN, user=(\S*), ip=\[(\S+)\], port=\S+/)) {
+ my $ip = $4;
+ my $acc = $3;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("imapd",$acc,$ip)} else {return}
+ }
+
+#dovecot
+ if (($config{LT_POP3D}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ dovecot(\[\d+\])?: pop3-login: Login: user=<(\S*)>, method=\S+, rip=(\S+), lip=/)) {
+ my $ip = $4;
+ my $acc = $3;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("pop3d",$acc,$ip)} else {return}
+ }
+ if (($config{LT_IMAPD}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ dovecot(\[\d+\])?: imap-login: Login: user=<(\S*)>, method=\S+, rip=(\S+), lip=/)) {
+ my $ip = $4;
+ my $acc = $3;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("imapd",$acc,$ip)} else {return}
+ }
+
+#InterWorx (dovecot)
+ if (($config{LT_POP3D}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) pop3-login: Info: Login: user=<(\S*)>, method=\S+, rip=(\S+), lip=/)) {
+ my $ip = $3;
+ my $acc = $2;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("pop3d",$acc,$ip)} else {return}
+ }
+ if (($config{LT_IMAPD}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) imap-login: Info: Login: user=<(\S*)>, method=\S+, rip=(\S+), lip=/)) {
+ my $ip = $3;
+ my $acc = $2;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ("imapd",$acc,$ip)} else {return}
+ }
+}
+# end processloginline
+###############################################################################
+# start processsshline
+sub processsshline {
+ my $line = shift;
+
+ if (($config{LF_SSH_EMAIL_ALERT}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) (\S+ )?sshd\[\d+\]: Accepted (\S+) for (\S+) from (\S+) port \S+/)) {
+ my $ip = $5;
+ my $acc = $4;
+ my $how = $3;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ($acc,$ip,$how)} else {return}
+ }
+}
+# end processsshline
+###############################################################################
+# start processsuline
+sub processsuline {
+ my $line = shift;
+
+#RH + Debian/Ubuntu
+ if (($config{LF_SU_EMAIL_ALERT}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) (\S+ )?su(\[\d+\])?: pam_unix\(su(-l)?:session\): session opened for user\s+(\S+)\s+by\s+(\S+)\s*$/)) {
+ return ($5,$6,"Successful login");
+ }
+ if (($config{LF_SU_EMAIL_ALERT}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) (\S+ )?su(\[\d+\])?: pam_unix\(su(-l)?:auth\): authentication failure; logname=\S*\s+\S+\s+\S+\s+\S+\s+ruser=(\S+)+\s+\S+\s+user=(\S+)\s*$/)) {
+ return ($6,$5,"Failed login");
+ }
+
+ if (($config{LF_SU_EMAIL_ALERT}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) (\S+ )?su(\[\d+\])?: pam_unix\(su(-l)?:session\): session opened for user\s+(\S+)\s+by\s+(\S+)\s*$/)) {
+ return ($5,$6,"Successful login");
+ }
+ if (($config{LF_SU_EMAIL_ALERT}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) (\S+ )?su(\[\d+\])?: pam_unix\(su(-l)?:auth\): authentication failure; logname=\S*\s+\S+\s+\S+\s+\S+\s+ruser=(\S+)+\s+\S+\s+user=(\S+)\s*$/)) {
+ return ($6,$5,"Failed login");
+ }
+
+ if (($config{LF_SU_EMAIL_ALERT}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) (\S+ )?su\(pam_unix\)\[\d+\]: session opened for user\s+(\S+)\s+by\s+(\S+)\s*$/)) {
+ return ($3,$4,"Successful login");
+ }
+ if (($config{LF_SU_EMAIL_ALERT}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) (\S+ )?su\(pam_unix\)\[\d+\]: authentication failure; logname=\S*\s+\S+\s+\S+\s+\S+\s+ruser=(\S+)+\s+\S+\s+user=(\S+)\s*$/)) {
+ return ($4,$3,"Failed login");
+ }
+ return;
+}
+# end processsuline
+###############################################################################
+# start processsudoline
+sub processsudoline {
+ my $line = shift;
+
+ if (($config{LF_SUDO_EMAIL_ALERT}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) (\S+ )?sudo(\[\d+\])?: pam_unix\(sudo(-l)?:auth\): authentication failure; logname=\S*\s+\S+\s+\S+\s+\S+\s+ruser=(\S+)+\s+\S+\s+user=(\S+)\s*$/)) {
+ return ($6,$5,"Failed login");
+ }
+ if (($config{LF_SUDO_EMAIL_ALERT}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) (\S+ )?sudo\(pam_unix\)\[\d+\]: authentication failure; logname=\S*\s+\S+\s+\S+\s+\S+\s+ruser=(\S+)+\s+\S+\s+user=(\S+)\s*$/)) {
+ return ($4,$3,"Failed login");
+ }
+
+ if (($config{LF_SUDO_EMAIL_ALERT}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) (\S+ )?sudo(\[\d+\])?:\s+(\S+)\s+:\s+(.*)$/)) {
+ my $from = $4;
+ my @items = split(/\s+;\s+/, $5);
+ if ($items[0] =~ /^TTY/) {
+ if ($items[2] =~ /^USER=(\S+)$/) {
+ return ($1,$from,"Successful login");
+ }
+ }
+ elsif ($items[0] =~ /^user NOT in sudoers/) {
+ if ($items[3] =~ /^USER=(\w+)$/) {
+ return ($1,$from,"Failed login");
+ }
+ }
+ }
+ return;
+}
+# end processsudoline
+###############################################################################
+# start processconsoleline
+sub processconsoleline {
+ my $line = shift;
+
+ if (($config{LF_CONSOLE_EMAIL_ALERT}) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ login(\[\d+\])?: ROOT LOGIN/)) {
+ return 1;
+ }
+}
+# end processconsoleline
+###############################################################################
+# start processcpanelline
+sub processcpanelline {
+ my $line = shift;
+
+ if ($config{LF_CPANEL_ALERT} and ($line =~ /^(\S+)\s+\-\s+(\w+)\s+\[[^\]]+\]\s\"[^\"]+\"\s200\s/)) {
+ my $ip = $1;
+ my $acc = $2;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ($ip,$acc)} else {return}
+ }
+}
+# end processcpanelline
+###############################################################################
+# start processwebminline
+sub processwebminline {
+ my $line = shift;
+
+ if ($config{LF_WEBMIN_EMAIL_ALERT} and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ webmin\[\d+\]: Successful login as (\S+) from (\S+)/)) {
+ my $ip = $3;
+ my $acc = $2;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ($acc,$ip)} else {return}
+ }
+}
+# end processwebminline
+###############################################################################
+# start scriptlinecheck
+sub scriptlinecheck {
+ my $line = shift;
+
+ if ($config{LF_SCRIPT_ALERT}) {
+ my $fulldir;
+ if ($line =~ /^\S+\s+\S+\s+(\[\d+\]\s)?cwd=(.*) \d+ args:/) {$fulldir = $2}
+ elsif ($line =~ /^\S+\s+\S+\s+(\[\d+\]\s)?\S+ H=localhost (.*)PWD=(.*) REMOTE_ADDR=\S+$/) {$fulldir = $3}
+ if ($fulldir ne "") {
+ my (undef,$dir,undef) = split(/\//,$fulldir);
+ if ($dir eq "home") {return $fulldir}
+ if ($cpconfig{HOMEDIR} and ($fulldir =~ /^$cpconfig{HOMEDIR}/)) {return $fulldir}
+ if ($cpconfig{HOMEMATCH} and ($dir =~ /$cpconfig{HOMEMATCH}/)) {return $fulldir}
+ }
+ }
+}
+# end scriptlinecheck
+###############################################################################
+# start relaycheck
+sub relaycheck {
+ my $line = shift;
+ my $tline = $line;
+ $tline =~ s/".*"/""/g;
+ my @bits =split(/\s+/,$tline);
+ my $ip;
+
+ if ($tline !~ /^\S+\s+\S+\s+(\[\d+\]\s)?\S+ <=/) {return}
+
+#exim
+ if ($tline =~ / U=(\S+) P=local /) {
+ return ($1, "LOCALRELAY");
+ }
+
+ if ($tline =~ / H=[^=]*\[(\S+)\]/) {
+ $ip = $1;
+ unless (checkip(\$ip) or $ip eq "127.0.0.1" or $ip eq "::1") {return}
+ } else {
+ return;
+ }
+
+ if (($tline =~ / A=(courier_plain|courier_login|dovecot_plain|dovecot_login|fixed_login|fixed_plain|login|plain):/) and ($tline =~ / P=(esmtpa|esmtpsa) /)) {
+ return ($ip, "AUTHRELAY");
+ }
+
+ if ($tline =~ / P=(smtp|esmtp|esmtps) /) {
+ return ($ip, "RELAY");
+ }
+
+}
+# end relaycheck
+###############################################################################
+# start pslinecheck
+sub pslinecheck {
+ my $line = shift;
+ if ($line !~ /^(\S+|\S+\s+\d+\s+\S+) \S+ kernel:\s(\[[^\]]+\]\s)?Firewall:/) {return}
+ if ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ kernel:\s(\[[^\]]+\]\s)?Firewall: \*INVALID\*/ and $config{PS_PORTS} !~ /INVALID/) {return}
+
+ if ($line =~ /IN=\S+.*SRC=(\S+).*DST=(\S+).*PROTO=(\w+).*DPT=(\d+)/) {
+ my $ip = $1;
+ my $dst = $2;
+ my $proto = $3;
+ my $port = $4;
+ $ip =~ s/^::ffff://;
+ if ($config{PS_PORTS} !~ /BRD/ and $proto eq "UDP" and $brd{$dst} and !$ips{$dst}) {return}
+ if ($config{PS_PORTS} !~ /OPEN/) {
+ my $hit = 0;
+ if ($proto eq "TCP" and $line =~ /kernel:\s(\[[^\]]+\]\s)?Firewall: \*TCP_IN Blocked\*/) {
+ foreach my $ports (split(/\,/,$config{TCP_IN})) {
+ if ($ports =~ /\:/) {
+ my ($start,$end) = split(/\:/,$ports);
+ if ($port >= $start and $port <= $end) {$hit = 1}
+ }
+ elsif ($port == $ports) {$hit = 1}
+ if ($hit) {last}
+ }
+ if ($hit) {
+ if ($config{DEBUG} >= 1) {ConfigServer::Logger::logfile("debug: *Port Scan* ignored TCP_IN port: $ip:$port")}
+ return;
+ }
+ }
+ elsif ($proto eq "UDP" and $line =~ /kernel:\s(\[[^\]]+\]\s)?Firewall: \*UDP_IN Blocked\*/) {
+ foreach my $ports (split(/\,/,$config{UDP_IN})) {
+ if ($ports =~ /\:/) {
+ my ($start,$end) = split(/\:/,$ports);
+ if ($port >= $start and $port <= $end) {$hit = 1}
+ }
+ elsif ($port == $ports) {$hit = 1}
+ if ($hit) {last}
+ }
+ if ($hit) {
+ if ($config{DEBUG} >= 1) {ConfigServer::Logger::logfile("debug: *Port Scan* ignored UDP_IN port: $ip:$port")}
+ return;
+ }
+ }
+ }
+ if (checkip(\$ip)) {return ($ip,$port)} else {return}
+ }
+ if ($line =~ /IN=\S+.*SRC=(\S+).*PROTO=(ICMP)/) {
+ my $ip = $1;
+ my $port = $2;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ($ip,$port)} else {return}
+ }
+ if ($line =~ /IN=\S+.*SRC=(\S+).*PROTO=(ICMPv6)/) {
+ my $ip = $1;
+ my $port = $2;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ($ip,$port)} else {return}
+ }
+}
+# end pslinecheck
+###############################################################################
+# start uidlinecheck
+sub uidlinecheck {
+ my $line = shift;
+ if ($line !~ /^(\S+|\S+\s+\d+\s+\S+) \S+ kernel(\[\d+\])?:\s(\[[^\]]+\]\s)?Firewall:/) {return}
+ if ($line =~ /OUT=\S+.*DPT=(\S+).*UID=(\d+)/) {return ($1,$2)}
+}
+# end uidlinecheck
+###############################################################################
+# start portknockingcheck
+sub portknockingcheck {
+ my $line = shift;
+ if ($line !~ /^(\S+|\S+\s+\d+\s+\S+) \S+ kernel(\[\d+\])?:\s(\[[^\]]+\]\s)?Knock: \*\d+_IN\*/) {return}
+
+ if ($line =~ /SRC=(\S+).*DPT=(\d+)/) {
+ my $ip = $1;
+ my $port = $2;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ($ip,$port)} else {return}
+ }
+}
+# end portknockingcheck
+###############################################################################
+# start processdistftpline
+sub processdistftpline {
+ my $line = shift;
+#pure-ftpd
+ if ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ pure-ftpd(\[\d+\])?: \(\?\@(\S+)\) \[INFO\] (\S*) is now logged in$/) {
+ my $ip = $3;
+ my $acc = $4;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ($ip,$acc)} else {return}
+ }
+#proftpd
+ if ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ proftpd\[\d+\]: \S+ \([^\[]+\[(\S+)\]\) - USER (\S*): Login successful\.\s*$/) {
+ my $ip = $2;
+ my $acc = $3;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ($ip,$acc)} else {return}
+ }
+#InterWorx proftpd
+ if ($line =~ /^\S+ \S+ \S+ proftpd\[\d+\]:? \S+ \(\S+?[^\[]+\[(\S+)\]\)( -)?:? USER (\S*): Login successful/) {
+ my $ip = $1;
+ my $acc = $3;
+ $ip =~ s/^::ffff://;
+ if (checkip(\$ip)) {return ($ip,$acc)} else {return}
+ }
+}
+# end processdistftpline
+###############################################################################
+# start processdistsmtpline
+sub processdistsmtpline {
+ my $line = shift;
+ my $tline = $line;
+ $tline =~ s/".*"/""/g;
+ my @bits =split(/\s+/,$tline);
+ my $ip;
+
+#postfix
+ if ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ postfix\/(submission\/)?smtpd(?:\[\d+\])?: \w+: client=\S+\[(\S+)\], sasl_method=(?:(?i)LOGIN|PLAIN|(?:CRAM|DIGEST)-MD5), sasl_username=(\S+)$/) {
+ $ip = $3; my $account = $4; $ip =~ s/^::ffff://;
+ if (checkip(\$ip) and $ip ne "127.0.0.1" and $ip ne "::1") {return ($ip,$account)} else {return}
+ }
+
+#InterWorx qmail
+ if ($line =~ /^\S+ qmail-smtpd\[\d+\]: AUTH successful \[(\S+)\] (\S+)/) {
+ $ip = $1; my $account = $2; $ip =~ s/^::ffff://;
+ if (checkip(\$ip) and $ip ne "127.0.0.1" and $ip ne "::1") {return ($ip,$account)} else {return}
+ }
+
+#exim
+ if ($tline !~ /^\S+\s+\S+\s+(\[\d+\]\s)?\S+ <=/) {return}
+
+ if ($tline =~ / U=(\S+) P=local /) {return}
+
+ if ($tline =~ / H=[^=]*\[(\S+)\]/) {
+ $ip = $1;
+ unless (checkip(\$ip) or $ip eq "127.0.0.1" or $ip eq "::1") {return}
+ } else {
+ return;
+ }
+
+ if (($tline =~ / A=(courier_plain|courier_login|dovecot_plain|dovecot_login|fixed_login|fixed_plain|login|plain):(\S+)/)){
+ my $account = $2;
+ if (($tline =~ / P=(esmtpa|esmtpsa) /)) {return ($ip, $account)}
+ }
+}
+# end processdistsmtpline
+###############################################################################
+# start loginline404
+sub loginline404 {
+ my $line = shift;
+ if ($line =~ /^\[\S+\s+\S+\s+\S+\s+\S+\s+\S+\] \[(\S*:)?(error|info)\] (\[pid \d+(:tid \d+)?\] )?\[(client|remote) (\S+)\] (\w+: )?File does not exist\:/) {
+ my $ip = $6;
+ $ip =~ s/^::ffff://;
+ if ($config{LF_APACHE_ERRPORT} == 2 and $ip =~ /(.*):\d+$/) {$ip = $1}
+ if (checkip(\$ip)) {return ($ip)} else {return}
+ }
+}
+# end loginline404
+###############################################################################
+# start loginline403
+sub loginline403 {
+ my $line = shift;
+ if ($line =~ /^\[\S+\s+\S+\s+\S+\s+\S+\s+\S+\] \[(\S*:)?error\] (\[pid \d+(:tid \d+)?\] )?\[(client|remote) (\S+)\] (\w+: )?client denied by server configuration\:/) {
+ my $ip = $5;
+ $ip =~ s/^::ffff://;
+ if ($config{LF_APACHE_ERRPORT} == 2 and $ip =~ /(.*):\d+$/) {$ip = $1}
+ if (checkip(\$ip)) {return ($ip)} else {return}
+ }
+}
+# end loginline403
+###############################################################################
+# start loginline401
+sub loginline401 {
+ my $line = shift;
+ if ($line =~ /^\[\S+\s+\S+\s+\S+\s+\S+\s+\S+\] \[(\S*:)?error\] (\[pid \d+(:tid \d+)?\] )?\[(client|remote) (\S+)\] (\w+: )?(user not found|user \w+ not found|user \w+: authentication failure for "\/\w+\/")\:/) {
+ my $ip = $5;
+ $ip =~ s/^::ffff://;
+ if ($config{LF_APACHE_ERRPORT} == 2 and $ip =~ /(.*):\d+$/) {$ip = $1}
+ if (checkip(\$ip)) {return ($ip)} else {return}
+ }
+}
+# end loginline401
+###############################################################################
+# start statscheck
+sub statscheck {
+ my $line = shift;
+ if ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ kernel:\s(\[[^\]]+\]\s)?(Firewall|Knock):/) {return 1}
+}
+# end statscheck
+###############################################################################
+# start syslogcheckline
+sub syslogcheckline {
+ my $line = shift;
+ my $syslogcheckcode = shift;
+ if ($line =~ /^(\S+|\S+\s+\d+\s+\S+) \S+ lfd\[\d+\]: SYSLOG check \[(\S+)\]\s*$/) {
+ if ($2 eq $syslogcheckcode) {return 1} else {return}
+ }
+}
+# end syslogcheckline
+###############################################################################
+
+1;
diff --git a/csf/ConfigServer/Sanity.pm b/csf/ConfigServer/Sanity.pm
new file mode 100644
index 0000000..dc2fc12
--- /dev/null
+++ b/csf/ConfigServer/Sanity.pm
@@ -0,0 +1,86 @@
+###############################################################################
+# Copyright (C) 2006-2025 Jonathan Michaelson
+#
+# https://github.com/waytotheweb/scripts
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, see .
+###############################################################################
+## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
+# start main
+package ConfigServer::Sanity;
+
+use strict;
+use lib '/usr/local/csf/lib';
+use Fcntl qw(:DEFAULT :flock);
+use Carp;
+use ConfigServer::Config;
+
+use Exporter qw(import);
+our $VERSION = 1.02;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw(sanity);
+
+my %sanity;
+my %sanitydefault;
+my $sanityfile = "/usr/local/csf/lib/sanity.txt";
+
+open (my $IN, "<", $sanityfile);
+flock ($IN, LOCK_SH);
+my @data = <$IN>;
+close ($IN);
+chomp @data;
+foreach my $line (@data) {
+ my ($name,$value,$def) = split(/\=/,$line);
+ $sanity{$name} = $value;
+ $sanitydefault{$name} = $def;
+}
+
+my $config = ConfigServer::Config->loadconfig();
+my %config = $config->config();
+
+if ($config{IPSET}) {
+ delete $sanity{"DENY_IP_LIMIT"};
+ delete $sanitydefault{"DENY_IP_LIMIT"};
+}
+
+# end main
+###############################################################################
+# start sanity
+sub sanity {
+ my $sanity_item = shift;
+ my $sanity_value = shift;
+ my $insane = 0;
+
+ $sanity_item =~ s/\s//g;
+ $sanity_value =~ s/\s//g;
+
+ if (defined $sanity{$sanity_item}) {
+ $insane = 1;
+ foreach my $check (split(/\|/,$sanity{$sanity_item})) {
+ if ($check =~ /-/) {
+ my ($from,$to) = split(/\-/,$check);
+ if (($sanity_value >= $from) and ($sanity_value <= $to)) {$insane = 0}
+
+ } else {
+ if ($sanity_value eq $check) {$insane = 0}
+ }
+ }
+ $sanity{$sanity_item} =~ s/\|/ or /g;
+ }
+ return ($insane,$sanity{$sanity_item},$sanitydefault{$sanity_item});
+}
+# end sanity
+###############################################################################
+
+1;
\ No newline at end of file
diff --git a/csf/ConfigServer/Sendmail.pm b/csf/ConfigServer/Sendmail.pm
new file mode 100644
index 0000000..8829440
--- /dev/null
+++ b/csf/ConfigServer/Sendmail.pm
@@ -0,0 +1,179 @@
+###############################################################################
+# Copyright (C) 2006-2025 Jonathan Michaelson
+#
+# https://github.com/waytotheweb/scripts
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, see .
+###############################################################################
+## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
+# start main
+package ConfigServer::Sendmail;
+
+use strict;
+use lib '/usr/local/csf/lib';
+use Carp;
+use POSIX qw(strftime);
+use Fcntl qw(:DEFAULT :flock);
+use ConfigServer::Config;
+use ConfigServer::CheckIP qw(checkip);
+
+use Exporter qw(import);
+our $VERSION = 1.02;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw();
+
+my $config = ConfigServer::Config->loadconfig();
+my %config = $config->config();
+my $tz = strftime("%z", localtime);
+my $hostname;
+if (-e "/proc/sys/kernel/hostname") {
+ open (my $IN, "<", "/proc/sys/kernel/hostname");
+ flock ($IN, LOCK_SH);
+ $hostname = <$IN>;
+ chomp $hostname;
+ close ($IN);
+} else {
+ $hostname = "unknown";
+}
+
+if ($config{LF_ALERT_SMTP}) {
+ require Net::SMTP;
+ import Net::SMTP;
+}
+
+# end main
+###############################################################################
+# start sendmail
+sub relay {
+ my ($to, $from, @message) = @_;
+ my $time = localtime(time);
+ if ($to eq "") {$to = $config{LF_ALERT_TO}} else {$config{LF_ALERT_TO} = $to}
+ if ($from eq "") {$from = $config{LF_ALERT_FROM}} else {$config{LF_ALERT_FROM} = $from}
+ my $data;
+
+ if ($from =~ /([\w\.\=\-\_]+\@[\w\.\-\_]+)/) {$from = $1}
+ if ($from eq "") {$from = "root"}
+ if ($to =~ /([\w\.\=\-\_]+\@[\w\.\-\_]+)/) {$to = $1}
+ if ($to eq "") {$to = "root"}
+
+ my $header = 1;
+ foreach my $line (@message) {
+ $line =~ s/\r//;
+ if ($line eq "") {$header = 0}
+ $line =~ s/\[time\]/$time $tz/ig;
+ $line =~ s/\[hostname\]/$hostname/ig;
+ if ($header) {
+ if ($line =~ /^To:\s*(.*)\s*$/i) {
+ my $totxt = $1;
+ if ($config{LF_ALERT_TO} ne "") {
+ $line =~ s/^To:.*$/To: $config{LF_ALERT_TO}/i;
+ } else {
+ $to = $totxt;
+ }
+ }
+ if ($line =~ /^From:\s*(.*)\s*$/i) {
+ my $fromtxt = $1;
+ if ($config{LF_ALERT_FROM} ne "") {
+ $line =~ s/^From:.*$/From: $config{LF_ALERT_FROM}/i;
+ } else {
+ $from = $1;
+ }
+ }
+ }
+ $data .= $line."\n";
+ }
+
+ $data = &wraptext($data, 990);
+
+ if ($config{LF_ALERT_SMTP}) {
+ if ($from !~ /\@/) {$from .= '@'.$hostname}
+ if ($to !~ /\@/) {$to .= '@'.$hostname}
+ my $smtp = Net::SMTP->new($config{LF_ALERT_SMTP}, Timeout => 10) or carp("Unable to send SMTP alert via [$config{LF_ALERT_SMTP}]: $!");
+ if (defined $smtp) {
+ $smtp->mail($from);
+ $smtp->to($to);
+ $smtp->data();
+ $smtp->datasend($data);
+ $smtp->dataend();
+ $smtp->quit();
+ }
+ } else {
+ local $SIG{CHLD} = 'DEFAULT';
+ my $error = 0;
+ open (my $MAIL, "|-", "$config{SENDMAIL} -f $from -t") or carp("Unable to send SENDMAIL alert via [$config{SENDMAIL}]: $!");
+ print $MAIL $data;
+ close ($MAIL) or $error = 1;
+ if ($error and $config{DEBUG}) {
+ logfile("Failed to send message via sendmail binary: $?");
+ logfile("Failed message: [$data]");
+ }
+ }
+
+ return;
+}
+# end sendmail
+###############################################################################
+# start wraptext
+sub wraptext {
+ my $text = shift;
+ my $column = shift;
+ my $original = $text;
+ my $return = "";
+ my $hit = 1;
+ my $loop = 0;
+ while ($hit) {
+ $hit = 0;
+ $return = "";
+ foreach my $line (split(/\n/, $text)) {
+ if (length($line) > $column) {
+ foreach ($line =~ /(.{1,$column})/g) {
+ my $chunk = $_;
+ my $newchunk = "";
+ my $thishit = 0;
+ my @chars = split(//,$chunk);
+ for (my $x = length($chunk)-1;$x >= 0; $x--) {
+ if ($chars[$x] =~ /\s/) {
+ for (0..$x) {$newchunk .= $chars[$_]}
+ $newchunk .= "\n";
+ for ($x+1..length($chunk)-1) {$newchunk .= $chars[$_]}
+ $thishit = 1;
+ last;
+ }
+ }
+ if ($thishit) {
+ $hit = 1;
+ $thishit = 0;
+ $return .= $newchunk;
+ } else {
+ $return .= $chunk."\n";
+ }
+ }
+ } else {
+ $return .= $line."\n";
+ }
+ }
+ $text = $return;
+ $loop++;
+ if ($loop > 1000) {
+ return $original;
+ last;
+ }
+ }
+ if (length($return) < length($original)) {$return = $original}
+ return $return;
+}
+# end wraptext
+###############################################################################
+
+1;
\ No newline at end of file
diff --git a/csf/ConfigServer/ServerCheck.pm b/csf/ConfigServer/ServerCheck.pm
new file mode 100644
index 0000000..af9283a
--- /dev/null
+++ b/csf/ConfigServer/ServerCheck.pm
@@ -0,0 +1,1775 @@
+###############################################################################
+# Copyright (C) 2006-2025 Jonathan Michaelson
+#
+# https://github.com/waytotheweb/scripts
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, see .
+###############################################################################
+## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
+# start main
+package ConfigServer::ServerCheck;
+
+use strict;
+use lib '/usr/local/csf/lib';
+use Fcntl qw(:DEFAULT :flock);
+use File::Basename;
+use IPC::Open3;
+use ConfigServer::Slurp qw(slurp);
+use ConfigServer::Sanity qw(sanity);;
+use ConfigServer::Config;
+use ConfigServer::GetIPs qw(getips);
+use ConfigServer::CheckIP qw(checkip);
+use ConfigServer::Service;
+use ConfigServer::GetEthDev;
+
+use Exporter qw(import);
+our $VERSION = 1.05;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw();
+
+my (%config, $cpconf, %daconfig, $cleanreg, $mypid, $childin, $childout,
+ $verbose, $cpurl, @processes, $total, $failures, $current, $DEBIAN,
+ $output, $sysinit, %g_ifaces, %g_ipv4, %g_ipv6);
+
+my $ipv4reg = ConfigServer::Config->ipv4reg;
+my $ipv6reg = ConfigServer::Config->ipv6reg;
+
+use Exporter qw(import);
+# end main
+###############################################################################
+# start report
+sub report {
+ $verbose = shift;
+ my $config = ConfigServer::Config->loadconfig();
+ %config = $config->config();
+ $cleanreg = ConfigServer::Slurp->cleanreg;
+ $| = 1;
+
+ if (defined $ENV{WEBMIN_VAR} and defined $ENV{WEBMIN_CONFIG}) {
+ $config{GENERIC} = 1;
+ $config{DIRECTADMIN} = 0;
+ }
+ elsif (-e "/usr/local/cpanel/version") {
+ use lib "/usr/local/cpanel";
+ require Cpanel::Form;
+ import Cpanel::Form;
+ require Cpanel::Config;
+ import Cpanel::Config;
+ $cpconf = Cpanel::Config::loadcpconf();
+ }
+ elsif (-e "/usr/local/directadmin/conf/directadmin.conf") {
+ my ($childin, $childout);
+ my $mypid = open3($childin, $childout, $childout, "/usr/local/directadmin/directadmin", "c");
+ my @data = <$childout>;
+ waitpid ($mypid, 0);
+ chomp @data;
+ foreach my $line (@data) {
+ my ($name,$value) = split(/\=/,$line);
+ $daconfig{lc($name)} = $value;
+ }
+ $config{DIRECTADMIN} = 1;
+ }
+ elsif (-e "/etc/psa/psa.conf") {
+ $config{PLESK} = 1;
+ }
+
+ $failures = 0;
+ $total = 0;
+ if ($ENV{cp_security_token}) {$cpurl = $ENV{cp_security_token}}
+ $DEBIAN = 0;
+ if (-e "/etc/lsb-release" or -e "/etc/debian_version") {$DEBIAN = 1}
+
+ $sysinit = ConfigServer::Service::type();
+ if ($sysinit ne "systemd") {$sysinit = "init"}
+
+ opendir (PROCDIR, "/proc");
+ while (my $pid = readdir(PROCDIR)) {
+ if ($pid !~ /^\d+$/) {next}
+ push @processes, readlink("/proc/$pid/exe");
+ }
+
+ my $ethdev = ConfigServer::GetEthDev->new();
+ %g_ifaces = $ethdev->ifaces;
+ %g_ipv4 = $ethdev->ipv4;
+ %g_ipv6 = $ethdev->ipv6;
+
+ &startoutput;
+
+ &firewallcheck;
+ &servercheck;
+ &sshtelnetcheck;
+ unless ($config{DNSONLY} or $config{GENERIC}) {&mailcheck}
+ unless ($config{DNSONLY} or $config{GENERIC}) {&apachecheck}
+ unless ($config{DNSONLY} or $config{GENERIC}) {&phpcheck}
+ unless ($config{DNSONLY} or $config{GENERIC}) {&whmcheck}
+ if ($config{DIRECTADMIN}) {
+ &mailcheck;
+ &apachecheck;
+ &phpcheck;
+ &dacheck;
+ }
+ &servicescheck;
+
+ &endoutput;
+ return $output;
+}
+# end report
+###############################################################################
+# start startoutput
+sub startoutput {
+ if ($config{THIS_UI} and !$config{GENERIC}) {
+ $output .= "Note: Internal WHM links will not work within the csf Integrated UI
\n";
+ }
+
+ return;
+}
+# end startoutput
+###############################################################################
+# start addline
+sub addline {
+ my $status = shift;
+ my $check = shift;
+ my $comment = shift;
+ $total++;
+
+ if ($status) {
+ $output .= "\n";
+ $output .= "
$check
\n";
+ $output .= "
$comment
\n";
+ $output .= "
\n";
+ $failures ++;
+ $current++;
+ }
+ elsif ($verbose) {
+ $output .= "\n";
+ $output .= "
$check
\n";
+ $output .= "
$comment
\n";
+ $output .= "
\n";
+ $current++;
+ }
+ return;
+}
+# end addline
+###############################################################################
+# start addtitle
+sub addtitle {
+ my $title = shift;
+ if (defined $current and $current == 0) {
+ $output .= "OK
\n";
+ }
+ $current = 0;
+ $output .= "$title
\n";
+ return;
+}
+# end addtitle
+###############################################################################
+# start endoutput
+sub endoutput {
+ if (defined $current and $current == 0) {
+ $output .= "OK
\n";
+ }
+ $output .= " \n";
+
+ my $gap = int(($total-3)/4);
+ my $score = ($total - $failures);
+ my $width = int ((400 / $total) * $score) - 4;
+ $output .= " \n\n\n";
+ $output .= "
Server Score: $score/$total* \n";
+ $output .= "
\n";
+ $output .= "
\n";
+ $output .= "\n";
+ $output .= " \n";
+ $output .= " \n";
+ $output .= " \n";
+ $output .= " $total (max) \n";
+ $output .= " \n";
+ $output .= "
\n";
+ $output .= "
\n";
+ $output .= "
\n";
+ $output .= "
\n";
+ $output .= "\n";
+ $output .= " \n";
+ $output .= " \n";
+ $output .= " $score (score) \n";
+ $output .= " \n";
+ $output .= "
\n";
+ $output .= "
\n";
+ $output .= "
* This scoring does not necessarily reflect the security of the server or the relative merits of each check
";
+ $output .= "
";
+ return;
+}
+# end endoutput
+###############################################################################
+# start firewallcheck
+sub firewallcheck {
+ &addtitle("Firewall Check");
+ my $status = 0;
+ open (my $IN, "<", "/etc/csf/csf.conf");
+ flock ($IN, LOCK_SH);
+ my @config = <$IN>;
+ chomp @config;
+
+ foreach my $line (@config) {
+ if ($line =~ /^\#/) {next}
+ if ($line !~ /=/) {next}
+ my ($name,$value) = split (/=/,$line,2);
+ $name =~ s/\s//g;
+ if ($value =~ /\"(.*)\"/) {
+ $value = $1;
+ } else {
+ &error(__LINE__,"Invalid configuration line");
+ }
+ $config{$name} = $value;
+ }
+
+ $status = 0;
+ if (-e "/etc/csf/csf.disable") {$status = 1}
+ &addline($status,"csf enabled check","csf is currently disabled and should be enabled otherwise it is not functioning");
+
+ if (-x $config{IPTABLES}) {
+ my ($childin, $childout);
+ my $mypid = open3($childin, $childout, $childout, "$config{IPTABLES} $config{IPTABLESWAIT} -L INPUT -n");
+ my @iptstatus = <$childout>;
+ waitpid ($mypid, 0);
+ chomp @iptstatus;
+ if ($iptstatus[0] =~ /# Warning: iptables-legacy tables present/) {shift @iptstatus}
+ $status = 0;
+ if ($iptstatus[0] =~ /policy ACCEPT/) {$status = 1}
+ &addline($status,"csf running check","iptables is not configured. You need to start csf");
+ }
+
+ $status = 0;
+ if ($config{TESTING}) {$status = 1}
+ &addline($status,"TESTING mode check","csf is in TESTING mode. If the firewall is working set TESTING to \"0\" in the Firewall Configuration otherwise it will continue to be stopped");
+
+ $status = 0;
+ unless ($config{RESTRICT_SYSLOG}) {$status = 1}
+ &addline($status,"RESTRICT_SYSLOG option check","Due to issues with syslog/rsyslog you should consider enabling this option. See the Firewall Configuration (/etc/csf/csf.conf) for more information");
+
+ $status = 0;
+ unless ($config{AUTO_UPDATES}) {$status = 1}
+ &addline($status,"AUTO_UPDATES option check","To keep csf up to date and secure you should enable AUTO_UPDATES. You should also monitor our blog ");
+
+ $status = 0;
+ unless ($config{LF_DAEMON}) {$status = 1}
+ &addline($status,"lfd enabled check","lfd is disabled in the csf configuration which limits the affectiveness of this application");
+
+ $status = 0;
+ if ($config{TCP_IN} =~ /\b3306\b/) {$status = 1}
+ &addline($status,"Incoming MySQL port check","The TCP incoming MySQL port (3306) is open. This can pose both a security and server abuse threat since not only can hackers attempt to break into MySQL, any user can host their SQL database on your server and access it from another host and so (ab)use your server resources");
+
+ unless ($config{DNSONLY} or $config{GENERIC}) {
+ unless ($config{VPS}) {
+ $status = 0;
+ unless ($config{SMTP_BLOCK}) {$status = 1}
+ &addline($status,"SMTP_BLOCK option check","This option will help prevent the most common form of spam abuse on a server that bypasses exim and sends spam directly out through port 25. Enabling this option will prevent any web script from sending out using socket connection, such scripts should use the exim or sendmail binary instead");
+ }
+
+ $status = 0;
+ unless ($config{LF_SCRIPT_ALERT}) {$status = 1}
+ &addline($status,"LF_SCRIPT_ALERT option check","This option will notify you when a large amount of email is sent from a particular script on the server, helping track down spam scripts");
+ }
+
+ $status = 0;
+ my @options = ("LF_SSHD","LF_FTPD","LF_SMTPAUTH","LF_POP3D","LF_IMAPD","LF_HTACCESS","LF_MODSEC","LF_CPANEL","LF_CPANEL_ALERT","SYSLOG_CHECK","RESTRICT_UI");
+ if ($config{GENERIC}) {@options = ("LF_SSHD","LF_FTPD","LF_SMTPAUTH","LF_POP3D","LF_IMAPD","LF_HTACCESS","LF_MODSEC","SYSLOG_CHECK","FASTSTART","RESTRICT_UI");}
+ if ($config{DNSONLY}) {@options = ("LF_SSHD","LF_CPANEL","SYSLOG_CHECK","FASTSTART","RESTRICT_UI")}
+
+ foreach my $option (@options) {
+ $status = 0;
+ unless ($config{$option}) {$status = 1}
+ &addline($status,"$option option check","This option helps prevent brute force attacks on your server services or overall server stability");
+ }
+
+ $status = 0;
+ unless ($config{LF_DIRWATCH}) {$status = 1}
+ &addline($status,"LF_DIRWATCH option check","This option will notify when a suspicious file is found in one of the common temp directories on the server");
+
+ $status = 0;
+ unless ($config{LF_INTEGRITY}) {$status = 1}
+ &addline($status,"LF_INTEGRITY option check","This option will notify when an executable in one of the common directories on the server changes in some way. This helps alert you to potential rootkit installation or server compromise");
+
+ $status = 0;
+ unless ($config{FASTSTART}) {$status = 1}
+ &addline($status,"FASTSTART option check","This option can dramatically improve the startup time of csf and the rule loading speed of lfd");
+
+ $status = 0;
+ if ($config{URLGET} == 1) {$status = 1}
+ &addline($status,"URLGET option check","This option determines which perl module is used to upgrade csf. It is recommended to set this to use LWP rather than HTTP::Tiny so that upgrades are performed over an SSL connection");
+
+ $status = 0;
+ if ($config{PT_USERKILL} == 1) {$status = 1}
+ &addline($status,"PT_USERKILL option check","This option should not normally be enabled as it can easily lead to legitimate processes being terminated, use csf.pignore instead");
+
+ unless ($config{DNSONLY} or $config{GENERIC}) {
+ $status = 0;
+ if ($config{PT_SKIP_HTTP}) {$status = 1}
+ &addline($status,"PT_SKIP_HTTP option check","This option disables checking of processes running under apache and can limit false-positives but may then miss running exploits");
+ }
+
+ $status = 0;
+ if (!$config{LF_IPSET} and !$config{VPS} and ($config{CC_DENY} or $config{CC_ALLOW} or $config{CC_ALLOW_FILTER} or $config{CC_ALLOW_PORTS} or $config{CC_DENY_PORTS})) {$status = 1}
+ &addline($status,"LF_IPSET option check","If support by your OS, you should install ipset and enable LF_IPSET when using Country Code (CC_*) filters");
+
+ unless ($config{DNSONLY} or $config{GENERIC}) {
+ $status = 0;
+ unless ($config{PT_ALL_USERS}) {$status = 1}
+ &addline($status,"PT_ALL_USERS option check","This option ensures that almost all Linux accounts are checked with Process Tracking, not just the cPanel ones");
+ }
+
+ sysopen (my $CONF, "/etc/csf/csf.conf", O_RDWR | O_CREAT);
+ flock ($CONF, LOCK_SH);
+ my @confdata = <$CONF>;
+ close ($CONF);
+ chomp @confdata;
+
+ foreach my $line (@confdata) {
+ if (($line !~ /^\#/) and ($line =~ /=/)) {
+ my ($start,$end) = split (/=/,$line,2);
+ my $name = $start;
+ $name =~ s/\s/\_/g;
+ if ($end =~ /\"(.*)\"/) {$end = $1}
+ my ($insane,$range,$default) = sanity($start,$end);
+ if ($insane) {
+ &addline(1,"$start sanity check","$start = $end. Recommended range: $range (Default: $default)");
+ }
+ }
+ }
+ return;
+}
+# end firewallcheck
+###############################################################################
+# start servercheck
+sub servercheck {
+ &addtitle("Server Check");
+ my $status = 0;
+
+ my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = stat("/tmp");
+ my $pmode = sprintf "%03o", $mode & oct("07777");
+
+ $status = 0;
+ if ($pmode != 1777) {$status = 1}
+ &addline($status,"Check /tmp permissions","/tmp should be chmod 1777");
+
+ $status = 0;
+ if (($uid != 0) or ($gid != 0)) {$status = 1}
+ &addline($status,"Check /tmp ownership","/tmp should be owned by root:root");
+
+ if (-d "/var/tmp") {
+ ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = stat("/var/tmp");
+ $pmode = sprintf "%04o", $mode & oct("07777");
+
+ $status = 0;
+ if ($pmode != 1777) {$status = 1}
+ &addline($status,"Check /var/tmp permissions","/var/tmp should be chmod 1777");
+
+ $status = 0;
+ if (($uid != 0) or ($gid != 0)) {$status = 1}
+ &addline($status,"Check /var/tmp ownership","/var/tmp should be owned by root:root");
+ }
+
+ if (-d "/usr/tmp") {
+ ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = stat("/usr/tmp");
+ $pmode = sprintf "%04o", $mode & oct("07777");
+
+ $status = 0;
+ if ($pmode != 1777) {$status = 1}
+ &addline($status,"Check /usr/tmp permissions","/usr/tmp should be chmod 1777");
+
+ $status = 0;
+ if (($uid != 0) or ($gid != 0)) {$status = 1}
+ &addline($status,"Check /usr/tmp ownership","/usr/tmp should be owned by root:root");
+ }
+
+ $status = 0;
+ if (&getportinfo(53)) {
+ my @files = ("/var/named/chroot/etc/named.conf","/etc/named.conf","/etc/bind/named.conf","/var/named/chroot/etc/bind/named.conf");
+ my @namedconf;
+ my @morefiles;
+ my $hit;
+ foreach my $file (@files) {
+ if (-e $file) {
+ $hit = 1;
+ open (my $IN, "<", "$file");
+ flock ($IN, LOCK_SH);
+ my @conf = <$IN>;
+ close ($IN);
+ chomp @conf;
+ if (my @ls = grep {$_ =~ /^\s*include\s+(.*)\;\s*$/i} @conf) {
+ foreach my $more (@ls) {
+ if ($more =~ /^\s*include\s+\"(.*)\"\s*\;\s*$/i) {push @morefiles, $1}
+ }
+ }
+ @namedconf = (@namedconf, @conf);
+ }
+ }
+ foreach my $file (@morefiles) {
+ if (-e $file) {
+ open (my $IN, "<", "$file");
+ flock ($IN, LOCK_SH);
+ my @conf = <$IN>;
+ close ($IN);
+ chomp @conf;
+ @namedconf = (@namedconf, @conf);
+ }
+ }
+
+ if ($hit) {
+# if (my @ls = grep {$_ =~ /^\s*(recursion\s+no|allow-recursion)/} @namedconf) {$status = 0} else {$status = 1}
+# &addline($status,"Check for DNS recursion restrictions","You have a local DNS server running but do not appear to have any recursion restrictions set. This is a security and performance risk and you should look at restricting recursive lookups to the local IP addresses only");
+
+ if (my @ls = grep {$_ =~ /^\s*(query-source\s[^\;]*53)/} @namedconf) {$status = 1} else {$status = 0}
+ &addline($status,"Check for DNS random query source port","ISC recommend that you do not configure BIND to use a static query port. You should remove/disable the query-source line that specifies port 53 from the named configuration files");
+ }
+ }
+
+ if (!$DEBIAN and $sysinit eq "init" and -x "/sbin/runlevel") {
+ $status = 0;
+ $mypid = open3($childin, $childout, $childout, "/sbin/runlevel");
+ my @conf = <$childout>;
+ waitpid ($mypid, 0);
+ chomp @conf;
+ my (undef,$runlevel) = split(/\s/,$conf[0]);
+ if ($runlevel != 3) {$status = 1}
+ &addline($status,"Check server runlevel","The servers runlevel is currently set to $runlevel. For a secure server environment you should only run the server at runlevel 3. You can fix this by editing /etc/inittab and changing the initdefault line to:id:3:initdefault: and then rebooting the server");
+ }
+
+ $status = 0;
+ if ((-e "/var/spool/cron/nobody") and !(-z "/var/spool/cron/nobody")) {$status = 1}
+ &addline($status,"Check nobody cron","You have a nobody cron log file - you should check that this has not been created by an exploit");
+
+ $status = 0;
+ my ($isfedora, $isrh, $version, $conf) = 0;
+ if (-e "/etc/fedora-release") {
+ open (my $IN, "<", "/etc/fedora-release");
+ flock ($IN, LOCK_SH);
+ $conf = <$IN>;
+ close ($IN);
+ $isfedora = 1;
+ if ($conf =~ /release (\d+)/i) {$version = $1}
+ } elsif (-e "/etc/redhat-release") {
+ open (my $IN, "<", "/etc/redhat-release");
+ flock ($IN, LOCK_SH);
+ $conf = <$IN>;
+ close ($IN);
+ $isrh = 1;
+ if ($conf =~ /release (\d+)/i) {$version = $1}
+ }
+ chomp $conf;
+
+ if ($isrh or $isfedora) {
+ if (($isfedora and $version < 30) or ($isrh and $version < 6)) {$status = 1}
+ &addline($status,"Check Operating System support","You are running an OS - $conf - that is no longer supported by the OS vendor, or is about to become obsolete. This means that you will be receiving no OS updates (i.e. application or security bug fixes) or kernel updates and should consider moving to an OS that is supported as soon as possible");
+ }
+
+ $status = 0;
+ if ($] < 5.008008) {
+ $status = 1;
+ } else {$status = 0}
+ &addline($status,"Check perl version","The version of perl (v$]) is out of date and you should upgrade it");
+
+ $status = 0;
+ while (my ($name,undef,$uid) = getpwent()) {
+ if (($uid == 0) and ($name ne "root")) {$status = 1}
+ }
+ &addline($status,"Check SUPERUSER accounts","You have accounts other than root set up with UID 0. This is a considerable security risk. You should use su , or best of all sudo for such access");
+
+ if (-e "/usr/local/cpanel/version" or $config{DIRECTADMIN}) {
+ $status = 0;
+ unless (-e "/etc/cxs/cxs.pl") {
+ $status = 1;
+ }
+ &addline($status,"Check for cxs","You should consider using cxs to scan web script uploads and user accounts for exploits uploaded to the server");
+ $status = 0;
+ unless (-e "/etc/osm/osmd.pl") {
+ $status = 1;
+ }
+ &addline($status,"Check for osm","You should consider using osm to provide protection from spammers exploiting the server");
+ }
+
+ unless ($config{IPV6}) {
+ $status = 0;
+ my $ipv6 = "";
+ foreach my $key (keys %g_ipv6) {
+ if ($ipv6) {$ipv6 .= ", "}
+ $ipv6 .= $key;
+ $status = 1;
+ }
+ if ($ipv6 eq "::1") {$ipv6 = ""; $status = 0}
+ &addline($status,"Check for IPv6","IPv6 appears to be enabled [$ipv6 ]. If ip6tables is installed, you should enable the csf IPv6 firewall (IPV6 in csf.conf)");
+ }
+
+ if ($sysinit eq "init") {
+ $status = 1;
+ my $syslog = 0;
+ if (grep {$_ =~ /\/syslogd\s*/} @processes) {
+ $syslog = 1;
+ if (grep {$_ =~ /\/klogd$/} @processes) {$status = 0}
+ &addline($status,"Check for kernel logger","syslogd appears to be running, but not klogd which logs kernel firewall messages to syslog. You should ensure that klogd is running");
+ }
+ if (grep {$_ =~ /\/rsyslogd\s*/} @processes) {
+ $syslog = 1;
+ if (grep {$_ =~ /\/rklogd\s*/} @processes) {
+ $status = 0;
+ } else {
+ open (my $IN, "<", "/etc/rsyslog.conf");
+ flock ($IN, LOCK_SH);
+ my @conf = <$IN>;
+ close ($IN);
+ chomp @conf;
+ if (grep {$_ =~ /^\$ModLoad imklog/} @conf) {$status = 0}
+ }
+ &addline($status,"Check for kernel logger","rsyslogd appears to be running, but klog may not be loaded which logs kernel firewall messages to rsyslog. You should modify /etc/rsyslogd to load the klog module with:\$ModLoad imklog Then restart rsyslog");
+ }
+ unless ($syslog) {
+ $status = 1;
+ &addline($status,"Check for syslog or rsyslog","Neither syslog nor rsyslog appear to be running");
+ }
+ }
+
+ $status = 0;
+ if (grep {$_ =~ /\/dhclient\s*/} @processes) {$status = 1}
+ &addline($status,"Check for dhclient","dhclient appears to be running which suggests that the server is obtaining an IP address via DHCP. This can pose a security risk. You should configure static IP addresses for all ethernet controllers");
+
+ unless ($config{VPS}) {
+ $status = 1;
+ open (my $IN, "<", "/proc/swaps");
+ flock ($IN, LOCK_SH);
+ my @swaps = <$IN>;
+ close ($IN);
+ if (scalar(@swaps) > 1) {$status = 0}
+ &addline($status,"Check for swap file","The server appears to have no swap file. This is usually considered a stability and performance risk. You should either add a swap partition, or create one via a normal file on an existing partition ");
+
+ if (-e "/etc/redhat-release") {
+ open (my $IN, "<", "/etc/redhat-release");
+ flock ($IN, LOCK_SH);
+ $conf = <$IN>;
+ close ($IN);
+ chomp $conf;
+
+ if ($conf =~ /^CloudLinux/i) {
+ $status = 0;
+ if (-e "/usr/sbin/cagefsctl") {
+ } else {$status = 1}
+ &addline($status,"CloudLinux CageFS","CloudLinux CageFS is not installed. This CloudLinux option greatly improves server security on we servers by separating user accounts into their own environment");
+
+ unless ($status) {
+ $status = 0;
+ $mypid = open3($childin, $childout, $childout, "/usr/sbin/cagefsctl","--cagefs-status");
+ my @conf = <$childout>;
+ waitpid ($mypid, 0);
+ chomp @conf;
+ if ($conf[0] !~ /^Enabled/) {$status = 1}
+ &addline($status,"CloudLinux CageFS Enabled","CloudLinux CageFS is not enabled. This CloudLinux option greatly improves server security on we servers by separating user accounts into their own environment");
+ }
+
+ $status = 0;
+ open (my $ENFORCE_SYMLINKSIFOWNER, "<", "/proc/sys/fs/enforce_symlinksifowner");
+ flock ($ENFORCE_SYMLINKSIFOWNER, LOCK_SH);
+ $conf = <$ENFORCE_SYMLINKSIFOWNER>;
+ close ($ENFORCE_SYMLINKSIFOWNER);
+ chomp $conf;
+ if ($conf < 1) {$status = 1}
+ &addline($status,"CloudLinux Symlink Protection","CloudLinux Symlink Protection is not configured. You should configure it in /etc/sysctl.conf to prevent symlink attacks on web servers");
+
+ $status = 0;
+ open (my $PROC_CAN_SEE_OTHER_UID, "<", "/proc/sys/fs/proc_can_see_other_uid");
+ flock ($PROC_CAN_SEE_OTHER_UID, LOCK_SH);
+ $conf = <$PROC_CAN_SEE_OTHER_UID>;
+ close ($PROC_CAN_SEE_OTHER_UID);
+ chomp $conf;
+ if ($conf > 0) {$status = 1}
+ &addline($status,"CloudLinux Virtualised /proc","CloudLinux Virtualised /proc is not configured. You should configure it in /etc/sysctl.conf to prevent users accessing server resources that they do not need on web servers");
+
+ $status = 0;
+ open (my $USER_PTRACE, "<", "/proc/sys/kernel/user_ptrace");
+ flock ($USER_PTRACE, LOCK_SH);
+ $conf = <$USER_PTRACE>;
+ close ($USER_PTRACE);
+ chomp $conf;
+ if ($conf > 0) {$status = 1}
+ &addline($status,"CloudLinux Disable ptrace","CloudLinux Disable ptrace is not configured. You should configure it in /etc/sysctl.conf to prevent users accessing server resources that they do not need on web servers");
+ }
+ }
+ }
+ return;
+}
+# end servercheck
+###############################################################################
+# start whmcheck
+sub whmcheck {
+ my $status = 0;
+ &addtitle("WHM Settings Check");
+
+ $status = 0;
+ unless ($cpconf->{alwaysredirecttossl}) {$status = 1}
+ &addline($status,"Check cPanel login is SSL only","You should check WHM > Tweak Settings > Choose the closest matched domain for which that the system has a valid certificate when redirecting from non-SSL to SSL URLs ");
+
+ $status = 0;
+ unless ($cpconf->{skipboxtrapper}) {$status = 1}
+ &addline($status,"Check boxtrapper is disabled","Having boxtrapper enabled can very easily lead to your server being listed in common RBLs and usually has the effect of increasing the overall spam load, not reducing it. You should disable it in WHM > Tweak Settings > BoxTrapper Spam Trap ");
+
+ $status = 0;
+ if (-e "/var/cpanel/greylist/enabled") {$status = 1}
+ &addline($status,"Check GreyListing is disabled","Using GreyListing can and will lead to lost legitimate emails. It can also cause significant problems with \"password verification\" systems. See here for more information");
+
+ if (defined $cpconf->{popbeforesmtp}) {
+ $status = 0;
+ if ($cpconf->{popbeforesmtp}) {$status = 1}
+ &addline($status,"Check popbeforesmtp is disabled","Using pop before smtp is considered a security risk, SMTP AUTH should be used instead. You should disable it in WHM > Tweak Settings > Allow users to relay mail if they use an IP address through which someone has validated an IMAP or POP3 login ");
+ }
+
+ $status = 0;
+ unless ($cpconf->{maxemailsperhour}) {$status = 1}
+ &addline($status,"Check max emails per hour is set","To limit the damage that can be caused by potential spammers on the server you should set a value for WHM > Tweak Settings > Max hourly emails per domain ");
+
+ $status = 0;
+ if ($cpconf->{resetpass}) {$status = 1}
+ &addline($status,"Check Reset Password for cPanel accounts","This poses a potential security risk and should be disabled unless necessary in WHM > Tweak Settings > Reset Password for cPanel accounts ");
+
+ $status = 0;
+ if ($cpconf->{resetpass_sub}) {$status = 1}
+ &addline($status,"Check Reset Password for Subaccounts","This poses a potential security risk and should be disabled unless necessary in WHM > Tweak Settings > Reset Password for Subaccounts ");
+
+ foreach my $openid (glob "/var/cpanel/authn/openid_connect/*") {
+ open (my $IN, "<", $openid);
+ flock ($IN, LOCK_SH);
+ my $line = <$IN>;
+ close ($IN);
+ chomp $line;
+
+ my ($file, $filedir) = fileparse($openid);
+ $status = 0;
+ if ($line =~ /\{"cpanelid"/) {$status = 1}
+ &addline($status,"Check cPanelID for $file","You should only enable this option if you are going to use it otherwise it is a potential security risk in WHM > Manage External Authentications > $file ");
+ }
+
+ unless ($cpconf->{nativessl} eq undef) {
+ $status = 0;
+ unless ($cpconf->{nativessl}) {$status = 1}
+ &addline($status,"Check whether native cPanel SSL is enabled","You should enable this option so that lfd tracks SSL cpanel login attempts WHM > Tweak Settings > Use native SSL support if possible, negating need for Stunnel ");
+ }
+
+ $status = 0;
+ my $cc = '/usr/bin/cc';
+ while ( readlink($cc) ) {
+ $cc = readlink($cc);
+ }
+ if ( $cc !~ /^\// ) { $cc = '/usr/bin/' . $cc; }
+ my $mode = substr( sprintf( "%o", ( ( stat($cc) )[2] ) ), 2, 4 );
+ if ( $mode > 750 ) {$status = 1}
+ &addline($status,"Check compilers","You should disable compilers WHM > Security Center > Compilers Access ");
+
+ if (-e "/etc/pure-ftpd.conf" and ($cpconf->{ftpserver} eq "pure-ftpd") and !(-e "/etc/ftpddisable")) {
+ $status = 0;
+ open (my $IN, "<", "/etc/pure-ftpd.conf");
+ flock ($IN, LOCK_SH);
+ my @conf = <$IN>;
+ close ($IN);
+ chomp @conf;
+ if (my @ls = grep {$_ =~ /^\s*NoAnonymous\s*(no|off)/i} @conf) {$status = 1}
+ &addline($status,"Check Anonymous FTP Logins","Used as an attack vector by hackers and should be disabled unless actively used WHM > FTP Server Configuration > Allow Anonymous Logins > No ");
+ $status = 0;
+ if (my @ls = grep {$_ =~ /^\s*AnonymousCantUpload\s*(no|off)/i} @conf) {$status = 1}
+ &addline($status,"Check Anonymous FTP Uploads","Used as an attack vector by hackers and should be disabled unless actively used WHM > FTP Server Configuration > Allow Anonymous Uploads > No ");
+
+ $status = 0;
+ my $ciphers;
+ my $error;
+ if (my @ls = grep {$_ =~ /^\s*TLSCipherSuite/} @conf) {
+ if ($ls[0] =~ /TLSCipherSuite\s+(.*)$/) {$ciphers = $1}
+ $ciphers =~ s/\s*|\"|\'//g;
+ if ($ciphers eq "") {
+ $status = 1;
+ }
+ elsif ($ciphers !~ /SSL/) {
+ $status = 0
+ } else {
+ if (-x "/usr/bin/openssl") {
+ my ($childin, $childout);
+ my $cmdpid = open3($childin, $childout, $childout, "/usr/bin/openssl","ciphers","-v",$ciphers);
+ my @openssl = <$childout>;
+ waitpid ($cmdpid, 0);
+ chomp @openssl;
+ if (my @ls = grep {$_ =~ /error/i} @openssl) {$error = $openssl[0]; $status=2}
+ if (my @ls = grep {$_ =~ /SSLv2/} @openssl) {$status = 1}
+ }
+ }
+ } else {$status = 1}
+ if ($status == 2) {
+ &addline($status,"Check pure-ftpd weak SSL/TLS Ciphers (TLSCipherSuite)","Unable to determine cipher list for [$ciphers] from openssl: [$error]");
+ }
+ &addline($status,"Check pure-ftpd weak SSL/TLS Ciphers (TLSCipherSuite)","Cipher list [$ciphers]. Due to weaknesses in the SSLv2 cipher you should disable SSLv2 in WHM > FTP Server Configuration > TLS Cipher Suite > Remove +SSLv2 or Add -SSLv2 ");
+
+ $status = 0;
+ unless (-e "/var/cpanel/conf/pureftpd/root_password_disabled") {$status = 1}
+ &addline($status,"Check FTP Logins with Root Password","Allowing root login via FTP is a considerable security risk and should be disabled WHM > FTP Server Configuration > Allow Logins with Root Password > No ");
+ }
+
+ if (-e "/var/cpanel/conf/proftpd/main" and ($cpconf->{ftpserver} eq "proftpd") and !(-e "/etc/ftpddisable")) {
+ $status = 0;
+ open (my $IN, "<", "/var/cpanel/conf/proftpd/main");
+ flock ($IN, LOCK_SH);
+ my @conf = <$IN>;
+ close ($IN);
+ chomp @conf;
+ if (my @ls = grep {$_ =~ /^cPanelAnonymousAccessAllowed: 'yes'/i} @conf) {$status = 1}
+ &addline($status,"Check Anonymous FTP Logins","Used as an attack vector by hackers and should be disabled unless actively used WHM > FTP Server Configuration > Allow Anonymous Logins > No ");
+
+ $status = 0;
+ my $ciphers;
+ my $error;
+ if (my @ls = grep {$_ =~ /^\s*TLSCipherSuite/} @conf) {
+ if ($ls[0] =~ /TLSCipherSuite\:\s+(.*)$/) {$ciphers = $1}
+ $ciphers =~ s/\s*|\"|\'//g;
+ if ($ciphers eq "") {
+ $status = 1;
+ } else {
+ if (-e "/usr/bin/openssl") {
+ my ($childin, $childout);
+ my $cmdpid = open3($childin, $childout, $childout, "/usr/bin/openssl","ciphers","-v",$ciphers);
+ my @openssl = <$childout>;
+ waitpid ($cmdpid, 0);
+ chomp @openssl;
+ if (my @ls = grep {$_ =~ /error/i} @openssl) {$error = $openssl[0]; $status=2}
+ if (my @ls = grep {$_ =~ /SSLv2/} @openssl) {$status = 1}
+ }
+ }
+ } else {$status = 1}
+ if ($status == 2) {
+ &addline($status,"Check proftpd weak SSL/TLS Ciphers (TLSCipherSuite)","Unable to determine cipher list for [$ciphers] from openssl: [$error]");
+ }
+ &addline($status,"Check proftpd weak SSL/TLS Ciphers (TLSCipherSuite)","Cipher list [$ciphers]. Due to weaknesses in the SSLv2 cipher you should disable SSLv2 in WHM > FTP Server Configuration > TLS Cipher Suite > Remove +SSLv2 or Add -SSLv2 ");
+
+ if ($config{VPS}) {
+ $status = 0;
+ open (my $IN, "<", "/etc/proftpd.conf");
+ flock ($IN, LOCK_SH);
+ my @conf = <$IN>;
+ close ($IN);
+ chomp @conf;
+ if (my @ls = grep {$_ =~ /^\s*PassivePorts\s+(\d+)\s+(\d+)/} @conf) {
+ if ($config{TCP_IN} !~ /\b$1:$2\b/) {$status = 1}
+ } else {$status = 1}
+ &addline($status,"Check VPS FTP PASV hole","Since the Virtuozzo VPS iptables ip_conntrack_ftp kernel module is currently broken you have to open a PASV port hole in iptables for incoming FTP connections to work correctly. See the csf readme.txt under 'A note about FTP Connection Issues' on how to do this");
+ }
+ }
+
+ $status = 0;
+ if ($cpconf->{allowremotedomains}) {$status = 1}
+ &addline($status,"Check allow remote domains","User can park domains that resolve to other servers on this server. You should disable WHM > Tweak Settings > Allow Remote Domains");
+
+ $status = 0;
+ unless ($cpconf->{blockcommondomains}) {$status = 1}
+ &addline($status,"Check block common domains","User can park common domain names on this server. You should disable WHM > Tweak Settings > Prevent cPanel users from creating specific domains");
+
+ $status = 0;
+ if ($cpconf->{allowparkonothers}) {$status = 1}
+ &addline($status,"Check allow park domains","User can park/addon domains that belong to other users on this server. You should disable WHM > Tweak Settings > Allow cPanel users to create subdomains across accounts");
+
+ $status = 0;
+ if ($cpconf->{proxysubdomains}) {$status = 1}
+ &addline($status,"Check proxy subdomains","This option can mask a users real IP address and hinder security. You should disable WHM > Tweak Settings > Service subdomains");
+
+ $status = 1;
+ if ($cpconf->{cpaddons_notify_owner}) {$status = 0}
+ &addline($status,"Check cPAddons update email to resellers","You should have cPAddons email users if cPAddon installations require updating WHM > Tweak Settings > Notify reseller of cPAddons Site Software installations");
+
+ $status = 1;
+ if ($cpconf->{cpaddons_notify_root}) {$status = 0}
+ &addline($status,"Check cPAddons update email to root","You should have cPAddons email root if cPAddon installations require updating WHM > Tweak Settings > Notify root of cPAddons Site Software installations");
+
+ if (-e "/etc/cpupdate.conf") {
+ open (my $IN, "<", "/etc/cpupdate.conf");
+ flock ($IN, LOCK_SH);
+ my @conf = <$IN>;
+ close ($IN);
+ chomp @conf;
+
+ $status = 0;
+ if (my @ls = grep {$_ =~ /^CPANEL=(edge|beta|nightly)/i} @conf) {$status = 1}
+ &addline($status,"Check cPanel tree","Running EDGE/BETA on a production server could lead to server instability");
+
+ $status = 1;
+ if (my @ls = grep {$_ =~ /^UPDATES=daily/i} @conf) {$status = 0}
+ &addline($status,"Check cPanel updates","You have cPanel updating disabled, this can pose a security and stability risk. WHM > Update Preferences > Enabled Automatic Updates ");
+
+# $status = 0;
+# if (grep {$_ =~ /^SYSUP=/i} @conf) {$status = 1}
+# if (grep {$_ =~ /^SYSUP=daily/i} @conf) {$status = 0}
+# &addline($status,"Check package updates","You have package updating disabled, this can pose a security and stability risk. WHM > Update Config >cPanel Package Updates > Automatic ");
+
+# $status = 1;
+# if (my @ls = grep {$_ =~ /^RPMUP=daily/i} @conf) {$status = 0}
+# &addline($status,"Check security updates","You have security updating disabled, this can pose a security and stability risk. WHM > Update Config >Operating System Package Updates > Automatic ");
+ } else {&addline(1,"Check cPanel updates","Unable to find /etc/cpupdate.conf");}
+
+ $status = 1;
+ if ($cpconf->{account_login_access} eq "user") {$status = 0}
+ &addline($status,"Check accounts that can access a cPanel user","You should consider setting this option to \"user\" after use. WHM > Tweak Settings > Accounts that can access a cPanel user account");
+
+ unless ($status) {
+ $status = 0;
+ open (my $IN, "<", "/usr/local/cpanel/3rdparty/etc/php.ini");
+ flock ($IN, LOCK_SH);
+ my @conf = <$IN>;
+ close ($IN);
+ chomp @conf;
+ if (my @ls = grep {$_ =~ /^\s*register_globals\s*=\s*on/i} @conf) {$status = 1}
+ &addline($status,"Check cPanel php.ini file for register_globals","PHP register_globals is considered a high security risk. It is currently enabled in /usr/local/cpanel/3rdparty/etc/php.ini and should be disabled (disabling may break 3rd party PHP cPanel apps)");
+ }
+
+ $status = 0;
+ if ($cpconf->{emailpasswords}) {$status = 1}
+ &addline($status,"Check cPanel passwords in email","You should not send passwords out in plain text emails. You should disable WHM > Tweak Settings > Send passwords when creating a new account");
+
+ $status = 0;
+ if ($cpconf->{coredump}) {$status = 1}
+ &addline($status,"Check core dumps","You should disable WHM > Tweak Settings > Allow WHM/Webmail/cPanel services to create core dumps for debugging purposes");
+
+ $status = 1;
+ if ($cpconf->{cookieipvalidation} eq "strict") {$status = 0}
+ &addline($status,"Check Cookie IP Validation","You should enable strict Cookie IP validation in WHM > Tweak Settings > Cookie IP validation");
+
+ $status = 1;
+ if ($cpconf->{use_apache_md5_for_htaccess}) {$status = 0}
+ &addline($status,"Check MD5 passwords with Apache","You should enable WHM > Tweak Settings > Use MD5 passwords with Apache");
+
+ $status = 1;
+ if ($cpconf->{referrerblanksafety}) {$status = 0}
+ &addline($status,"Check Referrer Blank Security","You should enable WHM > Tweak Settings > Blank referrer safety check");
+
+ $status = 1;
+ if ($cpconf->{referrersafety}) {$status = 0}
+ &addline($status,"Check Referrer Security","You should enable WHM > Tweak Settings > Referrer safety check");
+
+ $status = 1;
+ if ($cpconf->{skiphttpauth}) {$status = 0}
+ &addline($status,"Check HTTP Authentication","You should disable skiphttpauth in /var/cpanel/cpanel.config");
+
+ $status = 0;
+ if ($cpconf->{skipparentcheck}) {$status = 1}
+ &addline($status,"Check Parent Security","You should disable WHM > Tweak Settings > Allow other applications to run the cPanel and admin binaries");
+
+ $status = 0;
+ if ($cpconf->{"cpsrvd-domainlookup"}) {$status = 1}
+ &addline($status,"Check Domain Lookup Security","You should disable WHM > Tweak Settings > cpsrvd username domain lookup");
+
+ $status = 1;
+ if ($cpconf->{"cgihidepass"}) {$status = 0}
+ &addline($status,"Check Password ENV variable","You should enable WHM > Tweak Settings > Hide login password from cgi scripts ");
+
+ $status = 0;
+ if (-e "/var/cpanel/smtpgidonlytweak") {$status = 1}
+ &addline($status,"Check SMTP Restrictions","This option in WHM will not function when running csf. You should disable WHM > Security Center > SMTP Restrictions and use the csf configuration option SMTP_BLOCK instead");
+
+ if (-e "/etc/wwwacct.conf") {
+ $status = 1;
+ open (my $IN, "<", "/etc/wwwacct.conf");
+ flock ($IN, LOCK_SH);
+ my @conf = <$IN>;
+ close ($IN);
+ chomp @conf;
+
+ my %ips;
+ foreach my $key (keys %g_ipv4) {
+ $ips{$key} = 1;
+ }
+
+ my $nameservers;
+ my $local = 0;
+ my $allns = 0;
+ foreach my $line (@conf) {
+ if ($line =~ /^NS(\d)?\s+(.*)\s*$/) {
+ my $ns = $2;
+ $ns =~ s/\s//g;
+ if ($ns) {
+ $allns++;
+ $nameservers .= "$ns \n";
+ my $ip;
+ if (checkip(\$ns)) {
+ $ip = $ns;
+ if ($ips{$ip}) {$local++}
+ } else {
+ my @ips = getips($ns);
+ unless (scalar @ips) {&addline(1,"Check nameservers","Unable to resolve nameserver [$ns]")}
+ my $hit = 0;
+ foreach my $oip (@ips) {
+ if ($ips{$oip}) {$hit = 1}
+ }
+ if ($hit) {$local++}
+ }
+ }
+ }
+ }
+ if ($local < $allns) {$status = 0}
+ &addline($status,"Check nameservers","At least one of the configured nameservers: \n$nameservers should be located in a topologically and geographically dispersed location on the Internet - See RFC 2182 (Section 3.1)");
+ }
+
+ if (-e "/usr/local/cpanel/bin/register_appconfig") {
+ $status = 0;
+ if ($cpconf->{permit_unregistered_apps_as_reseller}) {$status = 1}
+ &addline($status,"Check AppConfig Required","You should disable WHM > Tweak Settings > Allow apps that have not registered with AppConfig to be run when logged in as a reseller in WHM");
+
+ $status = 0;
+ if ($cpconf->{permit_unregistered_apps_as_root}) {$status = 1}
+ &addline($status,"Check AppConfig as root","You should disable WHM > Tweak Settings > Allow apps that have not registered with AppConfig to be run when logged in as root or a reseller with the \"all\" ACL in WHM");
+
+ $status = 0;
+ if ($cpconf->{permit_appconfig_entries_without_acls}) {$status = 1}
+ &addline($status,"Check AppConfig ACLs","You should disable WHM > Tweak Settings > Allow WHM apps registered with AppConfig to be executed even if a Required ACLs list has not been defined");
+
+ $status = 0;
+ if ($cpconf->{permit_appconfig_entries_without_features}) {$status = 1}
+ &addline($status,"Check AppConfig Feature List","You should disable WHM > Tweak Settings > Allow cPanel and Webmail apps registered with AppConfig to be executed even if a Required Features list has not been defined");
+ }
+
+ $status = 0;
+ if ($cpconf->{"disable-security-tokens"}) {$status = 1}
+ &addline($status,"Check Security Tokens","Security Tokens should not be disabled as without them security of WHM/cPanel is compromised. The setting disable-security-tokens=0 should be set in /var/cpanel/cpanel.config");
+ return;
+}
+# end whmcheck
+###############################################################################
+# start dacheck
+sub dacheck {
+ my $status = 0;
+ &addtitle("DirectAdmin Settings Check");
+
+ $status = 0;
+ unless ($daconfig{ssl}) {$status = 1}
+ &addline($status,"Check DirectAdmin login is SSL only","You should enable SSL only login to DirectAdmin ");
+
+ if (($daconfig{ftpconfig} =~ /proftpd.conf/) and ($daconfig{pureftp} != 1)) {
+ $status = 0;
+ open (my $IN, "<", $daconfig{ftpconfig});
+ flock ($IN, LOCK_SH);
+ my @conf = <$IN>;
+ close ($IN);
+ chomp @conf;
+
+ my $ciphers;
+ my $error;
+ if (my @ls = grep {$_ =~ /^\s*TLSCipherSuite/} @conf) {
+ if ($ls[0] =~ /TLSCipherSuite\s+(.*)$/) {$ciphers = $1}
+ $ciphers =~ s/\s*|\"|\'//g;
+ if ($ciphers eq "") {
+ $status = 1;
+ } else {
+ if (-e "/usr/bin/openssl") {
+ my ($childin, $childout);
+ my $cmdpid = open3($childin, $childout, $childout, "/usr/bin/openssl","ciphers","-v",$ciphers);
+ my @openssl = <$childout>;
+ waitpid ($cmdpid, 0);
+ chomp @openssl;
+ if (my @ls = grep {$_ =~ /error/i} @openssl) {$error = $openssl[0]; $status=2}
+ if (my @ls = grep {$_ =~ /SSLv2/} @openssl) {$status = 1}
+ }
+ }
+ } else {$status = 1}
+ if ($status == 2) {
+ &addline($status,"Check proftpd weak SSL/TLS Ciphers (TLSCipherSuite)","Unable to determine cipher list for [$ciphers] from openssl: [$error]");
+ }
+ &addline($status,"Check proftpd weak SSL/TLS Ciphers (TLSCipherSuite)","Cipher list [$ciphers]. Due to weaknesses in the SSLv2 cipher you should add a TLSCipherSuite with SSLv2 disabled in $daconfig{ftpconfig}. For example,<IfModule mod_tls.c> TLSCipherSuite HIGH </IfModule> container ");
+
+ if ($config{VPS}) {
+ $status = 0;
+ if (my @ls = grep {$_ =~ /^\s*PassivePorts\s+(\d+)\s+(\d+)/} @conf) {
+ if ($config{TCP_IN} !~ /\b$1:$2\b/) {$status = 1}
+ } else {$status = 1}
+ &addline($status,"Check VPS FTP PASV hole","Since the Virtuozzo VPS iptables ip_conntrack_ftp kernel module is currently broken you have to open a PASV port hole in iptables for incoming FTP connections to work correctly. See the csf readme.txt under 'A note about FTP Connection Issues' on how to do this");
+ }
+ }
+
+ $status = 1;
+
+ my %ips;
+ foreach my $key (keys %g_ipv4) {
+ $ips{$key} = 1;
+ }
+
+ my $nameservers;
+ for (my $x = 1; $x < 3; $x++) {
+ my $ns = $daconfig{"ns$x"};
+ $ns =~ s/\s//g;
+ if ($ns) {
+ $nameservers .= "$ns \n";
+ my $ip;
+ if ($ns =~ /\d+\.\d+\.\d+\.d+/) {
+ $ip = $ns;
+ } else {
+ eval {
+ local $SIG{__DIE__} = undef;
+ local $SIG{'ALRM'} = sub {die};
+ alarm(5);
+ $ip = gethostbyname($ns);
+ $ip = inet_ntoa($ip);
+ alarm(0);
+ };
+ alarm(0);
+ unless ($ip) {&addline(1,"Check nameservers","Unable to resolve nameserver [$ns] within 5 seconds")}
+ }
+ if ($ip) {
+ unless ($ips{$ip}) {$status = 0}
+ }
+ }
+ }
+ &addline($status,"Check nameservers","At least one of the configured nameservers: \n$nameservers should be located in a topologically and geographically dispersed location on the Internet - See RFC 2182 (Section 3.1)");
+ return;
+}
+# end dacheck
+###############################################################################
+# start mailcheck
+sub mailcheck {
+ &addtitle("Mail Check");
+
+ my $status = 0;
+ unless ($config{DIRECTADMIN}) {
+ if (-e "/root/.forward") {
+ if (-z "/root/.forward") {$status = 1}
+ } else {$status = 1}
+ &addline($status,"Check root forwarder","The root account should have a forwarder set so that you receive essential email from your server");
+ }
+
+ if (-e "/etc/exim.conf" and -x "/usr/sbin/exim") {
+ $status = 0;
+ my ($childin, $childout);
+ my $cmdpid = open3($childin, $childout, $childout, "/usr/sbin/exim","-bP");
+ my @eximconf = <$childout>;
+ waitpid ($cmdpid, 0);
+ chomp @eximconf;
+ if (my @ls = grep {$_ =~ /^\s*log_selector/} @eximconf) {
+ if (($ls[0] !~ /\+all/) and ($ls[0] !~ /\+arguments/) and ($ls[0] !~ /\+arguments/)) {$status = 1}
+ } else {$status = 1}
+ if ($config{DIRECTADMIN}) {
+ &addline($status,"Check exim for extended logging (log_selector)","You should enable extended exim logging to enable easier tracking potential outgoing spam issues. Add:log_selector = +arguments +subject +received_recipients to /etc/exim.conf");
+ } else {
+ &addline($status,"Check exim for extended logging (log_selector)","You should enable extended exim logging to enable easier tracking potential outgoing spam issues. Add:log_selector = +arguments +subject +received_recipients in WHM > Exim Configuration Manager > Advanced Editor > log_selector");
+ }
+
+ $status = 0;
+ my $ciphers;
+ my $error;
+ if (my @ls = grep {$_ =~ /^\s*tls_require_ciphers/} @eximconf) {
+ (undef,$ciphers) = split(/\=/,$ls[0]);
+ $ciphers =~ s/\s*|\"|\'//g;
+ if ($ciphers eq "") {
+ $status = 1;
+ } else {
+ if (-x "/usr/bin/openssl") {
+ my ($childin, $childout);
+ my $cmdpid = open3($childin, $childout, $childout, "/usr/bin/openssl","ciphers","-v",$ciphers);
+ my @openssl = <$childout>;
+ waitpid ($cmdpid, 0);
+ chomp @openssl;
+ if (my @ls = grep {$_ =~ /error/i} @openssl) {$error = $openssl[0]; $status=2}
+ if (my @ls = grep {$_ =~ /SSLv2/} @openssl) {$status = 1}
+ }
+ }
+ } else {$status = 1}
+ if ($status == 2) {
+ &addline($status,"Check exim weak SSL/TLS Ciphers (tls_require_ciphers)","Unable to determine cipher list for [$ciphers] from openssl: [$error]");
+ }
+ if ($config{DIRECTADMIN}) {
+ &addline($status,"Check exim weak SSL/TLS Ciphers (tls_require_ciphers)","Cipher list [$ciphers]. Due to weaknesses in the SSLv2 cipher you should edit /etc/exim.conf and set tls_require_ciphers to explicitly exclude it. For example:tls_require_ciphers=ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:-LOW:-SSLv2:-EXP ");
+ } else {
+ &addline($status,"Check exim weak SSL/TLS Ciphers (tls_require_ciphers)","Cipher list [$ciphers]. Due to weaknesses in the SSLv2 cipher you should disable WHM > Exim Configuration Manager > Allow weak ssl/tls ciphers to be used, and also ensure tls_require_ciphers in /etc/exim.conf does not allow SSLv2 as openssl currently shows that it does");
+ }
+ } else {&addline(1,"Check exim configuration","Unable to find /etc/exim.conf and/or /usr/sbin/exim");}
+
+ if (-e "/etc/exim.conf.localopts") {
+ $status = 0;
+ open (my $IN, "<", "/etc/exim.conf.localopts");
+ flock ($IN, LOCK_SH);
+ my @conf = <$IN>;
+ close ($IN);
+ chomp @conf;
+
+ if (my @ls = grep {$_ =~ /require_secure_auth=0/i} @conf) {$status = 1}
+ &addline($status,"Check exim for secure authentication","You should require clients to connect with SSL or issue the STARTTLS command before they are allowed to authenticate with the server, otherwise passwords may be sent in plain text in WHM > Exim Configuration Manager ");
+ }
+
+ if ($config{DIRECTADMIN}) {
+ if (-e "/etc/dovecot.conf" and ($daconfig{dovecot})) {
+ $status = 0;
+ open (my $IN, "<", "/etc/dovecot.conf");
+ flock ($IN, LOCK_SH);
+ my @conf = <$IN>;
+ close ($IN);
+ chomp @conf;
+
+ my @morefiles;
+ if (my @ls = grep {$_ =~ /^\s*\!\s*include(_try)?\s+(.*)\s*$/i} @conf) {
+ foreach my $more (@ls) {
+ if ($more =~ /^\s*\!\s*include(_try)?\s+(.*)\s*$/i) {
+ my $conf = $2;
+ if ($conf !~ /^\//) {$conf = "/etc/dovecot/".$conf}
+ push @morefiles, $conf;
+ }
+ }
+ }
+ foreach my $file (@morefiles) {
+ if (-e $file) {
+ open (my $IN, "<", "$file");
+ flock ($IN, LOCK_SH);
+ my @moreconf = <$IN>;
+ close ($IN);
+ chomp @conf;
+ @conf = (@conf, @moreconf);
+ }
+ }
+
+ my $ciphers;
+ my $error;
+ if (my @ls = grep {$_ =~ /^ssl_cipher_list/} @conf) {
+ (undef,$ciphers) = split(/\=/,$ls[0]);
+ $ciphers =~ s/\s*|\"|\'//g;
+ if ($ciphers eq "") {
+ $status = 1;
+ } else {
+ if (-x "/usr/bin/openssl") {
+ my ($childin, $childout);
+ my $cmdpid = open3($childin, $childout, $childout, "/usr/bin/openssl","ciphers","-v",$ciphers);
+ my @openssl = <$childout>;
+ waitpid ($cmdpid, 0);
+ chomp @openssl;
+ if (my @ls = grep {$_ =~ /error/i} @openssl) {$error = $openssl[0]; $status=2}
+ if (my @ls = grep {$_ =~ /SSLv2/} @openssl) {$status = 1}
+ }
+ }
+ } else {$status = 1}
+ if ($status == 2) {
+ &addline($status,"Check dovecot weak SSL/TLS Ciphers (ssl_cipher_list)","Unable to determine cipher list for [$ciphers] from openssl: [$error]");
+ }
+ &addline($status,"Check dovecot weak SSL/TLS Ciphers (ssl_cipher_list)","Cipher list [$ciphers]. Due to weaknesses in the SSLv2 cipher you should /etc/dovecot.conf and set ssl_cipher_list to explicitly exclude it. For example:ssl_cipher_list = ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:-LOW:-SSLv2:-EXP ");
+ }
+ } else {
+ if (-e "/etc/dovecot/dovecot.conf" and ($cpconf->{mailserver} eq "dovecot")) {
+ $status = 0;
+ open (my $IN, "<", "/etc/dovecot/dovecot.conf");
+ flock ($IN, LOCK_SH);
+ my @conf = <$IN>;
+ close ($IN);
+ chomp @conf;
+
+ my @morefiles;
+ if (my @ls = grep {$_ =~ /^\s*\!?include(_try)?\s+(.*)\s*$/i} @conf) {
+ foreach my $more (@ls) {
+ if ($more =~ /^\s*\!?include(_try)?\s+(.*)\s*$/i) {push @morefiles, $2}
+ }
+ }
+ foreach my $file (@morefiles) {
+ if (-e $file) {
+ open (my $IN, "<", "$file");
+ flock ($IN, LOCK_SH);
+ my @moreconf = <$IN>;
+ close ($IN);
+ chomp @conf;
+ @conf = (@conf, @moreconf);
+ }
+ }
+
+ $status = 0;
+ my $ciphers;
+ my $error;
+ if (my @ls = grep {$_ =~ /^ssl_cipher_list/} @conf) {
+ (undef,$ciphers) = split(/\=/,$ls[0]);
+ $ciphers =~ s/\s*|\"|\'//g;
+ if ($ciphers eq "") {
+ $status = 1;
+ } else {
+ if (-x "/usr/bin/openssl") {
+ my ($childin, $childout);
+ my $cmdpid = open3($childin, $childout, $childout, "/usr/bin/openssl","ciphers","-v",$ciphers);
+ my @openssl = <$childout>;
+ waitpid ($cmdpid, 0);
+ chomp @openssl;
+ if (my @ls = grep {$_ =~ /error/i} @openssl) {$error = $openssl[0]; $status=2}
+ if (my @ls = grep {$_ =~ /SSLv2/} @openssl) {$status = 1}
+ }
+ }
+ } else {$status = 1}
+ if ($status == 2) {
+ &addline($status,"Check dovecot weak SSL/TLS Ciphers (ssl_cipher_list)","Unable to determine cipher list for [$ciphers] from openssl: [$error]");
+ }
+ &addline($status,"Check dovecot weak SSL/TLS Ciphers (ssl_cipher_list)","Cipher list [$ciphers]. Due to weaknesses in the SSLv2 cipher you should disable SSLv2 in WHM > Mailserver Configuration > SSL Cipher List > Remove +SSLv2 or Add -SSLv2 ");
+ }
+
+ if (-e "/usr/lib/courier-imap/etc/imapd-ssl" and ($cpconf->{mailserver} eq "courier")) {
+ $status = 0;
+ open (my $IN, "<", "/usr/lib/courier-imap/etc/imapd-ssl");
+ flock ($IN, LOCK_SH);
+ my @conf = <$IN>;
+ close ($IN);
+ chomp @conf;
+ $status = 0;
+ my $ciphers;
+ my $error;
+ if (my @ls = grep {$_ =~ /^TLS_CIPHER_LIST/} @conf) {
+ (undef,$ciphers) = split(/\=/,$ls[0]);
+ $ciphers =~ s/\s*|\"|\'//g;
+ if ($ciphers eq "") {
+ $status = 1;
+ } else {
+ if (-x "/usr/bin/openssl") {
+ my ($childin, $childout);
+ my $cmdpid = open3($childin, $childout, $childout, "/usr/bin/openssl","ciphers","-v",$ciphers);
+ my @openssl = <$childout>;
+ waitpid ($cmdpid, 0);
+ chomp @openssl;
+ if (my @ls = grep {$_ =~ /error/i} @openssl) {$error = $openssl[0]; $status=2}
+ if (my @ls = grep {$_ =~ /SSLv2/} @openssl) {$status = 1}
+ }
+ }
+ } else {$status = 1}
+ if ($status == 2) {
+ &addline($status,"Check Courier IMAP weak SSL/TLS Ciphers (TLS_CIPHER_LIST)","Unable to determine cipher list for [$ciphers] from openssl: [$error]");
+ }
+ &addline($status,"Check Courier IMAP weak SSL/TLS Ciphers (TLS_CIPHER_LIST)","Cipher list [$ciphers]. Due to weaknesses in the SSLv2 cipher you should disable SSLv2 in WHM > Mailserver Configuration > IMAP TLS/SSL Cipher List > Remove +SSLv2 or Add -SSLv2 ");
+ }
+
+ if (-e "/usr/lib/courier-imap/etc/pop3d-ssl" and ($cpconf->{mailserver} eq "courier")) {
+ $status = 0;
+ open (my $IN, "<", "/usr/lib/courier-imap/etc/pop3d-ssl");
+ flock ($IN, LOCK_SH);
+ my @conf = <$IN>;
+ close ($IN);
+ chomp @conf;
+ $status = 0;
+ my $ciphers;
+ my $error;
+ if (my @ls = grep {$_ =~ /^TLS_CIPHER_LIST/} @conf) {
+ (undef,$ciphers) = split(/\=/,$ls[0]);
+ $ciphers =~ s/\s*|\"|\'//g;
+ if ($ciphers eq "") {
+ $status = 1;
+ } else {
+ if (-x "/usr/bin/openssl") {
+ my ($childin, $childout);
+ my $cmdpid = open3($childin, $childout, $childout, "/usr/bin/openssl","ciphers","-v",$ciphers);
+ my @openssl = <$childout>;
+ waitpid ($cmdpid, 0);
+ chomp @openssl;
+ if (my @ls = grep {$_ =~ /error/i} @openssl) {$error = $openssl[0]; $status=2}
+ if (my @ls = grep {$_ =~ /SSLv2/} @openssl) {$status = 1}
+ }
+ }
+ } else {$status = 1}
+ if ($status == 2) {
+ &addline($status,"Check Courier POP3D weak SSL/TLS Ciphers (TLS_CIPHER_LIST)","Unable to determine cipher list for [$ciphers] from openssl: [$error]");
+ }
+ &addline($status,"Check Courier POP3D weak SSL/TLS Ciphers (TLS_CIPHER_LIST)","Cipher list [$ciphers]. Due to weaknesses in the SSLv2 cipher you should disable SSLv2 in WHM > Mailserver Configuration > POP3 TLS/SSL Cipher List > Remove +SSLv2 or Add -SSLv2 ");
+ }
+ }
+ return;
+}
+# end mailcheck
+###############################################################################
+# start phpcheck
+sub phpcheck {
+ &addtitle("PHP Check");
+ my %phpbinaries;
+ my %phpinis;
+
+ if (-e "/usr/local/cpanel/version" and -e "/etc/cpanel/ea4/is_ea4") {
+ foreach my $phpdir (glob("/opt/cpanel/ea-*")) {
+ if (-e "${phpdir}/root/usr/bin/php") {$phpbinaries{"${phpdir}/root/usr/bin/php"} = 1}
+ }
+ }
+ elsif ($config{DIRECTADMIN}) {
+ foreach my $phpdir (glob("/usr/local/php*")) {
+ if (-e "${phpdir}/bin/php") {$phpbinaries{"${phpdir}/bin/php"} = 1}
+ }
+ }
+ elsif (-e "/usr/local/bin/php") {$phpbinaries{"/usr/local/bin/php"} = 1}
+ elsif (-e "/usr/bin/php") {$phpbinaries{"/usr/bin/php"} = 1}
+
+ if (-e "/opt/alt/alt-php-config") {
+ foreach my $phpdir (glob("/opt/alt/php*")) {
+ if (-e "${phpdir}/usr/bin/php") {$phpbinaries{"${phpdir}/usr/bin/php"} = 1}
+ }
+ }
+
+ if (scalar(keys %phpbinaries) == 0) {
+ &addline(1,"PHP Binary","PHP binary not found or not executable");
+ return;
+ }
+
+ foreach my $phpbin (keys %phpbinaries) {
+ if ($phpbin =~ /php44/) {$phpinis{"/opt/alt/php44/etc/php.ini"} = $phpbin}
+ elsif ($phpbin =~ /php51/) {$phpinis{"/opt/alt/php51/etc/php.ini"} = $phpbin}
+ else {
+ my ($childin, $childout);
+ my $mypid = open3($childin, $childout, $childout, $phpbin,"-d","zlib.output_compression=Off","--ini");
+ my @conf = <$childout>;
+ waitpid ($mypid, 0);
+ chomp @conf;
+ foreach my $line (@conf) {
+ if ($line =~ /^Loaded Configuration File:\s*(\S+)$/) {$phpinis{$1} = $phpbin}
+ }
+ }
+ }
+ my %phpconf;
+ foreach my $phpini (sort keys %phpinis) {
+ my $phpbin = $phpinis{$phpini};
+
+ my $status = 0;
+ my ($childin, $childout);
+ my $mypid;
+ if ($phpbin =~ /php44|php51/) {
+ $mypid = open3($childin, $childout, $childout, $phpbin,"-i");
+ } else {
+ $mypid = open3($childin, $childout, $childout, $phpbin,"-d","zlib.output_compression=Off","-i");
+ }
+ my @conf = <$childout>;
+ waitpid ($mypid, 0);
+ chomp @conf;
+
+ if (my @ls = grep {$_ =~ /^PHP License/} @conf) {
+ my $version = 0;
+ my ($mas,$maj,$min);
+ if (my @ls = grep {$_ =~ /^PHP Version\s*=>\s*/i} @conf) {
+ my $line = $ls[0];
+ $line =~ /^PHP Version\s*=>\s*(.*)/i;
+ ($mas,$maj,$min) = split(/\./,$1);
+ $version = "$mas.$maj.$min";
+ if ($mas < 8) {$status = 1}
+ if ($mas == 8 and $maj < 1) {$status = 1}
+ }
+ open (my $IN, "<", "/usr/local/apache/conf/php.conf.yaml");
+ flock ($IN, LOCK_SH);
+ my @phpyamlconf = <$IN>;
+ close ($IN);
+ chomp @phpyamlconf;
+
+ if (my @ls = grep {$_ =~ /php4:/i} @phpyamlconf) {
+ if ($ls[0] !~ /none/) {
+ $status = 1;
+ $version = "4.*";
+ }
+ }
+ unless ($phpbin =~ m[^/opt/alt/php]) {
+ if ($status) {$phpconf{version} .= "$version ($phpbin),"}
+ }
+
+ $status = 1;
+ if (my @ls = grep {$_ =~ /^enable_dl\s*=>\s*Off/i} @conf) {
+ $status = 0;
+ }
+ if (my @ls = grep {$_ =~ /^disable_functions\s*=>.*dl.*/i} @conf) {
+ $status = 0;
+ }
+
+ if ($status) {$phpconf{enable_dl} .= "$phpini ($phpbin),"}
+
+ $status = 1;
+ if (my @ls = grep {$_ =~ /^disable_functions\s*=>.*\,/i} @conf) {
+ $status = 0;
+ }
+ if ($status) {$phpconf{disable_functions} .= "$phpini ($phpbin),"}
+
+ $status = 1;
+ if (my @ls = grep {$_ =~ /^disable_functions\s*=>.*ini_set.*/i} @conf) {
+ $status = 0;
+ }
+ if ($status) {$phpconf{ini_set} .= "$phpini ($phpbin),"}
+
+ my $oldver = "$mas.$maj";
+ if ($oldver < 5.4) {
+ $status = 1;
+ if (my @ls = grep {$_ =~ /^register_globals\s*=>\s*Off/i} @conf) {
+ $status = 0;
+ }
+ if ($status) {$phpconf{register_globals} .= "$phpini ($phpbin),"}
+
+ }
+ } else {
+ $status = 1;
+ &addline($status,"Check php [$phpbin]","Unable to examine PHP settings due to an error in the output from: [$phpbin -i]");
+ }
+ }
+ foreach my $key ("version","enable_dl","disable_functions","ini_set","register_globals") {
+ my $values;
+ foreach my $value (split(/\,/,$phpconf{$key})) {
+ if ($value eq "") {next}
+ $values .= " $value\n";
+ }
+ if ($key eq "version") {
+ my $status = 0;
+ if ($values ne "") {$status = 1}
+ &addline($status,"Check php version","Any version of PHP older than v8.1.* is now obsolete and should be considered a security threat. You should upgrade to at least PHP v8.1+:Affected PHP versions: $values");
+ }
+ if ($key eq "enable_dl") {
+ my $status = 0;
+ if ($values ne "") {$status = 1}
+ &addline($status,"Check php for enable_dl or disabled dl()","You should set:enable_dl = Off This prevents users from loading php modules that affect everyone on the server. Note that if use dynamic libraries, such as ioncube, you will have to load them directly in the PHP configuration:Affected PHP versions: $values");
+ }
+ if ($key eq "disable_functions") {
+ my $status = 0;
+ if ($values ne "") {$status = 1}
+ &addline($status,"Check php for disable_functions","You should consider disabling commonly abused php functions, e.g.:disable_functions = show_source, system, shell_exec, passthru, exec, popen, proc_open Some client web scripts may break with some of these functions disabled, so you may have to remove them from this list:Affected PHP versions: $values");
+ }
+ if ($key eq "register_globals") {
+ my $status = 0;
+ if ($values ne "") {$status = 1}
+ &addline($status,"Check php for register_globals","You should set:register_globals = Off unless it is absolutely necessary as it is seen as a significant security risk:Affected PHP versions: $values");
+ }
+ }
+ return;
+}
+# end phpcheck
+###############################################################################
+# start apachecheck
+sub apachecheck {
+ &addtitle("Apache Check");
+
+ my $status = 0;
+ my $mypid;
+ my ($childin, $childout);
+ my %ea4;
+
+ if (-e "/usr/local/cpanel/version" and -e "/etc/cpanel/ea4/is_ea4" and -e "/etc/cpanel/ea4/paths.conf") {
+ my @file = slurp("/etc/cpanel/ea4/paths.conf");
+ $ea4{enabled} = 1;
+ foreach my $line (@file) {
+ $line =~ s/$cleanreg//g;
+ if ($line =~ /^(\s|\#|$)/) {next}
+ if ($line !~ /=/) {next}
+ my ($name,$value) = split (/=/,$line,2);
+ $value =~ s/^\s+//g;
+ $value =~ s/\s+$//g;
+ $ea4{$name} = $value;
+ }
+ }
+
+ if ($ea4{enabled}) {
+ unless (-x $ea4{bin_httpd}) {&addline(1,"HTTP Binary","$ea4{bin_httpd} not found or not executable"); return}
+ }
+ elsif ($config{DIRECTADMIN}) {
+ unless (-x "/usr/sbin/httpd") {&addline(1,"HTTP Binary","/usr/sbin/httpd not found or not executable"); return}
+ }
+ else {
+ unless (-x "/usr/local/apache/bin/httpd") {&addline(1,"HTTP Binary","/usr/local/apache/bin/httpd not found or not executable"); return}
+ }
+
+ if ($ea4{enabled}) {
+ $mypid = open3($childin, $childout, $childout, $ea4{bin_httpd},"-v");
+ }
+ elsif ($config{DIRECTADMIN}) {
+ $mypid = open3($childin, $childout, $childout, "/usr/sbin/httpd","-v");
+ }
+ else {
+ $mypid = open3($childin, $childout, $childout, "/usr/local/apache/bin/httpd","-v");
+ }
+ my @version = <$childout>;
+ waitpid ($mypid, 0);
+ chomp @version;
+ $version[0] =~ /Apache\/(\d+)\.(\d+)\.(\d+)/;
+ my $mas = $1;
+ my $maj = $2;
+ my $min = $3;
+ if ("$mas.$maj" < 2.2) {$status = 1}
+ &addline($status,"Check apache version","You are running a legacy version of apache (v$mas.$maj.$min) and should consider upgrading to v2.2.* as recommended by the Apache developers");
+
+ unless ($config{DIRECTADMIN}) {
+ my $ruid2 = 0;
+ if ($ea4{enabled}) {
+ $mypid = open3($childin, $childout, $childout, $ea4{bin_httpd},"-M");
+ }
+ else {
+ $mypid = open3($childin, $childout, $childout, "/usr/local/apache/bin/httpd","-M");
+ }
+ my @modules = <$childout>;
+ waitpid ($mypid, 0);
+ chomp @modules;
+ if (my @ls = grep {$_ =~ /ruid2_module/} @modules) {$ruid2 = 1}
+ if (my @ls = grep {$_ =~ /mpm_itk_module/} @modules) {$ruid2 = 1}
+
+ $status = 0;
+ if (my @ls = grep {$_ =~ /security2_module/} @modules) {$status = 0} else {$status = 1}
+ &addline($status,"Check apache for ModSecurity","You should install the ModSecurity apache module during the easyapache build process to help prevent exploitation of vulnerable web scripts, together with a set of rules");
+
+ $status = 0;
+ if (my @ls = grep {$_ =~ /cloudflare_module/} @modules) {$status = 1} else {$status = 0}
+ if ($config{CF_ENABLE}) {$status = 0}
+ &addline($status,"Check apache for mod_cloudflare","This module logs the real users IP address to Apache. If this is reported to lfd via ModSecurity, cxs or some other vector through Apache it will lead to that IP being blocked, but because the IP is coming through the CloudFlare service the IP will not be blocked as so far as iptables is concerned the originating IP address is CloudFlare itself and the abuse will continue. To block these IP's in the CloudFlare Firewall look at using CF_ENABLE in csf.conf");
+
+ $status = 0;
+ if (my @ls = grep {$_ =~ /frontpage_module/} @modules) {$status = 1}
+ &addline($status,"Check apache for FrontPage","Microsoft Frontpage Extensions were EOL in 2006 and there is no support for bugs or security issues. For this reason, it should be considered a security risk to continue using them. You should rebuild apache through easyapache and deselect the option to build them");
+
+ my @conf;
+ if (-e "/usr/local/apache/conf/httpd.conf") {
+ open (my $IN, "<", "/usr/local/apache/conf/httpd.conf");
+ flock ($IN, LOCK_SH);
+ @conf = <$IN>;
+ close ($IN);
+ chomp @conf;
+ }
+ if (-e "$ea4{file_conf}") {
+ open (my $IN, "<", "$ea4{file_conf}");
+ flock ($IN, LOCK_SH);
+ @conf = <$IN>;
+ close ($IN);
+ chomp @conf;
+ }
+ if (@conf) {
+ $status = 0;
+ my $ciphers;
+ my $error;
+ if (my @ls = grep {$_ =~ /^\s*SSLCipherSuite/} @conf) {
+ $ls[0] =~ s/^\s+//g;
+ (undef,$ciphers) = split(/\ /,$ls[0]);
+ $ciphers =~ s/\s*|\"|\'//g;
+ if ($ciphers eq "") {
+ $status = 1;
+ } else {
+ if (-x "/usr/bin/openssl") {
+ my ($childin, $childout);
+ my $cmdpid = open3($childin, $childout, $childout, "/usr/bin/openssl","ciphers","-v",$ciphers);
+ my @openssl = <$childout>;
+ waitpid ($cmdpid, 0);
+ chomp @openssl;
+ if (my @ls = grep {$_ =~ /error/i} @openssl) {$error = $openssl[0]; $status=2}
+ if (my @ls = grep {$_ =~ /SSLv2/} @openssl) {$status = 1}
+ }
+ }
+ } else {$status = 1}
+ if ($status == 2) {
+ &addline($status,"Check Apache weak SSL/TLS Ciphers (SSLCipherSuite)","Unable to determine cipher list for [$ciphers] from openssl: [$error]");
+ }
+ &addline($status,"Check Apache weak SSL/TLS Ciphers (SSLCipherSuite)","Cipher list [$ciphers]. Due to weaknesses in the SSLv2 cipher you should disable SSLv2 in WHM > Apache Configuration > Global Configuration > SSLCipherSuite > Add -SSLv2 to SSLCipherSuite and/or remove +SSLv2. Do not forget to Save AND then Rebuild Configuration and Restart Apache, otherwise the changes will not take effect in httpd.conf");
+
+ $status = 0;
+ if (my @ls = grep {$_ =~ /^\s*TraceEnable Off/} @conf) {
+ $status = 0;
+ } else {$status = 1}
+ &addline($status,"Check apache for TraceEnable","You should set TraceEnable to Off in: WHM > Apache Configuration > Global Configuration > Trace Enable > Off. Do not forget to Save AND then Rebuild Configuration and Restart Apache, otherwise the changes will not take effect in httpd.conf");
+ $status = 0;
+ if (my @ls = grep {$_ =~ /^\s*ServerSignature Off/} @conf) {
+ $status = 0;
+ } else {$status = 1}
+ &addline($status,"Check apache for ServerSignature","You should set ServerSignature to Off in: WHM > Apache Configuration > Global Configuration > Server Signature > Off. Do not forget to Save AND then Rebuild Configuration and Restart Apache, otherwise the changes will not take effect in httpd.conf");
+ $status = 0;
+ if (my @ls = grep {$_ =~ /^\s*ServerTokens ProductOnly/} @conf) {
+ $status = 0;
+ } else {$status = 1}
+ &addline($status,"Check apache for ServerTokens","You should set ServerTokens to ProductOnly in: WHM > Apache Configuration > Global Configuration > Server Tokens > Product Only. Do not forget to Save AND then Rebuild Configuration and Restart Apache, otherwise the changes will not take effect in httpd.conf");
+ $status = 0;
+ if (my @ls = grep {$_ =~ /^\s*FileETag None/} @conf) {
+ $status = 0;
+ } else {$status = 1}
+ &addline($status,"Check apache for FileETag","You should set FileETag to None in: WHM > Apache Configuration > Global Configuration > File ETag > None. Do not forget to Save AND then Rebuild Configuration and Restart Apache, otherwise the changes will not take effect in httpd.conf");
+ }
+
+ my @apacheconf;
+ if (-e "/usr/local/apache/conf/php.conf.yaml") {
+ open (my $IN, "<", "/usr/local/apache/conf/php.conf.yaml");
+ flock ($IN, LOCK_SH);
+ @apacheconf = <$IN>;
+ close ($IN);
+ chomp @apacheconf;
+ }
+ if (-e "$ea4{dir_conf}/php.conf.yaml") {
+ open (my $IN, "<", "$ea4{dir_conf}/php.conf.yaml");
+ flock ($IN, LOCK_SH);
+ @apacheconf = <$IN>;
+ close ($IN);
+ chomp @apacheconf;
+ }
+ if (@apacheconf) {
+ unless ($ruid2) {
+ $status = 0;
+ if (my @ls = grep {$_ =~ /suphp/} @apacheconf) {
+ $status = 0;
+ } else {$status = 1}
+ &addline($status,"Check suPHP","To reduce the risk of hackers accessing all sites on the server from a compromised PHP web script, you should enable suPHP when you build apache/php. Note that there are sideeffects when enabling suPHP on a server and you should be aware of these before enabling it. Don\'t forget to enable it as the default PHP handler in WHM > PHP 5 Handler ");
+
+ $status = 0;
+ unless ($cpconf->{userdirprotect}) {$status = 1}
+ &addline($status,"Check mod_userdir protection","To prevents users from stealing bandwidth or hackers hiding access to your servers, you should check WHM > Security Center > mod_userdir Tweak ");
+
+ $status = 1;
+ if (my @ls = grep {$_ =~ /suexec_module/} @modules) {$status = 0}
+ &addline($status,"Check Suexec","To reduce the risk of hackers accessing all sites on the server from a compromised CGI web script, you should set WHM > Suexec on ");
+ }
+ }
+ }
+ return;
+}
+# end apachecheck
+###############################################################################
+# start sshtelnetcheck
+sub sshtelnetcheck {
+ my $status = 0;
+ &addtitle("SSH/Telnet Check");
+
+ if (-e "/etc/ssh/sshd_config") {
+ open (my $IN, "<", "/etc/ssh/sshd_config");
+ flock ($IN, LOCK_SH);
+ my @sshconf = <$IN>;
+ close ($IN);
+ chomp @sshconf;
+ if (my @ls = grep {$_ =~ /^\s*Protocol/i} @sshconf) {
+ if ($ls[0] =~ /1/) {$status = 1}
+ } else {$status = 0}
+ &addline($status,"Check SSHv1 is disabled","You should disable SSHv1 by editing /etc/ssh/sshd_config and setting:Protocol 2 ");
+
+ $status = 0;
+ my $sshport = "22";
+ if (my @ls = grep {$_ =~ /^\s*Port/i} @sshconf) {
+ if ($ls[0] =~ /^\s*Port\s+(\d*)/i) {
+ $sshport = $1;
+ if ($sshport eq "22") {$status = 1}
+ } else {$status = 1}
+ } else {$status = 1}
+ &addline($status,"Check SSH on non-standard port","You should consider moving SSH to a non-standard port [currently:$sshport] to evade basic SSH port scans. Don't forget to open the port in the firewall first if necessary");
+
+ $status = 0;
+ if (my @ls = grep {$_ =~ /^\s*PasswordAuthentication/i} @sshconf) {
+ if ($ls[0] =~ /\byes\b/i) {$status = 1}
+ } else {$status = 1}
+ &addline($status,"Check SSH PasswordAuthentication","You should disable PasswordAuthentication and only allow access using PubkeyAuthentication to improve brute-force SSH security");
+
+ $status = 0;
+ if (my @ls = grep {$_ =~ /^\s*UseDNS/i} @sshconf) {
+ if ($ls[0] !~ /\bno\b/i) {$status = 1}
+ } else {$status = 1}
+ &addline($status,"Check SSH UseDNS","You should disable UseDNS by editing /etc/ssh/sshd_config and setting:UseDNS no Otherwise, lfd will be unable to track SSHD login failures successfully as the log files will not report IP addresses");
+ } else {&addline(1,"Check SSH configuration","Unable to find /etc/ssh/sshd_config");}
+
+ $status = 0;
+ my $check = &getportinfo("23");
+ if ($check) {$status = 1}
+ &addline($status,"Check telnet port 23 is not in use","It appears that something is listening on port 23 which is normally used for telnet. Telnet is an insecure protocol and you should disable the telnet daemon if it is running");
+
+ unless ($config{DNSONLY} or $config{GENERIC}) {
+ unless ($config{VPS}) {
+ if (-e "/etc/redhat-release") {
+ open (my $IN, "<", "/etc/redhat-release");
+ flock ($IN, LOCK_SH);
+ my $conf = <$IN>;
+ close ($IN);
+ chomp $conf;
+
+ unless ($conf =~ /^CloudLinux/i) {
+ if (-e "/etc/profile") {
+ $status = 0;
+ open (my $IN, "<", "/etc/profile");
+ flock ($IN, LOCK_SH);
+ my @profile = <$IN>;
+ close ($IN);
+ chomp @profile;
+ if (grep {$_ =~ /^LIMITUSER=\$USER/} @profile) {
+ $status = 0;
+ } else {$status = 1}
+ &addline($status,"Check shell limits","You should enable shell resource limits to prevent shell users from consuming server resources - DOS exploits typically do this. A quick way to set this is to use WHM > Shell Fork Bomb Protection ");
+ } else {
+ &addline(1,"Check shell limits","Unable to find /etc/profile");
+ }
+ }
+ }
+ }
+
+ $status = 0;
+ if (-e "/var/cpanel/killproc.conf") {
+ open (my $IN, "<", "/var/cpanel/killproc.conf");
+ flock ($IN, LOCK_SH);
+ my @proc = <$IN>;
+ close ($IN);
+ chomp @proc;
+ if (@proc < 9) {$status = 1}
+ &addline($status,"Check Background Process Killer","You should enable each item in the WHM > Background Process Killer ");
+ } else {&addline(1,"Check Background Process Killer","You should enable each item in the WHM > Background Process Killer ")}
+ }
+ return;
+}
+# end sshtelnetcheck
+###############################################################################
+# start servicescheck
+sub servicescheck {
+ my $systemctl = "/usr/bin/systemctl";
+ my $chkconfig = "/sbin/chkconfig";
+ my $servicebin = "/sbin/service";
+ if (-e "/bin/systemctl") {$systemctl = "/bin/systemctl"}
+ if (-e "/usr/sbin/chkconfig") {$chkconfig = "/usr/sbin/chkconfig"}
+ if (-e "/usr/sbin/service") {$servicebin = "/usr/sbin/service"}
+ &addtitle("Server Services Check");
+ my @services = ("abrt-xorg", "abrtd", "alsa-state", "anacron", "avahi-daemon", "avahi-dnsconfd", "bluetooth", "bolt", "canna", "colord", "cups", "cups-config-daemon", "cupsd", "firewalld", "FreeWnn", "gdm", "gpm", "gssproxy", "hidd", "iiim", "ksmtuned", "mDNSResponder", "ModemManager", "nfslock", "nifd", "packagekit", "pcscd", "portreserve", "pulseaudio", "qpidd", "rpcbind", "rpcidmapd", "saslauthd", "sbadm", "wpa_supplicant", "xfs", "xinetd");
+
+ my $disable;
+ my ($childin, $childout);
+ my $mypid;
+ if ($sysinit eq "init") {
+ $disable = "$servicebin [service] stop $chkconfig [service] off";
+ $mypid = open3($childin, $childout, $childout, $chkconfig,"--list");
+ } else {
+ $disable = "$systemctl stop [service] $systemctl disable [service]";
+ $mypid = open3($childin, $childout, $childout, $systemctl,"list-unit-files","--state=enabled","--no-pager","--no-legend");
+ }
+ my @chkconfig = <$childout>;
+ waitpid ($mypid, 0);
+ chomp @chkconfig;
+
+ my @enabled;
+ foreach my $service (@services) {
+ if ($service eq "xinetd" and $config{PLESK}) {next}
+ if ($sysinit eq "init") {
+ if (my @ls = grep {$_ =~ /^$service\b/} @chkconfig) {
+ if ($ls[0] =~ /\:on/) {push @enabled, $service}
+ }
+ } else {
+ if (my @ls = grep {$_ =~ /^$service\.service/} @chkconfig) {push @enabled, $service}
+ }
+ }
+ if (scalar @enabled > 0) {
+ my $list;
+ foreach my $service (@enabled) {
+ if (length($list) == 0) {
+ $list = $service;
+ } else {
+ $list .= ",".$service;
+ }
+ }
+ &addline("1","Check server services","On most servers the following services are not needed and should be stopped and disabled from starting unless used:$list
\nEach service can usually be disabled using:$disable ");
+ } else {
+ &addline("0","Check server services","On most servers the following services are not needed and should be stopped and disabled from starting unless used:none found
\nEach service can usually be disabled using:$disable ");
+ }
+ return;
+}
+# end servicescheck
+###############################################################################
+# start getportinfo
+sub getportinfo {
+ my $port = shift;
+ my $hit = 0;
+
+ foreach my $proto ("udp","tcp","udp6","tcp6") {
+ open (my $IN, "<", "/proc/net/$proto");
+ flock ($IN, LOCK_SH);
+ while (<$IN>) {
+ my @rec = split();
+ if ($rec[9] =~ /uid/) {next}
+ my (undef,$sport) = split(/:/,$rec[1]);
+ if (hex($sport) == $port) {$hit = 1}
+ }
+ close ($IN);
+ }
+
+ return $hit;
+}
+# end getportinfo
+###############################################################################
+
+1;
diff --git a/csf/ConfigServer/ServerStats.pm b/csf/ConfigServer/ServerStats.pm
new file mode 100644
index 0000000..2fdbd49
--- /dev/null
+++ b/csf/ConfigServer/ServerStats.pm
@@ -0,0 +1,3560 @@
+###############################################################################
+# Copyright (C) 2006-2025 Jonathan Michaelson
+#
+# https://github.com/waytotheweb/scripts
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, see .
+###############################################################################
+## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
+# start main
+package ConfigServer::ServerStats;
+
+use strict;
+use lib '/usr/local/csf/lib';
+use Fcntl qw(:DEFAULT :flock);
+
+use Exporter qw(import);
+our $VERSION = 1.02;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw();
+
+my %minmaxavg;
+
+# end main
+###############################################################################
+# start init
+sub init {
+ eval ('use GD::Graph::bars;'); ##no critic
+ if ($@) {return undef}
+ eval ('use GD::Graph::pie;'); ##no critic
+ if ($@) {return undef}
+ eval ('use GD::Graph::lines;'); ##no critic
+ if ($@) {return undef}
+}
+# end init
+###############################################################################
+# start graphs
+sub graphs {
+ my $type = shift;
+ my $system_maxdays = shift;
+ my $imghddir = shift;
+ my $img;
+ $| = 1;
+
+ require GD::Graph::bars;
+ import GD::Graph::bars;
+ require GD::Graph::pie;
+ import GD::Graph::pie;
+ require GD::Graph::lines;
+ import GD::Graph::lines;
+
+ sysopen (my $STATS,"/var/lib/csf/stats/system", O_RDWR | O_CREAT);
+ flock ($STATS, LOCK_SH);
+ my @stats = <$STATS>;
+ chomp @stats;
+ close ($STATS);
+
+ if (@stats > 1) {
+ local $SIG{__DIE__} = undef;
+ my $time = time;
+ my %stata;
+ foreach my $line (@stats) {
+ my ($thistime,undef) = split(/\,/,$line);
+ if (time - $thistime > (86400 * $system_maxdays)) {next}
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($thistime);
+ $stata{$year}{$mon}{$mday}{$hour}{$min} = $line;
+ }
+
+ if ($type eq "cpu") {
+ my (@h,@p,@t);
+ my $cputotal_prev;
+ my $cpuidle_prev;
+ my $cpuiowait_prev;
+ for (my $mins = 0; $mins < 60;$mins++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($mins * 60));
+ push @h,$min;
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$min});
+ if ($time eq "" or $cputotal eq "") {
+ $cputotal_prev = 0;
+ $cpuidle_prev = 0;
+ $cpuiowait_prev = 0;
+ push @p,undef;
+ push @t,undef;
+ } else {
+ my $idle_diff = $cpuidle - $cpuidle_prev;
+ my $iowait_diff = $cpuiowait - $cpuiowait_prev;
+ my $total_diff = $cputotal - $cputotal_prev;
+ if ($total_diff == 0) {
+ $cputotal_prev = 0;
+ $cpuidle_prev = 0;
+ $cpuiowait_prev = 0;
+ push @p,undef;
+ push @t,undef;
+ next;
+ }
+ my $idle_use = 100 - 100 * ($total_diff - $idle_diff) / $total_diff;
+ my $iowait_use = 100 - 100 * ($total_diff - $iowait_diff) / $total_diff;
+ $cpuidle_prev = $cpuidle;
+ $cpuiowait_prev = $cpuiowait;
+ $cputotal_prev = $cputotal;
+ push @p,$idle_use;
+ push @t,$iowait_use;
+
+ &minmaxavg("HOUR","1Idle",$idle_use);
+ &minmaxavg("HOUR","2IOWAIT",$iowait_use);
+ }
+ }
+ if ($minmaxavg{HOUR}{"1Idle"}{CNT} > 0) {$minmaxavg{HOUR}{"1Idle"}{AVG} /= $minmaxavg{HOUR}{"1Idle"}{CNT}}
+ if ($minmaxavg{HOUR}{"2IOWAIT"}{CNT} > 0) {$minmaxavg{HOUR}{"2IOWAIT"}{AVG} /= $minmaxavg{HOUR}{"2IOWAIT"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Minute',
+ y_label => '% CPU',
+ x_label_skip => 3,
+ line_width => 2,
+ title => 'CPU Usage in last hour',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Idle IOWAIT));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemhour.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "cpu") {
+ my (@h,@p,@t);
+ my $cputotal_prev;
+ my $cpuidle_prev;
+ my $cpuiowait_prev;
+ for (my $mins = 0; $mins < 1440;$mins++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($mins * 60));
+ push @h,$hour;
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$min});
+ if ($time eq "" or $cputotal eq "") {
+ $cputotal_prev = 0;
+ $cpuidle_prev = 0;
+ $cpuiowait_prev = 0;
+ push @p,undef;
+ push @t,undef;
+ } else {
+ my $idle_diff = $cpuidle - $cpuidle_prev;
+ my $iowait_diff = $cpuiowait - $cpuiowait_prev;
+ my $total_diff = $cputotal - $cputotal_prev;
+ if ($total_diff == 0) {
+ $cputotal_prev = 0;
+ $cpuidle_prev = 0;
+ $cpuiowait_prev = 0;
+ push @p,undef;
+ push @t,undef;
+ next;
+ }
+ my $idle_use = 100 - 100 * ($total_diff - $idle_diff) / $total_diff;
+ my $iowait_use = 100 - 100 * ($total_diff - $iowait_diff) / $total_diff;
+ $cpuidle_prev = $cpuidle;
+ $cpuiowait_prev = $cpuiowait;
+ $cputotal_prev = $cputotal;
+ push @p,$idle_use;
+ push @t,$iowait_use;
+
+ &minmaxavg("DAY","1Idle",$idle_use);
+ &minmaxavg("DAY","2IOWAIT",$iowait_use);
+ }
+ }
+ if ($minmaxavg{DAY}{"1Idle"}{CNT} > 0) {$minmaxavg{DAY}{"1Idle"}{AVG} /= $minmaxavg{DAY}{"1Idle"}{CNT}}
+ if ($minmaxavg{DAY}{"2IOWAIT"}{CNT} > 0) {$minmaxavg{DAY}{"2IOWAIT"}{AVG} /= $minmaxavg{DAY}{"2IOWAIT"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Hour',
+ y_label => '% CPU',
+ x_label_skip => 60,
+ title => 'CPU Usage in last 24 hours',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Idle IOWAIT));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemday.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "cpu") {
+ my (@h,@p,@t);
+ my $cputotal_prev;
+ my $cpuidle_prev;
+ my $cpuiowait_prev;
+ $minmaxavg{WEEK}{"1Idle"}{MIN} = 100;
+ $minmaxavg{WEEK}{"1Idle"}{MAX} = 0;
+ $minmaxavg{WEEK}{"2IOWAIT"}{MIN} = 100;
+ $minmaxavg{WEEK}{"2IOWAIT"}{MAX} = 0;
+ for (my $hours = 0; $hours < 168;$hours++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($hours * 60 * 60));
+ push @h,$mday;
+ my $idle_avg;
+ my $iowait_avg;
+ my $cnt_avg;
+ for (my $mins = 59; $mins >= 0;$mins--) {
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$mins});
+ if ($time and $cputotal ne "") {
+ my $idle_diff = $cpuidle - $cpuidle_prev;
+ my $iowait_diff = $cpuiowait - $cpuiowait_prev;
+ my $total_diff = $cputotal - $cputotal_prev;
+ if ($total_diff == 0) {
+ $cputotal_prev = 0;
+ $cpuidle_prev = 0;
+ $cpuiowait_prev = 0;
+ next;
+ }
+ my $idle_use = 100 - 100 * ($total_diff - $idle_diff) / $total_diff;
+ my $iowait_use = 100 - 100 * ($total_diff - $iowait_diff) / $total_diff;
+ $cpuidle_prev = $cpuidle;
+ $cpuiowait_prev = $cpuiowait;
+ $cputotal_prev = $cputotal;
+ $idle_avg += $idle_use;
+ $iowait_avg += $iowait_use;
+ $cnt_avg++;
+ } else {
+ $cputotal_prev = 0;
+ $cpuidle_prev = 0;
+ $cpuiowait_prev = 0;
+ }
+ }
+ unless (defined $cnt_avg) {
+ push @p,undef;
+ push @t,undef;
+ } else {
+ if ($cnt_avg == 0) {$cnt_avg = 1}
+ push @p,$idle_avg/$cnt_avg;
+ push @t,$iowait_avg/$cnt_avg;
+ &minmaxavg("WEEK","1Idle",($idle_avg/$cnt_avg));
+ &minmaxavg("WEEK","2IOWAIT",($iowait_avg/$cnt_avg));
+ }
+ }
+ if ($minmaxavg{WEEK}{"1Idle"}{CNT} > 0) {$minmaxavg{WEEK}{"1Idle"}{AVG} /= $minmaxavg{WEEK}{"1Idle"}{CNT}}
+ if ($minmaxavg{WEEK}{"2IOWAIT"}{CNT} > 0) {$minmaxavg{WEEK}{"2IOWAIT"}{AVG} /= $minmaxavg{WEEK}{"2IOWAIT"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Day (Hourly Average)',
+ y_label => '% CPU',
+ x_label_skip => 24,
+ title => 'CPU Usage in last 7 days',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Idle IOWAIT));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemweek.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "cpu") {
+ my (@h,@p,@t);
+ my $cputotal_prev;
+ my $cpuidle_prev;
+ my $cpuiowait_prev;
+ $minmaxavg{MONTH}{"1Idle"}{MIN} = 100;
+ $minmaxavg{MONTH}{"1Idle"}{MAX} = 0;
+ $minmaxavg{MONTH}{"2IOWAIT"}{MIN} = 100;
+ $minmaxavg{MONTH}{"2IOWAIT"}{MAX} = 0;
+ for (my $hours = 0; $hours < (24 * $system_maxdays);$hours++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($hours * 60 * 60));
+ push @h,$mday;
+ my $idle_avg;
+ my $iowait_avg;
+ my $cnt_avg;
+ for (my $mins = 59; $mins >= 0;$mins--) {
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$mins});
+ if ($time and $cputotal ne "") {
+ my $idle_diff = $cpuidle - $cpuidle_prev;
+ my $iowait_diff = $cpuiowait - $cpuiowait_prev;
+ my $total_diff = $cputotal - $cputotal_prev;
+ if ($total_diff == 0) {
+ $cputotal_prev = 0;
+ $cpuidle_prev = 0;
+ $cpuiowait_prev = 0;
+ next;
+ }
+ my $idle_use = 100 - 100 * ($total_diff - $idle_diff) / $total_diff;
+ my $iowait_use = 100 - 100 * ($total_diff - $iowait_diff) / $total_diff;
+ $cpuidle_prev = $cpuidle;
+ $cpuiowait_prev = $cpuiowait;
+ $cputotal_prev = $cputotal;
+ $idle_avg += $idle_use;
+ $iowait_avg += $iowait_use;
+ $cnt_avg++;
+ } else {
+ $cputotal_prev = 0;
+ $cpuidle_prev = 0;
+ $cpuiowait_prev = 0;
+ }
+ }
+ unless (defined $cnt_avg) {
+ push @p,undef;
+ push @t,undef;
+ } else {
+ if ($cnt_avg == 0) {$cnt_avg = 1}
+ push @p,$idle_avg/$cnt_avg;
+ push @t,$iowait_avg/$cnt_avg;
+ &minmaxavg("MONTH","1Idle",($idle_avg/$cnt_avg));
+ &minmaxavg("MONTH","2IOWAIT",($iowait_avg/$cnt_avg));
+ }
+ }
+ if ($minmaxavg{MONTH}{"1Idle"}{CNT} > 0) {$minmaxavg{MONTH}{"1Idle"}{AVG} /= $minmaxavg{MONTH}{"1Idle"}{CNT}}
+ if ($minmaxavg{MONTH}{"2IOWAIT"}{CNT} > 0) {$minmaxavg{MONTH}{"2IOWAIT"}{AVG} /= $minmaxavg{MONTH}{"2IOWAIT"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Day (Hourly Average)',
+ y_label => '% CPU',
+ x_label_skip => 24,
+ title => "CPU Usage in last $system_maxdays days",
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Idle IOWAIT));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemmonth.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "mem") {
+ my (@h,@p,@t,@c,@a,@b);
+ for (my $mins = 0; $mins < 60;$mins++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($mins * 60));
+ push @h,$min;
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$min});
+ if ($time eq "" or $memtotal eq "") {
+ push @p,undef;
+ push @t,undef;
+ push @c,undef;
+ push @a,undef;
+ push @b,undef;
+ } else {
+ $memfree = $memtotal - $memfree;
+ $memswapfree = $memswaptotal - $memswapfree;
+ push @p,$memtotal;
+ push @t,$memfree;
+ push @c,$memcached;
+ push @a,$memswaptotal;
+ push @b,$memswapfree;
+
+ &minmaxavg("HOUR","1Used",$memfree);
+ &minmaxavg("HOUR","2Cached",$memcached);
+ &minmaxavg("HOUR","3SwapUsed",$memswapfree);
+ }
+ }
+ if ($minmaxavg{HOUR}{"1Used"}{CNT} > 0) {$minmaxavg{HOUR}{"1Used"}{AVG} /= $minmaxavg{HOUR}{"1Used"}{CNT}}
+ if ($minmaxavg{HOUR}{"2Cached"}{CNT} > 0) {$minmaxavg{HOUR}{"2Cached"}{AVG} /= $minmaxavg{HOUR}{"2Cached"}{CNT}}
+ if ($minmaxavg{HOUR}{"3SwapUsed"}{CNT} > 0) {$minmaxavg{HOUR}{"3SwapUsed"}{AVG} /= $minmaxavg{HOUR}{"3SwapUsed"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t],[reverse @c],[reverse @a],[reverse @b]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred purple blue green) ] );
+ $hour_graph->set(
+ x_label => 'Minute',
+ y_label => 'Memory (KB)',
+ x_label_skip => 3,
+ title => 'Memory Usage in last hour',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Total Used Cached SwapTotal SwapUsed));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemhour.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "mem") {
+ my (@h,@p,@c,@t,@a,@b);
+ for (my $mins = 0; $mins < 1440;$mins++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($mins * 60));
+ push @h,$hour;
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$min});
+ if ($time eq "" or $memtotal eq "") {
+ push @p,undef;
+ push @t,undef;
+ push @c,undef;
+ push @a,undef;
+ push @b,undef;
+ } else {
+ $memfree = $memtotal - $memfree;
+ $memswapfree = $memswaptotal - $memswapfree;
+ push @p,$memtotal;
+ push @t,$memfree;
+ push @c,$memcached;
+ push @a,$memswaptotal;
+ push @b,$memswapfree;
+
+ &minmaxavg("DAY","1Used",$memfree);
+ &minmaxavg("DAY","2Cached",$memcached);
+ &minmaxavg("DAY","3SwapUsed",$memswapfree);
+ }
+ }
+ if ($minmaxavg{DAY}{"1Used"}{CNT} > 0) {$minmaxavg{DAY}{"1Used"}{AVG} /= $minmaxavg{DAY}{"1Used"}{CNT}}
+ if ($minmaxavg{DAY}{"2Cached"}{CNT} > 0) {$minmaxavg{DAY}{"2Cached"}{AVG} /= $minmaxavg{DAY}{"2Cached"}{CNT}}
+ if ($minmaxavg{DAY}{"3SwapUsed"}{CNT} > 0) {$minmaxavg{DAY}{"3SwapUsed"}{AVG} /= $minmaxavg{DAY}{"3SwapUsed"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t],[reverse @c],[reverse @a],[reverse @b]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred purple blue green) ] );
+ $hour_graph->set(
+ x_label => 'Hour',
+ y_label => 'Memory (KB)',
+ x_label_skip => 60,
+ title => 'Memory Usage in last 24 hours',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Total Used Cached SwapTotal SwapUsed));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemday.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "mem") {
+ my (@h,@p,@t,@c,@a,@b);
+ for (my $hours = 0; $hours < 168;$hours++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($hours * 60 * 60));
+ push @h,$mday;
+ my $memtotal_avg;
+ my $memfree_avg;
+ my $memcached_avg;
+ my $memswaptotal_avg;
+ my $memswapfree_avg;
+ my $cnt_avg;
+ for (my $mins = 59; $mins >= 0;$mins--) {
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$mins});
+ if ($time and $memtotal ne "") {
+ $memfree = $memtotal - $memfree;
+ $memswapfree = $memswaptotal - $memswapfree;
+ $memtotal_avg += $memtotal;
+ $memfree_avg += $memfree;
+ $memcached_avg += $memcached;
+ $memswaptotal_avg += $memswaptotal;
+ $memswapfree_avg += $memswapfree;
+ $cnt_avg++;
+ }
+ }
+ unless (defined $cnt_avg) {
+ push @p,undef;
+ push @t,undef;
+ push @c,undef;
+ push @a,undef;
+ push @b,undef;
+ } else {
+ if ($cnt_avg == 0) {$cnt_avg = 1}
+ push @p,$memtotal_avg/$cnt_avg;
+ push @t,$memfree_avg/$cnt_avg;
+ push @c,$memcached_avg/$cnt_avg;
+ push @a,$memswaptotal_avg/$cnt_avg;
+ push @b,$memswapfree_avg/$cnt_avg;
+
+ &minmaxavg("WEEK","1Used",($memfree_avg/$cnt_avg));
+ &minmaxavg("WEEK","2Cached",($memcached_avg/$cnt_avg));
+ &minmaxavg("WEEK","3SwapUsed",($memswapfree_avg/$cnt_avg));
+ }
+ }
+ if ($minmaxavg{WEEK}{"1Used"}{CNT} > 0) {$minmaxavg{WEEK}{"1Used"}{AVG} /= $minmaxavg{WEEK}{"1Used"}{CNT}}
+ if ($minmaxavg{WEEK}{"2Cached"}{CNT} > 0) {$minmaxavg{WEEK}{"2Cached"}{AVG} /= $minmaxavg{WEEK}{"2Cached"}{CNT}}
+ if ($minmaxavg{WEEK}{"3SwapUsed"}{CNT} > 0) {$minmaxavg{WEEK}{"3SwapUsed"}{AVG} /= $minmaxavg{WEEK}{"3SwapUsed"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t],[reverse @c],[reverse @a],[reverse @b]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred purple blue green) ] );
+ $hour_graph->set(
+ x_label => 'Day (Hourly Average)',
+ y_label => 'Memory (KB)',
+ x_label_skip => 24,
+ title => 'Memory Usage in last 7 days',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Total Used Cached SwapTotal SwapUsed));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemweek.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "mem") {
+ my (@h,@p,@t,@c,@a,@b);
+ for (my $hours = 0; $hours < (24 * $system_maxdays);$hours++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($hours * 60 * 60));
+ push @h,$mday;
+ my $memtotal_avg;
+ my $memfree_avg;
+ my $memcached_avg;
+ my $memswaptotal_avg;
+ my $memswapfree_avg;
+ my $cnt_avg;
+ for (my $mins = 59; $mins >= 0;$mins--) {
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$mins});
+ if ($time and $memtotal ne "") {
+ $memfree = $memtotal - $memfree;
+ $memswapfree = $memswaptotal - $memswapfree;
+ $memtotal_avg += $memtotal;
+ $memfree_avg += $memfree;
+ $memcached_avg += $memcached;
+ $memswaptotal_avg += $memswaptotal;
+ $memswapfree_avg += $memswapfree;
+ $cnt_avg++;
+ }
+ }
+ unless (defined $cnt_avg) {
+ push @p,undef;
+ push @t,undef;
+ push @c,undef;
+ push @a,undef;
+ push @b,undef;
+ } else {
+ if ($cnt_avg == 0) {$cnt_avg = 1}
+ push @p,$memtotal_avg/$cnt_avg;
+ push @t,$memfree_avg/$cnt_avg;
+ push @c,$memcached_avg/$cnt_avg;
+ push @a,$memswaptotal_avg/$cnt_avg;
+ push @b,$memswapfree_avg/$cnt_avg;
+
+ &minmaxavg("MONTH","1Used",($memfree_avg/$cnt_avg));
+ &minmaxavg("MONTH","2Cached",($memcached_avg/$cnt_avg));
+ &minmaxavg("MONTH","3SwapUsed",($memswapfree_avg/$cnt_avg));
+ }
+ }
+ if ($minmaxavg{MONTH}{"1Used"}{CNT} > 0) {$minmaxavg{MONTH}{"1Used"}{AVG} /= $minmaxavg{MONTH}{"1Used"}{CNT}}
+ if ($minmaxavg{MONTH}{"2Cached"}{CNT} > 0) {$minmaxavg{MONTH}{"2Cached"}{AVG} /= $minmaxavg{MONTH}{"2Cached"}{CNT}}
+ if ($minmaxavg{MONTH}{"3SwapUsed"}{CNT} > 0) {$minmaxavg{MONTH}{"3SwapUsed"}{AVG} /= $minmaxavg{MONTH}{"3SwapUsed"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t],[reverse @c],[reverse @a],[reverse @b]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred purple blue green) ] );
+ $hour_graph->set(
+ x_label => 'Day (Hourly Average)',
+ y_label => 'Memory (Bytes)',
+ x_label_skip => 24,
+ title => "Memory Usage in last $system_maxdays days",
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Total Used Cached SwapTotal SwapUsed));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemmonth.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "load") {
+ my (@h,@p,@t,@a);
+ for (my $mins = 0; $mins < 60;$mins++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($mins * 60));
+ push @h,$min;
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$min});
+ if ($time eq "" or $load1 eq "") {
+ push @p,undef;
+ push @t,undef;
+ push @a,undef;
+ } else {
+ push @p,$load1;
+ push @t,$load5;
+ push @a,$load15;
+
+ &minmaxavg("HOUR","1Load_1",$load1);
+ &minmaxavg("HOUR","2Load_5",$load5);
+ &minmaxavg("HOUR","3Load_15",$load15);
+ }
+ }
+ if ($minmaxavg{HOUR}{"1Load_1"}{CNT} > 0) {$minmaxavg{HOUR}{"1Load_1"}{AVG} /= $minmaxavg{HOUR}{"1Load_1"}{CNT}}
+ if ($minmaxavg{HOUR}{"2Load_5"}{CNT} > 0) {$minmaxavg{HOUR}{"2Load_5"}{AVG} /= $minmaxavg{HOUR}{"2Load_5"}{CNT}}
+ if ($minmaxavg{HOUR}{"3Load_15"}{CNT} > 0) {$minmaxavg{HOUR}{"3Load_15"}{AVG} /= $minmaxavg{HOUR}{"3Load_15"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t],[reverse @a]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred purple) ] );
+ $hour_graph->set(
+ x_label => 'Minute',
+ y_label => 'Load Average',
+ x_label_skip => 3,
+ title => 'Load Averages in last hour',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Load_1 Load_5 Load_15));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemhour.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "load") {
+ my (@h,@p,@t,@a);
+ for (my $mins = 0; $mins < 1440;$mins++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($mins * 60));
+ push @h,$hour;
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$min});
+ if ($time eq "" or $load1 eq "") {
+ push @p,undef;
+ push @t,undef;
+ push @a,undef;
+ } else {
+ push @p,$load1;
+ push @t,$load5;
+ push @a,$load15;
+
+ &minmaxavg("DAY","1Load_1",$load1);
+ &minmaxavg("DAY","2Load_5",$load5);
+ &minmaxavg("DAY","3Load_15",$load15);
+ }
+ }
+ if ($minmaxavg{DAY}{"1Load_1"}{CNT} > 0) {$minmaxavg{DAY}{"1Load_1"}{AVG} /= $minmaxavg{DAY}{"1Load_1"}{CNT}}
+ if ($minmaxavg{DAY}{"2Load_5"}{CNT} > 0) {$minmaxavg{DAY}{"2Load_5"}{AVG} /= $minmaxavg{DAY}{"2Load_5"}{CNT}}
+ if ($minmaxavg{DAY}{"3Load_15"}{CNT} > 0) {$minmaxavg{DAY}{"3Load_15"}{AVG} /= $minmaxavg{DAY}{"3Load_15"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t],[reverse @a]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred purple) ] );
+ $hour_graph->set(
+ x_label => 'Hour',
+ y_label => 'Load Average',
+ x_label_skip => 60,
+ title => 'Load Averages in last 24 hours',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Load_1 Load_5 Load_15));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemday.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "load") {
+ my (@h,@p,@t,@a);
+ for (my $hours = 0; $hours < 168;$hours++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($hours * 60 * 60));
+ push @h,$mday;
+ my $load1_avg;
+ my $load5_avg;
+ my $load15_avg;
+ my $cnt_avg = 0;
+ for (my $mins = 59; $mins >= 0;$mins--) {
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$mins});
+ if ($time and $load1 ne "") {
+ $load1_avg += $load1;
+ $load5_avg += $load5;
+ $load15_avg += $load15;
+ $cnt_avg++;
+ }
+ }
+ unless (defined $cnt_avg) {
+ push @p,undef;
+ push @t,undef;
+ push @a,undef;
+ } else {
+ if ($cnt_avg == 0) {$cnt_avg = 1}
+ push @p,$load1_avg/$cnt_avg;
+ push @t,$load5_avg/$cnt_avg;
+ push @a,$load15_avg/$cnt_avg;
+
+ &minmaxavg("WEEK","1Load_1",($load1_avg/$cnt_avg));
+ &minmaxavg("WEEK","2Load_5",($load5_avg/$cnt_avg));
+ &minmaxavg("WEEK","3Load_15",($load15_avg/$cnt_avg));
+ }
+ }
+ if ($minmaxavg{WEEK}{"1Load_1"}{CNT} > 0) {$minmaxavg{WEEK}{"1Load_1"}{AVG} /= $minmaxavg{WEEK}{"1Load_1"}{CNT}}
+ if ($minmaxavg{WEEK}{"2Load_5"}{CNT} > 0) {$minmaxavg{WEEK}{"2Load_5"}{AVG} /= $minmaxavg{WEEK}{"2Load_5"}{CNT}}
+ if ($minmaxavg{WEEK}{"3Load_15"}{CNT} > 0) {$minmaxavg{WEEK}{"3Load_15"}{AVG} /= $minmaxavg{WEEK}{"3Load_15"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t],[reverse @a]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred purple) ] );
+ $hour_graph->set(
+ x_label => 'Day (Hourly Average)',
+ y_label => 'Load Average',
+ x_label_skip => 24,
+ title => 'Load Averages in last 7 days',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Load_1 Load_5 Load_15));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemweek.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "load") {
+ my (@h,@p,@t,@a);
+ for (my $hours = 0; $hours < (24 * $system_maxdays);$hours++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($hours * 60 * 60));
+ push @h,$mday;
+ my $load1_avg;
+ my $load5_avg;
+ my $load15_avg;
+ my $cnt_avg = 0;
+ for (my $mins = 59; $mins >= 0;$mins--) {
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$mins});
+ if ($time and $load1 ne "") {
+ $load1_avg += $load1;
+ $load5_avg += $load5;
+ $load15_avg += $load15;
+ $cnt_avg++;
+ }
+ }
+ unless (defined $cnt_avg) {
+ push @p,undef;
+ push @t,undef;
+ push @a,undef;
+ } else {
+ if ($cnt_avg == 0) {$cnt_avg = 1}
+ push @p,$load1_avg/$cnt_avg;
+ push @t,$load5_avg/$cnt_avg;
+ push @a,$load15_avg/$cnt_avg;
+
+ &minmaxavg("MONTH","1Load_1",($load1_avg/$cnt_avg));
+ &minmaxavg("MONTH","2Load_5",($load5_avg/$cnt_avg));
+ &minmaxavg("MONTH","3Load_15",($load15_avg/$cnt_avg));
+ }
+ }
+ if ($minmaxavg{MONTH}{"1Load_1"}{CNT} > 0) {$minmaxavg{MONTH}{"1Load_1"}{AVG} /= $minmaxavg{MONTH}{"1Load_1"}{CNT}}
+ if ($minmaxavg{MONTH}{"2Load_5"}{CNT} > 0) {$minmaxavg{MONTH}{"2Load_5"}{AVG} /= $minmaxavg{MONTH}{"2Load_5"}{CNT}}
+ if ($minmaxavg{MONTH}{"3Load_15"}{CNT} > 0) {$minmaxavg{MONTH}{"3Load_15"}{AVG} /= $minmaxavg{MONTH}{"3Load_15"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t],[reverse @a]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred purple blue) ] );
+ $hour_graph->set(
+ x_label => 'Day (Hourly Average)',
+ y_label => 'Load Average',
+ x_label_skip => 24,
+ title => "Load Averages in last $system_maxdays days",
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Load_1 Load_5 Load_15));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemmonth.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "net") {
+ my (@h,@p,@t);
+ my $netin_prev;
+ my $netout_prev;
+ for (my $mins = 0; $mins < 60;$mins++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($mins * 60));
+ push @h,$min;
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$min});
+ if ($time eq "" or $netin eq "") {
+ $netin_prev = 0;
+ $netout_prev = 0;
+ push @p,undef;
+ push @t,undef;
+ } else {
+ if ($netin_prev < $netin or $netin eq "") {
+ push @p,undef;
+ $netin_prev = $netin;
+ } else {
+ my $netin_val = ($netin_prev - $netin) / 60;
+ push @p,$netin_val;
+ $netin_prev = $netin;
+ &minmaxavg("HOUR","1Inbound",$netin_val);
+ }
+ if ($netout_prev < $netout or $netout eq "") {
+ push @t,undef;
+ $netout_prev = $netout;
+ } else {
+ my $netout_val = ($netout_prev - $netout) / 60;
+ push @t,$netout_val;
+ $netout_prev = $netout;
+ &minmaxavg("HOUR","2Outbound",$netout_val);
+ }
+ }
+ }
+ if ($minmaxavg{HOUR}{"1Inbound"}{CNT} > 0) {$minmaxavg{HOUR}{"1Inbound"}{AVG} /= $minmaxavg{HOUR}{"1Inbound"}{CNT}}
+ if ($minmaxavg{HOUR}{"2Outbound"}{CNT} > 0) {$minmaxavg{HOUR}{"2Outbound"}{AVG} /= $minmaxavg{HOUR}{"2Outbound"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Minute',
+ y_label => 'Bytes/Second',
+ x_label_skip => 3,
+ title => 'Network Usage in last hour',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Inbound Outbound));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemhour.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "net") {
+ my (@h,@p,@t);
+ my $netin_prev;
+ my $netout_prev;
+ for (my $mins = 0; $mins < 1440;$mins++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($mins * 60));
+ push @h,$hour;
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$min});
+ if ($time eq "" or $netin eq "") {
+ $netin_prev = 0;
+ $netout_prev = 0;
+ push @p,undef;
+ push @t,undef;
+ } else {
+ if ($netin_prev < $netin or $netin eq "") {
+ push @p,undef;
+ $netin_prev = $netin;
+ } else {
+ my $netin_val = ($netin_prev - $netin) / 60;
+ push @p,$netin_val;
+ $netin_prev = $netin;
+ &minmaxavg("DAY","1Inbound",$netin_val);
+ }
+ if ($netout_prev < $netout or $netout eq "") {
+ push @t,undef;
+ $netout_prev = $netout;
+ } else {
+ my $netout_val = ($netout_prev - $netout) / 60;
+ push @t,$netout_val;
+ $netout_prev = $netout;
+ &minmaxavg("DAY","2Outbound",$netout_val);
+ }
+ }
+ }
+ if ($minmaxavg{DAY}{"1Inbound"}{CNT} > 0) {$minmaxavg{DAY}{"1Inbound"}{AVG} /= $minmaxavg{DAY}{"1Inbound"}{CNT}}
+ if ($minmaxavg{DAY}{"2Outbound"}{CNT} > 0) {$minmaxavg{DAY}{"2Outbound"}{AVG} /= $minmaxavg{DAY}{"2Outbound"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Hour',
+ y_label => 'Bytes/Second',
+ x_label_skip => 60,
+ title => 'Network Usage in last 24 hours',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Inbound Outbound));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemday.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "net") {
+ my (@h,@p,@t);
+ my $netin_prev;
+ my $netout_prev;
+ for (my $hours = 0; $hours < 168;$hours++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($hours * 60 * 60));
+ push @h,$mday;
+ my $netin_avg;
+ my $netout_avg;
+ for (my $mins = 59; $mins >= 0;$mins--) {
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$mins});
+ if ($time eq "" or $netin eq "") {
+ $netin_prev = 0;
+ $netout_prev = 0;
+ } else {
+ if ($netin_prev < $netin or $netin eq "") {
+ $netin_prev = $netin;
+ } else {
+ my $netin_val = ($netin_prev - $netin) / 60;
+ $netin_avg = $netin_avg + $netin_val;
+ $netin_prev = $netin;
+ }
+ if ($netout_prev < $netout or $netout eq "") {
+ $netout_prev = $netout;
+ } else {
+ my $netout_val = ($netout_prev - $netout) / 60;
+ $netout_avg = $netout_avg + $netout_val;
+ $netout_prev = $netout;
+ }
+ }
+ }
+ unless (defined $netin_avg) {
+ push @p,undef;
+ } else {
+ push @p,($netin_avg/60);
+ &minmaxavg("WEEK","1Inbound",($netin_avg/60));
+ }
+ unless (defined $netout_avg) {
+ push @t,undef;
+ } else {
+ push @t,($netout_avg/60);
+ &minmaxavg("WEEK","2Outbound",($netout_avg/60));
+ }
+ }
+ if ($minmaxavg{WEEK}{"1Inbound"}{CNT} > 0) {$minmaxavg{WEEK}{"1Inbound"}{AVG} /= $minmaxavg{WEEK}{"1Inbound"}{CNT}}
+ if ($minmaxavg{WEEK}{"2Outbound"}{CNT} > 0) {$minmaxavg{WEEK}{"2Outbound"}{AVG} /= $minmaxavg{WEEK}{"2Outbound"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Day (Hourly Average)',
+ y_label => 'Bytes/Second',
+ x_label_skip => 24,
+ title => 'Network Usage in last 7 days',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Inbound Outbound));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemweek.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "net") {
+ my (@h,@p,@t);
+ my $netin_prev;
+ my $netout_prev;
+ for (my $hours = 0; $hours < (24 * $system_maxdays);$hours++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($hours * 60 * 60));
+ push @h,$mday;
+ my $netin_avg;
+ my $netout_avg;
+ for (my $mins = 59; $mins >= 0;$mins--) {
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$mins});
+ if ($time eq "" or $netin eq "") {
+ $netin_prev = 0;
+ $netout_prev = 0;
+ } else {
+ if ($netin_prev < $netin or $netin eq "") {
+ $netin_prev = $netin;
+ } else {
+ my $netin_val = ($netin_prev - $netin) / 60;
+ $netin_avg = $netin_avg + $netin_val;
+ $netin_prev = $netin;
+ }
+ if ($netout_prev < $netout or $netout eq "") {
+ $netout_prev = $netout;
+ } else {
+ my $netout_val = ($netout_prev - $netout) / 60;
+ $netout_avg = $netout_avg + $netout_val;
+ $netout_prev = $netout;
+ }
+ }
+ }
+ unless (defined $netin_avg) {
+ push @p,undef;
+ } else {
+ push @p,($netin_avg/60);
+ &minmaxavg("MONTH","1Inbound",($netin_avg/60));
+ }
+ unless (defined $netout_avg) {
+ push @t,undef;
+ } else {
+ push @t,($netout_avg/60);
+ &minmaxavg("MONTH","2Outbound",($netout_avg/60));
+ }
+ }
+ if ($minmaxavg{MONTH}{"1Inbound"}{CNT} > 0) {$minmaxavg{MONTH}{"1Inbound"}{AVG} /= $minmaxavg{MONTH}{"1Inbound"}{CNT}}
+ if ($minmaxavg{MONTH}{"2Outbound"}{CNT} > 0) {$minmaxavg{MONTH}{"2Outbound"}{AVG} /= $minmaxavg{MONTH}{"2Outbound"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Day (Hourly Average)',
+ y_label => 'Bytes/Second',
+ x_label_skip => 24,
+ title => "Network Usage in last $system_maxdays days",
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Inbound Outbound));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemmonth.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "disk") {
+ my (@h,@p,@t);
+ my $diskread_prev;
+ my $diskwrite_prev;
+ for (my $mins = 0; $mins < 60;$mins++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($mins * 60));
+ push @h,$min;
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$min});
+ if ($time eq "" or $diskread eq "") {
+ $diskread_prev = 0;
+ $diskwrite_prev = 0;
+ push @p,undef;
+ push @t,undef;
+ } else {
+ if ($diskread_prev < $diskread or $diskread eq "") {
+ push @p,undef;
+ $diskread_prev = $diskread;
+ } else {
+ my $diskread_val = ($diskread_prev - $diskread) / 60;
+ push @p,$diskread_val;
+ $diskread_prev = $diskread;
+ &minmaxavg("HOUR","1Reads",$diskread_val);
+ }
+ if ($diskwrite_prev < $diskwrite or $diskwrite eq "") {
+ push @t,undef;
+ $diskwrite_prev = $diskwrite;
+ } else {
+ my $diskwrite_val = ($diskwrite_prev - $diskwrite) / 60;
+ push @t,$diskwrite_val;
+ $diskwrite_prev = $diskwrite;
+ &minmaxavg("HOUR","2Writes",$diskwrite_val);
+ }
+ }
+ }
+ if ($minmaxavg{HOUR}{"1Reads"}{CNT} > 0) {$minmaxavg{HOUR}{"1Reads"}{AVG} /= $minmaxavg{HOUR}{"1Reads"}{CNT}}
+ if ($minmaxavg{HOUR}{"2Writes"}{CNT} > 0) {$minmaxavg{HOUR}{"2Writes"}{AVG} /= $minmaxavg{HOUR}{"2Writes"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Minute',
+ y_label => 'IO/Second',
+ x_label_skip => 3,
+ title => 'Disk Usage in last hour',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Reads Writes));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemhour.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "disk") {
+ my (@h,@p,@t);
+ my $diskread_prev;
+ my $diskwrite_prev;
+ for (my $mins = 0; $mins < 1440;$mins++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($mins * 60));
+ push @h,$hour;
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$min});
+ if ($time eq "" or $diskread eq "") {
+ $diskread_prev = 0;
+ $diskwrite_prev = 0;
+ push @p,undef;
+ push @t,undef;
+ } else {
+ if ($diskread_prev < $diskread or $diskread eq "") {
+ push @p,undef;
+ $diskread_prev = $diskread;
+ } else {
+ my $diskread_val = ($diskread_prev - $diskread) / 60;
+ push @p,$diskread_val;
+ $diskread_prev = $diskread;
+ &minmaxavg("DAY","1Reads",$diskread_val);
+ }
+ if ($diskwrite_prev < $diskwrite or $diskwrite eq "") {
+ push @t,undef;
+ $diskwrite_prev = $diskwrite;
+ } else {
+ my $diskwrite_val = ($diskwrite_prev - $diskwrite) / 60;
+ push @t,$diskwrite_val;
+ $diskwrite_prev = $diskwrite;
+ &minmaxavg("DAY","2Writes",$diskwrite_val);
+ }
+ }
+ }
+ if ($minmaxavg{DAY}{"1Reads"}{CNT} > 0) {$minmaxavg{DAY}{"1Reads"}{AVG} /= $minmaxavg{DAY}{"1Reads"}{CNT}}
+ if ($minmaxavg{DAY}{"2Writes"}{CNT} > 0) {$minmaxavg{DAY}{"2Writes"}{AVG} /= $minmaxavg{DAY}{"2Writes"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Hour',
+ y_label => 'IO/Second',
+ x_label_skip => 60,
+ title => 'Disk Usage in last 24 hours',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Reads Writes));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemday.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "disk") {
+ my (@h,@p,@t);
+ my $diskread_prev;
+ my $diskwrite_prev;
+ for (my $hours = 0; $hours < 168;$hours++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($hours * 60 * 60));
+ push @h,$mday;
+ my $diskread_avg;
+ my $diskwrite_avg;
+ for (my $mins = 59; $mins >= 0;$mins--) {
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$mins});
+ if ($time eq "" or $diskread eq "") {
+ $diskread_prev = 0;
+ $diskwrite_prev = 0;
+ } else {
+ if ($diskread_prev < $diskread or $diskread eq "") {
+ $diskread_prev = $diskread;
+ } else {
+ $diskread_avg = $diskread_avg + ($diskread_prev - $diskread)/60;
+ $diskread_prev = $diskread;
+ }
+ if ($diskwrite_prev < $diskwrite or $diskwrite eq "") {
+ $diskwrite_prev = $diskwrite;
+ } else {
+ $diskwrite_avg = $diskwrite_avg + ($diskwrite_prev - $diskwrite)/60;
+ $diskwrite_prev = $diskwrite;
+ }
+ }
+ }
+ unless (defined $diskread_avg) {
+ push @p,undef;
+ } else {
+ push @p,($diskread_avg/60);
+ &minmaxavg("WEEK","1Reads",($diskread_avg/60));
+ }
+ unless (defined $diskwrite_avg) {
+ push @t,undef;
+ } else {
+ push @t,($diskwrite_avg/60);
+ &minmaxavg("WEEK","2Writes",($diskwrite_avg/60));
+ }
+ }
+ if ($minmaxavg{WEEK}{"1Reads"}{CNT} > 0) {$minmaxavg{WEEK}{"1Reads"}{AVG} /= $minmaxavg{WEEK}{"1Reads"}{CNT}}
+ if ($minmaxavg{WEEK}{"2Writes"}{CNT} > 0) {$minmaxavg{WEEK}{"2Writes"}{AVG} /= $minmaxavg{WEEK}{"2Writes"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Day (Hourly Average)',
+ y_label => 'IO/Second',
+ x_label_skip => 24,
+ title => 'Disk Usage in last 7 days',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Reads Writes));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemweek.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "disk") {
+ my (@h,@p,@t);
+ my $diskread_prev;
+ my $diskwrite_prev;
+ for (my $hours = 0; $hours < (24 * $system_maxdays);$hours++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($hours * 60 * 60));
+ push @h,$mday;
+ my $diskread_avg;
+ my $diskwrite_avg;
+ for (my $mins = 59; $mins >= 0;$mins--) {
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$mins});
+ if ($time eq "" or $diskread eq "") {
+ $diskread_prev = 0;
+ $diskwrite_prev = 0;
+ } else {
+ if ($diskread_prev < $diskread or $diskread eq "") {
+ $diskread_prev = $diskread;
+ } else {
+ $diskread_avg = $diskread_avg + ($diskread_prev - $diskread)/60;
+ $diskread_prev = $diskread;
+ }
+ if ($diskwrite_prev < $diskwrite or $diskwrite eq "") {
+ $diskwrite_prev = $diskwrite;
+ } else {
+ $diskwrite_avg = $diskwrite_avg + ($diskwrite_prev - $diskwrite)/60;
+ $diskwrite_prev = $diskwrite;
+ }
+ }
+ }
+ unless (defined $diskread_avg) {
+ push @p,undef;
+ } else {
+ push @p,($diskread_avg/60);
+ &minmaxavg("MONTH","1Reads",($diskread_avg/60));
+ }
+ unless (defined $diskwrite_avg) {
+ push @t,undef;
+ } else {
+ push @t,($diskwrite_avg/60);
+ &minmaxavg("MONTH","2Writes",($diskwrite_avg/60));
+ }
+ }
+ if ($minmaxavg{MONTH}{"1Reads"}{CNT} > 0) {$minmaxavg{MONTH}{"1Reads"}{AVG} /= $minmaxavg{MONTH}{"1Reads"}{CNT}}
+ if ($minmaxavg{MONTH}{"2Writes"}{CNT} > 0) {$minmaxavg{MONTH}{"2Writes"}{AVG} /= $minmaxavg{MONTH}{"2Writes"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Day (Hourly Average)',
+ y_label => 'IO/Second',
+ x_label_skip => 24,
+ title => "Disk Usage in last $system_maxdays days",
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Reads Writes));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemmonth.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "email") {
+ my (@h,@p,@t);
+ for (my $mins = 0; $mins < 60;$mins++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($mins * 60));
+ push @h,$min;
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$min});
+ if ($time eq "" or $mailin eq "") {
+ push @p,undef;
+ push @t,undef;
+ } else {
+ push @p,$mailin;
+ push @t,$mailout;
+
+ &minmaxavg("HOUR","1Received",$mailin);
+ &minmaxavg("HOUR","2Sent",$mailout);
+ }
+ }
+ if ($minmaxavg{HOUR}{"1Received"}{CNT} > 0) {$minmaxavg{HOUR}{"1Received"}{AVG} /= $minmaxavg{HOUR}{"1Received"}{CNT}}
+ if ($minmaxavg{HOUR}{"2Sent"}{CNT} > 0) {$minmaxavg{HOUR}{"2Sent"}{AVG} /= $minmaxavg{HOUR}{"2Sent"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Minute',
+ y_label => 'Emails',
+ x_label_skip => 3,
+ title => 'Email Usage in last hour',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Received Sent));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemhour.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "email") {
+ my (@h,@p,@t);
+ for (my $mins = 0; $mins < 1440;$mins++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($mins * 60));
+ push @h,$hour;
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$min});
+ if ($time eq "" or $mailin eq "") {
+ push @p,undef;
+ push @t,undef;
+ } else {
+ push @p,$mailin;
+ push @t,$mailout;
+
+ &minmaxavg("DAY","1Received",$mailin);
+ &minmaxavg("DAY","2Sent",$mailout);
+ }
+ }
+ if ($minmaxavg{DAY}{"1Received"}{CNT} > 0) {$minmaxavg{DAY}{"1Received"}{AVG} /= $minmaxavg{DAY}{"1Received"}{CNT}}
+ if ($minmaxavg{DAY}{"2Sent"}{CNT} > 0) {$minmaxavg{DAY}{"2Sent"}{AVG} /= $minmaxavg{DAY}{"2Sent"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred purple) ] );
+ $hour_graph->set(
+ x_label => 'Hour',
+ y_label => 'Emails',
+ x_label_skip => 60,
+ title => 'Email Usage in last 24 hours',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Received Sent));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemday.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "email") {
+ my (@h,@p,@t);
+ for (my $hours = 0; $hours < 168;$hours++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($hours * 60 * 60));
+ push @h,$mday;
+ my $mailin_avg;
+ my $mailout_avg;
+ my $cnt_avg = 0;
+ for (my $mins = 59; $mins >= 0;$mins--) {
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$mins});
+ if ($time and $mailin ne "") {
+ $mailin_avg += $mailin;
+ $mailout_avg += $mailout;
+ $cnt_avg++;
+ }
+ }
+ unless (defined $cnt_avg) {
+ push @p,undef;
+ push @t,undef;
+ } else {
+ if ($cnt_avg == 0) {$cnt_avg = 1}
+ push @p,$mailin_avg/$cnt_avg;
+ push @t,$mailout_avg/$cnt_avg;
+
+ &minmaxavg("WEEK","1Received",($mailin_avg/$cnt_avg));
+ &minmaxavg("WEEK","2Sent",($mailout_avg/$cnt_avg));
+ }
+ }
+ if ($minmaxavg{WEEK}{"1Received"}{CNT} > 0) {$minmaxavg{WEEK}{"1Received"}{AVG} /= $minmaxavg{WEEK}{"1Received"}{CNT}}
+ if ($minmaxavg{WEEK}{"2Sent"}{CNT} > 0) {$minmaxavg{WEEK}{"2Sent"}{AVG} /= $minmaxavg{WEEK}{"2Sent"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Day (Hourly Average)',
+ y_label => 'Emails',
+ x_label_skip => 24,
+ title => 'Email Usage in last 7 days',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Received Sent));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemweek.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "email") {
+ my (@h,@p,@t);
+ for (my $hours = 0; $hours < (24 * $system_maxdays);$hours++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($hours * 60 * 60));
+ push @h,$mday;
+ my $mailin_avg;
+ my $mailout_avg;
+ my $cnt_avg = 0;
+ for (my $mins = 59; $mins >= 0;$mins--) {
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$mins});
+ if ($time and $mailin ne "") {
+ $mailin_avg += $mailin;
+ $mailout_avg += $mailout;
+ $cnt_avg++;
+ }
+ }
+ unless (defined $cnt_avg) {
+ push @p,undef;
+ push @t,undef;
+ } else {
+ if ($cnt_avg == 0) {$cnt_avg = 1}
+ push @p,$mailin_avg/$cnt_avg;
+ push @t,$mailout_avg/$cnt_avg;
+
+ &minmaxavg("MONTH","1Received",($mailin_avg/$cnt_avg));
+ &minmaxavg("MONTH","2Sent",($mailout_avg/$cnt_avg));
+ }
+ }
+ if ($minmaxavg{MONTH}{"1Received"}{CNT} > 0) {$minmaxavg{MONTH}{"1Received"}{AVG} /= $minmaxavg{MONTH}{"1Received"}{CNT}}
+ if ($minmaxavg{MONTH}{"2Sent"}{CNT} > 0) {$minmaxavg{MONTH}{"2Sent"}{AVG} /= $minmaxavg{MONTH}{"2Sent"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Day (Hourly Average)',
+ y_label => 'Emails',
+ x_label_skip => 24,
+ title => "Email Usage in last $system_maxdays",
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Received Sent));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemmonth.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "temp") {
+ my (@h,@p);
+ for (my $mins = 0; $mins < 60;$mins++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($mins * 60));
+ push @h,$min;
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$min});
+ if ($time eq "" or $cputemp eq "") {
+ push @p,undef;
+ } else {
+ push @p,$cputemp;
+
+ &minmaxavg("HOUR","1CPU",$cputemp);
+ }
+ }
+ if ($minmaxavg{HOUR}{"1CPU"}{CNT} > 0) {$minmaxavg{HOUR}{"1CPU"}{AVG} /= $minmaxavg{HOUR}{"1CPU"}{CNT}}
+ my @data = ([reverse @h],[reverse @p]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Minute',
+ y_label => 'Centigrade',
+ x_label_skip => 3,
+ title => 'CPU Temp in last hour',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend("Highest Core Temperature");
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemhour.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "temp") {
+ my (@h,@p);
+ for (my $mins = 0; $mins < 1440;$mins++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($mins * 60));
+ push @h,$hour;
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$min});
+ if ($time eq "" or $cputemp eq "") {
+ push @p,undef;
+ } else {
+ push @p,$cputemp;
+
+ &minmaxavg("DAY","1CPU",$cputemp);
+ }
+ }
+ if ($minmaxavg{DAY}{"1CPU"}{CNT} > 0) {$minmaxavg{DAY}{"1CPU"}{AVG} /= $minmaxavg{DAY}{"1CPU"}{CNT}}
+ my @data = ([reverse @h],[reverse @p]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred purple) ] );
+ $hour_graph->set(
+ x_label => 'Hour',
+ y_label => 'Centigrade',
+ x_label_skip => 60,
+ title => 'CPU Temp in last 24 hours',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend("Highest Core Temperature");
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemday.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "temp") {
+ my (@h,@p);
+ for (my $hours = 0; $hours < 168;$hours++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($hours * 60 * 60));
+ push @h,$mday;
+ my $cputemp_avg;
+ my $cnt_avg = 0;
+ for (my $mins = 59; $mins >= 0;$mins--) {
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$mins});
+ if ($time and $cputemp ne "") {
+ $cputemp_avg += $cputemp;
+ $cnt_avg++;
+ }
+ }
+ unless (defined $cnt_avg) {
+ push @p,undef;
+ } else {
+ if ($cnt_avg == 0) {$cnt_avg = 1}
+ push @p,$cputemp_avg/$cnt_avg;
+
+ &minmaxavg("WEEK","1CPU",($cputemp_avg/$cnt_avg));
+ }
+ }
+ if ($minmaxavg{WEEK}{"1CPU"}{CNT} > 0) {$minmaxavg{WEEK}{"1CPU"}{AVG} /= $minmaxavg{WEEK}{"1CPU"}{CNT}}
+ my @data = ([reverse @h],[reverse @p]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Day (Hourly Average)',
+ y_label => 'Centigrade',
+ x_label_skip => 24,
+ title => 'CPU Temp in last 7 days',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend("Highest Core Temperature");
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemweek.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "temp") {
+ my (@h,@p);
+ for (my $hours = 0; $hours < (24 * $system_maxdays);$hours++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($hours * 60 * 60));
+ push @h,$mday;
+ my $cputemp_avg;
+ my $cnt_avg = 0;
+ for (my $mins = 59; $mins >= 0;$mins--) {
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$mins});
+ if ($time and $cputemp ne "") {
+ $cputemp_avg += $cputemp;
+ $cnt_avg++;
+ }
+ }
+ unless (defined $cnt_avg) {
+ push @p,undef;
+ } else {
+ if ($cnt_avg == 0) {$cnt_avg = 1}
+ push @p,$cputemp_avg/$cnt_avg;
+
+ &minmaxavg("MONTH","1CPU",($cputemp_avg/$cnt_avg));
+ }
+ }
+ if ($minmaxavg{MONTH}{"1CPU"}{CNT} > 0) {$minmaxavg{MONTH}{"1CPU"}{AVG} /= $minmaxavg{MONTH}{"1CPU"}{CNT}}
+ my @data = ([reverse @h],[reverse @p]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Day (Hourly Average)',
+ y_label => 'Centigrade',
+ x_label_skip => 24,
+ title => "CPU Temp in last $system_maxdays days",
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend("Highest Core Temperature");
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemmonth.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "mysqldata") {
+ my (@h,@p,@t);
+ my $mysqlin_prev;
+ my $mysqlout_prev;
+ for (my $mins = 0; $mins < 60;$mins++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($mins * 60));
+ push @h,$min;
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$min});
+ if ($time eq "" or $mysqlin eq "") {
+ $mysqlin_prev = 0;
+ $mysqlout_prev = 0;
+ push @p,undef;
+ push @t,undef;
+ } else {
+ if ($mysqlin_prev < $mysqlin or $mysqlin eq "") {
+ push @p,undef;
+ $mysqlin_prev = $mysqlin;
+ } else {
+ my $mysqlin_val = ($mysqlin_prev - $mysqlin) / 60;
+ push @p,$mysqlin_val;
+ $mysqlin_prev = $mysqlin;
+ &minmaxavg("HOUR","1Inbound",$mysqlin_val);
+ }
+ if ($mysqlout_prev < $mysqlout or $mysqlout eq "") {
+ push @t,undef;
+ $mysqlout_prev = $mysqlout;
+ } else {
+ my $mysqlout_val = ($mysqlout_prev - $mysqlout) / 60;
+ push @t,$mysqlout_val;
+ $mysqlout_prev = $mysqlout;
+ &minmaxavg("HOUR","2Outbound",$mysqlout_val);
+ }
+ }
+ }
+ if ($minmaxavg{HOUR}{"1Inbound"}{CNT} > 0) {$minmaxavg{HOUR}{"1Inbound"}{AVG} /= $minmaxavg{HOUR}{"1Inbound"}{CNT}}
+ if ($minmaxavg{HOUR}{"2Outbound"}{CNT} > 0) {$minmaxavg{HOUR}{"2Outbound"}{AVG} /= $minmaxavg{HOUR}{"2Outbound"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Minute',
+ y_label => 'Bytes/Second',
+ x_label_skip => 3,
+ title => 'MySQL Data in last hour',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Inbound Outbound));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemhour.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "mysqldata") {
+ my (@h,@p,@t);
+ my $mysqlin_prev;
+ my $mysqlout_prev;
+ for (my $mins = 0; $mins < 1440;$mins++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($mins * 60));
+ push @h,$hour;
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$min});
+ if ($time eq "" or $mysqlin eq "") {
+ $mysqlin_prev = 0;
+ $mysqlout_prev = 0;
+ push @p,undef;
+ push @t,undef;
+ } else {
+ if ($mysqlin_prev < $mysqlin or $mysqlin eq "") {
+ push @p,undef;
+ $mysqlin_prev = $mysqlin;
+ } else {
+ my $mysqlin_val = ($mysqlin_prev - $mysqlin) / 60;
+ push @p,$mysqlin_val;
+ $mysqlin_prev = $mysqlin;
+ &minmaxavg("DAY","1Inbound",$mysqlin_val);
+ }
+ if ($mysqlout_prev < $mysqlout or $mysqlout eq "") {
+ push @t,undef;
+ $mysqlout_prev = $mysqlout;
+ } else {
+ my $mysqlout_val = ($mysqlout_prev - $mysqlout) / 60;
+ push @t,$mysqlout_val;
+ $mysqlout_prev = $mysqlout;
+ &minmaxavg("DAY","2Outbound",$mysqlout_val);
+ }
+ }
+ }
+ if ($minmaxavg{DAY}{"1Inbound"}{CNT} > 0) {$minmaxavg{DAY}{"1Inbound"}{AVG} /= $minmaxavg{DAY}{"1Inbound"}{CNT}}
+ if ($minmaxavg{DAY}{"2Outbound"}{CNT} > 0) {$minmaxavg{DAY}{"2Outbound"}{AVG} /= $minmaxavg{DAY}{"2Outbound"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Hour',
+ y_label => 'Bytes/Second',
+ x_label_skip => 60,
+ title => 'MySQL Data in last 24 hours',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Inbound Outbound));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemday.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "mysqldata") {
+ my (@h,@p,@t);
+ my $mysqlin_prev;
+ my $mysqlout_prev;
+ for (my $hours = 0; $hours < 168;$hours++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($hours * 60 * 60));
+ push @h,$mday;
+ my $mysqlin_avg;
+ my $mysqlout_avg;
+ for (my $mins = 59; $mins >= 0;$mins--) {
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$mins});
+ if ($time eq "" or $mysqlin eq "") {
+ $mysqlin_prev = 0;
+ $mysqlout_prev = 0;
+ } else {
+ if ($mysqlin_prev < $mysqlin or $mysqlin eq "") {
+ $mysqlin_prev = $mysqlin;
+ } else {
+ my $mysqlin_val = ($mysqlin_prev - $mysqlin) / 60;
+ $mysqlin_avg = $mysqlin_avg + $mysqlin_val;
+ $mysqlin_prev = $mysqlin;
+ }
+ if ($mysqlout_prev < $mysqlout or $mysqlout eq "") {
+ $mysqlout_prev = $mysqlout;
+ } else {
+ my $mysqlout_val = ($mysqlout_prev - $mysqlout) / 60;
+ $mysqlout_avg = $mysqlout_avg + $mysqlout_val;
+ $mysqlout_prev = $mysqlout;
+ }
+ }
+ }
+ unless (defined $mysqlin_avg) {
+ push @p,undef;
+ } else {
+ push @p,($mysqlin_avg/60);
+ &minmaxavg("WEEK","1Inbound",($mysqlin_avg/60));
+ }
+ unless (defined $mysqlout_avg) {
+ push @t,undef;
+ } else {
+ push @t,($mysqlout_avg/60);
+ &minmaxavg("WEEK","2Outbound",($mysqlout_avg/60));
+ }
+ }
+ if ($minmaxavg{WEEK}{"1Inbound"}{CNT} > 0) {$minmaxavg{WEEK}{"1Inbound"}{AVG} /= $minmaxavg{WEEK}{"1Inbound"}{CNT}}
+ if ($minmaxavg{WEEK}{"2Outbound"}{CNT} > 0) {$minmaxavg{WEEK}{"2Outbound"}{AVG} /= $minmaxavg{WEEK}{"2Outbound"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Day (Hourly Average)',
+ y_label => 'Bytes/Second',
+ x_label_skip => 24,
+ title => 'MySQL Data in last 7 days',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Inbound Outbound));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemweek.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "mysqldata") {
+ my (@h,@p,@t);
+ my $mysqlin_prev;
+ my $mysqlout_prev;
+ for (my $hours = 0; $hours < (24 * $system_maxdays);$hours++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($hours * 60 * 60));
+ push @h,$mday;
+ my $mysqlin_avg;
+ my $mysqlout_avg;
+ for (my $mins = 59; $mins >= 0;$mins--) {
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$mins});
+ if ($time eq "" or $mysqlin eq "") {
+ $mysqlin_prev = 0;
+ $mysqlout_prev = 0;
+ } else {
+ if ($mysqlin_prev < $mysqlin or $mysqlin eq "") {
+ $mysqlin_prev = $mysqlin;
+ } else {
+ my $mysqlin_val = ($mysqlin_prev - $mysqlin) / 60;
+ $mysqlin_avg = $mysqlin_avg + $mysqlin_val;
+ $mysqlin_prev = $mysqlin;
+ }
+ if ($mysqlout_prev < $mysqlout or $mysqlout eq "") {
+ $mysqlout_prev = $mysqlout;
+ } else {
+ my $mysqlout_val = ($mysqlout_prev - $mysqlout) / 60;
+ $mysqlout_avg = $mysqlout_avg + $mysqlout_val;
+ $mysqlout_prev = $mysqlout;
+ }
+ }
+ }
+ unless (defined $mysqlin_avg) {
+ push @p,undef;
+ } else {
+ push @p,($mysqlin_avg/60);
+ &minmaxavg("MONTH","1Inbound",($mysqlin_avg/60));
+ }
+ unless (defined $mysqlout_avg) {
+ push @t,undef;
+ } else {
+ push @t,($mysqlout_avg/60);
+ &minmaxavg("MONTH","2Outbound",($mysqlout_avg/60));
+ }
+ }
+ if ($minmaxavg{MONTH}{"1Inbound"}{CNT} > 0) {$minmaxavg{MONTH}{"1Inbound"}{AVG} /= $minmaxavg{MONTH}{"1Inbound"}{CNT}}
+ if ($minmaxavg{MONTH}{"2Outbound"}{CNT}) {$minmaxavg{MONTH}{"2Outbound"}{AVG} /= $minmaxavg{MONTH}{"2Outbound"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Day (Hourly Average)',
+ y_label => 'Bytes/Second',
+ x_label_skip => 24,
+ title => "MySQL Data in last $system_maxdays days",
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Inbound Outbound));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemmonth.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "mysqlqueries") {
+ my (@h,@p,@t);
+ my $mysqlq_prev;
+ my $mysqlsq_prev;
+ for (my $mins = 0; $mins < 60;$mins++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($mins * 60));
+ push @h,$min;
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$min});
+ if ($time eq "" or $mysqlq eq "") {
+ $mysqlq_prev = 0;
+ push @p,undef;
+ } else {
+ if ($mysqlq_prev < $mysqlq or $mysqlq eq "") {
+ push @p,undef;
+ $mysqlq_prev = $mysqlq;
+ } else {
+ my $mysqlq_val = ($mysqlq_prev - $mysqlq);
+ push @p,$mysqlq_val;
+ $mysqlq_prev = $mysqlq;
+ &minmaxavg("HOUR","1Queries",$mysqlq_val);
+ }
+ }
+ }
+ if ($minmaxavg{HOUR}{"1Queries"}{CNT} > 0) {$minmaxavg{HOUR}{"1Queries"}{AVG} /= $minmaxavg{HOUR}{"1Queries"}{CNT}}
+ my @data = ([reverse @h],[reverse @p]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Minute',
+ y_label => 'Queries',
+ x_label_skip => 3,
+ title => 'MySQL Queries in last hour',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Queries));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemhour.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "mysqlqueries") {
+ my (@h,@p,@t);
+ my $mysqlq_prev;
+ my $mysqlsq_prev;
+ for (my $mins = 0; $mins < 1440;$mins++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($mins * 60));
+ push @h,$hour;
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$min});
+ if ($time eq "" or $mysqlq eq "") {
+ $mysqlq_prev = 0;
+ push @p,undef;
+ } else {
+ if ($mysqlq_prev < $mysqlq or $mysqlq eq "") {
+ push @p,undef;
+ $mysqlq_prev = $mysqlq;
+ } else {
+ my $mysqlq_val = ($mysqlq_prev - $mysqlq);
+ push @p,$mysqlq_val;
+ $mysqlq_prev = $mysqlq;
+ &minmaxavg("DAY","1Queries",$mysqlq_val);
+ }
+ }
+ }
+ if ($minmaxavg{DAY}{"1Queries"}{CNT} > 0) {$minmaxavg{DAY}{"1Queries"}{AVG} /= $minmaxavg{DAY}{"1Queries"}{CNT}}
+ my @data = ([reverse @h],[reverse @p]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Hour',
+ y_label => 'Queries',
+ x_label_skip => 60,
+ title => 'MySQL Queries in last 24 hours',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Queries));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemday.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "mysqlqueries") {
+ my (@h,@p,@t);
+ my $mysqlq_prev;
+ for (my $hours = 0; $hours < 168;$hours++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($hours * 60 * 60));
+ push @h,$mday;
+ my $mysqlq_avg;
+ for (my $mins = 59; $mins >= 0;$mins--) {
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$mins});
+ if ($time eq "" or $mysqlq eq "") {
+ $mysqlq_prev = 0;
+ } else {
+ if ($mysqlq_prev < $mysqlq or $mysqlq eq "") {
+ $mysqlq_prev = $mysqlq;
+ } else {
+ my $mysqlq_val = ($mysqlq_prev - $mysqlq);
+ $mysqlq_avg = $mysqlq_avg + $mysqlq_val;
+ $mysqlq_prev = $mysqlq;
+ }
+ }
+ }
+ unless (defined $mysqlq_avg) {
+ push @p,undef;
+ } else {
+ push @p,($mysqlq_avg/60);
+ &minmaxavg("WEEK","1Queries",($mysqlq_avg/60));
+ }
+ }
+ if ($minmaxavg{WEEK}{"1Queries"}{CNT} > 0) {$minmaxavg{WEEK}{"1Queries"}{AVG} /= $minmaxavg{WEEK}{"1Queries"}{CNT}}
+ my @data = ([reverse @h],[reverse @p]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Day (Hourly Average)',
+ y_label => 'Queries',
+ x_label_skip => 24,
+ title => 'MySQL Queries in last 7 days',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Queries));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemweek.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "mysqlqueries") {
+ my (@h,@p,@t);
+ my $mysqlq_prev;
+ for (my $hours = 0; $hours < (24 * $system_maxdays);$hours++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($hours * 60 * 60));
+ push @h,$mday;
+ my $mysqlq_avg;
+ for (my $mins = 59; $mins >= 0;$mins--) {
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$mins});
+ if ($time eq "" or $mysqlq eq "") {
+ $mysqlq_prev = 0;
+ } else {
+ if ($mysqlq_prev < $mysqlq or $mysqlq eq "") {
+ $mysqlq_prev = $mysqlq;
+ } else {
+ my $mysqlq_val = ($mysqlq_prev - $mysqlq);
+ $mysqlq_avg = $mysqlq_avg + $mysqlq_val;
+ $mysqlq_prev = $mysqlq;
+ }
+ }
+ }
+ unless (defined $mysqlq_avg) {
+ push @p,undef;
+ } else {
+ push @p,($mysqlq_avg/60);
+ &minmaxavg("MONTH","1Queries",($mysqlq_avg/60));
+ }
+ }
+ if ($minmaxavg{MONTH}{"1Queries"}{CNT} > 0) {$minmaxavg{MONTH}{"1Queries"}{AVG} /= $minmaxavg{MONTH}{"1Queries"}{CNT}}
+ my @data = ([reverse @h],[reverse @p]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Day (Hourly Average)',
+ y_label => 'Queries',
+ x_label_skip => 24,
+ title => "MySQL Queries in last $system_maxdays",
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Queries));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemmonth.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "mysqlslowqueries") {
+ my (@h,@p,@t);
+ my $mysqlsq_prev;
+ for (my $mins = 0; $mins < 60;$mins++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($mins * 60));
+ push @h,$min;
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$min});
+ if ($time eq "" or $mysqlq eq "") {
+ $mysqlsq_prev = 0;
+ push @t,undef;
+ } else {
+ if ($mysqlsq_prev < $mysqlsq or $mysqlsq eq "") {
+ push @t,undef;
+ $mysqlsq_prev = $mysqlsq;
+ } else {
+ my $mysqlsq_val = ($mysqlsq_prev - $mysqlsq);
+ push @t,$mysqlsq_val;
+ $mysqlsq_prev = $mysqlsq;
+ &minmaxavg("HOUR","1Slow_Queries",$mysqlsq_val);
+ }
+ }
+ }
+ if ($minmaxavg{HOUR}{"1Slow_Queries"}{CNT} > 0) {$minmaxavg{HOUR}{"1Slow_Queries"}{AVG} /= $minmaxavg{HOUR}{"1Slow_Queries"}{CNT}}
+ my @data = ([reverse @h],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Minute',
+ y_label => 'Slow Queries',
+ x_label_skip => 3,
+ title => 'MySQL Slow Queries in last hour',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Slow_Queries));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemhour.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "mysqlslowqueries") {
+ my (@h,@p,@t);
+ my $mysqlsq_prev;
+ for (my $mins = 0; $mins < 1440;$mins++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($mins * 60));
+ push @h,$hour;
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$min});
+ if ($time eq "" or $mysqlq eq "") {
+ $mysqlsq_prev = 0;
+ push @t,undef;
+ } else {
+ if ($mysqlsq_prev < $mysqlsq or $mysqlsq eq "") {
+ push @t,undef;
+ $mysqlsq_prev = $mysqlsq;
+ } else {
+ my $mysqlsq_val = ($mysqlsq_prev - $mysqlsq);
+ push @t,$mysqlsq_val;
+ $mysqlsq_prev = $mysqlsq;
+ &minmaxavg("DAY","1Slow_Queries",$mysqlsq_val);
+ }
+ }
+ }
+ if ($minmaxavg{DAY}{"1Slow_Queries"}{CNT} > 0) {$minmaxavg{DAY}{"1Slow_Queries"}{AVG} /= $minmaxavg{DAY}{"1Slow_Queries"}{CNT}}
+ my @data = ([reverse @h],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Hour',
+ y_label => 'Slow Queries',
+ x_label_skip => 60,
+ title => 'MySQL Slow Queries in last 24 hours',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Slow_Queries));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemday.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "mysqlslowqueries") {
+ my (@h,@p,@t);
+ my $mysqlsq_prev;
+ for (my $hours = 0; $hours < 168;$hours++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($hours * 60 * 60));
+ push @h,$mday;
+ my $mysqlsq_avg;
+ for (my $mins = 59; $mins >= 0;$mins--) {
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$mins});
+ if ($time eq "" or $mysqlsq eq "") {
+ $mysqlsq_prev = 0;
+ } else {
+ if ($mysqlsq_prev < $mysqlsq or $mysqlsq eq "") {
+ $mysqlsq_prev = $mysqlsq;
+ } else {
+ my $mysqlsq_val = ($mysqlsq_prev - $mysqlsq);
+ $mysqlsq_avg = $mysqlsq_avg + $mysqlsq_val;
+ $mysqlsq_prev = $mysqlsq;
+ }
+ }
+ }
+ unless (defined $mysqlsq_avg) {
+ push @t,undef;
+ } else {
+ push @t,($mysqlsq_avg/60);
+ &minmaxavg("WEEK","1Slow_Queries",($mysqlsq_avg/60));
+ }
+ }
+ if ($minmaxavg{WEEK}{"1Slow_Queries"}{CNT} > 0) {$minmaxavg{WEEK}{"1Slow_Queries"}{AVG} /= $minmaxavg{WEEK}{"1Slow_Queries"}{CNT}}
+ my @data = ([reverse @h],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Day (Hourly Average)',
+ y_label => 'Slow Queries',
+ x_label_skip => 24,
+ title => 'MySQL Slow Queries in last 7 days',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Slow_Queries));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemweek.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "mysqlslowqueries") {
+ my (@h,@p,@t);
+ my $mysqlsq_prev;
+ for (my $hours = 0; $hours < (24 * $system_maxdays);$hours++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($hours * 60 * 60));
+ push @h,$mday;
+ my $mysqlsq_avg;
+ for (my $mins = 59; $mins >= 0;$mins--) {
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$mins});
+ if ($time eq "" or $mysqlsq eq "") {
+ $mysqlsq_prev = 0;
+ } else {
+ if ($mysqlsq_prev < $mysqlsq or $mysqlsq eq "") {
+ $mysqlsq_prev = $mysqlsq;
+ } else {
+ my $mysqlsq_val = ($mysqlsq_prev - $mysqlsq);
+ $mysqlsq_avg = $mysqlsq_avg + $mysqlsq_val;
+ $mysqlsq_prev = $mysqlsq;
+ }
+ }
+ }
+ unless (defined $mysqlsq_avg) {
+ push @t,undef;
+ } else {
+ push @t,($mysqlsq_avg/60);
+ &minmaxavg("MONTH","1Slow_Queries",($mysqlsq_avg/60));
+ }
+ }
+ if ($minmaxavg{MONTH}{"1Slow_Queries"}{CNT} > 0) {$minmaxavg{MONTH}{"1Slow_Queries"}{AVG} /= $minmaxavg{MONTH}{"1Slow_Queries"}{CNT}}
+ my @data = ([reverse @h],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Day (Hourly Average)',
+ y_label => 'Slow Queries',
+ x_label_skip => 24,
+ title => "MySQL Slow Queries in last $system_maxdays",
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Slow_Queries));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemmonth.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "mysqlconns") {
+ my (@h,@p,@t);
+ my $mysqlcn_prev;
+ for (my $mins = 0; $mins < 60;$mins++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($mins * 60));
+ push @h,$min;
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$min});
+ if ($time eq "" or $mysqlq eq "") {
+ $mysqlcn_prev = 0;
+ push @p,undef;
+ push @t,undef;
+ } else {
+ if ($mysqlcn_prev < $mysqlcn or $mysqlcn eq "") {
+ push @p,undef;
+ $mysqlcn_prev = $mysqlcn;
+ } else {
+ my $mysqlcn_val = ($mysqlcn_prev - $mysqlcn);
+ push @p,$mysqlcn_val;
+ $mysqlcn_prev = $mysqlcn;
+ &minmaxavg("HOUR","1Connections",$mysqlcn_val);
+ }
+ if ($mysqlth eq "") {
+ push @t,undef;
+ } else {
+ push @t,$mysqlth;
+ &minmaxavg("HOUR","2Threads",$mysqlth);
+ }
+ }
+ }
+ if ($minmaxavg{HOUR}{"1Connections"}{CNT} > 0) {$minmaxavg{HOUR}{"1Connections"}{AVG} /= $minmaxavg{HOUR}{"1Connections"}{CNT}}
+ if ($minmaxavg{HOUR}{"2Threads"}{CNT} > 0) {$minmaxavg{HOUR}{"2Threads"}{AVG} /= $minmaxavg{HOUR}{"2Threads"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Minute',
+ y_label => '',
+ x_label_skip => 3,
+ title => 'MySQL Connections & Threads in last hour',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Connections Threads));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemhour.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "mysqlconns") {
+ my (@h,@p,@t);
+ my $mysqlcn_prev;
+ for (my $mins = 0; $mins < 1440;$mins++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($mins * 60));
+ push @h,$hour;
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$min});
+ if ($time eq "" or $mysqlq eq "") {
+ $mysqlcn_prev = 0;
+ push @p,undef;
+ push @t,undef;
+ } else {
+ if ($mysqlcn_prev < $mysqlcn or $mysqlcn eq "") {
+ push @p,undef;
+ $mysqlcn_prev = $mysqlcn;
+ } else {
+ my $mysqlcn_val = ($mysqlcn_prev - $mysqlcn);
+ push @p,$mysqlcn_val;
+ $mysqlcn_prev = $mysqlcn;
+ &minmaxavg("DAY","1Connections",$mysqlcn_val);
+ }
+ if ($mysqlth eq "") {
+ push @t,undef;
+ } else {
+ push @t,$mysqlth;
+ &minmaxavg("DAY","2Threads",$mysqlth);
+ }
+ }
+ }
+ if ($minmaxavg{DAY}{"1Connections"}{CNT} > 0) {$minmaxavg{DAY}{"1Connections"}{AVG} /= $minmaxavg{DAY}{"1Connections"}{CNT}}
+ if ($minmaxavg{DAY}{"2Threads"}{CNT} > 0) {$minmaxavg{DAY}{"2Threads"}{AVG} /= $minmaxavg{DAY}{"2Threads"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Hour',
+ y_label => '',
+ x_label_skip => 60,
+ title => 'MySQL Connections & Threads in last 24 hours',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Connections Threads));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemday.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "mysqlconns") {
+ my (@h,@p,@t);
+ my $mysqlcn_prev;
+ for (my $hours = 0; $hours < 168;$hours++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($hours * 60 * 60));
+ push @h,$mday;
+ my $mysqlcn_avg;
+ my $mysqlth_avg;
+ for (my $mins = 59; $mins >= 0;$mins--) {
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$mins});
+ if ($time eq "" or $mysqlq eq "") {
+ $mysqlcn_prev = 0;
+ } else {
+ if ($mysqlcn_prev < $mysqlcn or $mysqlcn eq "") {
+ $mysqlcn_prev = $mysqlcn;
+ } else {
+ my $mysqlcn_val = ($mysqlcn_prev - $mysqlcn);
+ $mysqlcn_avg = $mysqlcn_avg + $mysqlcn_val;
+ $mysqlcn_prev = $mysqlcn;
+ }
+ $mysqlth_avg = $mysqlth_avg + $mysqlth;
+ }
+ }
+ unless (defined $mysqlcn_avg) {
+ push @p,undef;
+ } else {
+ push @p,($mysqlcn_avg/60);
+ &minmaxavg("WEEK","1Connections",($mysqlcn_avg/60));
+ }
+ unless (defined $mysqlth_avg) {
+ push @t,undef;
+ } else {
+ push @t,($mysqlth_avg/60);
+ &minmaxavg("WEEK","2Threads",($mysqlth_avg/60));
+ }
+ }
+ if ($minmaxavg{WEEK}{"1Connections"}{CNT} > 0) {$minmaxavg{WEEK}{"1Connections"}{AVG} /= $minmaxavg{WEEK}{"1Connections"}{CNT}}
+ if ($minmaxavg{WEEK}{"2Threads"}{CNT} > 0) {$minmaxavg{WEEK}{"2Threads"}{AVG} /= $minmaxavg{WEEK}{"2Threads"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Day (Hourly Average)',
+ y_label => '',
+ x_label_skip => 24,
+ title => 'MySQL Connections & Threads in last 7 days',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Connections Threads));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemweek.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "mysqlconns") {
+ my (@h,@p,@t);
+ my $mysqlcn_prev;
+ for (my $hours = 0; $hours < (24 * $system_maxdays);$hours++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($hours * 60 * 60));
+ push @h,$mday;
+ my $mysqlcn_avg;
+ my $mysqlth_avg;
+ for (my $mins = 59; $mins >= 0;$mins--) {
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$mins});
+ if ($time eq "" or $mysqlq eq "") {
+ $mysqlcn_prev = 0;
+ } else {
+ if ($mysqlcn_prev < $mysqlcn or $mysqlcn eq "") {
+ $mysqlcn_prev = $mysqlcn;
+ } else {
+ my $mysqlcn_val = ($mysqlcn_prev - $mysqlcn);
+ $mysqlcn_avg = $mysqlcn_avg + $mysqlcn_val;
+ $mysqlcn_prev = $mysqlcn;
+ }
+ $mysqlth_avg = $mysqlth_avg + $mysqlth;
+ }
+ }
+ unless (defined $mysqlcn_avg) {
+ push @p,undef;
+ } else {
+ push @p,($mysqlcn_avg/60);
+ &minmaxavg("MONTH","1Connections",($mysqlcn_avg/60));
+ }
+ unless (defined $mysqlth_avg) {
+ push @t,undef;
+ } else {
+ push @t,($mysqlth_avg/60);
+ &minmaxavg("MONTH","2Threads",($mysqlth_avg/60));
+ }
+ }
+ if ($minmaxavg{MONTH}{"1Connections"}{CNT} > 0) {$minmaxavg{MONTH}{"1Connections"}{AVG} /= $minmaxavg{MONTH}{"1Connections"}{CNT}}
+ if ($minmaxavg{MONTH}{"2Threads"}{CNT} > 0) {$minmaxavg{MONTH}{"2Threads"}{AVG} /= $minmaxavg{MONTH}{"2Threads"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Day (Hourly Average)',
+ y_label => '',
+ x_label_skip => 24,
+ title => "MySQL Connections & Threads in last $system_maxdays days",
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Connections Threads));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemmonth.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "apachecpu") {
+ my (@h,@p);
+ for (my $mins = 0; $mins < 60;$mins++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($mins * 60));
+ push @h,$min;
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$min});
+ if ($time eq "" or $apachecpu eq "") {
+ push @p,undef;
+ } else {
+ push @p,$apachecpu;
+
+ &minmaxavg("HOUR","1Apache_CPU",$apachecpu);
+ }
+ }
+ if ($minmaxavg{HOUR}{"1Apache_CPU"}{CNT} > 0) {$minmaxavg{HOUR}{"1Apache_CPU"}{AVG} /= $minmaxavg{HOUR}{"1Apache_CPU"}{CNT}}
+ my @data = ([reverse @h],[reverse @p]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Minute',
+ y_label => 'Percentage',
+ x_label_skip => 3,
+ title => 'Apache CPU Usage in last hour',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend("Apache CPU");
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemhour.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "apachecpu") {
+ my (@h,@p);
+ for (my $mins = 0; $mins < 1440;$mins++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($mins * 60));
+ push @h,$hour;
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$min});
+ if ($time eq "" or $apachecpu eq "") {
+ push @p,undef;
+ } else {
+ push @p,$apachecpu;
+
+ &minmaxavg("DAY","1Apache_CPU",$apachecpu);
+ }
+ }
+ if ($minmaxavg{DAY}{"1Apache_CPU"}{CNT} > 0) {$minmaxavg{DAY}{"1Apache_CPU"}{AVG} /= $minmaxavg{DAY}{"1Apache_CPU"}{CNT}}
+ my @data = ([reverse @h],[reverse @p]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred purple) ] );
+ $hour_graph->set(
+ x_label => 'Hour',
+ y_label => 'Percentage',
+ x_label_skip => 60,
+ title => 'Apache CPU Usage in last 24 hours',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend("Apache CPU");
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemday.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "apachecpu") {
+ my (@h,@p);
+ for (my $hours = 0; $hours < 168;$hours++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($hours * 60 * 60));
+ push @h,$mday;
+ my $apachecpu_avg;
+ my $cnt_avg = 0;
+ for (my $mins = 59; $mins >= 0;$mins--) {
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$mins});
+ if ($time and $apachecpu ne "") {
+ $apachecpu_avg += $apachecpu;
+ $cnt_avg++;
+ }
+ }
+ unless (defined $cnt_avg) {
+ push @p,undef;
+ } else {
+ if ($cnt_avg == 0) {$cnt_avg = 1}
+ push @p,$apachecpu_avg/$cnt_avg;
+
+ &minmaxavg("WEEK","1Apache_CPU",($apachecpu_avg/$cnt_avg));
+ }
+ }
+ if ($minmaxavg{WEEK}{"1Apache_CPU"}{CNT} > 0) {$minmaxavg{WEEK}{"1Apache_CPU"}{AVG} /= $minmaxavg{WEEK}{"1Apache_CPU"}{CNT}}
+ my @data = ([reverse @h],[reverse @p]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Day (Hourly Average)',
+ y_label => 'Percentage',
+ x_label_skip => 24,
+ title => 'Apache CPU Usage in last 7 days',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend("Apache CPU");
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemweek.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "apachecpu") {
+ my (@h,@p);
+ for (my $hours = 0; $hours < (24 * $system_maxdays);$hours++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($hours * 60 * 60));
+ push @h,$mday;
+ my $apachecpu_avg;
+ my $cnt_avg = 0;
+ for (my $mins = 59; $mins >= 0;$mins--) {
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$mins});
+ if ($time and $apachecpu ne "") {
+ $apachecpu_avg += $apachecpu;
+ $cnt_avg++;
+ }
+ }
+ unless (defined $cnt_avg) {
+ push @p,undef;
+ } else {
+ if ($cnt_avg == 0) {$cnt_avg = 1}
+ push @p,$apachecpu_avg/$cnt_avg;
+
+ &minmaxavg("MONTH","1Apache_CPU",($apachecpu_avg/$cnt_avg));
+ }
+ }
+ if ($minmaxavg{MONTH}{"1Apache_CPU"}{CNT} > 0) {$minmaxavg{MONTH}{"1Apache_CPU"}{AVG} /= $minmaxavg{MONTH}{"1Apache_CPU"}{CNT}}
+ my @data = ([reverse @h],[reverse @p]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Day (Hourly Average)',
+ y_label => 'Percentage',
+ x_label_skip => 24,
+ title => "Apache CPU Usage in last $system_maxdays",
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend("Apache CPU");
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemmonth.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "apacheconn") {
+ my (@h,@p);
+ my $apacheacc_prev;
+ for (my $mins = 0; $mins < 60;$mins++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($mins * 60));
+ push @h,$min;
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$min});
+ if ($time eq "" or $apacheacc eq "") {
+ $apacheacc_prev = 0;
+ push @p,undef;
+ } else {
+ if ($apacheacc_prev < $apacheacc or $apacheacc eq "") {
+ push @p,undef;
+ $apacheacc_prev = $apacheacc;
+ } else {
+ my $apacheacc_val = ($apacheacc_prev - $apacheacc);
+ push @p,$apacheacc_val;
+ $apacheacc_prev = $apacheacc;
+ &minmaxavg("HOUR","1Connections",$apacheacc_val);
+ }
+ }
+ }
+ if ($minmaxavg{HOUR}{"1Connections"}{CNT} > 0) {$minmaxavg{HOUR}{"1Connections"}{AVG} /= $minmaxavg{HOUR}{"1Connections"}{CNT}}
+ my @data = ([reverse @h],[reverse @p]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Minute',
+ y_label => '',
+ x_label_skip => 3,
+ title => 'Apache Connections in last hour',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Connections));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemhour.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "apacheconn") {
+ my (@h,@p,@t);
+ my $apacheacc_prev;
+ for (my $mins = 0; $mins < 1440;$mins++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($mins * 60));
+ push @h,$hour;
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$min});
+ if ($time eq "" or $apacheacc eq "") {
+ $apacheacc_prev = 0;
+ push @p,undef;
+ } else {
+ if ($apacheacc_prev < $apacheacc or $apacheacc eq "") {
+ push @p,undef;
+ $apacheacc_prev = $apacheacc;
+ } else {
+ my $apacheacc_val = ($apacheacc_prev - $apacheacc);
+ push @p,$apacheacc_val;
+ $apacheacc_prev = $apacheacc;
+ &minmaxavg("DAY","1Connections",$apacheacc_val);
+ }
+ }
+ }
+ if ($minmaxavg{DAY}{"1Connections"}{CNT} > 0) {$minmaxavg{DAY}{"1Connections"}{AVG} /= $minmaxavg{DAY}{"1Connections"}{CNT}}
+ my @data = ([reverse @h],[reverse @p]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Hour',
+ y_label => '',
+ x_label_skip => 60,
+ title => 'Apache Connections in last 24 hours',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Connections));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemday.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "apacheconn") {
+ my (@h,@p,@t);
+ my $apacheacc_prev;
+ for (my $hours = 0; $hours < 168;$hours++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($hours * 60 * 60));
+ push @h,$mday;
+ my $apacheacc_avg;
+ for (my $mins = 59; $mins >= 0;$mins--) {
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$mins});
+ if ($time eq "" or $apacheacc eq "") {
+ $apacheacc_prev = 0;
+ } else {
+ if ($apacheacc_prev < $apacheacc or $apacheacc eq "") {
+ $apacheacc_prev = $apacheacc;
+ } else {
+ my $apacheacc_val = ($apacheacc_prev - $apacheacc);
+ $apacheacc_avg = $apacheacc_avg + $apacheacc_val;
+ $apacheacc_prev = $apacheacc;
+ }
+ }
+ }
+ unless (defined $apacheacc_avg) {
+ push @p,undef;
+ } else {
+ push @p,($apacheacc_avg/60);
+ &minmaxavg("WEEK","1Connections",($apacheacc_avg/60));
+ }
+ }
+ if ($minmaxavg{WEEK}{"1Connections"}{CNT} > 0) {$minmaxavg{WEEK}{"1Connections"}{AVG} /= $minmaxavg{WEEK}{"1Connections"}{CNT}}
+ my @data = ([reverse @h],[reverse @p]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Day (Hourly Average)',
+ y_label => '',
+ x_label_skip => 24,
+ title => 'Apache Connections in last 7 days',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Connections Threads));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemweek.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "apacheconn") {
+ my (@h,@p,@t);
+ my $apacheacc_prev;
+ for (my $hours = 0; $hours < (24 * $system_maxdays);$hours++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($hours * 60 * 60));
+ push @h,$mday;
+ my $apacheacc_avg;
+ for (my $mins = 59; $mins >= 0;$mins--) {
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$mins});
+ if ($time eq "" or $apacheacc eq "") {
+ $apacheacc_prev = 0;
+ } else {
+ if ($apacheacc_prev < $apacheacc or $apacheacc eq "") {
+ $apacheacc_prev = $apacheacc;
+ } else {
+ my $apacheacc_val = ($apacheacc_prev - $apacheacc);
+ $apacheacc_avg = $apacheacc_avg + $apacheacc_val;
+ $apacheacc_prev = $apacheacc;
+ }
+ }
+ }
+ unless (defined $apacheacc_avg) {
+ push @p,undef;
+ } else {
+ push @p,($apacheacc_avg/60);
+ &minmaxavg("MONTH","1Connections",($apacheacc_avg/60));
+ }
+ }
+ if ($minmaxavg{MONTH}{"1Connections"}{CNT} > 0) {$minmaxavg{MONTH}{"1Connections"}{AVG} /= $minmaxavg{MONTH}{"1Connections"}{CNT}}
+ my @data = ([reverse @h],[reverse @p]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Day (Hourly Average)',
+ y_label => '',
+ x_label_skip => 24,
+ title => "Apache Connections in last $system_maxdays",
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Connections Threads));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemmonth.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "apachework") {
+ my (@h,@p,@t);
+ for (my $mins = 0; $mins < 60;$mins++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($mins * 60));
+ push @h,$min;
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$min});
+ if ($time eq "" or $apachebwork eq "") {
+ push @p,undef;
+ push @t,undef;
+ } else {
+ push @p,$apachebwork;
+ push @t,$apacheiwork;
+
+ &minmaxavg("HOUR","1Busy",$apachebwork);
+ &minmaxavg("HOUR","2Idle",$apacheiwork);
+ }
+ }
+ if ($minmaxavg{HOUR}{"1Busy"}{CNT} > 0) {$minmaxavg{HOUR}{"1Busy"}{AVG} /= $minmaxavg{HOUR}{"1Busy"}{CNT}}
+ if ($minmaxavg{HOUR}{"2Idle"}{CNT} > 0) {$minmaxavg{HOUR}{"2Idle"}{AVG} /= $minmaxavg{HOUR}{"2Idle"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Minute',
+ y_label => 'Workers',
+ x_label_skip => 3,
+ title => 'Apache Workers in last hour',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Busy Idle));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemhour.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "apachework") {
+ my (@h,@p,@t);
+ for (my $mins = 0; $mins < 1440;$mins++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($mins * 60));
+ push @h,$hour;
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$min});
+ if ($time eq "" or $apachebwork eq "") {
+ push @p,undef;
+ push @t,undef;
+ } else {
+ push @p,$apachebwork;
+ push @t,$apacheiwork;
+
+ &minmaxavg("DAY","1Busy",$apachebwork);
+ &minmaxavg("DAY","2Idle",$apacheiwork);
+ }
+ }
+ if ($minmaxavg{DAY}{"1Busy"}{CNT} > 0) {$minmaxavg{DAY}{"1Busy"}{AVG} /= $minmaxavg{DAY}{"1Busy"}{CNT}}
+ if ($minmaxavg{DAY}{"2Idle"}{CNT} > 0) {$minmaxavg{DAY}{"2Idle"}{AVG} /= $minmaxavg{DAY}{"2Idle"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred purple) ] );
+ $hour_graph->set(
+ x_label => 'Hour',
+ y_label => 'Workers',
+ x_label_skip => 60,
+ title => 'Apache Workers in last 24 hours',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Busy Idle));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemday.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "apachework") {
+ my (@h,@p,@t);
+ for (my $hours = 0; $hours < 168;$hours++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($hours * 60 * 60));
+ push @h,$mday;
+ my $apachebwork_avg;
+ my $apacheiwork_avg;
+ my $cnt_avg = 0;
+ for (my $mins = 59; $mins >= 0;$mins--) {
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$mins});
+ if ($time and $apachebwork ne "") {
+ $apachebwork_avg += $apachebwork;
+ $apacheiwork_avg += $apacheiwork;
+ $cnt_avg++;
+ }
+ }
+ unless (defined $cnt_avg) {
+ push @p,undef;
+ push @t,undef;
+ } else {
+ if ($cnt_avg == 0) {$cnt_avg = 1}
+ push @p,$apachebwork_avg/$cnt_avg;
+ push @t,$apacheiwork_avg/$cnt_avg;
+
+ &minmaxavg("WEEK","1Busy",($apachebwork_avg/$cnt_avg));
+ &minmaxavg("WEEK","2Idle",($apacheiwork_avg/$cnt_avg));
+ }
+ }
+ if ($minmaxavg{WEEK}{"1Busy"}{CNT} > 0) {$minmaxavg{WEEK}{"1Busy"}{AVG} /= $minmaxavg{WEEK}{"1Busy"}{CNT}}
+ if ($minmaxavg{WEEK}{"2Idle"}{CNT} > 0) {$minmaxavg{WEEK}{"2Idle"}{AVG} /= $minmaxavg{WEEK}{"2Idle"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Day (Hourly Average)',
+ y_label => 'Workers',
+ x_label_skip => 24,
+ title => 'Apache Workers in last 7 days',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Busy Idle));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemweek.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "apachework") {
+ my (@h,@p,@t);
+ for (my $hours = 0; $hours < (24 * $system_maxdays);$hours++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($hours * 60 * 60));
+ push @h,$mday;
+ my $apachebwork_avg;
+ my $apacheiwork_avg;
+ my $cnt_avg = 0;
+ for (my $mins = 59; $mins >= 0;$mins--) {
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$mins});
+ if ($time and $apachebwork ne "") {
+ $apachebwork_avg += $apachebwork;
+ $apacheiwork_avg += $apacheiwork;
+ $cnt_avg++;
+ }
+ }
+ unless (defined $cnt_avg) {
+ push @p,undef;
+ push @t,undef;
+ } else {
+ if ($cnt_avg == 0) {$cnt_avg = 1}
+ push @p,$apachebwork_avg/$cnt_avg;
+ push @t,$apacheiwork_avg/$cnt_avg;
+
+ &minmaxavg("MONTH","1Busy",($apachebwork_avg/$cnt_avg));
+ &minmaxavg("MONTH","2Idle",($apacheiwork_avg/$cnt_avg));
+ }
+ }
+ if ($minmaxavg{MONTH}{"1Busy"}{CNT} > 0) {$minmaxavg{MONTH}{"1Busy"}{AVG} /= $minmaxavg{MONTH}{"1Busy"}{CNT}}
+ if ($minmaxavg{MONTH}{"2Idle"}{CNT} > 0) {$minmaxavg{MONTH}{"2Idle"}{AVG} /= $minmaxavg{MONTH}{"2Idle"}{CNT}}
+ my @data = ([reverse @h],[reverse @p],[reverse @t]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Day (Hourly Average)',
+ y_label => 'Workers',
+ x_label_skip => 24,
+ title => "Apache Workers in last $system_maxdays",
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend( qw(Busy Idle));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemmonth.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+ if ($type eq "diskw") {
+ my (@h,@p);
+ for (my $mins = 0; $mins < 60;$mins++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($mins * 60));
+ push @h,$min;
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$min});
+ if ($time eq "" or $diskw eq "") {
+ push @p,undef;
+ } else {
+ push @p,$diskw;
+
+ &minmaxavg("HOUR","1Disk_Write",$diskw);
+ }
+ }
+ if ($minmaxavg{HOUR}{"1Disk_Write"}{CNT} > 0) {$minmaxavg{HOUR}{"1Disk_Write"}{AVG} /= $minmaxavg{HOUR}{"1Disk_Write"}{CNT}}
+ my @data = ([reverse @h],[reverse @p]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Minute',
+ y_label => 'MB/s',
+ x_label_skip => 3,
+ title => 'Disk Write Performance in last hour',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend("Disk_Write");
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemhour.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "diskw") {
+ my (@h,@p);
+ for (my $mins = 0; $mins < 1440;$mins++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($mins * 60));
+ push @h,$hour;
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$min});
+ if ($time eq "" or $diskw eq "") {
+ push @p,undef;
+ } else {
+ push @p,$diskw;
+
+ &minmaxavg("DAY","1Disk_Write",$diskw);
+ }
+ }
+ if ($minmaxavg{DAY}{"1Disk_Write"}{CNT} > 0) {$minmaxavg{DAY}{"1Disk_Write"}{AVG} /= $minmaxavg{DAY}{"1Disk_Write"}{CNT}}
+ my @data = ([reverse @h],[reverse @p]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred purple) ] );
+ $hour_graph->set(
+ x_label => 'Hour',
+ y_label => 'MB/s',
+ x_label_skip => 60,
+ title => 'Disk Write Performance in last 24 hours',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend("Disk_Write");
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemday.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "diskw") {
+ my (@h,@p);
+ for (my $hours = 0; $hours < 168;$hours++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($hours * 60 * 60));
+ push @h,$mday;
+ my $diskw_avg;
+ my $cnt_avg = 0;
+ for (my $mins = 59; $mins >= 0;$mins--) {
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$mins});
+ if ($time and $diskw ne "") {
+ $diskw_avg += $diskw;
+ $cnt_avg++;
+ }
+ }
+ unless (defined $cnt_avg) {
+ push @p,undef;
+ } else {
+ if ($cnt_avg == 0) {$cnt_avg = 1}
+ push @p,$diskw_avg/$cnt_avg;
+
+ &minmaxavg("WEEK","1Disk_Write",($diskw_avg/$cnt_avg));
+ }
+ }
+ if ($minmaxavg{WEEK}{"1Disk_Write"}{CNT} > 0) {$minmaxavg{WEEK}{"1Disk_Write"}{AVG} /= $minmaxavg{WEEK}{"1Disk_Write"}{CNT}}
+ my @data = ([reverse @h],[reverse @p]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Day (Hourly Average)',
+ y_label => 'MB/s',
+ x_label_skip => 24,
+ title => 'Disk Write Performance in last 7 days',
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend("Disk_Write");
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemweek.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+
+ if ($type eq "diskw") {
+ my (@h,@p);
+ for (my $hours = 0; $hours < (24 * $system_maxdays);$hours++) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time - ($hours * 60 * 60));
+ push @h,$mday;
+ my $diskw_avg;
+ my $cnt_avg = 0;
+ for (my $mins = 59; $mins >= 0;$mins--) {
+ my ($time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load1,$load5,$load15,$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached) = split(/\,/,$stata{$year}{$mon}{$mday}{$hour}{$mins});
+ if ($time and $diskw ne "") {
+ $diskw_avg += $diskw;
+ $cnt_avg++;
+ }
+ }
+ unless (defined $cnt_avg) {
+ push @p,undef;
+ } else {
+ if ($cnt_avg == 0) {$cnt_avg = 1}
+ push @p,$diskw_avg/$cnt_avg;
+
+ &minmaxavg("MONTH","1Disk_Write",($diskw_avg/$cnt_avg));
+ }
+ }
+ if ($minmaxavg{MONTH}{"1Disk_Write"}{CNT} > 0) {$minmaxavg{MONTH}{"1Disk_Write"}{AVG} /= $minmaxavg{MONTH}{"1Disk_Write"}{CNT}}
+ my @data = ([reverse @h],[reverse @p]);
+ my $hour_graph = GD::Graph::lines->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Day (Hourly Average)',
+ y_label => 'MB/s',
+ x_label_skip => 24,
+ title => "Disk Write Performance in last $system_maxdays",
+ borderclrs => $hour_graph->{dclrs},
+ transparent => 0,
+ );
+ $hour_graph->set_legend("Disk_Write");
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_systemmonth.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+ }
+ }
+
+ return;
+}
+# end graphs
+###############################################################################
+# start charts
+sub charts {
+ my $cc_lookups = shift;
+ my $imghddir = shift;
+ my $img;
+ $| = 1;
+
+ require GD::Graph::bars;
+ import GD::Graph::bars;
+ require GD::Graph::pie;
+ import GD::Graph::pie;
+ require GD::Graph::lines;
+ import GD::Graph::lines;
+
+ sysopen (my $STATS,"/var/lib/csf/stats/lfdstats", O_RDWR | O_CREAT);
+ flock ($STATS, LOCK_SH);
+ my @stats = <$STATS>;
+ chomp @stats;
+ close ($STATS);
+
+ if (@stats) {
+ my $time = time;
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time);
+
+ # Blocks by lfd in the last 24 hours
+ my $cnt = $hour + 1;
+ if ($cnt > 23) {$cnt = 0}
+ my (@h,@p,@t,@hp,@cp);
+ my %triggers;
+ for (my $hours = 0; $hours < 24;$hours++) {
+ push @h,$cnt;
+ my ($permdate,$permcount,$tempdate,$tempcount) = split(/\,/,$stats[$cnt]);
+ if ($time - $permdate > (24 * 60 * 60)) {$permdate = 0; $permcount = 0}
+ if ($time - $tempdate > (24 * 60 * 60)) {$tempdate = 0; $tempcount = 0}
+ push @p,$permcount;
+ push @t,$tempcount;
+ my @line = split(/\,/,$stats[$cnt]);
+ for (my $loop = 4; $loop < @line; $loop+=2) {
+ if ($time - $line[$loop] > (24 * 60 * 60)) {next}
+ my ($triggerstat,$triggercount) = split(/\:/,$line[$loop+1]);
+ $triggers{$triggerstat} += $triggercount;
+ }
+ $cnt++;
+ if ($cnt > 23) {$cnt = 0}
+ }
+ my @data = ([@h],[@p],[@t]);
+ my $hour_graph = GD::Graph::bars->new(750,350);
+ $hour_graph->set( dclrs => [ qw(yellow dred) ] );
+ $hour_graph->set(
+ x_label => 'Hour',
+ y_label => 'Total Blocks',
+ long_ticks => 1,
+ tick_length => 0,
+ x_ticks => 0,
+ title => 'Blocks by lfd in the last 24 hours',
+ cumulate => 1,
+ borderclrs => $hour_graph->{dclrs},
+ bar_spacing => 4,
+ shadow_depth => 1,
+ transparent => 0,
+ x_label_position => 1/2,
+ );
+ $hour_graph->set_legend( qw(Permanent Temporary));
+ $hour_graph->plot(\@data);
+ $img = $imghddir."lfd_hour.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $hour_graph->gd->gif();
+ close ($OUT);
+
+ foreach my $key (keys %triggers) {
+ push @hp, "$key ($triggers{$key})";
+ push @cp, $triggers{$key};
+ }
+ my @piedata = ([@hp],[@cp]);
+ my $hour_pie_graph = GD::Graph::pie->new( 400, 300 );
+ $hour_pie_graph->set(
+ title => 'Block triggers in the last 24 hours',
+ label => 'Trigger in csf.conf',
+ axislabelclr => 'black',
+ pie_height => 36,
+ l_margin => 15,
+ r_margin => 15,
+ start_angle => 235,
+ transparent => 0,
+ );
+ $hour_pie_graph->plot(\@piedata);
+ $img = $imghddir."lfd_pie_hour.gif";
+ open (my $OUT2, ">", "$img");
+ flock ($OUT2, LOCK_EX);
+ binmode ($OUT2);
+ print $OUT2 $hour_pie_graph->gd->gif();
+ close ($OUT2);
+
+
+ # Blocks by lfd in the last 30 Days
+ my $maxdays = 30;
+ my ($hsec,$hmin,$hhour,$hmday,$hmon,$hyear,$hwday,$hyday,$hisdst) = localtime($time - (29 * 24 * 60 * 60));
+ my $hdim = (31,28,31,30,31,30,31,31,30,31,30,31)[$hmon];
+ if ($hmon == 1 && (($hyear % 4 == 0) && ($hyear % 100 != 0) && ($hyear % 400 == 0))) {$hdim++}
+ if ($hmon == 1) {
+ $maxdays = $hdim;
+ ($hsec,$hmin,$hhour,$hmday,$hmon,$hyear,$hwday,$hyday,$hisdst) = localtime($time - (($maxdays - 1) * 24 * 60 * 60));
+ }
+ $cnt = $hmday;
+ my (@hh,@ph,@th,@hhp,@hcp);
+ my %htriggers;
+ for (my $days = 1; $days <= $maxdays;$days++) {
+ push @hh,$cnt;
+ my ($permdate,$permcount,$tempdate,$tempcount) = split(/\,/,$stats[$cnt+24]);
+ if ($time - $permdate > (($maxdays - 1) * 24 * 60 * 60)) {$permdate = 0; $permcount = 0}
+ if ($time - $tempdate > (($maxdays - 1) * 24 * 60 * 60)) {$tempdate = 0; $tempcount = 0}
+ push @ph,$permcount;
+ push @th,$tempcount;
+ my @line = split(/\,/,$stats[$cnt+24]);
+ for (my $loop = 4; $loop < @line; $loop+=2) {
+ if ($time - $line[$loop] > (($maxdays - 1) * 24 * 60 * 60)) {next}
+ my ($triggerstat,$triggercount) = split(/\:/,$line[$loop+1]);
+ $htriggers{$triggerstat} += $triggercount;
+ }
+ $cnt++;
+ if ($cnt > $hdim) {$cnt = 1}
+ }
+ my @datah = ([@hh],[@ph],[@th]);
+ my $day_graph = GD::Graph::bars->new(750,350);
+ $day_graph->set( dclrs => [ qw(yellow dred) ] );
+ $day_graph->set(
+ x_label => 'Day',
+ y_label => 'Total Blocks',
+ long_ticks => 1,
+ tick_length => 0,
+ x_ticks => 0,
+ title => "Blocks by lfd in the last $maxdays Days",
+ cumulate => 1,
+ borderclrs => $day_graph->{dclrs},
+ bar_spacing => 4,
+ shadow_depth => 1,
+ transparent => 0,
+ x_label_position => 1/2,
+ );
+ $day_graph->set_legend( qw(Permanent Temporary));
+ $day_graph->plot(\@datah);
+ $img = $imghddir."lfd_month.gif";
+ open (my $OUT3, ">", "$img");
+ flock ($OUT3, LOCK_EX);
+ binmode ($OUT3);
+ print $OUT3 $day_graph->gd->gif();
+ close ($OUT3);
+
+ foreach my $key (keys %htriggers) {
+ push @hhp, "$key ($htriggers{$key})";
+ push @hcp, $htriggers{$key};
+ }
+ my @hpiedata = ([@hhp],[@hcp]);
+ my $day_pie_graph = GD::Graph::pie->new( 400, 300 );
+ $day_pie_graph->set(
+ title => "Block triggers in the last $maxdays days",
+ label => 'Trigger in csf.conf',
+ axislabelclr => 'black',
+ pie_height => 36,
+ l_margin => 15,
+ r_margin => 15,
+ start_angle => 235,
+ transparent => 0,
+ );
+ $day_pie_graph->plot(\@hpiedata);
+ $img = $imghddir."lfd_pie_day.gif";
+ open (my $OUT4, ">", "$img");
+ flock ($OUT4, LOCK_EX);
+ binmode ($OUT4);
+ print $OUT4 $day_pie_graph->gd->gif();
+ close ($OUT4);
+
+ # Blocks by lfd in the last 12 months
+ $cnt = $mon + 2;
+ if ($cnt > 12) {$cnt = 1}
+ my (@hy,@py,@ty,@yhp,@ycp);
+ my %ytriggers;
+ for (my $months = 1; $months < 13;$months++) {
+ push @hy,$cnt;
+ my ($permdate,$permcount,$tempdate,$tempcount) = split(/\,/,$stats[$cnt+55]);
+ if ($time - $permdate > (364 * 24 * 60 * 60)) {$permdate = 0; $permcount = 0}
+ if ($time - $tempdate > (364 * 24 * 60 * 60)) {$tempdate = 0; $tempcount = 0}
+ push @py,$permcount;
+ push @ty,$tempcount;
+ my @line = split(/\,/,$stats[$cnt+55]);
+ for (my $loop = 4; $loop < @line; $loop+=2) {
+ if ($time - $line[$loop] > (364 * 24 * 60 * 60)) {next}
+ my ($triggerstat,$triggercount) = split(/\:/,$line[$loop+1]);
+ $ytriggers{$triggerstat} += $triggercount;
+ }
+ $cnt++;
+ if ($cnt > 12) {$cnt = 1}
+ }
+ my @datay = ([@hy],[@py],[@ty]);
+ my $year_graph = GD::Graph::bars->new(750,350);
+ $year_graph->set( dclrs => [ qw(yellow dred) ] );
+ $year_graph->set(
+ x_label => 'Month',
+ y_label => 'Total Blocks',
+ long_ticks => 1,
+ tick_length => 0,
+ x_ticks => 0,
+ title => 'Blocks by lfd in the last 12 months',
+ cumulate => 1,
+ borderclrs => $year_graph->{dclrs},
+ bar_spacing => 4,
+ shadow_depth => 1,
+ transparent => 0,
+ x_label_position => 1/2,
+ );
+ $year_graph->set_legend( qw(Permanent Temporary));
+ $year_graph->plot(\@datay);
+ $img = $imghddir."lfd_year.gif";
+ open (my $OUT5, ">", "$img");
+ flock ($OUT5, LOCK_EX);
+ binmode ($OUT5);
+ print $OUT5 $year_graph->gd->gif();
+ close ($OUT5);
+
+ foreach my $key (keys %ytriggers) {
+ push @yhp, "$key ($ytriggers{$key})";
+ push @ycp, $ytriggers{$key};
+ }
+ my @ypiedata = ([@yhp],[@ycp]);
+ my $year_pie_graph = GD::Graph::pie->new( 400, 300 );
+ $year_pie_graph->set(
+ title => 'Block triggers in the last 12 months',
+ label => 'Trigger in csf.conf',
+ axislabelclr => 'black',
+ pie_height => 36,
+ l_margin => 15,
+ r_margin => 15,
+ start_angle => 235,
+ transparent => 0,
+ );
+ $year_pie_graph->plot(\@ypiedata);
+ $img = $imghddir."lfd_pie_year.gif";
+ open (my $OUT6, ">", "$img");
+ flock ($OUT6, LOCK_EX);
+ binmode ($OUT6);
+ print $OUT6 $year_pie_graph->gd->gif();
+ close ($OUT6);
+
+ if ($cc_lookups) {
+ # Total Top 30 Country Code blocks by lfd
+ my (@ccy,@ccx);
+ my %ccs;
+ my $cntcc;
+ my @line = split(/\,/,$stats[69]);
+ for (my $x = 0; $x < @line; $x+=2) {$ccs{$line[$x]} = $line[$x+1]}
+ foreach my $key (sort {$ccs{$b} <=> $ccs{$a}} keys %ccs) {
+ push @ccy,$key;
+ push @ccx,$ccs{$key};
+ $cntcc++;
+ if ($cntcc > 29) {last}
+ }
+ my @datacc = ([@ccy],[@ccx]);
+ my $cc_graph = GD::Graph::bars->new(750,350);
+ $cc_graph->set( dclrs => [ qw(yellow) ] );
+ $cc_graph->set(
+ x_label => 'Country Code',
+ y_label => 'Total Blocks',
+ long_ticks => 1,
+ tick_length => 0,
+ x_ticks => 0,
+ title => 'Total Top 30 Country Code blocks by lfd',
+ cumulate => 1,
+ borderclrs => $cc_graph->{dclrs},
+ bar_spacing => 4,
+ shadow_depth => 1,
+ transparent => 0,
+ x_label_position => 1/2,
+ );
+ $cc_graph->plot(\@datacc);
+ $img = $imghddir."lfd_cc.gif";
+ open (my $OUT, ">", "$img");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ print $OUT $cc_graph->gd->gif();
+ close ($OUT);
+ }
+ }
+
+ return;
+}
+# end charts
+###############################################################################
+# start minmaxavg
+sub minmaxavg {
+ my $graph = shift;
+ my $name = shift;
+ my $value = shift;
+
+ unless (defined $minmaxavg{$graph}{$name}{MIN}) {$minmaxavg{$graph}{$name}{MIN} = $value}
+ unless (defined $minmaxavg{$graph}{$name}{MAX}) {$minmaxavg{$graph}{$name}{MAX} = $value}
+ if ($minmaxavg{$graph}{$name}{MIN} > $value) {$minmaxavg{$graph}{$name}{MIN} = $value}
+ if ($minmaxavg{$graph}{$name}{MAX} < $value) {$minmaxavg{$graph}{$name}{MAX} = $value}
+ $minmaxavg{$graph}{$name}{AVG} += $value;
+ $minmaxavg{$graph}{$name}{CNT}++;
+
+ return;
+}
+# end minmaxavg
+###############################################################################
+# start graphs_html
+sub graphs_html {
+ my $imgdir = shift;
+ my $html;
+
+ $html .= "\n";
+ $html .= "\n";
+ $html .= "
\n";
+ foreach my $key (sort keys %{$minmaxavg{HOUR}}) {
+ my $item = $key;
+ if ($key =~ /^\d(.*)$/) {$item = $1}
+ $html .= "$item ";
+ $html .= "Min:".sprintf("%.2f",$minmaxavg{HOUR}{$key}{MIN})." ";
+ $html .= "Max:".sprintf("%.2f",$minmaxavg{HOUR}{$key}{MAX})." ";
+ $html .= "Avg:".sprintf("%.2f",$minmaxavg{HOUR}{$key}{AVG})." \n";
+ }
+ $html .= "
Note: This graph displays per minute statistics unless otherwise stated
\n";
+ $html .= "
\n";
+ foreach my $key (sort keys %{$minmaxavg{DAY}}) {
+ my $item = $key;
+ if ($key =~ /^\d(.*)$/) {$item = $1}
+ $html .= "$item ";
+ $html .= "Min:".sprintf("%.2f",$minmaxavg{DAY}{$key}{MIN})." ";
+ $html .= "Max:".sprintf("%.2f",$minmaxavg{DAY}{$key}{MAX})." ";
+ $html .= "Avg:".sprintf("%.2f",$minmaxavg{DAY}{$key}{AVG})." \n";
+ }
+ $html .= "
Note: This graph displays per minute statistics unless otherwise stated
\n";
+ $html .= "
\n";
+ foreach my $key (sort keys %{$minmaxavg{WEEK}}) {
+ my $item = $key;
+ if ($key =~ /^\d(.*)$/) {$item = $1}
+ $html .= "$item ";
+ $html .= "Min:".sprintf("%.2f",$minmaxavg{WEEK}{$key}{MIN})." ";
+ $html .= "Max:".sprintf("%.2f",$minmaxavg{WEEK}{$key}{MAX})." ";
+ $html .= "Avg:".sprintf("%.2f",$minmaxavg{WEEK}{$key}{AVG})." \n";
+ }
+ $html .= "
Note: This graph displays an hourly average of the per minute statistics, so you will not see the peak minute values
\n";
+ $html .= "
\n";
+ foreach my $key (sort keys %{$minmaxavg{MONTH}}) {
+ my $item = $key;
+ if ($key =~ /^\d(.*)$/) {$item = $1}
+ $html .= "$item ";
+ $html .= "Min:".sprintf("%.2f",$minmaxavg{MONTH}{$key}{MIN})." ";
+ $html .= "Max:".sprintf("%.2f",$minmaxavg{MONTH}{$key}{MAX})." ";
+ $html .= "Avg:".sprintf("%.2f",$minmaxavg{MONTH}{$key}{AVG})." \n";
+ }
+ $html .= "
Note: This graph displays an hourly average of the per minute statistics, so you will not see the peak minute values
\n
\n";
+ return $html;
+}
+# end graphs_html
+###############################################################################
+# start charts_html
+sub charts_html {
+ my $cc_lookups = shift;
+ my $imgdir = shift;
+ my $html;
+
+ $html .= "\n";
+ $html .= "\n";
+ $html .= "
\n";
+ $html .= "
\n";
+ $html .= " \n";
+ $html .= "
\n";
+ $html .= "
\n";
+ if ($cc_lookups) {
+ $html .= " \n";
+ $html .= "
\n";
+ $html .= "
\n";
+ $html .= " \n\n";
+ $html .= "
\n";
+ } else {
+ $html .= " \n\n";
+ $html .= "
\n";
+ $html .= "
\n";
+ }
+ $html .= " \n
\n";
+
+ return $html;
+}
+# end charts_html
+###############################################################################
+
+1;
\ No newline at end of file
diff --git a/csf/ConfigServer/Service.pm b/csf/ConfigServer/Service.pm
new file mode 100644
index 0000000..7677920
--- /dev/null
+++ b/csf/ConfigServer/Service.pm
@@ -0,0 +1,131 @@
+###############################################################################
+# Copyright (C) 2006-2025 Jonathan Michaelson
+#
+# https://github.com/waytotheweb/scripts
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, see .
+###############################################################################
+## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
+# start main
+package ConfigServer::Service;
+
+use strict;
+use lib '/usr/local/csf/lib';
+use Carp;
+use IPC::Open3;
+use Fcntl qw(:DEFAULT :flock);
+use ConfigServer::Config;
+
+use Exporter qw(import);
+our $VERSION = 1.01;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw();
+
+my $config = ConfigServer::Config->loadconfig();
+my %config = $config->config();
+
+open (my $IN, "<", "/proc/1/comm");
+flock ($IN, LOCK_SH);
+my $sysinit = <$IN>;
+close ($IN);
+chomp $sysinit;
+if ($sysinit ne "systemd") {$sysinit = "init"}
+
+# end main
+###############################################################################
+# start type
+sub type {
+ return $sysinit;
+}
+# end type
+###############################################################################
+# start startlfd
+sub startlfd {
+ if ($sysinit eq "systemd") {
+ &printcmd($config{SYSTEMCTL},"start","lfd.service");
+ &printcmd($config{SYSTEMCTL},"status","lfd.service");
+ } else {
+ &printcmd("/etc/init.d/lfd","start");
+ }
+
+ return;
+}
+# end startlfd
+###############################################################################
+# start stoplfd
+sub stoplfd {
+ if ($sysinit eq "systemd") {
+ &printcmd($config{SYSTEMCTL},"stop","lfd.service");
+ }
+ else {
+ &printcmd("/etc/init.d/lfd","stop");
+ }
+
+ return;
+}
+# end stoplfd
+###############################################################################
+# start restartlfd
+sub restartlfd {
+ if ($sysinit eq "systemd") {
+ &printcmd($config{SYSTEMCTL},"restart","lfd.service");
+ &printcmd($config{SYSTEMCTL},"status","lfd.service");
+ }
+ else {
+ &printcmd("/etc/init.d/lfd","restart");
+ }
+
+ return;
+}
+# end restartlfd
+###############################################################################
+# start restartlfd
+sub statuslfd {
+ if ($sysinit eq "systemd") {
+ &printcmd($config{SYSTEMCTL},"status","lfd.service");
+ }
+ else {
+ &printcmd("/etc/init.d/lfd","status");
+ }
+
+ return 0
+}
+# end restartlfd
+###############################################################################
+# start printcmd
+sub printcmd {
+ my @command = @_;
+
+ if ($config{DIRECTADMIN}) {
+ my $doublepid = fork;
+ if ($doublepid == 0) {
+ my ($childin, $childout);
+ my $pid = open3($childin, $childout, $childout, @command);
+ while (<$childout>) {print $_}
+ waitpid ($pid, 0);
+ exit;
+ }
+ waitpid ($doublepid, 0);
+ } else {
+ my ($childin, $childout);
+ my $pid = open3($childin, $childout, $childout, @command);
+ while (<$childout>) {print $_}
+ waitpid ($pid, 0);
+ }
+ return;
+}
+# end printcmd
+###############################################################################
+
+1;
\ No newline at end of file
diff --git a/csf/ConfigServer/Slurp.pm b/csf/ConfigServer/Slurp.pm
new file mode 100644
index 0000000..6524b80
--- /dev/null
+++ b/csf/ConfigServer/Slurp.pm
@@ -0,0 +1,68 @@
+###############################################################################
+# Copyright (C) 2006-2025 Jonathan Michaelson
+#
+# https://github.com/waytotheweb/scripts
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, see .
+###############################################################################
+## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
+# start main
+package ConfigServer::Slurp;
+
+use strict;
+use lib '/usr/local/csf/lib';
+use Fcntl qw(:DEFAULT :flock);
+use Carp;
+
+use Exporter qw(import);
+our $VERSION = 1.02;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw(slurp);
+
+our $slurpreg = qr/(?>\x0D\x0A?|[\x0A-\x0C\x85\x{2028}\x{2029}])/;
+our $cleanreg = qr/(\r)|(\n)|(^\s+)|(\s+$)/;
+
+# end main
+###############################################################################
+# start slurp
+sub slurp {
+ my $file = shift;
+ if (-e $file) {
+ sysopen (my $FILE, $file, O_RDONLY) or carp "*Error* Unable to open [$file]: $!";
+ flock ($FILE, LOCK_SH) or carp "*Error* Unable to lock [$file]: $!";
+ my $text = do {local $/; <$FILE>};
+ close ($FILE);
+ return split(/$slurpreg/,$text);
+ } else {
+ carp "*Error* File does not exist: [$file]";
+ }
+
+ return;
+}
+# end slurp
+###############################################################################
+# start slurpreg
+sub slurpreg {
+ return $slurpreg;
+}
+# end slurpreg
+###############################################################################
+# start cleanreg
+sub cleanreg {
+ return $cleanreg;
+}
+# end cleanreg
+###############################################################################
+
+1;
\ No newline at end of file
diff --git a/csf/ConfigServer/URLGet.pm b/csf/ConfigServer/URLGet.pm
new file mode 100644
index 0000000..cdb8a66
--- /dev/null
+++ b/csf/ConfigServer/URLGet.pm
@@ -0,0 +1,305 @@
+###############################################################################
+# Copyright (C) 2006-2025 Jonathan Michaelson
+#
+# https://github.com/waytotheweb/scripts
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, see .
+###############################################################################
+## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
+# start main
+package ConfigServer::URLGet;
+
+use strict;
+use lib '/usr/local/csf/lib';
+use Fcntl qw(:DEFAULT :flock);
+use Carp;
+use IPC::Open3;
+use ConfigServer::Config;
+
+use Exporter qw(import);
+our $VERSION = 2.00;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw();
+
+my $agent = "ConfigServer";
+my $option = 1;
+my $proxy = "";
+
+my $config = ConfigServer::Config->loadconfig();
+my %config = $config->config();
+$SIG{PIPE} = 'IGNORE';
+
+# end main
+###############################################################################
+# start new
+sub new {
+ my $class = shift;
+ $option = shift;
+ $agent = shift;
+ $proxy = shift;
+ my $self = {};
+ bless $self,$class;
+
+ if ($option == 3) {
+ return $self;
+ }
+ elsif ($option == 2) {
+ eval ('use LWP::UserAgent;'); ##no critic
+ if ($@) {return undef}
+ }
+ else {
+ eval {
+ local $SIG{__DIE__} = undef;
+ eval ('use HTTP::Tiny;'); ##no critic
+ };
+ }
+
+ return $self;
+}
+# end new
+###############################################################################
+# start urlget
+sub urlget {
+ my $self = shift;
+ my $url = shift;
+ my $file = shift;
+ my $quiet = shift;
+ my $status;
+ my $text;
+
+ if (!defined $url) {carp "url not specified"; return}
+
+ if ($option == 3) {
+ ($status, $text) = &binget($url,$file,$quiet);
+ }
+ elsif ($option == 2) {
+ ($status, $text) = &urlgetLWP($url,$file,$quiet);
+ }
+ else {
+ ($status, $text) = &urlgetTINY($url,$file,$quiet);
+ }
+ return ($status, $text);
+}
+# end urlget
+###############################################################################
+# start urlgetTINY
+sub urlgetTINY {
+ my $url = shift;
+ my $file = shift;
+ my $quiet = shift;
+ my $status = 0;
+ my $timeout = 1200;
+ if ($proxy eq "") {undef $proxy}
+ my $ua = HTTP::Tiny->new(
+ 'agent' => $agent,
+ 'timeout' => 300,
+ 'proxy' => $proxy
+ );
+ my $res;
+ my $text;
+ ($status, $text) = eval {
+ local $SIG{__DIE__} = undef;
+ local $SIG{'ALRM'} = sub {die "Download timeout after $timeout seconds"};
+ alarm($timeout);
+ if ($file) {
+ local $|=1;
+ my $expected_length;
+ my $bytes_received = 0;
+ my $per = 0;
+ my $oldper = 0;
+ open (my $OUT, ">", "$file\.tmp") or return (1, "Unable to open $file\.tmp: $!");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ $res = $ua->request('GET', $url, {
+ data_callback => sub {
+ my($chunk, $res) = @_;
+ $bytes_received += length($chunk);
+ unless (defined $expected_length) {$expected_length = $res->{headers}->{'content-length'} || 0}
+ if ($expected_length) {
+ my $per = int(100 * $bytes_received / $expected_length);
+ if ((int($per / 5) == $per / 5) and ($per != $oldper) and !$quiet) {
+ print "...$per\%\n";
+ $oldper = $per;
+ }
+ } else {
+ unless ($quiet) {print "."}
+ }
+ print $OUT $chunk;
+ }
+ });
+ close ($OUT);
+ unless ($quiet) {print "\n"}
+ } else {
+ $res = $ua->request('GET', $url);
+ }
+ alarm(0);
+ if ($res->{success}) {
+ if ($file) {
+ rename ("$file\.tmp","$file") or return (1, "Unable to rename $file\.tmp to $file: $!");
+ return (0, $file);
+ } else {
+ return (0, $res->{content});
+ }
+ } else {
+ my $reason = $res->{reason};
+ if ($res->{status} == 599) {$reason = $res->{content}}
+ ($status, $text) = &binget($url,$file,$quiet,$reason);
+ return ($status, $text);
+ }
+ };
+ alarm(0);
+ if ($@) {return (1, $@)}
+ return ($status,$text);
+}
+# end urlgetTINY
+###############################################################################
+# start urlgetLWP
+sub urlgetLWP {
+ my $url = shift;
+ my $file = shift;
+ my $quiet = shift;
+ my $status = 0;
+ my $timeout = 300;
+ my $ua = LWP::UserAgent->new;
+ $ua->agent($agent);
+ $ua->timeout(30);
+ if ($proxy ne "") {$ua->proxy([ 'http', 'https' ], $proxy)}
+#use LWP::ConnCache;
+#my $cache = LWP::ConnCache->new;
+#$cache->total_capacity([1]);
+#$ua->conn_cache($cache);
+ my $req = HTTP::Request->new(GET => $url);
+ my $res;
+ my $text;
+ ($status, $text) = eval {
+ local $SIG{__DIE__} = undef;
+ local $SIG{'ALRM'} = sub {die "Download timeout after $timeout seconds"};
+ alarm($timeout);
+ if ($file) {
+ local $|=1;
+ my $expected_length;
+ my $bytes_received = 0;
+ my $per = 0;
+ my $oldper = 0;
+ open (my $OUT, ">", "$file\.tmp") or return (1, "Unable to open $file\.tmp: $!");
+ flock ($OUT, LOCK_EX);
+ binmode ($OUT);
+ $res = $ua->request($req,
+ sub {
+ my($chunk, $res) = @_;
+ $bytes_received += length($chunk);
+ unless (defined $expected_length) {$expected_length = $res->content_length || 0}
+ if ($expected_length) {
+ my $per = int(100 * $bytes_received / $expected_length);
+ if ((int($per / 5) == $per / 5) and ($per != $oldper) and !$quiet) {
+ print "...$per\%\n";
+ $oldper = $per;
+ }
+ } else {
+ unless ($quiet) {print "."}
+ }
+ print $OUT $chunk;
+ });
+ close ($OUT);
+ unless ($quiet) {print "\n"}
+ } else {
+ $res = $ua->request($req);
+ }
+ alarm(0);
+ if ($res->is_success) {
+ if ($file) {
+ rename ("$file\.tmp","$file") or return (1, "Unable to rename $file\.tmp to $file: $!");
+ return (0, $file);
+ } else {
+ return (0, $res->content);
+ }
+ } else {
+ ($status, $text) = &binget($url,$file,$quiet,$res->message);
+ return ($status, $text);
+ }
+ };
+ alarm(0);
+ if ($@) {
+ return (1, $@);
+ }
+ if ($text) {
+ return ($status,$text);
+ } else {
+ return (1, "Download timeout after $timeout seconds");
+ }
+}
+# end urlget
+###############################################################################
+# start binget
+sub binget {
+ my $url = shift;
+ my $file = shift;
+ my $quiet = shift;
+ my $errormsg = shift;
+ $url = "'$url'";
+
+ my $cmd;
+ if (-e $config{CURL}) {
+ $cmd = $config{CURL}." -skLf -m 120";
+ if ($file) {$cmd = $config{CURL}." -kLf -m 120 -o";}
+ }
+ elsif (-e $config{WGET}) {
+ $cmd = $config{WGET}." -qT 120 -O-";
+ if ($file) {$cmd = $config{WGET}." -T 120 -O"}
+ }
+ if ($cmd ne "") {
+ if ($file) {
+ my ($childin, $childout);
+ my $cmdpid = open3($childin, $childout, $childout, $cmd." $file\.tmp $url");
+ my @output = <$childout>;
+ waitpid ($cmdpid, 0);
+ unless ($quiet and $option != 3) {
+ print "Using fallback [$cmd]\n";
+ print @output;
+ }
+ if (-e "$file\.tmp") {
+ rename ("$file\.tmp","$file") or return (1, "Unable to rename $file\.tmp to $file: $!");
+ return (0, $file);
+ } else {
+ if ($option == 3) {
+ return (1, "Unable to download: ".$cmd." $file\.tmp $url".join("",@output));
+ } else {
+ return (1, "Unable to download: ".$errormsg);
+ }
+ }
+ } else {
+ my ($childin, $childout);
+ my $cmdpid = open3($childin, $childout, $childout, $cmd." $url");
+ my @output = <$childout>;
+ waitpid ($cmdpid, 0);
+ if (scalar @output > 0) {
+ return (0, join("",@output));
+ } else {
+ if ($option == 3) {
+ return (1, "Unable to download: [$cmd $url]".join("",@output));
+ } else {
+ return (1, "Unable to download: ".$errormsg);
+ }
+ }
+ }
+ }
+ if ($option == 3) {
+ return (1, "Unable to download (CURL/WGET also not present, see csf.conf)");
+ } else {
+ return (1, "Unable to download (CURL/WGET also not present, see csf.conf): ".$errormsg);
+ }
+}
+# end binget
+###############################################################################
+1;
\ No newline at end of file
diff --git a/csf/ConfigServer/cseUI.pm b/csf/ConfigServer/cseUI.pm
new file mode 100644
index 0000000..6b0d74a
--- /dev/null
+++ b/csf/ConfigServer/cseUI.pm
@@ -0,0 +1,1042 @@
+###############################################################################
+# Copyright (C) 2006-2025 Jonathan Michaelson
+#
+# https://github.com/waytotheweb/scripts
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, see .
+###############################################################################
+## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
+package ConfigServer::cseUI;
+
+use strict;
+use Fcntl qw(:DEFAULT :flock);
+use File::Find;
+use File::Copy;
+use IPC::Open3;
+
+use Exporter qw(import);
+our $VERSION = 2.03;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw();
+
+umask(0177);
+
+our ($chart, $ipscidr6, $ipv6reg, $ipv4reg, %config, %ips, $mobile,
+ %FORM, $script, $script_da, $images, $myv);
+
+our ($act, $destpath, $element, $extramessage, $fieldname, $fileinc,
+ $filetemp, $message, $name, $origpath, $storepath, $tgid, $thisdir,
+ $tuid, $value, $webpath, %ele, %header, @bits, @dirs, @filebodies,
+ @filenames, @files, @months, @parts, @passrecs, @thisdirs, @thisfiles,
+ $files);
+#
+###############################################################################
+# start main
+sub main {
+ my $FORM_ref = shift;
+ %FORM = %{$FORM_ref};
+ $fileinc = shift;
+ $script = shift;
+ $script_da = shift;
+ $images = shift;
+ $myv = shift;
+ $| = 1;
+
+ &loadconfig;
+
+ $webpath = '/';
+
+ if ($FORM{do} eq "view") {
+ &view;
+ exit;
+ }
+
+ print "Content-type: text/html\r\n\r\n";
+
+ my $bootstrapcss = " ";
+ my $jqueryjs = "";
+ my $bootstrapjs = "";
+
+ print <
+
+
+ ConfigServer Explorer
+
+
+ $bootstrapcss
+
+ $jqueryjs
+ $bootstrapjs
+
+
+
+EOF
+
+ unless ($FORM{do} eq "console") {
+ print "\n";
+ print "
\n";
+ if ($config{UI_CXS} or $config{UI_CSE}) {
+ print "
csf ";
+ if ($config{UI_CXS}) {print "cxs "}
+ if ($config{UI_CSE}) {print "cse "}
+ print "<", "/select> \n";
+ }
+ print "
cse Logout \n";
+ print "
\n";
+ print <
+ ConfigServer Explorer - cse
+
+EOF
+ }
+
+ $message = "";
+
+ if ($fileinc) {&uploadfile}
+ elsif ($FORM{do} eq "") {&browse}
+ elsif ($FORM{quit} == 2) {&browse}
+ elsif ($FORM{do} eq "b") {&browse}
+ elsif ($FORM{do} eq "p") {&browse}
+ elsif ($FORM{do} eq "o") {&browse}
+ elsif ($FORM{do} eq "c") {&browse}
+ elsif ($FORM{do} eq "m") {&browse}
+ elsif ($FORM{do} eq "pw") {&browse}
+ elsif ($FORM{do} eq "r") {&browse}
+ elsif ($FORM{do} eq "newf") {&browse}
+ elsif ($FORM{do} eq "newd") {&browse}
+ elsif ($FORM{do} eq "cnewf") {&cnewf}
+ elsif ($FORM{do} eq "cnewd") {&cnewd}
+ elsif ($FORM{do} eq "ren") {&ren}
+ elsif ($FORM{do} eq "del") {&del}
+ elsif ($FORM{do} eq "setp") {&setp}
+ elsif ($FORM{do} eq "seto") {&seto}
+ elsif ($FORM{do} eq "cd") {&cd}
+ elsif ($FORM{do} eq "console") {&console}
+ elsif ($FORM{do} eq "edit") {&edit}
+ elsif ($FORM{do} eq "Cancel") {&browse}
+ elsif ($FORM{do} eq "Save") {&save}
+ elsif ($FORM{do} eq "copyit") {©it}
+ elsif ($FORM{do} eq "moveit") {&moveit}
+ else {print "Invalid action"};
+
+ unless ($FORM{do} eq "console") {
+ print "©2006-2023, ConfigServer Services (Jonathan Michaelson)
\n";
+ }
+ print <
+
+
+
+EOF
+ exit;
+}
+# end main
+###############################################################################
+# start browse
+sub browse {
+ my $extra;
+ if ($FORM{c}) {
+ if (-e "$webpath$FORM{c}") {
+ $extra = "&c=$FORM{c}";
+ } else {
+ $FORM{c} = "";
+ }
+ }
+ if ($FORM{m}) {
+ if (-e "$webpath$FORM{m}") {
+ $extra = "&m=$FORM{m}"
+ } else {
+ $FORM{m} = "";
+ }
+ }
+
+ print "\n";
+
+ $thisdir = $webpath;
+ if ($thisdir !~ /\/$/) {$thisdir .= "/"}
+ $thisdir .= $FORM{p};
+ $thisdir =~ s/\/+/\//g;
+ @months = ("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec");
+
+ my $errordir = 0;
+ opendir (DIR, "$thisdir") or $errordir = 1;
+ while (my $file = readdir(DIR)) {
+ if (-d "$thisdir/$file") {
+ if ($file !~ /^\.$|^\.\.$/) {push (@thisdirs, $file)}
+ } else {
+ push (@thisfiles, $file);
+ }
+
+ }
+ closedir (DIR);
+
+ @thisdirs = sort @thisdirs;
+ @thisfiles = sort @thisfiles;
+
+ print "\n";
+ print "WARNING! While this utility can be very useful it is also very dangerous indeed. You can easily render your server inoperable and unrecoverable by performing ill advised actions. No warranty or guarantee is provided with the product that protects against system damage.\n";
+ print "
\n";
+
+ if ($message) {print "
$message \n";}
+ print "\n";
+ print "";
+ print "[Home ]";
+ my $path = "";
+ my $cnt = 2;
+ my @path = split(/\//,$FORM{p});
+ foreach my $dir (@path) {
+ if ($dir ne "" and ($dir ne "/")) {
+ if ($cnt == @path) {
+ print "/$dir";
+ } else {
+ print "/$dir ";
+ }
+ $path .= "/$dir";
+ $cnt++;
+ }
+ }
+ if ($FORM{c}) {print " Copy buffer:$FORM{c} \n"}
+ if ($FORM{m}) {print " Move buffer:$FORM{m} \n"}
+ print " \n";
+ if ($errordir) {
+ print "Permission Denied ";
+ } else {
+ if (@thisdirs > 0) {
+ print "\n";
+ print "";
+ print "Directory Name ";
+ print "Size ";
+ print "Date ";
+ print "User(uid)/Group(gid) ";
+ print "Perms ";
+ print "Actions ";
+ print " \n";
+ }
+ my $class = "tdshade2";
+ foreach my $dir (@thisdirs) {
+ if ($dir =~/'|"|\||\`/) {
+ print "".quotemeta($dir)."Invalid directory name - ignored ";
+ next;
+ }
+ my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = stat("$thisdir/$dir");
+ if ($size < 1024) {
+ }
+ elsif ($size < (1024 * 1024)) {
+ $size = sprintf("%.1f",($size/1024));
+ $size .= "k";
+ }
+ else {
+ $size = sprintf("%.1f",($size/(1024 * 1024)));
+ $size .= "M";
+ }
+ $mode = sprintf "%04o", $mode & oct("07777");
+ $tgid = getgrgid($gid);
+ if ($tgid eq "") {$tgid = $gid}
+ $tuid = getpwuid($uid);
+ if ($tuid eq "") {$tuid = $uid}
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($mtime);
+ $year += 1900;
+ my $time = sprintf "%02d:%02d:%02d", $hour, $min, $sec;
+ $mday = sprintf "%02d", $mday;
+ $mtime = "$mday-$months[$mon]-$year $time";
+ my $pp = "";
+ my $passfile = "$FORM{p}/$dir";
+ $passfile =~ s/\//\_/g;
+ $passfile =~ s/\\/\_/g;
+ $passfile =~ s/\:/\_/g;
+ if (-e "$storepath/$passfile.htpasswd") {
+ open (my $PASSFILE, "<","$storepath/$passfile.htpasswd") or die $!;
+ flock ($PASSFILE, LOCK_SH);
+ @passrecs = <$PASSFILE>;
+ close ($PASSFILE);
+ chomp @passrecs;
+ if (@passrecs > 0) {$pp = "**"}
+ }
+
+ print "";
+ if ($FORM{do} eq "r" and ($FORM{f} eq $dir)) {
+ print "\n";
+ print "";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print "$pp ";
+ print " \n";
+ }
+ elsif (-r "$webpath$FORM{p}/$dir") {
+ print "$dir $pp ";
+ }
+ else {
+ print "$dir ";
+ }
+ print "$size ";
+ print "$mtime ";
+ if ($FORM{do} eq "o" and ($FORM{f} eq $dir)) {
+ print "\n";
+ print "";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " ";
+ print " \n";
+ }
+ else {
+ print "$tuid($uid)/$tgid($gid) ";
+ }
+ if ($FORM{do} eq "p" and ($FORM{f} eq $dir)) {
+ print "\n";
+ print "";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " ";
+ print " \n";
+ }
+ else {
+ print "$mode ";
+ }
+ print " ";
+ print " ";
+ print " ";
+ print " ";
+ print " ";
+ print " \n";
+ }
+ if ($FORM{do} eq "newd") {
+ print "";
+ print "\n";
+ print "";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " ";
+ print " \n";
+ print " ";
+ print " ";
+ print " ";
+ print " ";
+ print " ";
+ print " \n";
+ }
+ if (($FORM{do} eq "c") and (-d "$webpath$FORM{c}")) {
+ my $newf = (split(/\//,$FORM{c}))[-1];
+ print "";
+ print "\n";
+ print "";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " ";
+ print " \n";
+ print " ";
+ print " ";
+ print " ";
+ print " ";
+ print " ";
+ print " \n";
+ }
+ if (($FORM{do} eq "m") and (-d "$webpath$FORM{m}")) {
+ my $newf = (split(/\//,$FORM{m}))[-1];
+ print "";
+ print "\n";
+ print "";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " ";
+ print " \n";
+ print " ";
+ print " ";
+ print " ";
+ print " ";
+ print " ";
+ print " \n";
+ }
+
+ if (@thisfiles > 0) {
+ print " \n";
+ print "";
+ print "File Name ";
+ print "Size ";
+ print "Date ";
+ print "User(uid)/Group(gid) ";
+ print "Perms ";
+ print "Actions ";
+ print " \n";
+ }
+ $class = "tdshade2";
+ foreach my $file (@thisfiles) {
+ if ($file =~/'|"|\||\`/) {
+ print "".quotemeta($file)."Invalid file name - ignored ";
+ next;
+ }
+ my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = stat("$thisdir/$file");
+ if ($size < 1024) {
+ }
+ elsif ($size < (1024 * 1024)) {
+ $size = sprintf("%.1f",($size/1024));
+ $size .= "k";
+ }
+ else {
+ $size = sprintf("%.1f",($size/(1024 * 1024)));
+ $size .= "M";
+ }
+ $mode = sprintf "%03o", $mode & oct("00777");
+ $tgid = getgrgid($gid);
+ if ($tgid eq "") {$tgid = $gid}
+ $tuid = getpwuid($uid);
+ if ($tuid eq "") {$tuid = $uid}
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($mtime);
+ $year += 1900;
+ my $time = sprintf "%02d:%02d:%02d", $hour, $min, $sec;
+ $mday = sprintf "%02d", $mday;
+ $mtime = "$mday-$months[$mon]-$year $time";
+ print "";
+ if ($FORM{do} eq "r" and ($FORM{f} eq $file)) {
+ print "\n";
+ print "";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " ";
+ print " \n";
+ }
+ else {
+ $act = "$script?do=view&p=$FORM{p}&f=$file$extra\#new";
+ print "$file ";
+ }
+ print "$size ";
+ print "$mtime ";
+ if ($FORM{do} eq "o" and ($FORM{f} eq $file)) {
+ print "\n";
+ print "";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " ";
+ print " \n";
+ }
+ else {
+ print "$tuid($uid)/$tgid($gid) ";
+ }
+ if ($FORM{do} eq "p" and ($FORM{f} eq $file)) {
+ print "\n";
+ print "";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " ";
+ print " \n";
+ }
+ else {
+ print "$mode ";
+ }
+ my $ext = (split(/\./,$file))[-1];
+ if (-T "$webpath$FORM{p}/$file") {
+ my $act = "";
+ print " $act ";
+ } else {
+ print " ";
+ }
+ print " ";
+ print " ";
+ print " ";
+ print " ";
+ print " \n";
+ }
+ if ($FORM{do} eq "newf") {
+ print "";
+ print "\n";
+ print "";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " ";
+ print " \n";
+ print " ";
+ print " ";
+ print " ";
+ print " ";
+ print " ";
+ print " \n";
+ }
+ if (($FORM{do} eq "c") and (-f "$webpath$FORM{c}")) {
+ my $newf = (split(/\//,$FORM{c}))[-1];
+ print "";
+ print "\n";
+ print "";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " ";
+ print " \n";
+ print " ";
+ print " ";
+ print " ";
+ print " ";
+ print " ";
+ print " \n";
+ }
+ if (($FORM{do} eq "m") and (-f "$webpath$FORM{m}")) {
+ my $newf = (split(/\//,$FORM{m}))[-1];
+ print "";
+ print "\n";
+ print "";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " ";
+ print " \n";
+ print " ";
+ print " ";
+ print " ";
+ print " ";
+ print " ";
+ print " \n";
+ }
+ }
+ print "
\n";
+
+ print "All the following actions apply to the current directory
\n";
+ print "\n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print "\n";
+ print " \n";
+ print "\n";
+ print "\n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print "\n";
+ print " \n";
+ print "\n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+
+ print "\n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print " \n";
+ print "\n";
+ print " \n";
+ return;
+}
+# end browse
+###############################################################################
+# start setp
+sub setp {
+ my $status = 0;
+ chmod (oct("0$FORM{newp}"),"$webpath$FORM{p}/$FORM{f}") or $status = $!;
+ if ($status) {$message = "Operation Failed - $status"} else {$message = ""}
+ &browse;
+ return;
+}
+# end setp
+###############################################################################
+# start seto
+sub seto {
+ my $status = "";
+ my ($uid,$gid) = split (/\:/,$FORM{newo});
+ if ($uid !~ /^\d/) {$uid = (getpwnam($uid))[2]}
+ if ($gid !~ /^\d/) {$gid = (getgrnam($gid))[2]}
+ if ($uid eq "") {$message .= "No such user \n"}
+ if ($gid eq "") {$message .= "No such group \n"}
+
+ if ($message eq "") {
+ chown ($uid,$gid,"$webpath$FORM{p}/$FORM{f}") or $status = $!;
+ if ($status) {$message = "Operation Failed - $status"} else {$message = ""}
+ }
+ &browse;
+ return;
+}
+# end seto
+###############################################################################
+# start ren
+sub ren {
+ my $status = 0;
+ rename ("$webpath$FORM{p}/$FORM{f}","$webpath$FORM{p}/$FORM{newf}") or $status = $!;
+ if ($status) {$message = "Operation Failed - $status"} else {$message = ""}
+ &browse;
+ return;
+}
+# end ren
+###############################################################################
+# start moveit
+sub moveit {
+ if ("$webpath$FORM{m}" eq "$webpath$FORM{p}/$FORM{newf}") {
+ $message = "Move Failed - Cannot overwrite original";
+ }
+ elsif ((-d "$webpath$FORM{m}") and ("$webpath$FORM{p}/$FORM{newf}" =~ /^$webpath$FORM{m}\//)) {
+ $message = "Move Failed - Cannot move inside original";
+ }
+ else {
+ my $status = 0;
+ rename ("$webpath$FORM{m}","$webpath$FORM{p}/$FORM{newf}") or $status = $!;
+ if ($status) {$message = "Operation Failed - $status"} else {$message = ""}
+ }
+ if ($message eq "") {$FORM{m} = ""}
+ &browse;
+ return;
+}
+# end moveit
+###############################################################################
+# start copyit
+sub copyit {
+ if ("$webpath$FORM{c}" eq "$webpath$FORM{p}/$FORM{newf}") {
+ $message = "Copy Failed - Cannot overwrite original";
+ }
+ elsif ((-d "$webpath$FORM{c}") and ("$webpath$FORM{p}/$FORM{newf}" =~ /^$webpath$FORM{c}\//)) {
+ $message = "Copy Failed - Cannot copy inside original";
+ }
+ else {
+ if (-d "$webpath$FORM{c}") {
+ $origpath = "$webpath$FORM{c}";
+ $destpath = "$webpath$FORM{p}/$FORM{newf}";
+ find(\&mycopy, $origpath);
+ } else {
+ copy ("$webpath$FORM{c}","$webpath$FORM{p}/$FORM{newf}") or $message = "Copy Failed - $!";
+ if ($message eq "") {
+ my $mode = sprintf "%04o", (stat("$webpath$FORM{c}"))[2] & oct("00777");
+ chmod (oct($mode),"$webpath$FORM{p}/$FORM{newf}") or $message = "Permission Change Failed - $!";
+ }
+ }
+ }
+ if ($message eq "") {$FORM{c} = ""}
+ &browse;
+ return;
+}
+# end copyit
+###############################################################################
+# start mycopy
+sub mycopy {
+ my $file = $File::Find::name;
+ (my $dest = $file) =~ s/^\Q$origpath/$destpath/;
+ my $status = "";
+ if (-d $file) {
+ my $err = (split(/\//,$dest))[-1];
+ mkpath ($dest) or $status = "Copy Failed Making New Dir [$err] - $! \n";
+ } elsif (-f $file) {
+ my $err = (split(/\//,$file))[-1];
+ copy ($file,$dest) or $status = "Copy Failed [$err] - $! \n";
+ }
+ if ($status eq "") {
+ my $err = (split(/\//,$file))[-1];
+ my $mode = sprintf "%04o", (stat("$file"))[2] & oct("00777");
+ chmod (oct($mode),"$dest") or $message .= "Copy Failed Setting Perms [$err] - $! \n";
+ } else {
+ $message .= $status;
+ }
+ return;
+}
+# end mycopy
+###############################################################################
+# start cnewd
+sub cnewd {
+ my $status = 0;
+ if ($FORM{newf} ne "") {
+ mkdir ("$webpath$FORM{p}/$FORM{newf}",0777) or $status = $!;
+ }
+ if ($status) {$message = "Operation Failed - $status"} else {$message = ""}
+ &browse;
+ return;
+}
+# end cnewd
+###############################################################################
+# start cnewf
+sub cnewf {
+ my $status = 0;
+ if ($FORM{newf} ne "") {
+ if (-f "$webpath$FORM{p}/$FORM{newf}") {
+ $status = "File exists";
+ } else {
+ open (my $OUT, ">","$webpath$FORM{p}/$FORM{newf}") or $status = $!;
+ flock ($OUT, LOCK_EX);
+ close ($OUT);
+ }
+ }
+ if ($status) {$message = "Operation Failed - $status"} else {$message = ""}
+ &browse;
+ return;
+}
+# end cnewf
+###############################################################################
+# start del
+sub del {
+ my $status = 0;
+ if (-d "$webpath$FORM{p}/$FORM{f}") {
+ rmtree("$webpath$FORM{p}/$FORM{f}", 0, 0) or $status = $!;
+ } else {
+ unlink ("$webpath$FORM{p}/$FORM{f}") or $status = $!;
+ }
+ if ($status) {$message = "Operation Failed - $status"} else {$message = ""}
+ &browse;
+ return;
+}
+# end del
+###############################################################################
+# start view
+sub view {
+ if (-e "$webpath$FORM{p}/$FORM{f}" ) {
+ if (-T "$webpath$FORM{p}/$FORM{f}") {
+ print "content-type: text/plain\r\n";
+ } else {
+ print "content-type: application/octet-stream\r\n";
+ }
+ print "content-disposition: attachment; filename=$FORM{f}\r\n\r\n";
+
+ open(my $IN,"<","$webpath$FORM{p}/$FORM{f}") or die $!;
+ flock ($IN, LOCK_SH);
+ while (<$IN>) {print}
+ close($IN);
+ }else{
+ print "content-type: text/html\r\n\r\n";
+ print "File [$webpath$FORM{p}/$FORM{f}] not found!";
+ }
+ return;
+}
+# end view
+###############################################################################
+# start console
+sub console {
+ my $thisdir = "$webpath$FORM{p}";
+ $thisdir =~ s/\/+/\//g;
+
+ print "
\n";
+ print "root [$thisdir]# $FORM{cmd}\n";
+ chdir $thisdir;
+
+ $| = 1;
+ my ($childin, $childout);
+ my $cmdpid = open3($childin, $childout, $childout, $FORM{cmd});
+ while (my $line = <$childout>) {
+ $line =~ s/\\<\;/g;
+ $line =~ s/\>/\>\;/g;
+ print $line;
+ }
+ waitpid ($cmdpid, 0);
+ print "root [$thisdir]# _ \n";
+ print "";
+ return;
+}
+# end console
+###############################################################################
+# start cd
+sub cd {
+ if (-d $FORM{directory}) {
+ $FORM{p} = $FORM{directory};
+ } else {
+ $message = "No such directory [$FORM{directory}]";
+ }
+
+ &browse;
+ return;
+}
+# end cd
+###############################################################################
+# start edit
+sub edit {
+ open (my $IN, "<","$webpath$FORM{p}/$FORM{f}") or die $!;
+ flock ($IN, LOCK_SH);
+ my @data = <$IN>;
+ close ($IN);
+
+ my $filedata;
+ foreach my $line (@data) {
+ $line =~ s/\</g;
+ $line =~ s/\>/>/g;
+ $filedata .= $line;
+ }
+
+ my $lf = 0;
+ if ($filedata =~ /\r/) {$lf = 1}
+
+ print "\n";
+ print "\n";
+ print "\n";
+ print " \n";
+ return;
+}
+# end edit
+###############################################################################
+# start save
+sub save {
+ unless ($FORM{lf}) {$FORM{newf} =~ s/\r//g}
+ my $status = 0;
+ open (my $OUT, ">","$webpath$FORM{p}/$FORM{f}") or $status = $!;
+ flock ($OUT, LOCK_EX);
+ print $OUT $FORM{newf};
+ close ($OUT);
+
+ if ($status) {$message = "Operation Failed - $status"} else {$message = ""}
+ &browse;
+ return;
+}
+# end save
+###############################################################################
+# start uploadfile
+sub uploadfile {
+ my $crlf = "\r\n";
+ my @data = split (/$crlf/,$fileinc);
+
+ my $boundary = $data[0];
+
+ $boundary =~ s/\"//g;
+ $boundary =~ s/$crlf//g;
+
+ my $start = 0;
+ my $part_cnt=-1;
+ undef @parts;
+ my $fileno = 0;
+
+ foreach my $line (@data) {
+ if ($line =~ /^$boundary--/) {
+ last;
+ }
+ if ($line =~ /^$boundary/) {
+ $part_cnt++;
+ $start = 1;
+ next;
+ }
+ if ($start) {
+ $parts[$part_cnt] .= $line.$crlf;
+ }
+ }
+
+ foreach my $part (@parts) {
+ my @partdata = split(/$crlf/,$part);
+ undef %header;
+ my $body = "";
+ my $dobody = 0;
+ my $lastfieldname = "";
+
+ foreach my $line (@partdata) {
+ if (($line eq "") and !($dobody)) {
+ $dobody = 1;
+ next;
+ }
+
+ if ($dobody) {
+ $body .= $line.$crlf;
+ } else {
+ if ($line =~ /^\s/) {
+ $header{$lastfieldname} .= $line;
+ } else {
+ ($fieldname, $value) = split (/\:\s/,$line,2);
+ $fieldname = lc $fieldname;
+ $fieldname =~ s/-/_/g;
+ $header{$fieldname} = $value;
+ $lastfieldname = $fieldname;
+ }
+ }
+ }
+
+ my @elements = split(/\;/,$header{content_disposition});
+ foreach my $element (@elements) {
+ $element =~ s/\s//g;
+ $element =~ s/\"//g;
+ ($name,$value) = split(/\=/,$element);
+ $FORM{$value} = $body;
+ $ele{$name} = $value;
+ $ele{$ele{name}} = $value;
+ if ($value =~ /^file(.*)$/) {$files = $1}
+ }
+
+ my $filename = $ele{"file$files"};
+ if ($filename ne "") {
+ $fileno++;
+ $filename =~ s/\"//g;
+ $filename =~ s/\r//g;
+ $filename =~ s/\n//g;
+ @bits = split(/\\/,$filename);
+ $filetemp=$bits[-1];
+ @bits = split(/\//,$filetemp);
+ $filetemp=$bits[-1];
+ @bits = split(/\:/,$filetemp);
+ $filetemp=$bits[-1];
+ @bits = split(/\"/,$filetemp);
+ $filename=$bits[0];
+ push (@filenames, $filename);
+ push (@filebodies, $body);
+ }
+ }
+
+ $FORM{p} =~ s/\r//g;
+ $FORM{p} =~ s/\n//g;
+ $FORM{type} =~ s/\r//g;
+ $FORM{type} =~ s/\n//g;
+ $FORM{c} =~ s/\r//g;
+ $FORM{c} =~ s/\n//g;
+ $FORM{m} =~ s/\r//g;
+ $FORM{m} =~ s/\n//g;
+ $FORM{caller} =~ s/\r//g;
+ $FORM{caller} =~ s/\n//g;
+
+ for (my $x = 0;$x < @filenames ;$x++) {
+ $filenames[$x] =~ s/\r//g;
+ $filenames[$x] =~ s/\n//g;
+ $filenames[$x] =~ s/^file-//g;
+ $filenames[$x] = (split (/\\/,$filenames[$x]))[-1];
+ $filenames[$x] = (split (/\//,$filenames[$x]))[-1];
+ if ($FORM{type} eq "ascii") {$filebodies[$x] =~ s/\r//g}
+ if (-e "$webpath$FORM{p}/$filenames[$x]") {
+ $extramessage .= " $filenames[$x] - Already exists, delete the original first";
+ $fileno--;
+ next;
+ }
+ sysopen (my $OUT,"$webpath$FORM{p}/$filenames[$x]", O_WRONLY | O_CREAT);
+ flock ($OUT, LOCK_EX);
+ print $OUT $filebodies[$x];
+ close ($OUT);
+ $extramessage .= " $filenames[$x] - Uploaded";
+ }
+
+ $message = "$fileno File(s) Uploaded".$extramessage;
+
+ &browse;
+ return;
+}
+# end uploadfile
+###############################################################################
+# start countfiles
+sub countfiles {
+ if (-d $File::Find::name) {push (@dirs, $File::Find::name)} else {push (@files, $File::Find::name)}
+ return;
+}
+# end countfiles
+###############################################################################
+# loadconfig
+sub loadconfig {
+ sysopen (my $IN, "/etc/csf/csf.conf", O_RDWR | O_CREAT) or die "Unable to open file: $!";
+ flock ($IN, LOCK_SH);
+ my @config = <$IN>;
+ close ($IN);
+ chomp @config;
+
+ foreach my $line (@config) {
+ if ($line =~ /^\#/) {next}
+ if ($line !~ /=/) {next}
+ my ($name,$value) = split (/=/,$line,2);
+ $name =~ s/\s//g;
+ if ($value =~ /\"(.*)\"/) {
+ $value = $1;
+ } else {
+ &error(__LINE__,"Invalid configuration line");
+ }
+ $config{$name} = $value;
+ }
+ return;
+}
+# end loadconfig
+###############################################################################
+
+1;
diff --git a/csf/Crypt/Blowfish_PP.pm b/csf/Crypt/Blowfish_PP.pm
new file mode 100644
index 0000000..455e3fc
--- /dev/null
+++ b/csf/Crypt/Blowfish_PP.pm
@@ -0,0 +1,519 @@
+# This is Crypt/Blowfish_PP.pm which is an implementation of Bruce Schneier's
+# blowfish cryptographic algorithm. I will write some proper docs when I get
+# time....
+# code is (c) copyright Matthew Byng-Maddick 2000-2023, and
+# some bits are copyright Bruce Schneier. For more information see his website
+# at http://www.counterpane.com/
+
+=head1 NAME
+
+B - Blowfish encryption algorithm implemented purely in Perl
+
+=head1 SYNOPSIS
+
+C;
+
+$blowfish=new Crypt::Blowfish_PP($key);
+
+$ciphertextBlock=$blowfish->encrypt($plaintextBlock);
+
+$plaintextBlock=$blowfish->decrypt($ciphertextBlock);
+
+=head1 DESCRIPTION
+
+The B module provides for users to use the Blowfish encryption
+algorithm in perl. The implementation is entirely Object Oriented, as there is
+quite a lot of context inherent in making blowfish as fast as it is. The key is
+anywhere between 64 and 448 bits (8 and 56 bytes), and should be passed as a
+packed string. The transformation itself is a 16-round Feistel Network, and
+operates on a 64 bit block.
+
+Object methods for the Crypt::Blowfish_PP module:
+
+=cut
+package Crypt::Blowfish_PP;
+
+use strict;
+use vars qw($VERSION);
+
+$VERSION="1.12";
+
+=head2 B(I)
+
+The B() method initialises a blowfish object with the key that is passed.
+This is the slow part of doing a blowfish encryption or decryption, as it
+initialises the 18 p-boxes and the 1024 s-boxes that are used for the algorithm.
+It will return undef if the key is not of a valid length.
+
+=cut
+
+sub new
+ {
+ my $pack=shift;
+ my $key=shift;
+ return undef if(!defined($key));
+ my %h=(
+ p_boxes =>
+ [
+ 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
+ 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
+ 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
+ 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
+ 0x9216d5d9, 0x8979fb1b
+ ],
+ s_boxes =>
+ [
+ [
+ 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
+ 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
+ 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
+ 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
+ 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
+ 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
+ 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
+ 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
+ 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
+ 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
+ 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
+ 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
+ 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
+ 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
+ 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
+ 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
+ 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
+ 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
+ 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
+ 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
+ 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
+ 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
+ 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
+ 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
+ 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
+ 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
+ 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
+ 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
+ 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
+ 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
+ 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
+ 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
+ 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
+ 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
+ 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
+ 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
+ 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
+ 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
+ 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
+ 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
+ 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
+ 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
+ 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
+ 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
+ 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
+ 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
+ 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
+ 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
+ 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
+ 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
+ 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
+ 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
+ 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
+ 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
+ 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
+ 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
+ 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
+ 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
+ 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
+ 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
+ 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
+ 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
+ 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
+ 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a
+ ],
+ [
+ 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
+ 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
+ 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
+ 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
+ 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
+ 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
+ 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
+ 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
+ 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
+ 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
+ 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
+ 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
+ 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
+ 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
+ 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
+ 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
+ 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
+ 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
+ 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
+ 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
+ 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
+ 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
+ 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
+ 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
+ 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
+ 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
+ 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
+ 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
+ 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
+ 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
+ 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
+ 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
+ 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
+ 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
+ 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
+ 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
+ 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
+ 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
+ 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
+ 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
+ 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
+ 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
+ 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
+ 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
+ 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
+ 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
+ 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
+ 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
+ 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
+ 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
+ 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
+ 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
+ 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
+ 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
+ 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
+ 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
+ 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
+ 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
+ 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
+ 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
+ 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
+ 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
+ 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
+ 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7
+ ],
+ [
+ 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
+ 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
+ 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
+ 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
+ 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
+ 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
+ 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
+ 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
+ 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
+ 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
+ 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
+ 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
+ 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
+ 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
+ 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
+ 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
+ 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
+ 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
+ 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
+ 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
+ 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
+ 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
+ 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
+ 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
+ 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
+ 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
+ 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
+ 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
+ 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
+ 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
+ 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
+ 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
+ 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
+ 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
+ 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
+ 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
+ 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
+ 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
+ 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
+ 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
+ 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
+ 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
+ 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
+ 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
+ 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
+ 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
+ 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
+ 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
+ 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
+ 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
+ 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
+ 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
+ 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
+ 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
+ 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
+ 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
+ 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
+ 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
+ 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
+ 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
+ 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
+ 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
+ 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
+ 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0
+ ],
+ [
+ 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
+ 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
+ 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
+ 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
+ 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
+ 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
+ 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
+ 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
+ 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
+ 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
+ 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
+ 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
+ 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
+ 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
+ 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
+ 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
+ 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
+ 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
+ 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
+ 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
+ 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
+ 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
+ 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
+ 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
+ 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
+ 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
+ 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
+ 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
+ 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
+ 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
+ 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
+ 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
+ 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
+ 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
+ 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
+ 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
+ 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
+ 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
+ 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
+ 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
+ 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
+ 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
+ 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
+ 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
+ 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
+ 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
+ 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
+ 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
+ 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
+ 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
+ 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
+ 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
+ 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
+ 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
+ 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
+ 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
+ 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
+ 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
+ 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
+ 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
+ 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
+ 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
+ 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
+ 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6
+ ]
+ ]
+ );
+ my $keylen=length($key);
+ return undef if(($keylen < 8) || ($keylen > 56));
+ my @keybytes=split//,$key;
+ my $b;
+ for $b (@keybytes)
+ {
+ $b=unpack("C",$b);
+ }
+ my $j=0;
+ my $i=0;
+ my($l,$r)=(0,0);
+
+ # BEGIN PROCESS OF SETTING UP S & P-BOXES FOR THE KEY
+ for ($i=0;$i<18;$i++)
+ {
+ my $temp= ($keybytes[$j]<<24) +
+ ($keybytes[($j+1)%($keylen)]<<16) +
+ ($keybytes[($j+2)%($keylen)]<<8) +
+ ($keybytes[($j+3)%($keylen)]) ;
+ $h{"p_boxes"}->[$i]^=$temp;
+ $j=($j+4)%($keylen);
+ }
+ for ($i=0;$i<18;$i+=2)
+ {
+ ($l,$r)=crypt_block(\%h,$l,$r,0);
+ $h{"p_boxes"}->[$i]=$l;
+ $h{"p_boxes"}->[$i+1]=$r;
+ }
+ for $i (0..3)
+ {
+ for($j=0;$j<256;$j+=2)
+ {
+ ($l,$r)=crypt_block(\%h,$l,$r,0);
+ $h{"s_boxes"}->[$i]->[$j]=$l;
+ $h{"s_boxes"}->[$i]->[$j+1]=$r;
+ }
+ }
+ # S-BOXES AND P-BOXES NOW SET UP, NEED NO LONGER CARE
+ # ABOUT ACTUAL KEY
+ return bless \%h, $pack;
+ }
+
+sub F
+ {
+ my $S0=$_[0]->{"s_boxes"}->[0]->[($_[1]&0xFF000000)>>24];
+ my $S1=$_[0]->{"s_boxes"}->[1]->[($_[1]&0x00FF0000)>>16];
+ my $S2=$_[0]->{"s_boxes"}->[2]->[($_[1]&0x0000FF00)>>8];
+ my $S3=$_[0]->{"s_boxes"}->[3]->[($_[1]&0x000000FF)];
+ # this is horrid, but otherwise Perl overflows. :(
+ if($S0>$S1)
+ {
+ $S0=$S0-4294967296 if($S0>2147483647);
+ }
+ else
+ {
+ $S1=$S1-4294967296 if($S1>2147483647);
+ }
+ my $F=($S0+$S1);
+ $F+=4294967296 if($F<0);
+ $F^=$S2;
+ if($F>$S3)
+ {
+ $F=$F-4294967296 if($F>2147483647);
+ }
+ else
+ {
+ $S3=$S3-4294967296 if($S3>2147483647);
+ }
+ $F+=$S3;
+ $F&=0xFFFFFFFF;
+ return $F;
+ }
+
+sub ROUND
+ {
+ return($_[1],($_[2]^($_[0]->{"p_boxes"}->[$_[3]]))^F($_[0],$_[1]));
+ }
+
+sub crypt_block
+ {
+ my $self=shift;
+ my $l=shift;
+ my $r=shift;
+ my $d=shift;
+ if(!$d)
+ {
+ $l^=$self->{"p_boxes"}->[0];
+ my $i;
+ for $i (1..16)
+ {
+ ($r,$l)=ROUND($self,$l,$r,$i);
+ }
+ $r^=$self->{"p_boxes"}->[17];
+ }
+ else
+ {
+ $l^=$self->{"p_boxes"}->[17];
+ my $i;
+ for $i (1..16)
+ {
+ ($r,$l)=ROUND($self,$l,$r,17-$i);
+ }
+ $r^=$self->{"p_boxes"}->[0];
+ }
+ return($r,$l);
+ }
+
+=head2 B(I)
+
+The B() method uses the initialised blowfish object to encrypt 8 bytes
+of data of the string passed to it. It returns the encrypted block.
+
+=cut
+
+sub encrypt
+ {
+ my($self)=shift;
+ my($block)=shift;
+ my(@block)=split//,$block;
+ map{$_=unpack("C",$_)}@block;
+ # I'm not sure what endianness these are.... so hey.
+ my($l)=$block[3]|($block[2]<<8)|($block[1]<<16)|($block[0]<<24);
+ my($r)=$block[7]|($block[6]<<8)|($block[5]<<16)|($block[4]<<24);
+
+ ($l,$r)=crypt_block($self,$l,$r,0);
+
+ @block=(
+ $l>>24,($l>>16)&0xFF,($l>>8)&0xFF,$l&0xFF,
+ $r>>24,($r>>16)&0xFF,($r>>8)&0xFF,$r&0xFF
+ );
+ map{$_=pack("C",$_)}@block;
+ return join"",@block;
+ }
+
+=head2 B(I)
+
+The B() method uses the initialised blowfish object to decrypt 8 bytes
+of data of the string passed to it. It returns the decrypted block.
+
+=cut
+
+sub decrypt
+ {
+ my($self)=shift;
+ my($block)=shift;
+ my(@block)=split//,$block;
+ map{$_=unpack("C",$_)}@block;
+ my($l)=$block[3]|($block[2]<<8)|($block[1]<<16)|($block[0]<<24);
+ my($r)=$block[7]|($block[6]<<8)|($block[5]<<16)|($block[4]<<24);
+
+ ($l,$r)=crypt_block($self,$l,$r,1);
+
+ @block=(
+ $l>>24,($l>>16)&0xFF,($l>>8)&0xFF,$l&0xFF,
+ $r>>24,($r>>16)&0xFF,($r>>8)&0xFF,$r&0xFF
+ );
+ map{$_=pack("C",$_)}@block;
+ return join"",@block;
+ }
+
+sub blocksize
+ {
+ return 8;
+ }
+
+sub keysize
+ {
+ return 56;
+ }
+
+=head1 COMMENTS
+
+This is probably crap software, but hey, its for general use. I'm happy to patch
+it with other people's code... :)
+
+If you want speed, then see the Crypt::Blowfish module.
+
+=head1 AUTHOR
+
+Matthew Byng-Maddick >
+
+=head1 SEE ALSO
+
+http://www.counterpane.com/,L
+
+=cut
+
+1;
diff --git a/csf/Crypt/CBC.pm b/csf/Crypt/CBC.pm
new file mode 100644
index 0000000..39ff251
--- /dev/null
+++ b/csf/Crypt/CBC.pm
@@ -0,0 +1,1064 @@
+package Crypt::CBC;
+
+use Digest::MD5 'md5';
+use Carp;
+use strict;
+use bytes;
+use vars qw($VERSION);
+$VERSION = '2.33';
+
+use constant RANDOM_DEVICE => '/dev/urandom';
+
+sub new {
+ my $class = shift;
+
+ my $options = {};
+
+ # hashref arguments
+ if (ref $_[0] eq 'HASH') {
+ $options = shift;
+ }
+
+ # CGI style arguments
+ elsif ($_[0] =~ /^-[a-zA-Z_]{1,20}$/) {
+ my %tmp = @_;
+ while ( my($key,$value) = each %tmp) {
+ $key =~ s/^-//;
+ $options->{lc $key} = $value;
+ }
+ }
+
+ else {
+ $options->{key} = shift;
+ $options->{cipher} = shift;
+ }
+
+ my $cipher_object_provided = $options->{cipher} && ref $options->{cipher};
+
+ # "key" is a misnomer here, because it is actually usually a passphrase that is used
+ # to derive the true key
+ my $pass = $options->{key};
+
+ if ($cipher_object_provided) {
+ carp "Both a key and a pre-initialized Crypt::* object were passed. The key will be ignored"
+ if defined $pass;
+ $pass ||= '';
+ }
+ elsif (!defined $pass) {
+ croak "Please provide an encryption/decryption passphrase or key using -key"
+ }
+
+ # header mode
+ my %valid_modes = map {$_=>1} qw(none salt randomiv);
+ my $header_mode = $options->{header};
+ $header_mode ||= 'none' if exists $options->{prepend_iv} && !$options->{prepend_iv};
+ $header_mode ||= 'none' if exists $options->{add_header} && !$options->{add_header};
+ $header_mode ||= 'salt'; # default
+ croak "Invalid -header mode '$header_mode'" unless $valid_modes{$header_mode};
+
+ croak "The -salt argument is incompatible with a -header mode of $header_mode"
+ if exists $options->{salt} && $header_mode ne 'salt';
+
+ my $cipher = $options->{cipher};
+ $cipher = 'Crypt::DES' unless $cipher;
+ my $cipherclass = ref $cipher || $cipher;
+
+ unless (ref $cipher) { # munge the class name if no object passed
+ $cipher = $cipher=~/^Crypt::/ ? $cipher : "Crypt::$cipher";
+ $cipher->can('encrypt') or eval "require $cipher; 1" or croak "Couldn't load $cipher: $@";
+ # some crypt modules use the class Crypt::, and others don't
+ $cipher =~ s/^Crypt::// unless $cipher->can('keysize');
+ }
+
+ # allow user to override these values
+ my $ks = $options->{keysize};
+ my $bs = $options->{blocksize};
+
+ # otherwise we get the values from the cipher
+ $ks ||= eval {$cipher->keysize};
+ $bs ||= eval {$cipher->blocksize};
+
+ # Some of the cipher modules are busted and don't report the
+ # keysize (well, Crypt::Blowfish in any case). If we detect
+ # this, and find the blowfish module in use, then assume 56.
+ # Otherwise assume the least common denominator of 8.
+ $ks ||= $cipherclass =~ /blowfish/i ? 56 : 8;
+ $bs ||= $ks;
+
+ my $pcbc = $options->{'pcbc'};
+
+ # Default behavior is to treat -key as a passphrase.
+ # But if the literal_key option is true, then use key as is
+ croak "The options -literal_key and -regenerate_key are incompatible with each other"
+ if exists $options->{literal_key} && exists $options->{regenerate_key};
+ my $key;
+ $key = $pass if $options->{literal_key};
+ $key = $pass if exists $options->{regenerate_key} && !$options->{regenerate_key};
+
+ # Get the salt.
+ my $salt = $options->{salt};
+ my $random_salt = 1 unless defined $salt && $salt ne '1';
+ croak "Argument to -salt must be exactly 8 bytes long" if defined $salt && length $salt != 8 && $salt ne '1';
+
+ # note: iv will be autogenerated by start() if not specified in options
+ my $iv = $options->{iv};
+ my $random_iv = 1 unless defined $iv;
+ croak "Initialization vector must be exactly $bs bytes long when using the $cipherclass cipher" if defined $iv and length($iv) != $bs;
+
+ my $literal_key = $options->{literal_key} || (exists $options->{regenerate_key} && !$options->{regenerate_key});
+ my $legacy_hack = $options->{insecure_legacy_decrypt};
+ my $padding = $options->{padding} || 'standard';
+
+ if ($padding && ref($padding) eq 'CODE') {
+ # check to see that this code does its padding correctly
+ for my $i (1..$bs-1) {
+ my $rbs = length($padding->(" "x$i,$bs,'e'));
+ croak "padding method callback does not behave properly: expected $bs bytes back, got $rbs bytes back."
+ unless ($rbs == $bs);
+ }
+ } else {
+ $padding = $padding eq 'none' ? \&_no_padding
+ :$padding eq 'null' ? \&_null_padding
+ :$padding eq 'space' ? \&_space_padding
+ :$padding eq 'oneandzeroes' ? \&_oneandzeroes_padding
+ :$padding eq 'rijndael_compat'? \&_rijndael_compat
+ :$padding eq 'standard' ? \&_standard_padding
+ :croak "'$padding' padding not supported. See perldoc Crypt::CBC for instructions on creating your own.";
+ }
+
+ # CONSISTENCY CHECKS
+ # HEADER consistency
+ if ($header_mode eq 'salt') {
+ croak "Cannot use salt-based key generation if literal key is specified"
+ if $options->{literal_key};
+ croak "Cannot use salt-based IV generation if literal IV is specified"
+ if exists $options->{iv};
+ }
+ elsif ($header_mode eq 'randomiv') {
+ croak "Cannot encrypt using a non-8 byte blocksize cipher when using randomiv header mode"
+ unless $bs == 8 || $legacy_hack;
+ }
+ elsif ($header_mode eq 'none') {
+ croak "You must provide an initialization vector using -iv when using -header=>'none'"
+ unless exists $options->{iv};
+ }
+
+ # KEYSIZE consistency
+ if (defined $key && length($key) != $ks) {
+ croak "If specified by -literal_key, then the key length must be equal to the chosen cipher's key length of $ks bytes";
+ }
+
+ # IV consistency
+ if (defined $iv && length($iv) != $bs) {
+ croak "If specified by -iv, then the initialization vector length must be equal to the chosen cipher's blocksize of $bs bytes";
+ }
+
+
+ return bless {'cipher' => $cipher,
+ 'passphrase' => $pass,
+ 'key' => $key,
+ 'iv' => $iv,
+ 'salt' => $salt,
+ 'padding' => $padding,
+ 'blocksize' => $bs,
+ 'keysize' => $ks,
+ 'header_mode' => $header_mode,
+ 'legacy_hack' => $legacy_hack,
+ 'literal_key' => $literal_key,
+ 'pcbc' => $pcbc,
+ 'make_random_salt' => $random_salt,
+ 'make_random_iv' => $random_iv,
+ },$class;
+}
+
+sub encrypt (\$$) {
+ my ($self,$data) = @_;
+ $self->start('encrypting');
+ my $result = $self->crypt($data);
+ $result .= $self->finish;
+ $result;
+}
+
+sub decrypt (\$$){
+ my ($self,$data) = @_;
+ $self->start('decrypting');
+ my $result = $self->crypt($data);
+ $result .= $self->finish;
+ $result;
+}
+
+sub encrypt_hex (\$$) {
+ my ($self,$data) = @_;
+ return join('',unpack 'H*',$self->encrypt($data));
+}
+
+sub decrypt_hex (\$$) {
+ my ($self,$data) = @_;
+ return $self->decrypt(pack'H*',$data);
+}
+
+# call to start a series of encryption/decryption operations
+sub start (\$$) {
+ my $self = shift;
+ my $operation = shift;
+ croak "Specify ncryption or ecryption" unless $operation=~/^[ed]/i;
+
+ $self->{'buffer'} = '';
+ $self->{'decrypt'} = $operation=~/^d/i;
+}
+
+# call to encrypt/decrypt a bit of data
+sub crypt (\$$){
+ my $self = shift;
+ my $data = shift;
+
+ my $result;
+
+ croak "crypt() called without a preceding start()"
+ unless exists $self->{'buffer'};
+
+ my $d = $self->{'decrypt'};
+
+ unless ($self->{civ}) { # block cipher has not yet been initialized
+ $result = $self->_generate_iv_and_cipher_from_datastream(\$data) if $d;
+ $result = $self->_generate_iv_and_cipher_from_options() unless $d;
+ }
+
+ my $iv = $self->{'civ'};
+ $self->{'buffer'} .= $data;
+
+ my $bs = $self->{'blocksize'};
+
+ croak "When using no padding, plaintext size must be a multiple of $bs"
+ if $self->{'padding'} eq \&_no_padding
+ and length($data) % $bs;
+
+ croak "When using rijndael_compat padding, plaintext size must be a multiple of $bs"
+ if $self->{'padding'} eq \&_rijndael_compat
+ and length($data) % $bs;
+
+ return $result unless (length($self->{'buffer'}) >= $bs);
+
+ my @blocks = unpack("a$bs "x(int(length($self->{'buffer'})/$bs)) . "a*", $self->{'buffer'});
+ $self->{'buffer'} = '';
+
+ if ($d) { # when decrypting, always leave a free block at the end
+ $self->{'buffer'} = length($blocks[-1]) < $bs ? join '',splice(@blocks,-2) : pop(@blocks);
+ } else {
+ $self->{'buffer'} = pop @blocks if length($blocks[-1]) < $bs; # what's left over
+ }
+
+ foreach my $block (@blocks) {
+ if ($d) { # decrypting
+ $result .= $iv = $iv ^ $self->{'crypt'}->decrypt($block);
+ $iv = $block unless $self->{pcbc};
+ } else { # encrypting
+ $result .= $iv = $self->{'crypt'}->encrypt($iv ^ $block);
+ }
+ $iv = $iv ^ $block if $self->{pcbc};
+ }
+ $self->{'civ'} = $iv; # remember the iv
+ return $result;
+}
+
+# this is called at the end to flush whatever's left
+sub finish (\$) {
+ my $self = shift;
+ my $bs = $self->{'blocksize'};
+ my $block = defined $self->{'buffer'} ? $self->{'buffer'} : '';
+
+ $self->{civ} ||= '';
+
+ my $result;
+ if ($self->{'decrypt'}) { #decrypting
+ $block = length $block ? pack("a$bs",$block) : ''; # pad and truncate to block size
+
+ if (length($block)) {
+ $result = $self->{'civ'} ^ $self->{'crypt'}->decrypt($block);
+ $result = $self->{'padding'}->($result, $bs, 'd');
+ } else {
+ $result = '';
+ }
+
+ } else { # encrypting
+ $block = $self->{'padding'}->($block,$bs,'e') || '';
+ $result = length $block ? $self->{'crypt'}->encrypt($self->{'civ'} ^ $block) : '';
+ }
+ delete $self->{'civ'};
+ delete $self->{'buffer'};
+ return $result;
+}
+
+# this subroutine will generate the actual {en,de}cryption key, the iv
+# and the block cipher object. This is called when reading from a datastream
+# and so it uses previous values of salt or iv if they are encoded in datastream
+# header
+sub _generate_iv_and_cipher_from_datastream {
+ my $self = shift;
+ my $input_stream = shift;
+ my $bs = $self->blocksize;
+
+ # use our header mode to figure out what to do with the data stream
+ my $header_mode = $self->header_mode;
+
+ if ($header_mode eq 'none') {
+ croak "You must specify a $bs byte initialization vector by passing the -iv option to new() when using -header_mode=>'none'"
+ unless exists $self->{iv};
+ $self->{civ} = $self->{iv}; # current IV equals saved IV
+ $self->{key} ||= $self->_key_from_key($self->{passphrase});
+ }
+
+ elsif ($header_mode eq 'salt') {
+ my ($salt) = $$input_stream =~ /^Salted__(.{8})/s;
+ croak "Ciphertext does not begin with a valid header for 'salt' header mode" unless defined $salt;
+ $self->{salt} = $salt; # new salt
+ substr($$input_stream,0,16) = '';
+ my ($key,$iv) = $self->_salted_key_and_iv($self->{passphrase},$salt);
+ $self->{iv} = $self->{civ} = $iv;
+ $self->{key} = $key;
+ }
+
+ elsif ($header_mode eq 'randomiv') {
+ my ($iv) = $$input_stream =~ /^RandomIV(.{8})/s;
+ croak "Ciphertext does not begin with a valid header for 'randomiv' header mode" unless defined $iv;
+ croak "randomiv header mode cannot be used securely when decrypting with a >8 byte block cipher.\nUse the -insecure_legacy_decrypt flag if you are sure you want to do this" unless $self->blocksize == 8 || $self->legacy_hack;
+ $self->{iv} = $self->{civ} = $iv;
+ $self->{key} = $self->_key_from_key($self->{passphrase});
+ undef $self->{salt}; # paranoia
+ substr($$input_stream,0,16) = ''; # truncate
+ }
+
+ else {
+ croak "Invalid header mode '$header_mode'";
+ }
+
+ # we should have the key and iv now, or we are dead in the water
+ croak "Cipher stream did not contain IV or salt, and you did not specify these values in new()"
+ unless $self->{key} && $self->{civ};
+
+ # now we can generate the crypt object itself
+ $self->{crypt} = ref $self->{cipher} ? $self->{cipher}
+ : $self->{cipher}->new($self->{key})
+ or croak "Could not create $self->{cipher} object: $@";
+ return '';
+}
+
+sub _generate_iv_and_cipher_from_options {
+ my $self = shift;
+ my $blocksize = $self->blocksize;
+
+ my $result = '';
+
+ my $header_mode = $self->header_mode;
+ if ($header_mode eq 'none') {
+ croak "You must specify a $blocksize byte initialization vector by passing the -iv option to new() when using -header_mode=>'none'"
+ unless exists $self->{iv};
+ $self->{civ} = $self->{iv};
+ $self->{key} ||= $self->_key_from_key($self->{passphrase});
+ }
+
+ elsif ($header_mode eq 'salt') {
+ $self->{salt} = $self->_get_random_bytes(8) if $self->{make_random_salt};
+ defined (my $salt = $self->{salt}) or croak "No header_mode of 'salt' specified, but no salt value provided"; # shouldn't happen
+ length($salt) == 8 or croak "Salt must be exactly 8 bytes long";
+ my ($key,$iv) = $self->_salted_key_and_iv($self->{passphrase},$salt);
+ $self->{key} = $key;
+ $self->{civ} = $self->{iv} = $iv;
+ $result = "Salted__${salt}";
+ }
+
+ elsif ($header_mode eq 'randomiv') {
+ croak "randomiv header mode cannot be used when encrypting with a >8 byte block cipher. There is no option to allow this"
+ unless $blocksize == 8;
+ $self->{key} ||= $self->_key_from_key($self->{passphrase});
+ $self->{iv} = $self->_get_random_bytes(8) if $self->{make_random_iv};
+ length($self->{iv}) == 8 or croak "IV must be exactly 8 bytes long when used with header mode of 'randomiv'";
+ $self->{civ} = $self->{iv};
+ $result = "RandomIV$self->{iv}";
+ }
+
+ croak "key and/or iv are missing" unless defined $self->{key} && defined $self->{civ};
+
+ $self->_taintcheck($self->{key});
+ $self->{crypt} = ref $self->{cipher} ? $self->{cipher}
+ : $self->{cipher}->new($self->{key})
+ or croak "Could not create $self->{cipher} object: $@";
+ return $result;
+}
+
+sub _taintcheck {
+ my $self = shift;
+ my $key = shift;
+ return unless ${^TAINT};
+
+ my $has_scalar_util = eval "require Scalar::Util; 1";
+ my $tainted;
+
+
+ if ($has_scalar_util) {
+ $tainted = Scalar::Util::tainted($key);
+ } else {
+ local($@, $SIG{__DIE__}, $SIG{__WARN__});
+ local $^W = 0;
+ eval { kill 0 * $key };
+ $tainted = $@ =~ /^Insecure/;
+ }
+
+ croak "Taint checks are turned on and your key is tainted. Please untaint the key and try again"
+ if $tainted;
+}
+
+sub _key_from_key {
+ my $self = shift;
+ my $pass = shift;
+ my $ks = $self->{keysize};
+
+ return $pass if $self->{literal_key};
+
+ my $material = md5($pass);
+ while (length($material) < $ks) {
+ $material .= md5($material);
+ }
+ return substr($material,0,$ks);
+}
+
+sub _salted_key_and_iv {
+ my $self = shift;
+ my ($pass,$salt) = @_;
+
+ croak "Salt must be 8 bytes long" unless length $salt == 8;
+
+ my $key_len = $self->{keysize};
+ my $iv_len = $self->{blocksize};
+
+ my $desired_len = $key_len+$iv_len;
+
+ my $data = '';
+ my $d = '';
+
+ while (length $data < $desired_len) {
+ $d = md5($d . $pass . $salt);
+ $data .= $d;
+ }
+ return (substr($data,0,$key_len),substr($data,$key_len,$iv_len));
+}
+
+sub random_bytes {
+ my $self = shift;
+ my $bytes = shift or croak "usage: random_bytes(\$byte_length)";
+ $self->_get_random_bytes($bytes);
+}
+
+sub _get_random_bytes {
+ my $self = shift;
+ my $length = shift;
+ my $result;
+
+ if (-r RANDOM_DEVICE && open(F,RANDOM_DEVICE)) {
+ read(F,$result,$length);
+ close F;
+ } else {
+ $result = pack("C*",map {rand(256)} 1..$length);
+ }
+ # Clear taint and check length
+ $result =~ /^(.+)$/s;
+ length($1) == $length or croak "Invalid length while gathering $length random bytes";
+ return $1;
+}
+
+sub _standard_padding ($$$) {
+ my ($b,$bs,$decrypt) = @_;
+ $b = length $b ? $b : '';
+ if ($decrypt eq 'd') {
+ my $pad_length = unpack("C",substr($b,-1));
+
+ # sanity check for implementations that don't pad correctly
+ return $b unless $pad_length >= 0 && $pad_length <= $bs;
+ my @pad_chars = unpack("C*",substr($b,-$pad_length));
+ return $b if grep {$pad_length != $_} @pad_chars;
+
+ return substr($b,0,$bs-$pad_length);
+ }
+ my $pad = $bs - length($b) % $bs;
+ return $b . pack("C*",($pad)x$pad);
+}
+
+sub _space_padding ($$$) {
+ my ($b,$bs,$decrypt) = @_;
+ return unless length $b;
+ $b = length $b ? $b : '';
+ if ($decrypt eq 'd') {
+ $b=~ s/ *\z//s;
+ return $b;
+ }
+ return $b . pack("C*", (32) x ($bs - length($b) % $bs));
+}
+
+sub _no_padding ($$$) {
+ my ($b,$bs,$decrypt) = @_;
+ return $b;
+}
+
+sub _null_padding ($$$) {
+ my ($b,$bs,$decrypt) = @_;
+ return unless length $b;
+ $b = length $b ? $b : '';
+ if ($decrypt eq 'd') {
+ $b=~ s/\0*\z//s;
+ return $b;
+ }
+ return $b . pack("C*", (0) x ($bs - length($b) % $bs));
+}
+
+sub _oneandzeroes_padding ($$$) {
+ my ($b,$bs,$decrypt) = @_;
+ $b = length $b ? $b : '';
+ if ($decrypt eq 'd') {
+ $b=~ s/\x80\0*\z//s;
+ return $b;
+ }
+ return $b . pack("C*", 128, (0) x ($bs - length($b) % $bs - 1) );
+}
+
+sub _rijndael_compat ($$$) {
+ my ($b,$bs,$decrypt) = @_;
+ return unless length $b;
+ if ($decrypt eq 'd') {
+ $b=~ s/\x80\0*\z//s;
+ return $b;
+ }
+ return $b . pack("C*", 128, (0) x ($bs - length($b) % $bs - 1) );
+}
+
+sub get_initialization_vector (\$) {
+ my $self = shift;
+ $self->iv();
+}
+
+sub set_initialization_vector (\$$) {
+ my $self = shift;
+ my $iv = shift;
+ my $bs = $self->blocksize;
+ croak "Initialization vector must be $bs bytes in length" unless length($iv) == $bs;
+ $self->iv($iv);
+}
+
+sub salt {
+ my $self = shift;
+ my $d = $self->{salt};
+ $self->{salt} = shift if @_;
+ $d;
+}
+
+sub iv {
+ my $self = shift;
+ my $d = $self->{iv};
+ $self->{iv} = shift if @_;
+ $d;
+}
+
+sub key {
+ my $self = shift;
+ my $d = $self->{key};
+ $self->{key} = shift if @_;
+ $d;
+}
+
+sub passphrase {
+ my $self = shift;
+ my $d = $self->{passphrase};
+ if (@_) {
+ undef $self->{key};
+ undef $self->{iv};
+ $self->{passphrase} = shift;
+ }
+ $d;
+}
+
+sub cipher { shift->{cipher} }
+sub padding { shift->{padding} }
+sub keysize { shift->{keysize} }
+sub blocksize { shift->{blocksize} }
+sub pcbc { shift->{pcbc} }
+sub header_mode {shift->{header_mode} }
+sub legacy_hack { shift->{legacy_hack} }
+
+1;
+__END__
+
+=head1 NAME
+
+Crypt::CBC - Encrypt Data with Cipher Block Chaining Mode
+
+=head1 SYNOPSIS
+
+ use Crypt::CBC;
+ $cipher = Crypt::CBC->new( -key => 'my secret key',
+ -cipher => 'Blowfish'
+ );
+
+ $ciphertext = $cipher->encrypt("This data is hush hush");
+ $plaintext = $cipher->decrypt($ciphertext);
+
+ $cipher->start('encrypting');
+ open(F,"./BIG_FILE");
+ while (read(F,$buffer,1024)) {
+ print $cipher->crypt($buffer);
+ }
+ print $cipher->finish;
+
+ # do-it-yourself mode -- specify key, initialization vector yourself
+ $key = Crypt::CBC->random_bytes(8); # assuming a 8-byte block cipher
+ $iv = Crypt::CBC->random_bytes(8);
+ $cipher = Crypt::CBC->new(-literal_key => 1,
+ -key => $key,
+ -iv => $iv,
+ -header => 'none');
+
+ $ciphertext = $cipher->encrypt("This data is hush hush");
+ $plaintext = $cipher->decrypt($ciphertext);
+
+ # RANDOMIV-compatible mode
+ $cipher = Crypt::CBC->new(-key => 'Super Secret!'
+ -header => 'randomiv');
+
+
+=head1 DESCRIPTION
+
+This module is a Perl-only implementation of the cryptographic cipher
+block chaining mode (CBC). In combination with a block cipher such as
+DES or IDEA, you can encrypt and decrypt messages of arbitrarily long
+length. The encrypted messages are compatible with the encryption
+format used by the B package.
+
+To use this module, you will first create a Crypt::CBC cipher object
+with new(). At the time of cipher creation, you specify an encryption
+key to use and, optionally, a block encryption algorithm. You will
+then call the start() method to initialize the encryption or
+decryption process, crypt() to encrypt or decrypt one or more blocks
+of data, and lastly finish(), to pad and encrypt the final block. For
+your convenience, you can call the encrypt() and decrypt() methods to
+operate on a whole data value at once.
+
+=head2 new()
+
+ $cipher = Crypt::CBC->new( -key => 'my secret key',
+ -cipher => 'Blowfish',
+ );
+
+ # or (for compatibility with versions prior to 2.13)
+ $cipher = Crypt::CBC->new( {
+ key => 'my secret key',
+ cipher => 'Blowfish'
+ }
+ );
+
+
+ # or (for compatibility with versions prior to 2.0)
+ $cipher = new Crypt::CBC('my secret key' => 'Blowfish');
+
+The new() method creates a new Crypt::CBC object. It accepts a list of
+-argument => value pairs selected from the following list:
+
+ Argument Description
+ -------- -----------
+
+ -key The encryption/decryption key (required)
+
+ -cipher The cipher algorithm (defaults to Crypt::DES), or
+ a preexisting cipher object.
+
+ -salt Enables OpenSSL-compatibility. If equal to a value
+ of "1" then causes a random salt to be generated
+ and used to derive the encryption key and IV. Other
+ true values are taken to be the literal salt.
+
+ -iv The initialization vector (IV)
+
+ -header What type of header to prepend to ciphertext. One of
+ 'salt' -- use OpenSSL-compatible salted header
+ 'randomiv' -- Randomiv-compatible "RandomIV" header
+ 'none' -- prepend no header at all
+
+ -padding The padding method, one of "standard" (default),
+ "space", "oneandzeroes", "rijndael_compat",
+ "null", or "none" (default "standard").
+
+ -literal_key If true, the key provided by "key" is used directly
+ for encryption/decryption. Otherwise the actual
+ key used will be a hash of the provided key.
+ (default false)
+
+ -pcbc Whether to use the PCBC chaining algorithm rather than
+ the standard CBC algorithm (default false).
+
+ -keysize Force the cipher keysize to the indicated number of bytes.
+
+ -blocksize Force the cipher blocksize to the indicated number of bytes.
+
+ -insecure_legacy_decrypt
+ Allow decryption of data encrypted using the "RandomIV" header
+ produced by pre-2.17 versions of Crypt::CBC.
+
+ -add_header [deprecated; use -header instread]
+ Whether to add the salt and IV to the header of the output
+ cipher text.
+
+ -regenerate_key [deprecated; use literal_key instead]
+ Whether to use a hash of the provided key to generate
+ the actual encryption key (default true)
+
+ -prepend_iv [deprecated; use add_header instead]
+ Whether to prepend the IV to the beginning of the
+ encrypted stream (default true)
+
+Crypt::CBC requires three pieces of information to do its job. First
+it needs the name of the block cipher algorithm that will encrypt or
+decrypt the data in blocks of fixed length known as the cipher's
+"blocksize." Second, it needs an encryption/decryption key to pass to
+the block cipher. Third, it needs an initialization vector (IV) that
+will be used to propagate information from one encrypted block to the
+next. Both the key and the IV must be exactly the same length as the
+chosen cipher's blocksize.
+
+Crypt::CBC can derive the key and the IV from a passphrase that you
+provide, or can let you specify the true key and IV manually. In
+addition, you have the option of embedding enough information to
+regenerate the IV in a short header that is emitted at the start of
+the encrypted stream, or outputting a headerless encryption stream. In
+the first case, Crypt::CBC will be able to decrypt the stream given
+just the original key or passphrase. In the second case, you will have
+to provide the original IV as well as the key/passphrase.
+
+The B<-cipher> option specifies which block cipher algorithm to use to
+encode each section of the message. This argument is optional and
+will default to the quick-but-not-very-secure DES algorithm unless
+specified otherwise. You may use any compatible block encryption
+algorithm that you have installed. Currently, this includes
+Crypt::DES, Crypt::DES_EDE3, Crypt::IDEA, Crypt::Blowfish,
+Crypt::CAST5 and Crypt::Rijndael. You may refer to them using their
+full names ("Crypt::IDEA") or in abbreviated form ("IDEA").
+
+Instead of passing the name of a cipher class, you may pass an
+already-created block cipher object. This allows you to take advantage
+of cipher algorithms that have parameterized new() methods, such as
+Crypt::Eksblowfish:
+
+ my $eksblowfish = Crypt::Eksblowfish->new(8,$salt,$key);
+ my $cbc = Crypt::CBC->new(-cipher=>$eksblowfish);
+
+The B<-key> argument provides either a passphrase to use to generate
+the encryption key, or the literal value of the block cipher key. If
+used in passphrase mode (which is the default), B<-key> can be any
+number of characters; the actual key will be derived by passing the
+passphrase through a series of MD5 hash operations. To take full
+advantage of a given block cipher, the length of the passphrase should
+be at least equal to the cipher's blocksize. To skip this hashing
+operation and specify the key directly, pass a true value to the
+B<-literal_key> option. In this case, you should choose a key of
+length exactly equal to the cipher's key length. You should also
+specify the IV yourself and a -header mode of 'none'.
+
+If you pass an existing Crypt::* object to new(), then the -key
+argument is ignored and the module will generate a warning.
+
+The B<-header> argument specifies what type of header, if any, to
+prepend to the beginning of the encrypted data stream. The header
+allows Crypt::CBC to regenerate the original IV and correctly decrypt
+the data without your having to provide the same IV used to encrypt
+the data. Valid values for the B<-header> are:
+
+ "salt" -- Combine the passphrase with an 8-byte random value to
+ generate both the block cipher key and the IV from the
+ provided passphrase. The salt will be appended to the
+ beginning of the data stream allowing decryption to
+ regenerate both the key and IV given the correct passphrase.
+ This method is compatible with current versions of OpenSSL.
+
+ "randomiv" -- Generate the block cipher key from the passphrase, and
+ choose a random 8-byte value to use as the IV. The IV will
+ be prepended to the data stream. This method is compatible
+ with ciphertext produced by versions of the library prior to
+ 2.17, but is incompatible with block ciphers that have non
+ 8-byte block sizes, such as Rijndael. Crypt::CBC will exit
+ with a fatal error if you try to use this header mode with a
+ non 8-byte cipher.
+
+ "none" -- Do not generate a header. To decrypt a stream encrypted
+ in this way, you will have to provide the original IV
+ manually.
+
+B
+
+When using a "salt" header, you may specify your own value of the
+salt, by passing the desired 8-byte salt to the B<-salt>
+argument. Otherwise, the module will generate a random salt for
+you. Crypt::CBC will generate a fatal error if you specify a salt
+value that isn't exactly 8 bytes long. For backward compatibility
+reasons, passing a value of "1" will generate a random salt, the same
+as if no B<-salt> argument was provided.
+
+The B<-padding> argument controls how the last few bytes of the
+encrypted stream are dealt with when they not an exact multiple of the
+cipher block length. The default is "standard", the method specified
+in PKCS#5.
+
+The B<-pcbc> argument, if true, activates a modified chaining mode
+known as PCBC. It provides better error propagation characteristics
+than the default CBC encryption and is required for authenticating to
+Kerberos4 systems (see RFC 2222).
+
+The B<-keysize> and B<-blocksize> arguments can be used to force the
+cipher's keysize and/or blocksize. This is only currently useful for
+the Crypt::Blowfish module, which accepts a variable length
+keysize. If -keysize is not specified, then Crypt::CBC will use the
+maximum length Blowfish key size of 56 bytes (448 bits). The Openssl
+library defaults to 16 byte Blowfish key sizes, so for compatibility
+with Openssl you may wish to set -keysize=>16. There are currently no
+Crypt::* modules that have variable block sizes, but an option to
+change the block size is provided just in case.
+
+For compatibility with earlier versions of this module, you can
+provide new() with a hashref containing key/value pairs. The key names
+are the same as the arguments described earlier, but without the
+initial hyphen. You may also call new() with one or two positional
+arguments, in which case the first argument is taken to be the key and
+the second to be the optional block cipher algorithm.
+
+B Versions of this module prior to 2.17 were
+incorrectly using 8-byte IVs when generating the "randomiv" style of
+header, even when the chosen cipher's blocksize was greater than 8
+bytes. This primarily affects the Rijndael algorithm. Such encrypted
+data streams were B. From versions 2.17 onward, Crypt::CBC
+will refuse to encrypt or decrypt using the "randomiv" header and non-8
+byte block ciphers. To decrypt legacy data encrypted with earlier
+versions of the module, you can override the check using the
+B<-insecure_legacy_decrypt> option. It is not possible to override
+encryption. Please use the default "salt" header style, or no headers
+at all.
+
+=head2 start()
+
+ $cipher->start('encrypting');
+ $cipher->start('decrypting');
+
+The start() method prepares the cipher for a series of encryption or
+decryption steps, resetting the internal state of the cipher if
+necessary. You must provide a string indicating whether you wish to
+encrypt or decrypt. "E" or any word that begins with an "e" indicates
+encryption. "D" or any word that begins with a "d" indicates
+decryption.
+
+=head2 crypt()
+
+ $ciphertext = $cipher->crypt($plaintext);
+
+After calling start(), you should call crypt() as many times as
+necessary to encrypt the desired data.
+
+=head2 finish()
+
+ $ciphertext = $cipher->finish();
+
+The CBC algorithm must buffer data blocks internally until they are
+even multiples of the encryption algorithm's blocksize (typically 8
+bytes). After the last call to crypt() you should call finish().
+This flushes the internal buffer and returns any leftover ciphertext.
+
+In a typical application you will read the plaintext from a file or
+input stream and write the result to standard output in a loop that
+might look like this:
+
+ $cipher = new Crypt::CBC('hey jude!');
+ $cipher->start('encrypting');
+ print $cipher->crypt($_) while <>;
+ print $cipher->finish();
+
+=head2 encrypt()
+
+ $ciphertext = $cipher->encrypt($plaintext)
+
+This convenience function runs the entire sequence of start(), crypt()
+and finish() for you, processing the provided plaintext and returning
+the corresponding ciphertext.
+
+=head2 decrypt()
+
+ $plaintext = $cipher->decrypt($ciphertext)
+
+This convenience function runs the entire sequence of start(), crypt()
+and finish() for you, processing the provided ciphertext and returning
+the corresponding plaintext.
+
+=head2 encrypt_hex(), decrypt_hex()
+
+ $ciphertext = $cipher->encrypt_hex($plaintext)
+ $plaintext = $cipher->decrypt_hex($ciphertext)
+
+These are convenience functions that operate on ciphertext in a
+hexadecimal representation. B is exactly
+equivalent to B. These functions
+can be useful if, for example, you wish to place the encrypted in an
+email message.
+
+=head2 get_initialization_vector()
+
+ $iv = $cipher->get_initialization_vector()
+
+This function will return the IV used in encryption and or decryption.
+The IV is not guaranteed to be set when encrypting until start() is
+called, and when decrypting until crypt() is called the first
+time. Unless the IV was manually specified in the new() call, the IV
+will change with every complete encryption operation.
+
+=head2 set_initialization_vector()
+
+ $cipher->set_initialization_vector('76543210')
+
+This function sets the IV used in encryption and/or decryption. This
+function may be useful if the IV is not contained within the
+ciphertext string being decrypted, or if a particular IV is desired
+for encryption. Note that the IV must match the chosen cipher's
+blocksize bytes in length.
+
+=head2 iv()
+
+ $iv = $cipher->iv();
+ $cipher->iv($new_iv);
+
+As above, but using a single method call.
+
+=head2 key()
+
+ $key = $cipher->key();
+ $cipher->key($new_key);
+
+Get or set the block cipher key used for encryption/decryption. When
+encrypting, the key is not guaranteed to exist until start() is
+called, and when decrypting, the key is not guaranteed to exist until
+after the first call to crypt(). The key must match the length
+required by the underlying block cipher.
+
+When salted headers are used, the block cipher key will change after
+each complete sequence of encryption operations.
+
+=head2 salt()
+
+ $salt = $cipher->salt();
+ $cipher->salt($new_salt);
+
+Get or set the salt used for deriving the encryption key and IV when
+in OpenSSL compatibility mode.
+
+=head2 passphrase()
+
+ $passphrase = $cipher->passphrase();
+ $cipher->passphrase($new_passphrase);
+
+This gets or sets the value of the B passed to new() when
+B is false.
+
+=head2 $data = random_bytes($numbytes)
+
+Return $numbytes worth of random data. On systems that support the
+"/dev/urandom" device file, this data will be read from the
+device. Otherwise, it will be generated by repeated calls to the Perl
+rand() function.
+
+=head2 cipher(), padding(), keysize(), blocksize(), pcbc()
+
+These read-only methods return the identity of the chosen block cipher
+algorithm, padding method, key and block size of the chosen block
+cipher, and whether PCBC chaining is in effect.
+
+=head2 Padding methods
+
+Use the 'padding' option to change the padding method.
+
+When the last block of plaintext is shorter than the block size,
+it must be padded. Padding methods include: "standard" (i.e., PKCS#5),
+"oneandzeroes", "space", "rijndael_compat", "null", and "none".
+
+ standard: (default) Binary safe
+ pads with the number of bytes that should be truncated. So, if
+ blocksize is 8, then "0A0B0C" will be padded with "05", resulting
+ in "0A0B0C0505050505". If the final block is a full block of 8
+ bytes, then a whole block of "0808080808080808" is appended.
+
+ oneandzeroes: Binary safe
+ pads with "80" followed by as many "00" necessary to fill the
+ block. If the last block is a full block and blocksize is 8, a
+ block of "8000000000000000" will be appended.
+
+ rijndael_compat: Binary safe, with caveats
+ similar to oneandzeroes, except that no padding is performed if
+ the last block is a full block. This is provided for
+ compatibility with Crypt::Rijndael only and can only be used
+ with messages that are a multiple of the Rijndael blocksize
+ of 16 bytes.
+
+ null: text only
+ pads with as many "00" necessary to fill the block. If the last
+ block is a full block and blocksize is 8, a block of
+ "0000000000000000" will be appended.
+
+ space: text only
+ same as "null", but with "20".
+
+ none:
+ no padding added. Useful for special-purpose applications where
+ you wish to add custom padding to the message.
+
+Both the standard and oneandzeroes paddings are binary safe. The
+space and null paddings are recommended only for text data. Which
+type of padding you use depends on whether you wish to communicate
+with an external (non Crypt::CBC library). If this is the case, use
+whatever padding method is compatible.
+
+You can also pass in a custom padding function. To do this, create a
+function that takes the arguments:
+
+ $padded_block = function($block,$blocksize,$direction);
+
+where $block is the current block of data, $blocksize is the size to
+pad it to, $direction is "e" for encrypting and "d" for decrypting,
+and $padded_block is the result after padding or depadding.
+
+When encrypting, the function should always return a string of
+ length, and when decrypting, can expect the string coming
+in to always be that length. See _standard_padding(), _space_padding(),
+_null_padding(), or _oneandzeroes_padding() in the source for examples.
+
+Standard and oneandzeroes padding are recommended, as both space and
+null padding can potentially truncate more characters than they should.
+
+=head1 EXAMPLES
+
+Two examples, des.pl and idea.pl can be found in the eg/ subdirectory
+of the Crypt-CBC distribution. These implement command-line DES and
+IDEA encryption algorithms.
+
+=head1 LIMITATIONS
+
+The encryption and decryption process is about a tenth the speed of
+the equivalent SSLeay programs (compiled C). This could be improved
+by implementing this module in C. It may also be worthwhile to
+optimize the DES and IDEA block algorithms further.
+
+=head1 BUGS
+
+Please report them.
+
+=head1 AUTHOR
+
+Lincoln Stein, lstein@cshl.org
+
+This module is distributed under the ARTISTIC LICENSE using the same
+terms as Perl itself.
+
+=head1 SEE ALSO
+
+perl(1), Crypt::DES(3), Crypt::IDEA(3), rfc2898 (PKCS#5)
+
+=cut
diff --git a/csf/HTTP/Tiny.pm b/csf/HTTP/Tiny.pm
new file mode 100644
index 0000000..541befe
--- /dev/null
+++ b/csf/HTTP/Tiny.pm
@@ -0,0 +1,2425 @@
+# vim: ts=4 sts=4 sw=4 et:
+package HTTP::Tiny;
+use strict;
+use warnings;
+# ABSTRACT: A small, simple, correct HTTP/1.1 client
+
+our $VERSION = '0.070';
+
+sub _croak { require Carp; Carp::croak(@_) }
+
+#pod =method new
+#pod
+#pod $http = HTTP::Tiny->new( %attributes );
+#pod
+#pod This constructor returns a new HTTP::Tiny object. Valid attributes include:
+#pod
+#pod =for :list
+#pod * C — A user-agent string (defaults to 'HTTP-Tiny/$VERSION'). If
+#pod C — ends in a space character, the default user-agent string is
+#pod appended.
+#pod * C — An instance of L — or equivalent class
+#pod that supports the C and C methods
+#pod * C — A hashref of default headers to apply to requests
+#pod * C — The local IP address to bind to
+#pod * C — Whether to reuse the last connection (if for the same
+#pod scheme, host and port) (defaults to 1)
+#pod * C — Maximum number of redirects allowed (defaults to 5)
+#pod * C — Maximum response size in bytes (only when not using a data
+#pod callback). If defined, responses larger than this will return an
+#pod exception.
+#pod * C — URL of a proxy server to use for HTTP connections
+#pod (default is C<$ENV{http_proxy}> — if set)
+#pod * C — URL of a proxy server to use for HTTPS connections
+#pod (default is C<$ENV{https_proxy}> — if set)
+#pod * C — URL of a generic proxy server for both HTTP and HTTPS
+#pod connections (default is C<$ENV{all_proxy}> — if set)
+#pod * C — List of domain suffixes that should not be proxied. Must
+#pod be a comma-separated string or an array reference. (default is
+#pod C<$ENV{no_proxy}> —)
+#pod * C — Request timeout in seconds (default is 60) If a socket open,
+#pod read or write takes longer than the timeout, an exception is thrown.
+#pod * C — A boolean that indicates whether to validate the SSL
+#pod certificate of an C — connection (default is false)
+#pod * C — A hashref of C — options to pass through to
+#pod L
+#pod
+#pod Passing an explicit C for C, C or C will
+#pod prevent getting the corresponding proxies from the environment.
+#pod
+#pod Exceptions from C, C or other errors will result in a
+#pod pseudo-HTTP status code of 599 and a reason of "Internal Exception". The
+#pod content field in the response will contain the text of the exception.
+#pod
+#pod The C parameter enables a persistent connection, but only to a
+#pod single destination scheme, host and port. Also, if any connection-relevant
+#pod attributes are modified, or if the process ID or thread ID change, the
+#pod persistent connection will be dropped. If you want persistent connections
+#pod across multiple destinations, use multiple HTTP::Tiny objects.
+#pod
+#pod See L for more on the C and C attributes.
+#pod
+#pod =cut
+
+my @attributes;
+BEGIN {
+ @attributes = qw(
+ cookie_jar default_headers http_proxy https_proxy keep_alive
+ local_address max_redirect max_size proxy no_proxy
+ SSL_options verify_SSL
+ );
+ my %persist_ok = map {; $_ => 1 } qw(
+ cookie_jar default_headers max_redirect max_size
+ );
+ no strict 'refs';
+ no warnings 'uninitialized';
+ for my $accessor ( @attributes ) {
+ *{$accessor} = sub {
+ @_ > 1
+ ? do {
+ delete $_[0]->{handle} if !$persist_ok{$accessor} && $_[1] ne $_[0]->{$accessor};
+ $_[0]->{$accessor} = $_[1]
+ }
+ : $_[0]->{$accessor};
+ };
+ }
+}
+
+sub agent {
+ my($self, $agent) = @_;
+ if( @_ > 1 ){
+ $self->{agent} =
+ (defined $agent && $agent =~ / $/) ? $agent . $self->_agent : $agent;
+ }
+ return $self->{agent};
+}
+
+sub timeout {
+ my ($self, $timeout) = @_;
+ if ( @_ > 1 ) {
+ $self->{timeout} = $timeout;
+ if ($self->{handle}) {
+ $self->{handle}->timeout($timeout);
+ }
+ }
+ return $self->{timeout};
+}
+
+sub new {
+ my($class, %args) = @_;
+
+ my $self = {
+ max_redirect => 5,
+ timeout => defined $args{timeout} ? $args{timeout} : 60,
+ keep_alive => 1,
+ verify_SSL => $args{verify_SSL} || $args{verify_ssl} || 0, # no verification by default
+ no_proxy => $ENV{no_proxy},
+ };
+
+ bless $self, $class;
+
+ $class->_validate_cookie_jar( $args{cookie_jar} ) if $args{cookie_jar};
+
+ for my $key ( @attributes ) {
+ $self->{$key} = $args{$key} if exists $args{$key}
+ }
+
+ $self->agent( exists $args{agent} ? $args{agent} : $class->_agent );
+
+ $self->_set_proxies;
+
+ return $self;
+}
+
+sub _set_proxies {
+ my ($self) = @_;
+
+ # get proxies from %ENV only if not provided; explicit undef will disable
+ # getting proxies from the environment
+
+ # generic proxy
+ if (! exists $self->{proxy} ) {
+ $self->{proxy} = $ENV{all_proxy} || $ENV{ALL_PROXY};
+ }
+
+ if ( defined $self->{proxy} ) {
+ $self->_split_proxy( 'generic proxy' => $self->{proxy} ); # validate
+ }
+ else {
+ delete $self->{proxy};
+ }
+
+ # http proxy
+ if (! exists $self->{http_proxy} ) {
+ # under CGI, bypass HTTP_PROXY as request sets it from Proxy header
+ local $ENV{HTTP_PROXY} if $ENV{REQUEST_METHOD};
+ $self->{http_proxy} = $ENV{http_proxy} || $ENV{HTTP_PROXY} || $self->{proxy};
+ }
+
+ if ( defined $self->{http_proxy} ) {
+ $self->_split_proxy( http_proxy => $self->{http_proxy} ); # validate
+ $self->{_has_proxy}{http} = 1;
+ }
+ else {
+ delete $self->{http_proxy};
+ }
+
+ # https proxy
+ if (! exists $self->{https_proxy} ) {
+ $self->{https_proxy} = $ENV{https_proxy} || $ENV{HTTPS_PROXY} || $self->{proxy};
+ }
+
+ if ( $self->{https_proxy} ) {
+ $self->_split_proxy( https_proxy => $self->{https_proxy} ); # validate
+ $self->{_has_proxy}{https} = 1;
+ }
+ else {
+ delete $self->{https_proxy};
+ }
+
+ # Split no_proxy to array reference if not provided as such
+ unless ( ref $self->{no_proxy} eq 'ARRAY' ) {
+ $self->{no_proxy} =
+ (defined $self->{no_proxy}) ? [ split /\s*,\s*/, $self->{no_proxy} ] : [];
+ }
+
+ return;
+}
+
+#pod =method get|head|put|post|delete
+#pod
+#pod $response = $http->get($url);
+#pod $response = $http->get($url, \%options);
+#pod $response = $http->head($url);
+#pod
+#pod These methods are shorthand for calling C for the given method. The
+#pod URL must have unsafe characters escaped and international domain names encoded.
+#pod See C for valid options and a description of the response.
+#pod
+#pod The C field of the response will be true if the status code is 2XX.
+#pod
+#pod =cut
+
+for my $sub_name ( qw/get head put post delete/ ) {
+ my $req_method = uc $sub_name;
+ no strict 'refs';
+ eval <<"HERE"; ## no critic
+ sub $sub_name {
+ my (\$self, \$url, \$args) = \@_;
+ \@_ == 2 || (\@_ == 3 && ref \$args eq 'HASH')
+ or _croak(q/Usage: \$http->$sub_name(URL, [HASHREF])/ . "\n");
+ return \$self->request('$req_method', \$url, \$args || {});
+ }
+HERE
+}
+
+#pod =method post_form
+#pod
+#pod $response = $http->post_form($url, $form_data);
+#pod $response = $http->post_form($url, $form_data, \%options);
+#pod
+#pod This method executes a C request and sends the key/value pairs from a
+#pod form data hash or array reference to the given URL with a C of
+#pod C. If data is provided as an array
+#pod reference, the order is preserved; if provided as a hash reference, the terms
+#pod are sorted on key and value for consistency. See documentation for the
+#pod C method for details on the encoding.
+#pod
+#pod The URL must have unsafe characters escaped and international domain names
+#pod encoded. See C for valid options and a description of the response.
+#pod Any C header or content in the options hashref will be ignored.
+#pod
+#pod The C field of the response will be true if the status code is 2XX.
+#pod
+#pod =cut
+
+sub post_form {
+ my ($self, $url, $data, $args) = @_;
+ (@_ == 3 || @_ == 4 && ref $args eq 'HASH')
+ or _croak(q/Usage: $http->post_form(URL, DATAREF, [HASHREF])/ . "\n");
+
+ my $headers = {};
+ while ( my ($key, $value) = each %{$args->{headers} || {}} ) {
+ $headers->{lc $key} = $value;
+ }
+ delete $args->{headers};
+
+ return $self->request('POST', $url, {
+ %$args,
+ content => $self->www_form_urlencode($data),
+ headers => {
+ %$headers,
+ 'content-type' => 'application/x-www-form-urlencoded'
+ },
+ }
+ );
+}
+
+#pod =method mirror
+#pod
+#pod $response = $http->mirror($url, $file, \%options)
+#pod if ( $response->{success} ) {
+#pod print "$file is up to date\n";
+#pod }
+#pod
+#pod Executes a C request for the URL and saves the response body to the file
+#pod name provided. The URL must have unsafe characters escaped and international
+#pod domain names encoded. If the file already exists, the request will include an
+#pod C header with the modification timestamp of the file. You
+#pod may specify a different C header yourself in the C<<
+#pod $options->{headers} >> hash.
+#pod
+#pod The C field of the response will be true if the status code is 2XX
+#pod or if the status code is 304 (unmodified).
+#pod
+#pod If the file was modified and the server response includes a properly
+#pod formatted C header, the file modification time will
+#pod be updated accordingly.
+#pod
+#pod =cut
+
+sub mirror {
+ my ($self, $url, $file, $args) = @_;
+ @_ == 3 || (@_ == 4 && ref $args eq 'HASH')
+ or _croak(q/Usage: $http->mirror(URL, FILE, [HASHREF])/ . "\n");
+
+ if ( exists $args->{headers} ) {
+ my $headers = {};
+ while ( my ($key, $value) = each %{$args->{headers} || {}} ) {
+ $headers->{lc $key} = $value;
+ }
+ $args->{headers} = $headers;
+ }
+
+ if ( -e $file and my $mtime = (stat($file))[9] ) {
+ $args->{headers}{'if-modified-since'} ||= $self->_http_date($mtime);
+ }
+ my $tempfile = $file . int(rand(2**31));
+
+ require Fcntl;
+ sysopen my $fh, $tempfile, Fcntl::O_CREAT()|Fcntl::O_EXCL()|Fcntl::O_WRONLY()
+ or _croak(qq/Error: Could not create temporary file $tempfile for downloading: $!\n/);
+ binmode $fh;
+ $args->{data_callback} = sub { print {$fh} $_[0] };
+ my $response = $self->request('GET', $url, $args);
+ close $fh
+ or _croak(qq/Error: Caught error closing temporary file $tempfile: $!\n/);
+
+ if ( $response->{success} ) {
+ rename $tempfile, $file
+ or _croak(qq/Error replacing $file with $tempfile: $!\n/);
+ my $lm = $response->{headers}{'last-modified'};
+ if ( $lm and my $mtime = $self->_parse_http_date($lm) ) {
+ utime $mtime, $mtime, $file;
+ }
+ }
+ $response->{success} ||= $response->{status} eq '304';
+ unlink $tempfile;
+ return $response;
+}
+
+#pod =method request
+#pod
+#pod $response = $http->request($method, $url);
+#pod $response = $http->request($method, $url, \%options);
+#pod
+#pod Executes an HTTP request of the given method type ('GET', 'HEAD', 'POST',
+#pod 'PUT', etc.) on the given URL. The URL must have unsafe characters escaped and
+#pod international domain names encoded.
+#pod
+#pod If the URL includes a "user:password" stanza, they will be used for Basic-style
+#pod authorization headers. (Authorization headers will not be included in a
+#pod redirected request.) For example:
+#pod
+#pod $http->request('GET', 'http://Aladdin:open sesame@example.com/');
+#pod
+#pod If the "user:password" stanza contains reserved characters, they must
+#pod be percent-escaped:
+#pod
+#pod $http->request('GET', 'http://john%40example.com:password@example.com/');
+#pod
+#pod A hashref of options may be appended to modify the request.
+#pod
+#pod Valid options are:
+#pod
+#pod =for :list
+#pod * C —
+#pod A hashref containing headers to include with the request. If the value for
+#pod a header is an array reference, the header will be output multiple times with
+#pod each value in the array. These headers over-write any default headers.
+#pod * C —
+#pod A scalar to include as the body of the request OR a code reference
+#pod that will be called iteratively to produce the body of the request
+#pod * C —
+#pod A code reference that will be called if it exists to provide a hashref
+#pod of trailing headers (only used with chunked transfer-encoding)
+#pod * C —
+#pod A code reference that will be called for each chunks of the response
+#pod body received.
+#pod * C —
+#pod Override host resolution and force all connections to go only to a
+#pod specific peer address, regardless of the URL of the request. This will
+#pod include any redirections! This options should be used with extreme
+#pod caution (e.g. debugging or very special circumstances).
+#pod
+#pod The C header is generated from the URL in accordance with RFC 2616. It
+#pod is a fatal error to specify C in the C option. Other headers
+#pod may be ignored or overwritten if necessary for transport compliance.
+#pod
+#pod If the C option is a code reference, it will be called iteratively
+#pod to provide the content body of the request. It should return the empty
+#pod string or undef when the iterator is exhausted.
+#pod
+#pod If the C option is the empty string, no C or
+#pod C headers will be generated.
+#pod
+#pod If the C option is provided, it will be called iteratively until
+#pod the entire response body is received. The first argument will be a string
+#pod containing a chunk of the response body, the second argument will be the
+#pod in-progress response hash reference, as described below. (This allows
+#pod customizing the action of the callback based on the C or C
+#pod received prior to the content body.)
+#pod
+#pod The C method returns a hashref containing the response. The hashref
+#pod will have the following keys:
+#pod
+#pod =for :list
+#pod * C —
+#pod Boolean indicating whether the operation returned a 2XX status code
+#pod * C —
+#pod URL that provided the response. This is the URL of the request unless
+#pod there were redirections, in which case it is the last URL queried
+#pod in a redirection chain
+#pod * C —
+#pod The HTTP status code of the response
+#pod * C —
+#pod The response phrase returned by the server
+#pod * C —
+#pod The body of the response. If the response does not have any content
+#pod or if a data callback is provided to consume the response body,
+#pod this will be the empty string
+#pod * C —
+#pod A hashref of header fields. All header field names will be normalized
+#pod to be lower case. If a header is repeated, the value will be an arrayref;
+#pod it will otherwise be a scalar string containing the value
+#pod * C
+#pod If this field exists, it is an arrayref of response hash references from
+#pod redirects in the same order that redirections occurred. If it does
+#pod not exist, then no redirections occurred.
+#pod
+#pod On an exception during the execution of the request, the C field will
+#pod contain 599, and the C field will contain the text of the exception.
+#pod
+#pod =cut
+
+my %idempotent = map { $_ => 1 } qw/GET HEAD PUT DELETE OPTIONS TRACE/;
+
+sub request {
+ my ($self, $method, $url, $args) = @_;
+ @_ == 3 || (@_ == 4 && ref $args eq 'HASH')
+ or _croak(q/Usage: $http->request(METHOD, URL, [HASHREF])/ . "\n");
+ $args ||= {}; # we keep some state in this during _request
+
+ # RFC 2616 Section 8.1.4 mandates a single retry on broken socket
+ my $response;
+ for ( 0 .. 1 ) {
+ $response = eval { $self->_request($method, $url, $args) };
+ last unless $@ && $idempotent{$method}
+ && $@ =~ m{^(?:Socket closed|Unexpected end)};
+ }
+
+ if (my $e = $@) {
+ # maybe we got a response hash thrown from somewhere deep
+ if ( ref $e eq 'HASH' && exists $e->{status} ) {
+ $e->{redirects} = delete $args->{_redirects} if @{ $args->{_redirects} || []};
+ return $e;
+ }
+
+ # otherwise, stringify it
+ $e = "$e";
+ $response = {
+ url => $url,
+ success => q{},
+ status => 599,
+ reason => 'Internal Exception',
+ content => $e,
+ headers => {
+ 'content-type' => 'text/plain',
+ 'content-length' => length $e,
+ },
+ ( @{$args->{_redirects} || []} ? (redirects => delete $args->{_redirects}) : () ),
+ };
+ }
+ return $response;
+}
+
+#pod =method www_form_urlencode
+#pod
+#pod $params = $http->www_form_urlencode( $data );
+#pod $response = $http->get("http://example.com/query?$params");
+#pod
+#pod This method converts the key/value pairs from a data hash or array reference
+#pod into a C string. The keys and values from the data
+#pod reference will be UTF-8 encoded and escaped per RFC 3986. If a value is an
+#pod array reference, the key will be repeated with each of the values of the array
+#pod reference. If data is provided as a hash reference, the key/value pairs in the
+#pod resulting string will be sorted by key and value for consistent ordering.
+#pod
+#pod =cut
+
+sub www_form_urlencode {
+ my ($self, $data) = @_;
+ (@_ == 2 && ref $data)
+ or _croak(q/Usage: $http->www_form_urlencode(DATAREF)/ . "\n");
+ (ref $data eq 'HASH' || ref $data eq 'ARRAY')
+ or _croak("form data must be a hash or array reference\n");
+
+ my @params = ref $data eq 'HASH' ? %$data : @$data;
+ @params % 2 == 0
+ or _croak("form data reference must have an even number of terms\n");
+
+ my @terms;
+ while( @params ) {
+ my ($key, $value) = splice(@params, 0, 2);
+ if ( ref $value eq 'ARRAY' ) {
+ unshift @params, map { $key => $_ } @$value;
+ }
+ else {
+ push @terms, join("=", map { $self->_uri_escape($_) } $key, $value);
+ }
+ }
+
+ return join("&", (ref $data eq 'ARRAY') ? (@terms) : (sort @terms) );
+}
+
+#pod =method can_ssl
+#pod
+#pod $ok = HTTP::Tiny->can_ssl;
+#pod ($ok, $why) = HTTP::Tiny->can_ssl;
+#pod ($ok, $why) = $http->can_ssl;
+#pod
+#pod Indicates if SSL support is available. When called as a class object, it
+#pod checks for the correct version of L and L.
+#pod When called as an object methods, if C is true or if C
+#pod is set in C, it checks that a CA file is available.
+#pod
+#pod In scalar context, returns a boolean indicating if SSL is available.
+#pod In list context, returns the boolean and a (possibly multi-line) string of
+#pod errors indicating why SSL isn't available.
+#pod
+#pod =cut
+
+sub can_ssl {
+ my ($self) = @_;
+
+ my($ok, $reason) = (1, '');
+
+ # Need IO::Socket::SSL 1.42 for SSL_create_ctx_callback
+ local @INC = @INC;
+ pop @INC if $INC[-1] eq '.';
+ unless (eval {require IO::Socket::SSL; IO::Socket::SSL->VERSION(1.42)}) {
+ $ok = 0;
+ $reason .= qq/IO::Socket::SSL 1.42 must be installed for https support\n/;
+ }
+
+ # Need Net::SSLeay 1.49 for MODE_AUTO_RETRY
+ unless (eval {require Net::SSLeay; Net::SSLeay->VERSION(1.49)}) {
+ $ok = 0;
+ $reason .= qq/Net::SSLeay 1.49 must be installed for https support\n/;
+ }
+
+ # If an object, check that SSL config lets us get a CA if necessary
+ if ( ref($self) && ( $self->{verify_SSL} || $self->{SSL_options}{SSL_verify_mode} ) ) {
+ my $handle = HTTP::Tiny::Handle->new(
+ SSL_options => $self->{SSL_options},
+ verify_SSL => $self->{verify_SSL},
+ );
+ unless ( eval { $handle->_find_CA_file; 1 } ) {
+ $ok = 0;
+ $reason .= "$@";
+ }
+ }
+
+ wantarray ? ($ok, $reason) : $ok;
+}
+
+#pod =method connected
+#pod
+#pod $host = $http->connected;
+#pod ($host, $port) = $http->connected;
+#pod
+#pod Indicates if a connection to a peer is being kept alive, per the C
+#pod option.
+#pod
+#pod In scalar context, returns the peer host and port, joined with a colon, or
+#pod C (if no peer is connected).
+#pod In list context, returns the peer host and port or an empty list (if no peer
+#pod is connected).
+#pod
+#pod B: This method cannot reliably be used to discover whether the remote
+#pod host has closed its end of the socket.
+#pod
+#pod =cut
+
+sub connected {
+ my ($self) = @_;
+
+ # If a socket exists...
+ if ($self->{handle} && $self->{handle}{fh}) {
+ my $socket = $self->{handle}{fh};
+
+ # ...and is connected, return the peer host and port.
+ if ($socket->connected) {
+ return wantarray
+ ? ($socket->peerhost, $socket->peerport)
+ : join(':', $socket->peerhost, $socket->peerport);
+ }
+ }
+ return;
+}
+
+#--------------------------------------------------------------------------#
+# private methods
+#--------------------------------------------------------------------------#
+
+my %DefaultPort = (
+ http => 80,
+ https => 443,
+);
+
+sub _agent {
+ my $class = ref($_[0]) || $_[0];
+ (my $default_agent = $class) =~ s{::}{-}g;
+ return $default_agent . "/" . $class->VERSION;
+}
+
+sub _request {
+ my ($self, $method, $url, $args) = @_;
+
+ my ($scheme, $host, $port, $path_query, $auth) = $self->_split_url($url);
+
+ my $request = {
+ method => $method,
+ scheme => $scheme,
+ host => $host,
+ port => $port,
+ host_port => ($port == $DefaultPort{$scheme} ? $host : "$host:$port"),
+ uri => $path_query,
+ headers => {},
+ };
+
+ my $peer = $args->{peer} || $host;
+
+ # We remove the cached handle so it is not reused in the case of redirect.
+ # If all is well, it will be recached at the end of _request. We only
+ # reuse for the same scheme, host and port
+ my $handle = delete $self->{handle};
+ if ( $handle ) {
+ unless ( $handle->can_reuse( $scheme, $host, $port, $peer ) ) {
+ $handle->close;
+ undef $handle;
+ }
+ }
+ $handle ||= $self->_open_handle( $request, $scheme, $host, $port, $peer );
+
+ $self->_prepare_headers_and_cb($request, $args, $url, $auth);
+ $handle->write_request($request);
+
+ my $response;
+ do { $response = $handle->read_response_header }
+ until (substr($response->{status},0,1) ne '1');
+
+ $self->_update_cookie_jar( $url, $response ) if $self->{cookie_jar};
+ my @redir_args = $self->_maybe_redirect($request, $response, $args);
+
+ my $known_message_length;
+ if ($method eq 'HEAD' || $response->{status} =~ /^[23]04/) {
+ # response has no message body
+ $known_message_length = 1;
+ }
+ else {
+ # Ignore any data callbacks during redirection.
+ my $cb_args = @redir_args ? +{} : $args;
+ my $data_cb = $self->_prepare_data_cb($response, $cb_args);
+ $known_message_length = $handle->read_body($data_cb, $response);
+ }
+
+ if ( $self->{keep_alive}
+ && $known_message_length
+ && $response->{protocol} eq 'HTTP/1.1'
+ && ($response->{headers}{connection} || '') ne 'close'
+ ) {
+ $self->{handle} = $handle;
+ }
+ else {
+ $handle->close;
+ }
+
+ $response->{success} = substr( $response->{status}, 0, 1 ) eq '2';
+ $response->{url} = $url;
+
+ # Push the current response onto the stack of redirects if redirecting.
+ if (@redir_args) {
+ push @{$args->{_redirects}}, $response;
+ return $self->_request(@redir_args, $args);
+ }
+
+ # Copy the stack of redirects into the response before returning.
+ $response->{redirects} = delete $args->{_redirects}
+ if @{$args->{_redirects}};
+ return $response;
+}
+
+sub _open_handle {
+ my ($self, $request, $scheme, $host, $port, $peer) = @_;
+
+ my $handle = HTTP::Tiny::Handle->new(
+ timeout => $self->{timeout},
+ SSL_options => $self->{SSL_options},
+ verify_SSL => $self->{verify_SSL},
+ local_address => $self->{local_address},
+ keep_alive => $self->{keep_alive}
+ );
+
+ if ($self->{_has_proxy}{$scheme} && ! grep { $host =~ /\Q$_\E$/ } @{$self->{no_proxy}}) {
+ return $self->_proxy_connect( $request, $handle );
+ }
+ else {
+ return $handle->connect($scheme, $host, $port, $peer);
+ }
+}
+
+sub _proxy_connect {
+ my ($self, $request, $handle) = @_;
+
+ my @proxy_vars;
+ if ( $request->{scheme} eq 'https' ) {
+ _croak(qq{No https_proxy defined}) unless $self->{https_proxy};
+ @proxy_vars = $self->_split_proxy( https_proxy => $self->{https_proxy} );
+ if ( $proxy_vars[0] eq 'https' ) {
+ _croak(qq{Can't proxy https over https: $request->{uri} via $self->{https_proxy}});
+ }
+ }
+ else {
+ _croak(qq{No http_proxy defined}) unless $self->{http_proxy};
+ @proxy_vars = $self->_split_proxy( http_proxy => $self->{http_proxy} );
+ }
+
+ my ($p_scheme, $p_host, $p_port, $p_auth) = @proxy_vars;
+
+ if ( length $p_auth && ! defined $request->{headers}{'proxy-authorization'} ) {
+ $self->_add_basic_auth_header( $request, 'proxy-authorization' => $p_auth );
+ }
+
+ $handle->connect($p_scheme, $p_host, $p_port, $p_host);
+
+ if ($request->{scheme} eq 'https') {
+ $self->_create_proxy_tunnel( $request, $handle );
+ }
+ else {
+ # non-tunneled proxy requires absolute URI
+ $request->{uri} = "$request->{scheme}://$request->{host_port}$request->{uri}";
+ }
+
+ return $handle;
+}
+
+sub _split_proxy {
+ my ($self, $type, $proxy) = @_;
+
+ my ($scheme, $host, $port, $path_query, $auth) = eval { $self->_split_url($proxy) };
+
+ unless(
+ defined($scheme) && length($scheme) && length($host) && length($port)
+ && $path_query eq '/'
+ ) {
+ _croak(qq{$type URL must be in format http[s]://[auth@]:/\n});
+ }
+
+ return ($scheme, $host, $port, $auth);
+}
+
+sub _create_proxy_tunnel {
+ my ($self, $request, $handle) = @_;
+
+ $handle->_assert_ssl;
+
+ my $agent = exists($request->{headers}{'user-agent'})
+ ? $request->{headers}{'user-agent'} : $self->{agent};
+
+ my $connect_request = {
+ method => 'CONNECT',
+ uri => "$request->{host}:$request->{port}",
+ headers => {
+ host => "$request->{host}:$request->{port}",
+ 'user-agent' => $agent,
+ }
+ };
+
+ if ( $request->{headers}{'proxy-authorization'} ) {
+ $connect_request->{headers}{'proxy-authorization'} =
+ delete $request->{headers}{'proxy-authorization'};
+ }
+
+ $handle->write_request($connect_request);
+ my $response;
+ do { $response = $handle->read_response_header }
+ until (substr($response->{status},0,1) ne '1');
+
+ # if CONNECT failed, throw the response so it will be
+ # returned from the original request() method;
+ unless (substr($response->{status},0,1) eq '2') {
+ die $response;
+ }
+
+ # tunnel established, so start SSL handshake
+ $handle->start_ssl( $request->{host} );
+
+ return;
+}
+
+sub _prepare_headers_and_cb {
+ my ($self, $request, $args, $url, $auth) = @_;
+
+ for ($self->{default_headers}, $args->{headers}) {
+ next unless defined;
+ while (my ($k, $v) = each %$_) {
+ $request->{headers}{lc $k} = $v;
+ $request->{header_case}{lc $k} = $k;
+ }
+ }
+
+ if (exists $request->{headers}{'host'}) {
+ die(qq/The 'Host' header must not be provided as header option\n/);
+ }
+
+ $request->{headers}{'host'} = $request->{host_port};
+ $request->{headers}{'user-agent'} ||= $self->{agent};
+ $request->{headers}{'connection'} = "close"
+ unless $self->{keep_alive};
+
+ if ( defined $args->{content} ) {
+ if (ref $args->{content} eq 'CODE') {
+ $request->{headers}{'content-type'} ||= "application/octet-stream";
+ $request->{headers}{'transfer-encoding'} = 'chunked'
+ unless $request->{headers}{'content-length'}
+ || $request->{headers}{'transfer-encoding'};
+ $request->{cb} = $args->{content};
+ }
+ elsif ( length $args->{content} ) {
+ my $content = $args->{content};
+ if ( $] ge '5.008' ) {
+ utf8::downgrade($content, 1)
+ or die(qq/Wide character in request message body\n/);
+ }
+ $request->{headers}{'content-type'} ||= "application/octet-stream";
+ $request->{headers}{'content-length'} = length $content
+ unless $request->{headers}{'content-length'}
+ || $request->{headers}{'transfer-encoding'};
+ $request->{cb} = sub { substr $content, 0, length $content, '' };
+ }
+ $request->{trailer_cb} = $args->{trailer_callback}
+ if ref $args->{trailer_callback} eq 'CODE';
+ }
+
+ ### If we have a cookie jar, then maybe add relevant cookies
+ if ( $self->{cookie_jar} ) {
+ my $cookies = $self->cookie_jar->cookie_header( $url );
+ $request->{headers}{cookie} = $cookies if length $cookies;
+ }
+
+ # if we have Basic auth parameters, add them
+ if ( length $auth && ! defined $request->{headers}{authorization} ) {
+ $self->_add_basic_auth_header( $request, 'authorization' => $auth );
+ }
+
+ return;
+}
+
+sub _add_basic_auth_header {
+ my ($self, $request, $header, $auth) = @_;
+ require MIME::Base64;
+ $request->{headers}{$header} =
+ "Basic " . MIME::Base64::encode_base64($auth, "");
+ return;
+}
+
+sub _prepare_data_cb {
+ my ($self, $response, $args) = @_;
+ my $data_cb = $args->{data_callback};
+ $response->{content} = '';
+
+ if (!$data_cb || $response->{status} !~ /^2/) {
+ if (defined $self->{max_size}) {
+ $data_cb = sub {
+ $_[1]->{content} .= $_[0];
+ die(qq/Size of response body exceeds the maximum allowed of $self->{max_size}\n/)
+ if length $_[1]->{content} > $self->{max_size};
+ };
+ }
+ else {
+ $data_cb = sub { $_[1]->{content} .= $_[0] };
+ }
+ }
+ return $data_cb;
+}
+
+sub _update_cookie_jar {
+ my ($self, $url, $response) = @_;
+
+ my $cookies = $response->{headers}->{'set-cookie'};
+ return unless defined $cookies;
+
+ my @cookies = ref $cookies ? @$cookies : $cookies;
+
+ $self->cookie_jar->add( $url, $_ ) for @cookies;
+
+ return;
+}
+
+sub _validate_cookie_jar {
+ my ($class, $jar) = @_;
+
+ # duck typing
+ for my $method ( qw/add cookie_header/ ) {
+ _croak(qq/Cookie jar must provide the '$method' method\n/)
+ unless ref($jar) && ref($jar)->can($method);
+ }
+
+ return;
+}
+
+sub _maybe_redirect {
+ my ($self, $request, $response, $args) = @_;
+ my $headers = $response->{headers};
+ my ($status, $method) = ($response->{status}, $request->{method});
+ $args->{_redirects} ||= [];
+
+ if (($status eq '303' or ($status =~ /^30[1278]/ && $method =~ /^GET|HEAD$/))
+ and $headers->{location}
+ and @{$args->{_redirects}} < $self->{max_redirect}
+ ) {
+ my $location = ($headers->{location} =~ /^\//)
+ ? "$request->{scheme}://$request->{host_port}$headers->{location}"
+ : $headers->{location} ;
+ return (($status eq '303' ? 'GET' : $method), $location);
+ }
+ return;
+}
+
+sub _split_url {
+ my $url = pop;
+
+ # URI regex adapted from the URI module
+ my ($scheme, $host, $path_query) = $url =~ m<\A([^:/?#]+)://([^/?#]*)([^#]*)>
+ or die(qq/Cannot parse URL: '$url'\n/);
+
+ $scheme = lc $scheme;
+ $path_query = "/$path_query" unless $path_query =~ m<\A/>;
+
+ my $auth = '';
+ if ( (my $i = index $host, '@') != -1 ) {
+ # user:pass@host
+ $auth = substr $host, 0, $i, ''; # take up to the @ for auth
+ substr $host, 0, 1, ''; # knock the @ off the host
+
+ # userinfo might be percent escaped, so recover real auth info
+ $auth =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
+ }
+ my $port = $host =~ s/:(\d*)\z// && length $1 ? $1
+ : $scheme eq 'http' ? 80
+ : $scheme eq 'https' ? 443
+ : undef;
+
+ return ($scheme, (length $host ? lc $host : "localhost") , $port, $path_query, $auth);
+}
+
+# Date conversions adapted from HTTP::Date
+my $DoW = "Sun|Mon|Tue|Wed|Thu|Fri|Sat";
+my $MoY = "Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec";
+sub _http_date {
+ my ($sec, $min, $hour, $mday, $mon, $year, $wday) = gmtime($_[1]);
+ return sprintf("%s, %02d %s %04d %02d:%02d:%02d GMT",
+ substr($DoW,$wday*4,3),
+ $mday, substr($MoY,$mon*4,3), $year+1900,
+ $hour, $min, $sec
+ );
+}
+
+sub _parse_http_date {
+ my ($self, $str) = @_;
+ require Time::Local;
+ my @tl_parts;
+ if ($str =~ /^[SMTWF][a-z]+, +(\d{1,2}) ($MoY) +(\d\d\d\d) +(\d\d):(\d\d):(\d\d) +GMT$/) {
+ @tl_parts = ($6, $5, $4, $1, (index($MoY,$2)/4), $3);
+ }
+ elsif ($str =~ /^[SMTWF][a-z]+, +(\d\d)-($MoY)-(\d{2,4}) +(\d\d):(\d\d):(\d\d) +GMT$/ ) {
+ @tl_parts = ($6, $5, $4, $1, (index($MoY,$2)/4), $3);
+ }
+ elsif ($str =~ /^[SMTWF][a-z]+ +($MoY) +(\d{1,2}) +(\d\d):(\d\d):(\d\d) +(?:[^0-9]+ +)?(\d\d\d\d)$/ ) {
+ @tl_parts = ($5, $4, $3, $2, (index($MoY,$1)/4), $6);
+ }
+ return eval {
+ my $t = @tl_parts ? Time::Local::timegm(@tl_parts) : -1;
+ $t < 0 ? undef : $t;
+ };
+}
+
+# URI escaping adapted from URI::Escape
+# c.f. http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1
+# perl 5.6 ready UTF-8 encoding adapted from JSON::PP
+my %escapes = map { chr($_) => sprintf("%%%02X", $_) } 0..255;
+$escapes{' '}="+";
+my $unsafe_char = qr/[^A-Za-z0-9\-\._~]/;
+
+sub _uri_escape {
+ my ($self, $str) = @_;
+ if ( $] ge '5.008' ) {
+ utf8::encode($str);
+ }
+ else {
+ $str = pack("U*", unpack("C*", $str)) # UTF-8 encode a byte string
+ if ( length $str == do { use bytes; length $str } );
+ $str = pack("C*", unpack("C*", $str)); # clear UTF-8 flag
+ }
+ $str =~ s/($unsafe_char)/$escapes{$1}/ge;
+ return $str;
+}
+
+package
+ HTTP::Tiny::Handle; # hide from PAUSE/indexers
+use strict;
+use warnings;
+
+use Errno qw[EINTR EPIPE];
+use IO::Socket qw[SOCK_STREAM];
+use Socket qw[SOL_SOCKET SO_KEEPALIVE];
+
+# PERL_HTTP_TINY_IPV4_ONLY is a private environment variable to force old
+# behavior if someone is unable to boostrap CPAN from a new perl install; it is
+# not intended for general, per-client use and may be removed in the future
+my $SOCKET_CLASS =
+ $ENV{PERL_HTTP_TINY_IPV4_ONLY} ? 'IO::Socket::INET' :
+ eval { require IO::Socket::IP; IO::Socket::IP->VERSION(0.25) } ? 'IO::Socket::IP' :
+ 'IO::Socket::INET';
+
+sub BUFSIZE () { 32768 } ## no critic
+
+my $Printable = sub {
+ local $_ = shift;
+ s/\r/\\r/g;
+ s/\n/\\n/g;
+ s/\t/\\t/g;
+ s/([^\x20-\x7E])/sprintf('\\x%.2X', ord($1))/ge;
+ $_;
+};
+
+my $Token = qr/[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]/;
+my $Field_Content = qr/[[:print:]]+ (?: [\x20\x09]+ [[:print:]]+ )*/x;
+
+sub new {
+ my ($class, %args) = @_;
+ return bless {
+ rbuf => '',
+ timeout => 60,
+ max_line_size => 16384,
+ max_header_lines => 64,
+ verify_SSL => 0,
+ SSL_options => {},
+ %args
+ }, $class;
+}
+
+sub timeout {
+ my ($self, $timeout) = @_;
+ if ( @_ > 1 ) {
+ $self->{timeout} = $timeout;
+ if ( $self->{fh} && $self->{fh}->can('timeout') ) {
+ $self->{fh}->timeout($timeout);
+ }
+ }
+ return $self->{timeout};
+}
+
+sub connect {
+ @_ == 5 || die(q/Usage: $handle->connect(scheme, host, port, peer)/ . "\n");
+ my ($self, $scheme, $host, $port, $peer) = @_;
+
+ if ( $scheme eq 'https' ) {
+ $self->_assert_ssl;
+ }
+ elsif ( $scheme ne 'http' ) {
+ die(qq/Unsupported URL scheme '$scheme'\n/);
+ }
+ $self->{fh} = $SOCKET_CLASS->new(
+ PeerHost => $peer,
+ PeerPort => $port,
+ $self->{local_address} ?
+ ( LocalAddr => $self->{local_address} ) : (),
+ Proto => 'tcp',
+ Type => SOCK_STREAM,
+ Timeout => $self->{timeout},
+ ) or die(qq/Could not connect to '$host:$port': $@\n/);
+
+ binmode($self->{fh})
+ or die(qq/Could not binmode() socket: '$!'\n/);
+
+ if ( $self->{keep_alive} ) {
+ unless ( defined( $self->{fh}->setsockopt( SOL_SOCKET, SO_KEEPALIVE, 1 ) ) ) {
+ CORE::close($self->{fh});
+ die(qq/Could not set SO_KEEPALIVE on socket: '$!'\n/);
+ }
+ }
+
+ $self->start_ssl($host) if $scheme eq 'https';
+
+ $self->{scheme} = $scheme;
+ $self->{host} = $host;
+ $self->{peer} = $peer;
+ $self->{port} = $port;
+ $self->{pid} = $$;
+ $self->{tid} = _get_tid();
+
+ return $self;
+}
+
+sub start_ssl {
+ my ($self, $host) = @_;
+
+ # As this might be used via CONNECT after an SSL session
+ # to a proxy, we shut down any existing SSL before attempting
+ # the handshake
+ if ( ref($self->{fh}) eq 'IO::Socket::SSL' ) {
+ unless ( $self->{fh}->stop_SSL ) {
+ my $ssl_err = IO::Socket::SSL->errstr;
+ die(qq/Error halting prior SSL connection: $ssl_err/);
+ }
+ }
+
+ my $ssl_args = $self->_ssl_args($host);
+ IO::Socket::SSL->start_SSL(
+ $self->{fh},
+ %$ssl_args,
+ SSL_create_ctx_callback => sub {
+ my $ctx = shift;
+ Net::SSLeay::CTX_set_mode($ctx, Net::SSLeay::MODE_AUTO_RETRY());
+ },
+ );
+
+ unless ( ref($self->{fh}) eq 'IO::Socket::SSL' ) {
+ my $ssl_err = IO::Socket::SSL->errstr;
+ die(qq/SSL connection failed for $host: $ssl_err\n/);
+ }
+}
+
+sub close {
+ @_ == 1 || die(q/Usage: $handle->close()/ . "\n");
+ my ($self) = @_;
+ CORE::close($self->{fh})
+ or die(qq/Could not close socket: '$!'\n/);
+}
+
+sub write {
+ @_ == 2 || die(q/Usage: $handle->write(buf)/ . "\n");
+ my ($self, $buf) = @_;
+
+ if ( $] ge '5.008' ) {
+ utf8::downgrade($buf, 1)
+ or die(qq/Wide character in write()\n/);
+ }
+
+ my $len = length $buf;
+ my $off = 0;
+
+ local $SIG{PIPE} = 'IGNORE';
+
+ while () {
+ $self->can_write
+ or die(qq/Timed out while waiting for socket to become ready for writing\n/);
+ my $r = syswrite($self->{fh}, $buf, $len, $off);
+ if (defined $r) {
+ $len -= $r;
+ $off += $r;
+ last unless $len > 0;
+ }
+ elsif ($! == EPIPE) {
+ die(qq/Socket closed by remote server: $!\n/);
+ }
+ elsif ($! != EINTR) {
+ if ($self->{fh}->can('errstr')){
+ my $err = $self->{fh}->errstr();
+ die (qq/Could not write to SSL socket: '$err'\n /);
+ }
+ else {
+ die(qq/Could not write to socket: '$!'\n/);
+ }
+
+ }
+ }
+ return $off;
+}
+
+sub read {
+ @_ == 2 || @_ == 3 || die(q/Usage: $handle->read(len [, allow_partial])/ . "\n");
+ my ($self, $len, $allow_partial) = @_;
+
+ my $buf = '';
+ my $got = length $self->{rbuf};
+
+ if ($got) {
+ my $take = ($got < $len) ? $got : $len;
+ $buf = substr($self->{rbuf}, 0, $take, '');
+ $len -= $take;
+ }
+
+ while ($len > 0) {
+ $self->can_read
+ or die(q/Timed out while waiting for socket to become ready for reading/ . "\n");
+ my $r = sysread($self->{fh}, $buf, $len, length $buf);
+ if (defined $r) {
+ last unless $r;
+ $len -= $r;
+ }
+ elsif ($! != EINTR) {
+ if ($self->{fh}->can('errstr')){
+ my $err = $self->{fh}->errstr();
+ die (qq/Could not read from SSL socket: '$err'\n /);
+ }
+ else {
+ die(qq/Could not read from socket: '$!'\n/);
+ }
+ }
+ }
+ if ($len && !$allow_partial) {
+ die(qq/Unexpected end of stream\n/);
+ }
+ return $buf;
+}
+
+sub readline {
+ @_ == 1 || die(q/Usage: $handle->readline()/ . "\n");
+ my ($self) = @_;
+
+ while () {
+ if ($self->{rbuf} =~ s/\A ([^\x0D\x0A]* \x0D?\x0A)//x) {
+ return $1;
+ }
+ if (length $self->{rbuf} >= $self->{max_line_size}) {
+ die(qq/Line size exceeds the maximum allowed size of $self->{max_line_size}\n/);
+ }
+ $self->can_read
+ or die(qq/Timed out while waiting for socket to become ready for reading\n/);
+ my $r = sysread($self->{fh}, $self->{rbuf}, BUFSIZE, length $self->{rbuf});
+ if (defined $r) {
+ last unless $r;
+ }
+ elsif ($! != EINTR) {
+ if ($self->{fh}->can('errstr')){
+ my $err = $self->{fh}->errstr();
+ die (qq/Could not read from SSL socket: '$err'\n /);
+ }
+ else {
+ die(qq/Could not read from socket: '$!'\n/);
+ }
+ }
+ }
+ die(qq/Unexpected end of stream while looking for line\n/);
+}
+
+sub read_header_lines {
+ @_ == 1 || @_ == 2 || die(q/Usage: $handle->read_header_lines([headers])/ . "\n");
+ my ($self, $headers) = @_;
+ $headers ||= {};
+ my $lines = 0;
+ my $val;
+
+ while () {
+ my $line = $self->readline;
+
+ if (++$lines >= $self->{max_header_lines}) {
+ die(qq/Header lines exceeds maximum number allowed of $self->{max_header_lines}\n/);
+ }
+ elsif ($line =~ /\A ([^\x00-\x1F\x7F:]+) : [\x09\x20]* ([^\x0D\x0A]*)/x) {
+ my ($field_name) = lc $1;
+ if (exists $headers->{$field_name}) {
+ for ($headers->{$field_name}) {
+ $_ = [$_] unless ref $_ eq "ARRAY";
+ push @$_, $2;
+ $val = \$_->[-1];
+ }
+ }
+ else {
+ $val = \($headers->{$field_name} = $2);
+ }
+ }
+ elsif ($line =~ /\A [\x09\x20]+ ([^\x0D\x0A]*)/x) {
+ $val
+ or die(qq/Unexpected header continuation line\n/);
+ next unless length $1;
+ $$val .= ' ' if length $$val;
+ $$val .= $1;
+ }
+ elsif ($line =~ /\A \x0D?\x0A \z/x) {
+ last;
+ }
+ else {
+ die(q/Malformed header line: / . $Printable->($line) . "\n");
+ }
+ }
+ return $headers;
+}
+
+sub write_request {
+ @_ == 2 || die(q/Usage: $handle->write_request(request)/ . "\n");
+ my($self, $request) = @_;
+ $self->write_request_header(@{$request}{qw/method uri headers header_case/});
+ $self->write_body($request) if $request->{cb};
+ return;
+}
+
+# Standard request header names/case from HTTP/1.1 RFCs
+my @rfc_request_headers = qw(
+ Accept Accept-Charset Accept-Encoding Accept-Language Authorization
+ Cache-Control Connection Content-Length Expect From Host
+ If-Match If-Modified-Since If-None-Match If-Range If-Unmodified-Since
+ Max-Forwards Pragma Proxy-Authorization Range Referer TE Trailer
+ Transfer-Encoding Upgrade User-Agent Via
+);
+
+my @other_request_headers = qw(
+ Content-Encoding Content-MD5 Content-Type Cookie DNT Date Origin
+ X-XSS-Protection
+);
+
+my %HeaderCase = map { lc($_) => $_ } @rfc_request_headers, @other_request_headers;
+
+# to avoid multiple small writes and hence nagle, you can pass the method line or anything else to
+# combine writes.
+sub write_header_lines {
+ (@_ >= 2 && @_ <= 4 && ref $_[1] eq 'HASH') || die(q/Usage: $handle->write_header_lines(headers, [header_case, prefix])/ . "\n");
+ my($self, $headers, $header_case, $prefix_data) = @_;
+ $header_case ||= {};
+
+ my $buf = (defined $prefix_data ? $prefix_data : '');
+
+ # Per RFC, control fields should be listed first
+ my %seen;
+ for my $k ( qw/host cache-control expect max-forwards pragma range te/ ) {
+ next unless exists $headers->{$k};
+ $seen{$k}++;
+ my $field_name = $HeaderCase{$k};
+ my $v = $headers->{$k};
+ for (ref $v eq 'ARRAY' ? @$v : $v) {
+ $_ = '' unless defined $_;
+ $buf .= "$field_name: $_\x0D\x0A";
+ }
+ }
+
+ # Other headers sent in arbitrary order
+ while (my ($k, $v) = each %$headers) {
+ my $field_name = lc $k;
+ next if $seen{$field_name};
+ if (exists $HeaderCase{$field_name}) {
+ $field_name = $HeaderCase{$field_name};
+ }
+ else {
+ if (exists $header_case->{$field_name}) {
+ $field_name = $header_case->{$field_name};
+ }
+ else {
+ $field_name =~ s/\b(\w)/\u$1/g;
+ }
+ $field_name =~ /\A $Token+ \z/xo
+ or die(q/Invalid HTTP header field name: / . $Printable->($field_name) . "\n");
+ $HeaderCase{lc $field_name} = $field_name;
+ }
+ for (ref $v eq 'ARRAY' ? @$v : $v) {
+ # unwrap a field value if pre-wrapped by user
+ s/\x0D?\x0A\s+/ /g;
+ die(qq/Invalid HTTP header field value ($field_name): / . $Printable->($_). "\n")
+ unless $_ eq '' || /\A $Field_Content \z/xo;
+ $_ = '' unless defined $_;
+ $buf .= "$field_name: $_\x0D\x0A";
+ }
+ }
+ $buf .= "\x0D\x0A";
+ return $self->write($buf);
+}
+
+# return value indicates whether message length was defined; this is generally
+# true unless there was no content-length header and we just read until EOF.
+# Other message length errors are thrown as exceptions
+sub read_body {
+ @_ == 3 || die(q/Usage: $handle->read_body(callback, response)/ . "\n");
+ my ($self, $cb, $response) = @_;
+ my $te = $response->{headers}{'transfer-encoding'} || '';
+ my $chunked = grep { /chunked/i } ( ref $te eq 'ARRAY' ? @$te : $te ) ;
+ return $chunked
+ ? $self->read_chunked_body($cb, $response)
+ : $self->read_content_body($cb, $response);
+}
+
+sub write_body {
+ @_ == 2 || die(q/Usage: $handle->write_body(request)/ . "\n");
+ my ($self, $request) = @_;
+ if ($request->{headers}{'content-length'}) {
+ return $self->write_content_body($request);
+ }
+ else {
+ return $self->write_chunked_body($request);
+ }
+}
+
+sub read_content_body {
+ @_ == 3 || @_ == 4 || die(q/Usage: $handle->read_content_body(callback, response, [read_length])/ . "\n");
+ my ($self, $cb, $response, $content_length) = @_;
+ $content_length ||= $response->{headers}{'content-length'};
+
+ if ( defined $content_length ) {
+ my $len = $content_length;
+ while ($len > 0) {
+ my $read = ($len > BUFSIZE) ? BUFSIZE : $len;
+ $cb->($self->read($read, 0), $response);
+ $len -= $read;
+ }
+ return length($self->{rbuf}) == 0;
+ }
+
+ my $chunk;
+ $cb->($chunk, $response) while length( $chunk = $self->read(BUFSIZE, 1) );
+
+ return;
+}
+
+sub write_content_body {
+ @_ == 2 || die(q/Usage: $handle->write_content_body(request)/ . "\n");
+ my ($self, $request) = @_;
+
+ my ($len, $content_length) = (0, $request->{headers}{'content-length'});
+ while () {
+ my $data = $request->{cb}->();
+
+ defined $data && length $data
+ or last;
+
+ if ( $] ge '5.008' ) {
+ utf8::downgrade($data, 1)
+ or die(qq/Wide character in write_content()\n/);
+ }
+
+ $len += $self->write($data);
+ }
+
+ $len == $content_length
+ or die(qq/Content-Length mismatch (got: $len expected: $content_length)\n/);
+
+ return $len;
+}
+
+sub read_chunked_body {
+ @_ == 3 || die(q/Usage: $handle->read_chunked_body(callback, $response)/ . "\n");
+ my ($self, $cb, $response) = @_;
+
+ while () {
+ my $head = $self->readline;
+
+ $head =~ /\A ([A-Fa-f0-9]+)/x
+ or die(q/Malformed chunk head: / . $Printable->($head) . "\n");
+
+ my $len = hex($1)
+ or last;
+
+ $self->read_content_body($cb, $response, $len);
+
+ $self->read(2) eq "\x0D\x0A"
+ or die(qq/Malformed chunk: missing CRLF after chunk data\n/);
+ }
+ $self->read_header_lines($response->{headers});
+ return 1;
+}
+
+sub write_chunked_body {
+ @_ == 2 || die(q/Usage: $handle->write_chunked_body(request)/ . "\n");
+ my ($self, $request) = @_;
+
+ my $len = 0;
+ while () {
+ my $data = $request->{cb}->();
+
+ defined $data && length $data
+ or last;
+
+ if ( $] ge '5.008' ) {
+ utf8::downgrade($data, 1)
+ or die(qq/Wide character in write_chunked_body()\n/);
+ }
+
+ $len += length $data;
+
+ my $chunk = sprintf '%X', length $data;
+ $chunk .= "\x0D\x0A";
+ $chunk .= $data;
+ $chunk .= "\x0D\x0A";
+
+ $self->write($chunk);
+ }
+ $self->write("0\x0D\x0A");
+ if ( ref $request->{trailer_cb} eq 'CODE' ) {
+ $self->write_header_lines($request->{trailer_cb}->())
+ }
+ else {
+ $self->write("\x0D\x0A");
+ }
+ return $len;
+}
+
+sub read_response_header {
+ @_ == 1 || die(q/Usage: $handle->read_response_header()/ . "\n");
+ my ($self) = @_;
+
+ my $line = $self->readline;
+
+ $line =~ /\A (HTTP\/(0*\d+\.0*\d+)) [\x09\x20]+ ([0-9]{3}) [\x09\x20]+ ([^\x0D\x0A]*) \x0D?\x0A/x
+ or die(q/Malformed Status-Line: / . $Printable->($line). "\n");
+
+ my ($protocol, $version, $status, $reason) = ($1, $2, $3, $4);
+
+ die (qq/Unsupported HTTP protocol: $protocol\n/)
+ unless $version =~ /0*1\.0*[01]/;
+
+ return {
+ status => $status,
+ reason => $reason,
+ headers => $self->read_header_lines,
+ protocol => $protocol,
+ };
+}
+
+sub write_request_header {
+ @_ == 5 || die(q/Usage: $handle->write_request_header(method, request_uri, headers, header_case)/ . "\n");
+ my ($self, $method, $request_uri, $headers, $header_case) = @_;
+
+ return $self->write_header_lines($headers, $header_case, "$method $request_uri HTTP/1.1\x0D\x0A");
+}
+
+sub _do_timeout {
+ my ($self, $type, $timeout) = @_;
+ $timeout = $self->{timeout}
+ unless defined $timeout && $timeout >= 0;
+
+ my $fd = fileno $self->{fh};
+ defined $fd && $fd >= 0
+ or die(qq/select(2): 'Bad file descriptor'\n/);
+
+ my $initial = time;
+ my $pending = $timeout;
+ my $nfound;
+
+ vec(my $fdset = '', $fd, 1) = 1;
+
+ while () {
+ $nfound = ($type eq 'read')
+ ? select($fdset, undef, undef, $pending)
+ : select(undef, $fdset, undef, $pending) ;
+ if ($nfound == -1) {
+ $! == EINTR
+ or die(qq/select(2): '$!'\n/);
+ redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0;
+ $nfound = 0;
+ }
+ last;
+ }
+ $! = 0;
+ return $nfound;
+}
+
+sub can_read {
+ @_ == 1 || @_ == 2 || die(q/Usage: $handle->can_read([timeout])/ . "\n");
+ my $self = shift;
+ if ( ref($self->{fh}) eq 'IO::Socket::SSL' ) {
+ return 1 if $self->{fh}->pending;
+ }
+ return $self->_do_timeout('read', @_)
+}
+
+sub can_write {
+ @_ == 1 || @_ == 2 || die(q/Usage: $handle->can_write([timeout])/ . "\n");
+ my $self = shift;
+ return $self->_do_timeout('write', @_)
+}
+
+sub _assert_ssl {
+ my($ok, $reason) = HTTP::Tiny->can_ssl();
+ die $reason unless $ok;
+}
+
+sub can_reuse {
+ my ($self,$scheme,$host,$port,$peer) = @_;
+ return 0 if
+ $self->{pid} != $$
+ || $self->{tid} != _get_tid()
+ || length($self->{rbuf})
+ || $scheme ne $self->{scheme}
+ || $host ne $self->{host}
+ || $port ne $self->{port}
+ || $peer ne $self->{peer}
+ || eval { $self->can_read(0) }
+ || $@ ;
+ return 1;
+}
+
+# Try to find a CA bundle to validate the SSL cert,
+# prefer Mozilla::CA or fallback to a system file
+sub _find_CA_file {
+ my $self = shift();
+
+ my $ca_file =
+ defined( $self->{SSL_options}->{SSL_ca_file} )
+ ? $self->{SSL_options}->{SSL_ca_file}
+ : $ENV{SSL_CERT_FILE};
+
+ if ( defined $ca_file ) {
+ unless ( -r $ca_file ) {
+ die qq/SSL_ca_file '$ca_file' not found or not readable\n/;
+ }
+ return $ca_file;
+ }
+
+ local @INC = @INC;
+ pop @INC if $INC[-1] eq '.';
+ return Mozilla::CA::SSL_ca_file()
+ if eval { require Mozilla::CA; 1 };
+
+ # cert list copied from golang src/crypto/x509/root_unix.go
+ foreach my $ca_bundle (
+ "/etc/ssl/certs/ca-certificates.crt", # Debian/Ubuntu/Gentoo etc.
+ "/etc/pki/tls/certs/ca-bundle.crt", # Fedora/RHEL
+ "/etc/ssl/ca-bundle.pem", # OpenSUSE
+ "/etc/openssl/certs/ca-certificates.crt", # NetBSD
+ "/etc/ssl/cert.pem", # OpenBSD
+ "/usr/local/share/certs/ca-root-nss.crt", # FreeBSD/DragonFly
+ "/etc/pki/tls/cacert.pem", # OpenELEC
+ "/etc/certs/ca-certificates.crt", # Solaris 11.2+
+ ) {
+ return $ca_bundle if -e $ca_bundle;
+ }
+
+ die qq/Couldn't find a CA bundle with which to verify the SSL certificate.\n/
+ . qq/Try installing Mozilla::CA from CPAN\n/;
+}
+
+# for thread safety, we need to know thread id if threads are loaded
+sub _get_tid {
+ no warnings 'reserved'; # for 'threads'
+ return threads->can("tid") ? threads->tid : 0;
+}
+
+sub _ssl_args {
+ my ($self, $host) = @_;
+
+ my %ssl_args;
+
+ # This test reimplements IO::Socket::SSL::can_client_sni(), which wasn't
+ # added until IO::Socket::SSL 1.84
+ if ( Net::SSLeay::OPENSSL_VERSION_NUMBER() >= 0x01000000 ) {
+ $ssl_args{SSL_hostname} = $host, # Sane SNI support
+ }
+
+ if ($self->{verify_SSL}) {
+ $ssl_args{SSL_verifycn_scheme} = 'http'; # enable CN validation
+ $ssl_args{SSL_verifycn_name} = $host; # set validation hostname
+ $ssl_args{SSL_verify_mode} = 0x01; # enable cert validation
+ $ssl_args{SSL_ca_file} = $self->_find_CA_file;
+ }
+ else {
+ $ssl_args{SSL_verifycn_scheme} = 'none'; # disable CN validation
+ $ssl_args{SSL_verify_mode} = 0x00; # disable cert validation
+ }
+
+ # user options override settings from verify_SSL
+ for my $k ( keys %{$self->{SSL_options}} ) {
+ $ssl_args{$k} = $self->{SSL_options}{$k} if $k =~ m/^SSL_/;
+ }
+
+ return \%ssl_args;
+}
+
+1;
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+HTTP::Tiny - A small, simple, correct HTTP/1.1 client
+
+=head1 VERSION
+
+version 0.070
+
+=head1 SYNOPSIS
+
+ use HTTP::Tiny;
+
+ my $response = HTTP::Tiny->new->get('http://example.com/');
+
+ die "Failed!\n" unless $response->{success};
+
+ print "$response->{status} $response->{reason}\n";
+
+ while (my ($k, $v) = each %{$response->{headers}}) {
+ for (ref $v eq 'ARRAY' ? @$v : $v) {
+ print "$k: $_\n";
+ }
+ }
+
+ print $response->{content} if length $response->{content};
+
+=head1 DESCRIPTION
+
+This is a very simple HTTP/1.1 client, designed for doing simple
+requests without the overhead of a large framework like L.
+
+It is more correct and more complete than L. It supports
+proxies and redirection. It also correctly resumes after EINTR.
+
+If L 0.25 or later is installed, HTTP::Tiny will use it instead
+of L for transparent support for both IPv4 and IPv6.
+
+Cookie support requires L or an equivalent class.
+
+=head1 METHODS
+
+=head2 new
+
+ $http = HTTP::Tiny->new( %attributes );
+
+This constructor returns a new HTTP::Tiny object. Valid attributes include:
+
+=over 4
+
+=item *
+
+C — A user-agent string (defaults to 'HTTP-Tiny/$VERSION'). If C — ends in a space character, the default user-agent string is appended.
+
+=item *
+
+C — An instance of L — or equivalent class that supports the C and C methods
+
+=item *
+
+C — A hashref of default headers to apply to requests
+
+=item *
+
+C — The local IP address to bind to
+
+=item *
+
+C — Whether to reuse the last connection (if for the same scheme, host and port) (defaults to 1)
+
+=item *
+
+C — Maximum number of redirects allowed (defaults to 5)
+
+=item *
+
+C — Maximum response size in bytes (only when not using a data callback). If defined, responses larger than this will return an exception.
+
+=item *
+
+C — URL of a proxy server to use for HTTP connections (default is C<$ENV{http_proxy}> — if set)
+
+=item *
+
+C — URL of a proxy server to use for HTTPS connections (default is C<$ENV{https_proxy}> — if set)
+
+=item *
+
+C — URL of a generic proxy server for both HTTP and HTTPS connections (default is C<$ENV{all_proxy}> — if set)
+
+=item *
+
+C — List of domain suffixes that should not be proxied. Must be a comma-separated string or an array reference. (default is C<$ENV{no_proxy}> —)
+
+=item *
+
+C — Request timeout in seconds (default is 60) If a socket open, read or write takes longer than the timeout, an exception is thrown.
+
+=item *
+
+C — A boolean that indicates whether to validate the SSL certificate of an C — connection (default is false)
+
+=item *
+
+C — A hashref of C — options to pass through to L
+
+=back
+
+Passing an explicit C for C, C or C will
+prevent getting the corresponding proxies from the environment.
+
+Exceptions from C, C