diff --git a/common/framework/drivers/push/apns.php b/common/framework/drivers/push/apns.php index 9d2ea0914..5257c86e3 100644 --- a/common/framework/drivers/push/apns.php +++ b/common/framework/drivers/push/apns.php @@ -42,11 +42,14 @@ class APNs extends Base implements \Rhymix\Framework\Drivers\PushInterface * * @param object $message * @param array $tokens - * @return bool + * @return object */ - public function send(\Rhymix\Framework\Push $message, array $tokens): bool + public function send(\Rhymix\Framework\Push $message, array $tokens) { - $status = true; + $output = new \stdClass; + $output->success = []; + $output->invalid = []; + $output->needUpdate = []; // Set parameters $local_cert = $this->_config['certificate']; @@ -68,18 +71,17 @@ class APNs extends Base implements \Rhymix\Framework\Drivers\PushInterface if(!$fp) { $message->addError('Failed to connect socket - error code: '. $err .' - '. $errstr); - $status = false; } $msg = chr(0) . pack('n', 32) . pack('H*', $token) . pack('n', strlen($payload)) . $payload; $result = fwrite($fp, $msg, strlen($msg)); if(!$result) { $message->addError('APNs return empty response.'); - $status = false; } + $output->success[] = $token; fclose($fp); } - return $status; + return $output; } } diff --git a/common/framework/drivers/push/base.php b/common/framework/drivers/push/base.php index 67718e834..23c2cb403 100644 --- a/common/framework/drivers/push/base.php +++ b/common/framework/drivers/push/base.php @@ -2,6 +2,8 @@ namespace Rhymix\Framework\Drivers\Push; +use stdClass; + /** * The base class for other Push drivers. */ @@ -86,10 +88,10 @@ abstract class Base implements \Rhymix\Framework\Drivers\PushInterface * * @param object $message * @param array $tokens - * @return bool + * @return object */ - public function send(\Rhymix\Framework\Push $message, array $tokens): bool + public function send(\Rhymix\Framework\Push $message, array $tokens) { - return false; + return new \stdClass; } } diff --git a/common/framework/drivers/push/fcm.php b/common/framework/drivers/push/fcm.php index e086ee977..67affb7c3 100644 --- a/common/framework/drivers/push/fcm.php +++ b/common/framework/drivers/push/fcm.php @@ -2,9 +2,6 @@ namespace Rhymix\Framework\Drivers\Push; -use message; -use stdClass; - /** * The FCM (Google) Push driver. */ @@ -45,11 +42,14 @@ class FCM extends Base implements \Rhymix\Framework\Drivers\PushInterface * * @param object $message * @param array $tokens - * @return bool + * @return object */ - public function send(\Rhymix\Framework\Push $message, array $tokens): bool + public function send(\Rhymix\Framework\Push $message, array $tokens) { - $status = true; + $output = new \stdClass; + $output->success = []; + $output->invalid = []; + $output->needUpdate = []; $url = 'https://fcm.googleapis.com/fcm/send'; $api_key = $this->_config['api_key']; @@ -64,31 +64,48 @@ class FCM extends Base implements \Rhymix\Framework\Drivers\PushInterface $notification['body'] = $message->getContent(); $notification['click_action'] = $message->getClickAction(); - foreach($tokens as $i => $token) + $chunked_token = array_chunk($tokens, 1000); + foreach($chunked_token as $token_unit) { $data = json_encode(array( - 'registration_ids' => [$token], + 'registration_ids' => $token_unit, 'notification' => $notification, 'priority' => 'normal', - 'data' => $message->getData() ?: new stdClass, + 'data' => $message->getData() ?: new \stdClass, )); - $result = \FileHandler::getRemoteResource($url, $data, 5, 'POST', 'application/json', $headers); - if($result) + $response = \FileHandler::getRemoteResource($url, $data, 5, 'POST', 'application/json', $headers); + if($response) { - $error = json_decode($result)->error_code; - if($error) + $decoded_response = json_decode($response); + if(!$decoded_response) { - $message->addError('FCM error code: '. $error); - $status = false; + $message->addError('FCM return invalid json : '. $response); + return $output; + } + $results = $decoded_response->results ?: []; + foreach($results as $i => $result) + { + if($result->error) + { + $message->addError('FCM error code: '. $result->error); + $output->invalid[$token_unit[$i]] = $token_unit[$i]; + } + else if($result->message_id && $result->registration_id) + { + $output->needUpdate[$token_unit[$i]] = $result->registration_id; + } + else + { + $output->success[$token_unit[$i]] = $result->message_id; + } } } else { $message->addError('FCM return empty response.'); - $status = false; } } - return $status; + return $output; } } diff --git a/common/framework/drivers/pushinterface.php b/common/framework/drivers/pushinterface.php index d9bd78ecb..e15158536 100644 --- a/common/framework/drivers/pushinterface.php +++ b/common/framework/drivers/pushinterface.php @@ -52,7 +52,7 @@ interface PushInterface * * @param object $message * @param array $tokens - * @return bool + * @return object */ - public function send(\Rhymix\Framework\Push $message, array $tokens): bool; + public function send(\Rhymix\Framework\Push $message, array $tokens); } diff --git a/common/framework/push.php b/common/framework/push.php index 9e86eedbb..b7834e0b2 100644 --- a/common/framework/push.php +++ b/common/framework/push.php @@ -2,9 +2,6 @@ namespace Rhymix\Framework; -use BaseObject; -use stdClass; - /** * The Push class. */ @@ -16,12 +13,12 @@ class Push protected $from = 0; protected $to = array(); protected $subject = ''; - protected $content = ''; + protected $content = ''; protected $click_action = ''; protected $data = []; protected $errors = array(); protected $sent = false; - + /** * Static properties. */ @@ -286,19 +283,25 @@ class Push try { - $tokens = $this->getDeviceTokens(); + $tokens = $this->_getDeviceTokens(); // Android FCM - if(count($tokens['android'])) + if(count($tokens->android)) { $fcm_driver = $this->getDriver('fcm'); - $this->sent = $fcm_driver->send($this, $tokens['android']); + $output = $fcm_driver->send($this, $tokens->android); + $this->sent = $output->invalid ? false : true; + $this->_deleteInvalidTokens($output->invalid); + $this->_updateDeviceTokens($output->needUpdate); } // iOS APNs - if(count($tokens['ios'])) + if(count($tokens->ios)) { $apns_driver =$this->getDriver('apns'); - $this->sent = $apns_driver->send($this, $tokens['ios']); + $output = $apns_driver->send($this, $tokens->ios); + $this->sent = $output->invalid ? false : true; + $this->_deleteInvalidTokens($output->invalid); + $this->_updateDeviceTokens($output->needUpdate); } } catch(\Exception $e) @@ -319,21 +322,21 @@ class Push /** * Get the device token * - * @return array + * @return object * */ - protected function getDeviceTokens(): array + protected function _getDeviceTokens() { $member_srl_list = $this->getRecipients(); - $result = []; - $result['android'] = []; - $result['ios'] = []; + $result = new \stdClass; + $result->android = []; + $result->ios = []; - $args = new stdClass; + $args = new \stdClass; $args->member_srl = $member_srl_list; $args->device_type = []; $driver_types = config('push.types') ?: array(); - if(count($driver_types)) + if(!count($driver_types)) { return $result; } @@ -354,12 +357,44 @@ class Push foreach($output->data as $row) { - $result[$row->device_type][] = $row->device_token; + $result->{$row->device_type}[] = $row->device_token; } return $result; } + + /** + * Delete the device toekn + * + * @param array + */ + protected function _deleteInvalidTokens(array $invalid_tokens): void + { + if(!count($invalid_tokens)) + { + return; + } + $args = new \stdClass; + $args->device_token = $invalid_tokens; + executeQueryArray('member.deleteMemberDevice', $args); + } + + /** + * Update the device toekn + * + * @param array + */ + protected function _updateDeviceTokens(array $update_tokens): void + { + $args = new \stdClass; + foreach($update_tokens as $key => $value) + { + $args->old_token = $key; + $args->new_token = $value; + executeQueryArray('member.updateMemberDevice', $args); + } + } /** * Check if the message was sent. diff --git a/modules/member/member.controller.php b/modules/member/member.controller.php index 20654ba37..926b2c80d 100644 --- a/modules/member/member.controller.php +++ b/modules/member/member.controller.php @@ -100,10 +100,6 @@ class memberController extends member $browserInfo = Rhymix\Framework\UA::getBrowserInfo(); $device_type = strtolower($browserInfo->os); - if('android' !== $device_type && 'ios' !== $device_type) - { - return new BaseObject(-1, 'NOT_SUPPORTED_OS'); - } if('ios' === $device_type) { @@ -134,7 +130,7 @@ class memberController extends member $logged_info = Context::get('logged_info'); $random_key = Rhymix\Framework\Security::getRandom(); - $device_key = hash_hmac('sha256', $random_key, $device_token); + $device_key = hash_hmac('sha256', $random_key, $logged_info->member_srl . ':' . config('crypto.authentication_key')); // Start transaction $oDB = DB::getInstance(); @@ -179,8 +175,8 @@ class memberController extends member Context::setResponseMethod('JSON'); // Check member_srl, device_token, device_key $member_srl = Context::get('member_srl'); - $device_token = escape(Context::get('device_token')); - $random_key = escape(Context::get('device_key')); + $device_token = Context::get('device_token'); + $random_key = Context::get('device_key'); // Return an error when id, password and device_key doesn't exist if(!$member_srl) return new BaseObject(-1, 'NULL_MEMBER_SRL'); @@ -190,7 +186,7 @@ class memberController extends member $args = new stdClass; $args->member_srl = $member_srl; $args->device_token = $device_token; - $args->device_key = hash_hmac('sha256', $random_key, $device_token); + $args->device_key = hash_hmac('sha256', $random_key, $member_srl . ':' . config('crypto.authentication_key')); $output = executeQueryArray('member.getMemberDevice', $args); if(!$output->toBool()) { diff --git a/modules/member/queries/deleteMemberDevice.xml b/modules/member/queries/deleteMemberDevice.xml new file mode 100644 index 000000000..1320b0df1 --- /dev/null +++ b/modules/member/queries/deleteMemberDevice.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/modules/member/queries/updateMemberDevice.xml b/modules/member/queries/updateMemberDevice.xml new file mode 100644 index 000000000..60847a22e --- /dev/null +++ b/modules/member/queries/updateMemberDevice.xml @@ -0,0 +1,11 @@ + + +
+ + + + + + + + diff --git a/modules/ncenterlite/ncenterlite.controller.php b/modules/ncenterlite/ncenterlite.controller.php index b653ebce0..324930590 100644 --- a/modules/ncenterlite/ncenterlite.controller.php +++ b/modules/ncenterlite/ncenterlite.controller.php @@ -1514,7 +1514,6 @@ class ncenterliteController extends ncenterlite $oPush = new \Rhymix\Framework\Push(); $oPush->setSubject($content); $oPush->setContent($args->extra_content); - $oPush->setImage($args->extra_image); $oPush->setData($args->extra_data); $oPush->setURL($target_url); $oPush->addTo($args->member_srl);