Skip to content

Instantly share code, notes, and snippets.

@keyosk
Last active August 29, 2015 14:00
Show Gist options
  • Save keyosk/9c86b981948a3cf7f378 to your computer and use it in GitHub Desktop.
Save keyosk/9c86b981948a3cf7f378 to your computer and use it in GitHub Desktop.
Pubnub PHP SDK 3.4 Altered with PAM hotfix
<?php
require_once('PubnubAES.php');
/**
* PubNub 3.4 Real-time Push Cloud API
* @package Pubnub
*/
class Pubnub {
private $ORIGIN = 'pubsub.pubnub.com';
private $PUBLISH_KEY = 'demo';
private $SUBSCRIBE_KEY = 'demo';
private $SECRET_KEY = false;
private $AUTH_TOKEN = false;
private $CIPHER_KEY = '';
private $SSL = false;
private $SESSION_UUID = '';
private $PROXY = false;
// New style response contains channel after timetoken
// Old style response does not
private $NEW_STYLE_RESPONSE = true;
private $PEM_PATH = __DIR__;
/**
* Pubnub
*
* Init the Pubnub Client API
*
* @param string $publish_key required key to send messages.
* @param string $subscribe_key required key to receive messages.
* @param string $secret_key optional key to sign messages.
* @param string $origin optional setting for cloud origin.
* @param boolean $ssl required for 2048 bit encrypted messages.
*/
function Pubnub(
$args = array(),
$subscribe_key = 'demo',
$secret_key = false,
$cipher_key = false,
$ssl = false,
$origin = false,
$pem_path = false,
$proxy = false
) {
$publish_key = 'demo';
$auth_token = false;
if (is_array($args)) {
$publish_key = isset($args['publish_key']) ? $args['publish_key'] : $publish_key;
$subscribe_key = isset($args['subscribe_key']) ? $args['subscribe_key'] : $subscribe_key;
$secret_key = isset($args['secret_key']) ? $args['secret_key'] : $secret_key;
$auth_token = isset($args['auth_token']) ? $args['auth_token'] : $auth_token;
$cipher_key = isset($args['cipher_key']) ? $args['cipher_key'] : $cipher_key;
$ssl = isset($args['ssl']) ? $args['ssl'] : $ssl;
$origin = isset($args['origin']) ? $args['origin'] : $origin;
$pem_path = isset($args['pem_path']) ? $args['pem_path'] : $pem_path;
$proxy = isset($args['proxy']) ? $args['proxy'] : $proxy;
} else {
$publish_key = $args;
}
$this->SESSION_UUID = $this->uuid();
$this->PUBLISH_KEY = $publish_key;
$this->SUBSCRIBE_KEY = $subscribe_key;
$this->AUTH_TOKEN = $auth_token;
$this->SECRET_KEY = $secret_key;
$this->PROXY = $proxy;
if (!isBlank($cipher_key)) {
$this->CIPHER_KEY = $cipher_key;
}
$this->SSL = $ssl;
if ($pem_path != false) $this->PEM_PATH = $pem_path;
if ($origin) $this->ORIGIN = $origin;
if ($ssl) $this->ORIGIN = 'https://' . $this->ORIGIN;
else $this->ORIGIN = 'http://' . $this->ORIGIN;
}
/**
* Publish
*
* Send a message to a channel.
*
* @param array $args with channel and message.
* @return array success information.
*/
function publish($args)
{
## Fail if bad input.
if (!(isset($args['channel']) && isset($args['message']))) {
echo('Missing Channel or Message');
return false;
}
## Capture User Input
$channel = $args['channel'];
$message_org = $args['message'];
$message = $this->sendMessage($message_org);
## Sign Message
$signature = "0";
/*
if ($this->AUTH_TOKEN) {
## Generate String to Sign
$string_to_sign = implode('/', array(
$this->PUBLISH_KEY,
$this->SUBSCRIBE_KEY,
$this->AUTH_TOKEN,
$channel,
$message
));
$signature = md5($string_to_sign);
}
*/
## Send Message
$publishResponse = $this->_request(array(
'publish',
$this->PUBLISH_KEY,
$this->SUBSCRIBE_KEY,
$signature,
$channel,
'0',
$message
));
if ($publishResponse == null)
return array(0, "Error during publish.");
else
return $publishResponse;
}
public function sendMessage($message_org)
{
if ($this->CIPHER_KEY != false) {
$message = json_encode(encrypt(json_encode($message_org), $this->CIPHER_KEY));
} else {
$message = json_encode($message_org);
}
return $message;
}
function here_now($args)
{
if (!($args['channel'])) {
echo('Missing Channel');
return false;
}
## Capture User Input
$channel = $args['channel'];
return $this->_request(array(
'v2',
'presence',
'sub_key',
$this->SUBSCRIBE_KEY,
'channel',
$channel
));
}
/**
* Subscribe
*
* This is BLOCKING.
* Listen for a message on a channel.
*
* @param array $args with channel and message.
* @return mixed false on fail, array on success.
*/
function subscribe($args, $presence = false)
{
## Capture User Input
$channel = $args['channel'];
$callback = $args['callback'];
$timetoken = isset($args['timetoken']) ? $args['timetoken'] : '0';
## Fail if missing channel
if (!$channel) {
echo("Missing Channel.\n");
return false;
}
## Fail if missing callback
if (!$callback) {
echo("Missing Callback.\n");
return false;
}
if ($presence == true) {
$mode = "presence";
} else
$mode = "default";
while (1) {
try {
## Wait for Message
$response = $this->_request(array(
'subscribe',
$this->SUBSCRIBE_KEY,
$channel,
'0',
$timetoken
));
if ($response == "_PUBNUB_TIMEOUT") {
continue;
} elseif ($response == "_PUBNUB_MESSAGE_TOO_LARGE") {
$timetoken = $this->throwAndResetTimetoken($callback, "Message Too Large");
continue;
} elseif ($response == null || $timetoken == null) {
$timetoken = $this->throwAndResetTimetoken($callback, "Bad server response.");
continue;
}
$messages = $response[0];
$timetoken = $response[1];
// determine the channel
if ((count($response) == 3)) {
$derivedChannel = explode(",", $response[2]);
} else {
$channel_array = array();
for ($a = 0; $a < sizeof($messages); $a++) {
array_push($channel_array, $channel);
}
$derivedChannel = $channel_array;
}
if (!count($messages)) {
continue;
}
$receivedMessages = $this->decodeAndDecrypt($messages, $mode);
$returnArray = $this->NEW_STYLE_RESPONSE ? array($receivedMessages, $derivedChannel, $timetoken) : array($receivedMessages, $timetoken);
# Call once for each message for each channel
$exit_now = false;
for ($i = 0; $i < sizeof($receivedMessages); $i++) {
$cbReturn = $callback(array("message" => $returnArray[0][$i], "channel" => $returnArray[1][$i], "timetoken" => $returnArray[2]));
if ($cbReturn == false) {
$exit_now = true;
}
}
if ($exit_now) {
return;
}
} catch (Exception $error) {
$this->handleError($error, $args);
$timetoken = $this->throwAndResetTimetoken($callback, "Unknown error.");
continue;
}
}
}
public function throwAndResetTimetoken($callback, $errorMessage)
{
$callback(array(0, $errorMessage));
$timetoken = "0";
return $timetoken;
}
public function decodeAndDecrypt($messages, $mode = "default")
{
$receivedMessages = array();
if ($mode == "presence") {
return $messages;
} elseif ($mode == "default") {
$messageArray = $messages;
$receivedMessages = $this->decodeDecryptLoop($messageArray);
} elseif ($mode == "detailedHistory") {
$decodedMessages = $this->decodeDecryptLoop($messages);
$receivedMessages = array($decodedMessages[0], $messages[1], $messages[2] );
}
return $receivedMessages;
}
public function decodeDecryptLoop($messageArray)
{
$receivedMessages = array();
foreach ($messageArray as $message) {
if ($this->CIPHER_KEY) {
$decryptedMessage = decrypt($message, $this->CIPHER_KEY);
$message = json_decode($decryptedMessage, true);
}
array_push($receivedMessages, $message);
}
return $receivedMessages;
}
public function handleError($error, $args)
{
$errorMsg = 'Error on line ' . $error->getLine() . ' in ' . $error->getFile() . $error->getMessage();
trigger_error($errorMsg, E_COMPILE_WARNING);
sleep(1);
}
/**
* Presence
*
* This is BLOCKING.
* Listen for a message on a channel.
*
* @param array $args with channel and message.
* @return mixed false on fail, array on success.
*/
function presence($args)
{
## Capture User Input
$args['channel'] = ($args['channel'] . "-pnpres");
$this->subscribe($args, true);
}
/**
* Detailed History
*
* Load history from a channel.
*
* @param array $args with 'channel' and 'limit'.
* @return mixed false on fail, array on success.
*/
function detailedHistory($args)
{
## Capture User Input
## Fail if bad input.
if (!$args['channel']) {
echo('Missing Channel');
return false;
}
$channel = $args['channel'];
$urlParams = "";
if ($args['count'] || $args['start'] || $args['end'] || $args['reverse']) {
$urlParamSep = "?";
if (isset($args['count'])) {
$urlParams .= $urlParamSep . "count=" . $args['count'];
$urlParamSep = "&";
}
if (isset($args['start'])) {
$urlParams .= $urlParamSep . "start=" . $args['start'];
$urlParamSep = "&";
}
if (isset($args['end'])) {
$urlParams .= $urlParamSep . "end=" . $args['end'];
$urlParamSep = "&";
}
if (isset($args['reverse'])) {
$urlParams .= $urlParamSep . "reverse=" . $args['reverse'];
}
}
$response = $this->_request(array(
'v2',
'history',
"sub-key",
$this->SUBSCRIBE_KEY,
"channel",
$channel
), $urlParams);
;
$receivedMessages = $this->decodeAndDecrypt($response, "detailedHistory");
return $receivedMessages;
}
/**
* History
*
* Load history from a channel.
*
* @param array $args with 'channel' and 'limit'.
* @return mixed false on fail, array on success.
*/
function history($args)
{
## Capture User Input
$limit = +$args['limit'] ? +$args['limit'] : 10;
$channel = $args['channel'];
## Fail if bad input.
if (!$channel) {
echo('Missing Channel');
return false;
}
## Get History
$response = $this->_request(array(
'history',
$this->SUBSCRIBE_KEY,
$channel,
'0',
$limit
));
;
$receivedMessages = $this->decodeAndDecrypt($response);
return $receivedMessages;
}
/**
* Time
*
* Timestamp from PubNub Cloud.
*
* @return int timestamp.
*/
function time()
{
## Get History
$response = $this->_request(array(
'time',
'0'
));
return $response[0];
}
/**
* UUID
*
* UUID generator
*
* @return UUID
*/
function uuid()
{
if (function_exists('com_create_guid') === true) {
return trim(com_create_guid(), '{}');
}
return sprintf('%04X%04X-%04X-%04X-%04X-%04X%04X%04X', mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(16384, 20479), mt_rand(32768, 49151), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535));
}
/**
* Request URL
*
* @param array $request of url directories.
* @return array from JSON response.
*/
private function _request($request, $urlParams = false)
{
$request = array_map('Pubnub::_encode', $request);
array_unshift($request, $this->ORIGIN);
$qs_not_and = false;;
$urlString = implode('/', $request);
if ($urlParams) {
$urlString .= $urlParams;
}
if (($request[1] === 'presence') || ($request[1] === 'subscribe')) {
$urlString .= '?uuid=' . $this->SESSION_UUID;
$qs_not_and = true;
}
if ($this->AUTH_TOKEN) {
$urlString .= ($qs_not_and ? '&' : '?') . 'auth=' . $this->AUTH_TOKEN;
}
$ch = curl_init();
$pubnubHeaders = array("V: 3.4", "Accept: */*"); // GZIP Support
curl_setopt($ch, CURLOPT_HTTPHEADER, $pubnubHeaders);
curl_setopt($ch, CURLOPT_USERAGENT, "PHP");
//curl_setopt($ch, CURLOPT_PIPELINING, 1); // optimal TCP packet usage.
//curl_setopt($ch, CURLOPT_MAXCONNECTS, 100); // concurrent sockets pipes.
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 310);
if($this->PROXY) {
curl_setopt($ch, CURLOPT_PROXY, $this->PROXY);
}
curl_setopt($ch, CURLOPT_URL, $urlString);
if ($this->SSL) {
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
$pemPathAndFilename = $this->PEM_PATH . "/pubnub.com.pem";
if (file_exists($pemPathAndFilename))
curl_setopt($ch, CURLOPT_CAINFO, $pemPathAndFilename);
else {
trigger_error("Can't find PEM file. Please set pem_path in initializer.");
exit;
}
}
$output = curl_exec($ch);
$curlError = curl_errno($ch);
$curlResponseCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$JSONdecodedResponse = json_decode($output, true);
if ($JSONdecodedResponse != null)
return $JSONdecodedResponse;
elseif ($curlError == 28)
return "_PUBNUB_TIMEOUT";
elseif ($curlResponseCode == 400 || $curlResponseCode == 404)
return "_PUBNUB_MESSAGE_TOO_LARGE";
}
private function setProxy($proxy) {
$this->PROXY = $proxy;
}
/**
* Encode
*
* @param string $part of url directories.
* @return string encoded string.
*/
private static function _encode($part)
{
$pieces = array_map('Pubnub::_encode_char', str_split($part));
return implode('', $pieces);
}
/**
* Encode Char
*
* @param string $char val.
* @return string encoded char.
*/
private static function _encode_char($char)
{
if (strpos(' ~`!@#$%^&*()+=[]\\{}|;\':",./<>?', $char) === false)
return $char;
else
return rawurlencode($char);
}
}
?>
<?php
#$plaintext = "this is my plaintext.";
$cipher_key = "enigma";
#printf("\ncipher key is %s\n", $cipher_key);
#$cipher_text = "q/xJqqN6qbiZMXYmiQC1Fw==";
#$decrypt = "RVOElAJIHskATgCCP+KlaQ==";
#$key = "67a4f45f0d1d9bc606486fc42dc49416";
#$iv = "0123456789012345";
## Manual Run
#$cipher_text = encrypt("hellohellohello!", $cipher_key, $iv);
#$p_text = decrypt($cipher_text, $cipher_key, $iv);
##
function decrypt($cipher_text, $cipher_key) {
$iv = "0123456789012345";
if (gettype($cipher_text) != "string")
return "DECRYPTION_ERROR";
$decoded = base64_decode($cipher_text);
$sha_cipher_key = hash("sha256", $cipher_key);
$padded_cipher_key = substr($sha_cipher_key, 0, 32);
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
mcrypt_generic_init($td, $padded_cipher_key, $iv);
$decrypted = mdecrypt_generic($td, $decoded); // TODO: handle non-encrypted unicode corner-case
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
$unpadded = unpadPKCS7($decrypted, 16);
#printf("\ndecoded: %s", $unpadded);
return $unpadded;
}
function encrypt($plain_text, $cipher_key) {
$iv = "0123456789012345";
$sha_cipher_key = hash("sha256", $cipher_key);
$padded_cipher_key = substr($sha_cipher_key, 0, 32);
$padded_plain_text = pkcs5_pad($plain_text, 16);
// printf("sha256 key is %s\n", $sha_cipher_key);
// printf("padded cipher key is %s\n\n", $padded_cipher_key);
// printf("padded plain_text is %s\n\n", $padded_plain_text);
# This is the way to do AES-256 using mcrypt PHP - its not AES-128 or anything other than that!
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
mcrypt_generic_init($td, $padded_cipher_key, $iv);
$encrypted = mcrypt_generic($td, $padded_plain_text);
$encode = base64_encode($encrypted);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
#printf("\nencoded: %s", $encode);
return $encode;
}
function pkcs5_pad ($text, $blocksize)
{
$pad = $blocksize - (strlen($text) % $blocksize);
return $text . str_repeat(chr($pad), $pad);
}
function unpadPKCS7($data, $blockSize)
{
$length = strlen($data);
if ($length > 0) {
$first = substr($data, -1);
if (ord($first) <= $blockSize) {
for ($i = $length - 2; $i > 0; $i--)
if (ord($data [$i] != $first))
break;
return substr($data, 0, $i+1);
}
}
return $data;
}
function isBlank($word)
{
if (($word == null) || ($word == false))
return true;
else
return false;
}
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment