ns-apple-push 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c645fa8a83f91369d9126fcc085be0486c4858b2
4
+ data.tar.gz: 8e9049bb60787b76067cbec4bb2d5262d84ab957
5
+ SHA512:
6
+ metadata.gz: b50b7d4d453584e95d2970d9d6c1f7654b935f67578e09a60f3c5c7870b4e63b76c75e035588f2d5ef3572dd623b07addf92b0d3d111f783247bc51bc11903e7
7
+ data.tar.gz: e57c0023d7bd6be877a5b227eefdac2bfce99a932b2373792c85407ae13d26993a29f3cc465de9be6029710ca904921103a9d10be473f39d6abe3fd7f6744c7b
data/CHANGELOG ADDED
@@ -0,0 +1,8 @@
1
+
2
+ *0.1*
3
+
4
+ * First public release
5
+
6
+ *1.0*
7
+
8
+ * Added silent notification support
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
+ Then 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,11 @@
1
+ require 'rake/testtask'
2
+
3
+ $LOAD_PATH.unshift 'lib'
4
+
5
+ task :default => [:test]
6
+
7
+ task :test do
8
+ Dir.glob('test/**/*_test.rb').each do |file|
9
+ require File.expand_path(file)
10
+ end
11
+ end
@@ -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,51 @@
1
+ module ApplePush
2
+ class Notification
3
+ attr_accessor :device_token, :alert, :badge, :sound, :other, :content_available
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
+ self.content_available = message[:content_available]
17
+ elsif message.is_a?(String)
18
+ self.alert = message
19
+ else
20
+ raise "Notification message needs to be either a hash or a string"
21
+ end
22
+ end
23
+
24
+ def device_token
25
+ if @device_token =~ /=$/
26
+ Base64.decode64(@device_token)
27
+ else
28
+ @device_token
29
+ end
30
+ end
31
+
32
+ def to_s
33
+ dt = self.device_token
34
+ pl = self.payload
35
+ [0, 0, dt.size, dt, 0, pl.size, pl].pack("ccca*cca*")
36
+ end
37
+
38
+ protected
39
+
40
+ def payload
41
+ aps = {'aps'=> {} }
42
+ aps['aps']['alert'] = self.alert if self.alert
43
+ aps['aps']['badge'] = self.badge if self.badge
44
+ aps['aps']['sound'] = self.sound if self.sound
45
+ aps['aps']['content-available'] = self.content_available if self.content_available
46
+ aps.merge!(self.other) if self.other
47
+ aps.to_json
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,3 @@
1
+ module ApplePush
2
+ Version = VERSION = '1.0.0'
3
+ 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
@@ -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
@@ -0,0 +1,6 @@
1
+
2
+ dir = File.dirname(File.expand_path(__FILE__))
3
+ $LOAD_PATH.unshift dir + '/../lib'
4
+
5
+ require 'test/unit'
6
+ require 'apple-push'
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ns-apple-push
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Niklas Holmgren
8
+ - Matt Price
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2018-11-21 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: json
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">"
19
+ - !ruby/object:Gem::Version
20
+ version: 1.0.0
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">"
26
+ - !ruby/object:Gem::Version
27
+ version: 1.0.0
28
+ description: ApplePush is a very simple implementation of the APNS (Apple Push Notification
29
+ Service) protocol. It differs from the APNS gem by being able to push to different
30
+ iOS apps from the same Ruby application.
31
+ email:
32
+ - niklas@sutajio.se
33
+ - matt@netsanity.net
34
+ executables: []
35
+ extensions: []
36
+ extra_rdoc_files:
37
+ - LICENSE
38
+ - README.md
39
+ files:
40
+ - CHANGELOG
41
+ - LICENSE
42
+ - README.md
43
+ - Rakefile
44
+ - certs/entrust_ssl_ca.cer
45
+ - lib/apple-push.rb
46
+ - lib/apple-push/notification.rb
47
+ - lib/apple-push/version.rb
48
+ - test/apple_push_test.rb
49
+ - test/notification_test.rb
50
+ - test/test_helper.rb
51
+ homepage: http://github.com/sutajio/apple-push/
52
+ licenses: []
53
+ metadata: {}
54
+ post_install_message:
55
+ rdoc_options:
56
+ - "--charset=UTF-8"
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ requirements: []
70
+ rubyforge_project:
71
+ rubygems_version: 2.6.14
72
+ signing_key:
73
+ specification_version: 4
74
+ summary: ApplePush is a very simple implementation of the APNS (Apple Push Notification
75
+ Service) protocol
76
+ test_files: []