├── VERSION ├── .gitignore ├── rails └── init.rb ├── init.rb ├── test ├── test_helper.rb └── apple_push_notification_test.rb ├── CHANGELOG.rdoc ├── lib ├── apple_push_notification │ └── acts_as_pushable.rb └── apple_push_notification.rb ├── LICENSE ├── Rakefile ├── apple_push_notification.gemspec └── Readme.markdown /VERSION: -------------------------------------------------------------------------------- 1 | 0.2.0 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | pkg 3 | -------------------------------------------------------------------------------- /rails/init.rb: -------------------------------------------------------------------------------- 1 | require "apple_push_notification" 2 | -------------------------------------------------------------------------------- /init.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + "/rails/init.rb" -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'active_support' 3 | require 'active_support/test_case' 4 | -------------------------------------------------------------------------------- /CHANGELOG.rdoc: -------------------------------------------------------------------------------- 1 | == 0.1.1 released 2009-11-10 2 | 3 | * Moved from model to "acts_as_pushable". 4 | * Updated readme for new interface. 5 | 6 | == 0.1.0 released 2009-11-10 7 | 8 | * Initial release. 9 | -------------------------------------------------------------------------------- /test/apple_push_notification_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ApplePushNotificationTest < ActiveSupport::TestCase 4 | # Someone could be awesome and write a test 5 | test "the truth" do 6 | assert true 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/apple_push_notification/acts_as_pushable.rb: -------------------------------------------------------------------------------- 1 | module ApplePushNotification 2 | module ActsAsPushable 3 | end 4 | end 5 | 6 | ActiveRecord::Base.class_eval do 7 | def self.acts_as_pushable(device_token_field = :device_token) 8 | class_inheritable_reader :acts_as_push_options 9 | write_inheritable_attribute :acts_as_push_options, { 10 | :device_token_field => device_token_field 11 | } 12 | include ApplePushNotification 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 Sam Soffes 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'rake/testtask' 3 | require 'rake/rdoctask' 4 | 5 | desc 'Default: run unit tests.' 6 | task :default => :test 7 | 8 | desc 'Test the apple_push_notification plugin.' 9 | Rake::TestTask.new(:test) do |t| 10 | t.libs << 'lib' 11 | t.libs << 'test' 12 | t.pattern = 'test/**/*_test.rb' 13 | t.verbose = true 14 | end 15 | 16 | desc 'Generate documentation for the apple_push_notification plugin.' 17 | Rake::RDocTask.new(:rdoc) do |rdoc| 18 | rdoc.rdoc_dir = 'rdoc' 19 | rdoc.title = 'ApplePushNotification' 20 | rdoc.options << '--line-numbers' << '--inline-source' 21 | rdoc.rdoc_files.include('README') 22 | rdoc.rdoc_files.include('lib/**/*.rb') 23 | end 24 | 25 | begin 26 | require 'jeweler' 27 | Jeweler::Tasks.new do |gemspec| 28 | gemspec.name = "apple_push_notification" 29 | gemspec.summary = "Rails plugin for Apple Push Notifications" 30 | gemspec.description = "Rails plugin for Apple Push Notifications" 31 | gemspec.email = "sam@samsoff.es" 32 | gemspec.homepage = "http://github.com/samsoffes/apple_push_notification" 33 | gemspec.authors = ["Sam Soffes"] 34 | end 35 | Jeweler::GemcutterTasks.new 36 | rescue LoadError 37 | puts "Jeweler not available. Install it with: sudo gem install jeweler -s http://gemcutter.org" 38 | end 39 | -------------------------------------------------------------------------------- /apple_push_notification.gemspec: -------------------------------------------------------------------------------- 1 | # Generated by jeweler 2 | # DO NOT EDIT THIS FILE DIRECTLY 3 | # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command 4 | # -*- encoding: utf-8 -*- 5 | 6 | Gem::Specification.new do |s| 7 | s.name = %q{apple_push_notification} 8 | s.version = "0.2.0" 9 | 10 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 11 | s.authors = ["Sam Soffes"] 12 | s.date = %q{2009-11-10} 13 | s.description = %q{Rails plugin for Apple Push Notifications} 14 | s.email = %q{sam@samsoff.es} 15 | s.extra_rdoc_files = [ 16 | "LICENSE" 17 | ] 18 | s.files = [ 19 | ".gitignore", 20 | "CHANGELOG.rdoc", 21 | "LICENSE", 22 | "Rakefile", 23 | "Readme.markdown", 24 | "VERSION", 25 | "apple_push_notification.gemspec", 26 | "init.rb", 27 | "lib/apple_push_notification.rb", 28 | "lib/apple_push_notification/acts_as_pushable.rb", 29 | "rails/init.rb", 30 | "test/apple_push_notification_test.rb", 31 | "test/test_helper.rb" 32 | ] 33 | s.homepage = %q{http://github.com/samsoffes/apple_push_notification} 34 | s.rdoc_options = ["--charset=UTF-8"] 35 | s.require_paths = ["lib"] 36 | s.rubygems_version = %q{1.3.5} 37 | s.summary = %q{Rails plugin for Apple Push Notifications} 38 | s.test_files = [ 39 | "test/apple_push_notification_test.rb", 40 | "test/test_helper.rb" 41 | ] 42 | 43 | if s.respond_to? :specification_version then 44 | current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION 45 | s.specification_version = 3 46 | 47 | if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then 48 | else 49 | end 50 | else 51 | end 52 | end 53 | 54 | -------------------------------------------------------------------------------- /lib/apple_push_notification.rb: -------------------------------------------------------------------------------- 1 | require 'socket' 2 | require 'openssl' 3 | 4 | module ApplePushNotification 5 | 6 | def self.extended(base) 7 | # Added device_token attribute if not included by acts_as_pushable 8 | unless base.respond_to?(:acts_as_push_options) 9 | base.class_eval do 10 | attr_accessor :device_token 11 | end 12 | end 13 | end 14 | 15 | APN_PORT = 2195 16 | @@apn_cert = nil 17 | @@apn_host = nil 18 | 19 | def self.apn_enviroment 20 | @@apn_enviroment 21 | end 22 | 23 | def self.apn_development? 24 | @@apn_enviroment != :production 25 | end 26 | 27 | def self.apn_production? 28 | @@apn_enviroment == :production 29 | end 30 | 31 | def self.apn_enviroment= enviroment 32 | @@apn_enviroment = enviroment.to_sym 33 | @@apn_host = self.apn_production? ? "gateway.push.apple.com" : "gateway.sandbox.push.apple.com" 34 | cert = self.apn_production? ? "apn_production.pem" : "apn_development.pem" 35 | path = File.join(File.expand_path(RAILS_ROOT), "config", "certs", cert) 36 | @@apn_cert = File.exists?(path) ? File.read(path) : nil 37 | raise "Missing apple push notification certificate in #{path}" unless @@apn_cert 38 | end 39 | 40 | self.apn_enviroment = :development 41 | 42 | def send_notification options 43 | raise "Missing apple push notification certificate" unless @@apn_cert 44 | 45 | ctx = OpenSSL::SSL::SSLContext.new 46 | ctx.key = OpenSSL::PKey::RSA.new(@@apn_cert) 47 | ctx.cert = OpenSSL::X509::Certificate.new(@@apn_cert) 48 | 49 | s = TCPSocket.new(@@apn_host, APN_PORT) 50 | ssl = OpenSSL::SSL::SSLSocket.new(s, ctx) 51 | ssl.sync = true 52 | ssl.connect 53 | 54 | ssl.write(self.apn_message_for_sending(options)) 55 | ssl.close 56 | s.close 57 | rescue SocketError => error 58 | raise "Error while sending notifications: #{error}" 59 | end 60 | 61 | def self.send_notification token, options = {} 62 | d = Object.new 63 | d.extend ApplePushNotification 64 | d.device_token = token 65 | d.send_notification options 66 | end 67 | 68 | protected 69 | 70 | def apn_message_for_sending options 71 | json = ApplePushNotification::apple_json_array options 72 | message = "\0\0 #{self.device_token_hexa}\0#{json.length.chr}#{json}" 73 | raise "The maximum size allowed for a notification payload is 256 bytes." if message.size.to_i > 256 74 | message 75 | end 76 | 77 | def device_token_hexa 78 | # Use `device_token` as the method to get the token from 79 | # unless it is overridde from acts_as_pushable 80 | apn_token_field = "device_token" 81 | if respond_to?(:acts_as_push_options) 82 | apn_token_field = acts_as_push_options[:device_token_field] 83 | end 84 | token = send(apn_token_field.to_sym) 85 | raise "Cannot send push notification without device token" if !token || token.empty? 86 | [token.delete(' ')].pack('H*') 87 | end 88 | 89 | def self.apple_json_array options 90 | result = {} 91 | result['aps'] = {} 92 | result['aps']['alert'] = options[:alert].to_s if options[:alert] 93 | result['aps']['badge'] = options[:badge].to_i if options[:badge] 94 | result['aps']['sound'] = options[:sound] if options[:sound] and options[:sound].is_a? String 95 | result['aps']['sound'] = 'default' if options[:sound] and options[:sound].is_a? TrueClass 96 | result.to_json 97 | end 98 | 99 | end 100 | 101 | require File.dirname(__FILE__) + "/apple_push_notification/acts_as_pushable" 102 | -------------------------------------------------------------------------------- /Readme.markdown: -------------------------------------------------------------------------------- 1 | This library is very old. I recommend using [Houston](https://github.com/nomad/houston) instead. 2 | 3 | --- 4 | 5 | # Apple Push Notification 6 | 7 | This plugin helps you use the Apple Push Notification system. 8 | 9 | ## Installing 10 | 11 | Install as a gem: 12 | 13 | # Add to config/environment.rb: 14 | config.gem "apple_push_notification", :source => "http://gemcutter.org/" 15 | 16 | # At command prompt: 17 | $ sudo rake gems:install 18 | 19 | or as a plugin: 20 | 21 | $ script/plugin install git://github.com/samsoffes/apple_push_notification.git 22 | 23 | Once you have installed ApplePushNotification, run the following command: 24 | 25 | $ rake apn:migrate 26 | 27 | ## Converting Your Certificate 28 | 29 | Once you have the certificate from Apple for your application, export your key 30 | and the apple certificate as p12 files. Here's how: 31 | 32 | 1. Click the disclosure arrow next to your certificate in Keychain Access and select the certificate and the key. 33 | 2. Right click and choose `Export 2 items...`. 34 | 3. Choose the p12 format from the drop down and name it `cert.p12`. 35 | 36 | Now covert the p12 file to a pem file: 37 | 38 | $ openssl pkcs12 -in cert.p12 -out apn_development.pem -nodes -clcerts && rm -f cert.p12 39 | 40 | Put `apn_development.pem` in `config/certs` in your rails app. For production, name your certificate `apn_production.pem` and put it in the same directory. See the environment section for more about environments. 41 | 42 | ## Environment 43 | 44 | By default, the development environment will always be used. This makes it easy to test your app in production before your iPhone application is approved and your production certificate is active. You can easily override this by adding this line in an initializer or environment file. 45 | 46 | ApplePushNotification.enviroment = Rails.env.to_sym 47 | 48 | You can also simply set `ApplePushNotification.enviroment` to `:development` or `:production`. Setting the `ApplePushNotification.enviroment` chooses the appropriate certificate in your `certs` folder and Apple push notification server. 49 | 50 | ## Usage 51 | 52 | You can use ApplePushNotification with an ActiveRecord model, standalone, or on any object. 53 | 54 | ### ActiveRecord 55 | 56 | Just add `acts_as_pushable` to your model. 57 | 58 | class Device < ActiveRecord::Base 59 | acts_as_pushable 60 | end 61 | 62 | You can then send a notification like this 63 | 64 | d.send_notification :alert => "I heart Rails" 65 | 66 | The `send_notification` method accepts a hash of options for the notification parameters. See the Parameters section for more information. 67 | 68 | Your model must have a `device_token` attribute. If you wish to change this to something else (like `device` for example), simply pass it like this `acts_as_pushable :device`. 69 | 70 | ### Standalone 71 | 72 | Simply call `ApplePushNotification.send_notification` and pass the device token as the first parameter and the hash of notification options as the second (see the Parameters section for more information). 73 | 74 | $ script/console 75 | >> token = "XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX" 76 | >> ApplePushNotification.send_notification token, :alert => "Hello World!", :badge => 5, :sound => true 77 | => nil 78 | 79 | ### Any Object 80 | 81 | You can extend ApplePushNotification with any class. It will look for the `device_token` method when sending the notification. When ApplePushNotification is extended, if `device_token=` isn't defined, getters and setters are generated. 82 | 83 | $ script/console 84 | >> token = "XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX" 85 | >> d = Object.new 86 | >> d.extend ApplePushNotification 87 | >> d.device_token = token 88 | >> d.send_notification :alert => "So flexible" 89 | => nil 90 | 91 | See the Parameters section for more information on what `send_notification` accepts. 92 | 93 | ## Parameters 94 | 95 | The following notification parameters can be defined in the options hash: 96 | 97 | * `alert` - text displayed to the use 98 | * `sound` - this can be the filename (i.e. `explosion.aiff`) or `true` which will play the default notification sound 99 | * `badge` - this must be an integer 100 | 101 | ### Notes 102 | 103 | * The spaces in `device_token` are optional and will be ignored. 104 | 105 | Copyright (c) 2009 [Sam Soffes](http://samsoff.es). Released under the MIT license. Forked from Fabien Penso. 106 | --------------------------------------------------------------------------------