mobile_notify 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+