Improved password storage schema
Improved password storage schema

file:a/.gitignore -> file:b/.gitignore
--- a/.gitignore
+++ b/.gitignore
@@ -7,4 +7,5 @@
 testcreds
 testdata
 testdata/*
+update/
 

--- a/Install/index.php
+++ b/Install/index.php
@@ -478,6 +478,8 @@
   `Address` blob,
   `UName` blob,
   `Custom` blob,
+  `hidden` tinyint(1) DEFAULT 0,
+  `comment` blob DEFAULT NULL,
   PRIMARY KEY (`id`),
   KEY `idx_Cred_Group` (`Group`),
   KEY `idx_cred_cust` (`cust`)
@@ -559,7 +561,11 @@
   PRIMARY KEY (`id`),
   UNIQUE KEY `idx_failed_user_ip` (`username`,`FailedIP`),
   KEY `idx_failedips` (`FailedIP`)
-) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;'
+) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;',
+
+'CREATE TABLE #__CustPortal( `id` INT NOT NULL, `email` TEXT, `pass` TEXT, `active` TINYINT(1), PRIMARY KEY (`id`));',
+'CREATE UNIQUE INDEX idx_portal_logins ON #__CustPortal(`email`(100));',
+'CREATE INDEX idx_active_portal ON #__CustPortal(`active`);'
 
 );
 
@@ -885,9 +891,9 @@
 <link rel="stylesheet" type="text/css" href="../Resources/bootstrap/css/bootstrap.css" />
 <link rel="stylesheet" type="text/css" href="../templates/EstDeus/css/EstDeus.css" />
 <link rel="stylesheet" type="text/css" href="../Resources/jquery.tooltip.css" />
-<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
+<script type="text/javascript" src="../Resources/jquery.min.js"></script>
 <script type="text/javascript" src="../Resources/jquery.tooltip.min.js"></script>
-<script type="text/javascript" src="../templates/EstDeus/css/bootstrap/js/bootstrap.js"></script>
+<script type="text/javascript" src="../Resources/bootstrap/js/bootstrap.js"></script>
 
 
 

file:a/README.md -> file:b/README.md
--- a/README.md
+++ b/README.md
@@ -10,11 +10,15 @@
 
 ------------------------------
 
-
-In BETA at the moment, shouldn't be too long until stable. Wouldn't recommend
-using in anger just yet as still have a few kinks to work out and backwards
-compatability isn't being worried about at this early stage!
-
 I'm not an interface designer, so the template is very rough around the edges.
 It's designed to support custom templates though so you can skin and brand as
-you see fit
+you see fit.
+
+Passwords are encrypted with either OpenSSL or MCrypt (depending what you have
+available). The system is intended for use over a https connection, though steps
+have been taken to help reduce the likelihood of credential compromise over a
+http connection. Still it's _STRONGLY_ recommended that connections be made over
+https to ensure that all credentials are protected in transit.
+
+You can view a demo at http://demo.bentasker.co.uk/PHPCredLocker/ including all
+developed plugins.

--- a/Resources/info.php
+++ b/Resources/info.php
@@ -34,6 +34,22 @@
 
 // Load the framework
 require_once 'lib/Framework/main.php';
+
+
+if (isset($_COOKIE['PHPCredLockerKeySet']) && BTMain::getVar('destSession') == 'Y'){
+
+$expires = strtotime("-2 days");
+setcookie("PHPCredLockerKeySet", 1, $expires, dirname($_SERVER["REQUEST_URI"]), $_SERVER['HTTP_HOST'], BTMain::getConf()->forceSSL);
+BTMain::unsetSessVar('tls');
+BTMain::unsetSessVar('KeyExpiry');
+BTMain::unsetSessVar('apiterms');
+
+$_COOKIE['PHPCredLockerKeySet'] = 0;
+
+}
+
+
+
 
 $tls = BTMain::getSessVar('tls');
 $expiry = BTMain::getSessVar('KeyExpiry');
@@ -123,16 +139,18 @@
 
       $x = 0;
       $str = '';
+      $str2 = '';
+
       while ($x < 40){
 
       $str .= chr(rand(33,126)) .mt_rand(16,45);
-      
+      $str2 .= chr(rand(33,126)) .mt_rand(16,45);
       $x++;
 
       }
 
 
-      BTMain::setSessVar('AuthKey',rtrim(base64_encode($str),"="));
+      BTMain::setSessVar('AuthKey',rtrim(base64_encode($str),"=") . ":" . rtrim(base64_encode($str2),"="));
       $enabled = 'true';
 
 
@@ -166,7 +184,7 @@
 
 ob_start();
 ?> 
-function getKey(){ return '<?php echo base64_encode(BTMain::getSessVar('tls'));?>'; }
+function getTLSKey(){ return '<?php echo base64_encode(BTMain::getSessVar('tls'));?>'; }
 
 
 function getDelimiter(){ return "|..|";}
@@ -177,7 +195,7 @@
 if (a == 'undefined' || a == 'null' || a == ''){
 return;}
 
-<?php foreach ($terms as $key=>$value){ echo "this.$value='".base64_encode($key) ."';"; }?>
+
 
 return this[a];
  }
@@ -197,16 +215,25 @@
 return window.destroyKeys = '';
 }
 
-function enabledEncryption(){
+function enabledTLSEncryption(){
 return <?php echo $enabled;?>;
 }
 
-
-
-
-
-
-
+function setStorage(){
+  if(typeof(Storage)!=="undefined"){
+	sessionStorage.setItem('key', getTLSKey());
+	sessionStorage.setItem('Terminology','{<?php foreach ($terms as $key=>$value){ echo "\"$value\":\"".base64_encode($key) ."\","; }?>"null":"null"}');
+	sessionStorage.setItem('CryptEnabled',enabledTLSEncryption());
+	sessionStorage.setItem('Delimiter',getDelimiter());
+    }else{
+    alert("You're using an out of date browser, this has a serious impact on security. Please use at least IE8, Firefox, Chrome or Safari");
+    }
+}
+
+
+
+
+setStorage();
 new getTerminology();
 <?php
 

--- a/Resources/main.js
+++ b/Resources/main.js
@@ -12,7 +12,7 @@
 */
 
 
-var counter=false, cancel='', dispcred, interval;
+var counter=false, cancel='', dispcred, interval,terms;
 
 
 
@@ -96,8 +96,12 @@
   if (count <= 0 || cancel == 1)
   {
      clearInterval(counter);
+     if (document.getElementById('credHidden'+id)){
+     field.innerHTML = 'Display<span class="DisPwdText"> Username</span>';  
+     }else{
+     field.innerHTML = 'Display<span class="DisPwdText"> Password</span>';  
+     }
      
-     field.innerHTML = 'Display<span class="DisPwdText"> Password</span>';
      document.getElementById('Address'+id).innerHTML = '';
      document.getElementById('UserName'+id).innerHTML = '';
      document.getElementById('Password'+id).innerHTML = '';
@@ -106,7 +110,7 @@
      return;
   }
 
-  field.innerHTML = 'Displaying Password for ' +count+ ' seconds';
+  field.innerHTML = 'Displaying for ' +count+ ' seconds';
 }
 
 
@@ -220,7 +224,8 @@
   var cred = document.getElementById('frmCredential'),
       user = document.getElementById('frmUser'),
       addr = document.getElementById('frmAddress'),
-      grp = document.getElementById('frmGroup');
+      grp = document.getElementById('frmGroup'),
+      comment = document.getElementById('frmComment');
       
       
       
@@ -244,6 +249,7 @@
  cred.value = Base64.encode(xorestr(cred.value,retKey()));
  user.value = Base64.encode(xorestr(user.value,retKey()));
  addr.value = Base64.encode(xorestr(addr.value,retKey()));
+ comment.value = Base64.encode(xorestr(comment.value,retKey()));
 }
 return true;
 
@@ -258,7 +264,8 @@
   var cred = document.getElementById('frmCredential'),
       user = document.getElementById('frmUser'),
       addr = document.getElementById('frmAddress'),
-      grp = document.getElementById('frmGroup');
+      grp = document.getElementById('frmGroup'),
+      comment = document.getElementById('frmComment');
       
       
       
@@ -285,6 +292,9 @@
 if (cred.value == null || cred.value == ''){
  cred.value = ' '; 
 }
+if (comment.value == null || comment.value == ''){
+ comment.value = ' '; 
+}
 
 if (user.value == null || user.value == ''){
  user.value = ' '; 
@@ -293,6 +303,7 @@
 if (addr.value == null || addr.value == ''){
  addr.value = ' '; 
 }
+
 
 if (enabledEncryption()){ 
  
@@ -300,6 +311,7 @@
  cred.value = Base64.encode(xorestr(cred.value,retKey()));
  user.value = Base64.encode(xorestr(user.value,retKey()));
  addr.value = Base64.encode(xorestr(addr.value,retKey()));
+ comment.value = Base64.encode(xorestr(comment.value,retKey()));
 }
 
 return true;
@@ -919,47 +931,60 @@
  * as it means a longer request.
  * 
  */
+
+
+function inlineDeCrypt(){
+ var i, eles = document.getElementsByClassName('inlineTLS');
+ 
+ for (i=0;i < eles.length;i++){
+   eles[i].innerHTML = decryptAPIResp(eles[i].innerHTML,retKey());   
+ }  
+  
+}
+
+
 function xorestr(str,key){
     if (!enabledEncryption()){ return str; }
     
-var a, b,
-    enc='',
-    keypos = 0;
-
-  for (var i=0; i<str.length;i++) {
-
-
-        a = str.charCodeAt(i);
-        b = a ^ key.charCodeAt(keypos) ;    
-        enc += b.toString()+" ";
-
-	keypos++;
-	if (keypos >= key.length){ keypos = 0;}
-    }
-
-
-
-
-
-return enc;
-}
-
-
-function xordstr(str,key){
-
-  if (!enabledEncryption()){ return str; }
-  
-  
 var a, b,
     enc='',
     keypos = 0,
-    str = str.split(" ");
-
+    k = key.split(":");
+
+  for (var i=0; i<str.length;i++) {
+
+
+        a = str.charCodeAt(i);
+        b = (a ^ k[0].charCodeAt(keypos)) ^ k[1].charCodeAt(keypos) ;    
+        enc += b.toString()+" ";
+
+	keypos++;
+	if (keypos >= key.length){ keypos = 0;}
+    }
+
+
+
+
+
+return enc;
+}
+
+
+function xordstr(str,key){
+
+  if (!enabledEncryption()){ return str; }
+  
+  
+var a, b,
+    enc='',
+    keypos = 0,
+    str = str.split(" "),
+    k = key.split(":");
   for (var i=0; i<str.length;i++) {
 
 	if (str[i].length == 0){ continue; }
         a = str[i];
-        b = a ^ key.charCodeAt(keypos) ;    
+        b = (a ^ k[1].charCodeAt(keypos)) ^ k[0].charCodeAt(keypos) ;    
         enc += String.fromCharCode(b);
 
 	keypos++;
@@ -1065,11 +1090,24 @@
 
 
 function getDivider(){
+  
+    if(typeof(Storage)!=="undefined" && sessionStorage.Delimiter){
+	return sessionStorage.getItem('Delimiter');
+  }
+  
  return getDelimiter(); 
 }
 
 
 function getTerms(a){
+  
+  if(typeof(Storage)!=="undefined"){
+	if (!terms){
+      terms = JSON.parse(sessionStorage.getItem('Terminology'))
+	}
+      return Base64.decode(terms[a.toString()]);
+  }
+  
   return Base64.decode(getTerminology(a));
 }
 
@@ -1118,7 +1156,7 @@
 function checkKeyAvailable(){
  
   
- if(typeof getKey != 'function') {
+ if(!getKey && typeof getTLSKey != 'function') {
    
    if (confirm("Key retrieval failed - Attempting to rectify, Click OK to continue - Screen may refresh")){
    
@@ -1131,7 +1169,7 @@
    
    removeCurrKey();
    
-    if(typeof getKey == 'function') {
+    if(typeof getTLSKey == 'function') {
       alert("Keys retrieved successfully");
       return true;
       
@@ -1148,7 +1186,32 @@
   
 }
 
-
+function getKey(){ 
+  
+ if(typeof(Storage)!=="undefined" && sessionStorage.key){
+    return sessionStorage.getItem('key');
+  }else{
+    if(typeof getTLSKey != 'function'){
+     return false; 
+    }
+   return getTLSKey(); 
+  }
+    
+}
+
+function enabledEncryption(){
+  
+   if(typeof(Storage)!=="undefined" && sessionStorage.CryptEnabled){
+    return sessionStorage.getItem('CryptEnabled');
+  }else{
+    if(typeof enabledTLSEncryption != 'function'){
+     return false; 
+    }
+   return enabledTLSEncryption(); 
+  }
+  
+
+}
 
 
 

file:a/conf.zip (deleted)
 Binary files a/conf.zip and /dev/null differ
--- a/conf/config.php.example
+++ b/conf/config.php.example
@@ -56,4 +56,8 @@
 
 // Time in hours to block the IP for
 $conf->banLength = 24;
+
+
+// Enable the customer facing portal?
+$conf->custPortalEnabled = 1;
 ?>

--- a/conf/lang.php
+++ b/conf/lang.php
@@ -21,3 +21,4 @@
 $lang['User'] = 'User';
 $lang['User Groups'] = 'User Groups';
 $lang['UserGroup'] = 'Group';
+$lang['Comment'] = 'Comment';

--- a/conf/notifications.php
+++ b/conf/notifications.php
@@ -14,6 +14,8 @@
 $notifs->HomePageTextLoggedIn->className = 'HomePgText';
 $notifs->HomePageTextLoggedIn->text = 'Welcome to PHPCredLocker, please use the menus to proceed';
 
+$notifs->CustPortalFail->className = 'alert alert-info';
+$notifs->CustPortalFail->text = "The customer has been successfully updated, but the customer portal record could not be updated. The customer will need to continue to use their old details";
 
 
 $notifs->addCustSuccess->className = 'alert alert-success';

--- a/conf/plugins.example.php
+++ /dev/null
@@ -1,21 +1,1 @@
-<?php
-/** Plugins Configuration
-*
-* Copyright (C) 2012 B Tasker
-* Released under GNU GPL V2
-* See LICENSE
-*
-*/ 
-defined('_CREDLOCK') or die;
 
-
-
-
-$plugins->Auth = array();
-$plugins->Logging = array('AffinityLive');
-$plugins->Customers = array();
-$plugins->Creds = array();
-$plugins->Cron = array();
-
-defined("CREDLOCK_PLUGIN__PATH") or define('CREDLOCK_PLUGIN__PATH','plugins/ACDK89345u3Bcd');
-

--- a/conf/plugins.php
+++ b/conf/plugins.php
@@ -14,8 +14,8 @@
 $plugins->Auth = array();
 $plugins->Logging = array('AffinityLive');
 $plugins->Customers = array();
-$plugins->Creds = array('');
-$plugins->CredTypes = array('');
+$plugins->Creds = array();
+$plugins->CredTypes = array();
 $plugins->Cron = array();
 $plugins->Resources = array('ExternalResources');
 

--- a/conf/plugins/AffinityLive/config.php
+++ b/conf/plugins/AffinityLive/config.php
@@ -12,7 +12,7 @@
 $this->active = false;
 
 // Set this to true to stop the plugin sending data to AL (Debug output will be generated instead
-$this->testmode = true;
+$this->testmode = false;
 
 // URL to post logging data to
 $this->url = "https://yourdomain.affinitylive.com/forms/public/issue";

--- a/conf/plugins/AutoAuth/config.php
+++ /dev/null
@@ -1,17 +1,1 @@
-<?php
-/** Auto Auth plugin Config
-*
-* Copyright (C) 2012 B Tasker
-* Released under GNU GPL V2
-* See LICENSE
-*
-*/
-defined('_CREDLOCK') or die;
 
-// Set this to false to disable the plugin
-$this->active = false;
-
-// Show a JS warning when the user clicks the button. Set to False to disable
-$this->warnredirect = true;
-
-?>

--- a/conf/plugins/AutoAuth/index.html
+++ /dev/null
@@ -1,8 +1,1 @@
-<html>
-  <head>
-    <title></title>
-    <meta content="">
-    <style></style>
-  </head>
-  <body></body>
-</html>
+

--- a/lib/.version.php
+++ b/lib/.version.php
@@ -13,7 +13,7 @@
 
 
 $versionmaj = "1.0";
-$versionmin = "1";
-$status = "b";
+$versionmin = "";
+$status = "";
 
 ?>

--- a/lib/API.php
+++ b/lib/API.php
@@ -15,6 +15,7 @@
 require_once 'lib/db/loggingdb.class.php';
 require_once 'lib/plugins.php';
 require_once 'lib/crypto.php';
+require_once 'lib/customer.class.php';
 
 $plg = new Plugins;
 $crypt = new Crypto;
@@ -81,7 +82,13 @@
     $key = 'Cre'.$cred->CredType;
 
     // Build the response
+
+    if ((BTMain::getUser()->PortalLogin != 1) || ($cred->hidden !=1)){
     $pass = htmlspecialchars($crypt->decrypt($cred->Hash,$key));
+    }else{
+    $pass = "<span style='font-size: x-small'>You are not authorised to view this password</span>";
+    }
+
     $address = htmlspecialchars($crypt->decrypt($cred->Address,$key));
     $uname = htmlspecialchars($crypt->decrypt($cred->UName,$key));
 
@@ -89,6 +96,10 @@
 	  $pass = "<a href='$pass' target=_blank title='Click to Open'>$pass</a>";
       }
 
+    // If a URL has been entered without http - See #31
+    if (substr($address,0,3) == "www"){
+    $address = "http://".$address;
+    }
 
     echo $pass.$opDivider."<a href='$address' target=_blank>".$address."</a>" .$opDivider. 
 	 $uname . $opDivider;

--- a/lib/Framework/main.php
+++ b/lib/Framework/main.php
@@ -151,10 +151,18 @@
 function buildACLQuery($tbl = false){
 $groups = BTMain::getUser()->groups;
 $tab ='';
-
 if ($tbl){
 $tab = "$tbl.";
 }
+
+
+if (BTMain::getUser()->PortalLogin == '1'){
+return "$tab.cust = '".BTMain::getUser()->PortalID."' ";
+
+}
+
+
+
 
 if (!in_array("-1",$groups)){
 return "$tab`Group`=" . implode(" OR $tab`Group`=",$groups) ;

--- a/lib/Handler.php
+++ b/lib/Handler.php
@@ -14,12 +14,16 @@
 require_once 'lib/plugins.php';
 require_once 'lib/db/loggingdb.class.php';
 require_once 'lib/crypto.php';
+require_once 'lib/customer.class.php';
 
 
 $html = new genOutput;
 $notifications = new notifications;
 $option = BTMain::getVar('option');
 $auth = new ProgAuth;
+
+$custportalmethods = array("logout","editCred");
+
 
     // See if the user has an active session
     if (BTMain::getsessVar('Session')){
@@ -70,7 +74,10 @@
 
 
 
-
+if ((BTMain::getUser()->PortalLogin == 1) && (!in_array($option,$custportalmethods))){
+$option = 'viewCust';
+BTMain::setVar('id',BTMain::getUser()->PortalID);
+}
 
 
 switch ($option){

--- a/lib/auth.class.php
+++ b/lib/auth.class.php
@@ -40,6 +40,48 @@
 
 return md5($salt.date('y-m-dHis'));
 
+}
+
+
+/** bcrypt the pass
+*
+* Ta to Jon Hulka on stackoverflow for this function!
+*
+*/
+function blowfishCrypt($password,$cost)
+{
+    $chars='./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+    $salt=sprintf('$2a$%02d$',$cost);
+//For PHP >= PHP 5.3.7 use this instead (as per martinstoeckli's suggestion)
+//    $salt=sprintf('$2y$%02d$',$cost);
+    //Create a 22 character salt -edit- 2013.01.15 - replaced rand with mt_rand
+    mt_srand();
+    for($i=0;$i<22;$i++) $salt.=$chars[mt_rand(0,63)];
+    return crypt($password,$salt);
+}
+
+
+/** Generate a random password of the specified length
+*
+* @arg length - INT
+*
+* @return string
+*
+*/
+function generatePassword($length = 8){
+
+ $key="(=?)+.,abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ $x = 0;
+ $p = '';
+
+  while ($x < $length){
+  $select = mt_rand(1,strlen($key)) - 1;
+  $p .= $key[$select];
+  $x++;
+  }
+
+return $p;
+  
 }
 
 
@@ -85,8 +127,8 @@
 	// We need to create a salt for the password
 	$user->salt = $this->createSalt();
 
-	// Salt the password
-	$user->pass = md5($pass.$user->salt);
+	// Salt the password - why the hell was I using MD5 here?? I don't remember doing that - embarassing
+	$user->pass = $this->blowfishCrypt($pass.$user->salt,12);
 
 	// Get the plaintext password out of memory
 	unset($pass);
@@ -121,7 +163,7 @@
 $user->salt = $this->createSalt();
 
 // Salt the password
-$user->pass = md5($plaintextPass.$user->salt);
+$user->pass = $this->blowfishCrypt($plaintextPass.$user->salt,12);
 $user->RealName = $RealName;
 $user->groups = $groups;
 $user->username = $username;
@@ -193,7 +235,11 @@
 $username = rtrim($username," ");
 
   if (!$user = $db->retrieveUserCreds($username)){
-  return false;
+    // Not a user, user. Check the Portal login
+    $cust = new CredLockCust;
+    if ((!BTMain::getConf()->custPortalEnabled) || (!$user = $cust->checkLogin($username))){
+      return false;
+    }
   }
 
 
@@ -205,21 +251,40 @@
 // Get the valid hash out of memory as we have it in an array anyway
 unset($user->pass);
 
-    if( md5($password.$pass[1]) != $pass[0]){
-      return $this->logFailedAttempt($username,$db);
+
+    if (crypt($password.$pass[1],$pass[0]) != $pass[0]){
+	// Check for a match on the old schema, there's only a risk of hash collision if the stored password is MD5
+	if (md5($password.$pass[1]) == $pass[0]){
+	  // We need to update the stored hash to use the new schema. Still don't remember going with MD5. Should know better!
+	  $db->updPass($username,$this->blowfishCrypt($password.$pass[1],12),$pass[1]);
+	}else{
+	  return $this->logFailedAttempt($username,$db);
+	}
+
       }
 
 // Create a Session ID
-    $sessID = md5(date('YmdHis') . mt_rand(10,80000) . mt_rand(11,500) . $username . mt_rand(0,90000));
+    $sessID = sha1(date('YmdHis') . mt_rand(10,80000) . mt_rand(11,500) . $username . mt_rand(0,90000));
 
 // Get the hashes out of memory
     unset($password);
     unset($pass);
 
 
+
+if ($user->membergroup != "-99,"){
+    $groups = explode(",","0,".$user->membergroup);
+    }else{
+    $groups = array("-99");
+    BTMain::setUserDetails('PortalLogin','1');
+    $username = $user->id;
+  }
+
 // Log the user in
 BTMain::setUser($username);
-BTMain::setUserDetails('groups',explode(",","0,".$user->membergroup));
+
+BTMain::setUserDetails('groups',$groups);
+
 BTMain::setUserDetails('RealName',$user->Name);
 
 
@@ -282,6 +347,7 @@
 }
 
 
+
 /** Invalid Login
 *
 */
@@ -320,8 +386,15 @@
  $user = $db->getUserSession($sessID);
 
   if (!$user){
-  $this->LoginInvalid();
-  }
+      $this->LoginInvalid();  
+  }
+
+$cust = new CredLockCust;
+if (is_numeric($user->User) && (BTMain::getConf()->custPortalEnabled) && ($usedets = $cust->checkSession($user->User))){
+$user->username = $usedets->email;
+$user->Name = $usedets->Name;
+$user->membergroup = "-99,";
+}
 
 $expiry = strtotime($user->Expires);
 
@@ -344,7 +417,18 @@
 
 // Users session is valid
 BTMain::setUser($user->username);
-BTMain::setUserDetails('groups',explode(",","0,".$user->membergroup));
+
+
+if ($user->membergroup != "-99,"){
+    $groups = explode(",","0,".$user->membergroup);
+    }else{
+    $groups = array("-99");
+    BTMain::setUserDetails('PortalLogin','1');
+    BTMain::setUserDetails('PortalID',$user->User);
+  }
+
+
+BTMain::setUserDetails('groups',$groups);
 BTMain::setUserDetails('RealName',$user->Name);
 
 

--- a/lib/crypto.php
+++ b/lib/crypto.php
@@ -58,14 +58,15 @@
 $keylength = strlen($key);
 $kpos = 0;
 $en = "";
-
+$k = explode(":",$key);
 $str = explode(" ",$str);
 
 foreach ($str as $string){
 	 if (strlen($string) == 0){ continue; }
         
 	// Convert the character in the key to a charcode and use bitwise XOR
-        $b = $string ^ ord($key[$kpos]);
+        $b = $string ^ ord($k[1][$kpos]);
+	$b = $b ^ ord($k[0][$kpos]);
 	
 	// Convert the result back to the appropriate character
         $en .= chr($b);
@@ -82,6 +83,11 @@
 
 
 
+function inlineEncrypt($string){
+return "<div class='inlineTLS'>".base64_encode($this->xorestring(base64_encode($string),$tlskey = BTMain::getsessVar('tls')))."</div>";
+}
+
+
 /** Xor the provided string to encrypt it
 *
 * @arg str - string
@@ -95,6 +101,7 @@
 $strlength = strlen($str);
 $kpos = 0;
 $en = "";
+$k = explode(":",$key);
 
 $i = 0;
 
@@ -104,8 +111,9 @@
         $a = ord($str[$i]);
 
 	// Perform a bitwise XOR
-        $b = $a ^ ord($key[$kpos]) ;    
+        $b = ($a ^ ord($k[0][$kpos])) ^ ord($k[1][$kpos]);    
         
+    
 	// add to the string
 	$en .= $b." ";
 	
@@ -145,12 +153,16 @@
 // Upped from 1024 because commit cae0ac5 increases the likelihood of key repetition
   while ($x <= 256){
 	$key = mt_rand(48,122);
+	$key2 = mt_rand(48,122);
+
 	if (in_array($key,$excludes)){ continue; }
+	if (in_array($key2,$excludes)){ continue; }
 	$str .= chr($key);
+	$str2 .= chr($key2);
 	$x++;
   }
 
-return $str;
+return $str.":".$str2;
 }
 
 

--- /dev/null
+++ b/lib/customer.class.php
@@ -1,1 +1,174 @@
+<?php
+/** Customer Portal related functions
+*
+* Copyright (c) 2012 B Tasker
+* Released under GNU GPL V2
+* See LICENSE
+*
+*/ 
 
+defined('_CREDLOCK') or die;
+
+class CredLockCust{
+
+/** Add a customer 
+*
+* @arg name - Customer's name
+* @arg group - Group ID
+* @arg firstname - Their given name
+* @arg surname - Family name
+* @arg email - Email address
+*
+*/
+function add($name,$group,$firstname,$surname,$email){
+$db = new CustDB;
+$id = $db->addCustomer($name,$group,$firstname,$surname,$email);
+$auth = new ProgAuth;
+$db = new AuthDB;
+
+if (!$id){
+  return false;
+}
+
+// We add the customer to the portal, even if we won't let them log-in (i.e. the portal is disabled)
+$password = $auth->generatePassword();
+$salt = $auth->createSalt();
+$pass = ProgAuth::blowfishCrypt($password.$salt,12);
+
+
+
+if ($db->addCusttoPortal($id,$email,$pass.":".$salt,1)) {
+
+    if (BTMain::getConf()->custPortalEnabled){
+    global $notifications;
+    $not->className = 'alert alert-success';
+    $not->text = "The customer has been successfully added to the customer portal and can use the password <i>$password</i> to manage their credentials";
+    // This echo is a temporary thing until I update Notifications
+    echo "<div class='{$not->className}'>{$not->text}</div>";
+
+
+    $notifications->setNotification($not);
+    }
+
+
+
+}
+
+return $id;
+
+}
+
+
+
+
+
+/** Edit a Customer record - including updating the portal details (i.e. update the email address if changed)
+*
+*/
+function edit($id,$name,$group,$firstname,$surname,$email){
+
+$db = new CustDB;
+$auth = new ProgAuth;
+
+if (!$db->editCustomer($id,$name,$group,$firstname,$surname,$email)){
+return false;
+}
+
+$db = new AuthDB;
+// We add the customer to the portal, even if we won't let them log-in (i.e. the portal is disabled)
+$password = $auth->generatePassword();
+$salt = $auth->createSalt();
+$pass = ProgAuth::blowfishCrypt($password.$salt,12);
+
+
+ global $notifications;
+
+if ($db->addCusttoPortal($id,$email,$pass.":".$salt,1)){
+ 
+
+
+   
+
+    $not->className = 'alert alert-success';
+    $not->text = "The customer has been successfully added to the customer portal and can use the password <i>$password</i> to manage their credentials";
+    // This echo is a temporary thing until I update Notifications
+    echo "<div class='{$not->className}'>{$not->text}</div>";
+   // $notifications->setNotification($not);
+    
+
+}else{
+    echo "<div class='alert alert-error'>Unable to update Customer Portal details</div>";
+}
+
+  
+
+  
+return true;
+
+
+
+
+}
+
+
+/** Called by ProgAuth::ProcessLogin - See if the supplied username matches any in the PortalDB
+*
+* @arg username
+*
+* @return object or false
+*/
+function checkLogin($username){
+$db = new AuthDB;
+
+if (!$user = $db->getPortalByUsername($username)){
+return false;
+}
+$crypt = new Crypto;
+
+// Set the customer indicator
+$user->membergroup = "-99,";
+$user->Name = $crypt->decrypt($user->ContactName,'auth')." ".$crypt->decrypt($user->Surname,'auth');
+return $user;
+
+
+
+}
+
+
+
+/** Portal sessions have an ID rather than a username stored against them. See if that ID matches our table
+*
+*/
+function checkSession($id){
+$db = new AuthDB;
+$crypt = new Crypto;
+
+$usedets = $db->getPortalByID($id);
+
+if (!$usedets){
+  return false;
+ }
+
+$usedets->membergroup = "-99,";
+$usedets->Name = $crypt->decrypt($usedets->ContactName,'auth')." ".$crypt->decrypt($usedets->ContactSurname,'auth');
+$usedets->email = $crypt->decrypt($usedets->email,'auth');
+return $usedets;
+}
+
+
+
+
+
+
+
+
+
+
+
+}
+
+
+
+
+?>
+

--- a/lib/db/Credentials.php
+++ b/lib/db/Credentials.php
@@ -60,7 +60,7 @@
 
 $id = $this->stringEscape($id);
 
-$sql = "SELECT Hash, Clicky, Address, UName, CredType, `Group` FROM #__Cred WHERE id='$id' AND ($ACL)";
+$sql = "SELECT Hash, Clicky, Address, UName, CredType, `hidden`, `Group` FROM #__Cred WHERE id='$id' AND ($ACL)";
 $this->setQuery($sql);
 
 
@@ -211,7 +211,7 @@
 *
 * @return object
 */
-function addCred($cust,$credtype,$cred,$clicky,$group = 1,$address = '', $uname = '')
+function addCred($cust,$credtype,$cred,$comment,$clicky,$group = 1,$address = '', $uname = '',$hidden = 0)
 {
 
 
@@ -229,20 +229,26 @@
 
 if (!empty($cred)){
 $cred = $crypt->encrypt($cred,'Cre'.$credtype);
+}
+
+if (!empty($comment)){
+$comment = $crypt->encrypt($cred,'Cre'.$comment);
 }
 
 $address = $this->stringEscape($address);
 $uname = $this->stringEscape($uname);
 $credtype = $this->stringEscape($credtype);
 $cred = $this->stringEscape($cred);
+$comment = $this->stringEscape($comment);
 $cust = $this->stringEscape($cust);
 $clicky = $this->stringEscape($clicky);
 $date = date('Y-m-d H:i:s');
 $group = $this->stringEscape($group);
-
-
-$sql = "INSERT INTO #__Cred (`cust`,`Added`,`Group`,`Hash`,`CredType`,`Clicky`,`Address`,`UName`) ".
-"VALUES ('$cust','$date','$group','$cred','$credtype','$clicky','$address','$uname')";
+$hidden = $this->stringEscape($hidden);
+
+
+$sql = "INSERT INTO #__Cred (`cust`,`Added`,`Group`,`Hash`,`CredType`,`Clicky`,`Address`,`UName`,`hidden`,`comment`) ".
+"VALUES ('$cust','$date','$group','$cred','$credtype','$clicky','$address','$uname','$hidden','$comment')";
 $this->setQuery($sql);
 
 $id = $this->insertID();
@@ -272,7 +278,7 @@
 *
 * @return object
 */
-function editCred($id,$credtype,$cred,$clicky,$group = 1,$address = '', $uname = '')
+function editCred($id,$credtype,$cred,$comment, $clicky,$group = 1,$address = '', $uname = '', $hidden = 0)
 {
 
 
@@ -280,6 +286,7 @@
 $crypt = new Crypto;
 $ACL = BTMain::buildACLQuery();
 $credtype = $this->stringEscape($credtype);
+$hidden = $this->stringEscape($hidden);
 $id = $this->stringEscape($id);
 $date = date('Y-m-d H:i:s');
 $group = $this->stringEscape($group);
@@ -287,7 +294,7 @@
 
 // build the SQL
 
-$sql = "UPDATE #__Cred SET `Added`='$date', `Group`='$group',";
+$sql = "UPDATE #__Cred SET `Added`='$date', `Group`='$group', hidden='$hidden',";
 
 if ($cred){
 $cred = $crypt->encrypt($cred,'Cre'.$credtype);
@@ -312,6 +319,14 @@
 $uname = $this->stringEscape($uname);
 $sql .= "`UName`='$uname',";
 }
+
+if ($comment){
+$comment = $crypt->encrypt($comment,'Cre'.$credtype);
+$comment = $this->stringEscape($comment);
+$sql .= "`comment`='$comment',";
+
+}
+
 
 // Get rid of the last comma to prevent a syntax error
 $sql = rtrim($sql,",");

--- a/lib/db/Customer.php
+++ b/lib/db/Customer.php
@@ -48,7 +48,7 @@
 $log = new Logging;
 $log->logEntry($newcust,3);
 
-return true;
+return $newcust;
 
 
 
@@ -171,7 +171,7 @@
 
 $ACL = BTMain::buildACLQuery();
 
-$sql = "SELECT a.CredType, a.id, b.Name as CredName, c.Name FROM #__Cred as a LEFT JOIN #__CredTypes as b on a.CredType = b.id LEFT JOIN #__Cust as c ON a.cust = c.id ".
+$sql = "SELECT a.CredType, a.id, a.comment, a.hidden, b.Name as CredName, c.Name FROM #__Cred as a LEFT JOIN #__CredTypes as b on a.CredType = b.id LEFT JOIN #__Cust as c ON a.cust = c.id ".
 "WHERE a.cust='$id' AND (" . str_replace("`Group`","a.`Group`",$ACL) . ") AND (" . str_replace("`Group`","c.`Group`",$ACL) . ")";
 $this->setQuery($sql);
 return $this->loadResults();

--- a/lib/db/authdb.class.php
+++ b/lib/db/authdb.class.php
@@ -26,6 +26,70 @@
 
 }
 
+
+
+
+/** Add a customer record to the portal authentication table
+*
+* @arg id - Customers Id
+* @arg email - Customer's login email address
+* @arg pass - A pre-salted pass phrase
+* @arg active - tinyint(1)
+*
+* @return mysql object
+*/
+function addCusttoPortal($id,$email,$pass,$active = 0){
+
+$crypt = new Crypto;
+
+$id = $this->stringEscape($id);
+$email = $this->stringEscape($crypt->encrypt($email,'auth'));
+$pass = $this->stringEscape($crypt->encrypt($pass,'auth'));
+$active = $this->stringEscape($active);
+
+
+
+$sql = "INSERT INTO #__CustPortal VALUES('$id','$email','$pass','$active') ON DUPLICATE KEY UPDATE `email`='$email'";
+$this->setQuery($sql);
+return $this->runQuery();
+
+}
+
+
+
+
+
+/** See if a Customer Portal record exists, and return it if it does
+* @arg username
+* @arg state - 1/0 - Does the user have to be active? 
+*
+* @return object
+*/
+function getPortalByUsername($username,$state = 1){
+$crypt = new Crypto;
+$username=$this->stringEscape($crypt->encrypt($username,'auth'));
+$state = $this->stringEscape($state);
+$sql = "SELECT a.*, b.ContactName, b.ContactSurname FROM #__CustPortal as a LEFT JOIN #__Cust as b ON a.id = b.id WHERE a.`email`='$username' AND a.active LIKE '$state'";
+$this->setQuery($sql);
+return $this->loadResult();
+}
+
+/** Get a Portal user's record by ID
+* @arg id
+* @arg state - 1/0 - Does the user have to be active? 
+*
+* @return object
+*/
+function getPortalByID($id, $state = 1){
+
+$id=$this->stringEscape($id);
+$state = $this->stringEscape($state);
+$sql = "SELECT a.*, b.ContactName, b.ContactSurname FROM #__CustPortal as a LEFT JOIN #__Cust as b ON a.id = b.id WHERE a.`id`='$id' AND a.active LIKE '$state'";
+$this->setQuery($sql);
+return $this->loadResult();
+
+
+}
 
 /** If an IP has crossed the ban threshold, ban them
 *
@@ -149,6 +213,25 @@
 
 return $this->runQuery();
 }
+
+
+
+/** Update a user's password hash in the database
+*
+*/
+function updPass($user,$hash,$salt){
+  $crypt = new Crypto;
+  $user = $this->stringEscape($user);
+  $hash = $this->stringEscape($crypt->encrypt($hash.":".$salt,'auth'));
+
+  $sql = "UPDATE #__Users SET `pass`='$hash' WHERE `username`='$user'";
+  
+  $this->setQuery($sql);
+  $result = $this->runQuery();
+
+}
+
+
 
 
 /** Edit user
@@ -351,7 +434,7 @@
 $date = date('Y-m-d H:i:s');
 $ip = BTMain::getip();
 
-$sql = "SELECT a.ClientIP, a.SessKey, a.`Expires`, b.* FROM #__Sessions as a LEFT JOIN #__Users as b ON a.User = b.username WHERE a.SessionID = '$sess' AND a.Expires > '$date' AND a.`ClientIP` = '$ip'";
+$sql = "SELECT a.ClientIP, a.SessKey, a.`Expires`, a.User, b.* FROM #__Sessions as a LEFT JOIN #__Users as b ON a.User = b.username WHERE a.SessionID = '$sess' AND a.Expires > '$date' AND a.`ClientIP` = '$ip'";
 $this->setQuery($sql);
 
 return $this->loadResult();

--- a/lib/db/loggingdb.class.php
+++ b/lib/db/loggingdb.class.php
@@ -41,7 +41,12 @@
 
 $loggingenabled = BTMain::getConf()->loggingenabled;
 
-$user = BTMain::getUser()->name;
+// Added to allow logging of Portal actions without revealing email addresses
+if (BTMain::getUser()->PortalID){
+  $user = BTMain::getUser()->PortalID;
+}else{
+  $user = BTMain::getUser()->name;
+}
 
 $useres = $this->stringEscape($user);
 $credes = $this->stringEscape($cred);

--- a/lib/includes/groupSelection.php
+++ b/lib/includes/groupSelection.php
@@ -26,7 +26,20 @@
 
 $groups = $auth->retrieveGroupNames();
 
-if ($multiselect != 1):
+if (BTMain::getUser()->PortalLogin == 1):?>
+
+
+<select name="frmGroup" id="frmGroup" style="display: none;">
+<option value="<?php if (isset($preselect)){ echo $preselect; } else{ echo 0;}?>">nochange</option>
+</select>
+
+</select>
+
+
+<?php else: ?>
+
+
+<?php if ($multiselect != 1):
 ?> 
 <label for="frmGroup">Group</label><select name="frmGroup" id="frmGroup">
 <option value='null'> -- Select Group --</option>
@@ -77,3 +90,5 @@
 ?>
 </fieldset>
 <?php endif;?>
+
+<?php endif; ?>

--- a/lib/output.php
+++ b/lib/output.php
@@ -37,7 +37,7 @@
 
 
 
-return $str . $notif->text . "</div>\n";
+return $str . ">" . $notif->text . "</div>\n";
 
 }
 
@@ -147,10 +147,10 @@
 
 $coreres->js->jquery->fname = 'jquery';
 $coreres->js->jquery->forcemin = '.min';
+$coreres->js->jquerytooltip->fname = 'jquery.tooltip';
+$coreres->js->jquerytooltip->forcemin = '.min';
 $coreres->js->bootstrap->fname = 'bootstrap';
 $coreres->js->bootstrap->path = 'bootstrap/js/';
-$coreres->js->jquerytooltip->fname = 'jquery.tooltip';
-$coreres->js->jquerytooltip->forcemin = '.min';
 $coreres->js->main->fname = 'main';
 $coreres->js->base64->fname = 'base64';
 
@@ -222,8 +222,8 @@
 	    <link rel="stylesheet" type="text/css" href='<?php echo $coreres->resources->resourcespath; ?>/<?php echo $css;?>.css'/>
     <?php endforeach;?>
 
-
-      <script id='kFile' src="Resources/info.php?<?php echo md5(session_id().$_SERVER['REMOTE_ADDR']); ?>" type="text/javascript"></script>
+      <style type="text/css">.inlineTLS {display: inline;}</style>
+      <script id='kFile' src="Resources/info.php?<?php $frs = BTMain::getSessVar('cacheFrustrate'); echo md5(session_id().$_SERVER['REMOTE_ADDR']).$frs; ?><?php $notif=BTMain::getVar('notif'); if (!empty($notif) && ($notif == 'LoginFailed' || $notif == 'LoggedOut' || $notif == 'InvalidSession')){ echo "&destSession=Y"; BTMain::setSessVar('cacheFrustrate',mt_rand());}?>" type="text/javascript"></script>
 
 
       <?php
@@ -266,7 +266,7 @@
 
 <!-- Fire the default scripts when the browser reports document ready -->
     <script type="text/javascript">
-    var sesscheck; jQuery(document).ready(function() {  checkKeyAvailable(); <?php if (BTMain::getUser()->name):?>sesscheck = setInterval("checkSession()",120000);<?php endif;?>});
+    var sesscheck; jQuery(document).ready(function() {  checkKeyAvailable(); inlineDeCrypt();<?php if (BTMain::getUser()->name):?>sesscheck = setInterval("checkSession()",120000);<?php endif;?>});
     </script>
 
 <?php
@@ -474,7 +474,7 @@
 $this->customJS[] = "$js";
 }
 
-
+/** TODO add support for custom notifications passed as an argument **/
 }
 
 

--- a/modules/login-navbar/login-navbar.php
+++ b/modules/login-navbar/login-navbar.php
@@ -22,7 +22,7 @@
 
 	<ul class="dropdown-menu" role="menu" id='CurUserMenu' aria-Labelled-by='dLabel'>
 	  <li><a href="index.php?option=logout">Log Out</a></li>
-	  <li><a href="index.php?option=changePassword">Change Password</a></li>
+	 <?php if (BTMain::getUser()->PortalLogin != 1):?> <li><a href="index.php?option=changePassword">Change Password</a></li><?php endif; ?>
 	</ul>
 
 

--- a/modules/menu/menu.php
+++ b/modules/menu/menu.php
@@ -19,7 +19,7 @@
 <li id="menuDivi1" class="divider-vertical"></li>
 
 <?php
-if (BTMain::getUser()->name):
+if (BTMain::getUser()->name && (!in_array("-99",BTMain::getUser()->groups))):
 
 $this->loadModule('search-table');
 ?>
@@ -60,7 +60,8 @@
 <div class="pull-right" style="position: relative">
  
 
-<?php if (BTMain::getUser()->name):?>
+<?php if (BTMain::getUser()->name && (!in_array("-99",BTMain::getUser()->groups))): ?>
+
   <!-- Search Box -->
   <?php $this->loadModule('search'); ?>
   <!-- Search Box ends -->

--- a/modules/search-table/search-table.php
+++ b/modules/search-table/search-table.php
@@ -350,7 +350,7 @@
     <td></td>
     <td>plg</td>
     <td>plgInfo</td>
-    <td>type=Res</td>
+    <td>type=Resources</td>
   </tr>
 
 	<?php

--- a/plugins/Blargle/ExternalResources/ExternalResources.php
+++ b/plugins/Blargle/ExternalResources/ExternalResources.php
@@ -12,7 +12,7 @@
 /**                            Resources PLUGIN STARTS                            **/
 
 
-
+ 
 /** Credentials Plugin class
 *
 */
@@ -29,7 +29,7 @@
 /** Return the plugin details
 *
 */
-function getDetails(){
+function getPlgDetails(){
 $details->Name = "plg_ExternalResources";
 $details->Description = "Allows the contents of the Resources directory (excluding info.php) to be moved to another server";
 $details->Author = 'B Tasker';

--- a/plugins/Blargle/ExternalResources/README.html
+++ b/plugins/Blargle/ExternalResources/README.html
@@ -3,6 +3,7 @@
 <ul>
   <li><a href="#about">About ExternalResources</a></li>
   <li><a href="#plgConfig">Plugin Configuration</a>
+  <li><a href="#SupResources">Supported Resources</a>
 </ul>
 
 
@@ -22,7 +23,7 @@
   <li><b><i>active</i></b> - Is the plugin enabled?</li>
   <li><b><i>url</i></b> - The main resources path to use, this allows all files (except info.php) to be automatically redirected to another URL. Each file's path will be appended onto the end</li>
 </ul>
-
+<br />
 <h4>Additional configuration</h4>
 
 It's also possible to override on a per-resource basis:
@@ -39,6 +40,22 @@
 <li><b>fname</b> - The filename to load (.js will be appended automatically)</li>
 <li><b>forcemin</b> - Force use of minimal code, takes one of three options - false, ".min","". The first disables the setting, the second forces the fname to be followed by .min and the final ensures that .min will never be added, regardless of the main system config</li>
 
+</ul>
 
 
 
+<h3><a name="SupResources">Supported Resources</a></h3>
+<p>Resources must be specified by internal name, the following are used in PHPCredLocker</p>
+<ul>
+	<li><em>css-&gt;bootstrap</em></li>
+	<li><em>css-&gt;bootstrapresponsive</em></li>
+	<li><em>css-&gt;jquerytooltip</em></li>
+	<li><em>js-&gt;jquery</em></li>
+	<li><em>js-&gt;bootstrap</em></li>
+	<li><em>js-&gt;jquerytooltip</em></li>
+	<li><em>js-&gt;main</em></li>
+	<li><em>js-&gt;base64</em></li>
+</ul>
+<p>Each can be overriden in the plugin config in the manner specified above</p>
+
+

--- a/views/Creds/add.php
+++ b/views/Creds/add.php
@@ -20,6 +20,8 @@
   $cred = BTMain::getVar('frmCredential');
   $addr = BTMain::getVar('frmAddress');
   $user = BTMain::getVar('frmUser');
+  $hidden = BTMain::getVar('frmHidden');
+  $comment = BTMain::getVar('frmComment');
   
   if (!BTMain::getConnTypeSSL()){
 	    $crypt = new Crypto;
@@ -27,10 +29,11 @@
 	    $cred = $crypt->xordstring(base64_decode($cred),$tlskey);
 	    $addr = $crypt->xordstring(base64_decode($addr),$tlskey);
 	    $user = $crypt->xordstring(base64_decode($user),$tlskey);
+	    $comment = $crypt->xordstring(base64_decode($comment),$tlskey);
 	 }
 
 
-  $newcred = $creds->addCred(BTMain::getVar('cust'),BTMain::getVar('FrmCredType'),$cred,BTMain::getVar('frmClicky'),BTMain::getVar('frmGroup'),$addr,$user);
+  $newcred = $creds->addCred(BTMain::getVar('cust'),BTMain::getVar('FrmCredType'),$cred,$comment,BTMain::getVar('frmClicky'),BTMain::getVar('frmGroup'),$addr,$user,$hidden);
   // Add the cred to the db
   if ($newcred){
   // Success
@@ -121,10 +124,12 @@
 <label for="frmCredential"><?php echo Lang::_("Password");?></label><textarea id="frmCredential" name="frmCredential"></textarea>
 <a href="javascript: genPwd('frmCredential',10);">Generate Password</a>
 
+<label for="frmComment"><?php echo Lang::_("Comment");?></label><input type="text" name="frmComment" id="frmComment">
+
 <label for="frmAddress"><?php echo Lang::_("Address");?></label><input type="text" name="frmAddress" id="frmAddress">
 
 
-
+<label for="frmCredentialHidden">Hide from Customer</label><input type="checkbox" name="frmHidden" id="frmHidden" value="1">
 <?php include 'lib/includes/groupSelection.php'; ?>
 
 

--- a/views/Creds/edit.php
+++ b/views/Creds/edit.php
@@ -30,6 +30,13 @@
 $address = BTMain::getVar('frmAddress');
 $uname = BTMain::getVar('frmUser');
 $group = BTMain::getVar('frmGroup');
+$comment = BTMain::getVar('frmComment');
+
+if (BTMain::getUser()->PortalLogin != 1){
+$hidden = BTMain::getVar('frmHidden');
+}else{
+$hidden = 0;
+}
   
   if (!BTMain::getConnTypeSSL()){
 	    $crypt = new Crypto;
@@ -37,6 +44,7 @@
 	    $cred = $crypt->xordstring(base64_decode($cred),$tlskey);
 	    $address = $crypt->xordstring(base64_decode($address),$tlskey);
 	    $uname = $crypt->xordstring(base64_decode($uname),$tlskey);
+	    $comment = $crypt->xordstring(base64_decode($comment),$tlskey);
 	 }
 
 
@@ -48,10 +56,11 @@
 if ($group == "NOCHANGE"){ $group = false; }
 if ($address == "NOCHANGE"){ $address = false; }
 if ($uname == "NOCHANGE"){ $uname = false; }
+if ($comment == 'NOCHANGE'){ $comment = false; }
 
 
   // Add the cred to the db
-  if ($creds->editCred($id,$credtype,$cred,$clicky,$group,$address,$uname)){
+  if ($creds->editCred($id,$credtype,$cred,$comment, $clicky,$group,$address,$uname,$hidden)){
   // Success
   $notifications->setNotification("addCredSuccess");
       $data->cred->id = $id;
@@ -119,14 +128,14 @@
 
 <label for='FrmCredType'><?php echo Lang::_("Credential Type");?></label><select id="FrmCredType" name="FrmCredType" readonly='readonly'>
 <?php 
-foreach ($credtypes as $cred){
+foreach ($credtypes as $credt){
 
 ?>
-<option value="<?php echo $cred->id;?>" 
-<?php if ($credtype == $cred->id):?>
+<option value="<?php echo $credt->id;?>" 
+<?php if ($credtype == $credt->id):?>
 selected
 <?php endif; ?>
-><?php echo htmlspecialchars($crypt->decrypt($cred->Name,'CredType'));?></option>
+><?php echo htmlspecialchars($crypt->decrypt($credt->Name,'CredType'));?></option>
 <?php
 
 }
@@ -138,7 +147,14 @@
 
 <label for="frmCredential"><?php echo Lang::_("Password");?></label><textarea id="frmCredential" name="frmCredential">NOCHANGE</textarea>
 <a href="javascript: genPwd('frmCredential',10);">Generate Password</a>
+
+<label for="frmComment"><?php echo Lang::_("Comment");?></label><input type="text" name="frmComment" id="frmComment" value="NOCHANGE">
+
 <label for="frmAddress"><?php echo Lang::_("Address");?></label><input type="text" name="frmAddress" id="frmAddress" value="NOCHANGE">
+
+<?php if (BTMain::getUser()->PortalLogin != 1): ?>
+<label for="frmCredentialHidden">Hide from Customer</label><input type="checkbox" name="frmHidden" id="frmHidden" value="1" <?php if ($cred->hidden){ echo "checked"; }?>>
+<?php endif; ?>
 
 <?php
 

--- a/views/Customer/add.php
+++ b/views/Customer/add.php
@@ -14,8 +14,8 @@
 
 if (BTMain::getVar('AddCustSubmitted')){
 
-$db = new CustDB;
 
+$cust = new CredLockCust;
 $crypt = new Crypto;
 $frmname = BTMain::getVar('FrmName');
 $fname = BTMain::getVar('FrmconName');
@@ -34,7 +34,7 @@
 	 }
 
 
-if ($db->addCustomer(htmlspecialchars($frmname),BTMain::getVar('frmGroup'),htmlspecialchars($fname),htmlspecialchars($sname),htmlspecialchars($email))){
+if ($cust->add(htmlspecialchars($frmname),BTMain::getVar('frmGroup'),htmlspecialchars($fname),htmlspecialchars($sname),htmlspecialchars($email))){
 
 
 $notifications->setNotification("addCustSuccess");

--- a/views/Customer/edit.php
+++ b/views/Customer/edit.php
@@ -15,10 +15,11 @@
 $notifications->setPageTitle("Edit ".Lang::_('Customer'));
 
 if (BTMain::getVar('EditCustSubmitted')){
+$cust = new CredLockCust;
 
 
 
-if ($db->editCustomer(BTMain::getVar('id'),htmlspecialchars(BTMain::getVar('FrmName')),BTMain::getVar('frmGroup'),htmlspecialchars(BTMain::getVar('FrmconName')),htmlspecialchars(BTMain::getVar('FrmSurname')),htmlspecialchars(BTMain::getVar('FrmEmail')))){
+if ($cust->edit(BTMain::getVar('id'),htmlspecialchars(BTMain::getVar('FrmName')),BTMain::getVar('frmGroup'),htmlspecialchars(BTMain::getVar('FrmconName')),htmlspecialchars(BTMain::getVar('FrmSurname')),htmlspecialchars(BTMain::getVar('FrmEmail')))){
 
 
 $notifications->setNotification("EditCustSuccess");

--- a/views/Customer/view.php
+++ b/views/Customer/view.php
@@ -10,9 +10,10 @@
 global $notifications;
 $custom = new CustDB;
 $custom->connreuse = 1;
+$portallogin = BTMain::getUser()->PortalLogin;
 
 
-
+if ($portallogin != 1){
 // Get the customer details
 $custdetails = $custom->getCustomerDetail(BTMain::getVar('id'));
 
@@ -23,6 +24,8 @@
   return;
 
   }
+
+}
 
 
 // Get credentials
@@ -50,7 +53,7 @@
 
 
 
-
+<?php if ($portallogin != 1): ?>
 <h1>Credentials for <?php echo $customer; ?></h1>
 
 
@@ -61,6 +64,8 @@
 
 </div>
 
+<?php endif; ?>
+
 <input type="hidden" id="defaultInterval" value="<?php echo BTMain::getConf()->CredDisplay; ?>">
 <table class='credTbl table table-hover' id='CredsTbl'>
 <tr><th><span class='DisPwdText'>Credential </span>Type</th><th></th>
@@ -70,25 +75,37 @@
 <th></th><th></th><th></th></tr>
 
 <?php
-
+$x = 0;
 
 
 foreach ($customers as $customer){
+$x++;
 ob_start();
 $cname = $crypt->decrypt($customer->CredName,'CredType');
+$comment = $crypt->decrypt($customer->comment,'Cre'.$customer->CredType);
 ?>
 
 <tr class="CredDisp" id='CredDisp<?php echo $customer->id;?>'>
-  <td>
+  <td <?php if (!empty($comment)):?>title="<?php echo htmlspecialchars($comment);?>"<?php endif;?>>
     <?php echo $cname;?>
   </td>
 
-  
+
   <td class="passViewNotif" onclick="getCreds('<?php echo $customer->id;?>');">
   <input type="hidden" id="clickCount<?php echo $customer->id;?>" value="0" disabled="disabled">
     <input type="hidden" id="PassCount<?php echo $customer->id;?>" value="<?php echo BTMain::getConf()->CredDisplay; ?>">
-    <span class='retrievePassword' id='retrievePassword<?php echo $customer->id;?>'>Display<span class='DisPwdText'> Password</span></span>
+    <span class='retrievePassword' id='retrievePassword<?php echo $customer->id;?>'>Display<span class='DisPwdText'> 
+
+  <?php if (($portallogin != 1) || ($customer->hidden !=1)): ?>
+      Password
+  <?php else: ?>
+      Username <input type="hidden" disabled="disabled" id="credHidden<?php echo $customer->id; ?>">
+  <?php endif; ?>
+</span></span>
   </td>
+
+   
+
 
   <td>
     <span id='Address<?php echo $customer->id;?>' class='CredAddress'></span>
@@ -104,11 +121,11 @@
 
 
 <td class='editicon' onclick="window.location.href = 'index.php?option=editCred&id=<?php echo $customer->id;?>'">
-<i class="icon-pencil"></i>
+ <?php if (($portallogin != 1) || ($customer->hidden !=1)): ?><i class="icon-pencil"></i><?php endif; ?>
 </td>
 
   <td class='delicon' onclick="DelCred('<?php echo $customer->id;?>');">
-  <i class="icon-remove"></i>
+  <?php if ($portallogin != 1): ?><i class="icon-remove"></i><?php endif; ?>
   </td>
 
   <td id='CredPluginOutput<?php echo $customer->id;?>' class="CredPluginOutput">
@@ -119,7 +136,14 @@
 
 <?php
 
+if (!isset($custs[$cname])){
 $custs[$cname] = ob_get_clean();
+}else{
+$custs[$cname."-".$x] = ob_get_clean();
+
+}
+
+
 }
 ksort($custs);
 echo implode("\n",$custs);
@@ -130,6 +154,8 @@
 <br />
 
 
+<?php if ($portallogin != 1): ?>
+
 <div class='viewButtons'>
 
 <button id='EditCustBtnBottom' onclick="window.location.href='index.php?option=EditCustomer&id=<?php echo htmlspecialchars(BTMain::getVar('id')); ?>';" class='btn btn-primary'>Edit <?php echo Lang::_('Customer');?></button>
@@ -137,3 +163,11 @@
 
 </div>
 
+<?php endif; ?>
+
+
+<script type="text/javascript">
+$('#CredsTbl *').tooltip({track: true, fade: 250});
+</script>
+
+

--- a/views/plugins/loaded.php
+++ b/views/plugins/loaded.php
@@ -179,7 +179,7 @@
 
     ?>
     <tr>
-	<td><?php echo $plugin; ?></td><td><?php echo Plugins::transStatus($status);?></td><td><a href="index.php?option=plgInfo&plg=<?php echo $plugin;?>&type=Res">View ReadMe</a></td>
+	<td><?php echo $plugin; ?></td><td><?php echo Plugins::transStatus($status);?></td><td><a href="index.php?option=plgInfo&plg=<?php echo $plugin;?>&type=Resources">View ReadMe</a></td>
     </tr>
     <?php
     }