From 0b0dc90a467c21513010d62af74a03c952652769 Mon Sep 17 00:00:00 2001
From: mcramer <m.cramer@pixcept.de>
Date: Tue, 23 Jun 2009 14:46:18 +0000
Subject: [PATCH] Added: Cron Jobs for clients (url, chrooted, full) Fixed:
 Missing server.ini config entries for vlogger and jailkit Added: Admin
 interface server_config for vlogger and cron

---
 install/dist/conf/centos52.conf.php           |   6 +
 install/dist/conf/centos53.conf.php           |   6 +
 install/dist/conf/debian40.conf.php           |   6 +
 install/dist/conf/fedora9.conf.php            |   6 +
 install/dist/conf/gentoo.conf.php             |   6 +
 install/dist/conf/opensuse110.conf.php        |   5 +
 install/lib/installer_base.lib.php            |   3 +-
 install/sql/ispconfig3.sql                    |  33 +++
 install/tpl/server.ini.master                 |   9 +
 interface/lib/classes/validate_cron.inc.php   | 191 ++++++++++++
 .../web/admin/form/server_config.tform.php    |  82 +++++-
 .../web/admin/lib/lang/de_server_config.lng   |   5 +
 .../web/admin/lib/lang/en_server_config.lng   |   5 +
 .../templates/server_config_cron_edit.htm     |  30 ++
 .../templates/server_config_jailkit_edit.htm  |   4 +
 .../templates/server_config_vlogger_edit.htm  |  22 ++
 interface/web/client/form/client.tform.php    |  34 +++
 .../web/client/form/client_template.tform.php |  34 +++
 interface/web/client/lib/lang/de_client.lng   |   5 +
 .../client/lib/lang/de_client_template.lng    |   5 +
 interface/web/client/lib/lang/en_client.lng   |   5 +
 .../client/lib/lang/en_client_template.lng    |   5 +
 .../client/templates/client_edit_limits.htm   |  14 +
 .../templates/client_template_edit_limits.htm |  14 +
 interface/web/sites/cron_del.php              |  64 +++++
 interface/web/sites/cron_edit.php             | 226 +++++++++++++++
 interface/web/sites/cron_list.php             |  23 ++
 interface/web/sites/form/cron.tform.php       | 189 ++++++++++++
 interface/web/sites/lib/lang/de_cron.lng      |  21 ++
 interface/web/sites/lib/lang/de_cron_list.lng |  12 +
 interface/web/sites/lib/lang/en_cron.lng      |  21 ++
 interface/web/sites/lib/lang/en_cron_list.lng |  12 +
 interface/web/sites/lib/module.conf.php       |  15 +
 interface/web/sites/list/cron.list.php        | 152 ++++++++++
 interface/web/sites/templates/cron_edit.htm   |  68 +++++
 interface/web/sites/templates/cron_list.htm   |  71 +++++
 interface/web/sites/web_domain_del.php        |   6 +
 server/mods-available/cron_module.inc.php     |  97 +++++++
 .../cron_jailkit_plugin.inc.php               | 272 ++++++++++++++++++
 server/plugins-available/cron_plugin.inc.php  | 212 ++++++++++++++
 40 files changed, 1994 insertions(+), 2 deletions(-)
 create mode 100644 interface/lib/classes/validate_cron.inc.php
 create mode 100644 interface/web/admin/templates/server_config_cron_edit.htm
 create mode 100644 interface/web/admin/templates/server_config_vlogger_edit.htm
 create mode 100644 interface/web/sites/cron_del.php
 create mode 100644 interface/web/sites/cron_edit.php
 create mode 100644 interface/web/sites/cron_list.php
 create mode 100644 interface/web/sites/form/cron.tform.php
 create mode 100644 interface/web/sites/lib/lang/de_cron.lng
 create mode 100644 interface/web/sites/lib/lang/de_cron_list.lng
 create mode 100644 interface/web/sites/lib/lang/en_cron.lng
 create mode 100644 interface/web/sites/lib/lang/en_cron_list.lng
 create mode 100644 interface/web/sites/list/cron.list.php
 create mode 100644 interface/web/sites/templates/cron_edit.htm
 create mode 100644 interface/web/sites/templates/cron_list.htm
 create mode 100644 server/mods-available/cron_module.inc.php
 create mode 100644 server/plugins-available/cron_jailkit_plugin.inc.php
 create mode 100644 server/plugins-available/cron_plugin.inc.php

diff --git a/install/dist/conf/centos52.conf.php b/install/dist/conf/centos52.conf.php
index 0f7cac0796..e1ee4be440 100644
--- a/install/dist/conf/centos52.conf.php
+++ b/install/dist/conf/centos52.conf.php
@@ -147,8 +147,14 @@ $conf['jailkit']['config_dir'] = '/etc/jailkit';
 $conf['jailkit']['jk_init'] = 'jk_init.ini';
 $conf['jailkit']['jk_chrootsh'] = 'jk_chrootsh.ini';
 $conf['jailkit']['jailkit_chroot_app_programs'] = '/usr/bin/groups /usr/bin/id /usr/bin/dircolors /bin/basename /usr/bin/dirname /usr/bin/nano';
+$conf['jailkit']['jailkit_chroot_cron_programs'] = '/usr/bin/php /usr/bin/perl /usr/share/perl /usr/share/php';
 
 //* vlogger
 $conf['vlogger']['config_dir'] = '/etc';
 
+//* cron
+$conf['cron']['init_script'] = 'crond';
+$conf['cron']['crontab_dir'] = '/etc/cron.d';
+$conf['cron']['wget'] = '/usr/bin/wget';
+
 ?>
\ No newline at end of file
diff --git a/install/dist/conf/centos53.conf.php b/install/dist/conf/centos53.conf.php
index 51a5ba448e..260eedb61f 100644
--- a/install/dist/conf/centos53.conf.php
+++ b/install/dist/conf/centos53.conf.php
@@ -147,8 +147,14 @@ $conf['jailkit']['config_dir'] = '/etc/jailkit';
 $conf['jailkit']['jk_init'] = 'jk_init.ini';
 $conf['jailkit']['jk_chrootsh'] = 'jk_chrootsh.ini';
 $conf['jailkit']['jailkit_chroot_app_programs'] = '/usr/bin/groups /usr/bin/id /usr/bin/dircolors /bin/basename /usr/bin/dirname /usr/bin/nano';
+$conf['jailkit']['jailkit_chroot_cron_programs'] = '/usr/bin/php /usr/bin/perl /usr/share/perl /usr/share/php';
 
 //* vlogger
 $conf['vlogger']['config_dir'] = '/etc';
 
+//* cron
+$conf['cron']['init_script'] = 'crond';
+$conf['cron']['crontab_dir'] = '/etc/cron.d';
+$conf['cron']['wget'] = '/usr/bin/wget';
+
 ?>
\ No newline at end of file
diff --git a/install/dist/conf/debian40.conf.php b/install/dist/conf/debian40.conf.php
index 92fd4289b0..6d107113c9 100644
--- a/install/dist/conf/debian40.conf.php
+++ b/install/dist/conf/debian40.conf.php
@@ -147,8 +147,14 @@ $conf['jailkit']['config_dir'] = '/etc/jailkit';
 $conf['jailkit']['jk_init'] = 'jk_init.ini';
 $conf['jailkit']['jk_chrootsh'] = 'jk_chrootsh.ini';
 $conf['jailkit']['jailkit_chroot_app_programs'] = '/usr/bin/groups /usr/bin/id /usr/bin/dircolors /usr/bin/lesspipe /usr/bin/basename /usr/bin/dirname /usr/bin/nano /usr/bin/pico';
+$conf['jailkit']['jailkit_chroot_cron_programs'] = '/usr/bin/php /usr/bin/perl /usr/share/perl /usr/share/php';
 
 //* vlogger
 $conf['vlogger']['config_dir'] = '/etc';
 
+//* cron
+$conf['cron']['init_script'] = 'cron';
+$conf['cron']['crontab_dir'] = '/etc/cron.d';
+$conf['cron']['wget'] = '/usr/bin/wget';
+
 ?>
\ No newline at end of file
diff --git a/install/dist/conf/fedora9.conf.php b/install/dist/conf/fedora9.conf.php
index fd41690358..c49f133b33 100644
--- a/install/dist/conf/fedora9.conf.php
+++ b/install/dist/conf/fedora9.conf.php
@@ -147,8 +147,14 @@ $conf['jailkit']['config_dir'] = '/etc/jailkit';
 $conf['jailkit']['jk_init'] = 'jk_init.ini';
 $conf['jailkit']['jk_chrootsh'] = 'jk_chrootsh.ini';
 $conf['jailkit']['jailkit_chroot_app_programs'] = '/usr/bin/groups /usr/bin/id /usr/bin/dircolors /bin/basename /usr/bin/dirname /usr/bin/nano';
+$conf['jailkit']['jailkit_chroot_cron_programs'] = '/usr/bin/php /usr/bin/perl /usr/share/perl /usr/share/php';
 
 //* vlogger
 $conf['vlogger']['config_dir'] = '/etc';
 
+//* cron
+$conf['cron']['init_script'] = 'crond';
+$conf['cron']['crontab_dir'] = '/etc/cron.d';
+$conf['cron']['wget'] = '/usr/bin/wget';
+
 ?>
\ No newline at end of file
diff --git a/install/dist/conf/gentoo.conf.php b/install/dist/conf/gentoo.conf.php
index 4d9a7308b8..9003aebdf8 100644
--- a/install/dist/conf/gentoo.conf.php
+++ b/install/dist/conf/gentoo.conf.php
@@ -96,8 +96,14 @@ $conf['jailkit']['config_dir'] = '/etc/jailkit';
 $conf['jailkit']['jk_init'] = 'jk_init.ini';
 $conf['jailkit']['jk_chrootsh'] = 'jk_chrootsh.ini';
 $conf['jailkit']['jailkit_chroot_app_programs'] = '/usr/bin/groups /usr/bin/id /usr/bin/dircolors /usr/bin/lesspipe /usr/bin/basename /usr/bin/dirname /usr/bin/nano /usr/bin/pico';
+$conf['jailkit']['jailkit_chroot_cron_programs'] = '/usr/bin/php /usr/bin/perl /usr/share/perl /usr/share/php';
 
 //* vlogger
 $conf['vlogger']['config_dir'] = '/etc';
 
+//* cron
+$conf['cron']['init_script'] = 'cron';
+$conf['cron']['crontab_dir'] = '/etc/cron.d';
+$conf['cron']['wget'] = '/usr/bin/wget';
+
 ?>
\ No newline at end of file
diff --git a/install/dist/conf/opensuse110.conf.php b/install/dist/conf/opensuse110.conf.php
index e46d77ece0..58b9d38017 100644
--- a/install/dist/conf/opensuse110.conf.php
+++ b/install/dist/conf/opensuse110.conf.php
@@ -147,9 +147,14 @@ $conf['jailkit']['config_dir'] = '/etc/jailkit';
 $conf['jailkit']['jk_init'] = 'jk_init.ini';
 $conf['jailkit']['jk_chrootsh'] = 'jk_chrootsh.ini';
 $conf['jailkit']['jailkit_chroot_app_programs'] = '/usr/bin/groups /usr/bin/id /usr/bin/dircolors /usr/bin/basename /usr/bin/dirname /usr/bin/nano /usr/bin/pico';
+$conf['jailkit']['jailkit_chroot_cron_programs'] = '/usr/bin/php /usr/bin/perl /usr/share/perl /usr/share/php';
 
 //* vlogger
 $conf['vlogger']['config_dir'] = '/etc';
 
+//* cron
+$conf['cron']['init_script'] = 'cron';
+$conf['cron']['crontab_dir'] = '/etc/cron.d';
+$conf['cron']['wget'] = '/usr/bin/wget';
 
 ?>
\ No newline at end of file
diff --git a/install/lib/installer_base.lib.php b/install/lib/installer_base.lib.php
index 8d92e2b4c5..82b5b170ed 100644
--- a/install/lib/installer_base.lib.php
+++ b/install/lib/installer_base.lib.php
@@ -210,7 +210,8 @@ class installer_base {
 		$tpl_ini_array['web']['website_basedir'] = $conf['web']['website_basedir'];
 		$tpl_ini_array['web']['website_path'] = $conf['web']['website_path'];
 		$tpl_ini_array['web']['website_symlinks'] = $conf['web']['website_symlinks'];
-		
+        $tpl_ini_array['cron']['crontab_dir'] = $conf['cron']['crontab_dir'];
+        
 		$server_ini_content = array_to_ini($tpl_ini_array);
 		$server_ini_content = mysql_real_escape_string($server_ini_content);
 		
diff --git a/install/sql/ispconfig3.sql b/install/sql/ispconfig3.sql
index 376ff66889..1e1e8e4b33 100644
--- a/install/sql/ispconfig3.sql
+++ b/install/sql/ispconfig3.sql
@@ -92,6 +92,9 @@ CREATE TABLE `client` (
   `limit_dns_record` int(11) NOT NULL default '-1',
   `default_dbserver` int(11) NOT NULL default '1',
   `limit_database` int(11) NOT NULL default '-1',
+  `limit_cron` int(11) NOT NULL default '0',
+  `limit_cron_type` enum('url','chrooted','full') NOT NULL default 'url',
+  `limit_cron_frequency` int(11) NOT NULL default '5',
   `limit_client` int(11) NOT NULL default '0',
   `parent_client_id` int(11) unsigned NOT NULL default '0',
   `username` varchar(64) default NULL,
@@ -140,10 +143,40 @@ CREATE TABLE `client_template` (
   `limit_dns_zone` int(11) NOT NULL default '-1',
   `limit_dns_record` int(11) NOT NULL default '-1',
   `limit_database` int(11) NOT NULL default '-1',
+  `limit_cron` int(11) NOT NULL default '0',
+  `limit_cron_type` enum('url','chrooted','full') NOT NULL default 'url',
+  `limit_cron_frequency` int(11) NOT NULL default '5',
   `limit_client` int(11) NOT NULL default '0',
   PRIMARY KEY  (`template_id`)
 ) ENGINE=MyISAM AUTO_INCREMENT=1;
 
+
+-- --------------------------------------------------------
+
+-- 
+-- Table structure for table  `dns_rr`
+-- 
+CREATE TABLE `cron` (
+  `id` int(11) unsigned NOT NULL auto_increment,
+  `sys_userid` int(11) unsigned NOT NULL default '0',
+  `sys_groupid` int(11) unsigned NOT NULL default '0',
+  `sys_perm_user` varchar(5) NULL default NULL,
+  `sys_perm_group` varchar(5) NULL default NULL,
+  `sys_perm_other` varchar(5) NULL default NULL,
+  `server_id` int(11) unsigned NOT NULL default '0',
+  `parent_domain_id` int(11) unsigned NOT NULL default '0',
+  `type` enum('url','chrooted','full') NOT NULL default 'url',
+  `command` varchar(255) NOT NULL,
+  `run_min` varchar(100) NULL,
+  `run_hour` varchar(100) NULL,
+  `run_mday` varchar(100) NULL,
+  `run_month` varchar(100) NULL,
+  `run_wday` varchar(100) NULL,
+  `active` enum('n','y') NOT NULL default 'y',
+  PRIMARY KEY  (`id`)
+) ENGINE=MyISAM  AUTO_INCREMENT=1;
+
+
 -- --------------------------------------------------------
 
 -- 
diff --git a/install/tpl/server.ini.master b/install/tpl/server.ini.master
index 463397e6b4..d33afc7d3b 100644
--- a/install/tpl/server.ini.master
+++ b/install/tpl/server.ini.master
@@ -48,3 +48,12 @@ fastcgi_bin=/usr/bin/php-cgi
 jailkit_chroot_home=/home/[username]
 jailkit_chroot_app_sections=basicshell editors extendedshell netutils ssh sftp scp groups jk_lsh
 jailkit_chroot_app_programs=/usr/bin/groups /usr/bin/id /usr/bin/dircolors /usr/bin/lesspipe /usr/bin/basename /usr/bin/dirname /usr/bin/nano /usr/bin/pico
+jailkit_chroot_cron_programs=/usr/bin/php /usr/bin/perl /usr/share/perl /usr/share/php
+
+[vlogger]
+config_dir=/etc
+
+[cron]
+init_script=cron
+crontab_dir=/etc/cron.d
+wget=/usr/bin/wget
diff --git a/interface/lib/classes/validate_cron.inc.php b/interface/lib/classes/validate_cron.inc.php
new file mode 100644
index 0000000000..42d9eb67f3
--- /dev/null
+++ b/interface/lib/classes/validate_cron.inc.php
@@ -0,0 +1,191 @@
+<?php
+
+/*
+Copyright (c) 2007, Till Brehm, projektfarm Gmbh
+Modified 2009, Marius Cramer, pixcept KG
+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 validate_cron {
+	
+    function get_error($errmsg) {
+        global $app;
+        
+        if(isset($app->tform->wordbook[$errmsg])) {
+            return $app->tform->wordbook[$errmsg]."<br>\r\n";
+        } else {
+            return $errmsg."<br>\r\n";
+        }
+    }
+    
+    /*
+        Validator function to check if a given cron command is in correct form (url only).
+    */
+    function command_format($field_name, $field_value, $validator) {
+        if(preg_match("'^(\w+):\/\/'", $field_value, $matches)) {
+            
+            $parsed = parse_url($field_value);
+            if($parsed === false) return $this->get_error($validator['errmsg']);
+            
+            if($parsed["scheme"] != "http" && $parsed["scheme"] != "https") return $this->get_error($validator['errmsg']);
+            
+            if(preg_match("'^([a-z0-9][a-z0-9-]{0,62}\.)+([a-z]{2,4})$'i", $parsed["host"]) == false) return $this->get_error($validator['errmsg']);
+        }
+    }
+    
+	/*
+		Validator function to check if a given cron time is in correct form.
+	*/
+	function run_time_format($field_name, $field_value, $validator) {
+		global $app;
+		
+        //* check general form
+        $is_ok = true;
+        $field_value = str_replace(" ", "", $field_value); // spaces are not needed
+        $used_times = array();
+        
+        if(preg_match("'^[0-9\-\,\/\*]+$'", $field_value) == false) return $this->get_error($validator['errmsg']); // allowed characters are 0-9, comma, *, -, /
+        elseif(preg_match("'[\-\,\/][\-\,\/]'", $field_value) == true) return $this->get_error($validator['errmsg']); // comma, - and / never stand together
+        //* now split list and check each entry. store used values in array for later limit-check
+        $time_list = split(",", $field_value);
+        if(count($time_list) < 1) return $this->get_error($validator['errmsg']);
+        
+        $max_entry = 0;
+        $min_entry = 0;
+        $in_minutes = 1;
+        //* get maximum value of entry for each field type (name)
+        switch($field_name) {
+            case "run_min":
+                $max_entry = 59;
+                break;
+            case "run_hour":
+                $max_entry = 23;
+                $in_minutes = 60;
+                break;
+            case "run_mday":
+                $max_entry = 31;
+                $min_entry = 1;
+                $in_minutes = 1440;
+                break;
+            case "run_month":
+                $max_entry = 12;
+                $min_entry = 1;
+                $in_minutes = 1440 * 28; // not exactly but enough
+                break;
+            case "run_wday":
+                $max_entry = 7;
+                $in_minutes = 1440;
+                break;
+        }
+        
+        if($max_entry == 0) return $this->get_error('unknown_fieldtype_error');
+        
+        foreach($time_list as $entry) {
+            //* possible value combinations:
+            //* x               =>      ^(\d+)$
+            //* x-y             =>      ^(\d+)\-(\d+)$
+            //* x/y             =>      ^(\d+)\/([1-9]\d*)$
+            //* x-y/z           =>      ^(\d+)\-(\d+)\/([1-9]\d*)$
+            //* */x             =>      ^\*\/([1-9]\d*)$
+            //* combined regex  =>      ^(\d+|\*)(\-(\d+))?(\/([1-9]\d*))?$
+            
+            if(preg_match("'^(((\d+)(\-(\d+))?)|\*)(\/([1-9]\d*))?$'", $entry, $matches) == false) {
+                return $this->get_error($validator['errmsg']);
+            }
+            
+            //* matches contains:
+            //* 1       =>      * or value or x-y range
+            //* 2       =>      unused
+            //* 3       =>      value if [1] != *
+            //* 4       =>      empty if no range was used
+            //* 5       =>      2nd value of range if [1] != * and range was used
+            //* 6       =>      empty if step was not used
+            //* 7       =>      step
+            
+            $loop_step = 1;
+            $loop_from = $min_entry;
+            $loop_to = $max_entry;
+            
+            //* calculate used values
+            if($matches[1] == "*") {
+                //* not to check
+            } else {
+                if($matches[3] < $min_entry || $matches[3] > $max_entry) {
+                    //* check if value is in allowed range
+                    return $this->get_error($validator['errmsg']);
+                } elseif($matches[4] && ($matches[5] < $min_entry || $matches[5] > $max_entry || $matches[5] <= $matches[3])) {
+                    //* check if value is in allowed range and not less or equal to first value
+                    return $this->get_error($validator['errmsg']);
+                }
+                
+                $loop_from = $matches[3];
+                $loop_to = $matches[3];
+                if($matches[4]) $loop_to = $matches[5];
+            }
+            if($matches[6] && ($matches[7] < 2 || $matches[7] > $max_entry - 1)) {
+                //* check if step value is valid
+                return $this->get_error($validator['errmsg']);
+            }
+            if($matches[7]) $loop_step = $matches[7];
+            
+            //* loop through values to set used times
+            for($t = $loop_from; $t <= $loop_to; $t = $t + $loop_step) {
+                $used_times[] = $t;
+            }
+        } //* end foreach entry loop
+        
+        //* sort used times and erase doubles
+        sort($used_times);
+        $used_times = array_unique($used_times);
+        
+        //* get minimum frequency and store it in $app->tform->cron_min_freq for usage in onUpdateSave and onInsertSave!
+        $min_freq = -1;
+        $prev_time = -1;
+        foreach($used_times as $curtime) {
+            if($prev_time != -1) {
+                $freq = $curtime - $prev_time;
+                if($min_freq == -1 || $freq < $min_freq) $min_freq = $freq;
+            }
+            $prev_time = $curtime;
+        }
+        
+        //* check last against first (needed because e.g. wday 1,4,7 has diff 1 not 3
+        $prev_time = $used_times[0];
+        $freq = ($prev_time - $min_entry) + ($max_entry - $curtime) + 1;
+        if($min_freq == -1 || $freq < $min_freq) $min_freq = $freq;
+        
+        if($min_freq > 0 && $min_freq <= $max_entry) { //* only store if > 1 && < $max_entry!
+            $min_freq = $min_freq * $in_minutes; // we have to overwrite $app->tform->cron_min_freq if this is higher value
+            if(!$app->tform->cron_min_freq || $app->tform->cron_min_freq > $min_freq) $app->tform->cron_min_freq = $min_freq;
+        }
+        
+        //return "DEBUG: " . $app->tform->cron_min_freq . " ($min_freq) --- " . var_export($used_times, true) . "<br />";
+	}
+	
+	
+	
+	
+}
\ No newline at end of file
diff --git a/interface/web/admin/form/server_config.tform.php b/interface/web/admin/form/server_config.tform.php
index 754000b8d0..3e081b40d7 100644
--- a/interface/web/admin/form/server_config.tform.php
+++ b/interface/web/admin/form/server_config.tform.php
@@ -478,6 +478,17 @@ $form["tabs"]['jailkit'] = array (
 			'width'		=> '40',
 			'maxlength'	=> '1000'
 		),
+        'jailkit_chroot_cron_programs' => array (
+            'datatype'  => 'VARCHAR',
+            'formtype'  => 'TEXT',
+            'default'   => '',
+            'validators'    => array (  0 => array (    'type'  => 'NOTEMPTY',
+                                                        'errmsg'=> 'jailkit_chroot_cron_programs_error_empty'),
+                                    ),
+            'value'     => '',
+            'width'     => '40',
+            'maxlength' => '1000'
+        ),
 	##################################
 	# ENDE Datatable fields
 	##################################
@@ -485,10 +496,79 @@ $form["tabs"]['jailkit'] = array (
 );
 
 
+$form["tabs"]['vlogger'] = array (
+    'title'     => "vlogger",
+    'width'     => 80,
+    'template'  => "templates/server_config_vlogger_edit.htm",
+    'fields'    => array (
+    ##################################
+    # Begin Datatable fields
+    ##################################
+        'config_dir' => array (
+            'datatype'  => 'VARCHAR',
+            'formtype'  => 'TEXT',
+            'default'   => '',
+            'validators'    => array (  0 => array (    'type'  => 'NOTEMPTY',
+                                                        'errmsg'=> 'vlogger_config_dir_error_empty'),
+                                    ),
+            'value'     => '',
+            'width'     => '40',
+            'maxlength' => '255'
+        ),
+    ##################################
+    # ENDE Datatable fields
+    ##################################
+    )
+);
 
 
 
-
+$form["tabs"]['cron'] = array (
+    'title'     => "Cron",
+    'width'     => 80,
+    'template'  => "templates/server_config_cron_edit.htm",
+    'fields'    => array (
+    ##################################
+    # Begin Datatable fields
+    ##################################
+        'init_script' => array (
+            'datatype'  => 'VARCHAR',
+            'formtype'  => 'TEXT',
+            'default'   => '',
+            'validators'    => array (  0 => array (    'type'  => 'NOTEMPTY',
+                                                        'errmsg'=> 'cron_init_script_error_empty'),
+                                    ),
+            'value'     => '',
+            'width'     => '40',
+            'maxlength' => '255'
+        ),
+        'crontab_dir' => array (
+            'datatype'  => 'VARCHAR',
+            'formtype'  => 'TEXT',
+            'default'   => '',
+            'validators'    => array (  0 => array (    'type'  => 'NOTEMPTY',
+                                                        'errmsg'=> 'crontab_dir_error_empty'),
+                                    ),
+            'value'     => '',
+            'width'     => '40',
+            'maxlength' => '255'
+        ),
+        'wget' => array (
+            'datatype'  => 'VARCHAR',
+            'formtype'  => 'TEXT',
+            'default'   => '',
+            'validators'    => array (  0 => array (    'type'  => 'NOTEMPTY',
+                                                        'errmsg'=> 'cron_wget_error_empty'),
+                                    ),
+            'value'     => '',
+            'width'     => '40',
+            'maxlength' => '255'
+        ),
+    ##################################
+    # ENDE Datatable fields
+    ##################################
+    )
+);
 
 
 
diff --git a/interface/web/admin/lib/lang/de_server_config.lng b/interface/web/admin/lib/lang/de_server_config.lng
index 97f3e7ff04..8c24857e4b 100644
--- a/interface/web/admin/lib/lang/de_server_config.lng
+++ b/interface/web/admin/lib/lang/de_server_config.lng
@@ -2,6 +2,7 @@
 $wb['jailkit_chroot_home_txt'] = 'Jailkit chroot home';
 $wb['jailkit_chroot_app_sections_txt'] = 'Jailkit chroot app sections';
 $wb['jailkit_chroot_app_programs_txt'] = 'Jailkit chrooted applications';
+$wb['jailkit_chroot_cron_programs_txt'] = 'Jailkit cron chrooted applications';
 $wb['website_path_txt'] = 'Website path';
 $wb['website_symlinks_txt'] = 'Website symlinks';
 $wb['vhost_conf_dir_txt'] = 'Vhost config dir';
@@ -38,4 +39,8 @@ $wb['netmask_error_wrong'] = 'Invalid Netmask format.';
 $wb['gateway_error_wrong'] = 'Invalid Gateway format.';
 $wb['hostname_error_empty'] = 'Hostname is empty.';
 $wb['nameservers_error_empty'] = 'Nameserver is empty.';
+$wb["config_dir_txt"] = 'Config directory';
+$wb["init_script_txt"] = 'Cron init script name';
+$wb["crontab_dir_txt"] = 'Path for individual crontabs';
+$wb["wget_txt"] = 'Path to wget program';
 ?>
diff --git a/interface/web/admin/lib/lang/en_server_config.lng b/interface/web/admin/lib/lang/en_server_config.lng
index 447ae3aff6..ef736cc42b 100644
--- a/interface/web/admin/lib/lang/en_server_config.lng
+++ b/interface/web/admin/lib/lang/en_server_config.lng
@@ -2,6 +2,7 @@
 $wb["jailkit_chroot_home_txt"] = 'Jailkit chroot home';
 $wb["jailkit_chroot_app_sections_txt"] = 'Jailkit chroot app sections';
 $wb["jailkit_chroot_app_programs_txt"] = 'Jailkit chrooted applications';
+$wb['jailkit_chroot_cron_programs_txt'] = 'Jailkit cron chrooted applications';
 $wb["website_path_txt"] = 'Website path';
 $wb["website_symlinks_txt"] = 'Website symlinks';
 $wb["website_basedir_txt"] = 'Website basedir';
@@ -38,4 +39,8 @@ $wb["netmask_error_wrong"] = 'Invalid Netmask format.';
 $wb["gateway_error_wrong"] = 'Invalid Gateway format.';
 $wb["hostname_error_empty"] = 'Hostname is empty.';
 $wb["nameservers_error_empty"] = 'Nameserver is empty.';
+$wb["config_dir_txt"] = 'Config directory';
+$wb["init_script_txt"] = 'Cron init script name';
+$wb["crontab_dir_txt"] = 'Path for individual crontabs';
+$wb["wget_txt"] = 'Path to wget program';
 ?>
\ No newline at end of file
diff --git a/interface/web/admin/templates/server_config_cron_edit.htm b/interface/web/admin/templates/server_config_cron_edit.htm
new file mode 100644
index 0000000000..066693ed83
--- /dev/null
+++ b/interface/web/admin/templates/server_config_cron_edit.htm
@@ -0,0 +1,30 @@
+<h2><tmpl_var name="list_head_txt"></h2>
+<p><tmpl_var name="list_desc_txt"></p>
+
+<div class="panel panel_server_config">
+  
+  <div class="pnl_formsarea">
+    <fieldset class="inlineLabels">
+      <div class="ctrlHolder">
+        <label for="init_script">{tmpl_var name='init_script_txt'}</label>
+        <input name="init_script" id="init_script" value="{tmpl_var name='init_script'}" size="40" maxlength="255" type="text" class="textInput" />
+            </div>
+      <div class="ctrlHolder">
+        <label for="crontab_dir">{tmpl_var name='crontab_dir_txt'}</label>
+        <input name="crontab_dir" id="crontab_dir" value="{tmpl_var name='crontab_dir'}" size="40" maxlength="255" type="text" class="textInput" />
+            </div>
+      <div class="ctrlHolder">
+        <label for="wget">{tmpl_var name='wget_txt'}</label>
+        <input name="wget" id="wget" value="{tmpl_var name='wget'}" size="40" maxlength="255" type="text" class="textInput" />
+            </div>
+    </fieldset>
+
+    <input type="hidden" name="id" value="{tmpl_var name='id'}">
+
+    <div class="buttonHolder buttons">
+      <button class="positive iconstxt icoPositive" type="button" value="{tmpl_var name='btn_save_txt'}" onClick="submitForm('pageForm','admin/server_config_edit.php');"><span>{tmpl_var name='btn_save_txt'}</span></button>
+      <button class="negative iconstxt icoNegative" type="button" value="{tmpl_var name='btn_cancel_txt'}" onClick="loadContent('admin/server_config_list.php');"><span>{tmpl_var name='btn_cancel_txt'}</span></button>
+    </div>
+  </div>
+  
+</div>
diff --git a/interface/web/admin/templates/server_config_jailkit_edit.htm b/interface/web/admin/templates/server_config_jailkit_edit.htm
index ec2081de6e..320fb87542 100644
--- a/interface/web/admin/templates/server_config_jailkit_edit.htm
+++ b/interface/web/admin/templates/server_config_jailkit_edit.htm
@@ -17,6 +17,10 @@
       	<label for="jailkit_chroot_app_programs">{tmpl_var name='jailkit_chroot_app_programs_txt'}</label>
         <input name="jailkit_chroot_app_programs" id="jailkit_chroot_app_programs" value="{tmpl_var name='jailkit_chroot_app_programs'}" size="40" maxlength="1000" type="text" class="textInput" />
 			</div>
+      <div class="ctrlHolder">
+        <label for="jailkit_chroot_cron_programs">{tmpl_var name='jailkit_chroot_cron_programs_txt'}</label>
+        <input name="jailkit_chroot_cron_programs" id="jailkit_chroot_cron_programs" value="{tmpl_var name='jailkit_chroot_cron_programs'}" size="40" maxlength="1000" type="text" class="textInput" />
+            </div>
     </fieldset>
 
     <input type="hidden" name="id" value="{tmpl_var name='id'}">
diff --git a/interface/web/admin/templates/server_config_vlogger_edit.htm b/interface/web/admin/templates/server_config_vlogger_edit.htm
new file mode 100644
index 0000000000..bf6a82ab31
--- /dev/null
+++ b/interface/web/admin/templates/server_config_vlogger_edit.htm
@@ -0,0 +1,22 @@
+<h2><tmpl_var name="list_head_txt"></h2>
+<p><tmpl_var name="list_desc_txt"></p>
+
+<div class="panel panel_server_config">
+  
+  <div class="pnl_formsarea">
+    <fieldset class="inlineLabels">
+      <div class="ctrlHolder">
+      	<label for="config_dir">{tmpl_var name='config_dir_txt'}</label>
+        <input name="config_dir" id="config_dir" value="{tmpl_var name='config_dir'}" size="40" maxlength="255" type="text" class="textInput" />
+			</div>
+    </fieldset>
+
+    <input type="hidden" name="id" value="{tmpl_var name='id'}">
+
+    <div class="buttonHolder buttons">
+      <button class="positive iconstxt icoPositive" type="button" value="{tmpl_var name='btn_save_txt'}" onClick="submitForm('pageForm','admin/server_config_edit.php');"><span>{tmpl_var name='btn_save_txt'}</span></button>
+      <button class="negative iconstxt icoNegative" type="button" value="{tmpl_var name='btn_cancel_txt'}" onClick="loadContent('admin/server_config_list.php');"><span>{tmpl_var name='btn_cancel_txt'}</span></button>
+    </div>
+  </div>
+  
+</div>
diff --git a/interface/web/client/form/client.tform.php b/interface/web/client/form/client.tform.php
index 288e8939a1..4cc9dd57e6 100644
--- a/interface/web/client/form/client.tform.php
+++ b/interface/web/client/form/client.tform.php
@@ -663,6 +663,40 @@ $form["tabs"]['limits'] = array (
 			'rows'		=> '',
 			'cols'		=> ''
 		),
+        'limit_cron' => array (
+            'datatype'  => 'INTEGER',
+            'formtype'  => 'TEXT',
+            'validators'    => array (  0 => array (    'type'  => 'ISINT',
+                                                        'errmsg'=> 'limit_cron_error_notint'),
+                                    ),
+            'default'   => '0',
+            'value'     => '',
+            'separator' => '',
+            'width'     => '10',
+            'maxlength' => '10',
+            'rows'      => '',
+            'cols'      => ''
+        ),
+        'limit_cron_type' => array (
+            'datatype'  => 'VARCHAR',
+            'formtype'  => 'SELECT',
+            'default'   => '',
+            'value'     => array('full' => 'Full Cron','chrooted' => 'Chrooted Cron','url' => 'URL Cron')
+        ),
+        'limit_cron_frequency' => array (
+            'datatype'  => 'INTEGER',
+            'formtype'  => 'TEXT',
+            'validators'    => array (  0 => array (    'type'  => 'ISINT',
+                                                        'errmsg'=> 'limit_cron_error_frequency'),
+                                    ),
+            'default'   => '-1',
+            'value'     => '',
+            'separator' => '',
+            'width'     => '10',
+            'maxlength' => '10',
+            'rows'      => '',
+            'cols'      => ''
+        ),
 	##################################
 	# END Datatable fields
 	##################################
diff --git a/interface/web/client/form/client_template.tform.php b/interface/web/client/form/client_template.tform.php
index f732faf577..e1dd51dcd3 100644
--- a/interface/web/client/form/client_template.tform.php
+++ b/interface/web/client/form/client_template.tform.php
@@ -395,6 +395,40 @@ $form["tabs"]['limits'] = array (
 			'rows'		=> '',
 			'cols'		=> ''
 		),
+        'limit_cron' => array (
+            'datatype'  => 'INTEGER',
+            'formtype'  => 'TEXT',
+            'validators'    => array (  0 => array (    'type'  => 'ISINT',
+                                                        'errmsg'=> 'limit_cron_error_notint'),
+                                    ),
+            'default'   => '0',
+            'value'     => '',
+            'separator' => '',
+            'width'     => '10',
+            'maxlength' => '10',
+            'rows'      => '',
+            'cols'      => ''
+        ),
+        'limit_cron_type' => array (
+            'datatype'  => 'VARCHAR',
+            'formtype'  => 'SELECT',
+            'default'   => '',
+            'value'     => array('full' => 'Full Cron','chrooted' => 'Chrooted Cron','url' => 'URL Cron')
+        ),
+        'limit_cron_frequency' => array (
+            'datatype'  => 'INTEGER',
+            'formtype'  => 'TEXT',
+            'validators'    => array (  0 => array (    'type'  => 'ISINT',
+                                                        'errmsg'=> 'limit_cron_error_frequency'),
+                                    ),
+            'default'   => '-1',
+            'value'     => '',
+            'separator' => '',
+            'width'     => '10',
+            'maxlength' => '10',
+            'rows'      => '',
+            'cols'      => ''
+        ),
 	##################################
 	# END Datatable fields
 	##################################
diff --git a/interface/web/client/lib/lang/de_client.lng b/interface/web/client/lib/lang/de_client.lng
index 05fad56a60..d126fa846c 100644
--- a/interface/web/client/lib/lang/de_client.lng
+++ b/interface/web/client/lib/lang/de_client.lng
@@ -39,6 +39,9 @@ $wb['limit_domain_txt'] = 'Max. Anzahl an Domains';
 $wb['limit_subdomain_txt'] = 'Max. Anzahl an Subdomains';
 $wb['limit_webquota_txt'] = 'Max. Webquota';
 $wb['limit_database_txt'] = 'Max. Anzahl an Datenbanken';
+$wb["limit_cron_txt"] = 'Max. Anzahl Cron Jobs';
+$wb["limit_cron_type_txt"] = 'Max. erlaubter Typ von Cron Jobs (chrooted und full erlauben auch url)';
+$wb["limit_cron_frequency_txt"] = 'Min. Abstand zwischen Ausf&uuml;hrungen';
 $wb['ip_address_txt'] = 'IP Adresse';
 $wb['limit_client_error_notint'] = 'The sub client limit must be a number.';
 $wb['firstname_error_empty'] = 'Vorname ist leer.';
@@ -74,6 +77,8 @@ $wb['limit_shell_user_error_notint'] = 'Das Shell Benutzer Limit muss eine Numme
 $wb['limit_dns_zone_error_notint'] = 'Das DNS Einträge Limit muss eine Nummer sein.';
 $wb['default_dbserver_txt'] = 'Standarddatenbankserver';
 $wb['limit_database_error_notint'] = 'Das Datenbank Limit muss eine Nummer sein.';
+$wb["limit_cron_error_notint"] = 'Das Cron Job Limit muss eine Zahl sein.';
+$wb["limit_cron_error_frequency"] = 'Das Cron Job Intervall-Limit muss eine Zahl sein.';
 $wb['username_error_regex'] = 'Der Benutzername enthält ungültige Zeichen.';
 $wb['password_strength_txt'] = 'Passwortkomplexität';
 $wb['template_master_txt'] = 'Master';
diff --git a/interface/web/client/lib/lang/de_client_template.lng b/interface/web/client/lib/lang/de_client_template.lng
index 8a29d471fd..cdcce8d5a7 100644
--- a/interface/web/client/lib/lang/de_client_template.lng
+++ b/interface/web/client/lib/lang/de_client_template.lng
@@ -17,6 +17,9 @@ $wb['limit_domain_txt'] = 'Max. Anzahl an Domains';
 $wb['limit_subdomain_txt'] = 'Max. Anzahl an Subdomains';
 $wb['limit_webquota_txt'] = 'Max. Webquota';
 $wb['limit_database_txt'] = 'Max. Anzahl an Datenbanken';
+$wb["limit_cron_txt"] = 'Max. Anzahl Cron Jobs';
+$wb["limit_cron_type_txt"] = 'Max. erlaubter Typ von Cron Jobs (chrooted und full erlauben auch url)';
+$wb["limit_cron_frequency_txt"] = 'Min. Abstand zwischen Ausf&uuml;hrungen';
 $wb['limit_web_domain_txt'] = 'Max. Anzahl an Web Domains';
 $wb['limit_web_aliasdomain_txt'] = 'Max. Anzahl an Web Aliasdomains';
 $wb['limit_web_subdomain_txt'] = 'Max. Anzahl an Web Subdomains';
@@ -43,5 +46,7 @@ $wb['limit_ftp_user_error_notint'] = 'Das FTP Benutzer Limit muss eine Nummer s
 $wb['limit_shell_user_error_notint'] = 'Das Shell Benutzer Limit muss eine Nummer sein.';
 $wb['limit_dns_zone_error_notint'] = 'Das DNS Einträge Limit muss eine Nummer sein.';
 $wb['limit_database_error_notint'] = 'Das Datenbanken Limit muss eine Nummer sein.';
+$wb["limit_cron_error_notint"] = 'Das Cron Job Limit muss eine Zahl sein.';
+$wb["limit_cron_error_frequency"] = 'Das Cron Job Intervall-Limit muss eine Zahl sein.';
 $wb['error_template_name_empty'] = 'Bitte geben sie einen Vorlagenamen ein';
 ?>
diff --git a/interface/web/client/lib/lang/en_client.lng b/interface/web/client/lib/lang/en_client.lng
index 74c166fe8b..03cec3feee 100644
--- a/interface/web/client/lib/lang/en_client.lng
+++ b/interface/web/client/lib/lang/en_client.lng
@@ -39,6 +39,9 @@ $wb["limit_domain_txt"] = 'limit_domain';
 $wb["limit_subdomain_txt"] = 'limit_subdomain';
 $wb["limit_webquota_txt"] = 'limit_webquota';
 $wb["limit_database_txt"] = 'limit_database';
+$wb["limit_cron_txt"] = 'Max. number of cron jobs';
+$wb["limit_cron_type_txt"] = 'Max. type of cron jobs (chrooted and full implies url)';
+$wb["limit_cron_frequency_txt"] = 'Min. delay between executions';
 $wb["ip_address_txt"] = 'ip_address';
 $wb["limit_client_error_notint"] = 'Client Limit is not a number.';
 $wb["firstname_error_empty"] = 'Firstname is empty.';
@@ -78,6 +81,8 @@ $wb["limit_client_error_notint"] = 'The sub client limit must be a number.';
 $wb["default_dbserver_txt"] = 'Default Database Server';
 $wb["limit_database_txt"] = 'Max. number of Databases';
 $wb["limit_database_error_notint"] = 'The database limit must be a number.';
+$wb["limit_cron_error_notint"] = 'The cron limit must be a number.';
+$wb["limit_cron_error_frequency"] = 'The cron frequency limit must be a number.';
 $wb["username_error_regex"] = 'The Username contains invalid chracaters.';
 $wb["template_master_txt"] = 'Master template';
 $wb["template_additional_txt"] = 'Addon template';
diff --git a/interface/web/client/lib/lang/en_client_template.lng b/interface/web/client/lib/lang/en_client_template.lng
index c3453a8b57..6d9ee11154 100644
--- a/interface/web/client/lib/lang/en_client_template.lng
+++ b/interface/web/client/lib/lang/en_client_template.lng
@@ -17,6 +17,9 @@ $wb["limit_domain_txt"] = 'limit_domain';
 $wb["limit_subdomain_txt"] = 'limit_subdomain';
 $wb["limit_webquota_txt"] = 'limit_webquota';
 $wb["limit_database_txt"] = 'limit_database';
+$wb["limit_cron_txt"] = 'Max. number of cron jobs';
+$wb["limit_cron_type_txt"] = 'Max. type of cron jobs (chrooted and full implies url)';
+$wb["limit_cron_frequency_txt"] = 'Min. delay between executions';
 $wb["limit_web_domain_txt"] = 'Max. number of web domains';
 $wb["limit_web_aliasdomain_txt"] = 'Max. number of web aliasdomains';
 $wb["limit_web_subdomain_txt"] = 'Max. number of web subdomains';
@@ -46,5 +49,7 @@ $wb["limit_dns_zone_error_notint"] = 'The dns zone limit must be a number.';
 $wb["limit_dns_zone_error_notint"] = 'The dns record limit must be a number.';
 $wb["limit_database_txt"] = 'Max. number of Databases';
 $wb["limit_database_error_notint"] = 'The database limit must be a number.';
+$wb["limit_cron_error_notint"] = 'The cron limit must be a number.';
+$wb["limit_cron_error_frequency"] = 'The cron frequency limit must be a number.';
 $wb["error_template_name_empty"] = 'Please enter a Template name';
 ?>
\ No newline at end of file
diff --git a/interface/web/client/templates/client_edit_limits.htm b/interface/web/client/templates/client_edit_limits.htm
index e12abe94d4..208514164f 100644
--- a/interface/web/client/templates/client_edit_limits.htm
+++ b/interface/web/client/templates/client_edit_limits.htm
@@ -155,6 +155,20 @@
       	<label for="limit_database">{tmpl_var name='limit_database_txt'}</label>
         <input name="limit_database" id="limit_database" value="{tmpl_var name='limit_database'}" size="10" maxlength="10" type="text" class="textInput formLengthLimit" />
 			</div>
+      <div class="ctrlHolder">
+        <label for="limit_cron">{tmpl_var name='limit_cron_txt'}</label>
+        <input name="limit_cron" id="limit_cron" value="{tmpl_var name='limit_cron'}" size="10" maxlength="10" type="text" class="textInput formLengthLimit" />
+            </div>
+      <div class="ctrlHolder">
+        <label for="limit_cron_type">{tmpl_var name='limit_cron_type_txt'}</label>
+        <select name="limit_cron_type" id="limit_cron_type" class="selectInput formLengthHalf">
+          {tmpl_var name='limit_cron_type'}
+        </select>
+            </div>
+      <div class="ctrlHolder">
+        <label for="limit_cron_frequency">{tmpl_var name='limit_cron_frequency_txt'}</label>
+        <input name="limit_cron_frequency" id="limit_cron_frequency" value="{tmpl_var name='limit_cron_frequency'}" size="10" maxlength="10" type="text" class="textInput formLengthLimit" />
+            </div>
     </fieldset>
 
     <input type="hidden" name="id" value="{tmpl_var name='id'}">
diff --git a/interface/web/client/templates/client_template_edit_limits.htm b/interface/web/client/templates/client_template_edit_limits.htm
index bd01dcec04..2a4374cbfb 100644
--- a/interface/web/client/templates/client_template_edit_limits.htm
+++ b/interface/web/client/templates/client_template_edit_limits.htm
@@ -89,6 +89,20 @@
       	<label for="limit_database">{tmpl_var name='limit_database_txt'}</label>
         <input name="limit_database" id="limit_database" value="{tmpl_var name='limit_database'}" size="10" maxlength="10" type="text" class="textInput formLengthLimit" />
 			</div>
+      <div class="ctrlHolder">
+        <label for="limit_cron">{tmpl_var name='limit_cron_txt'}</label>
+        <input name="limit_cron" id="limit_cron" value="{tmpl_var name='limit_cron'}" size="10" maxlength="10" type="text" class="textInput formLengthLimit" />
+            </div>
+      <div class="ctrlHolder">
+        <label for="limit_cron_type">{tmpl_var name='limit_cron_type_txt'}</label>
+        <select name="limit_cron_type" id="limit_cron_type" class="selectInput formLengthHalf">
+          {tmpl_var name='limit_cron_type'}
+        </select>
+            </div>
+      <div class="ctrlHolder">
+        <label for="limit_cron_frequency">{tmpl_var name='limit_cron_frequency_txt'}</label>
+        <input name="limit_cron_frequency" id="limit_cron_frequency" value="{tmpl_var name='limit_cron_frequency'}" size="10" maxlength="10" type="text" class="textInput formLengthLimit" />
+            </div>
     </fieldset>
 
     <input type="hidden" name="id" value="{tmpl_var name='id'}">
diff --git a/interface/web/sites/cron_del.php b/interface/web/sites/cron_del.php
new file mode 100644
index 0000000000..516b4473b9
--- /dev/null
+++ b/interface/web/sites/cron_del.php
@@ -0,0 +1,64 @@
+<?php
+
+/*
+Copyright (c) 2008, Till Brehm, projektfarm Gmbh
+Modified 2009, Marius Cramer, pixcept KG
+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.
+*/
+
+/******************************************
+* Begin Form configuration
+******************************************/
+
+$list_def_file = "list/cron.list.php";
+$tform_def_file = "form/cron.tform.php";
+
+/******************************************
+* End Form configuration
+******************************************/
+
+require_once('../../lib/config.inc.php');
+require_once('../../lib/app.inc.php');
+
+//* Check permissions for module
+$app->auth->check_module_permissions('sites');
+
+$app->uses('tpl,tform,tform_actions');
+$app->load('tform_actions');
+
+class page_action extends tform_actions {
+
+	function onBeforeDelete() {
+		global $app; $conf;
+		
+		if($app->tform->checkPerm($this->id,'d') == false) $app->error($app->lng('error_no_delete_permission'));
+	}
+}
+
+$page = new page_action;
+$page->onDelete();
+
+?>
\ No newline at end of file
diff --git a/interface/web/sites/cron_edit.php b/interface/web/sites/cron_edit.php
new file mode 100644
index 0000000000..1f0a025b50
--- /dev/null
+++ b/interface/web/sites/cron_edit.php
@@ -0,0 +1,226 @@
+<?php
+
+/*
+Copyright (c) 2007, Till Brehm, projektfarm Gmbh
+Modified 2009, Marius Cramer, pixcept KG
+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.
+*/
+
+/******************************************
+* Begin Form configuration
+******************************************/
+
+$tform_def_file = "form/cron.tform.php";
+
+/******************************************
+* End Form configuration
+******************************************/
+
+require_once('../../lib/config.inc.php');
+require_once('../../lib/app.inc.php');
+
+//* Check permissions for module
+$app->auth->check_module_permissions('sites');
+
+// Loading classes
+$app->uses('tpl,tform,tform_actions,validate_cron');
+$app->load('tform_actions');
+
+class page_action extends tform_actions {
+	
+	function onShowNew() {
+		global $app, $conf;
+		
+		// we will check only users, not admins
+		if($_SESSION["s"]["user"]["typ"] == 'user') {
+			
+			// Get the limits of the client
+			$client_group_id = $_SESSION["s"]["user"]["default_group"];
+			$client = $app->db->queryOneRecord("SELECT limit_cron FROM sys_group, client WHERE sys_group.client_id = client.client_id and sys_group.groupid = $client_group_id");
+			
+			// Check if the user may add another cron job.
+			if($client["limit_cron"] >= 0) {
+				$tmp = $app->db->queryOneRecord("SELECT count(id) as number FROM cron WHERE sys_groupid = $client_group_id");
+				if($tmp["number"] >= $client["limit_cron"]) {
+					$app->error($app->tform->wordbook["limit_cron_txt"]);
+				}
+			}
+		}
+		
+		parent::onShowNew();
+	}
+	
+	function onShowEnd() {
+		global $app, $conf;
+		
+        if($this->id > 0) {
+            //* we are editing a existing record
+            $app->tpl->setVar("edit_disabled", 1);
+            $app->tpl->setVar("parent_domain_id_value", $this->dataRecord["parent_domain_id"]);
+        } else {
+            $app->tpl->setVar("edit_disabled", 0);
+        }
+		
+		parent::onShowEnd();
+	}
+	
+	function onSubmit() {
+		global $app, $conf;
+		
+		if($_SESSION["s"]["user"]["typ"] != 'admin') {
+			// Get the limits of the client
+			$client_group_id = $_SESSION["s"]["user"]["default_group"];
+			$client = $app->db->queryOneRecord("SELECT limit_cron, limit_cron_type FROM sys_group, client WHERE sys_group.client_id = client.client_id and sys_group.groupid = $client_group_id");
+		
+			// When the record is updated
+			if($this->id > 0) {
+			// When the record is inserted
+			} else {
+				// Check if the user may add another cron job.
+				if($client["limit_cron"] >= 0) {
+					$tmp = $app->db->queryOneRecord("SELECT count(id) as number FROM cron WHERE sys_groupid = $client_group_id");
+					if($tmp["number"] >= $client["limit_cron"]) {
+						$app->error($app->tform->wordbook["limit_cron_txt"]);
+					}
+				}
+			}
+		}
+		
+        // Get the record of the parent domain
+        $parent_domain = $app->db->queryOneRecord("select * FROM web_domain WHERE domain_id = ".intval(@$this->dataRecord["parent_domain_id"]));
+        
+        // Set fixed values
+        $this->dataRecord["server_id"] = $parent_domain["server_id"];
+
+        //* get type of command
+        $command = $this->dataRecord["command"];
+        if(preg_match("'^http(s)?:\/\/'i", $command)) {
+            $this->dataRecord["type"] = 'url';
+        } else {
+            $domain_owner = $app->db->queryOneRecord("SELECT limit_cron_type FROM sys_group, client WHERE sys_group.client_id = client.client_id and sys_group.groupid = ".intval($parent_domain["sys_groupid"]));
+            if($domain_owner["limit_cron_type"] == 'full') $this->dataRecord["type"] = 'full';
+            else $this->dataRecord["type"] = 'chrooted';
+        }
+        
+        parent::onSubmit();
+	}
+	
+    function onUpdateSave($sql) {
+        global $app;
+        
+        $has_error = false;
+        //* last chance to stop this, so check frequency limit!
+        if($_SESSION["s"]["user"]["typ"] != 'admin') {
+            // Get the limits of the client
+            $client_group_id = $_SESSION["s"]["user"]["default_group"];
+            $client = $app->db->queryOneRecord("SELECT limit_cron_frequency FROM sys_group, client WHERE sys_group.client_id = client.client_id and sys_group.groupid = $client_group_id");
+        
+            if($client["limit_cron_frequency"] > 1) {
+                if($app->tform->cron_min_freq < $client["limit_cron_frequency"]) {
+                    $app->error($app->tform->wordbook["limit_cron_frequency_txt"]);
+                    $has_error = true;
+                }
+            }
+        }
+        
+        if($has_error == true) {
+            parent::onError();
+            exit;
+        }
+        else parent::onUpdateSave($sql);
+    }
+    
+    function onInsertSave($sql) {
+        global $app;
+    
+        $has_error = false;
+        //* last chance to stop this, so check frequency limit!
+        if($_SESSION["s"]["user"]["typ"] != 'admin') {
+            // Get the limits of the client
+            $client_group_id = $_SESSION["s"]["user"]["default_group"];
+            $client = $app->db->queryOneRecord("SELECT limit_cron_frequency FROM sys_group, client WHERE sys_group.client_id = client.client_id and sys_group.groupid = $client_group_id");
+        
+            if($client["limit_cron_frequency"] > 1) {
+                if($app->tform->cron_min_freq < $client["limit_cron_frequency"]) {
+                    $app->error($app->tform->wordbook["limit_cron_frequency_txt"]);
+                    $has_error = true;
+                }
+            }
+        }
+        
+        if($has_error == true) {
+            parent::onError();
+            exit;
+        }
+        else parent::onInsertSave($sql);
+    }
+    
+	function onAfterInsert() {
+		global $app, $conf;
+		
+        $web = $app->db->queryOneRecord("SELECT * FROM web_domain WHERE domain_id = ".intval($this->dataRecord["parent_domain_id"]));
+        $server_id = $web["server_id"];
+        
+        // The cron shall be owned by the same group then the website
+        $sys_groupid = $web['sys_groupid'];
+        
+        $sql = "UPDATE shell_user SET server_id = $server_id, sys_groupid = '$sys_groupid' WHERE id = ".$this->id;
+        $app->db->query($sql);
+	}
+	
+	function onAfterUpdate() {
+		global $app, $conf;
+		
+		
+	}
+    
+    function getClientName() {
+        global $app, $conf;
+    
+        if($_SESSION["s"]["user"]["typ"] != 'admin') {
+            // Get the group-id of the user
+            $client_group_id = $_SESSION["s"]["user"]["default_group"];
+        } else {
+            // Get the group-id from the data itself
+            $web = $app->db->queryOneRecord("SELECT sys_groupid FROM web_domain WHERE domain_id = ".intval($this->dataRecord['parent_domain_id']));
+            $client_group_id = $web['sys_groupid'];
+        }
+        /* get the name of the client */
+        $tmp = $app->db->queryOneRecord("SELECT name FROM sys_group WHERE groupid = " . $client_group_id);
+        $clientName = $tmp['name'];
+        if ($clientName == "") $clientName = 'default';
+        $clientName = convertClientName($clientName);
+        
+        return $clientName;
+    
+    }
+	
+}
+
+$page = new page_action;
+$page->onLoad();
+
+?>
\ No newline at end of file
diff --git a/interface/web/sites/cron_list.php b/interface/web/sites/cron_list.php
new file mode 100644
index 0000000000..68e23163f2
--- /dev/null
+++ b/interface/web/sites/cron_list.php
@@ -0,0 +1,23 @@
+<?php
+require_once('../../lib/config.inc.php');
+require_once('../../lib/app.inc.php');
+
+/******************************************
+* Begin Form configuration
+******************************************/
+
+$list_def_file = "list/cron.list.php";
+
+/******************************************
+* End Form configuration
+******************************************/
+
+//* Check permissions for module
+$app->auth->check_module_permissions('sites');
+
+$app->uses('listform_actions');
+
+$app->listform_actions->onLoad();
+
+
+?>
\ No newline at end of file
diff --git a/interface/web/sites/form/cron.tform.php b/interface/web/sites/form/cron.tform.php
new file mode 100644
index 0000000000..4f9f8dd5ba
--- /dev/null
+++ b/interface/web/sites/form/cron.tform.php
@@ -0,0 +1,189 @@
+<?php
+
+/*
+	Form Definition
+
+	Tabledefinition
+
+	Datatypes:
+	- INTEGER (Forces the input to Int)
+	- DOUBLE
+	- CURRENCY (Formats the values to currency notation)
+	- VARCHAR (no format check, maxlength: 255)
+	- TEXT (no format check)
+	- DATE (Dateformat, automatic conversion to timestamps)
+
+	Formtype:
+	- TEXT (Textfield)
+	- TEXTAREA (Textarea)
+	- PASSWORD (Password textfield, input is not shown when edited)
+	- SELECT (Select option field)
+	- RADIO
+	- CHECKBOX
+	- CHECKBOXARRAY
+	- FILE
+
+	VALUE:
+	- Wert oder Array
+
+	Hint:
+	The ID field of the database table is not part of the datafield definition.
+	The ID field must be always auto incement (int or bigint).
+
+
+*/
+
+$form["title"] 			= "Cron Job";
+$form["description"] 	= "";
+$form["name"] 			= "cron";
+$form["action"]			= "cron_edit.php";
+$form["db_table"]		= "cron";
+$form["db_table_idx"]	= "id";
+$form["db_history"]		= "yes";
+$form["tab_default"]	= "cron";
+$form["list_default"]	= "cron_list.php";
+$form["auth"]			= 'yes'; // yes / no
+
+$form["auth_preset"]["userid"]  = 0; // 0 = id of the user, > 0 id must match with id of current user
+$form["auth_preset"]["groupid"] = 0; // 0 = default groupid of the user, > 0 id must match with groupid of current user
+$form["auth_preset"]["perm_user"] = 'riud'; //r = read, i = insert, u = update, d = delete
+$form["auth_preset"]["perm_group"] = 'riud'; //r = read, i = insert, u = update, d = delete
+$form["auth_preset"]["perm_other"] = ''; //r = read, i = insert, u = update, d = delete
+
+$form["tabs"]['cron'] = array (
+	'title' 	=> "Cron Job",
+	'width' 	=> 100,
+	'template' 	=> "templates/cron_edit.htm",
+	'fields' 	=> array (
+	##################################
+	# Begin Datatable fields
+	##################################
+		'server_id' => array (
+			'datatype'	=> 'INTEGER',
+			'formtype'	=> 'SELECT',
+			'datasource'	=> array ( 	'type'	=> 'SQL',
+										'querystring' => 'SELECT server_id,server_name FROM server WHERE web_server = 1 AND {AUTHSQL} ORDER BY server_name',
+										'keyfield'=> 'server_id',
+										'valuefield'=> 'server_name'
+									 ),
+			'validators'	=> array ( 	0 => array (	'type'	=> 'NOTEMPTY',
+														'errmsg'=> 'server_id_error_empty'),
+									),
+			'default'	=> '',
+			'value'		=> '',
+			'width'		=> '30',
+			'maxlength'	=> '255'
+		),
+        'parent_domain_id' => array (
+            'datatype'  => 'INTEGER',
+            'formtype'  => 'SELECT',
+            'default'   => '',
+            'datasource'    => array (  'type'  => 'SQL',
+                                        'querystring' => "SELECT domain_id,domain FROM web_domain WHERE type = 'vhost' AND {AUTHSQL} ORDER BY domain",
+                                        'keyfield'=> 'domain_id',
+                                        'valuefield'=> 'domain'
+                                     ),
+            'value'     => ''
+        ),
+        'run_min' => array (
+            'datatype'  => 'VARCHAR',
+            'formtype'  => 'TEXT',
+            'validators'    => array (  0 => array (    'type'  => 'CUSTOM',
+                                                        'class' => 'validate_cron',
+                                                        'function' => 'run_time_format',
+                                                        'errmsg'=> 'run_min_error_format'),
+                                    ),
+            'default'   => '',
+            'value'     => '',
+            'width'     => '30',
+            'maxlength' => '255'
+        ),
+        'run_hour' => array (
+            'datatype'  => 'VARCHAR',
+            'formtype'  => 'TEXT',
+            'validators'    => array (  0 => array (    'type'  => 'CUSTOM',
+                                                        'class' => 'validate_cron',
+                                                        'function' => 'run_time_format',
+                                                        'errmsg'=> 'run_hour_error_format'),
+                                    ),
+            'default'   => '',
+            'value'     => '',
+            'width'     => '30',
+            'maxlength' => '255'
+        ),
+        'run_mday' => array (
+            'datatype'  => 'VARCHAR',
+            'formtype'  => 'TEXT',
+            'validators'    => array (  0 => array (    'type'  => 'CUSTOM',
+                                                        'class' => 'validate_cron',
+                                                        'function' => 'run_time_format',
+                                                        'errmsg'=> 'run_mday_error_format'),
+                                    ),
+            'default'   => '',
+            'value'     => '',
+            'width'     => '30',
+            'maxlength' => '255'
+        ),
+        'run_month' => array (
+            'datatype'  => 'VARCHAR',
+            'formtype'  => 'TEXT',
+            'validators'    => array (  0 => array (    'type'  => 'CUSTOM',
+                                                        'class' => 'validate_cron',
+                                                        'function' => 'run_time_format',
+                                                        'errmsg'=> 'run_month_error_format'),
+                                    ),
+            'default'   => '',
+            'value'     => '',
+            'width'     => '30',
+            'maxlength' => '255'
+        ),
+        'run_wday' => array (
+            'datatype'  => 'VARCHAR',
+            'formtype'  => 'TEXT',
+            'validators'    => array (  0 => array (    'type'  => 'CUSTOM',
+                                                        'class' => 'validate_cron',
+                                                        'function' => 'run_time_format',
+                                                        'errmsg'=> 'run_wday_error_format'),
+                                    ),
+            'default'   => '',
+            'value'     => '',
+            'width'     => '30',
+            'maxlength' => '255'
+        ),
+		'command' => array (
+			'datatype'	=> 'VARCHAR',
+			'formtype'	=> 'TEXT',
+			'validators'	=> array ( 	0 => array (	'type'	=> 'NOTEMPTY',
+														'errmsg'=> 'command_error_empty'),
+                                        1 => array (    'type'  => 'CUSTOM',
+                                                        'class' => 'validate_cron',
+                                                        'function' => 'command_format',
+                                                        'errmsg'=> 'command_error_format'),
+									),
+			'default'	=> '',
+			'value'		=> '',
+			'width'		=> '30',
+			'maxlength'	=> '255'
+		),
+        'type' => array (
+            'datatype'  => 'VARCHAR',
+            'formtype'  => 'SELECT',
+            'default'   => 'url',
+            'valuelimit' => 'list:url,full,chrooted',
+            'value'     => array('url' => 'Url', 'full' => 'Full', 'chrooted' => 'Chrooted')
+        ),
+		'active' => array (
+			'datatype'	=> 'VARCHAR',
+			'formtype'	=> 'CHECKBOX',
+			'default'	=> 'y',
+			'value'		=> array(0 => 'n',1 => 'y')
+		),
+	##################################
+	# ENDE Datatable fields
+	##################################
+	)
+);
+
+
+
+?>
\ No newline at end of file
diff --git a/interface/web/sites/lib/lang/de_cron.lng b/interface/web/sites/lib/lang/de_cron.lng
new file mode 100644
index 0000000000..c65bf38be8
--- /dev/null
+++ b/interface/web/sites/lib/lang/de_cron.lng
@@ -0,0 +1,21 @@
+<?php
+$wb['server_id_txt'] = 'Server';
+$wb['parent_domain_id_txt'] = 'Zugeordnete Website';
+$wb['active_txt'] = 'Aktiv';
+$wb['client_txt'] = 'Kunde';
+$wb['run_min_txt'] = 'Minuten';
+$wb['run_hour_txt'] = 'Stunden';
+$wb['run_mday_txt'] = 'Tage des Monats';
+$wb['run_month_txt'] = 'Monate';
+$wb['run_wday_txt'] = 'Tage der Woche';
+$wb['command_txt'] = 'Auszuf&uuml;hrender Befehl (Befehle werden mit sh ausgef&uuml;hrt, urls mit wget)';
+$wb['limit_cron_txt'] = 'Die maximale Anzahl von erlaubten Cron Jobs ist bereits erreicht.';
+$wb['limit_cron_frequency_txt'] = 'Die Ausf&uuml;hrungsh&auml;ufigkeit &uuml;bersteigt Ihr erlaubtes Limit.';
+$wb['run_min_error_format'] = 'Das Format f&uuml;r Minuten ist nicht korrekt.';
+$wb['run_hour_error_format'] = 'Das Format f&uuml;r Stunden ist nicht korrekt.';
+$wb['run_mday_error_format'] = 'Das Format f&uuml;r Tage des Monats ist nicht korrekt.';
+$wb['run_month_error_format'] = 'Das Format f&uuml;r Monate ist nicht korrekt.';
+$wb['run_wday_error_format'] = 'Das Format f&uuml;r Wochentage ist nicht korrekt.';
+$wb['command_error_format'] = 'Das Format f&uuml;r den Befehl ist nicht korrekt. Beachte, dass bei einem URL Aufruf nur http und https erlaubt ist.';
+$wb['unknown_fieldtype_error'] = 'Es wurde ein unbekanntes Feld verwendet.';
+?>
diff --git a/interface/web/sites/lib/lang/de_cron_list.lng b/interface/web/sites/lib/lang/de_cron_list.lng
new file mode 100644
index 0000000000..4f20d8d832
--- /dev/null
+++ b/interface/web/sites/lib/lang/de_cron_list.lng
@@ -0,0 +1,12 @@
+<?php
+$wb['list_head_txt'] = 'Cron Jobs';
+$wb['active_txt'] = 'Activ';
+$wb['server_id_txt'] = 'Server';
+$wb['run_min_txt'] = 'Minute';
+$wb["run_hour_txt"] = 'Stunde';
+$wb["run_mday_txt"] = 'Tag des Monats';
+$wb["run_month_txt"] = 'Monat';
+$wb["run_wday_txt"] = 'Tag der Woche';
+$wb['command_txt'] = 'Befehl';
+$wb['add_new_cron_txt'] = 'Neuen Cron Job anlegen';
+?>
diff --git a/interface/web/sites/lib/lang/en_cron.lng b/interface/web/sites/lib/lang/en_cron.lng
new file mode 100644
index 0000000000..8cb7f08e6d
--- /dev/null
+++ b/interface/web/sites/lib/lang/en_cron.lng
@@ -0,0 +1,21 @@
+<?php
+$wb["server_id_txt"] = 'Server';
+$wb['parent_domain_id_txt'] = 'Parent website';
+$wb['active_txt'] = 'Active';
+$wb['client_txt'] = 'Client';
+$wb['run_min_txt'] = 'Minutes';
+$wb['run_hour_txt'] = 'Hours';
+$wb['run_mday_txt'] = 'Days of month';
+$wb['run_month_txt'] = 'Months';
+$wb['run_wday_txt'] = 'Days of week';
+$wb['command_txt'] = 'Command to run (commands are executed via sh, urls via wget)';
+$wb['limit_cron_txt'] = 'The maximum number of allowed cron jobs was reached.';
+$wb['limit_cron_frequency_txt'] = 'The cron job frequency exceeds the allowed limit.';
+$wb['run_min_error_format'] = 'Invalid format for minutes.';
+$wb['run_hour_error_format'] = 'Invalid format for hours.';
+$wb['run_mday_error_format'] = 'Invalid format for days of month.';
+$wb['run_month_error_format'] = 'Invalid format for months.';
+$wb['run_wday_error_format'] = 'Invalid format for days of the week.';
+$wb['command_error_format'] = 'Invalid command format. Please note that in case of an url call only http/https is allowed.';
+$wb['unknown_fieldtype_error'] = 'An unknown field type has been used.';
+?>
\ No newline at end of file
diff --git a/interface/web/sites/lib/lang/en_cron_list.lng b/interface/web/sites/lib/lang/en_cron_list.lng
new file mode 100644
index 0000000000..b5c8d1566e
--- /dev/null
+++ b/interface/web/sites/lib/lang/en_cron_list.lng
@@ -0,0 +1,12 @@
+<?php
+$wb["list_head_txt"] = 'Cron Jobs';
+$wb["active_txt"] = 'Active';
+$wb["server_id_txt"] = 'Server';
+$wb["run_min_txt"] = 'Minute';
+$wb["run_hour_txt"] = 'Hour';
+$wb["run_mday_txt"] = 'Day of month';
+$wb["run_month_txt"] = 'Month';
+$wb["run_wday_txt"] = 'Day of week';
+$wb["command_txt"] = 'Command';
+$wb["add_new_cron_txt"] = 'Add new Cron job';
+?>
\ No newline at end of file
diff --git a/interface/web/sites/lib/module.conf.php b/interface/web/sites/lib/module.conf.php
index 6a437e8276..3207cafdc2 100644
--- a/interface/web/sites/lib/module.conf.php
+++ b/interface/web/sites/lib/module.conf.php
@@ -77,6 +77,21 @@ $module["nav"][] = array(	'title'	=> 'Database',
 							'items'	=> $items);
 
 
+/*
+    Cron menu
+*/
+$items = array();
+
+$items[] = array( 'title'   => "Cron Jobs",
+                  'target'  => 'content',
+                  'link'    => 'sites/cron_list.php');
+
+
+$module["nav"][] = array(   'title' => 'Cron',
+                            'open'  => 1,
+                            'items' => $items);
+
+
 //**** Statistics menu
 $items = array();
 
diff --git a/interface/web/sites/list/cron.list.php b/interface/web/sites/list/cron.list.php
new file mode 100644
index 0000000000..dfa2d4b353
--- /dev/null
+++ b/interface/web/sites/list/cron.list.php
@@ -0,0 +1,152 @@
+<?php
+
+/*
+	Datatypes:
+	- INTEGER
+	- DOUBLE
+	- CURRENCY
+	- VARCHAR
+	- TEXT
+	- DATE
+*/
+
+
+
+// Name of the list
+$liste["name"] 				= "cron";
+
+// Database table
+$liste["table"] 			= "cron";
+
+// Index index field of the database table
+$liste["table_idx"]			= "id";
+
+// Search Field Prefix
+$liste["search_prefix"] 	= "search_";
+
+// Records per page
+$liste["records_per_page"] 	= 15;
+
+// Script File of the list
+$liste["file"]				= "cron_list.php";
+
+// Script file of the edit form
+$liste["edit_file"]			= "cron_edit.php";
+
+// Script File of the delete script
+$liste["delete_file"]		= "cron_del.php";
+
+// Paging Template
+$liste["paging_tpl"]		= "templates/paging.tpl.htm";
+
+// Enable auth
+$liste["auth"]				= "yes";
+
+
+/*****************************************************
+* Suchfelder
+*****************************************************/
+
+
+$liste["item"][] = array(	'field'		=> "active",
+							'datatype'	=> "VARCHAR",
+							'formtype'	=> "SELECT",
+							'op'		=> "=",
+							'prefix'	=> "",
+							'suffix'	=> "",
+							'width'		=> "",
+							'value'		=> array('y' => "<div id=\"ir-Yes\" class=\"swap\"><span>Yes</span></div>",'n' => "<div class=\"swap\" id=\"ir-No\"><span>No</span></div>"));
+
+
+$liste["item"][] = array(	'field'		=> "server_id",
+							'datatype'	=> "VARCHAR",
+							'formtype'	=> "SELECT",
+							'op'		=> "like",
+							'prefix'	=> "%",
+							'suffix'	=> "%",
+							'datasource'	=> array ( 	'type'	=> 'SQL',
+														'querystring' => 'SELECT server_id,server_name FROM server WHERE {AUTHSQL} ORDER BY server_name',
+														'keyfield'=> 'server_id',
+														'valuefield'=> 'server_name'
+									 				  ),
+							'width'		=> "",
+							'value'		=> "");
+
+$liste["item"][] = array(   'field'     => "parent_domain_id",
+                            'datatype'  => "VARCHAR",
+                            'formtype'  => "SELECT",
+                            'op'        => "like",
+                            'prefix'    => "%",
+                            'suffix'    => "%",
+                            'datasource'    => array (  'type'  => 'SQL',
+                                        'querystring' => "SELECT domain_id,domain FROM web_domain WHERE type = 'vhost' AND {AUTHSQL} ORDER BY domain",
+                                        'keyfield'=> 'domain_id',
+                                        'valuefield'=> 'domain'
+                                     ),
+                            'width'     => "",
+                            'value'     => "");
+
+$liste["item"][] = array(   'field'     => "run_min",
+                            'datatype'  => "VARCHAR",
+                            'formtype'  => "TEXT",
+                            'op'        => "=",
+                            'prefix'    => "",
+                            'suffix'    => "",
+                            'width'     => "",
+                            'value'     => "");
+
+$liste["item"][] = array(   'field'     => "run_hour",
+                            'datatype'  => "VARCHAR",
+                            'formtype'  => "TEXT",
+                            'op'        => "=",
+                            'prefix'    => "",
+                            'suffix'    => "",
+                            'width'     => "",
+                            'value'     => "");
+
+$liste["item"][] = array(   'field'     => "run_mday",
+                            'datatype'  => "VARCHAR",
+                            'formtype'  => "TEXT",
+                            'op'        => "=",
+                            'prefix'    => "",
+                            'suffix'    => "",
+                            'width'     => "",
+                            'value'     => "");
+
+$liste["item"][] = array(   'field'     => "run_month",
+                            'datatype'  => "VARCHAR",
+                            'formtype'  => "TEXT",
+                            'op'        => "=",
+                            'prefix'    => "",
+                            'suffix'    => "",
+                            'width'     => "",
+                            'value'     => "");
+
+$liste["item"][] = array(   'field'     => "run_wday",
+                            'datatype'  => "VARCHAR",
+                            'formtype'  => "TEXT",
+                            'op'        => "=",
+                            'prefix'    => "",
+                            'suffix'    => "",
+                            'width'     => "",
+                            'value'     => "");
+
+
+$liste["item"][] = array(	'field'		=> "command",
+							'datatype'	=> "VARCHAR",
+							'formtype'	=> "TEXT",
+							'op'		=> "like",
+							'prefix'	=> "%",
+							'suffix'	=> "%",
+							'width'		=> "",
+							'value'		=> "");
+
+
+
+
+
+
+
+
+
+?>
\ No newline at end of file
diff --git a/interface/web/sites/templates/cron_edit.htm b/interface/web/sites/templates/cron_edit.htm
new file mode 100644
index 0000000000..39f9bc6469
--- /dev/null
+++ b/interface/web/sites/templates/cron_edit.htm
@@ -0,0 +1,68 @@
+<h2><tmpl_var name="list_head_txt"></h2>
+<p><tmpl_var name="list_desc_txt"></p>
+
+<div class="panel panel_cron">
+
+  <div class="pnl_formsarea">
+    <fieldset class="inlineLabels"><legend>Cron Job</legend>
+      <div class="ctrlHolder">
+        <tmpl_if name="edit_disabled">
+        <label for="parent_domain_id">{tmpl_var name='parent_domain_id_txt'}</label>
+        <select name="parent_domain_id" id="parent_domain_id" class="selectInput" disabled="disabled">
+                    {tmpl_var name='parent_domain_id'}
+        </select>
+        <input type="hidden" name="parent_domain_id" value="{tmpl_var name='parent_domain_id_value'}" />
+        <tmpl_else>
+        <label for="parent_domain_id">{tmpl_var name='parent_domain_id_txt'}</label>
+        <select name="parent_domain_id" id="parent_domain_id" class="selectInput">
+                    {tmpl_var name='parent_domain_id'}
+        </select>
+        </tmpl_if>
+      </div>
+      <div class="ctrlHolder">
+        <label for="run_min">{tmpl_var name='run_min_txt'}</label>
+        <input name="run_min" id="run_min" value="{tmpl_var name='run_min'}" size="10" maxlength="255" type="text" class="textInput" />
+                <p class="formHint">e.g. *, */3, 10-20</p>
+            </div>
+      <div class="ctrlHolder">
+        <label for="run_hour">{tmpl_var name='run_hour_txt'}</label>
+        <input name="run_hour" id="run_hour" value="{tmpl_var name='run_hour'}" size="10" maxlength="255" type="text" class="textInput" />
+                <p class="formHint">e.g. *, */2, 0, 10-12</p>
+            </div>
+      <div class="ctrlHolder">
+        <label for="run_mday">{tmpl_var name='run_mday_txt'}</label>
+        <input name="run_mday" id="run_mday" value="{tmpl_var name='run_mday'}" size="10" maxlength="255" type="text" class="textInput" />
+                <p class="formHint">e.g. *, */4, 1-5</p>
+            </div>
+      <div class="ctrlHolder">
+        <label for="run_month">{tmpl_var name='run_month_txt'}</label>
+        <input name="run_month" id="run_month" value="{tmpl_var name='run_month'}" size="10" maxlength="255" type="text" class="textInput" />
+                <p class="formHint">e.g. *, 1-6</p>
+            </div>
+      <div class="ctrlHolder">
+        <label for="run_wday">{tmpl_var name='run_wday_txt'}</label>
+        <input name="run_wday" id="run_wday" value="{tmpl_var name='run_wday'}" size="10" maxlength="255" type="text" class="textInput" />
+                <p class="formHint">e.g. *, 0, 1-5</p>
+            </div>
+      <div class="ctrlHolder">
+      	<label for="command">{tmpl_var name='command_txt'}</label>
+        <input name="command" id="command" value="{tmpl_var name='command'}" size="30" maxlength="255" type="text" class="textInput" />
+				<p class="formHint">e.g. /var/www/clients/client1/myscript.sh or http://www.mydomain.com/path/script.php</p>
+			</div>
+      <div class="ctrlHolder">
+				<p class="label">{tmpl_var name='active_txt'}</p>
+					<div class="multiField">
+						{tmpl_var name='active'}
+					</div>
+			</div>
+    </fieldset>
+
+    <input type="hidden" name="id" value="{tmpl_var name='id'}">
+    
+    <div class="buttonHolder buttons">
+      <button class="positive iconstxt icoPositive" type="button" value="{tmpl_var name='btn_save_txt'}" onClick="submitForm('pageForm','sites/cron_edit.php');"><span>{tmpl_var name='btn_save_txt'}</span></button>
+      <button class="negative iconstxt icoNegative" type="button" value="{tmpl_var name='btn_cancel_txt'}" onClick="loadContent('sites/cron_list.php');"><span>{tmpl_var name='btn_cancel_txt'}</span></button>
+    </div>
+  </div>
+  
+</div>
diff --git a/interface/web/sites/templates/cron_list.htm b/interface/web/sites/templates/cron_list.htm
new file mode 100644
index 0000000000..e2e8ca9d55
--- /dev/null
+++ b/interface/web/sites/templates/cron_list.htm
@@ -0,0 +1,71 @@
+<h2><tmpl_var name="list_head_txt"></h2>
+<p><tmpl_var name="list_desc_txt"></p>
+
+<div class="panel panel_list_cron">
+
+  <div class="pnl_toolsarea">
+    <fieldset><legend>Tools</legend>
+      <div class="buttons">
+        <button class="iconstxt icoAdd" type="button" onClick="loadContent('sites/cron_edit.php');">
+          <span>{tmpl_var name="add_new_cron_txt"}</span>
+        </button>
+      </div>
+    </fieldset>
+  </div>
+
+  <div class="pnl_listarea">
+    <fieldset><legend><tmpl_var name="list_head_txt"></legend>
+      <table class="list">
+        <thead>
+          <tr>
+            <th class="tbl_col_active" scope="col"><tmpl_var name="active_txt"></th>
+            <th class="tbl_col_server_id" scope="col"><tmpl_var name="server_id_txt"></th>
+            <th class="tbl_col_run_min" scope="col"><tmpl_var name="run_min_txt"></th>
+            <th class="tbl_col_run_hour" scope="col"><tmpl_var name="run_hour_txt"></th>
+            <th class="tbl_col_run_mday" scope="col"><tmpl_var name="run_mday_txt"></th>
+            <th class="tbl_col_run_month" scope="col"><tmpl_var name="run_month_txt"></th>
+            <th class="tbl_col_run_wday" scope="col"><tmpl_var name="run_wday_txt"></th>
+            <th class="tbl_col_command" scope="col"><tmpl_var name="command_txt"></th>
+            <th class="tbl_col_buttons" scope="col">&nbsp;</th>
+          </tr>
+          <tr>
+            <td class="tbl_col_active"><select name="search_active" onChange="submitForm('pageForm','sites/cron_list.php');">{tmpl_var name='search_active'}</select></td>
+            <td class="tbl_col_server_id"><select name="search_server_id" onChange="submitForm('pageForm','sites/cron_list.php');">{tmpl_var name='search_server_id'}</select></td>
+            <td class="tbl_col_run_min"><input type="text" name="search_run_min" size="3" value="{tmpl_var name='search_run_min'}" /></td>
+            <td class="tbl_col_run_hour"><input type="text" name="search_run_hour" size="3" value="{tmpl_var name='search_run_hour'}" /></td>
+            <td class="tbl_col_run_mday"><input type="text" name="search_run_mday" size="3" value="{tmpl_var name='search_run_mday'}" /></td>
+            <td class="tbl_col_run_month"><input type="text" name="search_run_month" size="3" value="{tmpl_var name='search_run_month'}" /></td>
+            <td class="tbl_col_run_wday"><input type="text" name="search_run_wday" size="3" value="{tmpl_var name='search_run_wday'}" /></td>
+            <td class="tbl_col_command"><input type="text" name="search_command" value="{tmpl_var name='search_command'}" /></td>
+            <td class="tbl_col_buttons"><div class="buttons"><button type="button" class="icons16 icoFilter" name="Filter" id="Filter" value="{tmpl_var name="filter_txt"}" onClick="submitForm('pageForm','sites/cron_list.php');"><span>{tmpl_var name="filter_txt"}</span></button></div></td>
+          </tr>
+        </thead>
+        <tbody>
+          <tmpl_loop name="records">
+          <tr class="tbl_row_<tmpl_if name='__EVEN__'}even<tmpl_else>uneven</tmpl_if>">
+            <td class="tbl_col_active"><a href="#" onClick="loadContent('sites/cron_edit.php?id={tmpl_var name='id'}');"><img src="themes/{tmpl_var name='theme'}/icons/{tmpl_var name='_active_'}" border="0" /></a></td>
+            <td class="tbl_col_server_id"><a href="#" onClick="loadContent('sites/cron_edit.php?id={tmpl_var name='id'}');">{tmpl_var name="server_id"}</a></td>
+            <td class="tbl_col_run_min"><a href="#" onClick="loadContent('sites/cron_edit.php?id={tmpl_var name='id'}');">{tmpl_var name="run_min"}</a></td>
+            <td class="tbl_col_run_hour"><a href="#" onClick="loadContent('sites/cron_edit.php?id={tmpl_var name='id'}');">{tmpl_var name="run_hour"}</a></td>
+            <td class="tbl_col_run_mday"><a href="#" onClick="loadContent('sites/cron_edit.php?id={tmpl_var name='id'}');">{tmpl_var name="run_mday"}</a></td>
+            <td class="tbl_col_run_month"><a href="#" onClick="loadContent('sites/cron_edit.php?id={tmpl_var name='id'}');">{tmpl_var name="run_month"}</a></td>
+            <td class="tbl_col_run_wday"><a href="#" onClick="loadContent('sites/cron_edit.php?id={tmpl_var name='id'}');">{tmpl_var name="run_wday"}</a></td>
+            <td class="tbl_col_commnd"><a href="#" onClick="loadContent('sites/cron_edit.php?id={tmpl_var name='id'}');">{tmpl_var name="command"}</a></td>
+            <td class="tbl_col_buttons">
+              <div class="buttons icons16">    
+                <a class="icons16 icoDelete" href="javascript: del_record('sites/cron_del.php?id={tmpl_var name='id'}&phpsessid={tmpl_var name='phpsessid'}','{tmpl_var name='delete_confirmation'}');"><span>{tmpl_var name='delete_txt'}</span></a>
+              </div>
+            </td>
+          </tr>
+          </tmpl_loop>
+        </tbody>
+        <tfoot>
+          <tr>
+            <td class="tbl_footer tbl_paging" colspan="9"><tmpl_var name="paging"></td>
+          </tr>
+        </tfoot>
+      </table>
+    </fieldset>
+  </div>
+
+</div>
diff --git a/interface/web/sites/web_domain_del.php b/interface/web/sites/web_domain_del.php
index 867d1f40ea..e5614cf27c 100644
--- a/interface/web/sites/web_domain_del.php
+++ b/interface/web/sites/web_domain_del.php
@@ -72,6 +72,12 @@ class page_action extends tform_actions {
 		foreach($records as $rec) {
 			$app->db->datalogDelete('shell_user','shell_user_id',$rec['shell_user_id']);
 		}
+        
+        // Delete all records that belog to this zone.
+        $records = $app->db->queryAllRecords("SELECT id FROM cron WHERE parent_domain_id = '".intval($this->id)."'");
+        foreach($records as $rec) {
+            $app->db->datalogDelete('cron','id',$rec['id']);
+        }
 	}
 }
 
diff --git a/server/mods-available/cron_module.inc.php b/server/mods-available/cron_module.inc.php
new file mode 100644
index 0000000000..778585cb49
--- /dev/null
+++ b/server/mods-available/cron_module.inc.php
@@ -0,0 +1,97 @@
+<?php
+
+/*
+Copyright (c) 2007, Till Brehm, projektfarm Gmbh
+Modified 2009, Marius Cramer, pixcept KG
+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 cron_module {
+	
+	var $module_name = 'cron_module';
+	var $class_name = 'cron_module';
+	var $actions_available = array(	'cron_insert',
+									'cron_update',
+									'cron_delete');
+	
+	//* This function is called during ispconfig installation to determine
+	//  if a symlink shall be created for this plugin.
+	function onInstall() {
+		global $conf;
+		
+		return true;
+		
+	}
+	
+	/*
+	 	This function is called when the module is loaded
+	*/
+	
+	function onLoad() {
+		global $app;
+		
+		/*
+		Annonce the actions that where provided by this module, so plugins 
+		can register on them.
+		*/
+		
+		$app->plugins->announceEvents($this->module_name,$this->actions_available);
+		
+		/*
+		As we want to get notified of any changes on several database tables,
+		we register for them.
+		
+		The following function registers the function "functionname"
+ 		to be executed when a record for the table "dbtable" is 
+ 		processed in the sys_datalog. "classname" is the name of the
+ 		class that contains the function functionname.
+		*/
+		
+		$app->modules->registerTableHook('cron',$this->module_name,'process');
+		
+	}
+	
+	/*
+	 This function is called when a change in one of the registered tables is detected.
+	 The function then raises the events for the plugins.
+	*/
+
+	function process($tablename,$action,$data) {
+		global $app;
+		
+		switch ($tablename) {
+			case 'cron':
+				if($action == 'i') $app->plugins->raiseEvent('cron_insert',$data);
+				if($action == 'u') $app->plugins->raiseEvent('cron_update',$data);
+				if($action == 'd') $app->plugins->raiseEvent('cron_delete',$data);
+			break;
+		} // end switch
+	} // end function
+	
+
+} // end class
+
+?>
\ No newline at end of file
diff --git a/server/plugins-available/cron_jailkit_plugin.inc.php b/server/plugins-available/cron_jailkit_plugin.inc.php
new file mode 100644
index 0000000000..fc19db6cfd
--- /dev/null
+++ b/server/plugins-available/cron_jailkit_plugin.inc.php
@@ -0,0 +1,272 @@
+<?php
+
+/*
+Copyright (c) 2007, Till Brehm, projektfarm Gmbh
+Modified 2009, Marius Cramer, pixcept KG
+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 cron_jailkit_plugin {
+	
+	//* $plugin_name and $class_name have to be the same then the name of this class
+	var $plugin_name = 'cron_jailkit_plugin';
+	var $class_name = 'cron_jailkit_plugin';
+	
+	//* This function is called during ispconfig installation to determine
+	//  if a symlink shall be created for this plugin.
+	function onInstall() {
+		global $conf;
+		
+		if($conf['services']['web'] == true) {
+			return true;
+		} else {
+			return false;
+		}
+		
+	}
+	
+		
+	/*
+	 	This function is called when the plugin is loaded
+	*/
+	
+	function onLoad() {
+		global $app;
+		
+		/*
+		Register for the events
+		*/
+		
+        $app->plugins->registerEvent('cron_insert', $this->plugin_name, 'insert');
+        $app->plugins->registerEvent('cron_update', $this->plugin_name, 'update');
+        $app->plugins->registerEvent('cron_delete', $this->plugin_name, 'delete');
+		
+	}
+	
+	//* This function is called, when a cron job is inserted in the database
+	function insert($event_name,$data) {
+		global $app, $conf;
+		
+        if($data["new"]["parent_domain_id"] == '') {
+            $app->log("Parent domain not set",LOGLEVEL_WARN);
+            return 0;
+        }
+        
+        //* get data from web
+        $parent_domain = $app->db->queryOneRecord("SELECT `domain_id`, `system_user`, `system_group`, `document_root`, `domain` FROM `web_domain` WHERE `domain_id` = ".intval($data["new"]["parent_domain_id"]));
+        if(!$parent_domain["domain_id"]) {
+            $app->log("Parent domain not found",LOGLEVEL_WARN);
+            return 0;
+        } elseif($parent_domain["system_user"] == 'root' or $parent_domain["system_group"] == 'root') {
+            $app->log("Websites (and Crons) can not be owned by the root user or group.",LOGLEVEL_WARN);
+            return 0;
+        }
+		
+        $app->uses('system');
+		
+		if($app->system->is_user($parent_domain['system_user'])) {
+		
+			/**
+		 	* Setup Jailkit Chroot System If Enabled 
+		 	*/
+			if ($data['new']['type'] == "chrooted")
+			{
+				// load the server configuration options
+				$app->uses("getconf");
+				$this->data = $data;
+				$this->app = $app;
+				$this->jailkit_config = $app->getconf->get_server_config($conf["server_id"], 'jailkit');
+                $this->parent_domain = $parent_domain;
+			
+				$this->_setup_jailkit_chroot();
+				
+				//$command .= 'usermod -U '.escapeshellcmd($parent_domain['system_user']);
+				//exec($command);
+				
+				$this->_add_jailkit_user();
+			}
+		
+			$app->log("Jailkit Plugin (Cron) -> insert username:".$parent_domain['system_user'],LOGLEVEL_DEBUG);
+			
+		} else {
+			$app->log("Jailkit Plugin (Cron) -> insert username:".$parent_domain['system_user']." skipped, the user does not exist.",LOGLEVEL_WARN);
+		}
+		
+	}
+	
+	//* This function is called, when a cron job is updated in the database
+	function update($event_name,$data) {
+		global $app, $conf;
+		
+        if($data["new"]["parent_domain_id"] == '') {
+            $app->log("Parent domain not set",LOGLEVEL_WARN);
+            return 0;
+        }
+        //* get data from web
+        $parent_domain = $app->db->queryOneRecord("SELECT `domain_id`, `system_user`, `system_group`, `document_root`, `domain` FROM `web_domain` WHERE `domain_id` = ".intval($data["new"]["parent_domain_id"]));
+
+        if(!$parent_domain["domain_id"]) {
+            $app->log("Parent domain not found",LOGLEVEL_WARN);
+            return 0;
+        } elseif($parent_domain["system_user"] == 'root' or $parent_domain["system_group"] == 'root') {
+            $app->log("Websites (and Crons) can not be owned by the root user or group.",LOGLEVEL_WARN);
+            return 0;
+        }
+		
+        $app->uses('system');
+		
+		if($app->system->is_user($parent_domain['system_user'])) {
+        
+			/**
+		 	* Setup Jailkit Chroot System If Enabled 
+		 	*/
+			if ($data['new']['type'] == "chrooted")
+			{
+                $app->log("Jailkit Plugin (Cron) -> setting up jail", LOGLEVEL_DEBUG);
+				// load the server configuration options
+				$app->uses("getconf");
+				$this->data = $data;
+				$this->app = $app;
+				$this->jailkit_config = $app->getconf->get_server_config($conf["server_id"], 'jailkit');
+                $this->parent_domain = $parent_domain;
+			
+				$this->_setup_jailkit_chroot();
+				$this->_add_jailkit_user();
+			}
+		
+			$app->log("Jailkit Plugin (Cron) -> update username:".$parent_domain['system_user'],LOGLEVEL_DEBUG);
+			
+		} else {
+			$app->log("Jailkit Plugin (Cron) -> update username:".$parent_domain['system_user']." skipped, the user does not exist.",LOGLEVEL_WARN);
+		}
+		
+	}
+	
+	//* This function is called, when a cron job is deleted in the database
+	function delete($event_name,$data) {
+		global $app, $conf;
+		
+		//* nothing to do here!
+		
+	}
+	
+	function _setup_jailkit_chroot()
+	{
+			//check if the chroot environment is created yet if not create it with a list of program sections from the config
+			if (!is_dir($this->parent_domain['document_root'].'/etc/jailkit'))
+			{
+				$command = '/usr/local/ispconfig/server/scripts/create_jailkit_chroot.sh';
+				$command .= ' '.escapeshellcmd($this->parent_domain['document_root']);
+				$command .= ' \''.$this->jailkit_config['jailkit_chroot_app_sections'].'\'';
+				exec($command);
+				
+				$this->app->log("Added jailkit chroot with command: ".$command,LOGLEVEL_DEBUG);
+				
+				//$this->_add_jailkit_programs(); // done later on
+				
+				$this->app->load('tpl');
+		
+				$tpl = new tpl();
+				$tpl->newTemplate("bash.bashrc.master");
+				
+				$tpl->setVar('jailkit_chroot',true);
+				$tpl->setVar('domain',$this->parent_domain['domain']);
+                $tpl->setVar('home_dir',$this->_get_home_dir(""));
+				
+				$bashrc = escapeshellcmd($this->parent_domain['document_root']).'/etc/bash.bashrc';
+				if(@is_file($bashrc)) exec('rm '.$bashrc);
+				
+				file_put_contents($bashrc,$tpl->grab());
+				unset($tpl);
+				
+				$this->app->log("Added bashrc scrpt : ".$bashrc,LOGLEVEL_DEBUG);
+				
+				$tpl = new tpl();
+				$tpl->newTemplate("motd.master");
+				
+				$tpl->setVar('domain',$this->parent_domain['domain']);
+				
+				$motd = escapeshellcmd($this->parent_domain['document_root']).'/var/run/motd';
+				if(@is_file($motd)) exec('rm '.$motd);
+				
+				file_put_contents($motd,$tpl->grab());
+				
+			}
+            $this->_add_jailkit_programs();
+	}
+	
+	function _add_jailkit_programs()
+	{
+		//copy over further programs and its libraries
+		$command = '/usr/local/ispconfig/server/scripts/create_jailkit_programs.sh';
+		$command .= ' '.escapeshellcmd($this->parent_domain['document_root']);
+		$command .= ' \''.$this->jailkit_config['jailkit_chroot_app_programs'].'\'';
+		exec($command);
+		
+		$this->app->log("Added programs to jailkit chroot with command: ".$command,LOGLEVEL_DEBUG);
+        
+        $command = '/usr/local/ispconfig/server/scripts/create_jailkit_programs.sh';
+        $command .= ' '.escapeshellcmd($this->parent_domain['document_root']);
+        $command .= ' \''.$this->jailkit_config['jailkit_chroot_cron_programs'].'\'';
+        exec($command);
+        
+        $this->app->log("Added cron programs to jailkit chroot with command: ".$command,LOGLEVEL_DEBUG);
+	}
+	
+	function _add_jailkit_user()
+	{
+			//add the user to the chroot
+            $jailkit_chroot_userhome = $this->_get_home_dir($this->parent_domain['system_user']);
+			
+			if(!is_dir($this->parent_domain['document_root'].'/etc')) mkdir($this->parent_domain['document_root'].'/etc');
+			if(!is_file($this->parent_domain['document_root'].'/etc/passwd')) exec('touch '.$this->parent_domain['document_root'].'/etc/passwd');
+			
+			// IMPORTANT!
+			// ALWAYS create the user. Even if the user was created before
+			// if we check if the user exists, then a update (no shell -> jailkit) will not work
+			// and the user has FULL ACCESS to the root of the server!
+			$command = '/usr/local/ispconfig/server/scripts/create_jailkit_user.sh';
+			$command .= ' '.escapeshellcmd($this->parent_domain['system_user']);
+			$command .= ' '.escapeshellcmd($this->parent_domain['document_root']);
+			$command .= ' '.$jailkit_chroot_userhome;
+			$command .= ' '.escapeshellcmd("/bin/bash");
+			exec($command);
+				
+			$this->app->log("Added jailkit user to chroot with command: ".$command,LOGLEVEL_DEBUG);
+				
+			exec("mkdir -p ".escapeshellcmd($this->parent_domain['document_root'].$jailkit_chroot_userhome));
+	}
+	
+    function _get_home_dir($username)
+    {
+        return str_replace("[username]",escapeshellcmd($username),$this->jailkit_config["jailkit_chroot_home"]);
+    }
+    
+	
+
+} // end class
+
+?>
\ No newline at end of file
diff --git a/server/plugins-available/cron_plugin.inc.php b/server/plugins-available/cron_plugin.inc.php
new file mode 100644
index 0000000000..d923d1b761
--- /dev/null
+++ b/server/plugins-available/cron_plugin.inc.php
@@ -0,0 +1,212 @@
+<?php
+
+/*
+Copyright (c) 2007 - 2009, Till Brehm, projektfarm Gmbh
+Modified 2009, Marius Cramer, pixcept KG
+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 cron_plugin {
+	
+	var $plugin_name = 'cron_plugin';
+	var $class_name = 'cron_plugin';
+	
+	// private variables
+	var $action = '';
+	
+	//* This function is called during ispconfig installation to determine
+	//  if a symlink shall be created for this plugin.
+	function onInstall() {
+		global $conf;
+		
+		if($conf['services']['web'] == true) {
+			return true;
+		} else {
+			return false;
+		}
+		
+	}
+	
+		
+	/*
+	 	This function is called when the plugin is loaded
+	*/
+	
+	function onLoad() {
+		global $app;
+		
+		/*
+		Register for the events
+		*/
+		
+		$app->plugins->registerEvent('cron_insert',$this->plugin_name,'insert');
+		$app->plugins->registerEvent('cron_update',$this->plugin_name,'update');
+		$app->plugins->registerEvent('cron_delete',$this->plugin_name,'delete');
+		
+	}
+	
+	function insert($event_name,$data) {
+		global $app, $conf;
+		
+		$this->action = 'insert';
+		// just run the update function
+		$this->update($event_name,$data);
+		
+	}
+	
+	
+	function update($event_name,$data) {
+		global $app, $conf;
+		
+		if($this->action != 'insert') $this->action = 'update';
+		
+		// load the server configuration options
+		$app->uses("getconf");
+		
+		if($data["new"]["parent_domain_id"] == '') {
+			$app->log("Parent domain not set",LOGLEVEL_WARN);
+			return 0;
+		}
+        
+        //* get data from web
+        $parent_domain = $app->db->queryOneRecord("SELECT `domain_id`, `system_user`, `system_group`, `document_root`, `hd_quota` FROM `web_domain` WHERE `domain_id` = ".intval($data["new"]["parent_domain_id"]));
+        if(!$parent_domain["domain_id"]) {
+            $app->log("Parent domain not found",LOGLEVEL_WARN);
+            return 0;
+        } elseif($parent_domain["system_user"] == 'root' or $parent_domain["system_group"] == 'root') {
+			$app->log("Websites (and Crons) can not be owned by the root user or group.",LOGLEVEL_WARN);
+			return 0;
+		}
+		
+		// Get the client ID
+		$client = $app->dbmaster->queryOneRecord("SELECT client_id FROM sys_group WHERE sys_group.groupid = ".intval($data["new"]["sys_groupid"]));
+		$client_id = intval($client["client_id"]);
+		unset($client);
+		
+		// Create group and user, if not exist
+		$app->uses("system");
+		
+		$groupname = escapeshellcmd($parent_domain["system_group"]);
+		if($parent_domain["system_group"] != '' && !$app->system->is_group($parent_domain["system_group"])) {
+			exec("groupadd $groupname");
+			$app->log("Adding the group: $groupname",LOGLEVEL_DEBUG);
+		}
+		
+		$username = escapeshellcmd($parent_domain["system_user"]);
+		if($parent_domain["system_user"] != '' && !$app->system->is_user($parent_domain["system_user"])) {
+			exec("useradd -d ".escapeshellcmd($parent_domain["document_root"])." -g $groupname $username -s /bin/false");
+			$app->log("Adding the user: $username",LOGLEVEL_DEBUG);
+		}
+		
+		// Set the quota for the user
+		if($username != '' && $app->system->is_user($username)) {
+			if($parent_domain["hd_quota"] > 0){
+    			$blocks_soft = $parent_domain["hd_quota"] * 1024;
+    			$blocks_hard = $blocks_soft + 1024;
+  			} else {
+    			$blocks_soft = $blocks_hard = 0;
+  			}
+			exec("setquota -u $username $blocks_soft $blocks_hard 0 0 -a &> /dev/null");
+			exec("setquota -T -u $username 604800 604800 -a &> /dev/null");
+		}
+		
+		// make temp direcory writable for the apache user and the website user
+		exec("chmod 777 ".escapeshellcmd($parent_domain["document_root"]."/tmp"));
+		
+        /** TODO READ CRON MASTER **/
+        
+        $this->parent_domain = $parent_domain;
+		$this->_write_crontab();
+		
+        
+	}
+	
+	function delete($event_name,$data) {
+		global $app, $conf;
+		
+        //* get data from web
+        $parent_domain = $app->db->queryOneRecord("SELECT `domain_id`, `system_user`, `system_group`, `document_root`, `hd_quota` FROM `web_domain` WHERE `domain_id` = ".intval($data["old"]["parent_domain_id"]));
+        if(!$parent_domain["domain_id"]) {
+            $app->log("Parent domain not found",LOGLEVEL_WARN);
+            return 0;
+        }
+        
+        // Get the client ID
+        $client = $app->dbmaster->queryOneRecord("SELECT client_id FROM sys_group WHERE sys_group.groupid = ".intval($data["old"]["sys_groupid"]));
+        $client_id = intval($client["client_id"]);
+        unset($client);
+        
+        /** TODO remove deleted record **/
+        $this->parent_domain = $parent_domain;
+        $this->_write_crontab();
+	}
+    
+    function _write_crontab() {
+        global $app, $conf;
+        
+        //* load the server configuration options
+        $app->uses("getconf");
+        
+        $cron_config = $app->getconf->get_server_config($conf["server_id"], 'cron');
+        
+        //* try to find customer's mail address
+        
+        $cron_content = "MAILTO=''\n\n";
+        
+        //* read all active cron jobs from database and write them to file
+        $cron_jobs = $app->db->queryAllRecords("SELECT `run_min`, `run_hour`, `run_mday`, `run_month`, `run_wday`, `command`, `type` FROM `cron` WHERE `parent_domain_id` = ".intval($this->parent_domain["domain_id"]) . " AND `active` = 'y'");
+        if($cron_jobs && count($cron_jobs) > 0) {
+            foreach($cron_jobs as $job) {
+                $command = "{$job['run_min']}\t{$job['run_hour']}\t{$job['run_mday']}\t{$job['run_month']}\t{$job['run_wday']}";
+                $command .= "\t{$this->parent_domain['system_user']}"; //* running as user
+                if($job['type'] == 'url') {
+                    $command .= "\t{$cron_config['wget']} -q -O /dev/null " . escapeshellarg($job['command']) . " >/dev/null 2>&1";
+                } else {
+                    if($job['type'] == 'chrooted') {
+                        if(substr($job['command'], 0, strlen($this->parent_domain['document_root'])) == $this->parent_domain['document_root']) {
+                            //* delete the unneeded path part
+                            $job['command'] = substr($job['command'], strlen($this->parent_domain['document_root']));
+                        }
+                    }
+                    
+                    $command .= "\t";
+                    if(substr($job['command'], 0, 1) != "/") $command .= $this->parent_domain['document_root'];
+                    $command .= $job['command'];
+                }
+                $cron_content .= $command . "\n";
+            }
+        }
+        
+        $cron_file = escapeshellcmd($cron_config["crontab_dir"].'/ispc_'.$this->parent_domain["system_user"]);
+        file_put_contents($cron_file, $cron_content);
+        
+        $app->log("Wrote Cron file $cron_file with content:\n$cron_content",LOGLEVEL_DEBUG);
+        return 0;
+    }
+
+} // end class
+
+?>
\ No newline at end of file
-- 
GitLab