apnd 0.0.1

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