예제 #1
0
파일: MsgTest.php 프로젝트: thefox/phpchat
 /**
  * @expectedException RuntimeException
  * @expectedExceptionCode 203
  */
 public function testDecryptionRuntimeException203()
 {
     $msg = new Msg();
     $msg->setVersion(1);
     $msg->setId('cafed00d-2131-4159-8e11-0b4dbadb1738');
     $msg->setSrcNodeId('cafed00d-2331-4159-8e11-0b4dbadb1738');
     $msg->setSrcSslKeyPub(static::SRC1_SSL_KEY_PUB);
     $msg->setSrcUserNickname('username1');
     $msg->setDstNodeId('cafed00d-2431-4159-8e11-0b4dbadb1738');
     $msg->setDstSslPubKey(static::DST1_SSL_KEY_PUB);
     $msg->setSubject('subj1');
     $msg->setText('text1');
     $msg->setSslKeyPrv(static::SRC1_SSL_KEY_PRV, static::SSL_KEY_PRV_PASS);
     $msg->setIgnore(false);
     $this->assertTrue($msg->encrypt());
     $body = $msg->getBody();
     $timeCreated = $msg->getTimeCreated();
     $password = $msg->getPassword();
     $checksum = $msg->getChecksum();
     $body = base64_decode($body);
     $body = gzdecode($body);
     $body = json_decode($body, true);
     $body['data'] = 'invalid';
     $body = json_encode($body);
     $body = gzencode($body, 9);
     $body = base64_encode($body);
     $msg = new Msg();
     $msg->setVersion(1);
     $msg->setId('cafed00d-2131-4159-8e11-0b4dbadb1738');
     $msg->setSrcNodeId('cafed00d-2331-4159-8e11-0b4dbadb1738');
     $msg->setBody($body);
     $msg->setSrcSslKeyPub(static::SRC1_SSL_KEY_PUB);
     $msg->setDstSslPubKey(static::DST1_SSL_KEY_PUB);
     $msg->setSslKeyPrv(static::DST1_SSL_KEY_PRV, static::SSL_KEY_PRV_PASS);
     $msg->setDstNodeId('cafed00d-2431-4159-8e11-0b4dbadb1738');
     $msg->setTimeCreated($timeCreated);
     $msg->setPassword($password);
     $msg->setChecksum($checksum);
     $msg->decrypt();
 }
예제 #2
0
파일: Client.php 프로젝트: thefox/phpchat
 public function msgHandle($msgName, $msgData)
 {
     #fwrite(STDOUT, 'msgHandle: /'.$msgRaw.'/'."\n");
     $msgHandleReturnValue = '';
     if ($msgName == 'noop') {
         $this->log('debug', 'no operation');
     } elseif ($msgName == 'test') {
         $len = 0;
         $test_data = 'N/A';
         if (array_key_exists('len', $msgData)) {
             $len = (int) $msgData['len'];
         }
         if (array_key_exists('test_data', $msgData)) {
             $test_data = $msgData['test_data'];
         }
         $this->log('debug', 'test: ' . $len . ' /' . $test_data . '/');
     } elseif ($msgName == 'hello') {
         if (array_key_exists('ip', $msgData)) {
             $ip = $msgData['ip'];
             if ($ip != '127.0.0.1' && strIsIp($ip)) {
                 $this->getSettings()->data['node']['uriPub'] = 'tcp://' . $ip;
                 $this->getSettings()->setDataChanged(true);
             }
         }
         $this->log('debug', 'actions execute: CRITERION_AFTER_HELLO');
         $this->actionsExecute(ClientAction::CRITERION_AFTER_HELLO);
         $msgHandleReturnValue .= $this->sendId();
     } elseif ($msgName == 'id') {
         #print __CLASS__.'->'.__FUNCTION__.': '.$msgName.', '.(int)$this->getStatus('hasId')."\n";
         if ($this->getTable()) {
             if (!$this->getStatus('hasId')) {
                 $release = 0;
                 $id = '';
                 $port = 0;
                 $strKeyPub = '';
                 $strKeyPubSign = '';
                 $strKeyPubFingerprint = '';
                 $bridgeServer = false;
                 $bridgeClient = false;
                 $isChannelPeer = false;
                 $hashcash = '';
                 if (array_key_exists('release', $msgData)) {
                     $release = (int) $msgData['release'];
                 }
                 if (array_key_exists('id', $msgData)) {
                     $id = $msgData['id'];
                 }
                 if (array_key_exists('port', $msgData)) {
                     $port = (int) $msgData['port'];
                 }
                 if (array_key_exists('sslKeyPub', $msgData)) {
                     $strKeyPub = base64_decode($msgData['sslKeyPub']);
                 }
                 if (array_key_exists('sslKeyPubSign', $msgData)) {
                     $strKeyPubSign = base64_decode($msgData['sslKeyPubSign']);
                 }
                 if (array_key_exists('bridgeServer', $msgData)) {
                     $bridgeServer = (bool) $msgData['bridgeServer'];
                 }
                 if (array_key_exists('bridgeClient', $msgData)) {
                     $bridgeClient = (bool) $msgData['bridgeClient'];
                 }
                 if (array_key_exists('isChannel', $msgData)) {
                     // isChannelPeer
                     $isChannelPeer = (bool) $msgData['isChannel'];
                 }
                 if ($isChannelPeer) {
                     $this->setStatus('isChannelPeer', true);
                 }
                 $this->log('debug', $this->getUri() . ' recv ' . $msgName . ': /' . $id . '/ /' . $port . '/ bs=' . (int) $bridgeServer . '');
                 $idOk = false;
                 $node = new Node();
                 if (Uuid::isValid($id) && $id != Uuid::NIL) {
                     $node->setIdHexStr($id);
                     $node->setUri('tcp://' . $this->getUri()->getHost() . ':' . $port);
                     $node->setBridgeServer($bridgeServer);
                     $node->setBridgeClient($bridgeClient);
                     $node->setTimeLastSeen(time());
                     $node = $this->getTable()->nodeEnclose($node);
                     #$this->log('debug', 'node ok: '.(int)is_object($node).' /'.$node->getIdHexStr().'/');
                     // Check if not Local Node
                     if (!$this->getLocalNode()->isEqual($node)) {
                         if ($strKeyPub) {
                             if ($strKeyPubSign) {
                                 if (openssl_verify($strKeyPub, $strKeyPubSign, $strKeyPub, OPENSSL_ALGO_SHA1)) {
                                     if (Node::genIdHexStr($strKeyPub) == $id) {
                                         // Check if a public key already exists.
                                         if ($node->getSslKeyPub()) {
                                             #$this->logColor('debug', 'SSL public key ok [Aa]', 'green');
                                             $idOk = true;
                                             if ($node->getSslKeyPub() == $strKeyPub) {
                                                 #$this->logColor('debug', 'SSL public key ok [Ab]', 'green');
                                                 $node->setSslKeyPubStatus('C');
                                                 $node->setDataChanged(true);
                                             }
                                         } else {
                                             // No public key found.
                                             $sslPubKey = openssl_pkey_get_public($strKeyPub);
                                             if ($sslPubKey !== false) {
                                                 $sslPubKeyDetails = openssl_pkey_get_details($sslPubKey);
                                                 if ($sslPubKeyDetails['bits'] >= Node::SSL_KEY_LEN_MIN) {
                                                     #$this->logColor('debug', 'SSL public key ok [B]', 'green');
                                                     $idOk = true;
                                                     $node->setSslKeyPub($strKeyPub);
                                                     $node->setSslKeyPubStatus('C');
                                                     $node->setDataChanged(true);
                                                 } else {
                                                     $msgHandleReturnValue .= $this->sendError(2020, $msgName);
                                                     $this->log('error', static::getErrorMsg(2020));
                                                 }
                                             } else {
                                                 $msgHandleReturnValue .= $this->sendError(2040, $msgName);
                                                 $this->log('error', static::getErrorMsg(2040));
                                             }
                                         }
                                     } else {
                                         $msgHandleReturnValue .= $this->sendError(1030, $msgName);
                                         $this->log('error', static::getErrorMsg(1030));
                                     }
                                 } else {
                                     $msgHandleReturnValue .= $this->sendError(2080, $msgName);
                                     $this->log('error', static::getErrorMsg(2080));
                                     while ($openSslErrorStr = openssl_error_string()) {
                                         $this->log('error', 'SSL: ' . $openSslErrorStr);
                                     }
                                 }
                             } else {
                                 $msgHandleReturnValue .= $this->sendError(2005, $msgName);
                                 $this->log('error', static::getErrorMsg(2005));
                             }
                         } else {
                             $msgHandleReturnValue .= $this->sendError(2000, $msgName);
                             $this->log('error', static::getErrorMsg(2000));
                         }
                     } else {
                         // It's the ID from the Local Node. Something is wrong.
                         $msgHandleReturnValue .= $this->sendError(1020, $msgName);
                         $this->log('error', static::getErrorMsg(1020));
                     }
                 } else {
                     $msgHandleReturnValue .= $this->sendError(9000, $msgName);
                     $this->log('error', static::getErrorMsg(9000));
                 }
                 if ($idOk) {
                     $this->setStatus('hasId', true);
                     $this->setNode($node);
                     if ($this->getStatus('isOutbound')) {
                         $this->getNode()->incConnectionsOutboundSucceed();
                     }
                     if ($this->getStatus('isInbound')) {
                         $this->getNode()->incConnectionsInboundSucceed();
                     }
                     if (!$this->debug && $node->getBridgeServer() && $this->getStatus('bridgeTargetUri')) {
                         $this->logColor('debug', 'bridge server: ' . $this->getStatus('bridgeServerUri'), 'yellow');
                         $this->logColor('debug', 'bridge target: ' . $this->getStatus('bridgeTargetUri'), 'yellow');
                         $actions = array();
                         $action = new ClientAction(ClientAction::CRITERION_AFTER_ID_SUCCESSFULL);
                         $action->setName('bridge_server_init_ssl');
                         $action->functionSet(function ($action, $client) {
                             $this->logColor('debug', 'init ssl because of bridge server', 'green');
                             $client->sendSslInit();
                         });
                         $actions[] = $action;
                         $action = new ClientAction(ClientAction::CRITERION_AFTER_HAS_SSL);
                         $action->setName('bridge_server_send_connect');
                         $action->functionSet(function ($action, $client) {
                             $this->logColor('debug', 'bridge ssl ok', 'yellow');
                             $client->sendBridgeConnect($client->getStatus('bridgeTargetUri'));
                         });
                         $actions[] = $action;
                         $this->actionsAdd($actions);
                     }
                     $msgHandleReturnValue .= $this->sendIdOk();
                     $this->log('debug', $this->getUri() . ' recv ' . $msgName . ': ID OK');
                 } else {
                     $msgHandleReturnValue .= $this->sendQuit();
                     $this->shutdown();
                 }
             } else {
                 $msgHandleReturnValue .= $this->sendError(1010, $msgName);
                 $this->log('error', static::getErrorMsg(1010));
             }
         } else {
             $msgHandleReturnValue .= $this->sendError(9010, $msgName);
             $this->log('error', static::getErrorMsg(9010));
         }
     } elseif ($msgName == 'id_ok') {
         $this->log('debug', $this->getUri() . ' recv ' . $msgName);
         $this->log('debug', 'actions execute: CRITERION_AFTER_ID_SUCCESSFULL');
         $this->actionsExecute(ClientAction::CRITERION_AFTER_ID_SUCCESSFULL);
         if ($this->getStatus('isChannelPeer')) {
             $this->consoleMsgAdd('New incoming channel connection from ' . $this->getUri() . '.', true, true, true);
         }
         if ($this->getStatus('isChannelPeer') || $this->getStatus('isChannelLocal')) {
             if ($this->getServer() && $this->getServer()->getKernel()) {
                 $contact = $this->getServer()->getKernel()->getAddressbook()->contactGetByNodeId($this->getNode()->getIdHexStr());
                 if ($contact) {
                     $text = 'You talked to ';
                     $text .= $this->getNode()->getIdHexStr() . ' (' . $contact->getUserNickname() . ')';
                     $text .= ' once before.';
                     $this->consoleMsgAdd($text, true, false);
                 } else {
                     $this->consoleMsgAdd('You never talked to ' . $this->getNode()->getIdHexStr() . ' before.', true, false);
                     $this->consoleMsgAdd('Verify the public keys with you conversation partner on another channel.', true, false);
                     $this->consoleMsgAdd('Public keys fingerprints:', true, false);
                     $this->consoleMsgAdd('  Yours: ' . $this->getLocalNode()->getSslKeyPubFingerprint(), true, false);
                     $this->consoleMsgAdd('  Peers: ' . $this->getNode()->getSslKeyPubFingerprint(), true, false);
                 }
             }
         }
     } elseif ($msgName == 'node_find') {
         if ($this->getStatus('hasId')) {
             $rid = '';
             $num = static::NODE_FIND_NUM;
             $nodeId = '';
             $hashcash = '';
             if (array_key_exists('rid', $msgData)) {
                 $rid = $msgData['rid'];
             }
             if (array_key_exists('num', $msgData)) {
                 $num = $msgData['num'];
             }
             if (array_key_exists('nodeId', $msgData)) {
                 $nodeId = $msgData['nodeId'];
             }
             if (array_key_exists('hashcash', $msgData)) {
                 $hashcash = $msgData['hashcash'];
             }
             $this->log('debug', $this->getUri() . ' recv ' . $msgName . ': ' . $rid);
             if ($rid) {
                 if ($hashcash && $this->hashcashVerify($hashcash, $this->getNode()->getIdHexStr(), static::HASHCASH_BITS_MIN)) {
                     if ($nodeId) {
                         $node = new Node();
                         $node->setIdHexStr($nodeId);
                         if ($node->isEqual($this->getLocalNode())) {
                             $this->log('debug', 'node find: find myself');
                             $msgHandleReturnValue .= $this->sendNodeFound($rid);
                         } elseif (!$node->isEqual($this->getNode()) && ($onode = $this->getTable()->nodeFind($node))) {
                             $this->log('debug', 'node find: find in table');
                             $msgHandleReturnValue .= $this->sendNodeFound($rid, array($onode));
                         } else {
                             $this->log('debug', 'node find: closest to "' . $node->getIdHexStr() . '"');
                             $nodes = $this->getTable()->nodeFindClosest($node, $num);
                             foreach ($nodes as $cnodeId => $cnode) {
                                 if ($cnode->isEqual($this->getNode())) {
                                     unset($nodes[$cnodeId]);
                                     break;
                                 }
                             }
                             $msgHandleReturnValue .= $this->sendNodeFound($rid, $nodes);
                         }
                     }
                 } else {
                     $msgHandleReturnValue .= $this->sendError(4000, $msgName);
                 }
             } else {
                 $msgHandleReturnValue .= $this->sendError(9000, $msgName);
                 $this->log('error', static::getErrorMsg(9000));
             }
         } else {
             $msgHandleReturnValue .= $this->sendError(1000, $msgName);
         }
     } elseif ($msgName == 'node_found') {
         if ($this->getStatus('hasId')) {
             $rid = '';
             $nodes = array();
             $hashcash = '';
             if (array_key_exists('rid', $msgData)) {
                 $rid = $msgData['rid'];
             }
             if (array_key_exists('nodes', $msgData)) {
                 $nodes = $msgData['nodes'];
             }
             if (array_key_exists('hashcash', $msgData)) {
                 $hashcash = $msgData['hashcash'];
             }
             if ($rid) {
                 $this->log('debug', $this->getUri() . ' recv ' . $msgName . ': ' . $rid);
                 $request = $this->requestGetByRid($rid);
                 if ($request) {
                     if ($hashcash && $this->hashcashVerify($hashcash, $this->getNode()->getIdHexStr(), static::HASHCASH_BITS_MAX)) {
                         $this->requestRemove($request);
                         $nodeId = $request['data']['nodeId'];
                         $nodesFoundIds = $request['data']['nodesFoundIds'];
                         $distanceOld = $request['data']['distance'];
                         $uri = '';
                         $this->log('debug', $this->getUri() . ' recv ' . $msgName . ': ' . $rid . ' nodes: ' . count($nodes));
                         if ($nodes) {
                             // Find the smallest distance.
                             foreach ($nodes as $nodeArId => $nodeAr) {
                                 $nodeArId = '';
                                 $nodeArSslPubKey = '';
                                 $nodeArBridgeServer = false;
                                 $nodeArBridgeClient = false;
                                 $nodeArBridgeDst = array();
                                 $node = new Node();
                                 if (isset($nodeAr['id'])) {
                                     $nodeArId = $nodeAr['id'];
                                 }
                                 if (isset($nodeAr['uri'])) {
                                     $node->setUri($nodeAr['uri']);
                                 }
                                 if (isset($nodeAr['sslKeyPub']) && $nodeAr['sslKeyPub']) {
                                     $nodeArSslPubKey = base64_decode($nodeAr['sslKeyPub']);
                                 }
                                 if (isset($nodeAr['bridgeServer'])) {
                                     $nodeArBridgeServer = $nodeAr['bridgeServer'];
                                 }
                                 if (isset($nodeAr['bridgeClient'])) {
                                     $nodeArBridgeClient = $nodeAr['bridgeClient'];
                                 }
                                 if (isset($nodeAr['bridgeDst'])) {
                                     # TODO
                                     $nodeArBridgeDst = $nodeAr['bridgeDst'];
                                 }
                                 $node->setBridgeServer($nodeArBridgeServer);
                                 $node->setBridgeServer($nodeArBridgeClient);
                                 $node->setTimeLastSeen(time());
                                 $distanceNew = $this->getLocalNode()->distanceHexStr($node);
                                 $this->log('debug', 'node found: ' . $nodeArId . ', do=/' . $distanceOld . '/ dn=/' . $distanceNew . '/');
                                 if ($nodeArId) {
                                     $node->setIdHexStr($nodeArId);
                                     if ($nodeArSslPubKey) {
                                         if (Node::genIdHexStr($nodeArSslPubKey) == $nodeArId) {
                                             if ($node->setSslKeyPub($nodeArSslPubKey)) {
                                                 if (!$this->getLocalNode()->isEqual($node)) {
                                                     if (!in_array($node->getIdHexStr(), $nodesFoundIds)) {
                                                         $nodesFoundIds[] = $nodeAr['id'];
                                                         if (count($nodesFoundIds) > static::NODE_FIND_MAX_NODE_IDS) {
                                                             array_shift($nodesFoundIds);
                                                         }
                                                         if ($nodeAr['id'] == $nodeId) {
                                                             $this->log('debug', 'node found: find completed');
                                                             $uri = '';
                                                         } else {
                                                             if ($distanceOld != $distanceNew) {
                                                                 $distanceMin = Node::idMinHexStr($distanceOld, $distanceNew);
                                                                 if ($distanceMin == $distanceNew) {
                                                                     // Is smaller then $distanceOld.
                                                                     $distanceOld = $distanceNew;
                                                                     $uri = $node->getUri();
                                                                 }
                                                             }
                                                         }
                                                         $this->getTable()->nodeEnclose($node);
                                                     } else {
                                                         $this->log('debug', 'node found: already known');
                                                     }
                                                 } else {
                                                     $this->log('debug', 'node found: myself, node equal');
                                                 }
                                             } else {
                                                 $this->log('debug', 'node found: public key invalid');
                                             }
                                         } else {
                                             $this->log('debug', 'node found: ID does not match public key');
                                         }
                                     } else {
                                         $this->log('debug', 'node found: no public key set');
                                     }
                                 } else {
                                     $this->log('debug', 'node found: no node id set');
                                 }
                             }
                         }
                         $this->log('debug', 'actions execute: CRITERION_AFTER_NODE_FOUND');
                         $this->actionsExecute(ClientAction::CRITERION_AFTER_NODE_FOUND);
                         if ((string) $uri) {
                             // Further search at the nearest node.
                             $this->log('debug', 'node found: uri (' . (string) $uri . ') ok');
                             $clientActions = array();
                             $action = new ClientAction(ClientAction::CRITERION_AFTER_ID_SUCCESSFULL);
                             $action->functionSet(function ($action, $client) use($nodeId, $distanceOld, $nodesFoundIds) {
                                 $client->sendNodeFind($nodeId, $distanceOld, $nodesFoundIds);
                             });
                             $clientActions[] = $action;
                             $this->getServer()->connect($uri, $clientActions);
                         }
                     } else {
                         $msgHandleReturnValue .= $this->sendError(4000, $msgName);
                     }
                 } else {
                     $msgHandleReturnValue .= $this->sendError(9000, $msgName);
                 }
                 $this->log('debug', $this->getUri() . ' recv ' . $msgName . ': ' . $rid . ' end');
             } else {
                 $msgHandleReturnValue .= $this->sendError(9000, $msgName);
             }
         } else {
             $msgHandleReturnValue .= $this->sendError(1000, $msgName);
         }
     } elseif ($msgName == 'msg') {
         if ($this->getStatus('hasId')) {
             if ($this->getMsgDb()) {
                 $rid = '';
                 $version = 0;
                 $id = '';
                 $srcNodeId = '';
                 $srcSslKeyPub = '';
                 $dstNodeId = '';
                 $subject = '';
                 $body = '';
                 $password = '';
                 $checksum = '';
                 $relayCount = 0;
                 $timeCreated = 0;
                 $hashcash = '';
                 if (array_key_exists('rid', $msgData)) {
                     $rid = $msgData['rid'];
                 }
                 if (array_key_exists('version', $msgData)) {
                     $version = (int) $msgData['version'];
                 }
                 if (array_key_exists('id', $msgData)) {
                     $id = $msgData['id'];
                 }
                 if (array_key_exists('srcNodeId', $msgData)) {
                     $srcNodeId = $msgData['srcNodeId'];
                 }
                 if (array_key_exists('srcSslKeyPub', $msgData)) {
                     $srcSslKeyPub = base64_decode($msgData['srcSslKeyPub']);
                 }
                 if (array_key_exists('dstNodeId', $msgData)) {
                     $dstNodeId = $msgData['dstNodeId'];
                 }
                 if (array_key_exists('body', $msgData)) {
                     $body = $msgData['body'];
                 }
                 if (array_key_exists('password', $msgData)) {
                     $password = $msgData['password'];
                 }
                 if (array_key_exists('checksum', $msgData)) {
                     $checksum = $msgData['checksum'];
                 }
                 if (array_key_exists('relayCount', $msgData)) {
                     $relayCount = (int) $msgData['relayCount'];
                 }
                 if (array_key_exists('timeCreated', $msgData)) {
                     $timeCreated = (int) $msgData['timeCreated'];
                 }
                 if (array_key_exists('hashcash', $msgData)) {
                     $hashcash = $msgData['hashcash'];
                 }
                 $this->log('debug', $this->getUri() . ' recv ' . $msgName . ': ' . $id);
                 #fwrite(STDOUT, __CLASS__.'->'.__FUNCTION__.' body: '.$body."\n");
                 #$this->log('debug', 'msg '.$id.' body: '.$body);
                 $status = 1;
                 // New
                 if ($this->getMsgDb()->getMsgById($id)) {
                     $status = 2;
                     // Reject
                 }
                 $srcNode = new Node();
                 $srcNode->setIdHexStr($srcNodeId);
                 $srcNode = $this->getTable()->nodeEnclose($srcNode);
                 #fwrite(STDOUT, __CLASS__.'->'.__FUNCTION__.': srcNode: '.$srcNode->getIdHexStr()."\n");
                 $this->log('debug', 'msg ' . $id . ' srcNode: ' . $srcNode->getIdHexStr());
                 if ($srcNode->getSslKeyPub()) {
                     if ($srcNode->getSslKeyPub() != $srcSslKeyPub) {
                         $status = 3;
                         // Error
                     }
                 } else {
                     if (Node::genIdHexStr($srcSslKeyPub) == $srcNodeId) {
                         if ($srcNode->setSslKeyPub($srcSslKeyPub)) {
                             $srcNode->setDataChanged(true);
                         }
                     } else {
                         $status = 3;
                         // Error
                     }
                 }
                 if ($hashcash && $this->hashcashVerify($hashcash, $this->getNode()->getIdHexStr(), static::HASHCASH_BITS_MAX)) {
                     $msgHandleReturnValue .= $this->sendMsgResponse($rid, $status);
                     if ($status == 1) {
                         $msg = new Msg();
                         $msg->setVersion($version);
                         $msg->setId($id);
                         $msg->setRelayNodeId($this->getNode()->getIdHexStr());
                         $msg->setSrcNodeId($srcNodeId);
                         $msg->setSrcSslKeyPub($srcSslKeyPub);
                         $msg->setDstNodeId($dstNodeId);
                         $msg->setBody($body);
                         $msg->setPassword($password);
                         $msg->setChecksum($checksum);
                         $msg->setRelayCount($relayCount);
                         $msg->setEncryptionMode('D');
                         $msg->setStatus('U');
                         $msg->setTimeCreated($timeCreated);
                         $msg->setTimeReceived(time());
                         if ($msg->getDstNodeId() == $this->getLocalNode()->getIdHexStr()) {
                             $msg->setDstSslPubKey($this->getLocalNode()->getSslKeyPub());
                             $msg->setSsl($this->getSsl());
                             try {
                                 if ($msg->decrypt()) {
                                     #fwrite(STDOUT, 'msg '.$id.': decrypt ok'."\n");
                                     $this->log('debug', 'msg ' . $id . ' decrypt ok');
                                     if (!$msg->getIgnore()) {
                                         #fwrite(STDOUT, 'msg '.$id.': not ignore'."\n");
                                         $this->log('debug', 'msg ' . $id . ' not ignore');
                                         $this->log('debug', 'msg ' . $id . ' subject: ' . $msg->getSubject());
                                         $this->getServer()->imapMailAdd($msg);
                                         $this->getServer()->consoleMsgAdd('You got mail.', true, true, true);
                                     } else {
                                         #fwrite(STDOUT, 'msg '.$id.': ignore'."\n");
                                         $this->log('debug', 'msg ' . $id . ' ignore');
                                     }
                                 } else {
                                     #fwrite(STDOUT, 'msg '.$id.': decrypt failed B'."\n");
                                     $this->log('debug', 'msg ' . $id . ' decrypt failed B');
                                 }
                             } catch (Exception $e) {
                                 #fwrite(STDOUT, 'msg '.$id.': decrypt failed A: '.$e->getMessage()."\n");
                                 $this->log('debug', 'msg ' . $id . ' decrypt failed A: ' . $e->getMessage());
                             }
                         } else {
                             #fwrite(STDOUT, 'msg '.$id.': msg not for me'."\n");
                             $this->log('debug', 'msg ' . $id . ' not for me');
                         }
                         $this->getMsgDb()->msgAdd($msg);
                         // Add all messages.
                     }
                 } else {
                     $msgHandleReturnValue .= $this->sendError(4000, $msgName);
                 }
             } else {
                 $msgHandleReturnValue .= $this->sendError(3090, $msgName);
             }
         } else {
             $msgHandleReturnValue .= $this->sendError(1000, $msgName);
         }
     } elseif ($msgName == 'msg_response') {
         if ($this->getStatus('hasId')) {
             $rid = '';
             $status = 0;
             if (array_key_exists('rid', $msgData)) {
                 $rid = $msgData['rid'];
             }
             if (array_key_exists('status', $msgData)) {
                 $status = (int) $msgData['status'];
             }
             $this->log('debug', $this->getUri() . ' recv ' . $msgName . ': ' . $rid . ', ' . $status);
             $request = $this->requestGetByRid($rid);
             if ($request) {
                 #ve($request);
                 $msg = $request['data']['msg'];
                 $msg->addSentNode($this->getNode()->getIdHexStr());
                 $msg->setStatus('S');
                 if ($this->getNode()->getIdHexStr() == $msg->getDstNodeId()) {
                     $msg->setStatus('D');
                 }
                 $this->log('debug', 'actions execute: CRITERION_AFTER_MSG_RESPONSE_SUCCESSFULL');
                 $this->actionsExecute(ClientAction::CRITERION_AFTER_MSG_RESPONSE_SUCCESSFULL);
             } else {
                 $msgHandleReturnValue .= $this->sendError(9000, $msgName);
             }
         } else {
             $msgHandleReturnValue .= $this->sendError(1000, $msgName);
         }
         $this->log('debug', 'actions execute: CRITERION_AFTER_MSG_RESPONSE');
         $this->actionsExecute(ClientAction::CRITERION_AFTER_MSG_RESPONSE);
     } elseif ($msgName == 'ssl_init') {
         #ve($this);
         if ($this->getSsl()) {
             if ($this->getStatus('hasId')) {
                 if (!$this->getStatus('hasSslInit')) {
                     $rid = '';
                     $hashcash = '';
                     if (array_key_exists('rid', $msgData)) {
                         $rid = $msgData['rid'];
                     }
                     if (array_key_exists('hashcash', $msgData)) {
                         $hashcash = $msgData['hashcash'];
                     }
                     $this->logColor('debug', 'SSL: init A: ' . $rid, 'green');
                     if ($hashcash && $this->hashcashVerify($hashcash, $this->getNode()->getIdHexStr(), static::HASHCASH_BITS_MIN)) {
                         $this->logColor('debug', 'SSL: init B', 'green');
                         $this->setStatus('hasSslInit', true);
                         $msgHandleReturnValue .= $this->sendSslInit();
                         $msgHandleReturnValue .= $this->sendSslInitResponse($rid, 1);
                         #fwrite(STDOUT, 'ssl init: /'.$msgHandleReturnValue.'/'."\n");
                         #ve($msgHandleReturnValue);
                     } else {
                         $this->resetStatusSsl();
                         #$msgHandleReturnValue .= $this->sendError(4000, $msgName);
                         $msgHandleReturnValue .= $this->sendSslInitResponse($rid, 4000);
                     }
                 }
             } else {
                 $this->resetStatusSsl();
                 #$msgHandleReturnValue .= $this->sendError(1000, $msgName);
                 $msgHandleReturnValue .= $this->sendSslInitResponse(null, 1000);
             }
         } else {
             $this->resetStatusSsl();
             #$msgHandleReturnValue .= $this->sendError(3090, $msgName);
             $msgHandleReturnValue .= $this->sendSslInitResponse(null, 3090);
         }
     } elseif ($msgName == 'ssl_init_response') {
         $rid = '';
         $status = 0;
         if (array_key_exists('rid', $msgData)) {
             $rid = $msgData['rid'];
         }
         if (array_key_exists('status', $msgData)) {
             $status = $msgData['status'];
         }
         $this->logColor('debug', 'SSL: init response: ' . $rid . ' ' . $status, 'green');
         if ($status) {
             if ($status == 1) {
                 // Ok
                 if ($this->getStatus('hasSslInit') && !$this->getStatus('hasSslInitOk')) {
                     $this->logColor('debug', 'SSL: init ok', 'green');
                     $this->setStatus('hasSslInitOk', true);
                     $msgHandleReturnValue .= $this->sendSslTest();
                 } else {
                     $this->logColor('warning', $msgName . ' SSL: you already initialized ssl', 'green');
                     $msgHandleReturnValue .= $this->sendError(2050, $msgName);
                 }
             } else {
                 $this->logColor('warning', $msgName . ' SSL: failed. status = ' . $status, 'green');
                 $this->resetStatusSsl();
                 $msgHandleReturnValue .= $this->sendError(3100, $msgName);
             }
         } else {
             $this->logColor('warning', $msgName . ' SSL: failed, invalid data', 'green');
             $this->resetStatusSsl();
             $msgHandleReturnValue .= $this->sendError(3100, $msgName);
         }
     } elseif ($msgName == 'ssl_test') {
         if ($this->getStatus('hasSslInitOk') && !$this->getStatus('hasSslTest')) {
             $msgData = $this->sslMsgDataPrivateDecrypt($msgData);
             if ($msgData) {
                 $token = '';
                 if (array_key_exists('token', $msgData)) {
                     $token = $msgData['token'];
                 }
                 if ($token) {
                     $this->logColor('debug', 'SSL: test', 'green');
                     $this->setStatus('hasSslTest', true);
                     $msgHandleReturnValue .= $this->sendSslVerify($token);
                 } else {
                     $msgHandleReturnValue .= $this->sendError(9000, $msgName);
                 }
             } else {
                 $msgHandleReturnValue .= $this->sendError(2070, $msgName);
                 $this->logColor('warning', $msgName . ' SSL: decryption failed', 'green');
             }
         } else {
             $msgHandleReturnValue .= $this->sendError(2060, $msgName);
             $logTmp = 'you need to initialize ssl /' . (int) $this->getStatus('hasSslInitOk') . '/';
             $logTmp .= ' /' . (int) $this->getStatus('hasSslTest') . '/';
             $this->logColor('warning', $msgName . ' SSL: ' . $logTmp, 'green');
         }
     } elseif ($msgName == 'ssl_verify') {
         if ($this->getStatus('hasSslTest') && !$this->getStatus('hasSslVerify')) {
             $msgData = $this->sslMsgDataPrivateDecrypt($msgData);
             if ($msgData) {
                 $token = '';
                 if (array_key_exists('token', $msgData)) {
                     $token = $msgData['token'];
                 }
                 if ($token && $this->sslTestToken && $token == $this->sslTestToken) {
                     $this->logColor('debug', 'SSL: verified', 'green');
                     #print __CLASS__.'->'.__FUNCTION__.': '.$msgName.' SSL: verified'."\n";
                     $this->setStatus('hasSslVerify', true);
                     $msgHandleReturnValue .= $this->sendSslPasswordPut();
                 } else {
                     $msgHandleReturnValue .= $this->sendError(2080, $msgName);
                 }
             } else {
                 $msgHandleReturnValue .= $this->sendError(2070, $msgName);
                 $this->logColor('warning', $msgName . ' SSL: decryption failed', 'green');
             }
         } else {
             $msgHandleReturnValue .= $this->sendError(2060, $msgName);
             $this->logColor('warning', $msgName . ' SSL: you need to initialize ssl', 'green');
         }
     } elseif ($msgName == 'ssl_password_put') {
         if ($this->getStatus('hasSslVerify') && !$this->getStatus('hasSslPasswortPut')) {
             $msgData = $this->sslMsgDataPrivateDecrypt($msgData);
             if ($msgData) {
                 $password = '';
                 if (array_key_exists('password', $msgData)) {
                     $password = $msgData['password'];
                 }
                 if ($password) {
                     $this->logColor('debug', 'SSL: password put', 'green');
                     $this->setStatus('hasSslPasswortPut', true);
                     $this->sslPasswordPeerCurrent = $password;
                     #$this->logColor('debug', 'SSL: peer password: '******'green');
                     $msgHandleReturnValue .= $this->sendSslPasswordTest();
                 } else {
                     $msgHandleReturnValue .= $this->sendError(9000, $msgName);
                 }
             } else {
                 $msgHandleReturnValue .= $this->sendError(2070, $msgName);
                 $this->logColor('warning', $msgName . ' SSL: decryption failed', 'green');
             }
         } else {
             $msgHandleReturnValue .= $this->sendError(2060, $msgName);
             $this->logColor('warning', $msgName . ' SSL: you need to initialize ssl', 'green');
         }
     } elseif ($msgName == 'ssl_password_test') {
         if ($this->getStatus('hasSslPasswortPut') && !$this->getStatus('hasSslPasswortTest')) {
             $msgData = $this->sslMsgDataPasswordDecrypt($msgData);
             if ($msgData) {
                 $token = '';
                 if (array_key_exists('token', $msgData)) {
                     $token = $msgData['token'];
                 }
                 if ($token) {
                     $this->setStatus('hasSslPasswortTest', true);
                     $msgHandleReturnValue .= $this->sendSslPasswordVerify($token);
                 } else {
                     $msgHandleReturnValue .= $this->sendError(9000, $msgName);
                 }
             } else {
                 $msgHandleReturnValue .= $this->sendError(2070, $msgName);
                 $this->logColor('warning', $msgName . ' SSL: decryption failed', 'green');
             }
         } else {
             $msgHandleReturnValue .= $this->sendError(2060, $msgName);
             $this->logColor('warning', $msgName . ' SSL: you need to initialize ssl', 'green');
         }
     } elseif ($msgName == 'ssl_password_verify') {
         if ($this->getStatus('hasSslPasswortTest')) {
             $msgData = $this->sslMsgDataPasswordDecrypt($msgData);
             if ($msgData) {
                 $token = '';
                 if (array_key_exists('token', $msgData)) {
                     $token = $msgData['token'];
                 }
                 #print __CLASS__.'->'.__FUNCTION__.': '.$msgName.' SSL: password token: '.$token."\n";
                 if ($token) {
                     $testToken = hash('sha512', $this->sslPasswordToken . '_' . $this->getNode()->getIdHexStr());
                     if ($this->sslPasswordToken && $token == $testToken) {
                         $this->logColor('debug', 'SSL: password verified', 'green');
                         $this->logColor('debug', 'SSL: OK', 'green');
                         $this->setStatus('hasSsl', true);
                         $this->log('debug', 'actions execute: CRITERION_AFTER_HAS_SSL');
                         $this->actionsExecute(ClientAction::CRITERION_AFTER_HAS_SSL);
                     } else {
                         $msgHandleReturnValue .= $this->sendError(2090, $msgName);
                     }
                 } else {
                     $msgHandleReturnValue .= $this->sendError(9000, $msgName);
                 }
             } else {
                 $msgHandleReturnValue .= $this->sendError(2070, $msgName);
                 $this->logColor('warning', $msgName . ' SSL: decryption failed', 'green');
             }
         } else {
             $msgHandleReturnValue .= $this->sendError(2060, $msgName);
             $this->logColor('warning', $msgName . ' SSL: you need to initialize ssl', 'green');
         }
         $this->sslTestToken = '';
         $this->sslPasswordToken = '';
     } elseif ($msgName == 'ssl_password_reput') {
         if ($this->getStatus('hasSsl')) {
             if (!$this->getStatus('hasReSslPasswortPut')) {
                 $msgData = $this->sslMsgDataPasswordDecrypt($msgData);
                 if ($msgData) {
                     $password = '';
                     if (array_key_exists('password', $msgData)) {
                         $password = $msgData['password'];
                     }
                     if ($password) {
                         $this->logColor('debug', 're-SSL: password reput 1A', 'green');
                         $this->setStatus('hasReSslPasswortPut', true);
                         $this->sslPasswordPeerNew = $password;
                         #$this->logColor('debug', 'SSL: peer password: '******'green');
                         #$this->logColor('debug', 'SSL: peer password new: '.substr($this->sslPasswordPeerNew, 0, 20), 'green');
                         if (!$this->getStatus('hasSendReSslPasswortPut')) {
                             $this->sslMsgCount = 0;
                             $this->sslPasswordToken = '';
                             $this->sslPasswordLocalNew = '';
                             #$this->sslPasswordPeerNew = '';
                             #$this->setStatus('hasSendReSslPasswortPut', true);
                             #$this->setStatus('hasReSslPasswortTest', false);
                             $this->logColor('debug', 're-SSL: password reput 1B', 'green');
                             $msgHandleReturnValue .= $this->sendSslPasswordReput();
                         }
                         $this->logColor('debug', 're-SSL: password reput 2', 'green');
                         $msgHandleReturnValue .= $this->sendSslPasswordRetest();
                     } else {
                         $msgHandleReturnValue .= $this->sendError(9000, $msgName);
                     }
                 } else {
                     $msgHandleReturnValue .= $this->sendError(2070, $msgName);
                     $this->logColor('warning', $msgName . ' re-SSL: decryption failed', 'green');
                 }
             }
         } else {
             $msgHandleReturnValue .= $this->sendError(2060, $msgName);
             $logMsg = $msgName . ' re-SSL: you need to initialize ssl, ';
             $logMsg .= 'hasSsl=/' . (int) $this->getStatus('hasSsl') . '/ ';
             $logMsg .= 'hasReSslPasswortPut=/' . (int) $this->getStatus('hasReSslPasswortPut') . '/';
             $this->logColor('warning', $logMsg, 'green');
         }
     } elseif ($msgName == 'ssl_password_retest') {
         if ($this->getStatus('hasReSslPasswortPut') && !$this->getStatus('hasReSslPasswortTest')) {
             $msgData = $this->sslMsgDataPasswordDecrypt($msgData, $this->sslPasswordLocalNew, $this->sslPasswordPeerNew);
             if ($msgData) {
                 $token = '';
                 if (array_key_exists('token', $msgData)) {
                     $token = $msgData['token'];
                 }
                 if ($token) {
                     $this->logColor('debug', 're-SSL: password retest', 'green');
                     $this->setStatus('hasReSslPasswortTest', true);
                     $msgHandleReturnValue .= $this->sendSslPasswordReverify($token);
                 } else {
                     $msgHandleReturnValue .= $this->sendError(9000, $msgName);
                 }
             } else {
                 $msgHandleReturnValue .= $this->sendError(2070, $msgName);
                 $this->logColor('warning', $msgName . ' re-SSL: decryption failed', 'green');
             }
         } else {
             $msgHandleReturnValue .= $this->sendError(2060, $msgName);
             $logMsg = $msgName . ' re-SSL: you need to initialize ssl, ';
             $logMsg .= 'hasReSslPasswortPut=/' . (int) $this->getStatus('hasReSslPasswortPut') . '/ ';
             $logMsg .= 'hasReSslPasswortTest=/' . (int) $this->getStatus('hasReSslPasswortTest') . '/';
             $this->logColor('warning', $logMsg, 'green');
         }
     } elseif ($msgName == 'ssl_password_reverify') {
         if ($this->getStatus('hasReSslPasswortTest')) {
             $msgData = $this->sslMsgDataPasswordDecrypt($msgData, $this->sslPasswordLocalNew, $this->sslPasswordPeerNew);
             if ($msgData) {
                 $token = '';
                 if (array_key_exists('token', $msgData)) {
                     $token = $msgData['token'];
                 }
                 #print __CLASS__.'->'.__FUNCTION__.': '.$msgName.' re-SSL: password token: '.$token."\n";
                 if ($token) {
                     $testToken = hash('sha512', $this->sslPasswordToken . '_' . $this->getNode()->getSslKeyPubFingerprint());
                     if ($this->sslPasswordToken && $token == $testToken) {
                         $this->logColor('debug', 're-SSL: password verified', 'green');
                         $this->logColor('debug', 're-SSL: OK', 'green');
                         $this->setStatus('hasSendReSslPasswortPut', false);
                         $this->setStatus('hasReSslPasswortPut', false);
                         $this->setStatus('hasReSslPasswortTest', false);
                         $this->sslPasswordLocalCurrent = $this->sslPasswordLocalNew;
                         $this->sslPasswordPeerCurrent = $this->sslPasswordPeerNew;
                         $this->logColor('debug', 'actions execute: CRITERION_AFTER_HAS_RESSL', 'green');
                         $this->actionsExecute(ClientAction::CRITERION_AFTER_HAS_RESSL);
                     } else {
                         $msgHandleReturnValue .= $this->sendError(2090, $msgName);
                     }
                 } else {
                     $msgHandleReturnValue .= $this->sendError(9000, $msgName);
                 }
             } else {
                 $msgHandleReturnValue .= $this->sendError(2070, $msgName);
                 $this->logColor('warning', $msgName . ' re-SSL: decryption failed', 'green');
             }
         } else {
             $msgHandleReturnValue .= $this->sendError(2060, $msgName);
             $logMsg = $msgName . ' re-SSL: you need to initialize ssl, ';
             $logMsg .= 'hasReSslPasswortTest=/' . (int) $this->getStatus('hasReSslPasswortTest') . '/';
             $this->logColor('warning', $logMsg, 'green');
         }
         $this->sslPasswordToken = '';
         $this->sslPasswordLocalNew = '';
         $this->sslPasswordLocalNew = '';
     } elseif ($msgName == 'talk_request') {
         if ($this->getStatus('hasSsl')) {
             $msgData = $this->sslMsgDataPasswordDecrypt($msgData);
             if ($msgData) {
                 $rid = '';
                 $userNickname = '[unknown]';
                 $hashcash = '';
                 if (array_key_exists('rid', $msgData)) {
                     $rid = $msgData['rid'];
                 }
                 if (array_key_exists('userNickname', $msgData) && $msgData['userNickname']) {
                     $userNickname = $msgData['userNickname'];
                 }
                 if (array_key_exists('hashcash', $msgData)) {
                     $hashcash = $msgData['hashcash'];
                 }
                 $this->log('debug', $this->getUri() . ' recv ' . $msgName . ': ' . $rid . ', ' . $userNickname);
                 if ($rid) {
                     if ($hashcash && $this->hashcashVerify($hashcash, $this->getNode()->getIdHexStr(), static::HASHCASH_BITS_MAX)) {
                         if ($this->getServer() && $this->getServer()->kernelHasConsole()) {
                             $this->setStatus('hasTalkRequest', true);
                             $this->consoleTalkRequestAdd($rid, $userNickname);
                         } else {
                             $msgHandleReturnValue .= $this->sendTalkResponse($rid, 4);
                             #$msgHandleReturnValue .= $this->sendQuit();
                             #$this->shutdown();
                             $action = new ClientAction(ClientAction::CRITERION_AFTER_PREVIOUS_ACTIONS);
                             $action->setName('talk_request_after_previous_actions_shutdown');
                             $action->functionSet(function ($action, $client) {
                                 $client->shutdown();
                             });
                             $this->actionAdd($action);
                         }
                     } else {
                         $msgHandleReturnValue .= $this->sendError(4000, $msgName);
                     }
                 } else {
                     $msgHandleReturnValue .= $this->sendError(9000, $msgName);
                 }
             } else {
                 $msgHandleReturnValue .= $this->sendError(9000, $msgName);
             }
         } else {
             $msgHandleReturnValue .= $this->sendError(2060, $msgName);
             $this->logColor('warning', $msgName . ' SSL: you need to initialize ssl', 'green');
         }
     } elseif ($msgName == 'talk_response') {
         if ($this->getStatus('hasSsl')) {
             $msgData = $this->sslMsgDataPasswordDecrypt($msgData);
             if ($msgData) {
                 $rid = '';
                 $status = 0;
                 $userNickname = '[unknown]';
                 if (array_key_exists('rid', $msgData)) {
                     $rid = $msgData['rid'];
                 }
                 if (array_key_exists('status', $msgData)) {
                     $status = (int) $msgData['status'];
                 }
                 if (array_key_exists('userNickname', $msgData) && $msgData['userNickname']) {
                     $userNickname = $msgData['userNickname'];
                 }
                 #$this->log('debug', $this->getUri().' recv '.$msgName.': '.$rid.', '.(int)$status.', '.$userNickname);
                 $this->log('debug', $this->getUri() . ' recv ' . $msgName . ': ' . $rid . ', ' . (int) $status);
                 $request = $this->requestGetByRid($rid);
                 if ($request) {
                     $this->requestRemove($request);
                     $this->log('debug', $this->getUri() . ' recv ' . $msgName . ': request ok (' . $status . ')');
                     //if($status == 0){} // Undefined
                     if ($status == 1) {
                         // Accepted
                         $this->setStatus('hasTalk', true);
                         $this->consoleMsgAdd('Talk request accepted.', true, false, true);
                         $this->consoleMsgAdd('Now talking to "' . $userNickname . '".', true, true);
                         $this->consoleSetModeChannel(true);
                         $this->consoleSetModeChannelClient($this);
                         if ($this->getServer() && $this->getServer()->getKernel()) {
                             // Add to addressbook.
                             $contact = new Contact();
                             $contact->setNodeId($this->getNode()->getIdHexStr());
                             $contact->setUserNickname($userNickname);
                             $this->getServer()->getKernel()->getAddressbook()->contactAdd($contact);
                         }
                     } elseif ($status == 2) {
                         // Declined
                         $this->consoleMsgAdd('Talk request declined.', true, true, true);
                     } elseif ($status == 3) {
                         // Timeout
                         $this->consoleMsgAdd('Talk request timed out.', true, true, true);
                     } elseif ($status == 4) {
                         // No console, standalone server.
                         $this->consoleMsgAdd($this->getUri() . ' has no user interface. Can\'t talk to you.', true, true, true);
                         #$msgHandleReturnValue .= $this->sendQuit();
                         #$this->shutdown();
                         $action = new ClientAction(ClientAction::CRITERION_AFTER_PREVIOUS_ACTIONS);
                         $action->setName('talk_request_after_previous_actions_shutdown');
                         $action->functionSet(function ($action, $client) {
                             $client->sendQuit();
                             $client->shutdown();
                         });
                         $this->actionAdd($action);
                         $this->log('debug', 'actions left: ' . count($this->actions));
                     }
                 } else {
                     $msgHandleReturnValue .= $this->sendError(9000, $msgName);
                 }
             }
         }
     } elseif ($msgName == 'talk_msg') {
         #$this->log('debug', $this->getUri().' recv '.$msgName);
         if ($this->getStatus('hasSsl')) {
             $msgData = $this->sslMsgDataPasswordDecrypt($msgData);
             if ($msgData) {
                 $rid = '';
                 $userNickname = '[unknown]';
                 $text = '';
                 $ignore = false;
                 if (array_key_exists('rid', $msgData)) {
                     $rid = $msgData['rid'];
                 }
                 if (array_key_exists('userNickname', $msgData) && $msgData['userNickname']) {
                     $userNickname = $msgData['userNickname'];
                 }
                 if (array_key_exists('text', $msgData)) {
                     $text = $msgData['text'];
                 }
                 if (array_key_exists('ignore', $msgData)) {
                     $ignore = $msgData['ignore'];
                 }
                 #$debugText = '/'.$rid.'/ /'.$userNickname.'/ '.(int)$ignore.' /'.$text.'/';
                 #$this->log('debug', $this->getUri().' recv '.$msgName.': '.$debugText);
                 $this->log('debug', $this->getUri() . ' recv ' . $msgName);
                 if (!$ignore) {
                     $this->consoleTalkMsgAdd($rid, $userNickname, $text);
                 }
             }
         }
     } elseif ($msgName == 'talk_user_nickname_change') {
         #$this->log('debug', $this->getUri().' recv '.$msgName);
         if ($this->getStatus('hasSsl')) {
             $msgData = $this->sslMsgDataPasswordDecrypt($msgData);
             if ($msgData) {
                 $userNicknameOld = '[unknown]';
                 $userNicknameNew = '';
                 if (array_key_exists('userNicknameOld', $msgData) && $msgData['userNicknameOld']) {
                     $userNicknameOld = $msgData['userNicknameOld'];
                 }
                 if (array_key_exists('userNicknameNew', $msgData)) {
                     $userNicknameNew = $msgData['userNicknameNew'];
                 }
                 if ($this->getServer() && $this->getServer()->getKernel()) {
                     $contact = $this->getServer()->getKernel()->getAddressbook()->contactGetByNodeId($this->getNode()->getIdHexStr());
                     if ($contact) {
                         if (!$userNicknameOld) {
                             $userNicknameOld = $contact->getUserNickname();
                         }
                         if ($userNicknameNew) {
                             $contact->setUserNickname($userNicknameNew);
                             $this->getServer()->getKernel()->getAddressbook()->setDataChanged(true);
                         }
                     }
                 }
                 if ($userNicknameNew) {
                     #$this->log('debug', $this->getUri().' recv '.$msgName.': '.$userNicknameOld.', '.$userNicknameNew);
                     $this->consoleMsgAdd('User "' . $userNicknameOld . '" is now known as "' . $userNicknameNew . '".', true, true, true);
                 }
             }
         }
     } elseif ($msgName == 'talk_close') {
         if ($this->getStatus('hasSsl')) {
             $msgData = $this->sslMsgDataPasswordDecrypt($msgData);
             if ($msgData) {
                 $rid = '';
                 $userNickname = '[unknown]';
                 if (array_key_exists('rid', $msgData)) {
                     $rid = $msgData['rid'];
                 }
                 if (array_key_exists('userNickname', $msgData) && $msgData['userNickname']) {
                     $userNickname = $msgData['userNickname'];
                 }
                 $this->log('debug', $this->getUri() . ' recv ' . $msgName . ': ' . $rid . ', ' . $userNickname);
                 $msgHandleReturnValue .= $this->sendQuit();
                 $this->setStatus('hasTalkClose', true);
                 $this->shutdown();
                 $this->consoleMsgAdd('Talk closed by "' . $userNickname . '".', true, true, true);
                 $this->consoleSetModeChannel(false);
                 $this->consoleSetModeChannelClient(null);
             }
         }
     } elseif ($msgName == 'bridge_connect') {
         if ($this->getSettings()->data['node']['bridge']['server']['enabled']) {
             if ($this->getStatus('hasSsl')) {
                 $msgData = $this->sslMsgDataPasswordDecrypt($msgData);
                 if ($msgData) {
                     $targetUri = '';
                     if (array_key_exists('uri', $msgData)) {
                         $targetUri = $msgData['uri'];
                         $targetUri = UriFactory::factory($targetUri);
                     }
                     $this->logColor('debug', $this->getUri() . ' recv ' . $msgName . ': target /' . $targetUri . '/', 'yellow');
                     $client = $this->getServer()->connect($targetUri);
                     if ($client !== null) {
                         $this->logColor('debug', 'bridge connected to target /' . $targetUri . '/', 'yellow');
                         $this->setBridgeClient($client);
                         $client->setBridgeClient($client);
                         $client->setStatus('bridgeTargetUri', $targetUri);
                         $msgHandleReturnValue .= $this->sendBridgeConnectResponse(1);
                     } else {
                         $this->logColor('debug', 'bridge connection to target /' . $targetUri . '/ failed', 'yellow');
                         $msgHandleReturnValue .= $this->sendBridgeConnectResponse(2);
                     }
                 } else {
                     $msgHandleReturnValue .= $this->sendError(9000, $msgName);
                 }
             } else {
                 $msgHandleReturnValue .= $this->sendError(2060, $msgName);
                 $this->log('warning', static::getErrorMsg(2060));
             }
         } else {
             $msgHandleReturnValue .= $this->sendError(5000, $msgName);
             $this->log('warning', static::getErrorMsg(5000));
         }
     } elseif ($msgName == 'bridge_connect_response') {
         if ($this->getSettings()->data['node']['bridge']['server']['enabled'] || $this->getSettings()->data['node']['bridge']['client']['enabled']) {
             if ($this->getStatus('hasSsl')) {
                 $msgData = $this->sslMsgDataPasswordDecrypt($msgData);
                 if ($msgData) {
                     $status = 0;
                     if (array_key_exists('status', $msgData)) {
                         $status = (int) $msgData['status'];
                     }
                     $this->logColor('debug', $this->getUri() . ' recv ' . $msgName . ': /' . $status . '/', 'yellow');
                     if ($status) {
                         if ($status == 1) {
                             $this->logColor('debug', 'bridge connection ok', 'yellow');
                             $this->bridgeActionsExecute(ClientAction::CRITERION_AFTER_HELLO);
                             $this->bridgeActionsExecute(ClientAction::CRITERION_AFTER_HAS_SSL);
                             /*foreach($this->bridgeActions as $bridgeActionId => $bridgeAction){
                             			$name = $bridgeAction->getName();
                             			$criteria = join(',', $bridgeAction->getCriteria());
                             			$this->logColor('debug', 'bridgeAction: /'.$name.'/ /'.$criteria.'/', 'yellow');
                             			#$this->bridgeActionRemove($bridgeAction);
                             			#$bridgeAction->functionExec($this);
                             		}*/
                         } elseif ($status == 2) {
                             $this->logColor('debug', 'bridge connection failed', 'yellow');
                         } else {
                             $this->logColor('debug', 'bridge connect response unknown status', 'yellow');
                         }
                     } else {
                         $msgHandleReturnValue .= $this->sendError(9000, $msgName);
                     }
                 } else {
                     $msgHandleReturnValue .= $this->sendError(9000, $msgName);
                 }
             } else {
                 $msgHandleReturnValue .= $this->sendError(2060, $msgName);
                 $this->log('warning', static::getErrorMsg(2060));
             }
         } else {
             $msgHandleReturnValue .= $this->sendError(5100, $msgName);
             $this->log('warning', static::getErrorMsg(5100));
         }
     } elseif ($msgName == 'bridge_msg') {
         if ($this->getSettings()->data['node']['bridge']['server']['enabled']) {
             if ($this->getStatus('hasSsl')) {
                 $msgData = $this->sslMsgDataPasswordDecrypt($msgData);
                 if ($msgData) {
                     $data = '';
                     if (array_key_exists('data', $msgData)) {
                         $data = $msgData['data'];
                     }
                     $this->logColor('debug', $this->getUri() . ' recv ' . $msgName . ': /' . $data . '/', 'yellow');
                     $this->logColor('debug', 'bridge server: ' . $this->getStatus('bridgeServerUri'), 'yellow');
                     $this->logColor('debug', 'bridge target: ' . $this->getStatus('bridgeTargetUri'), 'yellow');
                     $this->logColor('debug', 'bridge client: ' . (int) ($this->bridgeClient !== null), 'yellow');
                     # TODO
                     if ($this->bridgeClient) {
                     }
                 } else {
                     $msgHandleReturnValue .= $this->sendError(9000, $msgName);
                 }
             } else {
                 $msgHandleReturnValue .= $this->sendError(2060, $msgName);
                 $this->log('warning', static::getErrorMsg(2060));
             }
         } else {
             $msgHandleReturnValue .= $this->sendError(5200, $msgName);
             $this->log('warning', static::getErrorMsg(5200));
         }
     } elseif ($msgName == 'ping') {
         $rid = '';
         if (array_key_exists('rid', $msgData)) {
             $rid = $msgData['rid'];
         }
         $msgHandleReturnValue .= $this->sendPong($rid);
     } elseif ($msgName == 'pong') {
         $rid = '';
         if (array_key_exists('rid', $msgData)) {
             $rid = $msgData['rid'];
         }
         $this->pongTime = time();
     } elseif ($msgName == 'error') {
         $code = 0;
         $msg = '';
         $name = '';
         if (array_key_exists('msg', $msgData)) {
             $code = (int) $msgData['code'];
         }
         if (array_key_exists('msg', $msgData)) {
             $msg = $msgData['msg'];
         }
         if (array_key_exists('msg', $msgData)) {
             $name = $msgData['name'];
         }
         if ($code >= 2000 && $code <= 3999) {
             // SSL
             $this->logColor('debug', $this->getUri() . ' recv ' . $msgName . ': ' . $code . ', ' . $msg . ', ' . $name, 'green');
         } elseif ($code >= 5000 && $code <= 5999) {
             // Bridge
             $this->logColor('debug', $this->getUri() . ' recv ' . $msgName . ': ' . $code . ', ' . $msg . ', ' . $name, 'yellow');
         } else {
             $this->log('debug', $this->getUri() . ' recv ' . $msgName . ': ' . $code . ', ' . $msg . ', ' . $name);
         }
     } elseif ($msgName == 'quit') {
         $this->shutdown();
     } else {
         $this->log('debug', $this->getUri() . ' recv /' . $msgName . '/: not implemented.');
         $msgHandleReturnValue .= $this->sendError(9020, $msgName);
     }
     return $msgHandleReturnValue;
 }