apple-push 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +4 -0
- data/LICENSE +20 -0
- data/README.md +45 -0
- data/Rakefile +11 -0
- data/certs/entrust_ssl_ca.cer +28 -0
- data/lib/apple-push.rb +63 -0
- data/lib/apple-push/notification.rb +49 -0
- data/lib/apple-push/version.rb +3 -0
- data/test/apple_push_test.rb +28 -0
- data/test/cert.p12 +0 -0
- data/test/cert.pem +69 -0
- data/test/cert_secure.p12 +0 -0
- data/test/cert_secure.pem +69 -0
- data/test/notification_test.rb +45 -0
- data/test/test_helper.rb +6 -0
- metadata +93 -0
data/CHANGELOG
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010, 2011 Niklas Holmgren, Sutajio
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
ApplePush
|
2
|
+
=========
|
3
|
+
|
4
|
+
ApplePush is a very simple implementation of the APNS (Apple Push Notification
|
5
|
+
Service) protocol. It differs from the
|
6
|
+
[APNS gem](https://github.com/jpoz/APNS) by being able to push to different
|
7
|
+
iOS apps from the same Ruby application.
|
8
|
+
|
9
|
+
Setup
|
10
|
+
-----
|
11
|
+
|
12
|
+
$ gem install apple-push
|
13
|
+
|
14
|
+
require 'apple-push'
|
15
|
+
ApplePush.host = 'gateway.push.apple.com'
|
16
|
+
ApplePush.port = 2195
|
17
|
+
|
18
|
+
Creating a PEM certificate
|
19
|
+
--------------------------
|
20
|
+
|
21
|
+
In Keychain access export your certificate as a p12. Then run the following command to convert it to a .pem
|
22
|
+
|
23
|
+
openssl pkcs12 -in cert.p12 -out cert.pem -nodes -clcerts
|
24
|
+
|
25
|
+
The store the PEM data in your database or in a file.
|
26
|
+
|
27
|
+
Example usage
|
28
|
+
-------------
|
29
|
+
|
30
|
+
ApplePush.send_notification(pem, device_token, 'Hello, world!')
|
31
|
+
ApplePush.send_notification(pem, device_token, :alert => 'Hello, world!', :badge => 1, :sound => 'default')
|
32
|
+
ApplePush.send_notification(pem, device_token, :alert => 'Hello, world!', :badge => 1, :sound => 'default', :other => { :foo => 'bar' })
|
33
|
+
|
34
|
+
The `pem` parameter can either be the path to a .pem file or the PEM data it
|
35
|
+
self, retrieved from a database for example. If the PEM certificate is
|
36
|
+
encrypted with a password you should pass an array with the pem and the
|
37
|
+
password instead of just the pem.
|
38
|
+
|
39
|
+
The `device_token` can either be binary data, or Base64 encoded.
|
40
|
+
|
41
|
+
Author
|
42
|
+
------
|
43
|
+
|
44
|
+
Created by Niklas Holmgren (niklas@sutajio.se) and released under
|
45
|
+
the MIT license.
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC
|
3
|
+
VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u
|
4
|
+
ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc
|
5
|
+
KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u
|
6
|
+
ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTA1
|
7
|
+
MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIGA1UE
|
8
|
+
ChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5j
|
9
|
+
b3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF
|
10
|
+
bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUg
|
11
|
+
U2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUA
|
12
|
+
A4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/
|
13
|
+
I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3
|
14
|
+
wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OC
|
15
|
+
AdcwggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHb
|
16
|
+
oIHYpIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5
|
17
|
+
BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1p
|
18
|
+
dHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVk
|
19
|
+
MTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp
|
20
|
+
b24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu
|
21
|
+
dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0
|
22
|
+
MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8Bdi
|
23
|
+
E1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAa
|
24
|
+
MAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI
|
25
|
+
hvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN
|
26
|
+
95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd
|
27
|
+
2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI=
|
28
|
+
-----END CERTIFICATE-----
|
data/lib/apple-push.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'openssl'
|
3
|
+
require 'socket'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
require 'apple-push/version'
|
7
|
+
require 'apple-push/notification'
|
8
|
+
|
9
|
+
module ApplePush
|
10
|
+
|
11
|
+
@host = 'gateway.sandbox.push.apple.com'
|
12
|
+
@port = 2195
|
13
|
+
|
14
|
+
class<<self
|
15
|
+
attr_accessor :host
|
16
|
+
attr_accessor :port
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.send_notification(apns_pem, apns_token, message = {})
|
20
|
+
connection(apns_pem) do |ssl|
|
21
|
+
notification = ApplePush::Notification.new(apns_token, message)
|
22
|
+
puts "#{Time.now} [#{host}:#{port}] Device: #{notification.device_token.unpack('H*')} sending #{message.inspect}"
|
23
|
+
ssl.write(notification.to_s)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
|
29
|
+
def self.connection(apns_pem, &block)
|
30
|
+
sock = TCPSocket.new(host.to_s, port.to_i)
|
31
|
+
ssl = OpenSSL::SSL::SSLSocket.new(sock, security_context(apns_pem))
|
32
|
+
ssl.connect
|
33
|
+
|
34
|
+
yield(ssl)
|
35
|
+
|
36
|
+
ssl.close
|
37
|
+
sock.close
|
38
|
+
|
39
|
+
return true
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.security_context(apns_pem)
|
43
|
+
pem, password = self.pem_and_password(apns_pem)
|
44
|
+
context = OpenSSL::SSL::SSLContext.new
|
45
|
+
context.cert = OpenSSL::X509::Certificate.new(pem)
|
46
|
+
context.key = OpenSSL::PKey::RSA.new(pem, password)
|
47
|
+
context.ca_file = File.expand_path(File.join(File.dirname(__FILE__),'../certs/entrust_ssl_ca.cer'))
|
48
|
+
context
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.pem_and_password(apns_pem)
|
52
|
+
if apns_pem.is_a?(Array)
|
53
|
+
return self.read_pem(apns_pem.first), apns_pem.last
|
54
|
+
else
|
55
|
+
return self.read_pem(apns_pem), nil
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.read_pem(file_or_string)
|
60
|
+
File.exist?(file_or_string) ? File.read(file_or_string) : file_or_string
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module ApplePush
|
2
|
+
class Notification
|
3
|
+
attr_accessor :device_token, :alert, :badge, :sound, :other
|
4
|
+
|
5
|
+
def initialize(device_token, message)
|
6
|
+
if device_token
|
7
|
+
self.device_token = device_token
|
8
|
+
else
|
9
|
+
raise "Invalid device token"
|
10
|
+
end
|
11
|
+
if message.is_a?(Hash)
|
12
|
+
self.alert = message[:alert]
|
13
|
+
self.badge = message[:badge]
|
14
|
+
self.sound = message[:sound]
|
15
|
+
self.other = message[:other]
|
16
|
+
elsif message.is_a?(String)
|
17
|
+
self.alert = message
|
18
|
+
else
|
19
|
+
raise "Notification message needs to be either a hash or a string"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def device_token
|
24
|
+
if @device_token =~ /=$/
|
25
|
+
Base64.decode64(@device_token)
|
26
|
+
else
|
27
|
+
@device_token
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_s
|
32
|
+
dt = self.device_token
|
33
|
+
pl = self.payload
|
34
|
+
[0, 0, dt.size, dt, 0, pl.size, pl].pack("ccca*cca*")
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
|
39
|
+
def payload
|
40
|
+
aps = {'aps'=> {} }
|
41
|
+
aps['aps']['alert'] = self.alert if self.alert
|
42
|
+
aps['aps']['badge'] = self.badge if self.badge
|
43
|
+
aps['aps']['sound'] = self.sound if self.sound
|
44
|
+
aps.merge!(self.other) if self.other
|
45
|
+
aps.to_json
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.expand_path('test/test_helper')
|
2
|
+
|
3
|
+
class ApplePushTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def test_can_push_notification
|
6
|
+
pem = File.read('test/cert.pem')
|
7
|
+
device_token = 'gM/RJodgEmAI////WGWMs/QjbFq05pyQ8hCVsTTDeiI='
|
8
|
+
assert ApplePush.send_notification(pem, device_token, 'Hello, world!')
|
9
|
+
assert ApplePush.send_notification(pem, device_token, :alert => 'Hello, world!', :badge => 1)
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_can_create_security_context_from_file
|
13
|
+
assert ApplePush.security_context('test/cert.pem')
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_can_create_security_context_from_string
|
17
|
+
assert ApplePush.security_context(File.read('test/cert.pem'))
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_can_create_security_context_from_file_with_password
|
21
|
+
assert ApplePush.security_context(['test/cert_secure.pem', '123456'])
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_can_create_security_context_from_string_with_password
|
25
|
+
assert ApplePush.security_context([File.read('test/cert_secure.pem'), '123456'])
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
data/test/cert.p12
ADDED
Binary file
|
data/test/cert.pem
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
Bag Attributes
|
2
|
+
friendlyName: Apple Development IOS Push Services: 9YGP3C6FRF:9YGP3C6FRF
|
3
|
+
localKeyID: 72 FA 95 A0 3B AD A0 B7 B8 FF 25 FF 6E 15 4E 06 7C 57 C3 A6
|
4
|
+
subject=/UID=com.sutajio.Friend/CN=Apple Development IOS Push Services: 9YGP3C6FRF:9YGP3C6FRF/C=SE
|
5
|
+
issuer=/C=US/O=Apple Inc./OU=Apple Worldwide Developer Relations/CN=Apple Worldwide Developer Relations Certification Authority
|
6
|
+
-----BEGIN CERTIFICATE-----
|
7
|
+
MIIFdjCCBF6gAwIBAgIIYdy07VBp8fIwDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNV
|
8
|
+
BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js
|
9
|
+
ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3
|
10
|
+
aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
|
11
|
+
HhcNMTEwODE0MTg1NjMwWhcNMTExMTEyMTg1NjMwWjB2MSIwIAYKCZImiZPyLGQB
|
12
|
+
AQwSY29tLnN1dGFqaW8uRnJpZW5kMUMwQQYDVQQDDDpBcHBsZSBEZXZlbG9wbWVu
|
13
|
+
dCBJT1MgUHVzaCBTZXJ2aWNlczogOVlHUDNDNkZSRjo5WUdQM0M2RlJGMQswCQYD
|
14
|
+
VQQGEwJTRTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKB9nU1aCeC4
|
15
|
+
SJAX2/WC6bl2VrINAaOmHJt15Z47cbeppSJ9sirnZYP420fUlDCT1LQMR5hw0yT2
|
16
|
+
MCRH6cC76jFKMSlt80eQ5ahiVwDACmIc3CuGafcfiNh/F3oJ6Z7esUqU8y2wIQGn
|
17
|
+
W4BfSMVlxtUEnW5z3r9GugsFOovBB39GckgvQO9GOrZRuIVQc+8chWvqBxBrKpds
|
18
|
+
//vqwAYvTsnIbpSBh80G9Z9z/Ni6KFldaa+uz3ylG86Vh/r4QmCzlHHivC8H1Qmk
|
19
|
+
3OygKx0M9sUVUsu2OuwngCP2S9cN6JcMSiIlp3clk1jOK4aUh4hwc5TOoQAlZXey
|
20
|
+
5/vQ/9I6wxkCAwEAAaOCAeUwggHhMB0GA1UdDgQWBBRy+pWgO62gt7j/Jf9uFU4G
|
21
|
+
fFfDpjAJBgNVHRMEAjAAMB8GA1UdIwQYMBaAFIgnFwmpthhgi+zruvZHWcVSVKO3
|
22
|
+
MIIBDwYDVR0gBIIBBjCCAQIwgf8GCSqGSIb3Y2QFATCB8TCBwwYIKwYBBQUHAgIw
|
23
|
+
gbYMgbNSZWxpYW5jZSBvbiB0aGlzIGNlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBh
|
24
|
+
c3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFy
|
25
|
+
ZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRlIHBvbGlj
|
26
|
+
eSBhbmQgY2VydGlmaWNhdGlvbiBwcmFjdGljZSBzdGF0ZW1lbnRzLjApBggrBgEF
|
27
|
+
BQcCARYdaHR0cDovL3d3dy5hcHBsZS5jb20vYXBwbGVjYS8wTQYDVR0fBEYwRDBC
|
28
|
+
oECgPoY8aHR0cDovL2RldmVsb3Blci5hcHBsZS5jb20vY2VydGlmaWNhdGlvbmF1
|
29
|
+
dGhvcml0eS93d2RyY2EuY3JsMAsGA1UdDwQEAwIHgDATBgNVHSUEDDAKBggrBgEF
|
30
|
+
BQcDAjAQBgoqhkiG92NkBgMBBAIFADANBgkqhkiG9w0BAQUFAAOCAQEAsPFrmajA
|
31
|
+
pd4c9h63Z4j0BjS9aFdlTvuP1rrZUwQG2vCGfbg9BAv1YQfPGmHqOFDuCbr2Br9y
|
32
|
+
K6apMwgzfhO2C94H0uw+CmppUwo4h2zyweZC2BNT13a28Lg8QRnqDG4JdByTCyvp
|
33
|
+
Sa4pLwSG4dhZqp6tKB5e9k80x/73oXLQOLWIfhgg0jxxeLP/ZJQbjrO4re8PNOxi
|
34
|
+
pFohKnjVB6cxc/GWTkWUkjaZhRzMAcPGCKm7zDzsoHF7si55sc4l5QPLFxMT/BF5
|
35
|
+
Ble5Yv70jcJ9uBiQBu6gVO2+SC/8EouCORcKEsE90MNFQWuH0zafiwdw57Eq1SG5
|
36
|
+
RyzYEuwYfe5xAA==
|
37
|
+
-----END CERTIFICATE-----
|
38
|
+
Bag Attributes
|
39
|
+
friendlyName: Niklas Holmgren
|
40
|
+
localKeyID: 72 FA 95 A0 3B AD A0 B7 B8 FF 25 FF 6E 15 4E 06 7C 57 C3 A6
|
41
|
+
Key Attributes: <No Attributes>
|
42
|
+
-----BEGIN PRIVATE KEY-----
|
43
|
+
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCgfZ1NWgnguEiQ
|
44
|
+
F9v1gum5dlayDQGjphybdeWeO3G3qaUifbIq52WD+NtH1JQwk9S0DEeYcNMk9jAk
|
45
|
+
R+nAu+oxSjEpbfNHkOWoYlcAwApiHNwrhmn3H4jYfxd6Ceme3rFKlPMtsCEBp1uA
|
46
|
+
X0jFZcbVBJ1uc96/RroLBTqLwQd/RnJIL0DvRjq2UbiFUHPvHIVr6gcQayqXbP/7
|
47
|
+
6sAGL07JyG6UgYfNBvWfc/zYuihZXWmvrs98pRvOlYf6+EJgs5Rx4rwvB9UJpNzs
|
48
|
+
oCsdDPbFFVLLtjrsJ4Aj9kvXDeiXDEoiJad3JZNYziuGlIeIcHOUzqEAJWV3suf7
|
49
|
+
0P/SOsMZAgMBAAECggEAeyrsJgkLu6KyTaOB85FcvjDLqCnDMEyzd7urVRxZ0ZIo
|
50
|
+
YagFrFeZ2QaLgzzp3b4WZ3dlGuC7qooluDJYesm8mcdOo3T2V94j4m5WRwUa8uEj
|
51
|
+
N2QYvsc8xqoC4m3pFTvi5FIbrtyU+CfwWTzHJHdPePvptnLGaBVngj2pCMhDcLDw
|
52
|
+
bmcpe6NtE62tYj12ToVKZZ6L85sTZ+cZ3u4nrIghWz0YivJJyXoLncozxfRKq77p
|
53
|
+
e9tYcJcidW2FurrfTRRsGB4U1YPULv7c4IKe7i+6T90YBc4230dl61VFQ/mAb2Or
|
54
|
+
26TkSpMjcT74lIJ8Pdte9HgNsAmA5maTPpczx99BgQKBgQDTatn3bxJkIpPbYTug
|
55
|
+
RlatQeHigaxuwUIJxz3LIfcaa5QllgTPbM5LP090ocqn8S7U24b3U5FC/CbCgjyV
|
56
|
+
SdgZg/rCYhALcOYtJJPZAZuF/G9ZN2fdcsn7yd5RYwcz2/RRZEVH9QIlrQDIvim+
|
57
|
+
CTLhndwp8PL2esM1hRuOypuyLQKBgQDCVYmcjZ1gyCIG60ORwXiOH1FjYD+8g37f
|
58
|
+
3HAB6GojAStZMzQs+csv5NDtmp19jnMgHTi4iuL3/U9R/cyvsg8bGDO6gU8KHy7t
|
59
|
+
1x3fmfa2DH03QPah+eISSMWZ0iJKfZZOVh7q7seJLL801Y3CxtON5MA0+NivKV0E
|
60
|
+
5K2chbZkHQKBgQCNr5FuKukHZMj0gEBmlF0odfu9QixeeXQZe4alKRkigrBhVB3b
|
61
|
+
KU9DzKUiQ/+993vsOUdFX6L9YmFTf9a8MiX6S6H1PaUFFd+cp2PSTip9bATvpJse
|
62
|
+
JPzKmqo8nwJHtgQRvAJWTBPpTf01i4QMELwhyhqvnQQnPXSv3Inwc0ZO7QKBgCLj
|
63
|
+
pH8c1aJoi+NeHZVvfKtXaA9FWmwH16FtPBa7mUKLlTLAhJ+GgToqBHCED75Y8sZT
|
64
|
+
OLm5BaKsMa9wt2zbtOaSY8caLAyPKKe+tThNtgNdHqXq3dyatkG+GuvQ1CM3hOKv
|
65
|
+
h4F6OQyhofpkVL0hYR3acb+Q5aYc8PxN6fVt9B7pAoGAcgWYHNs0LpTlpvQCDOuE
|
66
|
+
hEZEU5NdAO1HXB+DYSs31gWVSs1d2j6lkaeyIqaGDSDS06NsS8D5715GXMsqzkGd
|
67
|
+
a5q92h76eW9keosF2VtG2llHevghWe73yB2qpoxK8myR2XkMPTRa+L4yS/8BrcWJ
|
68
|
+
oSXfBq9svsqxpGadVIkxeng=
|
69
|
+
-----END PRIVATE KEY-----
|
Binary file
|
@@ -0,0 +1,69 @@
|
|
1
|
+
Bag Attributes
|
2
|
+
friendlyName: Apple Development IOS Push Services: 9YGP3C6FRF:9YGP3C6FRF
|
3
|
+
localKeyID: 72 FA 95 A0 3B AD A0 B7 B8 FF 25 FF 6E 15 4E 06 7C 57 C3 A6
|
4
|
+
subject=/UID=com.sutajio.Friend/CN=Apple Development IOS Push Services: 9YGP3C6FRF:9YGP3C6FRF/C=SE
|
5
|
+
issuer=/C=US/O=Apple Inc./OU=Apple Worldwide Developer Relations/CN=Apple Worldwide Developer Relations Certification Authority
|
6
|
+
-----BEGIN CERTIFICATE-----
|
7
|
+
MIIFdjCCBF6gAwIBAgIIYdy07VBp8fIwDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNV
|
8
|
+
BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js
|
9
|
+
ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3
|
10
|
+
aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
|
11
|
+
HhcNMTEwODE0MTg1NjMwWhcNMTExMTEyMTg1NjMwWjB2MSIwIAYKCZImiZPyLGQB
|
12
|
+
AQwSY29tLnN1dGFqaW8uRnJpZW5kMUMwQQYDVQQDDDpBcHBsZSBEZXZlbG9wbWVu
|
13
|
+
dCBJT1MgUHVzaCBTZXJ2aWNlczogOVlHUDNDNkZSRjo5WUdQM0M2RlJGMQswCQYD
|
14
|
+
VQQGEwJTRTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKB9nU1aCeC4
|
15
|
+
SJAX2/WC6bl2VrINAaOmHJt15Z47cbeppSJ9sirnZYP420fUlDCT1LQMR5hw0yT2
|
16
|
+
MCRH6cC76jFKMSlt80eQ5ahiVwDACmIc3CuGafcfiNh/F3oJ6Z7esUqU8y2wIQGn
|
17
|
+
W4BfSMVlxtUEnW5z3r9GugsFOovBB39GckgvQO9GOrZRuIVQc+8chWvqBxBrKpds
|
18
|
+
//vqwAYvTsnIbpSBh80G9Z9z/Ni6KFldaa+uz3ylG86Vh/r4QmCzlHHivC8H1Qmk
|
19
|
+
3OygKx0M9sUVUsu2OuwngCP2S9cN6JcMSiIlp3clk1jOK4aUh4hwc5TOoQAlZXey
|
20
|
+
5/vQ/9I6wxkCAwEAAaOCAeUwggHhMB0GA1UdDgQWBBRy+pWgO62gt7j/Jf9uFU4G
|
21
|
+
fFfDpjAJBgNVHRMEAjAAMB8GA1UdIwQYMBaAFIgnFwmpthhgi+zruvZHWcVSVKO3
|
22
|
+
MIIBDwYDVR0gBIIBBjCCAQIwgf8GCSqGSIb3Y2QFATCB8TCBwwYIKwYBBQUHAgIw
|
23
|
+
gbYMgbNSZWxpYW5jZSBvbiB0aGlzIGNlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBh
|
24
|
+
c3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFy
|
25
|
+
ZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRlIHBvbGlj
|
26
|
+
eSBhbmQgY2VydGlmaWNhdGlvbiBwcmFjdGljZSBzdGF0ZW1lbnRzLjApBggrBgEF
|
27
|
+
BQcCARYdaHR0cDovL3d3dy5hcHBsZS5jb20vYXBwbGVjYS8wTQYDVR0fBEYwRDBC
|
28
|
+
oECgPoY8aHR0cDovL2RldmVsb3Blci5hcHBsZS5jb20vY2VydGlmaWNhdGlvbmF1
|
29
|
+
dGhvcml0eS93d2RyY2EuY3JsMAsGA1UdDwQEAwIHgDATBgNVHSUEDDAKBggrBgEF
|
30
|
+
BQcDAjAQBgoqhkiG92NkBgMBBAIFADANBgkqhkiG9w0BAQUFAAOCAQEAsPFrmajA
|
31
|
+
pd4c9h63Z4j0BjS9aFdlTvuP1rrZUwQG2vCGfbg9BAv1YQfPGmHqOFDuCbr2Br9y
|
32
|
+
K6apMwgzfhO2C94H0uw+CmppUwo4h2zyweZC2BNT13a28Lg8QRnqDG4JdByTCyvp
|
33
|
+
Sa4pLwSG4dhZqp6tKB5e9k80x/73oXLQOLWIfhgg0jxxeLP/ZJQbjrO4re8PNOxi
|
34
|
+
pFohKnjVB6cxc/GWTkWUkjaZhRzMAcPGCKm7zDzsoHF7si55sc4l5QPLFxMT/BF5
|
35
|
+
Ble5Yv70jcJ9uBiQBu6gVO2+SC/8EouCORcKEsE90MNFQWuH0zafiwdw57Eq1SG5
|
36
|
+
RyzYEuwYfe5xAA==
|
37
|
+
-----END CERTIFICATE-----
|
38
|
+
Bag Attributes
|
39
|
+
friendlyName: Niklas Holmgren
|
40
|
+
localKeyID: 72 FA 95 A0 3B AD A0 B7 B8 FF 25 FF 6E 15 4E 06 7C 57 C3 A6
|
41
|
+
Key Attributes: <No Attributes>
|
42
|
+
-----BEGIN PRIVATE KEY-----
|
43
|
+
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCgfZ1NWgnguEiQ
|
44
|
+
F9v1gum5dlayDQGjphybdeWeO3G3qaUifbIq52WD+NtH1JQwk9S0DEeYcNMk9jAk
|
45
|
+
R+nAu+oxSjEpbfNHkOWoYlcAwApiHNwrhmn3H4jYfxd6Ceme3rFKlPMtsCEBp1uA
|
46
|
+
X0jFZcbVBJ1uc96/RroLBTqLwQd/RnJIL0DvRjq2UbiFUHPvHIVr6gcQayqXbP/7
|
47
|
+
6sAGL07JyG6UgYfNBvWfc/zYuihZXWmvrs98pRvOlYf6+EJgs5Rx4rwvB9UJpNzs
|
48
|
+
oCsdDPbFFVLLtjrsJ4Aj9kvXDeiXDEoiJad3JZNYziuGlIeIcHOUzqEAJWV3suf7
|
49
|
+
0P/SOsMZAgMBAAECggEAeyrsJgkLu6KyTaOB85FcvjDLqCnDMEyzd7urVRxZ0ZIo
|
50
|
+
YagFrFeZ2QaLgzzp3b4WZ3dlGuC7qooluDJYesm8mcdOo3T2V94j4m5WRwUa8uEj
|
51
|
+
N2QYvsc8xqoC4m3pFTvi5FIbrtyU+CfwWTzHJHdPePvptnLGaBVngj2pCMhDcLDw
|
52
|
+
bmcpe6NtE62tYj12ToVKZZ6L85sTZ+cZ3u4nrIghWz0YivJJyXoLncozxfRKq77p
|
53
|
+
e9tYcJcidW2FurrfTRRsGB4U1YPULv7c4IKe7i+6T90YBc4230dl61VFQ/mAb2Or
|
54
|
+
26TkSpMjcT74lIJ8Pdte9HgNsAmA5maTPpczx99BgQKBgQDTatn3bxJkIpPbYTug
|
55
|
+
RlatQeHigaxuwUIJxz3LIfcaa5QllgTPbM5LP090ocqn8S7U24b3U5FC/CbCgjyV
|
56
|
+
SdgZg/rCYhALcOYtJJPZAZuF/G9ZN2fdcsn7yd5RYwcz2/RRZEVH9QIlrQDIvim+
|
57
|
+
CTLhndwp8PL2esM1hRuOypuyLQKBgQDCVYmcjZ1gyCIG60ORwXiOH1FjYD+8g37f
|
58
|
+
3HAB6GojAStZMzQs+csv5NDtmp19jnMgHTi4iuL3/U9R/cyvsg8bGDO6gU8KHy7t
|
59
|
+
1x3fmfa2DH03QPah+eISSMWZ0iJKfZZOVh7q7seJLL801Y3CxtON5MA0+NivKV0E
|
60
|
+
5K2chbZkHQKBgQCNr5FuKukHZMj0gEBmlF0odfu9QixeeXQZe4alKRkigrBhVB3b
|
61
|
+
KU9DzKUiQ/+993vsOUdFX6L9YmFTf9a8MiX6S6H1PaUFFd+cp2PSTip9bATvpJse
|
62
|
+
JPzKmqo8nwJHtgQRvAJWTBPpTf01i4QMELwhyhqvnQQnPXSv3Inwc0ZO7QKBgCLj
|
63
|
+
pH8c1aJoi+NeHZVvfKtXaA9FWmwH16FtPBa7mUKLlTLAhJ+GgToqBHCED75Y8sZT
|
64
|
+
OLm5BaKsMa9wt2zbtOaSY8caLAyPKKe+tThNtgNdHqXq3dyatkG+GuvQ1CM3hOKv
|
65
|
+
h4F6OQyhofpkVL0hYR3acb+Q5aYc8PxN6fVt9B7pAoGAcgWYHNs0LpTlpvQCDOuE
|
66
|
+
hEZEU5NdAO1HXB+DYSs31gWVSs1d2j6lkaeyIqaGDSDS06NsS8D5715GXMsqzkGd
|
67
|
+
a5q92h76eW9keosF2VtG2llHevghWe73yB2qpoxK8myR2XkMPTRa+L4yS/8BrcWJ
|
68
|
+
oSXfBq9svsqxpGadVIkxeng=
|
69
|
+
-----END PRIVATE KEY-----
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require File.expand_path('test/test_helper')
|
2
|
+
|
3
|
+
class NotificationTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@notification = ApplePush::Notification.new('FkjWc8HztEiEblJ7g1n8AKw8kvyCtkQKc10tmoo2Tak=',
|
7
|
+
:alert => 'Hello, world!', :badge => 1, :sound => 'default', :other => { :foo => 'bar' })
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_raises_errors
|
11
|
+
assert_raises RuntimeError do
|
12
|
+
ApplePush::Notification.new(nil, 'Hello, world!')
|
13
|
+
end
|
14
|
+
assert_raises RuntimeError do
|
15
|
+
ApplePush::Notification.new('...', nil)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_can_create_notification_with_only_an_alert_string
|
20
|
+
assert_equal 'Hello, world!', ApplePush::Notification.new('...', 'Hello, world!').alert
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_can_handle_base64_encoded_device_token
|
24
|
+
@notification.device_token = 'FkjWc8HztEiEblJ7g1n8AKw8kvyCtkQKc10tmoo2Tak='
|
25
|
+
assert_equal Base64.decode64('FkjWc8HztEiEblJ7g1n8AKw8kvyCtkQKc10tmoo2Tak='), @notification.device_token
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_can_handle_binary_device_token
|
29
|
+
@notification.device_token = Base64.decode64('FkjWc8HztEiEblJ7g1n8AKw8kvyCtkQKc10tmoo2Tak=')
|
30
|
+
assert_equal Base64.decode64('FkjWc8HztEiEblJ7g1n8AKw8kvyCtkQKc10tmoo2Tak='), @notification.device_token
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_payload
|
34
|
+
payload = JSON.parse(@notification.send(:payload))
|
35
|
+
assert_equal 'Hello, world!', payload['aps']['alert']
|
36
|
+
assert_equal 1, payload['aps']['badge']
|
37
|
+
assert_equal 'default', payload['aps']['sound']
|
38
|
+
assert_equal 'bar', payload['foo']
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_packaged_notification
|
42
|
+
assert_equal "\x00\x00 \x16H\xD6s\xC1\xF3\xB4H\x84nR{\x83Y\xFC\x00\xAC<\x92\xFC\x82\xB6D\ns]-\x9A\x8A6M\xA9\x00I{\"aps\":{\"alert\":\"Hello, world!\",\"badge\":1,\"sound\":\"default\"},\"foo\":\"bar\"}", @notification.to_s
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: apple-push
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Niklas Holmgren
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-09-27 00:00:00 +02:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: json
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">"
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 1
|
30
|
+
- 0
|
31
|
+
- 0
|
32
|
+
version: 1.0.0
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
description: ApplePush is a very simple implementation of the APNS (Apple Push Notification Service) protocol. It differs from the APNS gem by being able to push to different iOS apps from the same Ruby application.
|
36
|
+
email: niklas@sutajio.se
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- LICENSE
|
43
|
+
- README.md
|
44
|
+
files:
|
45
|
+
- README.md
|
46
|
+
- Rakefile
|
47
|
+
- LICENSE
|
48
|
+
- CHANGELOG
|
49
|
+
- lib/apple-push/notification.rb
|
50
|
+
- lib/apple-push/version.rb
|
51
|
+
- lib/apple-push.rb
|
52
|
+
- test/apple_push_test.rb
|
53
|
+
- test/cert.p12
|
54
|
+
- test/cert.pem
|
55
|
+
- test/cert_secure.p12
|
56
|
+
- test/cert_secure.pem
|
57
|
+
- test/notification_test.rb
|
58
|
+
- test/test_helper.rb
|
59
|
+
- certs/entrust_ssl_ca.cer
|
60
|
+
has_rdoc: true
|
61
|
+
homepage: http://github.com/sutajio/apple-push/
|
62
|
+
licenses: []
|
63
|
+
|
64
|
+
post_install_message:
|
65
|
+
rdoc_options:
|
66
|
+
- --charset=UTF-8
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
segments:
|
75
|
+
- 0
|
76
|
+
version: "0"
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
none: false
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
segments:
|
83
|
+
- 0
|
84
|
+
version: "0"
|
85
|
+
requirements: []
|
86
|
+
|
87
|
+
rubyforge_project:
|
88
|
+
rubygems_version: 1.3.7
|
89
|
+
signing_key:
|
90
|
+
specification_version: 3
|
91
|
+
summary: ApplePush is a very simple implementation of the APNS (Apple Push Notification Service) protocol
|
92
|
+
test_files: []
|
93
|
+
|