mobile_notify 0.1

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.
@@ -0,0 +1,54 @@
1
+ require "rubygems"
2
+ require "pathname"
3
+ require "rake"
4
+ require "rake/testtask"
5
+
6
+ # task :default => [:test]
7
+ # Rake::TestTask.new do |t|
8
+ # t.libs << "test"
9
+ # t.test_files = FileList['test/**/*_test.rb']
10
+ # t.verbose = true
11
+ # end
12
+
13
+ # Gem
14
+ require "rake/gempackagetask"
15
+ require "lib/mobile_notify/version"
16
+
17
+ NAME = "mobile_notify"
18
+ SUMMARY = "Mobile Notification Services w/ support for the Apple Push Notification Service (APNS)"
19
+ GEM_VERSION = MobileNotify::VERSION
20
+
21
+ spec = Gem::Specification.new do |s|
22
+ s.name = NAME
23
+ s.summary = s.description = SUMMARY
24
+ s.author = "Scott Bauer"
25
+ s.homepage = "http://rdoc.info/projects/Bauerpauer/mobile_notify"
26
+ s.version = GEM_VERSION
27
+ s.platform = Gem::Platform::RUBY
28
+ s.require_path = 'lib'
29
+ s.files = %w(Rakefile) + Dir.glob("lib/**/*")
30
+ s.executables = ['apns_ping']
31
+
32
+ s.add_dependency "json"
33
+ end
34
+
35
+ Rake::GemPackageTask.new(spec) do |pkg|
36
+ pkg.gem_spec = spec
37
+ end
38
+
39
+ desc "Install #{NAME} as a gem"
40
+ task :install => [:repackage] do
41
+ sh %{gem install pkg/#{NAME}-#{GEM_VERSION}}
42
+ end
43
+
44
+ task :version do
45
+ puts GEM_VERSION
46
+ end
47
+
48
+ spec_file = ".gemspec"
49
+ desc "Create #{spec_file}"
50
+ task :gemspec do
51
+ File.open(spec_file, "w") do |file|
52
+ file.puts spec.to_ruby
53
+ end
54
+ end
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "rubygems"
4
+
5
+ gem "mobile_notify"
6
+ require "mobile_notify"
7
+
8
+ gateway_uri = if ARGV[0].to_s.downcase == '-p'
9
+ ARGV.shift
10
+ MobileNotify::Apns::PRODUCTION_GATEWAY_URI
11
+ else
12
+ MobileNotify::Apns::SANDBOX_GATEWAY_URI
13
+ end
14
+
15
+ device_token, cert, key, badge, alert, sound = if File.file?(ARGV[2])
16
+ [ARGV[0], ARGV[1], ARGV[2], ARGV[3].to_i, ARGV[4], ARGV[5]]
17
+ else
18
+ [ARGV[0], ARGV[1], nil, ARGV[2].to_i, ARGV[3], ARGV[4]]
19
+ end
20
+
21
+ puts "Gateway: #{gateway_uri}"
22
+ puts "Device Token: #{device_token}"
23
+ puts "Badge: #{badge}"
24
+ puts "Alert: #{alert}"
25
+ puts "Sound: #{sound}"
26
+
27
+ MobileNotify::Apns::Connection.open(gateway_uri, cert, key) do |connection|
28
+ connection.send(MobileNotify::Apns::SimpleNotification.new(device_token, badge, alert, sound))
29
+ end
@@ -0,0 +1,8 @@
1
+ require "json"
2
+ require "openssl"
3
+ require "socket"
4
+ require "uri"
5
+
6
+ require "mobile_notify/version"
7
+ require "mobile_notify/apns/connection"
8
+ require "mobile_notify/apns/notification"
@@ -0,0 +1,124 @@
1
+ module MobileNotify
2
+
3
+ module Apns
4
+
5
+ SANDBOX_GATEWAY_URI = URI.parse("apns://gateway.sandbox.push.apple.com:2195")
6
+ PRODUCTION_GATEWAY_URI = URI.parse("apns://gateway.push.apple.com:2195")
7
+
8
+ class Connection
9
+
10
+ class NotConnectedError < StandardError
11
+ def initialize(uri)
12
+ super("A connection to #{uri} has not yet been established.")
13
+ end
14
+ end
15
+
16
+ class AlreadyConnectedError < StandardError
17
+ def initialize(uri)
18
+ super("A connection to #{uri} has already been established.")
19
+ end
20
+ end
21
+
22
+ class ConnectionTimeoutError < StandardError
23
+ def initialize(uri, timeout, original_error)
24
+ super("Unable to establish connection to #{uri.host}:#{uri.port} within #{timeout} seconds. SSL Reported: #{original_error.inspect}")
25
+ end
26
+ end
27
+
28
+ class TransmissionTimeoutError < StandardError
29
+ def initialize(uri, timeout, original_error)
30
+ super("Unable to send data to #{uri.host}:#{uri.port} within #{timeout} seconds. SSL Reported: #{original_error.inspect}")
31
+ end
32
+ end
33
+
34
+ DEFAULT_CONNECTION_TIMEOUT = 30
35
+ DEFAULT_CONNECTION_RETRY_DELAY = 2
36
+ DEFAULT_TRANSMISSION_TIMEOUT = 10
37
+ DEFAULT_TRANSMISSION_RETRY_DELAY = 2
38
+
39
+ attr_accessor :connection_timeout, :connection_retry_delay
40
+ attr_accessor :transmission_timeout, :transmission_retry_delay
41
+
42
+ def self.open(uri, certificate_file, key_file = certificate_file)
43
+ connection = new(uri, certificate_file, key_file = certificate_file)
44
+ connection.open
45
+ yield connection
46
+ ensure
47
+ connection.close if connection
48
+ end
49
+
50
+ def initialize(uri, certificate_file, key_file = certificate_file)
51
+ @uri = uri
52
+ @certificate_file = certificate_file
53
+ @key_file = key_file.nil? ? certificate_file : key_file
54
+
55
+ @connection_timeout = DEFAULT_CONNECTION_TIMEOUT
56
+ @connection_retry_delay = DEFAULT_CONNECTION_RETRY_DELAY
57
+ @transmission_timeout = DEFAULT_TRANSMISSION_TIMEOUT
58
+ @transmission_retry_delay = DEFAULT_TRANSMISSION_RETRY_DELAY
59
+
60
+ @ssl_context = OpenSSL::SSL::SSLContext.new()
61
+ @ssl_context.cert = OpenSSL::X509::Certificate.new(File::read(@certificate_file))
62
+ @ssl_context.key = OpenSSL::PKey::RSA.new(File::read(@key_file))
63
+ @tcp_socket = TCPSocket.new(@uri.host, @uri.port)
64
+ @ssl_socket = nil
65
+ end
66
+
67
+ def open
68
+ raise AlreadyConnectedError.new(@uri) if @ssl_socket
69
+
70
+ @ssl_socket = establish_connection
71
+ self
72
+ end
73
+
74
+ def send(notification)
75
+ raise NotConnectedError.new(@uri) unless @ssl_socket
76
+
77
+ unless defined?(retry_timer)
78
+ retry_timer = 0
79
+ end
80
+
81
+ @ssl_socket.write(notification.to_data)
82
+ self
83
+ rescue OpenSSL::SSL::SSLError, Errno::EPIPE
84
+ sleep(self.transmission_retry_delay)
85
+ retry_timer += self.transmission_retry_delay
86
+ retry if retry_timer < self.transmission_timeout
87
+
88
+ raise TransmissionTimeoutError.new(@uri, self.transmission_timeout, $!) if retry_timer >= self.transmission_timeout
89
+ end
90
+
91
+ def close
92
+ @ssl_socket.close if @ssl_socket
93
+ @ssl_socket = nil
94
+
95
+ self
96
+ end
97
+
98
+ protected
99
+
100
+ def establish_connection
101
+ unless defined?(retry_timer)
102
+ retry_timer = 0
103
+ end
104
+
105
+ # Wrap the TCP socket w/ SSL
106
+ ssl_socket = OpenSSL::SSL::SSLSocket.new(@tcp_socket, @ssl_context)
107
+ ssl_socket.connect
108
+ ssl_socket.sync_close = true
109
+
110
+ ssl_socket
111
+ rescue OpenSSL::SSL::SSLError, Errno::EPIPE
112
+ sleep(self.connection_retry_delay)
113
+ retry_timer += self.connection_retry_delay
114
+ retry if retry_timer < self.connection_timeout
115
+
116
+ raise ConnectionTimeoutError.new(@uri, self.connection_timeout, $!) if retry_timer >= self.connection_timeout
117
+ end
118
+
119
+ end
120
+
121
+ end
122
+
123
+ end
124
+
@@ -0,0 +1,75 @@
1
+ module MobileNotify
2
+
3
+ module Apns
4
+
5
+ class Notification
6
+
7
+ MAX_PAYLOAD_LENGTH = 256
8
+
9
+ # Counterpart of {Notification#to_s} - parses from binary string
10
+ # @param [String] bitstring string to parse
11
+ # @return [Notification] parsed Notification object
12
+ def self.parse(bitstring)
13
+ command, tokenlen, device_token, payloadlen, payload = bitstring.unpack("CnH64na*")
14
+ new(device_token, payload)
15
+ end
16
+
17
+ # Creates new notification with given token and payload
18
+ # @param [String, Fixnum] token APNs token of device to notify
19
+ # @param [Hash, String] payload attached payload
20
+ # @example
21
+ # APNs4r::Notification.new 'e754dXXXX...', { :aps => {:alert => "Hey, dude!", :badge => 1}, :custom_data => "asd" }
22
+ def initialize(device_token, payload)
23
+ @device_token = device_token.delete(' ')
24
+ @payload = payload.kind_of?(Hash) ? payload.to_json : payload
25
+ end
26
+
27
+ def valid?
28
+ @payload.length <= MAX_PAYLOAD_LENGTH
29
+ end
30
+
31
+ # Converts to binary string wich can be writen directly into socket
32
+ # @return [String] binary string representation
33
+ def to_data
34
+ [0, 32, @device_token, @payload.length, @payload ].pack("CnH*na*")
35
+ end
36
+
37
+ end
38
+
39
+ class AlertNotification < Notification
40
+ def initialize(device_token, alert, sound = nil)
41
+ payload = { "aps" => {} }
42
+ payload["aps"]["alert"] = alert
43
+ payload["aps"]["sound"] = sound if sound
44
+ super(device_token, payload)
45
+ end
46
+ end
47
+
48
+ class BadgeNotification < Notification
49
+ def initialize(device_token, badge, sound = nil)
50
+ payload = { "aps" => {} }
51
+ payload["aps"]["badge"] = badge_value.to_i
52
+ payload["aps"]["sound"] = sound if sound
53
+ super(device_token, payload)
54
+ end
55
+ end
56
+
57
+ class SimpleNotification < Notification
58
+
59
+ EMPTY_BADGE = 0
60
+
61
+ def initialize(device_token, badge_value = EMPTY_BADGE, alert = nil, sound = nil, extra = nil)
62
+ payload = { "aps" => {} }
63
+ payload["aps"]["badge"] = badge_value.to_i
64
+ payload["aps"]["alert"] = alert if alert
65
+ payload["aps"]["sound"] = sound if sound
66
+ payload.update(extra) if extra.is_a?(Hash)
67
+
68
+ super(device_token, payload)
69
+ end
70
+
71
+ end
72
+
73
+ end
74
+
75
+ end
@@ -0,0 +1,3 @@
1
+ module MobileNotify
2
+ VERSION = "0.1"
3
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mobile_notify
3
+ version: !ruby/object:Gem::Version
4
+ hash: 9
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ version: "0.1"
10
+ platform: ruby
11
+ authors:
12
+ - Scott Bauer
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-05-27 00:00:00 -05: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
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ description: Mobile Notification Services w/ support for the Apple Push Notification Service (APNS)
35
+ email:
36
+ executables:
37
+ - apns_ping
38
+ extensions: []
39
+
40
+ extra_rdoc_files: []
41
+
42
+ files:
43
+ - Rakefile
44
+ - lib/mobile_notify/apns/connection.rb
45
+ - lib/mobile_notify/apns/notification.rb
46
+ - lib/mobile_notify/version.rb
47
+ - lib/mobile_notify.rb
48
+ - bin/apns_ping
49
+ has_rdoc: true
50
+ homepage: http://rdoc.info/projects/Bauerpauer/mobile_notify
51
+ licenses: []
52
+
53
+ post_install_message:
54
+ rdoc_options: []
55
+
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ hash: 3
64
+ segments:
65
+ - 0
66
+ version: "0"
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ hash: 3
73
+ segments:
74
+ - 0
75
+ version: "0"
76
+ requirements: []
77
+
78
+ rubyforge_project:
79
+ rubygems_version: 1.3.7
80
+ signing_key:
81
+ specification_version: 3
82
+ summary: Mobile Notification Services w/ support for the Apple Push Notification Service (APNS)
83
+ test_files: []
84
+