ass 0.0.21 → 0.0.24

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.
Files changed (8) hide show
  1. checksums.yaml +15 -0
  2. data/README.md +14 -2
  3. data/VERSION +1 -1
  4. data/ass.yml +3 -1
  5. data/lib/ass.rb +2 -404
  6. data/lib/ass/app.rb +277 -0
  7. data/lib/ass/conf.rb +165 -0
  8. metadata +57 -86
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ZTA5ZjhkMzc5ZjFlNThiNzQ3YjExOGJiMTI4MmJmMDEzNjU3MjFlYg==
5
+ data.tar.gz: !binary |-
6
+ NTJjOGI2M2IyMGI2NTUyNTcwYTFiZTNmYjhkODBkZjUxOGVmMWNkZA==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ NmFhYjAzNGJmZTA4MmYwOGJkMDU2ZWFiMjMyZWM4YzUwNmZlNTdiYTcyYzk3
10
+ MmI5ZjYzMGY3Y2U0MTYwYzI4MWRjYjMxNWNiNTRkYWMxMTU1OTFjMDdkYmRl
11
+ MDQ5NmUwNjZiMTZjNGUxMWRmN2UyZjdlOTNiMjkwNWM2YzIxN2E=
12
+ data.tar.gz: !binary |-
13
+ ZmI4NTFiMDdjNDQ4YTU2Y2ZjYWNiNTA1MDYzYTM1ODZkNWIxNTE2NGFiZTQ0
14
+ MTc5NmY1ZGJlYjg4YTViN2RkMTllYjNmNGQ2NjA3YWJlOWNmMTQ4M2U0OGY2
15
+ OGJlNjlkYjJkMzI0YjNmZDUxNmNjOGY2MGNkMjVkY2JmZTc4NmQ=
data/README.md CHANGED
@@ -116,6 +116,9 @@ when you run 'ass' first time, it will generate 'ass.yml' config file under curr
116
116
  timer: 0 # how often you run the cron job, unit: minute. when set with 0, means no cron job execute.
117
117
  user: admin # admin username
118
118
  pass: pass # admin password
119
+ flood: 1 # request time from same ip every one minute as Flood Attack
120
+ pempass: pempass # pem password
121
+ loglevel: info # logger level
119
122
  apps:
120
123
  - app1 ## appid you want to supprt APNS, ASS Server can give push notification support for many iOS apps, just list the appid here.
121
124
 
@@ -156,13 +159,11 @@ In AppDelegate file, add methods below to register device token
156
159
 
157
160
  - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
158
161
 
159
- if ([((AppDelegate *) [[UIApplication sharedApplication] delegate]) checkNetwork1]) {
160
162
  NSString *tokenAsString = [[[deviceToken description]
161
163
  stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]]
162
164
  stringByReplacingOccurrencesOfString:@" " withString:@""];
163
165
  NSLog(@"My token is: [%@]", tokenAsString);
164
166
  [self sendToken:tokenAsString];
165
- }
166
167
 
167
168
  }
168
169
 
@@ -211,6 +212,17 @@ open your web browser and access http://localhost:4567/ (localhost should be cha
211
212
 
212
213
  ![ass usage](https://raw.github.com/eiffelqiu/ass/master/doc/capture5.png)
213
214
 
215
+ 4. How to run ass in background?
216
+ -------
217
+
218
+ $ nohup ass
219
+
220
+ control + z to return to shell prompt
221
+
222
+ $ bg
223
+
224
+ now ass is running as a background service .
225
+
214
226
  Contributing to ass
215
227
  =======
216
228
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.21
1
+ 0.0.24
data/ass.yml CHANGED
@@ -3,8 +3,10 @@ mode: development
3
3
  log: off
4
4
  cron: cron
5
5
  timer: 0
6
- user: admim
6
+ user: admin
7
7
  pass: pass
8
8
  flood: 1
9
+ pempass: pempass
10
+ loglevel: info
9
11
  apps:
10
12
  - app1
data/lib/ass.rb CHANGED
@@ -1,406 +1,4 @@
1
1
  #encoding: utf-8
2
2
 
3
- require 'rubygems'
4
- require 'sinatra'
5
- require 'sequel'
6
- require 'socket'
7
- require 'openssl'
8
- require 'cgi'
9
- require 'rufus/scheduler'
10
- require 'eventmachine'
11
- require 'sinatra/base'
12
- require 'rack/mobile-detect'
13
- require 'yaml'
14
- require 'uri-handler'
15
- require 'net/http'
16
- require 'active_support'
17
- require 'json'
18
- require 'digest/sha2'
19
- require 'will_paginate'
20
- require 'will_paginate/sequel' # or data_mapper/sequel
21
- require 'uri'
22
- require 'sinatra/reloader' if development?
23
- require 'sinatra/synchrony'
24
-
25
- ############################################################
26
- ## Initilization Setup
27
- ############################################################
28
- LIBDIR = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
29
- ROOTDIR = File.expand_path(File.join(File.dirname(__FILE__), '..'))
30
- unless $LOAD_PATH.include?(LIBDIR)
31
- $LOAD_PATH << LIBDIR
32
- end
33
-
34
- unless File.exist?("#{Dir.pwd}/ass.yml") then
35
- puts 'create config file: ass.yml'
36
- system "cp #{ROOTDIR}/ass.yml #{Dir.pwd}/ass.yml"
37
- end
38
-
39
- unless File.exist?("#{Dir.pwd}/cron") then
40
- puts "create a demo 'cron' script"
41
- system "cp #{ROOTDIR}/cron #{Dir.pwd}/cron"
42
- end
43
-
44
- ############################################################
45
- ## Configuration Setup
46
- ############################################################
47
- env = ENV['SINATRA_ENV'] || "development"
48
- config = YAML.load_file("#{Dir.pwd}/ass.yml")
49
- $timer = "#{config['timer']}".to_i
50
- $cron = config['cron'] || 'cron'
51
- $port = "#{config['port']}".to_i || 4567
52
- $mode = config['mode'] || env
53
- $VERSION = File.open("#{ROOTDIR}/VERSION", "rb").read
54
- $apps = config['apps'] || []
55
- $log = config['log'] || 'off'
56
- $user = config['user'] || 'admin'
57
- $pass = config['pass'] || 'pass'
58
- $flood = "#{config['flood']}".to_i || 1 # default 1 minute
59
-
60
- $client_ip = '127.0.0.1'
61
- $last_access = 0
62
-
63
- ############################################################
64
- ## Certificate Key Setup
65
- ############################################################
66
-
67
- $certkey = {}
68
-
69
- def check_cert
70
- $apps.each { |app|
71
- unless File.exist?("#{Dir.pwd}/#{app}_#{$mode}.pem") then
72
- puts "Please provide #{app}_#{$mode}.pem under '#{Dir.pwd}/' directory"
73
- return false;
74
- else
75
- puts "'#{app}'s #{$mode} PEM: (#{app}_#{$mode}.pem)"
76
- certfile = File.read("#{Dir.pwd}/#{app}_#{$mode}.pem")
77
- openSSLContext = OpenSSL::SSL::SSLContext.new
78
- openSSLContext.cert = OpenSSL::X509::Certificate.new(certfile)
79
- openSSLContext.key = OpenSSL::PKey::RSA.new(certfile)
80
- $certkey["#{app}"] = openSSLContext
81
- end
82
- }
83
- return true
84
- end
85
-
86
- unless check_cert then
87
- html = <<-END
88
- 1: please provide certificate key pem file under current directory, name should be: appid_development.pem for development and appid_production.pem for production
89
- 2: edit your ass.yml under current directory
90
- 3: run ass
91
- 4: iOS Client: in AppDelegate file, didRegisterForRemoteNotificationsWithDeviceToken method should access url below:
92
- END
93
- $apps.each { |app|
94
- html << "'#{app}'s registration url: http://serverIP:#{$port}/v1/apps/#{app}/DeviceToken"
95
- }
96
- html << "5: Server: cron should access 'curl http://localhost:#{$port}/v1/app/push/{messages}/{pid}' to send push message"
97
- puts html
98
- exit
99
- else
100
- html = <<-END
101
- #{'*'*80}
102
- Apple Service Server(#{$VERSION}) is Running ...
103
- Push Notification Service: Enabled
104
- Mode: #{$mode}
105
- Port: #{$port}
106
- END
107
- html << "#{'*'*80}"
108
- html << "Cron Job: '#{Dir.pwd}/#{$cron}' script is running every #{$timer} #{($timer == 1) ? 'minute' : 'minutes'} " unless "#{$timer}".to_i == 0
109
- html << "\n"
110
- html << "access http://localhost:#{$port}/ for more information"
111
- html << "\n"
112
- html << "#{'*'*80}"
113
- puts html
114
- end
115
-
116
- ############################################################
117
- ## Sequel Database Setup
118
- ############################################################
119
-
120
- unless File.exist?("#{Dir.pwd}/ass-#{$mode}.db") then
121
- $DB = Sequel.connect("sqlite://#{Dir.pwd}/ass-#{$mode}.db")
122
- $DB.create_table :tokens do
123
- primary_key :id
124
- String :app, :unique => false, :null => false
125
- String :token, :unique => false, :null => false, :size => 100
126
- Time :created_at
127
- index [:app, :token]
128
- end
129
- $DB.create_table :pushes do
130
- primary_key :id
131
- String :pid, :unique => false, :null => false, :size => 100
132
- String :app, :unique => false, :null => false, :size => 30
133
- String :message, :unique => false, :null => false, :size => 107
134
- Time :created_at
135
- index [:pid, :app, :message]
136
- end
137
- else
138
- $DB = Sequel.connect("sqlite://#{Dir.pwd}/ass-#{$mode}.db")
139
- end
140
-
141
- WillPaginate.per_page = 10
142
-
143
- class Token < Sequel::Model
144
- Sequel.extension :pagination
145
- end
146
-
147
- class Push < Sequel::Model
148
- Sequel.extension :pagination
149
- end
150
-
151
- ############################################################
152
- ## Timer Job Setup
153
- ############################################################
154
- scheduler = Rufus::Scheduler.start_new
155
-
156
- unless $timer == 0 then
157
- scheduler.every "#{$timer}m" do
158
- puts "running job: '#{Dir.pwd}/#{$cron}' every #{$timer} #{($timer == 1) ? 'minute' : 'minutes'}"
159
- system "./#{$cron}"
160
- end
161
- end
162
-
163
- ############################################################
164
- ## Apple Service Server based on Sinatra
165
- ############################################################
166
-
167
- class App < Sinatra::Base
168
-
169
- register Sinatra::Synchrony
170
-
171
- use Rack::MobileDetect
172
-
173
- set :root, File.expand_path('../../', __FILE__)
174
- set :port, "#{$port}".to_i
175
- set :public_folder, File.dirname(__FILE__) + '/../public'
176
- set :views, File.dirname(__FILE__) + '/../views'
177
-
178
- helpers do
179
-
180
- def checkFlood?(req)
181
- if $client_ip != "#{req.ip}" then
182
- $client_ip = "#{req.ip}"
183
- return false
184
- else
185
- if $last_access == 0 then
186
- return false
187
- else
188
- return isFlood?
189
- end
190
- end
191
- end
192
-
193
- def isFlood?
194
- result = (Time.now - $last_access) < $flood * 60
195
- $last_access = Time.now
196
- return result
197
- end
198
-
199
- def iOS?
200
- result = case request.env['X_MOBILE_DEVICE']
201
- when /iPhone|iPod|iPad/ then
202
- true
203
- else false
204
- end
205
- return result
206
- end
207
-
208
- def authorized?
209
- @auth ||= Rack::Auth::Basic::Request.new(request.env)
210
- @auth.provided? && @auth.basic? && @auth.credentials && @auth.credentials == ["#{$user}", "#{$pass}"]
211
- end
212
-
213
- def protected!
214
- unless authorized?
215
- response['WWW-Authenticate'] = %(Basic realm="Restricted Area")
216
- throw(:halt, [401, "Oops... we need your login name & password\n"])
217
- end
218
- end
219
- end
220
-
221
- configure :production, :development do
222
- if "#{$log}".strip == 'on' then
223
- enable :logging
224
- end
225
- end
226
-
227
- if "#{$mode}".strip == 'development' then
228
- set :show_exceptions, true
229
- set :dump_errors, true
230
- else
231
- set :show_exceptions, false
232
- set :dump_errors, false
233
- end
234
-
235
- get '/' do
236
- erb :index
237
- end
238
-
239
- get '/about' do
240
- erb :about
241
- end
242
-
243
- not_found do
244
- erb :not_found
245
- end
246
-
247
- error do
248
- @error = "";
249
- @error = params['captures'].first.inspect if "#{$mode}".strip == 'development'
250
- end
251
-
252
- post '/v1/send' do
253
- app = params[:app]
254
- message = CGI::escape(params[:message] || "")
255
- pid = "#{Time.now.to_i}"
256
- # begin
257
- # url = URI.parse("http://localhost:#{$port}/v1/apps/#{app}/push")
258
- # post_args1 = { :alert => "#{message}".encode('UTF-8'), :pid => "#{pid}" }
259
- # Net::HTTP.post_form(url, post_args1)
260
- # rescue =>err
261
- # puts "#{err.class} ##{err}"
262
- # end
263
- system "curl http://localhost:#{$port}/v1/apps/#{app}/push/#{message}/#{pid}"
264
- redirect '/v1/admin/push' if (params[:app] and params[:message])
265
- end
266
-
267
- get "/v1/admin/:db" do
268
- protected!
269
- db = params[:db] || 'token'
270
- page = 1
271
- page = params[:page].to_i if params[:page]
272
- if (db == 'token') then
273
- @o = []
274
- $apps.each_with_index { |app, index|
275
- @o << Token.where(:app => app).order(:id).reverse.paginate(page, 20)
276
- }
277
- erb :token
278
- elsif (db == 'push') then
279
- @p = []
280
- $apps.each_with_index { |app, index|
281
- @p << Push.where(:app => app).order(:id).reverse.paginate(page, 20)
282
- }
283
- erb :push
284
- else
285
- erb :not_found
286
- end
287
- end
288
-
289
- $apps.each { |app|
290
-
291
- ## register token api
292
- get "/v1/apps/#{app}/:token" do
293
- if (("#{params[:token]}".length == 64) and iOS? and checkFlood?(request) ) then
294
- puts "[#{params[:token]}] was added to '#{app}'" if "#{$mode}".strip == 'development'
295
- o = Token.first(:app => app, :token => params[:token])
296
- unless o
297
- Token.insert(
298
- :app => app,
299
- :token => params[:token],
300
- :created_at => Time.now
301
- )
302
- end
303
- end
304
- end
305
-
306
- ## http POST method push api
307
- post "/v1/apps/#{app}/push" do
308
- protected! unless request.host == 'localhost'
309
- message = CGI::unescape(params[:alert] || "")[0..107]
310
- badge = 1
311
- puts "params[:badge] = [#{params[:badge]}]"
312
- badge = params[:badge].to_i if params[:badge] and params[:badge] != ''
313
- sound = CGI::unescape(params[:sound] || "")
314
- extra = CGI::unescape(params[:extra] || "")
315
-
316
- puts "#{badge} : #{message} extra: #{extra}" if "#{$mode}".strip == 'development'
317
- pid = params[:pid]
318
-
319
- puts "'#{message}' was sent to (#{app}) with pid: [#{pid}], badge:#{badge} , sound: #{sound}, extra:#{extra}" if "#{$mode}".strip == 'development'
320
-
321
- @tokens = Token.where(:app => "#{app}")
322
- @exist = Push.first(:pid => "#{pid}", :app => "#{app}")
323
-
324
- unless @exist
325
-
326
- Push.insert(:pid => pid, :message => message, :created_at => Time.now, :app => "#{app}" )
327
-
328
- openSSLContext = $certkey["#{app}"]
329
- # Connect to port 2195 on the server.
330
- sock = nil
331
- if $mode == 'production' then
332
- sock = TCPSocket.new('gateway.push.apple.com', 2195)
333
- else
334
- sock = TCPSocket.new('gateway.sandbox.push.apple.com', 2195)
335
- end
336
- # do our SSL handshaking
337
- sslSocket = OpenSSL::SSL::SSLSocket.new(sock, openSSLContext)
338
- sslSocket.connect
339
-
340
- # write our packet to the stream
341
- @tokens.each do |o|
342
- tokenText = o[:token]
343
- # pack the token to convert the ascii representation back to binary
344
- tokenData = [tokenText].pack('H*')
345
- # construct the payload
346
- po = {:aps => {:alert => "#{message}", :badge => badge, :sound => "#{sound}"}, :extra => "#{extra}"}
347
- payload = ActiveSupport::JSON.encode(po)
348
- # construct the packet
349
- packet = [0, 0, 32, tokenData, 0, payload.length, payload].pack("ccca*cca*")
350
- # read our certificate and set up our SSL context
351
- sslSocket.write(packet)
352
- end
353
- # cleanup
354
- sslSocket.close
355
- sock.close
356
- end
357
- end
358
-
359
- ## http GET method push api
360
- get "/v1/apps/#{app}/push/:message/:pid" do
361
- protected! unless request.host == 'localhost'
362
- message = CGI::unescape(params[:message])
363
- puts message if "#{$mode}".strip == 'development'
364
- pid = params[:pid]
365
-
366
- puts "'#{message}' was sent to (#{app}) with pid: [#{pid}]" if "#{$mode}".strip == 'development'
367
-
368
- @tokens = Token.where(:app => "#{app}")
369
- @exist = Push.first(:pid => "#{pid}", :app => "#{app}")
370
-
371
- unless @exist
372
-
373
- Push.insert(:pid => pid, :message => message, :created_at => Time.now, :app => "#{app}" )
374
-
375
- openSSLContext = $certkey["#{app}"]
376
- # Connect to port 2195 on the server.
377
- sock = nil
378
- if $mode == 'production' then
379
- sock = TCPSocket.new('gateway.push.apple.com', 2195)
380
- else
381
- sock = TCPSocket.new('gateway.sandbox.push.apple.com', 2195)
382
- end
383
- # do our SSL handshaking
384
- sslSocket = OpenSSL::SSL::SSLSocket.new(sock, openSSLContext)
385
- sslSocket.connect
386
-
387
- # write our packet to the stream
388
- @tokens.each do |o|
389
- tokenText = o[:token]
390
- # pack the token to convert the ascii representation back to binary
391
- tokenData = [tokenText].pack('H*')
392
- # construct the payload
393
- po = {:aps => {:alert => "#{message}", :badge => 1}}
394
- payload = ActiveSupport::JSON.encode(po)
395
- # construct the packet
396
- packet = [0, 0, 32, tokenData, 0, payload.length, payload].pack("ccca*cca*")
397
- # read our certificate and set up our SSL context
398
- sslSocket.write(packet)
399
- end
400
- # cleanup
401
- sslSocket.close
402
- sock.close
403
- end
404
- end
405
- }
406
- end
3
+ require 'ass/conf'
4
+ require 'ass/app'