2013-03-06 11:34:22 +01:00
< ? php
App :: uses ( 'AppModel' , 'Model' );
App :: uses ( 'AuthComponent' , 'Controller/Component' );
/**
* User Model
*
* @ property Role $Role
* @ property Event $Event
*/
class User extends AppModel {
/**
* Display field
*
* @ var string
*/
public $displayField = 'email' ;
2015-04-20 11:46:55 +02:00
public $orgField = array ( 'Organisation' , 'name' ); // TODO Audit, LogableBehaviour + org
2013-03-06 11:34:22 +01:00
2013-08-21 11:33:30 +02:00
2013-03-06 11:34:22 +01:00
/**
* Validation rules
*
* @ var array
*/
public $validate = array (
'role_id' => array (
'numeric' => array (
'rule' => array ( 'numeric' ),
//'message' => 'Your custom message here',
//'allowEmpty' => false,
//'required' => false,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
),
'password' => array (
'minlength' => array (
2015-01-27 10:41:43 +01:00
'rule' => array ( 'passwordLength' ),
'message' => 'Password length requirement not met.' ,
2013-03-06 11:34:22 +01:00
//'allowEmpty' => false,
'required' => true ,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
'complexity' => array (
'rule' => array ( 'complexPassword' ),
2015-01-27 10:41:43 +01:00
'message' => 'Password complexity requirement not met.' ,
2013-03-06 11:34:22 +01:00
//'allowEmpty' => false,
//'required' => true,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
'identical' => array (
'rule' => array ( 'identicalFieldValues' , 'confirm_password' ),
'message' => 'Please re-enter your password twice so that the values match.' ,
//'allowEmpty' => false,
//'required' => true,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
),
2015-10-12 09:41:20 +02:00
2015-04-14 17:51:38 +02:00
'org_id' => array (
2015-10-09 15:59:25 +02:00
'valueNotEmpty' => array (
'rule' => array ( 'valueNotEmpty' ),
2013-03-06 11:34:22 +01:00
),
2015-03-21 14:27:53 +01:00
'numeric' => array (
2015-10-18 20:51:37 +02:00
'rule' => array ( 'numeric' ),
2015-03-21 14:27:53 +01:00
'message' => 'The organisation ID has to be a numeric value.' ,
2013-03-06 11:34:22 +01:00
),
),
'email' => array (
'email' => array (
'rule' => array ( 'email' ),
'message' => 'Please enter a valid email address.' ,
//'allowEmpty' => false,
'required' => true ,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
'unique' => array (
'rule' => 'isUnique' ,
'message' => 'An account with this email address already exists.'
),
),
'autoalert' => array (
'boolean' => array (
'rule' => array ( 'boolean' ),
//'message' => 'Your custom message here',
'allowEmpty' => true ,
'required' => false ,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
),
'contactalert' => array (
'boolean' => array (
'rule' => array ( 'boolean' ),
//'message' => 'Your custom message here',
'allowEmpty' => true ,
'required' => false ,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
),
'authkey' => array (
'minlength' => array (
'rule' => array ( 'minlength' , 40 ),
'message' => 'A authkey of a minimum length of 40 is required.' ,
'required' => true ,
),
2015-10-09 15:59:25 +02:00
'valueNotEmpty' => array (
'rule' => array ( 'valueNotEmpty' ),
2013-03-06 11:34:22 +01:00
),
),
'invited_by' => array (
'numeric' => array (
'rule' => array ( 'numeric' ),
//'message' => 'Your custom message here',
//'allowEmpty' => false,
//'required' => false,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
),
'change_pw' => array (
'numeric' => array (
'rule' => array ( 'numeric' ),
//'message' => 'Your custom message here',
'allowEmpty' => true ,
'required' => false ,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
),
'gpgkey' => array (
2015-10-15 11:35:44 +02:00
'gpgvalidation' => array (
'rule' => array ( 'validateGpgkey' ),
'message' => 'GPG key not valid, please enter a valid key.' ,
2013-03-06 11:34:22 +01:00
),
),
2016-04-04 19:23:05 +02:00
'certif_public' => array (
'notempty' => array (
'rule' => array ( 'validateCertificate' ),
'message' => 'Certificate not valid, please enter a valid certificate (x509).' ,
//'allowEmpty' => false,
//'required' => false,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
),
2013-03-06 11:34:22 +01:00
'nids_sid' => array (
'numeric' => array (
'rule' => array ( 'numeric' ),
'message' => 'A SID should be an integer.' ,
'allowEmpty' => false ,
'required' => true ,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
),
'termsaccepted' => array (
'boolean' => array (
'rule' => array ( 'boolean' ),
//'message' => 'Your custom message here',
//'allowEmpty' => false,
//'required' => false,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
),
'newsread' => array (
'date' => array (
'rule' => array ( 'date' ),
//'message' => 'Your custom message here',
//'allowEmpty' => false,
//'required' => false,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
),
);
//The Associations below have been created with all possible keys, those that are not needed can be removed
/**
* belongsTo associations
*
* @ var array
*/
public $belongsTo = array (
'Role' => array (
'className' => 'Role' ,
'foreignKey' => 'role_id' ,
'conditions' => '' ,
'fields' => '' ,
'order' => ''
2015-02-23 11:33:38 +01:00
),
'Organisation' => array (
'className' => 'Organisation' ,
2015-04-14 17:51:38 +02:00
'foreignKey' => 'org_id' ,
2015-02-23 11:33:38 +01:00
'conditions' => '' ,
'fields' => '' ,
'order' => ''
2015-08-03 14:12:20 +02:00
),
'Server' => array (
'className' => 'Server' ,
'foreignKey' => 'server_id' ,
'conditions' => '' ,
2015-12-06 19:41:29 +01:00
'fields' => array ( 'Server.id' , 'Server.url' , 'Server.push_rules' ),
2015-08-03 14:12:20 +02:00
'order' => ''
2013-03-06 11:34:22 +01:00
)
);
/**
* hasMany associations
*
* @ var array
*/
public $hasMany = array (
'Event' => array (
'className' => 'Event' ,
'foreignKey' => 'user_id' ,
'dependent' => false ,
'conditions' => '' ,
'fields' => '' ,
'order' => '' ,
'limit' => '' ,
'offset' => '' ,
'exclusive' => '' ,
'finderQuery' => '' ,
'counterQuery' => ''
2013-08-14 17:46:57 +02:00
),
2015-04-25 20:49:29 +02:00
'Post'
2013-03-06 11:34:22 +01:00
);
public $actsAs = array (
'SysLogLogable.SysLogLogable' => array ( // TODO Audit, logable
'userModel' => 'User' ,
'userKey' => 'user_id' ,
2015-11-16 00:22:58 +01:00
'change' => 'full' ,
'ignore' => array ( 'password' )
2013-03-06 11:34:22 +01:00
),
'Trim' ,
2013-08-21 11:33:30 +02:00
'Containable'
2013-03-06 11:34:22 +01:00
);
2016-03-15 23:04:20 +01:00
private function __generatePassword () {
$groups = array (
'0123456789' ,
'abcdefghijklmnopqrstuvwxyz' ,
'ABCDEFGHIJKLOMNOPQRSTUVWXYZ' ,
'!@#$%^&*()_-'
);
$passwordLength = Configure :: read ( 'Security.password_policy_length' ) ? Configure :: read ( 'Security.password_policy_length' ) : 12 ;
$pw = '' ;
for ( $i = 0 ; $i < $passwordLength ; $i ++ ) {
$chars = implode ( '' , $groups );
$pw .= $chars [ mt_rand ( 0 , strlen ( $chars ) - 1 )];
}
foreach ( $groups as & $group ) {
$pw .= $group [ mt_rand ( 0 , strlen ( $group ) - 1 )];
}
return $pw ;
}
public function beforeValidate ( $options = array ()) {
if ( ! isset ( $this -> data [ 'User' ][ 'id' ])) {
2016-03-30 09:39:00 +02:00
if ( isset ( $this -> data [ 'User' ][ 'enable_password' ]) && ( ! $this -> data [ 'User' ][ 'enable_password' ] || ( empty ( $this -> data [ 'User' ][ 'password' ]) && empty ( $this -> data [ 'User' ][ 'confirm_password' ])))) {
2016-03-15 23:04:20 +01:00
$this -> data [ 'User' ][ 'password' ] = $this -> __generatePassword ();
$this -> data [ 'User' ][ 'confirm_password' ] = $this -> data [ 'User' ][ 'password' ];
}
}
if ( ! isset ( $this -> data [ 'User' ][ 'nids_sid' ]) || empty ( $this -> data [ 'User' ][ 'nids_sid' ])) $this -> data [ 'User' ][ 'nids_sid' ] = mt_rand ( 1000000 , 9999999 );
return true ;
}
2013-03-06 11:34:22 +01:00
2013-05-13 14:27:40 +02:00
public function beforeSave ( $options = array ()) {
2013-03-06 11:34:22 +01:00
if ( isset ( $this -> data [ $this -> alias ][ 'password' ])) {
$this -> data [ $this -> alias ][ 'password' ] = AuthComponent :: password ( $this -> data [ $this -> alias ][ 'password' ]);
}
return true ;
}
/**
* Checks if the GPG key is a valid key
* But also import it in the keychain .
*/
2014-01-21 11:28:18 +01:00
// TODO: this will NOT fail on keys that can only be used for signing but not encryption!
// the method in verifyUsers will fail in that case.
2013-03-06 11:34:22 +01:00
public function validateGpgkey ( $check ) {
// LATER first remove the old gpgkey from the keychain
// empty value
if ( empty ( $check [ 'gpgkey' ])) {
return true ;
}
// we have a clean, hopefull public, key here
// key is entered
require_once 'Crypt/GPG.php' ;
try {
2015-06-09 13:04:33 +02:00
$gpg = new Crypt_GPG ( array ( 'homedir' => Configure :: read ( 'GnuPG.homedir' ), 'binary' => ( Configure :: read ( 'GnuPG.binary' ) ? Configure :: read ( 'GnuPG.binary' ) : '/usr/bin/gpg' )));
2013-03-06 11:34:22 +01:00
try {
$keyImportOutput = $gpg -> importKey ( $check [ 'gpgkey' ]);
if ( ! empty ( $keyImportOutput [ 'fingerprint' ])) {
return true ;
}
} catch ( Exception $e ) {
//debug($e);
2013-07-17 12:54:55 +02:00
$this -> log ( $e -> getMessage ());
2013-03-06 11:34:22 +01:00
return false ;
}
} catch ( Exception $e ) {
//debug($e);
2013-07-17 12:54:55 +02:00
$this -> log ( $e -> getMessage ());
2013-03-06 11:34:22 +01:00
return true ; // TODO was false
}
}
2016-04-04 19:23:05 +02:00
/**
* Checks if the certificate is a valid x509 certificate
* But also import it in the keychain .
*/
// TODO: this will NOT fail on keys that can only be used for signing but not encryption!
// the method in verifyUsers will fail in that case.
public function validateCertificate ( $check ) {
// LATER first remove the old certif_public from the keychain
$this -> log ( 'validateCertificate' , 'debug' );
// empty value
if ( empty ( $check [ 'certif_public' ])) {
return true ;
}
// certif_public is entered
// Check if $check is a x509 certificate ?
if ( openssl_x509_read ( $check [ 'certif_public' ])){
$this -> log ( 'openssl_x509_read is good' , 'debug' );
try {
$msg_test = tempnam ( '/dev/shm/' , 'SMIME' );
$fp = fopen ( $msg_test , " w " );
$test = 'test' ;
fwrite ( $fp , $test );
fclose ( $fp );
$msg_test_encrypted = tempnam ( '/dev/shm/' , 'SMIME' );
$this -> log ( $msg_test , 'debug' );
$this -> log ( $msg_test_encrypted , 'debug' );
$this -> log ( $check [ 'certif_public' ], 'debug' );
$this -> log ( $this -> validate [ 'certif_public' ][ 'notempty' ][ 'message' ], 'debug' );
// encrypt it
if ( openssl_pkcs7_encrypt ( $msg_test , $msg_test_encrypted , $check [ 'certif_public' ], null , 0 , OPENSSL_CIPHER_AES_256_CBC )){
$this -> log ( 'openssl_pkcs7_encrypt good -- validateCertificate' , 'debug' );
$parse = openssl_x509_parse ( $check [ 'certif_public' ]);
// Valid certificate ?
$now = new DateTime ( " now " );
$validTo_time_t_epoch = $parse [ 'validTo_time_t' ];
$validTo_time_t = new DateTime ( " @ $validTo_time_t_epoch " );
if ( $validTo_time_t > $now ){
$this -> log ( 'openssl_pkcs7_encrypt good -- validateCertificate IS VALID -- $validTo_time_t' , 'debug' );
// purposes smimeencrypt ?
if (( $parse [ 'purposes' ][ 5 ][ 0 ] == 1 ) and ( $parse [ 'purposes' ][ 5 ][ 2 ] == 'smimeencrypt' )){
$this -> log ( 'openssl_pkcs7_encrypt good -- validateCertificate purposes is GOOD' , 'debug' );
return true ;
} else {
$this -> log ( 'openssl_pkcs7_encrypt good -- validateCertificate purposes is NOT GOOD' , 'debug' );
$this -> log ( $this -> validate [ 'certif_public' ][ 'notempty' ][ 'message' ], 'debug' );
return 'This certificate cannot be used to encrypt email' ;
}
} else {
$this -> log ( 'openssl_pkcs7_encrypt good -- validateCertificate expired' , 'debug' );
return 'This certificate is expired' ;
}
} else {
$this -> log ( 'openssl_pkcs7_encrypt NOT good -- validateCertificate' , 'debug' );
return false ;
}
} catch ( Exception $e ){
$this -> log ( $e -> getMessage ());
}
unlink ( $msg_test );
unlink ( $msg_test_encrypted );
}
else {
$this -> log ( 'openssl_x509_read is NOT good' , 'debug' );
return false ;
}
}
2015-01-27 10:41:43 +01:00
public function passwordLength ( $check ) {
$length = Configure :: read ( 'Security.password_policy_length' );
if ( empty ( $length ) || $length < 0 ) $length = 6 ;
$value = array_values ( $check );
$value = $value [ 0 ];
if ( strlen ( $value ) < $length ) return false ;
return true ;
}
2013-03-06 11:34:22 +01:00
public function complexPassword ( $check ) {
/*
2015-01-27 10:41:43 +01:00
default password :
2013-03-06 11:34:22 +01:00
6 characters minimum
1 or more upper - case letters
1 or more lower - case letters
1 or more digits or special characters
example : " EasyPeasy34 "
2015-01-27 10:41:43 +01:00
If Security . password_policy_complexity is set and valid , use the regex provided .
2013-03-06 11:34:22 +01:00
*/
2015-01-27 10:41:43 +01:00
$regex = Configure :: read ( 'Security.password_policy_complexity' );
2016-03-29 15:13:23 +02:00
if ( empty ( $regex ) || @ preg_match ( $regex , 'test' ) === false ) $regex = '/((?=.*\d)|(?=.*\W+))(?![\n])(?=.*[A-Z])(?=.*[a-z]).*$/' ;
2013-03-06 11:34:22 +01:00
$value = array_values ( $check );
$value = $value [ 0 ];
2015-01-27 10:41:43 +01:00
return preg_match ( $regex , $value );
2013-03-06 11:34:22 +01:00
}
public function identicalFieldValues ( $field = array (), $compareField = null ) {
foreach ( $field as $key => $value ) {
$v1 = $value ;
$v2 = $this -> data [ $this -> name ][ $compareField ];
if ( $v1 !== $v2 ) {
return false ;
} else {
continue ;
}
}
return true ;
}
/**
* Generates an authentication key for each user
*/
public function generateAuthKey () {
$length = 40 ;
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' ;
$charLen = strlen ( $characters ) - 1 ;
$key = '' ;
for ( $p = 0 ; $p < $length ; $p ++ ) {
$key .= $characters [ rand ( 0 , $charLen )];
}
2013-07-11 14:26:28 +02:00
return $key ;
}
2013-03-06 11:34:22 +01:00
2013-07-11 14:30:56 +02:00
public function generateRandomPassword () {
2013-07-11 14:26:28 +02:00
$length = 12 ;
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-+=!@#$%&*()<>/?' ;
$charLen = strlen ( $characters ) - 1 ;
$key = '' ;
for ( $p = 0 ; $p < $length ; $p ++ ) {
$key .= $characters [ rand ( 0 , $charLen )];
}
2013-03-06 11:34:22 +01:00
return $key ;
}
2013-07-11 14:26:28 +02:00
2013-03-06 11:34:22 +01:00
public function checkAndCorrectPgps () {
$fails = array ();
$users = $this -> find ( 'all' , array ( 'recursive' => 0 ));
foreach ( $users as $user ) {
if ( strlen ( $user [ 'User' ][ 'gpgkey' ]) && strpos ( $user [ 'User' ][ 'gpgkey' ], " \n " )) {
$fails [] = $user [ 'User' ][ 'id' ] . ':' . $user [ 'User' ][ 'id' ];
//$check['gpgkey'] = trim(preg_replace('/\n', '', $check['gpgkey']));
}
}
return $fails ;
}
2014-01-03 15:26:35 +01:00
public function getOrgs () {
2016-01-10 19:47:21 +01:00
$orgs = $this -> Organisation -> find ( 'list' , array (
2015-03-21 14:27:53 +01:00
'recursive' => - 1 ,
'fields' => array ( 'name' ),
2014-01-03 15:26:35 +01:00
));
2016-01-10 19:47:21 +01:00
return $orgs ;
2014-01-03 15:26:35 +01:00
}
2014-01-21 11:28:18 +01:00
public function getOrgMemberCount ( $org ) {
return $this -> find ( 'count' , array (
'conditions' => array (
'org =' => $org ,
)));
}
2015-11-11 17:03:59 +01:00
public function verifyGPG ( $id = false ) {
2014-01-21 11:28:18 +01:00
require_once 'Crypt/GPG.php' ;
$this -> Behaviors -> detach ( 'Trim' );
$results = array ();
2015-11-11 17:03:59 +01:00
$conditions = array ( 'not' => array ( 'gpgkey' => '' ));
if ( $id !== false ) $conditions [ 'User.id' ] = $id ;
2014-01-21 11:28:18 +01:00
$users = $this -> find ( 'all' , array (
2015-11-11 17:03:59 +01:00
'conditions' => $conditions ,
2014-01-21 11:28:18 +01:00
'recursive' => - 1 ,
));
2015-11-11 17:03:59 +01:00
if ( empty ( $users )) return results ;
$currentTimestamp = time ();
$gpg = new Crypt_GPG ( array ( 'homedir' => Configure :: read ( 'GnuPG.homedir' ), 'binary' => ( Configure :: read ( 'GnuPG.binary' ) ? Configure :: read ( 'GnuPG.binary' ) : '/usr/bin/gpg' )));
2014-01-21 11:28:18 +01:00
foreach ( $users as $k => $user ) {
try {
2015-11-11 17:03:59 +01:00
$temp = $gpg -> importKey ( $user [ 'User' ][ 'gpgkey' ]);
$key = $gpg -> getKeys ( $temp [ 'fingerprint' ]);
$subKeys = $key [ 0 ] -> getSubKeys ();
$sortedKeys = array ( 'valid' => 0 , 'expired' => 0 , 'noEncrypt' => 0 );
foreach ( $subKeys as $subKey ) {
$issue = false ;
2015-11-12 09:46:33 +01:00
$expiration = $subKey -> getExpirationDate ();
if ( $expiration != 0 && $currentTimestamp > $expiration ) {
2015-11-11 17:03:59 +01:00
$sortedKeys [ 'expired' ] ++ ;
continue ;
}
if ( ! $subKey -> canEncrypt ()) {
$sortedKeys [ 'noEncrypt' ] ++ ;
continue ;
}
$sortedKeys [ 'valid' ] ++ ;
}
if ( ! $sortedKeys [ 'valid' ]) {
$results [ $user [ 'User' ][ 'id' ]][ 2 ] = 'The user\'s PGP key does not include a valid subkey that could be used for encryption.' ;
if ( $sortedKeys [ 'expired' ]) $results [ $user [ 'User' ][ 'id' ]][ 2 ] .= ' Found ' . $sortedKeys [ 'expired' ] . ' subkey(s) that have expired.' ;
if ( $sortedKeys [ 'noEncrypt' ]) $results [ $user [ 'User' ][ 'id' ]][ 2 ] .= ' Found ' . $sortedKeys [ 'noEncrypt' ] . ' subkey(s) that are sign only.' ;
$results [ $user [ 'User' ][ 'id' ]][ 0 ] = true ;
}
2014-01-21 11:28:18 +01:00
} catch ( Exception $e ){
2015-10-27 15:18:04 +01:00
$results [ $user [ 'User' ][ 'id' ]][ 2 ] = $e -> getMessage ();
2014-01-21 11:28:18 +01:00
$results [ $user [ 'User' ][ 'id' ]][ 0 ] = true ;
}
$results [ $user [ 'User' ][ 'id' ]][ 1 ] = $user [ 'User' ][ 'email' ];
}
return $results ;
}
2016-04-04 19:23:05 +02:00
public function verifyCertificate () {
$this -> log ( 'verifyCertificate -- Model/User' , 'debug' );
$this -> Behaviors -> detach ( 'Trim' );
$results = array ();
$users = $this -> find ( 'all' , array (
'conditions' => array ( 'not' => array ( 'certif_public' => '' )),
//'fields' => array('id', 'email', 'gpgkey'),
'recursive' => - 1 ,
));
foreach ( $users as $k => $user ) {
$certif_public = $user [ 'User' ][ 'certif_public' ];
$this -> log ( 'foreach -- Model/User' , 'debug' );
$this -> log ( $certif_public , 'debug' );
try {
$msg_test = tempnam ( '/dev/shm/' , 'SMIME' );
$fp = fopen ( $msg_test , " w " );
$test = 'test' ;
fwrite ( $fp , $test );
fclose ( $fp );
$msg_test_encrypted = tempnam ( '/dev/shm/' , 'SMIME' );
$this -> log ( $msg_test , 'debug' );
$this -> log ( $msg_test_encrypted , 'debug' );
$this -> log ( $certif_public , 'debug' );
// encrypt it
if ( openssl_pkcs7_encrypt ( $msg_test , $msg_test_encrypted , $certif_public , null , 0 , OPENSSL_CIPHER_AES_256_CBC )){
$this -> log ( 'openssl_pkcs7_encrypt good -- Model/User' , 'debug' );
$parse = openssl_x509_parse ( $certif_public );
// Valid certificate ?
$now = new DateTime ( " now " );
$validTo_time_t_epoch = $parse [ 'validTo_time_t' ];
$validTo_time_t = new DateTime ( " @ $validTo_time_t_epoch " );
if ( $validTo_time_t > $now ){
$this -> log ( 'openssl_pkcs7_encrypt good -- Model/User IS VALID -- $validTo_time_t' , 'debug' );
// purposes smimeencrypt ?
if (( $parse [ 'purposes' ][ 5 ][ 0 ] == 1 ) and ( $parse [ 'purposes' ][ 5 ][ 2 ] == 'smimeencrypt' )){
$this -> log ( 'openssl_pkcs7_encrypt good -- Model/User purposes is GOOD' , 'debug' );
} else {
$this -> log ( 'openssl_pkcs7_encrypt good -- Model/User purposes is NOT GOOD' , 'debug' );
$results [ $user [ 'User' ][ 'id' ]][ 0 ] = true ;
}
} else {
$this -> log ( 'openssl_pkcs7_encrypt good -- Model/User expired' , 'debug' );
$results [ $user [ 'User' ][ 'id' ]][ 0 ] = true ;
}
} else {
$this -> log ( 'openssl_pkcs7_encrypt NOT good -- Model/User' , 'debug' );
$results [ $user [ 'User' ][ 'id' ]][ 0 ] = true ;
}
$results [ $user [ 'User' ][ 'id' ]][ 1 ] = $user [ 'User' ][ 'email' ];
} catch ( Exception $e ){
$this -> log ( $e -> getMessage ());
}
unlink ( $msg_test );
unlink ( $msg_test_encrypted );
}
$this -> log ( 'verifyCertificate -- end' , 'debug' );
$this -> log ( $results , 'debug' );
return $results ;
}
2014-04-01 16:20:47 +02:00
public function getPGP ( $id ) {
$result = $this -> find ( 'first' , array (
'recursive' => - 1 ,
'fields' => array ( 'id' , 'gpgkey' ),
2014-08-21 15:27:25 +02:00
'conditions' => array ( 'id' => $id ),
2014-04-01 16:20:47 +02:00
));
return $result [ 'User' ][ 'gpgkey' ];
}
2016-04-04 19:23:05 +02:00
public function getCertificate ( $id ) {
$result = $this -> find ( 'first' , array (
'recursive' => - 1 ,
'fields' => array ( 'id' , 'certif_public' ),
'conditions' => array ( 'id' => $id ),
));
return $result [ 'User' ][ 'certif_public' ];
}
2015-04-13 12:42:26 +02:00
// get the current user and rearrange it to be in the same format as in the auth component
public function getAuthUser ( $id ) {
2016-02-19 12:40:50 +01:00
$conditions = array ( 'User.id' => $id );
$user = $this -> find ( 'first' , array ( 'conditions' => $conditions , 'recursive' => - 1 , 'contain' => array ( 'Organisation' , 'Role' , 'Server' )));
if ( empty ( $user )) return $user ;
// Rearrange it a bit to match the Auth object created during the login
$user [ 'User' ][ 'Role' ] = $user [ 'Role' ];
$user [ 'User' ][ 'Organisation' ] = $user [ 'Organisation' ];
$user [ 'User' ][ 'Server' ] = $user [ 'Server' ];
unset ( $user [ 'Organisation' ], $user [ 'Role' ], $user [ 'Server' ]);
return $user [ 'User' ];
}
// get the current user and rearrange it to be in the same format as in the auth component
public function getAuthUserByUuid ( $id ) {
$conditions = array ( 'User.authkey' => $id );
$user = $this -> find ( 'first' , array ( 'conditions' => $conditions , 'recursive' => - 1 , 'contain' => array ( 'Organisation' , 'Role' , 'Server' )));
2015-11-09 08:52:06 +01:00
if ( empty ( $user )) return $user ;
2015-04-13 12:42:26 +02:00
// Rearrange it a bit to match the Auth object created during the login
$user [ 'User' ][ 'Role' ] = $user [ 'Role' ];
$user [ 'User' ][ 'Organisation' ] = $user [ 'Organisation' ];
2015-12-06 19:41:29 +01:00
$user [ 'User' ][ 'Server' ] = $user [ 'Server' ];
unset ( $user [ 'Organisation' ], $user [ 'Role' ], $user [ 'Server' ]);
2015-04-18 07:53:18 +02:00
return $user [ 'User' ];
2015-04-13 12:42:26 +02:00
}
2015-04-20 11:46:55 +02:00
2016-03-15 23:04:20 +01:00
public function getAuthUserByExternalAuth ( $id ) {
$conditions = array ( 'User.external_auth_key' => $id , 'User.external_auth_required' => true );
$user = $this -> find ( 'first' , array ( 'conditions' => $conditions , 'recursive' => - 1 , 'contain' => array ( 'Organisation' , 'Role' , 'Server' )));
if ( empty ( $user )) return $user ;
// Rearrange it a bit to match the Auth object created during the login
$user [ 'User' ][ 'Role' ] = $user [ 'Role' ];
$user [ 'User' ][ 'Organisation' ] = $user [ 'Organisation' ];
$user [ 'User' ][ 'Server' ] = $user [ 'Server' ];
unset ( $user [ 'Organisation' ], $user [ 'Role' ], $user [ 'Server' ]);
return $user [ 'User' ];
}
2015-04-20 11:46:55 +02:00
// Fetch all users that have access to an event / discussion for e-mailing (or maybe something else in the future.
// parameters are an array of org IDs that are owners (for an event this would be orgc and org)
public function getUsersWithAccess ( $owners = array (), $distribution , $sharing_group_id = 0 , $userConditions = array ()) {
$sgModel = ClassRegistry :: init ( 'SharingGroup' );
$conditions = array ();
$validOrgs = array ();
$all = true ;
// add owners to the conditions
if ( $distribution == 0 || $distribution == 4 ) {
$all = false ;
$validOrgs = $owners ;
}
// add all orgs to the conditions that can see the SG
2015-12-18 16:27:25 +01:00
if ( $distribution == 4 ) {
2015-04-20 11:46:55 +02:00
$sgOrgs = $sgModel -> getOrgsWithAccess ( $sharing_group_id );
if ( $sgOrgs === true ) $all = true ;
else $validOrgs = array_merge ( $validOrgs , $sgOrgs );
}
$validOrgs = array_unique ( $validOrgs );
2016-02-02 15:30:23 +01:00
$conditions [ 'AND' ][] = array ( 'disabled' => 0 );
2015-12-18 16:33:41 +01:00
if ( ! $all ) {
$conditions [ 'AND' ][ 'OR' ][] = array ( 'org_id' => $validOrgs );
// Add the site-admins to the list
$roles = $this -> Role -> find ( 'all' , array (
'conditions' => array ( 'perm_site_admin' => 1 ),
'fields' => array ( 'id' )
));
$roleIDs = array ();
foreach ( $roles as $role ) $roleIDs [] = $role [ 'Role' ][ 'id' ];
$conditions [ 'AND' ][ 'OR' ][] = array ( 'role_id' => $roleIDs );
}
2015-04-20 11:46:55 +02:00
$conditions [ 'AND' ][] = $userConditions ;
2015-12-18 16:33:41 +01:00
2015-04-20 11:46:55 +02:00
$users = $this -> find ( 'all' , array (
'conditions' => $conditions ,
'recursive' => - 1 ,
2016-04-04 19:23:05 +02:00
'fields' => array ( 'id' , 'email' , 'gpgkey' , 'certif_public' , 'org_id' ),
2015-04-20 11:46:55 +02:00
'contain' => array ( 'Role' => array ( 'fields' => array ( 'perm_site_admin' ))),
));
foreach ( $users as & $user ) {
$temp = $user [ 'User' ];
unset ( $user [ 'User' ]);
$user = array_merge ( $temp , $user );
}
return $users ;
}
2015-06-10 18:07:48 +02:00
Rework of the e-mailing, part 1
- Reworking the way e-mails are sent - all of it goes through a centralised e-mail method
- just pass the recipient, recipient encryption key collection, body, alternate body if the message cannot be encrypted, subject, reply to address and pgp key for reply to along and the method will do the rest
- encrypt if possible, check if sending without encryption is allowed, signing, adding attachment for reply to encryption key, using alternate sanitised body if it is enforced for accounts that cannot use encryption is all done in one place
- easy to maintain and expand with future changes (such as the S/MIME pull request on github)
2015-05-25 17:18:39 +02:00
// all e-mail sending is now handled by this method
// Just pass the user ID in an array that is the target of the e-mail along with the message body and the alternate message body if the message cannot be encrypted
2015-05-27 18:27:53 +02:00
// the remaining two parameters are the e-mail subject and a secondary user object which will be used as the replyto address if set. If it is set and an encryption key for the replyTo user exists, then his/her public key will also be attached
2015-05-25 20:54:10 +02:00
public function sendEmail ( $user , $body , $bodyNoEnc = false , $subject , $replyToUser = false ) {
2015-12-15 10:52:31 +01:00
if ( Configure :: read ( 'MISP.disable_emailing' )) {
$this -> Log = ClassRegistry :: init ( 'Log' );
$this -> Log -> create ();
$this -> Log -> save ( array (
'org' => 'SYSTEM' ,
'model' => 'User' ,
'model_id' => $user [ 'User' ][ 'id' ],
'email' => $user [ 'User' ][ 'email' ],
'action' => 'email' ,
'title' => 'Email to ' . $user [ 'User' ][ 'email' ] . ', titled "' . $subject . '" failed. Reason: Emailing is currently disabled on this instance.' ,
'change' => null ,
));
return true ;
}
2015-05-25 20:54:10 +02:00
$failed = false ;
$failureReason = " " ;
Rework of the e-mailing, part 1
- Reworking the way e-mails are sent - all of it goes through a centralised e-mail method
- just pass the recipient, recipient encryption key collection, body, alternate body if the message cannot be encrypted, subject, reply to address and pgp key for reply to along and the method will do the rest
- encrypt if possible, check if sending without encryption is allowed, signing, adding attachment for reply to encryption key, using alternate sanitised body if it is enforced for accounts that cannot use encryption is all done in one place
- easy to maintain and expand with future changes (such as the S/MIME pull request on github)
2015-05-25 17:18:39 +02:00
// check if the e-mail can be encrypted
2016-04-04 19:23:05 +02:00
$canEncryptGPG = false ;
if ( isset ( $user [ 'User' ][ 'gpgkey' ]) && ! empty ( $user [ 'User' ][ 'gpgkey' ])) $canEncryptGPG = true ;
$canEncryptSMIME = false ;
if ( isset ( $user [ 'User' ][ 'certif_public' ]) && ! empty ( $user [ 'User' ][ 'certif_public' ])) $canEncryptSMIME = true ;
Rework of the e-mailing, part 1
- Reworking the way e-mails are sent - all of it goes through a centralised e-mail method
- just pass the recipient, recipient encryption key collection, body, alternate body if the message cannot be encrypted, subject, reply to address and pgp key for reply to along and the method will do the rest
- encrypt if possible, check if sending without encryption is allowed, signing, adding attachment for reply to encryption key, using alternate sanitised body if it is enforced for accounts that cannot use encryption is all done in one place
- easy to maintain and expand with future changes (such as the S/MIME pull request on github)
2015-05-25 17:18:39 +02:00
// If bodyonlencrypted is enabled and the user has no encryption key, use the alternate body (if it exists)
2016-04-05 13:29:40 +02:00
if ( Configure :: read ( 'GnuPG.bodyonlyencrypted' ) && ! $canEncryptSMIME && ! $canEncryptGPG && $bodyNoEnc ) {
Rework of the e-mailing, part 1
- Reworking the way e-mails are sent - all of it goes through a centralised e-mail method
- just pass the recipient, recipient encryption key collection, body, alternate body if the message cannot be encrypted, subject, reply to address and pgp key for reply to along and the method will do the rest
- encrypt if possible, check if sending without encryption is allowed, signing, adding attachment for reply to encryption key, using alternate sanitised body if it is enforced for accounts that cannot use encryption is all done in one place
- easy to maintain and expand with future changes (such as the S/MIME pull request on github)
2015-05-25 17:18:39 +02:00
$body = $bodyNoEnc ;
}
2015-06-10 09:50:38 +02:00
$body = str_replace ( '\n' , PHP_EOL , $body );
2016-04-04 19:23:05 +02:00
if ( $canEncryptGPG ){
// Sign the body
require_once 'Crypt/GPG.php' ;
try {
$gpg = new Crypt_GPG ( array ( 'homedir' => Configure :: read ( 'GnuPG.homedir' ), 'binary' => ( Configure :: read ( 'GnuPG.binary' ) ? Configure :: read ( 'GnuPG.binary' ) : '/usr/bin/gpg' ))); // , 'debug' => true
$gpg -> addSignKey ( Configure :: read ( 'GnuPG.email' ), Configure :: read ( 'GnuPG.password' ));
$body = $gpg -> sign ( $body , Crypt_GPG :: SIGN_MODE_CLEAR );
} catch ( Exception $e ) {
$failureReason = " the message could not be signed. The following error message was returned by gpg: " . $e -> getMessage ();
$this -> log ( $e -> getMessage ());
$failed = true ;
}
2015-05-25 20:54:10 +02:00
}
2016-04-04 19:23:05 +02:00
$Email = new CakeEmail ();
2015-05-25 20:54:10 +02:00
// If we cannot encrypt the mail and the server settings restricts sending unencrypted messages, return false
2016-04-04 19:23:05 +02:00
if ( ! $failed && ( ! $canEncryptGPG && Configure :: read ( 'GnuPG.onlyencrypted' )) && ( ! $canEncryptSMIME && Configure :: read ( 'SMIME.onlyencrypted' ))) {
2015-05-25 20:54:10 +02:00
$failed = true ;
$failureReason = " encrypted messages are enforced and the message could not be encrypted for this user as no valid encryption key was found. " ;
}
// Let's encrypt the message if we can
2016-04-04 19:23:05 +02:00
if ( ! $failed && $canEncryptGPG ) {
2015-05-25 20:54:10 +02:00
$keyImportOutput = $gpg -> importKey ( $user [ 'User' ][ 'gpgkey' ]);
try {
2015-12-22 16:27:08 +01:00
$key = $gpg -> getKeys ( $keyImportOutput [ 'fingerprint' ]);
$subKeys = $key [ 0 ] -> getSubKeys ();
2016-04-04 19:23:05 +02:00
$canEncryptGPG = false ;
2015-12-22 16:27:08 +01:00
$currentTimestamp = time ();
foreach ( $subKeys as $subKey ) {
$expiration = $subKey -> getExpirationDate ();
2016-04-04 19:23:05 +02:00
if (( $expiration == 0 || $currentTimestamp < $expiration ) && $subKey -> canEncrypt ()) $canEncryptGPG = true ;
2015-12-22 16:27:08 +01:00
}
2016-04-04 19:23:05 +02:00
if ( $canEncryptGPG ) {
2015-12-22 16:27:08 +01:00
$gpg -> addEncryptKey ( $keyImportOutput [ 'fingerprint' ]); // use the key that was given in the import
$body = $gpg -> encrypt ( $body , true );
} else {
$failed = true ;
$failureReason = " the message could not be encrypted because the provided key is either expired or cannot be used for encryption. " ;
}
2015-05-25 20:54:10 +02:00
} catch ( Exception $e ){
// despite the user having a PGP key and the signing already succeeding earlier, we get an exception. This must mean that there is an issue with the user's key.
$failureReason = " the message could not be encrypted because there was an issue with the user's PGP key. The following error message was returned by gpg: " . $e -> getMessage ();
$this -> log ( $e -> getMessage ());
$failed = true ;
}
}
2016-04-04 19:23:05 +02:00
// SMIME if not GPG key
if ( ! $failed && ! $canEncryptGPG && $canEncryptSMIME ) {
try {
// save message to file
// Dirty !!!!
$msg = tempnam ( '/dev/shm/' , 'SMIME' );
$fp = fopen ( $msg , " w " );
fwrite ( $fp , $body );
fclose ( $fp );
$signed = tempnam ( '/dev/shm/' , 'SMIME' );
// sign it
if ( openssl_pkcs7_sign ( $msg , $signed , 'file://' . Configure :: read ( 'SMIME.cert_public_sign' ), array ( 'file://' . Configure :: read ( 'SMIME.key_sign' ), Configure :: read ( 'SMIME.password' )), array (), PKCS7_TEXT )){
$this -> log ( 'sign YES' , 'debug' );
$fp = fopen ( $signed , " r " );
$bodySigned = fread ( $fp , filesize ( $signed ));
fclose ( $fp );
unlink ( $msg );
unlink ( $signed );
} else {
$this -> log ( 'sign No' , 'debug' );
$this -> log ( 'SMIME.cert_public_sign' , 'debug' );
$this -> log ( Configure :: read ( 'SMIME.cert_public_sign' ), 'debug' );
$this -> log ( Configure :: read ( 'SMIME.key_sign' ), 'debug' );
}
// save message to file
// Dirty !!!!
$msg_signed = tempnam ( '/dev/shm/' , 'SMIME' );
$fp = fopen ( $msg_signed , " w " );
fwrite ( $fp , $bodySigned );
fclose ( $fp );
$msg_signed_encrypted = tempnam ( '/dev/shm/' , 'SMIME' );
$this -> log ( '$msg_signed -- 1' , 'debug' );
$this -> log ( $msg_signed , 'debug' );
$headers_smime = array ( " To " => $user [ 'User' ][ 'email' ], " From " => Configure :: read ( 'MISP.email' ), " Subject " => $subject );
$this -> log ( $headers_smime , 'debug' );
// encrypt it
if ( openssl_pkcs7_encrypt ( $msg_signed , $msg_signed_encrypted , $user [ 'User' ][ 'certif_public' ], $headers_smime , 0 , OPENSSL_CIPHER_AES_256_CBC )){
$this -> log ( 'encrypt YES' , 'debug' );
$fp = fopen ( $msg_signed_encrypted , 'r' );
$bodyEncSig = fread ( $fp , filesize ( $msg_signed_encrypted ));
fclose ( $fp );
unlink ( $msg_signed );
unlink ( $msg_signed_encrypted );
//$this->log('$bodyEncSig -- 1', 'debug');
//$this->log($bodyEncSig,'debug');
$parts = explode ( " \n \n " , $bodyEncSig );
$bodyEncSig = $parts [ 1 ];
// SMIME transport (hardcoded headers
$Email = $Email -> transport ( 'Smime' );
$body = $bodyEncSig ;
//$Email->send($bodyEncSig);
} else {
$this -> log ( 'encrypt No' , 'debug' );
$this -> log ( 'SMIME.cert_public_sign' , 'debug' );
$this -> log ( Configure :: read ( 'SMIME.cert_public_sign' ), 'debug' );
$this -> log ( Configure :: read ( 'SMIME.key_sign' ), 'debug' );
$this -> log ( Configure :: read ( 'SMIME.password' ), 'debug' );
}
} catch ( Exception $e ) {
// despite the user having a certificate. This must mean that there is an issue with the user's certificate.
$failureReason = " the message could not be encrypted because there was an issue with the user's public certificate. The following error message was returned by openssl: " . $e -> getMessage ();
$this -> log ( $e -> getMessage ());
$failed = true ;
}
}
2015-05-25 20:54:10 +02:00
$replyToLog = '' ;
if ( ! $failed ) {
2016-04-04 19:23:05 +02:00
//$Email = new CakeEmail(); --> declared below (needed in the SMIME part (headers)
2015-05-25 20:54:10 +02:00
// If the e-mail is sent on behalf of a user, then we want the target user to be able to respond to the sender
// For this reason we should also attach the public key of the sender along with the message (if applicable)
if ( $replyToUser != false ) {
$Email -> replyTo ( $replyToUser [ 'User' ][ 'email' ]);
2016-04-04 19:23:05 +02:00
if ( ! empty ( $replyToUser [ 'User' ][ 'gpgkey' ])) {
$Email -> attachments ( array ( 'gpgkey.asc' => array ( 'data' => $replyToUser [ 'User' ][ 'gpgkey' ])));
} elseif ( ! empty ( $replyToUser [ 'User' ][ 'certif_public' ])) {
$Email -> attachments ( array ( $replyToUser [ 'User' ][ 'email' ] . '.pem' => array ( 'data' => $replyToUser [ 'User' ][ 'certif_public' ])));
}
2015-05-25 20:54:10 +02:00
$replyToLog = 'from ' . $replyToUser [ 'User' ][ 'email' ];
}
$Email -> from ( Configure :: read ( 'MISP.email' ));
2016-01-28 11:16:49 +01:00
$Email -> returnPath ( Configure :: read ( 'MISP.email' ));
2015-05-25 20:54:10 +02:00
$Email -> to ( $user [ 'User' ][ 'email' ]);
$Email -> subject ( $subject );
$Email -> emailFormat ( 'text' );
$result = $Email -> send ( $body );
$Email -> reset ();
}
$this -> Log = ClassRegistry :: init ( 'Log' );
$this -> Log -> create ();
if ( ! $failed && $result ) {
$this -> Log -> save ( array (
'org' => 'SYSTEM' ,
'model' => 'User' ,
'model_id' => $user [ 'User' ][ 'id' ],
'email' => $user [ 'User' ][ 'email' ],
'action' => 'email' ,
'title' => 'Email ' . $replyToLog . ' to ' . $user [ 'User' ][ 'email' ] . ' sent, titled "' . $subject . '".' ,
'change' => null ,
));
return true ;
} else {
if ( isset ( $result ) && ! $result ) $failureReason = " there was an error sending the e-mail. " ;
$this -> Log -> save ( array (
'org' => 'SYSTEM' ,
'model' => 'User' ,
'model_id' => $user [ 'User' ][ 'id' ],
'email' => $user [ 'User' ][ 'email' ],
'action' => 'email' ,
2015-05-27 17:46:01 +02:00
'title' => 'Email ' . $replyToLog . ' to ' . $user [ 'User' ][ 'email' ] . ', titled "' . $subject . '" failed. Reason: ' . $failureReason ,
2015-05-25 20:54:10 +02:00
'change' => null ,
));
}
return false ;
Rework of the e-mailing, part 1
- Reworking the way e-mails are sent - all of it goes through a centralised e-mail method
- just pass the recipient, recipient encryption key collection, body, alternate body if the message cannot be encrypted, subject, reply to address and pgp key for reply to along and the method will do the rest
- encrypt if possible, check if sending without encryption is allowed, signing, adding attachment for reply to encryption key, using alternate sanitised body if it is enforced for accounts that cannot use encryption is all done in one place
- easy to maintain and expand with future changes (such as the S/MIME pull request on github)
2015-05-25 17:18:39 +02:00
}
2015-05-27 17:46:01 +02:00
public function adminMessageResolve ( $message ) {
$resolveVars = array ( '$contact' => 'MISP.contact' , '$org' => 'MISP.org' , '$misp' => 'MISP.baseurl' );
foreach ( $resolveVars as $k => $v ) {
$v = Configure :: read ( $v );
$message = str_replace ( $k , $v , $message );
2015-04-20 11:46:55 +02:00
}
2015-05-27 17:46:01 +02:00
return $message ;
2015-04-20 11:46:55 +02:00
}
2015-06-23 10:56:19 +02:00
public function fetchPGPKey ( $email ) {
2015-07-13 11:05:53 +02:00
App :: uses ( 'SyncTool' , 'Tools' );
$syncTool = new SyncTool ();
$HttpSocket = $syncTool -> setupHttpSocket ();
2015-06-23 10:56:19 +02:00
$response = $HttpSocket -> get ( 'https://pgp.mit.edu/pks/lookup?search=' . $email . '&op=index&fingerprint=on' );
if ( $response -> code != 200 ) return $response -> code ;
$string = str_replace ( array ( " \r " , " \n " ), " " , $response -> body );
$result = preg_match_all ( '/<pre>pub(.*?)<\/pre>/' , $string , $matches );
$results = $this -> __extractPGPInfo ( $matches [ 1 ]);
return $results ;
}
private function __extractPGPInfo ( $lines ) {
$extractionRules = array (
'key_id' => array ( 'regex' => '/\">(.*?)<\/a>/' , 'all' => false , 'alternate' => false ),
'date' => array ( 'regex' => '/([0-9]{4}\-[0-9]{2}\-[0-9]{2})/' , 'all' => false , 'alternate' => false ),
'fingerprint' => array ( 'regex' => '/Fingerprint=(.*)$/m' , 'all' => false , 'alternate' => false ),
'uri' => array ( 'regex' => '/<a href=\"(.*?)\">/' , 'all' => false , 'alternate' => false ),
'address' => array ( 'regex' => '/<a href="\/pks\/lookup\?op=vindex[^>]*>([^\<]*)<\/a>(.*)Fingerprint/s' , 'all' => true , 'alternate' => true ),
);
$final = array ();
foreach ( $lines as $line ) {
if ( strpos ( $line , 'KEY REVOKED' )) continue ;
$temp = array ();
foreach ( $extractionRules as $ruleName => $rule ) {
if ( $rule [ 'all' ]) preg_match_all ( $rule [ 'regex' ], $line , ${$ruleName} );
else preg_match ( $rule [ 'regex' ], $line , ${$ruleName} );
if ( $rule [ 'alternate' ] && isset ( ${$ruleName} [ 2 ]) && trim ( ${$ruleName} [ 2 ][ 0 ]) != '' ) $temp [ $ruleName ] = ${$ruleName} [ 2 ];
else $temp [ $ruleName ] = ${$ruleName} [ 1 ];
if ( $rule [ 'all' ]) $temp [ $ruleName ] = $temp [ $ruleName ][ 0 ];
$temp [ $ruleName ] = html_entity_decode ( $temp [ $ruleName ]);
}
$temp [ 'address' ] = preg_replace ( '/\s{2,}/' , PHP_EOL , trim ( $temp [ 'address' ]));
$final [] = $temp ;
}
return $final ;
}
2013-03-06 11:34:22 +01:00
}