spns 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (8) hide show
  1. data/LICENSE.txt +20 -0
  2. data/README.md +48 -0
  3. data/VERSION +1 -0
  4. data/bin/spns +34 -0
  5. data/cron +30 -0
  6. data/lib/spns.rb +130 -0
  7. data/spns.yml +8 -0
  8. metadata +235 -0
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Eiffel Qiu
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,48 @@
1
+ spns
2
+ =======
3
+ Simple Push Notification Server written with Sinatra and Sequel(Sqlite3)
4
+
5
+ Prerequisites
6
+ =======
7
+ Ruby
8
+ -------
9
+ spns is a ruby gem, require ruby installed on your mac machine. Since now all Mac OSX system preinstalled ruby enviroment, that's not a big issue.
10
+
11
+ Rubygem(latest)
12
+ -------
13
+ spns require latest rubygem installed, so you need to update your rubygem to latest version, run command below to update your rubygem to the latest one.
14
+
15
+ $ sudo gem update --system # double dash option
16
+
17
+ Installation
18
+ =======
19
+
20
+ $ sudo gem install spns
21
+
22
+ Usage
23
+ =======
24
+ 1. under the current directory, provide single pem file combined with certificate and key, HOWTO ([Check this link](http://www.raywenderlich.com/3443/apple-push-notification-services-tutorial-part-12))
25
+
26
+ 2. edit spns.yml
27
+
28
+ 3. provide a cron script under current directory, default named "cron" according to spns.yml
29
+
30
+ 4. start spns server, default port is 4567(sinatra's default port)
31
+
32
+ $ spns
33
+
34
+ == Contributing to spns
35
+
36
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
37
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
38
+ * Fork the project.
39
+ * Start a feature/bugfix branch.
40
+ * Commit and push until you are happy with your contribution.
41
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
42
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
43
+
44
+ == Copyright
45
+
46
+ Copyright (c) 2012 Eiffel Qiu. See LICENSE.txt for
47
+ further details.
48
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.1
data/bin/spns ADDED
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems' unless defined?(Gem)
3
+ require 'sinatra/base'
4
+ require 'thread'
5
+
6
+ class Object
7
+ alias sh system
8
+ end
9
+
10
+ unless RUBY_PLATFORM =~ /darwin/i
11
+ $stderr.puts "what? you do not have a MAC, dude? Shame on you!!!"
12
+ end
13
+
14
+ lib_dir = File.expand_path(File.join(File.dirname(__FILE__),'..','lib'))
15
+ root_dir = File.expand_path(File.join(File.dirname(__FILE__),'..'))
16
+ unless $LOAD_PATH.include?(lib_dir)
17
+ $LOAD_PATH << lib_dir
18
+ end
19
+
20
+ if File.exist?('push.db') then system "rm push.db" end
21
+
22
+ unless File.exist?("#{Dir.pwd}/spns.yml") then
23
+ puts 'create spns.yml'
24
+ system "cp #{root_dir}/spns.yml #{Dir.pwd}/spns.yml"
25
+ end
26
+
27
+ unless File.exist?("#{Dir.pwd}/cron") then
28
+ puts 'create cron script'
29
+ system "cp #{root_dir}/cron #{Dir.pwd}/cron"
30
+ end
31
+
32
+ require 'spns'
33
+
34
+ App.run!
data/cron ADDED
@@ -0,0 +1,30 @@
1
+ require 'rubygems'
2
+ require 'sequel'
3
+ require 'socket'
4
+ require 'openssl'
5
+ require 'cgi'
6
+ require 'rufus/scheduler'
7
+ require 'feedzirra'
8
+ require 'yaml'
9
+
10
+ # fetching a single feed
11
+ feed = Feedzirra::Feed.fetch_and_parse("http://earthquake.usgs.gov/earthquakes/feed/atom/1.0/hour")
12
+
13
+ messages = ''
14
+ big = 0
15
+ pid = ''
16
+ feed.entries.each do |o|
17
+ t = o.title.split(',')
18
+ pids = o.id.split(':')
19
+ pid = pids[3]
20
+ m = t[0].split(' ')
21
+ if m[1].to_i >= 1 then
22
+ big = m[1].to_i if big < m[1].to_i
23
+ messages = CGI::escape("Earthquake:#{o.title}")
24
+ puts "Earthquake:#{o.title}"
25
+ end
26
+ end
27
+ puts "curl http://192.168.1.105:4567/v1/app/push/#{messages}/#{pid}"
28
+ if big >= 1 then
29
+ system "curl http://192.168.1.105:4567/v1/app/push/#{messages}/#{pid}"
30
+ end
data/lib/spns.rb ADDED
@@ -0,0 +1,130 @@
1
+ require 'rubygems'
2
+ require 'sinatra'
3
+ require 'sequel'
4
+ require 'socket'
5
+ require 'openssl'
6
+ require 'cgi'
7
+ require 'rufus/scheduler'
8
+ require 'eventmachine'
9
+ require 'sinatra/base'
10
+ require 'yaml'
11
+
12
+ ############################################################
13
+ ## Configuration Setup
14
+ ############################################################
15
+ env = ENV['SINATRA_ENV'] || "development"
16
+ config = YAML.load_file("#{Dir.pwd}/spns.yml")
17
+ $timer = config['timer'] || 1
18
+ $cron = config['cron'] || 'cron'
19
+ $port = config['port'] || 4567
20
+ $mode = config['mode'] || env
21
+ $certificate = config["#{env}"][0]['certificate'] || 'ck.pem'
22
+
23
+ ############################################################
24
+ ## Certificate Key Setup
25
+ ############################################################
26
+
27
+ unless File.exist?("#{Dir.pwd}/#{$certificate}") then
28
+ puts "1: please provide certificate key pem file under current directory"
29
+ puts "2: edit your spns.yml under current directory first"
30
+ puts "3: run spns"
31
+ exit
32
+ end
33
+
34
+ $cert = File.read("#{Dir.pwd}/#{$certificate}")
35
+ $openSSLContext = OpenSSL::SSL::SSLContext.new
36
+ $openSSLContext.cert = OpenSSL::X509::Certificate.new($cert)
37
+ $openSSLContext.key = OpenSSL::PKey::RSA.new($cert)
38
+
39
+ ############################################################
40
+ ## Sequel Database Setup
41
+ ############################################################
42
+
43
+ DB = Sequel.connect("sqlite://#{Dir.pwd}/push.db")
44
+
45
+ DB.create_table :tokens do
46
+ primary_key :id
47
+ String :app, :unique => true, :null => false
48
+ String :token, :unique => true, :null => false, :size => 100
49
+ index [:app, :token]
50
+ end
51
+
52
+ DB.create_table :pushes do
53
+ primary_key :id
54
+ String :pid, :unique => true, :null => false, :size => 100
55
+ index :pid
56
+ end
57
+
58
+ Token = DB[:tokens]
59
+ Push = DB[:pushes]
60
+
61
+ ############################################################
62
+ ## Timer Job Setup
63
+ ############################################################
64
+ scheduler = Rufus::Scheduler.start_new
65
+
66
+ scheduler.every "#{$timer}m" do
67
+ puts "running job: '#{Dir.pwd}/#{$cron}' every #{$timer} minutes"
68
+ system "#{Dir.pwd}/#{$cron}"
69
+ end
70
+
71
+ ############################################################
72
+ ## Simple Push Notification Server based on Sinatra
73
+ ############################################################
74
+
75
+ class App < Sinatra::Base
76
+
77
+ set :port, "#{$port}".to_i
78
+
79
+ get '/' do
80
+ puts "Simple Push Notification Server"
81
+ end
82
+
83
+ get '/v1/app/:token' do
84
+ o = Token.first(:token => params[:token])
85
+ unless o
86
+ Token.insert(
87
+ :app => 'app',
88
+ :token => params[:token]
89
+ )
90
+ end
91
+ end
92
+
93
+ get '/v1/app/push/:message/:pid' do
94
+ message = CGI::unescape(params[:message])
95
+ pid = params[:pid]
96
+ @push = Token.where(:app => 'app')
97
+ @exist = Push.first(:pid => pid )
98
+
99
+ unless @exist
100
+ # Connect to port 2195 on the server.
101
+ sock = nil
102
+ if $mode == 'production' then
103
+ sock = TCPSocket.new('gateway.push.apple.com', 2195)
104
+ else
105
+ sock = TCPSocket.new('gateway.sandbox.push.apple.com', 2195)
106
+ end
107
+ # do our SSL handshaking
108
+ sslSocket = OpenSSL::SSL::SSLSocket.new(sock, $openSSLContext)
109
+ sslSocket.connect
110
+ #Push.create( :pid => pid )
111
+ Push.insert( :pid => pid )
112
+ # write our packet to the stream
113
+ @push.each do |o|
114
+ tokenText = o[:token]
115
+ # pack the token to convert the ascii representation back to binary
116
+ tokenData = [tokenText].pack('H*')
117
+ # construct the payload
118
+ payload = "{\"aps\":{\"alert\":\"#{message}\", \"badge\":1}}"
119
+ # construct the packet
120
+ packet = [0, 0, 32, tokenData, 0, payload.length, payload].pack("ccca*cca*")
121
+ # read our certificate and set up our SSL context
122
+ sslSocket.write(packet)
123
+ end
124
+ # cleanup
125
+ sslSocket.close
126
+ sock.close
127
+ end
128
+ end
129
+
130
+ end
data/spns.yml ADDED
@@ -0,0 +1,8 @@
1
+ port: 4567
2
+ mode: development
3
+ cron: cron
4
+ timer: 1 # minute
5
+ development:
6
+ - certificate : ck_dev.pem
7
+ production:
8
+ - certificate : ck_prod.pem
metadata ADDED
@@ -0,0 +1,235 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: spns
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Eiffel Qiu
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-10 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: sinatra
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: sequel
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rufus-scheduler
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: feedzirra
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: eventmachine
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: activesupport
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: 3.2.8
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: 3.2.8
110
+ - !ruby/object:Gem::Dependency
111
+ name: shoulda
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: rdoc
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ~>
132
+ - !ruby/object:Gem::Version
133
+ version: '3.12'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ~>
140
+ - !ruby/object:Gem::Version
141
+ version: '3.12'
142
+ - !ruby/object:Gem::Dependency
143
+ name: bundler
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ~>
148
+ - !ruby/object:Gem::Version
149
+ version: 1.2.0
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ~>
156
+ - !ruby/object:Gem::Version
157
+ version: 1.2.0
158
+ - !ruby/object:Gem::Dependency
159
+ name: jeweler
160
+ requirement: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ~>
164
+ - !ruby/object:Gem::Version
165
+ version: 1.8.4
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ~>
172
+ - !ruby/object:Gem::Version
173
+ version: 1.8.4
174
+ - !ruby/object:Gem::Dependency
175
+ name: simplecov
176
+ requirement: !ruby/object:Gem::Requirement
177
+ none: false
178
+ requirements:
179
+ - - ! '>='
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ type: :development
183
+ prerelease: false
184
+ version_requirements: !ruby/object:Gem::Requirement
185
+ none: false
186
+ requirements:
187
+ - - ! '>='
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
190
+ description: Simple Push Notification Server
191
+ email: eiffelqiu@gmail.com
192
+ executables:
193
+ - spns
194
+ extensions: []
195
+ extra_rdoc_files:
196
+ - LICENSE.txt
197
+ - README.md
198
+ files:
199
+ - LICENSE.txt
200
+ - README.md
201
+ - VERSION
202
+ - cron
203
+ - lib/spns.rb
204
+ - spns.yml
205
+ - !binary |-
206
+ YmluL3NwbnM=
207
+ homepage: http://github.com/eiffelqiu/spns
208
+ licenses:
209
+ - MIT
210
+ post_install_message:
211
+ rdoc_options: []
212
+ require_paths:
213
+ - lib
214
+ required_ruby_version: !ruby/object:Gem::Requirement
215
+ none: false
216
+ requirements:
217
+ - - ! '>='
218
+ - !ruby/object:Gem::Version
219
+ version: '0'
220
+ segments:
221
+ - 0
222
+ hash: -3524247809256863557
223
+ required_rubygems_version: !ruby/object:Gem::Requirement
224
+ none: false
225
+ requirements:
226
+ - - ! '>='
227
+ - !ruby/object:Gem::Version
228
+ version: '0'
229
+ requirements: []
230
+ rubyforge_project:
231
+ rubygems_version: 1.8.21
232
+ signing_key:
233
+ specification_version: 3
234
+ summary: Simple Push Notification Server
235
+ test_files: []