diff --git a/install/dist/lib/fedora.lib.php b/install/dist/lib/fedora.lib.php
index 9ffc7d0ef4fea39a70ff8fde2a893f070c23044a..0cf2141f72ad6c4c478ab8c1fc4982386591ce75 100644
--- a/install/dist/lib/fedora.lib.php
+++ b/install/dist/lib/fedora.lib.php
@@ -121,12 +121,6 @@ class installer_dist extends installer_base {
 		$content = rfsel($conf['ispconfig_install_dir'].'/server/conf-custom/install/postfix-'.$filename.'.master', 'tpl/postfix-'.$filename.'.master');
 		wf($full_file_name, $content);
 
-		//* Changing mode and group of the new created config files.
-		caselog('chmod o= '.$config_dir.'/mysql-virtual_*.cf* &> /dev/null',
-			__FILE__, __LINE__, 'chmod on mysql-virtual_*.cf*', 'chmod on mysql-virtual_*.cf* failed');
-		caselog('chgrp '.$cf['group'].' '.$config_dir.'/mysql-virtual_*.cf* &> /dev/null',
-			__FILE__, __LINE__, 'chgrp on mysql-virtual_*.cf*', 'chgrp on mysql-virtual_*.cf* failed');
-
 		//* Creating virtual mail user and group
 		$command = 'groupadd -g '.$cf['vmail_groupid'].' '.$cf['vmail_groupname'];
 		if(!is_group($cf['vmail_groupname'])) caselog($command.' &> /dev/null', __FILE__, __LINE__, "EXECUTED: $command", "Failed to execute the command $command");
diff --git a/install/dist/lib/opensuse.lib.php b/install/dist/lib/opensuse.lib.php
index 15ab5b6e91c7dded34c8a5285d5a87424a4b474e..1401614c3fd2b4c1dc3a8b6ddf687c50638fc342 100644
--- a/install/dist/lib/opensuse.lib.php
+++ b/install/dist/lib/opensuse.lib.php
@@ -118,12 +118,6 @@ class installer_dist extends installer_base {
 		$content = rfsel($conf['ispconfig_install_dir'].'/server/conf-custom/install/postfix-'.$filename.'.master', 'tpl/postfix-'.$filename.'.master');
 		wf($full_file_name, $content);
 
-		//* Changing mode and group of the new created config files.
-		caselog('chmod o= '.$config_dir.'/mysql-virtual_*.cf* &> /dev/null',
-			__FILE__, __LINE__, 'chmod on mysql-virtual_*.cf*', 'chmod on mysql-virtual_*.cf* failed');
-		caselog('chgrp '.$cf['group'].' '.$config_dir.'/mysql-virtual_*.cf* &> /dev/null',
-			__FILE__, __LINE__, 'chgrp on mysql-virtual_*.cf*', 'chgrp on mysql-virtual_*.cf* failed');
-
 		if(!is_dir($cf['vmail_mailbox_base'])) mkdir($cf['vmail_mailbox_base']);
 
 		//* Creating virtual mail user and group
diff --git a/install/lib/installer_base.lib.php b/install/lib/installer_base.lib.php
index 2121eadeb13e6736d857acd6508a4b92fcb81286..10533dd66f9bed87df51632425cf988649aaa8d5 100644
--- a/install/lib/installer_base.lib.php
+++ b/install/lib/installer_base.lib.php
@@ -737,11 +737,16 @@ class installer_base {
 		global $conf;
 
 		$config_dir = $conf['postfix']['config_dir'].'/';
+		$postfix_group = $conf['postfix']['group'];
 		$full_file_name = $config_dir.$configfile;
+
 		//* Backup exiting file
 		if(is_file($full_file_name)) {
 			copy($full_file_name, $config_dir.$configfile.'~');
+			chmod($config_dir.$configfile.'~',0600);
 		}
+		
+		//* Replace variables in config file template
 		$content = rfsel($conf['ispconfig_install_dir'].'/server/conf-custom/install/'.$configfile.'.master', 'tpl/'.$configfile.'.master');
 		$content = str_replace('{mysql_server_ispconfig_user}', $conf['mysql']['ispconfig_user'], $content);
 		$content = str_replace('{mysql_server_ispconfig_password}', $conf['mysql']['ispconfig_password'], $content);
@@ -749,6 +754,13 @@ class installer_base {
 		$content = str_replace('{mysql_server_ip}', $conf['mysql']['ip'], $content);
 		$content = str_replace('{server_id}', $conf['server_id'], $content);
 		wf($full_file_name, $content);
+		
+		//* Changing mode and group of the new created config file
+		caselog('chmod u=rw,g=r,o= '.escapeshellarg($full_file_name).' &> /dev/null',
+			__FILE__, __LINE__, 'chmod on '.$full_file_name, 'chmod on '.$full_file_name.' failed');
+		caselog('chgrp '.escapeshellarg($postfix_group).' '.escapeshellarg($full_file_name).' &> /dev/null',
+			__FILE__, __LINE__, 'chgrp on '.$full_file_name, 'chgrp on '.$full_file_name.' failed');
+		
 	}
 
 	public function configure_jailkit() {
@@ -1028,12 +1040,6 @@ class installer_base {
 		}
 		wf($full_file_name, $content);
 
-		//* Changing mode and group of the new created config files.
-		caselog('chmod u=rw,g=r,o= '.$config_dir.'/mysql-virtual_*.cf* &> /dev/null',
-			__FILE__, __LINE__, 'chmod on mysql-virtual_*.cf*', 'chmod on mysql-virtual_*.cf* failed');
-		caselog('chgrp '.$cf['group'].' '.$config_dir.'/mysql-virtual_*.cf* &> /dev/null',
-			__FILE__, __LINE__, 'chgrp on mysql-virtual_*.cf*', 'chgrp on mysql-virtual_*.cf* failed');
-
 		//* Creating virtual mail user and group
 		$command = 'groupadd -g '.$cf['vmail_groupid'].' '.$cf['vmail_groupname'];
 		if(!is_group($cf['vmail_groupname'])) caselog($command.' &> /dev/null', __FILE__, __LINE__, "EXECUTED: $command", "Failed to execute the command $command");
diff --git a/interface/web/admin/directive_snippets_edit.php b/interface/web/admin/directive_snippets_edit.php
index af700213a47dfca1cce592c4f7d898a0a98c572a..e22a7cf42a2d7903121f6c87873c19a06f93a71f 100644
--- a/interface/web/admin/directive_snippets_edit.php
+++ b/interface/web/admin/directive_snippets_edit.php
@@ -77,9 +77,6 @@ class page_action extends tform_actions {
 		}
 
 		$app->tpl->setVar("is_master", $is_master);
-
-		if($this->dataRecord['managed_snippet_id'] > 0) {
-		}
 		
 		parent::onShowEnd();
 	}
diff --git a/interface/web/dashboard/dashlets/mailquota.php b/interface/web/dashboard/dashlets/mailquota.php
index 4629d6a4630c23bea7fe6d71a3846ca76667da6d..a9434e58eaac846087c77738d9d131ebe4d51abe 100644
--- a/interface/web/dashboard/dashlets/mailquota.php
+++ b/interface/web/dashboard/dashlets/mailquota.php
@@ -21,13 +21,17 @@ class dashlet_mailquota {
 
 		$has_mailquota = false;
 		if(is_array($emails) && !empty($emails)){
+			foreach($emails as &$email) {
+				$email['email'] = $app->functions->idn_decode($email['email']);
+			}
+			unset($email);
 			// email username is quoted in quota.lib already, so no htmlentities here to prevent double encoding
 			//$emails = $app->functions->htmlentities($emails);
 			$tpl->setloop('mailquota', $emails);
 			$has_mailquota = isset($emails[0]['used']);
 		}
 		$tpl->setVar('has_mailquota', $has_mailquota);
-		
+
 		return $tpl->grab();
 	}
 
diff --git a/interface/web/dashboard/dashlets/quota.php b/interface/web/dashboard/dashlets/quota.php
index dfb82d5c242bbe6ad8699e97db26855e1d378829..3fd5adbb85c5843bd51646304cb071f54f1eda33 100644
--- a/interface/web/dashboard/dashlets/quota.php
+++ b/interface/web/dashboard/dashlets/quota.php
@@ -7,9 +7,9 @@ class dashlet_quota {
 
 		//* Loading Template
 		$app->uses('tpl,quota_lib');
-               if (!$app->auth->verify_module_permissions('sites')) {
-                       return;
-               }
+		if (!$app->auth->verify_module_permissions('sites')) {
+				return;
+		}
 
 		$tpl = new tpl;
 		$tpl->newTemplate("dashlets/templates/quota.htm");
@@ -24,6 +24,11 @@ class dashlet_quota {
 
 		$has_quota = false;
 		if(is_array($sites) && !empty($sites)){
+			foreach($sites as &$site) {
+				$site['domain'] = $app->functions->idn_decode($site['domain']);
+			}
+			unset($site);
+
 			$sites = $app->functions->htmlentities($sites);
 			$tpl->setloop('quota', $sites);
 			$has_quota = isset($sites[0]['used']);
diff --git a/interface/web/sites/form/web_vhost_domain.tform.php b/interface/web/sites/form/web_vhost_domain.tform.php
index 6031b7ea943a91a7a951f824defcaf067e87aa21..eecb634c989be6e610e56784a9cb0b1afa4b7178 100644
--- a/interface/web/sites/form/web_vhost_domain.tform.php
+++ b/interface/web/sites/form/web_vhost_domain.tform.php
@@ -629,7 +629,7 @@ $form["tabs"]['stats'] = array (
 			'datatype' => 'VARCHAR',
 			'formtype' => 'SELECT',
 			'default' => 'awstats',
-			'value'  => array('webalizer' => 'Webalizer', 'awstats' => 'AWStats', '' => 'None')
+			'value'  => array('awstats' => 'AWStats', 'goaccess' => 'GoAccess', 'webalizer' => 'Webalizer','' => 'None')
 		),
 		//#################################
 		// END Datatable fields
diff --git a/interface/web/tools/form/user_settings.tform.php b/interface/web/tools/form/user_settings.tform.php
index a696d7533944348113cf550ab9265b7fb31a8eda..f063634b0ccf96ed6222acb33274c81b7dbd9ea2 100644
--- a/interface/web/tools/form/user_settings.tform.php
+++ b/interface/web/tools/form/user_settings.tform.php
@@ -68,7 +68,7 @@ $form['db_table']  = 'sys_user';
 $form['db_table_idx'] = 'userid';
 $form["db_history"]  = "no";
 $form['tab_default'] = 'users';
-$form['list_default'] = 'index.php';
+$form['list_default'] = 'user_settings.php';
 $form['auth']   = 'no';
 
 //* 0 = id of the user, > 0 id must match with id of current user
diff --git a/server/conf/awstats_index.php.master b/server/conf/awstats_index.php.master
index f7222c968721c87da286dc0ec937e25346bd58cc..b3e694ebbf3441038b45508f8e1e1624c3a0f481 100644
--- a/server/conf/awstats_index.php.master
+++ b/server/conf/awstats_index.php.master
@@ -45,10 +45,21 @@ arsort($awprev);
 $options = "";
 foreach ($awprev as $key => $value)
 {
+
+	if(file_exists($value.'/awsindex.html') && file_exists($value.'/goaindex.html')) {
+		$awstatsindex = 'awsindex.html';
+	} elseif(file_exists($value.'/awsindex.html') && !file_exists($value.'/goaindex.html')) {
+                $awstatsindex = 'awsindex.html';
+	} else {
+		$awstatsindex = 'goaindex.html';
+	}
+
 	if($key == $current) $options .= "<option selected=\"selected\" value=\"{$awstatsindex}\">{$value}</option>\n";
 	else $options .= "<option value=\"{$value}/{$awstatsindex}\">{$value}</option>\n";
 }
 
+$awstatsindex = 'awsindex.html';
+
 $html = "<!DOCTYPE html>\n<html>\n<head>\n<title>Stats</title>\n";
 $html .= "<style>\nhtml,body {margin:0px;padding:0px;width:100%;height:100%;background-color: #ccc;}\n";
 $html .= "#header\n{\nwidth:100%;margin:0px auto;\nheight:20px;\nposition:fixed;\npadding:4px;\ntext-align:center;\n}\n";
@@ -60,4 +71,4 @@ $html .= $options;
 $html .= "</select>\n</div>\n<iframe src=\"{$awstatsindex}\" id=\"content\"></iframe>\n";
 $html .= "</body></html>";
 echo $html;
-?>
\ No newline at end of file
+?>
diff --git a/server/conf/goaccess_index.php.master b/server/conf/goaccess_index.php.master
new file mode 100644
index 0000000000000000000000000000000000000000..d0a8bf3c84a39a3e1cab901fef8f64f674a37315
--- /dev/null
+++ b/server/conf/goaccess_index.php.master
@@ -0,0 +1,73 @@
+<?php
+$yearmonth_text = "Jump to previous stats: ";
+$script = "<script>function load_content(url){var iframe = document.getElementById(\"content\");iframe.src = url;}</script>\n";
+
+if ($handle = opendir('.'))
+{
+        while(false !== ($file = readdir($handle)))
+        {
+                if (substr($file,0,1) != "." && is_dir($file))
+                {
+                        $orderkey = substr($file,0,4).substr($file,5,2);
+                        if (substr($file,5,2) < 10 )
+                        {
+                                $orderkey = substr($file,0,4)."0".substr($file,5,2);
+                        }
+                        $goaprev[$orderkey] = $file;
+                }
+        }
+
+        $month = date("n");
+        $year = date("Y");
+		
+        if (date("d") == 1)
+        {
+                $month = date("m")-1;
+                if (date("m") == 1)
+                {
+                        $year = date("Y")-1;
+                        $month = "12";
+                }
+        }
+
+        $current = $year.$month;
+		if ( $month < 10 ) {
+			$current = $year."0".$month;
+		}
+		$goaprev[$current] = $year."-".$month;
+
+		closedir($handle);
+}
+
+arsort($goaprev);
+
+$options = "";
+foreach ($goaprev as $key => $value)
+{
+
+	if(file_exists($value.'/awsindex.html') && file_exists($value.'/goaindex.html')) {
+		$goaccessindex = 'goaindex.html';
+	} elseif(file_exists($value.'/awsindex.html') && !file_exists($value.'/goaindex.html')) {
+                $goaccessindex = 'awsindex.html';
+	} else {
+		$goaccessindex = 'goaindex.html';
+	}
+	
+	if($key == $current) $options .= "<option selected=\"selected\" value=\"{$goaccessindex}\">{$value}</option>\n";
+	else $options .= "<option value=\"{$value}/{$goaccessindex}\">{$value}</option>\n";
+
+}
+$goaccessindex = 'goaindex.html';
+
+$html = "<!DOCTYPE html>\n<html>\n<head>\n<title>Stats</title>\n";
+$html .= "<style>\nhtml,body {margin:0px;padding:0px;width:100%;height:100%;background-color: #ccc;}\n";
+$html .= "#header\n{\nwidth:100%;margin:0px auto;\nheight:20px;\nposition:fixed;\npadding:4px;\ntext-align:center;\n}\n";
+$html .= "iframe {width:100%;height:95%;margin:0px;margin-top:40px;border:0px;padding:0px;}\n</style>\n</head>\n<body>\n";
+$html .= $script;
+$html .= "<div id=\"header\">{$yearmonth_text}\n";
+$html .= "<select name=\"goadate\" onchange=\"load_content(this.value)\">\n";
+$html .= $options;
+$html .= "</select>\n</div>\n<iframe src=\"{$goaccessindex}\" id=\"content\"></iframe>\n";
+$html .= "</body></html>";
+echo $html;
+?>
diff --git a/server/lib/classes/cron.d/150-goaccess.inc.php b/server/lib/classes/cron.d/150-goaccess.inc.php
new file mode 100644
index 0000000000000000000000000000000000000000..8c9d7644420490272e36578cc933f8f883073fa2
--- /dev/null
+++ b/server/lib/classes/cron.d/150-goaccess.inc.php
@@ -0,0 +1,254 @@
+<?php
+
+/*
+Copyright (c) 2013, Marius Cramer, pixcept KG
+Copyright (c) 2020, Michael Seevogel
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+    * Neither the name of ISPConfig nor the names of its contributors
+      may be used to endorse or promote products derived from this software without
+      specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+class cronjob_goaccess extends cronjob {
+
+	// job schedule
+	protected $_schedule = '0 0 * * *';
+
+	/* this function is optional if it contains no custom code */
+	public function onPrepare() {
+		global $app;
+
+		parent::onPrepare();
+	}
+
+	/* this function is optional if it contains no custom code */
+	public function onBeforeRun() {
+		global $app;
+
+		return parent::onBeforeRun();
+	}
+
+	public function onRunJob() {
+		global $app, $conf;
+
+
+		//######################################################################################################
+		// Create goaccess statistics
+		//######################################################################################################
+
+		$sql = "SELECT domain_id, domain, document_root, web_folder, type, system_user, system_group, parent_domain_id FROM web_domain WHERE (type = 'vhost' or type = 'vhostsubdomain' or type = 'vhostalias') and stats_type = 'goaccess' AND server_id = ?";
+		$records = $app->db->queryAllRecords($sql, $conf['server_id']);
+
+		$web_config = $app->getconf->get_server_config($conf['server_id'], 'web');
+
+                $goaccess_conf_locs = array('/etc/goaccess.conf', '/etc/goaccess/goaccess.conf');
+                $count = 0;
+
+                foreach($goaccess_conf_locs as $goa_loc) {
+                        if(is_file($goa_loc) && (filesize($goa_loc) > 0)) {
+                                $goaccess_conf_main = $goa_loc;
+                                break;
+                        } else {
+                                $count++;
+                                if($count == 2) {
+                                        $app->log("No GoAccess base config found. Make sure that GoAccess is installed and that the goaccess.conf does exist in /etc or /etc/goaccess", LOGLEVEL_ERROR);
+                                }
+                        }
+                }
+
+
+                /* Check if goaccess binary is in path */
+                system("type goaccess 2>&1>/dev/null", $retval);
+		if ($retval === 0) {
+
+			foreach($records as $rec) {
+				$yesterday = date('Ymd', strtotime("-1 day", time()));
+
+				$log_folder = 'log';
+
+				if($rec['type'] == 'vhostsubdomain' || $rec['type'] == 'vhostalias') {
+					$tmp = $app->db->queryOneRecord('SELECT `domain` FROM web_domain WHERE domain_id = ?', $rec['parent_domain_id']);
+					$subdomain_host = preg_replace('/^(.*)\.' . preg_quote($tmp['domain'], '/') . '$/', '$1', $rec['domain']);
+					if($subdomain_host == '') $subdomain_host = 'web'.$rec['domain_id'];
+					$log_folder .= '/' . $subdomain_host;
+					unset($tmp);
+				}
+
+				$logfile = $rec['document_root'].'/' . $log_folder . '/'.$yesterday.'-access.log';
+
+				if(!@is_file($logfile)) {
+					$logfile = $rec['document_root'].'/' . $log_folder . '/'.$yesterday.'-access.log.gz';
+					if(!@is_file($logfile)) {
+						continue;
+					}
+				}
+
+				$web_folder = (($rec['type'] == 'vhostsubdomain' || $rec['type'] == 'vhostalias') ? $rec['web_folder'] : 'web');
+				$domain = $rec['domain'];
+				$statsdir = $rec['document_root'].'/'.$web_folder.'/stats';
+				$goaccess_conf = $rec['document_root'].'/log/goaccess.conf';
+
+				/*
+				 In case that you use a different log format, you should use a custom goaccess.conf which you'll have to put into /usr/local/ispconfig/server/conf-custom/.
+				 By default the originally, with GoAccess shipped goaccess.conf from /etc/ or /etc/goaccess will be used along with the log-format value COMBINED. 
+				*/
+
+				if(file_exists("/usr/local/ispconfig/server/conf-custom/goaccess.conf.master") && (!file_exists($goaccess_conf))) {
+					$app->system->copy("/usr/local/ispconfig/server/conf-custom/goaccess.conf.master", $goaccess_conf);
+				} elseif(!file_exists($goaccess_conf)) {
+					/*
+					 By default the goaccess.conf should get copied by the webserver plugin but in case it wasn't, or it got deleted by accident we gonna copy it again to the destination dir.
+					 Also there was no /usr/local/ispconfig/server/conf-custom/goaccess.conf.master, so we gonna use /etc/goaccess.conf as the base conf.
+					*/
+
+					$app->system->copy($goaccess_conf_main, $goaccess_conf);
+					$content = $app->system->file_get_contents($goaccess_conf, true);
+					$content = preg_replace('/^(#)?log-format COMBINED/m', "log-format COMBINED", $content);
+					$app->system->file_put_contents($goaccess_conf, $content, true);
+					unset($content);
+				}
+
+				/* Update the primary domain name in the title, it could occasionally change */
+				if(is_file($goaccess_conf) && (filesize($goaccess_conf) > 0)) {
+					$content = $app->system->file_get_contents($goaccess_conf, true);
+					$content = preg_replace('/^(#)?html-report-title(.*)/m', "html-report-title $domain", $content);
+					$app->system->file_put_contents($goaccess_conf, $content, true);
+					unset($content);
+				}
+
+                                $username = $rec['system_user'];
+                                $groupname = $rec['system_group'];
+                                $docroot = $rec['document_root'];
+
+				if(!@is_dir($statsdir)) $app->system->mkdirpath($statsdir, 0755, $username, $groupname);
+
+				$goa_db_dir = $docroot.'/'.$web_folder.'/stats/.db/';
+				$output_html = $docroot.'/'.$web_folder.'/stats/goaindex.html';
+	                        if(!@is_dir($goa_db_dir)) $app->system->mkdirpath($goa_db_dir);
+	
+	                        if(is_link('/var/log/ispconfig/httpd/'.$domain.'/yesterday-access.log')) $app->system->unlink('/var/log/ispconfig/httpd/'.$domain.'/yesterday-access.log');
+	                        symlink($logfile, '/var/log/ispconfig/httpd/'.$domain.'/yesterday-access.log');
+
+
+				$app->system->exec_safe('chown -R ?:? ?', $username, $groupname, $statsdir);
+
+				$goamonth = date("n");
+				$goayear = date("Y");
+
+				if (date("d") == 1) {
+					$goamonth = date("m")-1;
+					if (date("m") == 1) {
+						$goayear = date("Y")-1;
+						$goamonth = "12";
+					}
+				}
+
+				if (date("d") == 2) {
+					$goamonth = date("m")-1;
+					if (date("m") == 1) {
+						$goayear = date("Y")-1;
+						$goamonth = "12";
+					}
+
+					$statsdirold = $statsdir."/".$goayear."-".$goamonth."/";
+
+					if(!is_dir($statsdirold)) {
+						 $app->system->mkdirpath($statsdirold, 0755, $username, $groupname);
+					}
+
+					// don't rotate db files per month
+					//rename($goa_db_dir, $statsdirold.'db');
+	                                //mkdir($goa_db_dir);
+
+					$files = scandir($statsdir);
+
+					foreach ($files as $file) {
+						if (substr($file, 0, 1) != "." && !is_dir("$statsdir"."/"."$file") && substr($file, 0, 1) != "w" && substr($file, 0, 1) != "i") $app->system->copy("$statsdir"."/"."$file", "$statsdirold"."$file");
+					}
+				}
+
+				// Get the GoAccess version
+				$match = array();
+
+				$goaccess_version = $app->system->system_safe('goaccess --version 2>&1');
+
+				if(preg_match('/[0-9]\.[0-9]{1,2}/', $goaccess_version, $match)) {
+					$goaccess_version = $match[0];
+				}
+
+
+				/*
+				 * GoAccess removed with 1.4 btree support and supports from this version on only "In-Memory with On-Disk Persitance Storage".
+				 * For versions prior 1.4 you need GoAccess with btree support compiled!
+				 */
+				
+				$cust_lang = $conf['language']."_".strtoupper($conf['language']);
+
+				if(version_compare($goaccess_version,1.4) >= 0) {
+					$app->system->exec_safe("LANG=? goaccess -f ? --config-file ? --restore --persist --db-path=? --output=?", $cust_lang, $logfile, $goaccess_conf, $goa_db_dir, $output_html);
+				} else {
+					$output = $app->system->system_safe('goaccess --help 2>&1');
+					preg_match('/keep-db-files/', $output, $match);
+					if($match[0] == "keep-db-files") {
+						$app->system->exec_safe("LANG=? goaccess -f ? --config-file ? --load-from-disk --keep-db-files --db-path=? --output=?", $cust_lang, $logfile, $goaccess_conf, $goa_db_dir, $output_html);
+					} else {
+	                                        $app->log("Stats not generated. The GoAccess binary was not compiled with btree support. Please recompile/reinstall GoAccess with btree support, or install GoAccess version >= 1.4!", LOGLEVEL_ERROR);
+					}
+	                                unset($output);
+				}
+				unset($cust_lang);
+
+				if(!is_file($rec['document_root']."/".$web_folder."/stats/index.php")) {
+					if(file_exists("/usr/local/ispconfig/server/conf-custom/goaccess_index.php.master")) {
+						$app->system->copy("/usr/local/ispconfig/server/conf-custom/goaccess_index.php.master", $rec['document_root']."/".$web_folder."/stats/index.php");
+					} else {
+						$app->system->copy("/usr/local/ispconfig/server/conf/goaccess_index.php.master", $rec['document_root']."/".$web_folder."/stats/index.php");
+					}
+				}
+
+	                        $app->log('Created GoAccess statistics for ' . $domain, LOGLEVEL_DEBUG);
+	                        if(is_file($rec['document_root']."/".$web_folder."/stats/index.php")) {
+	                                chown($rec['document_root']."/".$web_folder."/stats/index.php", $rec['system_user']);
+	                                chgrp($rec['document_root']."/".$web_folder."/stats/index.php", $rec['system_group']);
+	                        }
+
+				$app->system->exec_safe('chown -R ?:? ?', $username, $groupname, $statsdir);
+			}
+
+	} else {
+		$app->log("Stats not generated. The GoAccess binary couldn't be found. Make sure that GoAccess is installed and that it is in \$PATH", LOGLEVEL_ERROR);
+	}
+
+		parent::onRunJob();
+	}
+
+	/* this function is optional if it contains no custom code */
+	public function onAfterRun() {
+		global $app;
+
+		parent::onAfterRun();
+	}
+
+}
+
+?>
diff --git a/server/lib/classes/cron.d/150-webalizer.inc.php b/server/lib/classes/cron.d/150-webalizer.inc.php
index 42aa125e0f9b427883196bf2d511e25a3ad6d182..6c68cef183a6bcad8532bc5cbb13edcf01201800 100644
--- a/server/lib/classes/cron.d/150-webalizer.inc.php
+++ b/server/lib/classes/cron.d/150-webalizer.inc.php
@@ -127,8 +127,8 @@ class cronjob_webalizer extends cronjob {
 			chown($statsdir, $username);
 			chgrp($statsdir, $groupname);
 			$app->system->exec_safe("$webalizer -c ? -n ? -s ? -r ? -q -T -p -o ? ?", $webalizer_conf, $domain, $domain, $domain, $statsdir, $logfile);
-			
-			exec('chown -R ?:? ?', $username, $groupname, $statsdir);
+
+			$app->system->exec_safe('chown -R ?:? ?', $username, $groupname, $statsdir);
 		}
 
 
diff --git a/server/plugins-available/apache2_plugin.inc.php b/server/plugins-available/apache2_plugin.inc.php
index 3ef5e21467f5c98c2ab901e1f0a3bc14f3cb037d..82340587140d9b8c8cc550c9157677adf1cd4889 100644
--- a/server/plugins-available/apache2_plugin.inc.php
+++ b/server/plugins-available/apache2_plugin.inc.php
@@ -107,7 +107,7 @@ class apache2_plugin {
 				$affected_snippets = $app->db->queryAllRecords('SELECT directive_snippets_id FROM directive_snippets WHERE required_php_snippets RLIKE(?) AND type = ?', $rlike, 'apache');
 				if(is_array($affected_snippets) && !empty($affected_snippets)) {
 					foreach($affected_snippets as $snippet) $sql_in[] = $snippet['directive_snippets_id'];
-					$affected_sites = $app->db->queryAllRecords('SELECT domain_id FROM web_domain WHERE server_id = ? AND directive_snippets_id IN('.implode(',', $sql_in).')', $conf['server_id']);
+					$affected_sites = $app->db->queryAllRecords('SELECT domain_id FROM web_domain WHERE server_id = ? AND directive_snippets_id IN ?', $conf['server_id'], $sql_in);
 				}
 			}
 			if($snippet['type'] == 'apache') $affected_sites = $app->db->queryAllRecords('SELECT domain_id FROM web_domain WHERE server_id = ? AND directive_snippets_id = ?', $conf['server_id'], $snippet['directive_snippets_id']);
@@ -1915,6 +1915,11 @@ class apache2_plugin {
 			$this->awstats_update($data, $web_config);
 		}
 
+                //* Create GoAccess configuration
+                if($data['new']['stats_type'] == 'goaccess' && ($data['new']['type'] == 'vhost' || $data['new']['type'] == 'vhostsubdomain' || $data['new']['type'] == 'vhostalias')) {
+                        $this->goaccess_update($data, $web_config);
+                }
+
 		//* Remove Stats-Folder when Statistics set to none
 		if($data['new']['stats_type'] == '' && ($data['new']['type'] == 'vhost' || $data['new']['type'] == 'vhostsubdomain' || $data['new']['type'] == 'vhostalias')) {
 			$app->file->removeDirectory($data['new']['document_root'].'/web/stats');
@@ -2328,6 +2333,11 @@ class apache2_plugin {
 				$this->awstats_delete($data, $web_config);
 			}
 
+			//* Remove the GoAccess configuration file
+			if($data['old']['stats_type'] == 'goaccess') {
+				$this->goaccess_delete($data, $web_config);
+			}
+
 			if($data['old']['type'] == 'vhostsubdomain' || $data['old']['type'] == 'vhostalias') {
 				$app->system->web_folder_protection($parent_web_document_root, true);
 			}
@@ -3025,15 +3035,96 @@ class apache2_plugin {
 		}
 	}
 
-	//* Delete the awstats configuration file
-	private function awstats_delete ($data, $web_config) {
+        //* Delete the awstats configuration file
+        private function awstats_delete ($data, $web_config) {
+                global $app;
+
+                $awstats_conf_dir = $web_config['awstats_conf_dir'];
+
+                if ( @is_file($awstats_conf_dir.'/awstats.'.$data['old']['domain'].'.conf') ) {
+                        $app->system->unlink($awstats_conf_dir.'/awstats.'.$data['old']['domain'].'.conf');
+                        $app->log('Removed AWStats config file: '.$awstats_conf_dir.'/awstats.'.$data['old']['domain'].'.conf', LOGLEVEL_DEBUG);
+                }
+        }
+
+	//* Update the GoAccess configuration file
+        private function goaccess_update ($data, $web_config) {
+                global $app;
+
+                $web_folder = $data['new']['web_folder'];
+                if($data['new']['type'] == 'vhost') $web_folder = 'web';
+
+                $goaccess_conf_locs = array('/etc/goaccess.conf', '/etc/goaccess/goaccess.conf');
+                $count = 0;
+
+                foreach($goaccess_conf_locs as $goa_loc) {
+                        if(is_file($goa_loc) && (filesize($goa_loc) > 0)) {
+                                $goaccess_conf_main = $goa_loc;
+                                break;
+                        } else {
+                                $count++;
+                                if($count == 2) {
+                                        $app->log("No GoAccess base config found. Make sure that GoAccess is installed and that the goaccess.conf does exist in /etc or /etc/goaccess", LOGLEVEL_WARN);
+                                }
+                        }
+                }
+
+                if(!is_dir($data['new']['document_root']."/" . $web_folder . "/stats/.db")) $app->system->mkdirpath($data['new']['document_root'] . "/" . $web_folder . "/stats/.db");
+                $goaccess_conf = $data['new']['document_root'].'/log/goaccess.conf';
+
+                /*
+                In case that you use a different log format, you should use a custom goaccess.conf which you'll have to put into /usr/local/ispconfig/server/conf-custom/.
+                By default the originaly with GoAccess shipped goaccess.conf from /etc/ will be used along with the log-format value COMBINED. 
+		*/
+
+                if(file_exists("/usr/local/ispconfig/server/conf-custom/goaccess.conf.master")) {
+                        $app->system->copy("/usr/local/ispconfig/server/conf-custom/goaccess_index.php.master", $goaccess_conf);
+
+                } elseif(!file_exists($goaccess_conf)) {
+
+			/*
+                         By default the goaccess.conf should get copied by the webserver plugin but in case it wasn't, or it got deleted by accident we gonna copy it again to the destination dir.
+                         Also there was no /usr/local/ispconfig/server/conf-custom/goaccess.conf.master, so we gonna use /etc/goaccess.conf as the base conf.
+			 */
+
+			$app->system->copy($goaccess_conf_main, $goaccess_conf);
+			$content = $app->system->file_get_contents($goaccess_conf, true);
+			$content = preg_replace('/^(#)?log-format COMBINED/m', "log-format COMBINED", $content);
+			$app->system->file_put_contents($goaccess_conf, $content, true);
+			unset($content);
+
+                }
+
+                if(file_exists($goaccess_conf)) {
+                        $domain = $data['new']['domain'];
+                        $content = $app->system->file_get_contents($goaccess_conf, true);
+                        $content = preg_replace('/^(#)?html-report-title(.*)/m', "html-report-title $domain", $content);
+                        $app->system->file_put_contents($goaccess_conf, $content, true);
+                        unset($content);
+
+                }
+
+                if(is_file($goaccess_conf) && (filesize($goaccess_conf) > 0)) {
+                        $app->log('Created GoAccess config file: '.$goaccess_conf, LOGLEVEL_DEBUG);
+                } 
+
+                if(is_file($data['new']['document_root']."/" . $web_folder . "/stats/index.html")) $app->system->unlink($data['new']['document_root']."/" . $web_folder . "/stats/index.html");
+                if(file_exists("/usr/local/ispconfig/server/conf-custom/goaccess_index.php.master")) {
+                        $app->system->copy("/usr/local/ispconfig/server/conf-custom/goaccess_index.php.master", $data['new']['document_root']."/" . $web_folder . "/stats/index.php");
+                } else {
+                        $app->system->copy("/usr/local/ispconfig/server/conf/goaccess_index.php.master", $data['new']['document_root']."/" . $web_folder . "/stats/index.php");
+                }
+        }
+
+	//* Delete the GoAccess configuration file
+	private function goaccess_delete ($data, $web_config) {
 		global $app;
 
-		$awstats_conf_dir = $web_config['awstats_conf_dir'];
+		$goaccess_conf = $data['old']['document_root'] . "/log/goaccess.conf";
 
-		if ( @is_file($awstats_conf_dir.'/awstats.'.$data['old']['domain'].'.conf') ) {
-			$app->system->unlink($awstats_conf_dir.'/awstats.'.$data['old']['domain'].'.conf');
-			$app->log('Removed AWStats config file: '.$awstats_conf_dir.'/awstats.'.$data['old']['domain'].'.conf', LOGLEVEL_DEBUG);
+		if ( @is_file($goaccess_conf) ) {
+			$app->system->unlink($goaccess_conf);
+			$app->log('Removed GoAccess config file: '.$goaccess_conf, LOGLEVEL_DEBUG);
 		}
 	}
 
diff --git a/server/plugins-available/nginx_plugin.inc.php b/server/plugins-available/nginx_plugin.inc.php
index 85661b97ce2a7aac4ff911b1d4fffc86077a9ded..6483df4eb99555599963ae66254c8b946fa500ea 100644
--- a/server/plugins-available/nginx_plugin.inc.php
+++ b/server/plugins-available/nginx_plugin.inc.php
@@ -103,7 +103,7 @@ class nginx_plugin {
 				$affected_snippets = $app->db->queryAllRecords('SELECT directive_snippets_id FROM directive_snippets WHERE required_php_snippets RLIKE(?) AND type = ?', $rlike, 'nginx');
 				if(is_array($affected_snippets) && !empty($affected_snippets)) {
 					foreach($affected_snippets as $snippet) $sql_in[] = $snippet['directive_snippets_id'];
-					$affected_sites = $app->db->queryAllRecords('SELECT domain_id FROM web_domain WHERE server_id = ? AND directive_snippets_id IN('.implode(',', $sql_in).')', $conf['server_id']);
+					$affected_sites = $app->db->queryAllRecords('SELECT domain_id FROM web_domain WHERE server_id = ? AND directive_snippets_id IN ?', $conf['server_id'], $sql_in);
 				}
 			}
 			if($snippet['type'] == 'nginx') $affected_sites = $app->db->queryAllRecords('SELECT domain_id FROM web_domain WHERE server_id = ? AND directive_snippets_id = ?', $conf['server_id'], $snippet['directive_snippets_id']);
@@ -1934,6 +1934,11 @@ class nginx_plugin {
 			$this->awstats_update($data, $web_config);
 		}
 
+                //* Create GoAccess configuration
+                if($data['new']['stats_type'] == 'goaccess' && ($data['new']['type'] == 'vhost' || $data['new']['type'] == 'vhostsubdomain' || $data['new']['type'] == 'vhostalias')) {
+                        $this->goaccess_update($data, $web_config);
+                }
+
 		//* Remove Stats-Folder when Statistics set to none
 		if($data['new']['stats_type'] == '' && ($data['new']['type'] == 'vhost' || $data['new']['type'] == 'vhostsubdomain' || $data['new']['type'] == 'vhostalias')) {
 			$app->file->removeDirectory($data['new']['document_root'].'/web/stats');
@@ -2320,6 +2325,11 @@ class nginx_plugin {
 				$this->awstats_delete($data, $web_config);
 			}
 
+			//* Remove the GoAccess configuration file
+                        if($data['old']['stats_type'] == 'goaccess') {
+                                $this->goaccess_delete($data, $web_config);
+                        }
+
 			//* Delete the web-backups
 			if($data['old']['type'] == 'vhost') {
 				$server_config = $app->getconf->get_server_config($conf['server_id'], 'server');
@@ -2560,6 +2570,89 @@ class nginx_plugin {
 		//$app->services->restartServiceDelayed('httpd','reload');
 	}
 
+
+
+	//* Update the GoAccess configuration file
+	private function goaccess_update ($data, $web_config) {
+                global $app;
+
+                $web_folder = $data['new']['web_folder'];
+                if($data['new']['type'] == 'vhost') $web_folder = 'web';
+
+                $goaccess_conf_locs = array('/etc/goaccess.conf', '/etc/goaccess/goaccess.conf');
+                $count = 0;
+
+                foreach($goaccess_conf_locs as $goa_loc) {
+                        if(is_file($goa_loc) && (filesize($goa_loc) > 0)) {
+                                $goaccess_conf_main = $goa_loc;
+                                break;
+                        } else {
+                                $count++; 
+                                if($count == 2) {
+                                        $app->log("No GoAccess base config found. Make sure that GoAccess is installed and that the goaccess.conf does exist in /etc or /etc/goaccess", LOGLEVEL_WARN);
+                                }
+                        }
+                }
+
+                if(!is_dir($data['new']['document_root']."/" . $web_folder . "/stats/.db")) $app->system->mkdirpath($data['new']['document_root'] . "/" . $web_folder . "/stats/.db");
+		$goaccess_conf = $data['new']['document_root'].'/log/goaccess.conf';
+
+                /*
+                In case that you use a different log format, you should use a custom goaccess.conf which you'll have to put into /usr/local/ispconfig/server/conf-custom/.
+                By default the originaly with GoAccess shipped goaccess.conf from /etc/ will be used along with the log-format value COMBINED. 
+                */
+
+                if(file_exists("/usr/local/ispconfig/server/conf-custom/goaccess.conf.master")) {
+                        $app->system->copy("/usr/local/ispconfig/server/conf-custom/goaccess_index.php.master", $goaccess_conf);
+
+                } elseif(!file_exists($goaccess_conf)) {
+
+                        /*
+                         By default the goaccess.conf should get copied by the webserver plugin but in case it wasn't, or it got deleted by accident we gonna copy it again to the destination dir.
+                         Also there was no /usr/local/ispconfig/server/conf-custom/goaccess.conf.master, so we gonna use /etc/goaccess.conf as the base conf.
+                         */
+
+                        $app->system->copy($goaccess_conf_main, $goaccess_conf);
+                        $content = $app->system->file_get_contents($goaccess_conf, true);
+                        $content = preg_replace('/^(#)?log-format COMBINED/m', "log-format COMBINED", $content);
+                        $app->system->file_put_contents($goaccess_conf, $content, true);
+                        unset($content);
+
+                }
+
+                if(file_exists($goaccess_conf)) {
+                        $domain = $data['new']['domain'];
+                        $content = $app->system->file_get_contents($goaccess_conf, true);
+                        $content = preg_replace('/^(#)?html-report-title(.*)/m', "html-report-title $domain", $content);
+                        $app->system->file_put_contents($goaccess_conf, $content, true);
+                        unset($content);
+
+                }
+
+                if(is_file($goaccess_conf) && (filesize($goaccess_conf) > 0)) {
+                        $app->log('Created GoAccess config file: '.$goaccess_conf, LOGLEVEL_DEBUG);
+                }
+
+                if(is_file($data['new']['document_root']."/" . $web_folder . "/stats/index.html")) $app->system->unlink($data['new']['document_root']."/" . $web_folder . "/stats/index.html");
+                if(file_exists("/usr/local/ispconfig/server/conf-custom/goaccess_index.php.master")) {
+                        $app->system->copy("/usr/local/ispconfig/server/conf-custom/goaccess_index.php.master", $data['new']['document_root']."/" . $web_folder . "/stats/index.php");
+                } else {
+                        $app->system->copy("/usr/local/ispconfig/server/conf/goaccess_index.php.master", $data['new']['document_root']."/" . $web_folder . "/stats/index.php");
+		}
+	}
+
+        //* Delete the GoAccess configuration file
+        private function goaccess_delete ($data, $web_config) {
+                global $app;
+
+                $goaccess_conf = $data['old']['document_root'] . "/log/goaccess.conf";
+
+                if ( @is_file($goaccess_conf) ) {
+                        $app->system->unlink($goaccess_conf);
+                        $app->log('Removed GoAccess config file: '.$goaccess_conf, LOGLEVEL_DEBUG);
+                }
+        }
+
 	//* Update the awstats configuration file
 	private function awstats_update ($data, $web_config) {
 		global $app;