package Birdy::PushNotification::APNS; use 5.018; use Mojo::Base -base; no if $] >= 5.018, warnings => "experimental"; use Socket; use Net::SSLeay qw/die_now die_if_ssl_error/; use JSON::XS; use Encode qw(encode); BEGIN { $Net::SSLeay::trace = 4; $Net::SSLeay::ssl_version = 10; Net::SSLeay::load_error_strings(); Net::SSLeay::SSLeay_add_ssl_algorithms(); Net::SSLeay::randomize(); } sub new { my ($class, $sandbox) = @_; my $port = 2195; my $address = $sandbox ? 'gateway.sandbox.push.apple.com' : 'gateway.push.apple.com'; my $apns_key = "$ENV{MOJO_HOME}/apns_key.pem"; my $apns_cert = "$ENV{MOJO_HOME}/apns_cert.pem"; my $socket; socket( $socket, PF_INET, SOCK_STREAM, getprotobyname('tcp') ) or die "socket: $!"; connect( $socket, sockaddr_in( $port, inet_aton($address) ) ) or die "Connect: $!"; my $ctx = Net::SSLeay::CTX_new() or die_now("Failed to create SSL_CTX $!."); Net::SSLeay::CTX_set_options($ctx, &Net::SSLeay::OP_ALL); die_if_ssl_error("ssl ctx set options"); Net::SSLeay::CTX_use_RSAPrivateKey_file($ctx, $apns_key, &Net::SSLeay::FILETYPE_PEM); die_if_ssl_error("private key"); Net::SSLeay::CTX_use_certificate_file($ctx, $apns_cert, &Net::SSLeay::FILETYPE_PEM); die_if_ssl_error("certificate"); my $ssl = Net::SSLeay::new($ctx); Net::SSLeay::set_fd($ssl, fileno($socket)); Net::SSLeay::connect($ssl) or die_now("Failed SSL connect ($!)"); my $self = bless { 'ssl' => $ssl, 'ctx' => $ctx, 'socket' => $socket, }, $class; return $self; } sub close { my ($self) = @_; my ($ssl, $ctx, $socket) = @{$self}{qw/ssl ctx socket/} CORE::shutdown($socket, 1); Net::SSLeay::free($ssl); Net::SSLeay::CTX_free($ctx); close($socket); } sub write { my ($self, $token, $alert, $data_id, $sound) = @_; Net::SSLeay::write( $self->{'ssl'}, $self->_pack_payload($token, $alert, $data_id, $sound) ); } sub _pack_payload { my ($self, $token, $alert, $data_id, $sound) = @_; my $data = { 'aps' => { 'alert' => encode('unicode', $alert), }, 'data_id' => $data_id, }; # $data->{'aps'}->{'sound'} = 'default' if $sound; my $xs = JSON::XS->new->utf8(1); my $payload = chr(0) . pack('n', 32) . pack('H*', $token); # if (!$self->{'_alert'}) { # , 256 my $json = $xs->encode($data); my $overload = length($payload) + length(pack 'n', length $json) + length($json) - 256; if ($overload > 0) { substr( $data->{'aps'}->{'alert'}, -$overload ) = ''; } # $self->{'_alert'} = $data->{'aps'}->{'alert'}; } my $json = $xs->encode($data); $payload .= pack('n', length $json) . $json; return $payload; }
sub write { my ($self, $token, $alert, $data_id, $sound, $push_id) = @_; Net::SSLeay::write( $self->{'ssl'}, $self->_pack_payload($token, $alert, $data_id, $sound, $push_id) ); } sub _pack_payload { my ($self, $token, $alert, $data_id, $sound, $push_id) = @_; my $data = { 'aps' => { 'alert' => encode('unicode', $alert), }, 'data_id' => $data_id, }; # $data->{'aps'}->{'sound'} = 'default' if $sound; my $xs = JSON::XS->new->utf8(1); my $payload = chr(1) . pack('N', $push_id) . pack('N', time + (3600 * 24) ) . pack('n', 32) . pack('H*', $token); # if (!$self->{'_alert'}) { # , 256 my $json = $xs->encode($data); my $overload = length($payload) + length(pack 'n', length $json) + length($json) - 256; if ($overload > 0) { substr( $data->{'aps'}->{'alert'}, -$overload ) = ''; } # $self->{'_alert'} = $data->{'aps'}->{'alert'}; } my $json = $xs->encode($data); $payload .= pack('n', length $json) . $json; return $payload; }
sub read_feedback { my ($self) = @_; my $result = []; my $bytes = Net::SSLeay::read( $self->{'ssl'} ); while ($bytes) { my ($ts, $token); ($ts, $token, $bytes) = unpack 'N n/aa*', $bytes; $token = unpack 'H*', $token; push @$result, { 'ts' => $ts, 'token' => $token, }; } return $result; }
Source: https://habr.com/ru/post/245087/
All Articles