apnd 0.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.
data/README.markdown ADDED
@@ -0,0 +1,97 @@
1
+ # APND
2
+
3
+ APND (Apple Push Notification Daemon) is a ruby library to send Apple Push
4
+ Notifications (APNs) to iPhones.
5
+
6
+ Apple recommends application developers create one connection to their
7
+ upstream push notification server, rather than creating one per notification.
8
+
9
+ APND acts as an intermediary between your application and Apple (see **APND
10
+ Daemon** below). Your application's notifications are queued to APND, which
11
+ are then sent to Apple over a single connection.
12
+
13
+ Within ruby applications, `APND::Notification` can be used to send
14
+ notifications to a running APND instance (see **APND Notification** below) or
15
+ directly to Apple. A command line utility, `apnd-push`, can be used to send
16
+ single notifications for testing purposes (see **APND Client** below).
17
+
18
+
19
+ ## General Usage
20
+
21
+ ### APND Daemon
22
+
23
+ APND receives push notifications from your application and relays them to
24
+ Apple over a single connection as explained above. The `apnd` command line
25
+ utility is used to start APND.
26
+
27
+ Usage:
28
+ apnd [OPTIONS] --apple-cert </path/to/cert>
29
+
30
+ Required Arguments:
31
+ --apple-cert [PATH] PATH to APN certificate from Apple
32
+
33
+ Optional Arguments:
34
+ --apple-host [HOST] Connect to Apple at HOST (default is gateway.sandbox.push.apple.com)
35
+ --apple-port [PORT] Connect to Apple on PORT (default is 2195)
36
+ --apple-cert-pass [PASSWORD] PASSWORD for APN certificate from Apple
37
+ --daemon-port [PORT] Run APND on PORT (default is 22195)
38
+ --daemon-bind [ADDRESS] Bind APND to ADDRESS (default is 0.0.0.0)
39
+ --daemon-log-file [PATH] PATH to APND log file (default is /var/log/apnd.log)
40
+ --foreground Run APND in foreground without daemonizing
41
+
42
+ Help:
43
+ --version Show version
44
+ --help Show this message
45
+
46
+
47
+ ### APND Client
48
+
49
+ APND includes a command line utility, `apnd-push`, which can be used to send
50
+ notifications to a running APND instance, or Apple directly. It is only
51
+ recommended to send notifications directly to Apple for testing purposes.
52
+
53
+ Usage:
54
+ apnd-push [OPTIONS] --token <token> --alert <alert>
55
+
56
+ Required Arguments:
57
+ --token [TOKEN] Set Notification's iPhone token to TOKEN
58
+ --alert [MESSAGE] Set Notification's alert to MESSAGE
59
+
60
+ Optional Arguments:
61
+ --sound [SOUND] Set Notification's sound to SOUND (default is 'default')
62
+ --badge [NUMBER] Set Notification's badge number to NUMBER
63
+ --custom [JSON] Set Notification's custom data to JSON
64
+ --host [HOST] Send Notification to HOST, usually the one running APND (default is 'localhost')
65
+ --port [PORT] Send Notification on PORT (default is 22195)
66
+
67
+ Help:
68
+ --version Show version
69
+ --help Show this message
70
+
71
+
72
+ ### APND Notification
73
+
74
+ The `APND::Notification` class can be used within your application to send
75
+ push notifications to APND.
76
+
77
+ require 'apnd'
78
+
79
+ # Set the host/port APND is running on
80
+ # (not needed if you're using localhost:22195)
81
+
82
+ APND::Notification.upstream_host = 'localhost'
83
+ APND::Notification.upstream_port = 22195
84
+
85
+ notification = APND::Notification.create(
86
+ :alert => 'Alert!',
87
+ :token => 'fe15a27d5df3c34778defb1f4f3880265cc52c0c047682223be59fb68500a9a2',
88
+ :badge => 99
89
+ )
90
+
91
+
92
+ ## Credit
93
+
94
+ APND is based on [apnserver](http://github.com/bpoweski/apnserver) and
95
+ [apn_on_rails](http://github.com/PRX/apn_on_rails). Either worked just how I
96
+ wanted, so I rolled my own using theirs as starting points. If APND doesn't
97
+ suit you, check them out instead.
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+
3
+ require 'rake/testtask'
4
+ Rake::TestTask.new(:test) do |test|
5
+ test.libs << 'lib' << 'test' << '.'
6
+ test.pattern = 'test/**/*_test.rb'
7
+ test.verbose = true
8
+ end
9
+
10
+ desc "Open an irb session preloaded with this library"
11
+ task :console do
12
+ sh "irb -rubygems -r ./lib/apnd.rb -I ./lib"
13
+ end
data/bin/apnd ADDED
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
4
+
5
+ require 'apnd'
6
+
7
+ require 'daemons'
8
+ require 'optparse'
9
+
10
+ help = <<HELP
11
+ Usage:
12
+ apnd [OPTIONS] --apple-cert </path/to/cert>
13
+
14
+ HELP
15
+
16
+ options = {}
17
+
18
+ opts = OptionParser.new do |opt|
19
+ opt.banner = help
20
+
21
+ opt.separator "Required Arguments:\n"
22
+
23
+ opt.on('--apple-cert [PATH]', 'PATH to APN certificate from Apple') do |cert|
24
+ options[:apple_cert] = cert
25
+ end
26
+
27
+ opt.separator "\nOptional Arguments:\n"
28
+
29
+ opt.on('--apple-host [HOST]', "Connect to Apple at HOST (default is gateway.sandbox.push.apple.com)") do |host|
30
+ options[:apple_host] = host
31
+ end
32
+
33
+ opt.on('--apple-port [PORT]', 'Connect to Apple on PORT (default is 2195)') do |port|
34
+ options[:apple_port] = port.to_i
35
+ end
36
+
37
+ opt.on('--apple-cert-pass [PASSWORD]', 'PASSWORD for APN certificate from Apple') do |pass|
38
+ options[:apple_cert_pass] = pass
39
+ end
40
+
41
+ opt.on('--daemon-port [PORT]', 'Run APND on PORT (default is 22195)') do |port|
42
+ options[:daemon_port] = port.to_i
43
+ end
44
+
45
+ opt.on('--daemon-bind [ADDRESS]', 'Bind APND to ADDRESS (default is 0.0.0.0)') do |bind|
46
+ options[:daemon_bind] = bind
47
+ end
48
+
49
+ opt.on('--daemon-log-file [PATH]', 'PATH to APND log file (default is /var/log/apnd.log)') do |log|
50
+ options[:daemon_log_file] = log
51
+ end
52
+
53
+ opt.on('--foreground', 'Run APND in foreground without daemonizing') do
54
+ options[:foreground] = true
55
+ end
56
+
57
+ opt.separator "\nHelp:\n"
58
+
59
+ opt.on('--version', 'Show version') do
60
+ puts "APND #{APND::Version}"
61
+ exit
62
+ end
63
+
64
+ opt.on('--help', 'Show this message') do
65
+ puts opt
66
+ exit
67
+ end
68
+ end
69
+
70
+ begin
71
+ opts.parse!
72
+ if options.empty?
73
+ puts opts
74
+ exit
75
+ end
76
+
77
+ unless options[:apple_cert]
78
+ raise OptionParser::MissingArgument, "must specify --apple-cert"
79
+ end
80
+ rescue OptionParser::InvalidOption, OptionParser::MissingArgument
81
+ puts "#{$0}: #{$!.message}"
82
+ puts "#{$0}: try '#{$0} --help' for more information"
83
+ exit
84
+ end
85
+
86
+ APND.configure do |config|
87
+ # Setup AppleConnection
88
+ config.apple.cert = options[:apple_cert] if options[:apple_cert]
89
+ config.apple.cert_pass = options[:apple_cert_pass] if options[:apple_cert_pass]
90
+ config.apple.host = options[:apple_host] if options[:apple_host]
91
+ config.apple.port = options[:apple_port] if options[:apple_port]
92
+
93
+ # Setup Daemon
94
+ config.daemon.bind = options[:daemon_bind] if options[:daemon_bind]
95
+ config.daemon.port = options[:daemon_port] if options[:daemon_port]
96
+ config.daemon.log_file = options[:daemon_log_file] if options[:daemon_log_file]
97
+ end
98
+
99
+ if APND.settings.apple.cert.nil?
100
+ puts opts
101
+ exit
102
+ else
103
+ unless options[:foreground]
104
+ Daemonize.daemonize(APND.settings.daemon.log_file, 'apnd')
105
+ end
106
+ APND::Daemon.run!
107
+ end
data/bin/apnd-push ADDED
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
4
+
5
+ require 'apnd'
6
+
7
+ require 'optparse'
8
+
9
+ help = <<HELP
10
+ Usage:
11
+ apnd-push [OPTIONS] --token <token> --alert <alert>
12
+
13
+ HELP
14
+
15
+ options = {}
16
+
17
+ opts = OptionParser.new do |opt|
18
+ opt.banner = help
19
+
20
+ opt.separator "Required Arguments:\n"
21
+
22
+ opt.on('--token [TOKEN]', "Set Notification's iPhone token to TOKEN") do |token|
23
+ options[:token] = token
24
+ end
25
+
26
+ opt.on('--alert [MESSAGE]', "Set Notification's alert to MESSAGE") do |alert|
27
+ options[:alert] = alert
28
+ end
29
+
30
+ opt.separator "\nOptional Arguments:\n"
31
+
32
+ opt.on('--sound [SOUND]', "Set Notification's sound to SOUND (default is 'default')") do |sound|
33
+ options[:sound] = sound
34
+ end
35
+
36
+ opt.on('--badge [NUMBER]', "Set Notification's badge number to NUMBER") do |badge|
37
+ options[:badge] = badge.to_i
38
+ end
39
+
40
+ opt.on('--custom [JSON]', "Set Notification's custom data to JSON") do |custom|
41
+ begin
42
+ options[:custom] = JSON.parse(custom)
43
+ rescue JSON::ParserError => e
44
+ puts "Invalid JSON: #{e}"
45
+ exit -1
46
+ end
47
+ end
48
+
49
+ opt.on('--host [HOST]', "Send Notification to HOST, usually the one running APND (default is 'localhost')") do |host|
50
+ options[:host] = host
51
+ end
52
+
53
+ opt.on('--port [PORT]', 'Send Notification on PORT (default is 22195)') do |port|
54
+ options[:port] = port.to_i
55
+ end
56
+
57
+ opt.separator "\nHelp:\n"
58
+
59
+ opt.on('--version', 'Show version') do
60
+ puts "APND #{APND::Version}"
61
+ exit
62
+ end
63
+
64
+ opt.on('--help', 'Show this message') do
65
+ puts opt
66
+ exit
67
+ end
68
+ end
69
+
70
+ begin
71
+ opts.parse!
72
+ if options.empty?
73
+ puts opts
74
+ exit
75
+ end
76
+
77
+ unless options[:token] && options[:alert]
78
+ raise OptionParser::MissingArgument, "must specify --token and --alert"
79
+ end
80
+ rescue OptionParser::InvalidOption, OptionParser::MissingArgument
81
+ puts "#{$0}: #{$!.message}"
82
+ puts "#{$0}: try '#{$0} --help' for more information"
83
+ exit
84
+ end
85
+
86
+ # Configure Notification upstream host/port
87
+ APND::Notification.upstream_host = options.delete(:host) if options[:host]
88
+ APND::Notification.upstream_port = options.delete(:port) if options[:port]
89
+
90
+ APND::Notification.create(options)
data/lib/apnd.rb ADDED
@@ -0,0 +1,28 @@
1
+ require 'json'
2
+
3
+ module APND
4
+ autoload :Version, 'apnd/version'
5
+ autoload :Errors, 'apnd/errors'
6
+ autoload :Settings, 'apnd/settings'
7
+ autoload :Daemon, 'apnd/daemon'
8
+ autoload :Notification, 'apnd/notification'
9
+
10
+ #
11
+ # APND Settings
12
+ #
13
+ def self.settings
14
+ @@settings ||= Settings.new
15
+ end
16
+
17
+ #
18
+ # Yields APND Settings
19
+ #
20
+ def self.configure
21
+ yield settings
22
+ end
23
+
24
+ end
25
+
26
+ def ohai(message)
27
+ puts "[%s] %s" % [Time.now.strftime("%Y-%m-%d %H:%M:%S"), message]
28
+ end
@@ -0,0 +1,65 @@
1
+ require 'eventmachine'
2
+
3
+ module APND
4
+ #
5
+ # The APND::Daemon maintains a persistent secure connection with Apple,
6
+ # (APND::Daemon::AppleConnection). Notifications are queued and periodically
7
+ # writen to the AppleConnection
8
+ #
9
+ class Daemon
10
+ autoload :Protocol, 'apnd/daemon/protocol'
11
+ autoload :AppleConnection, 'apnd/daemon/apple_connection'
12
+
13
+ #
14
+ # Create a new Daemon and run it
15
+ #
16
+ def self.run!
17
+ server = APND::Daemon.new
18
+ server.run!
19
+ end
20
+
21
+ #
22
+ # Create a connection to Apple and a new EM queue
23
+ #
24
+ def initialize
25
+ @queue = EM::Queue.new
26
+ @apple = APND::Daemon::AppleConnection.new
27
+ @bind = APND.settings.daemon.bind
28
+ @port = APND.settings.daemon.port
29
+ @timer = APND.settings.daemon.timer
30
+ end
31
+
32
+ #
33
+ # Run the daemon
34
+ #
35
+ def run!
36
+ EventMachine::run do
37
+ ohai "Starting APND Daemon on #{@bind}:#{@port}"
38
+ EventMachine::start_server(@bind, @port, APND::Daemon::Protocol) do |server|
39
+ server.queue = @queue
40
+ end
41
+
42
+ EventMachine::PeriodicTimer.new(@timer) do
43
+ count = @queue.size
44
+ if count > 0
45
+ ohai "Queue has #{count} item#{count == 1 ? '' : 's'}"
46
+ count.times do
47
+ @queue.pop do |notification|
48
+ begin
49
+ ohai "Sending notification"
50
+ @apple.write(notification.to_bytes)
51
+ rescue Errno::EPIPE, OpenSSL::SSL::SSLError
52
+ ohai "Error, notification has been added back to the queue"
53
+ @queue.push(notification)
54
+ rescue RuntimeError => error
55
+ ohai "Error: #{error}"
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,76 @@
1
+ require 'openssl'
2
+ require 'socket'
3
+
4
+ module APND
5
+ #
6
+ # Daemon::AppleConnection handles the persistent connection between
7
+ # APND and Apple
8
+ #
9
+ class Daemon::AppleConnection
10
+ attr_reader :ssl, :sock
11
+
12
+ #
13
+ # Setup a new connection
14
+ #
15
+ def initialize(params = {})
16
+ @options = {
17
+ :cert => APND.settings.apple.cert,
18
+ :cert_pass => APND.settings.apple.cert_pass,
19
+ :host => APND.settings.apple.host,
20
+ :port => APND.settings.apple.port.to_i
21
+ }.merge(params)
22
+ end
23
+
24
+ #
25
+ # Returns true if the connection to Apple is open
26
+ #
27
+ def connected?
28
+ ! @ssl.nil?
29
+ end
30
+
31
+ #
32
+ # Connect to Apple over SSL
33
+ #
34
+ def connect!
35
+ cert = File.read(@options[:cert])
36
+ context = OpenSSL::SSL::SSLContext.new
37
+ context.key = OpenSSL::PKey::RSA.new(cert, @options[:cert_pass])
38
+ context.cert = OpenSSL::X509::Certificate.new(cert)
39
+
40
+ @sock = TCPSocket.new(@options[:host], @options[:port])
41
+ @ssl = OpenSSL::SSL::SSLSocket.new(@sock, context)
42
+ @ssl.sync = true
43
+ @ssl.connect
44
+ end
45
+
46
+ #
47
+ # Close connection
48
+ #
49
+ def disconnect!
50
+ @ssl.close
51
+ @sock.close
52
+ @ssl = nil
53
+ @sock = nil
54
+ end
55
+
56
+ #
57
+ # Establishes a connection if needed and yields it
58
+ #
59
+ # Ex: open { |conn| conn.write('write to socket) }
60
+ #
61
+ def open(&block)
62
+ unless connected?
63
+ connect!
64
+ end
65
+
66
+ yield @ssl
67
+ end
68
+
69
+ #
70
+ # Write to the connection socket
71
+ #
72
+ def write(raw)
73
+ open { |conn| conn.write(raw) }
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,30 @@
1
+ module APND
2
+ #
3
+ # Daemon::Protocol handles incoming APNs
4
+ #
5
+ class Daemon::Protocol < ::EventMachine::Connection
6
+ attr_accessor :queue
7
+
8
+ def post_init
9
+ @address = Socket.unpack_sockaddr_in(self.get_peername)
10
+ ohai "#{@address.last}:#{@address.first} opened connection"
11
+ end
12
+
13
+ def unbind
14
+ ohai "#{@address.last}:#{@address.first} closed connection"
15
+ end
16
+
17
+ #
18
+ # Add incoming notification to the queue if it is valid
19
+ #
20
+ def receive_data(data)
21
+ (@buffer ||= "") << data
22
+ if notification = APND::Notification.valid?(@buffer)
23
+ ohai "#{@address.last}:#{@address.first} added new Notification to queue"
24
+ queue.push(notification)
25
+ else
26
+ ohai "#{@address.last}:#{@address.first} submitted invalid Notification"
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,13 @@
1
+ module APND
2
+ module Errors
3
+
4
+ #
5
+ # Raised if APN payload is larger than 256 bytes
6
+ #
7
+ class InvalidPayload < StandardError
8
+ def initialize(message)
9
+ super("Payload is larger than 256 bytes: '#{message}'")
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,139 @@
1
+ require 'socket'
2
+
3
+ module APND
4
+ #
5
+ # APND::Notification is the base class for creating new push notifications.
6
+ #
7
+ class Notification
8
+
9
+ class << self
10
+ attr_accessor :upstream_host
11
+ attr_accessor :upstream_port
12
+ end
13
+
14
+ self.upstream_host = APND.settings.notification.host
15
+ self.upstream_port = APND.settings.notification.port.to_i
16
+
17
+ attr_accessor :token, :alert, :badge, :sound, :custom
18
+
19
+ #
20
+ # Create a new APN
21
+ #
22
+ def self.create(params = {}, push = true)
23
+ notification = Notification.new(params)
24
+ notification.push! if push
25
+ notification
26
+ end
27
+
28
+ #
29
+ # Try to create a new Notification from raw data
30
+ # Used by Daemon::Protocol to validate incoming data
31
+ #
32
+ def self.valid?(data)
33
+ parse(data)
34
+ rescue
35
+ false
36
+ end
37
+
38
+ #
39
+ # Parse raw data into a new Notification
40
+ #
41
+ def self.parse(data)
42
+ buffer = data.dup
43
+ notification = Notification.new
44
+
45
+ header = buffer.slice!(0, 3).unpack('ccc')
46
+
47
+ if header[0] != 0
48
+ raise RuntimeError, "Invalid Notification header: #{header.inspect}"
49
+ end
50
+
51
+ notification.token = buffer.slice!(0, 32).unpack('H*').first
52
+
53
+ json_length = buffer.slice!(0, 2).unpack('CC')
54
+
55
+ json = buffer.slice!(0, json_length.last)
56
+
57
+ payload = JSON.parse(json)
58
+
59
+ %w[alert sound badge].each do |key|
60
+ if payload['aps'] && payload['aps'][key]
61
+ notification.send("#{key}=", payload['aps'][key])
62
+ end
63
+ end
64
+
65
+ payload.delete('aps')
66
+
67
+ unless payload.empty?
68
+ notification.custom = payload
69
+ end
70
+
71
+ notification
72
+ end
73
+
74
+ #
75
+ # Create a new Notification object from a hash
76
+ #
77
+ def initialize(params = {})
78
+ @token = params[:token]
79
+ @alert = params[:alert]
80
+ @badge = params[:badge]
81
+ @sound = params[:sound] || 'default'
82
+ @custom = params[:custom]
83
+ end
84
+
85
+ #
86
+ # Token in hex format
87
+ #
88
+ def hex_token
89
+ [self.token.delete(' ')].pack('H*')
90
+ end
91
+
92
+ #
93
+ # aps hash sent to Apple
94
+ #
95
+ def aps
96
+ aps = {}
97
+ aps['alert'] = self.alert if self.alert
98
+ aps['badge'] = self.badge.to_i if self.badge
99
+ aps['sound'] = self.sound if self.sound
100
+
101
+ output = { 'aps' => aps }
102
+
103
+ if self.custom
104
+ self.custom.each do |key, value|
105
+ output[key.to_s] = value
106
+ end
107
+ end
108
+ output
109
+ end
110
+
111
+ #
112
+ # Pushes notification to upstream host:port (default is localhost:22195)
113
+ #
114
+ def push!
115
+ socket = TCPSocket.new(self.class.upstream_host, self.class.upstream_port)
116
+ socket.write(to_bytes)
117
+ socket.close
118
+ end
119
+
120
+ #
121
+ # Returns the Notification's aps hash as json
122
+ #
123
+ def payload
124
+ return @payload if @payload
125
+ json = aps.to_json
126
+ raise APND::InvalidPayload.new(json) if json.size > 256
127
+ @payload = json
128
+ end
129
+
130
+ #
131
+ # Format the notification as a string for submission
132
+ # to Apple
133
+ #
134
+ def to_bytes
135
+ @bytes ||= "\0\0 %s\0%s%s" % [hex_token, payload.length.chr, payload]
136
+ end
137
+
138
+ end
139
+ end
@@ -0,0 +1,128 @@
1
+ module APND
2
+ #
3
+ # Settings for APND
4
+ #
5
+ class Settings
6
+
7
+ #
8
+ # Settings for APND::Daemon::AppleConnection
9
+ #
10
+ class AppleConnection
11
+
12
+ #
13
+ # Host used to connect to Apple
14
+ #
15
+ # Development: gateway.sandbox.push.apple.com
16
+ # Production: gateway.push.apple.com
17
+ #
18
+ attr_accessor :host
19
+
20
+ #
21
+ # Port used to connect to Apple
22
+ #
23
+ attr_accessor :port
24
+
25
+ #
26
+ # Path to APN cert for your application
27
+ #
28
+ attr_accessor :cert
29
+
30
+ #
31
+ # Password for APN cert, optional
32
+ #
33
+ attr_accessor :cert_pass
34
+
35
+ def initialize
36
+ @host = 'gateway.sandbox.push.apple.com'
37
+ @port = 2195
38
+ end
39
+ end
40
+
41
+ #
42
+ # Settings for APND::Daemon
43
+ #
44
+ class Daemon
45
+
46
+ #
47
+ # IP to bind APND::Daemon to
48
+ #
49
+ # Default: '0.0.0.0'
50
+ #
51
+ attr_accessor :bind
52
+
53
+ #
54
+ # Port APND::Daemon will run on
55
+ #
56
+ # Default: 22195
57
+ #
58
+ attr_accessor :port
59
+
60
+ #
61
+ # Path to APND::Daemon log
62
+ #
63
+ # Default: /var/log/apnd.log
64
+ #
65
+ attr_accessor :log_file
66
+
67
+ #
68
+ # Interval (in seconds) the queue will be processed
69
+ #
70
+ # Default: 30
71
+ #
72
+ attr_accessor :timer
73
+
74
+ def initialize
75
+ @timer = 30
76
+ @bind = '0.0.0.0'
77
+ @port = 22195
78
+ @log_file = '/var/log/apnd.log'
79
+ end
80
+ end
81
+
82
+ #
83
+ # Settings for APND::Notification
84
+ #
85
+ class Notification
86
+
87
+ #
88
+ # Host to send notification to, usually the one running APND::Daemon
89
+ #
90
+ # Default: localhost
91
+ #
92
+ attr_accessor :host
93
+
94
+ #
95
+ # Port to send notifications to
96
+ #
97
+ # Default: 22195
98
+ #
99
+ attr_accessor :port
100
+
101
+ def initialize
102
+ @host = 'localhost'
103
+ @port = 22195
104
+ end
105
+ end
106
+
107
+ #
108
+ # Returns the AppleConnection settings
109
+ #
110
+ def apple
111
+ @apple ||= APND::Settings::AppleConnection.new
112
+ end
113
+
114
+ #
115
+ # Returns the Daemon settings
116
+ #
117
+ def daemon
118
+ @daemon ||= APND::Settings::Daemon.new
119
+ end
120
+
121
+ #
122
+ # Returns the Notification settings
123
+ #
124
+ def notification
125
+ @notification ||= APND::Settings::Notification.new
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,11 @@
1
+ module APND
2
+ class Version
3
+ MAJOR = 0
4
+ MINOR = 0
5
+ TINY = 1
6
+
7
+ def self.to_s
8
+ [MAJOR, MINOR, TINY].join('.')
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ context "APND Daemon" do
4
+
5
+ end
@@ -0,0 +1,5 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ context "APND Notification" do
4
+
5
+ end
@@ -0,0 +1,28 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+
4
+ dir = File.dirname(File.expand_path(__FILE__))
5
+ $LOAD_PATH.unshift(File.join(dir, '..', 'lib'))
6
+ $LOAD_PATH.unshift(dir)
7
+
8
+ require 'apnd'
9
+
10
+ ##
11
+ # test/spec/mini 3
12
+ # http://gist.github.com/25455
13
+ # chris@ozmm.org
14
+ #
15
+ def context(*args, &block)
16
+ return super unless (name = args.first) && block
17
+ require 'test/unit'
18
+ klass = Class.new(defined?(ActiveSupport::TestCase) ? ActiveSupport::TestCase : Test::Unit::TestCase) do
19
+ def self.test(name, &block)
20
+ define_method("test_#{name.gsub(/\W/,'_')}", &block) if block
21
+ end
22
+ def self.xtest(*args) end
23
+ def self.setup(&block) define_method(:setup, &block) end
24
+ def self.teardown(&block) define_method(:teardown, &block) end
25
+ end
26
+ (class << klass; self end).send(:define_method, :name) { name.gsub(/\W/,'_') }
27
+ klass.class_eval &block
28
+ end
metadata ADDED
@@ -0,0 +1,130 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: apnd
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Joshua Priddle
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-09-29 00:00:00 -04:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: eventmachine
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :runtime
31
+ version_requirements: *id001
32
+ - !ruby/object:Gem::Dependency
33
+ name: json
34
+ prerelease: false
35
+ requirement: &id002 !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ segments:
40
+ - 0
41
+ version: "0"
42
+ type: :runtime
43
+ version_requirements: *id002
44
+ - !ruby/object:Gem::Dependency
45
+ name: daemons
46
+ prerelease: false
47
+ requirement: &id003 !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ segments:
52
+ - 0
53
+ version: "0"
54
+ type: :runtime
55
+ version_requirements: *id003
56
+ description: |
57
+
58
+ # APND
59
+
60
+ APND (Apple Push Notification Daemon) is a ruby library to send Apple Push
61
+ Notifications (APNs) to iPhones.
62
+
63
+ Apple recommends application developers create one connection to their
64
+ upstream push notification server, rather than creating one per notification.
65
+
66
+ APND acts as an intermediary between your application and Apple. Your
67
+ application's notifications are queued to APND, which are then sent to
68
+ Apple over a single connection.
69
+
70
+ Within ruby applications, `APND::Notification` can be used to send
71
+ notifications to a running APND instance or directly to Apple. A command
72
+ line utility, `apnd-push`, can be used to send single notifications for
73
+ testing purposes.
74
+
75
+ email: jpriddle@nevercraft.net
76
+ executables:
77
+ - apnd
78
+ - apnd-push
79
+ extensions: []
80
+
81
+ extra_rdoc_files:
82
+ - README.markdown
83
+ files:
84
+ - Rakefile
85
+ - README.markdown
86
+ - bin/apnd
87
+ - bin/apnd-push
88
+ - lib/apnd/daemon/apple_connection.rb
89
+ - lib/apnd/daemon/protocol.rb
90
+ - lib/apnd/daemon.rb
91
+ - lib/apnd/errors.rb
92
+ - lib/apnd/notification.rb
93
+ - lib/apnd/settings.rb
94
+ - lib/apnd/version.rb
95
+ - lib/apnd.rb
96
+ - test/daemon_test.rb
97
+ - test/notification_test.rb
98
+ - test/test_helper.rb
99
+ has_rdoc: true
100
+ homepage: http://github.com/itspriddle/apnd
101
+ licenses: []
102
+
103
+ post_install_message:
104
+ rdoc_options:
105
+ - --charset=UTF-8
106
+ require_paths:
107
+ - lib
108
+ required_ruby_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ segments:
113
+ - 0
114
+ version: "0"
115
+ required_rubygems_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ segments:
120
+ - 0
121
+ version: "0"
122
+ requirements: []
123
+
124
+ rubyforge_project:
125
+ rubygems_version: 1.3.6
126
+ signing_key:
127
+ specification_version: 3
128
+ summary: "APND: Apple Push Notification Daemon sends Apple Push Notifications to iPhones"
129
+ test_files: []
130
+