diff --git a/public_html/appmonitor/classes/appmonitor-checks.class.php b/public_html/appmonitor/classes/appmonitor-checks.class.php
index 9301efe3a8b0d4012544623bd48abd821a633726..c7825de60a00db42367dabd51482777850761203 100644
--- a/public_html/appmonitor/classes/appmonitor-checks.class.php
+++ b/public_html/appmonitor/classes/appmonitor-checks.class.php
@@ -23,6 +23,7 @@ define("RESULT_ERROR", 3);
  * 2014-10-24  0.5   axel.hahn@iml.unibe.ch<br>
  * 2015-04-08  0.9   axel.hahn@iml.unibe.ch  added sochket test: checkPortTcp<br>
  * 2018-06-29  0.24  axel.hahn@iml.unibe.ch  add file and directory checks<br>
+ * 2018-07-17  0.42  axel.hahn@iml.unibe.ch  add port on mysqli check<br>
  * --------------------------------------------------------------------------------<br>
  * @version 0.9
  * @author Axel Hahn
@@ -47,6 +48,10 @@ class appmonitorcheck {
      * @var array
      */
     private $_aData = array();
+    
+    
+    // protected $_units = array( 'B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB');
+    protected $_units = array( 'B', 'KB', 'MB', 'GB', 'TB');
 
     // ----------------------------------------------------------------------
     // CONSTRUCTOR
@@ -195,46 +200,216 @@ class appmonitorcheck {
     // CHECK FUNCTIONS (private)
     // ----------------------------------------------------------------------
 
+    /**
+     * helper function: read certificate data
+     * called in checkCert()
+     * @param string $sUrl  url to connect
+     * @return array
+     */
+    protected function _certGetInfos($sUrl) {
+        $iTimeout=10;
+        $aUrldata=parse_url($sUrl);
+        $sHost = isset($aUrldata['host']) ? $aUrldata['host'] : false;
+        $iPort = isset($aUrldata['port']) ? $aUrldata['port'] : ((isset($aUrldata['scheme']) && $aUrldata['scheme'] === 'https') ? 443 : false);
+
+        
+        $get = stream_context_create(array("ssl" => array("capture_peer_cert" => TRUE)));
+        if(!$get){
+            return array('_error' => 'Error: Cannot create stream_context');
+        }
+        $errno=-1;
+        $errstr="stream_socket_client failed.";
+        $read = stream_socket_client("ssl://$sHost:$iPort", $errno, $errstr, $iTimeout, STREAM_CLIENT_CONNECT, $get);
+        if(!$read){
+            return array('_error' => "Error $errno: $errstr; cannot create stream_context to ssl://$sHost:$iPort");
+        }
+        $cert = stream_context_get_params($read);
+        if(!$cert){
+            return array('_error' => "Error: socket was connected to ssl://$sHost:$iPort - but I cannot read certificate infos with stream_context_get_params ");
+        }
+        $certinfo = openssl_x509_parse($cert['options']['ssl']['peer_certificate']);
+        return $certinfo;
+    }
+
+    
+    
+    /**
+     * check SSL certificate 
+     * @param array $aParams
+     * array(
+     *     "url"       optional: url to connect check; default: own protocol + server
+     *     "warning"   optional: count of days to warn; default=30
+     * )
+     * @return boolean
+     */
+    public function checkCert($aParams) {
+        $sUrl = isset($aParams["url"]) 
+                ? $aParams["url"] 
+                : 'http'. ($_SERVER['HTTPS'] ? 's' : '') . '://' . $_SERVER['SERVER_NAME'] .':' . $_SERVER['SERVER_PORT']
+                ;
+        $iWarn = isset($aParams["warning"]) ? (int)($aParams["warning"]) : 30;
+
+        $sMessage="url $sUrl ... ";
+        $certinfo=$this->_certGetInfos($sUrl);
+        if(isset($certinfo['_error'])){
+            $this->_setReturn(RESULT_ERROR, $certinfo['_error'] . $sMessage);
+            return true;
+        }
+        
+        $sDNS=isset($certinfo['extensions']['subjectAltName']) ? $certinfo['extensions']['subjectAltName'] : false;
+        $sHost=parse_url($url,PHP_URL_HOST);
+        if(strstr($sDNS, 'DNS:'.$sHost)===false){
+            $aReturn['errors'][]="Domainname $sHost ist nicht als DNS ALias im Zertifikat enthalten.";
+            $this->_setReturn(RESULT_ERROR, 'Wrong certificate: '.$sHost.' is not listed as DNS alias in ['.$sDNS.']  ' . $sMessage);
+            return true;
+        }
+        
+        $iDaysleft = round(($certinfo['validTo_time_t'] - date('U')) / 60 / 60 / 24);
+        $sMessage.= 'Issuer: '. $sIssuer=$certinfo['issuer']['O'] 
+                . ' valid from: '. date("Y-m-d H:i", $certinfo['validFrom_time_t'])
+                . ' to '.date("Y-m-d H:i", $certinfo['validTo_time_t']).' '
+                . ( $iDaysleft ? "($iDaysleft days left)" : "expired since ".(-$iDaysleft)." days.")
+                ;
+        if ($iDaysleft<0) {
+            $this->_setReturn(RESULT_ERROR, 'Expired ' . $sMessage);
+            return true;
+        }
+        if ($iDaysleft<=$iWarn) {
+            $this->_setReturn(RESULT_WARNING, 'Expires soon ' . $sMessage);
+            return true;
+        }
+        // echo '<pre>';
+        $this->_setReturn(RESULT_OK, 'OK, is valid ' . $sMessage);
+        return true;
+    }
+
+    /**
+     * get human readable space value
+     * @param type $size
+     * @return string
+     */
+    protected function _getHrSize($size){
+        $power = $size > 0 ? floor(log($size, 1024)) : 0;
+        return number_format($size / pow(1024, $power), 2, '.', ',') . ' ' . $this->_units[$power];
+    }
+    /**
+     * get a space in a real value if an integer has added MB|GB|...
+     * @param type $sValue
+     * @return integer
+     */
+    protected function _getSize($sValue){
+        if(is_int($sValue)){
+            return $sValue;
+        }
+        $power=0;
+        foreach($this->_units as $sUnit){
+            if (preg_match('/^[0-9\.]*'.$sUnit.'/', $sValue)){
+                $i=preg_replace('/([0-9\.]*).*/', '$1', $sValue);
+                $iReal=$i*pow(1024, $power);
+                // die("FOUND: $sValue with unit ${sUnit} - 1024^$power * $i = $iReal");
+                return $iReal;
+            }
+            $power++;
+        }
+        header('HTTP/1.0 503 Service Unavailable');
+        die("ERROR in space value parameter - there is no size unit in [$sValue] - allowed size units are " . implode('|', $this->_units));
+        return false;
+    }
+
+    /**
+     * check free disk space on a given directory
+     * @param array $aParams
+     * array(
+     *     "directory"   directory that must exist
+     *     "warning"     space for warning (optional)
+     *     "critical"    minimal space
+     * )
+     * @return boolean
+     */
+    public function checkDiskfree($aParams) {
+        $this->_checkArrayKeys($aParams, "directory", "critical");
+        
+        $sDirectory = $aParams["directory"];
+        if(!is_dir($sDirectory)){
+            $this->_setReturn(RESULT_ERROR, 'directory [' . $sDirectory . '] does not exist. Maybe it is wrong or is not mounted.');
+            return true;
+        }
+        
+        $iWarn = isset($aParams["warning"]) ? $this->_getSize($aParams["warning"]) : false;
+        $iCritical = $this->_getSize($aParams["critical"]);
+        $iSpaceLeft=disk_free_space($sDirectory);
+        
+        
+        $sMessage='[' . $sDirectory . '] has '.$this->_getHrSize($iSpaceLeft).' left.';
+        
+        if($iWarn){
+            if($iWarn<=$iCritical){
+                header('HTTP/1.0 503 Service Unavailable');
+                die("ERROR in a Diskfree check - warning value must be larger than critical.<pre>" . print_r($aParams, true));
+            }
+            if ($iWarn<$iSpaceLeft){
+                $this->_setReturn(RESULT_OK, $sMessage.' Warning level is not reached yet (still '.$this->_getHrSize($iSpaceLeft-$iWarn).' over warning limit).');
+                return true;
+            }
+            if ($iWarn>$iSpaceLeft && $iCritical<$iSpaceLeft){
+                $this->_setReturn(RESULT_WARNING, $sMessage.' Warning level '.$this->_getHrSize($iWarn).' was reached (space is '.$this->_getHrSize($iWarn-$iSpaceLeft).' below warning limit; still '.$this->_getHrSize($iSpaceLeft-$iCritical).' over critical limit).');
+                return true;
+            }
+        }
+        // check space
+        if ($iCritical<$iSpaceLeft){
+            $this->_setReturn(RESULT_OK, $sMessage .' Minimum is not reached yet (still '.$this->_getHrSize($iSpaceLeft-$iCritical).' over critical limit).');
+        } else {
+            $this->_setReturn(RESULT_ERROR, $sMessage);
+        }
+        return true;
+    }
     /**
      * check a file
      * @param array $aParams
      * array(
-     *     "filename"  directory that must exist
-     *     "writable"  flag to check that it must be writable too
+     *     "filename"    directory that must exist
+     *     "exists"      "filename" must exist/ must be absent
+     *     "dir"         filetype directory
+     *     "file"        filetype file
+     *     "link"        filetype symbolic link
+     *     "executable"  flag executable
+     *     "readable"    flag is readable
+     *     "writable"    flag is writable
      * )
      * @return boolean
      */
     public function checkFile($aParams) {
-        $aOK=array();
-        $aErrors=array();
+        $aOK = array();
+        $aErrors = array();
         $this->_checkArrayKeys($aParams, "filename");
-        $sFile=$aParams["filename"];
-        
-        if (isset($aParams['exists'])){
-            $sMyflag='exists='.($aParams['exists'] ? 'yes' : 'no');
-            if (file_exists($sFile) && $aParams['exists']){
-                $aOK[]=$sMyflag;
+        $sFile = $aParams["filename"];
+
+        if (isset($aParams['exists'])) {
+            $sMyflag = 'exists=' . ($aParams['exists'] ? 'yes' : 'no');
+            if (file_exists($sFile) && $aParams['exists']) {
+                $aOK[] = $sMyflag;
             } else {
-                $aErrors[]=$sMyflag;
+                $aErrors[] = $sMyflag;
             }
         }
-        foreach(array('dir', 'executable', 'file', 'link', 'readable', 'writable') as $sFiletest){
-            if (isset($aParams[$sFiletest])){
-                $sTestCmd='return is_'.$sFiletest.'("'.$sFile.'");';
-                if (eval($sTestCmd) && $aParams[$sFiletest]){
-                    $aOK[]=$sFiletest . '='.($aParams[$sFiletest] ? 'yes' : 'no');
+        foreach (array('dir', 'executable', 'file', 'link', 'readable', 'writable') as $sFiletest) {
+            if (isset($aParams[$sFiletest])) {
+                $sTestCmd = 'return is_' . $sFiletest . '("' . $sFile . '");';
+                if (eval($sTestCmd) && $aParams[$sFiletest]) {
+                    $aOK[] = $sFiletest . '=' . ($aParams[$sFiletest] ? 'yes' : 'no');
                 } else {
-                    $aErrors[]=$sFiletest . '='.($aParams[$sFiletest] ? 'yes' : 'no');
+                    $aErrors[] = $sFiletest . '=' . ($aParams[$sFiletest] ? 'yes' : 'no');
                 }
             }
         }
-        $sMessage=(count($aOK)     ? ' flags OK: '     .implode('|', $aOK)     : '')
-                .' '. (count($aErrors) ? ' flags FAILED: '.implode('|', $aErrors) : '')
-                ;
-        if(count($aErrors)){
-            $this->_setReturn(RESULT_ERROR, 'file test ['. $sFile . '] '.$sMessage);
+        $sMessage = (count($aOK) ? ' flags OK: ' . implode('|', $aOK) : '')
+                . ' ' . (count($aErrors) ? ' flags FAILED: ' . implode('|', $aErrors) : '')
+        ;
+        if (count($aErrors)) {
+            $this->_setReturn(RESULT_ERROR, 'file test [' . $sFile . '] ' . $sMessage);
         } else {
-            $this->_setReturn(RESULT_OK, 'file test ['. $sFile . '] '.$sMessage);
+            $this->_setReturn(RESULT_OK, 'file test [' . $sFile . '] ' . $sMessage);
         }
         return true;
     }
@@ -281,15 +456,20 @@ class appmonitorcheck {
      *     "user" 
      *     "password" 
      *     "db" 
+     *     "port"     <<< optional
      * )
      */
     private function checkMysqlConnect($aParams) {
         $this->_checkArrayKeys($aParams, "server,user,password,db");
+        if (!isset($aParams["port"])) {
+            $aParams["port"] = false;
+        }
         $db = mysqli_connect(
-                $aParams["server"], $aParams["user"], $aParams["password"], $aParams["db"]
+                $aParams["server"], $aParams["user"], $aParams["password"], $aParams["db"], $aParams["port"]
         );
         if ($db) {
             $this->_setReturn(RESULT_OK, "OK: Mysql database " . $aParams["db"] . " was connected");
+            mysqli_close($db);
             return true;
         } else {
             $this->_setReturn(RESULT_ERROR, "ERROR: Mysql database " . $aParams["db"] . " was not connected. " . mysqli_connect_error());
@@ -334,7 +514,11 @@ class appmonitorcheck {
 
     /**
      * most simple check: set values
-     * @return type
+     * @param array $aParams
+     * array(
+     *     "result" integer; RESUL_nn
+     *     "value"  description text
+     * )
      */
     private function checkSimple($aParams) {
         $this->_checkArrayKeys($aParams, "result,value");
@@ -345,7 +529,7 @@ class appmonitorcheck {
      * check sqlite connection
      * @param array $aParams
      * array(
-     *     "db" 
+     *     "db"  full path of sqlite file 
      * )
      * @return boolean
      */
diff --git a/public_html/appmonitor/index.php b/public_html/appmonitor/index.php
index 9fb40bbaa132c50f782d48a1e027a13b7c14affb..d867a90f1114adfa83f8618ccb8bc4f72971cacd 100644
--- a/public_html/appmonitor/index.php
+++ b/public_html/appmonitor/index.php
@@ -74,6 +74,32 @@ foreach(array('dataDir', 'buildDir', 'packageDir', 'archiveDir') as $sDirKey){
     );
         
 }
+foreach(array('dataDir', 'buildDir', 'packageDir', 'archiveDir') as $sDirKey){
+    $oMonitor->addCheck(
+        array(
+            "name" => "Disk space in dir [$sDirKey]",
+            "description" => "Check if workdir $sDirKey has enough space",
+            "check" => array(
+                "function" => "Diskfree",
+                "params" => array(
+                    "directory" => $aConfig[$sDirKey],
+                    "warning"   => "500MB",
+                    "critical"  => "100MB",
+                ),
+            ),
+        )
+    );
+        
+}
+    $oMonitor->addCheck(
+        array(
+            "name" => "Certificate check",
+            "description" => "Check if SSL cert is valid and does not expire soon",
+            "check" => array(
+                "function" => "Cert",
+            ),
+        )
+    );
     
 // ----------------------------------------------------------------------
 // databases