iPhoneにpushするApple Push Notification Serviceなコードかいてみた
とりあえず動くこと最優先。実務に使うにはやばい出来です。Moose使ってNet::APNSなモジュールに書き直してる最中。
phpのサンプルが文字コードに触れてなかったので、その部分手を抜いたら大やけど。pushで送られるpayloadはunicodeのようです。まぁ、当たり前なんだろうけどな。
JSON::XSでutf8のフラグを操作しないとiPhone側にalertが出ないのを確認済み。
#!/usr/local/bin/perl use warnings; use strict; use Encode qw/encode decode/; use Socket; use Net::SSLeay qw/die_now die_if_ssl_error/; use JSON::XS; use YAML::Syck; #YAML $YAML::Syck::ImplicitTyping = 1; $YAML::Syck::SingleQuote = 1; #Net::SSLeay initialize $Net::SSLeay::trace = 4; $Net::SSLeay::ssl_version = 10; Net::SSLeay::load_error_strings(); Net::SSLeay::SSLeay_add_ssl_algorithms(); Net::SSLeay::randomize(); use constant ssl_error_want_read => 2; use constant ssl_error_want_write => 3; use constant ssl_error_want_connect => 4; use constant ssl_error_want_accept => 5; my $app_config = LoadFile("APNS_CONF.yaml"); my $message = $ARGV[0] || die "Need message"; $message = decode('utf8', $message); $message = encode('unicode', $message); my $badge = $ARGV[1] || die "Need numbers"; $badge =~ s/[^0-9]//g; if($badge eq "") { die "Badge must set numbers."; } $app_config->{device_token} =~ s/\s//g; if( $app_config->{device_token} eq "" ) { die "device_token nothing!"; } #Apple port initialize my $apple_port = 2195; #my $apple_port = 2196; my $apple_host = 'gateway.sandbox.push.apple.com'; #my $apple_host = 'feedback.push.apple.com'; my $packed_apple_host = inet_aton($apple_host) or die "Cannot pack $apple_host: $!"; print "Packed $apple_host\n"; my $apple_serv_params = sockaddr_in($apple_port, $packed_apple_host) or die "Cannot pack $apple_host:$apple_port: $!"; print "Packed $apple_host:$apple_port\n"; my $payload = JSON::XS->new->utf8(1)->encode({ aps => { alert => $message, badge => $badge, }, }); $payload =~ s/("badge":)"([^"]+)"/$1$2/; #Connect to apple my $sock; socket ($sock, PF_INET, SOCK_STREAM, getprotobyname('tcp')) or die "socket: $!"; connect ($sock, $apple_serv_params) or die "connect: $!"; my $ctx = Net::SSLeay::CTX_new() or die_now("Failed to create SSL_CTX $!."); print "Create SSL_CTX\n"; Net::SSLeay::CTX_set_options($ctx, &Net::SSLeay::OP_ALL) and die_if_ssl_error("ssl ctx set options"); print "Set options to ssl ctx\n"; Net::SSLeay::CTX_set_default_passwd_cb($ctx, sub { $app_config->{passwd} }); Net::SSLeay::CTX_use_RSAPrivateKey_file($ctx, $app_config->{key}, &Net::SSLeay::FILETYPE_PEM); Net::SSLeay::die_if_ssl_error("private key"); print "Set RSA private key\n"; Net::SSLeay::CTX_use_certificate_file ($ctx, $app_config->{cert}, &Net::SSLeay::FILETYPE_PEM); Net::SSLeay::die_if_ssl_error("certificate"); my $ssl = Net::SSLeay::new($ctx) or die_now("Failed to create SSL $!."); print "Created SSL\n"; Net::SSLeay::set_fd($ssl, fileno($sock)) or die_now("Failed to set SSL Toolkit $!"); Net::SSLeay::connect($ssl) or die_now("Failed to connect SSL $!"); my $res = Net::SSLeay::write($ssl, chr(0).pack('n',32).pack('H*',$app_config->{device_token}).pack('n',length($payload)).$payload ) or die_if_ssl_error("ssl write"); if($res > 0) { print "Written $payload Size:$res\n"; }else{ print "Failed to write $!\n"; } #my $got = Net::SSLeay::read($ssl) or die_if_ssl_error("ssl read"); #print $got; CORE::shutdown($sock, 1); Net::SSLeay::free ($ssl); Net::SSLeay::CTX_free ($ctx); close($sock);