anjlab-ruby-smpp 0.6.3 → 0.6.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 422a4789de23b5d5d553e358c14f9fc51084a563
4
+ data.tar.gz: 11ee81c9199fe2538ca0ef75ad07373800961cf7
5
+ SHA512:
6
+ metadata.gz: 0c8a7f4e2b3349b639095a7cefbb1638a3a83a78e6232e9be37f21e536e6353b9441cdaf9f08915ab6de462d72f15a895ad834ec4f45371f771c714d63a83885
7
+ data.tar.gz: 46b026a70ed509c048634f8bd69026f415866b6eb71190ca213b8e181c4d3651bd40f0158f1763e86f8b524d28586b2ff8d10dea956abc1b27a76359a6a9cbee
@@ -0,0 +1,28 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+ *.gem
8
+
9
+ ## EMACS
10
+ *~
11
+ \#*
12
+ .\#*
13
+
14
+ ## VIM
15
+ *.swp
16
+
17
+ ## PROJECT::GENERAL
18
+ coverage
19
+ rdoc
20
+ pkg
21
+ tags
22
+ .bundle
23
+ vendor/ruby
24
+ .rvmrc
25
+
26
+ ## PROJECT::SPECIFIC
27
+ *.log
28
+ nbproject
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.3
5
+ - 2.0.0
6
+
data/Gemfile CHANGED
@@ -1,8 +1,4 @@
1
- source 'http://rubygems.org'
1
+ source 'https://rubygems.org'
2
2
 
3
- gem "eventmachine", ">= 0.10.0"
4
-
5
- group :development do
6
- gem "jeweler"
7
- gem "rake"
8
- end
3
+ # Specify your gem's dependencies in ruby-smpp.gemspec
4
+ gemspec
@@ -1,18 +1,21 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ ruby-smpp (0.6.0)
5
+ eventmachine (>= 0.10.0)
6
+
1
7
  GEM
2
- remote: http://rubygems.org/
8
+ remote: https://rubygems.org/
3
9
  specs:
4
10
  eventmachine (0.12.10)
5
- git (1.2.5)
6
- jeweler (1.6.4)
7
- bundler (~> 1.0)
8
- git (>= 1.2.5)
9
- rake
10
11
  rake (0.9.2)
12
+ test-unit (2.5.4)
11
13
 
12
14
  PLATFORMS
13
15
  ruby
14
16
 
15
17
  DEPENDENCIES
16
- eventmachine (>= 0.10.0)
17
- jeweler
18
+ bundler (~> 1.3)
18
19
  rake
20
+ ruby-smpp!
21
+ test-unit
@@ -1,5 +1,7 @@
1
1
  = Ruby-SMPP
2
2
 
3
+ {<img src="https://travis-ci.org/raykrueger/ruby-smpp.png" />}[https://travis-ci.org/raykrueger/ruby-smpp]
4
+
3
5
  == DESCRIPTION:
4
6
 
5
7
  Ruby-SMPP is a Ruby implementation of the SMPP v3.4 protocol. It is suitable for writing gateway daemons that communicate with SMSCs for sending and receiving SMS messages.
@@ -24,7 +26,7 @@ The SMPP 3.4 protocol spec can be downloaded here: http://smsforum.net/SMPP_v3_4
24
26
 
25
27
  === Testing/Sample Code
26
28
 
27
- Logica provides an SMPP simulator that you can download from http://opensmpp.logica.com. You can
29
+ Logica provides an SMPP simulator that you can download from http://opensmpp.logica.com. You can
28
30
  also sign up for a demo SMPP account at one of the many bulk-SMS providers out there.
29
31
 
30
32
  For a quick test, download smscsim.jar and smpp.jar from the Logica site, and start the simulator by typing:
@@ -35,7 +37,7 @@ Then type 1 (start simulation), and enter 6000 for port number. The simulator th
35
37
 
36
38
  ruby sample_gateway.rb
37
39
 
38
- You will be able to send MT messages from the sample gateway terminal window by typing the message body. In the simulator terminal window you should see SMPP PDUs being sent from the sample gateway.
40
+ You will be able to send MT messages from the sample gateway terminal window by typing the message body. In the simulator terminal window you should see SMPP PDUs being sent from the sample gateway.
39
41
 
40
42
  You can also send MO messages from the simulator to the sample gateway by typing 7 (log to screen off) and then 4 (send message). MO messages received by the sample gateway will be logged to ./sms_gateway.log.
41
43
 
@@ -47,18 +49,18 @@ You can also send MO messages from the simulator to the sample gateway by typing
47
49
 
48
50
  == BASIC USAGE:
49
51
 
50
- Start the transceiver. Receive delegate callbacks whenever incoming messages or delivery reports arrive. Send messages with Transceiver#send_mt.
52
+ Start the transceiver. Receive delegate callbacks whenever incoming messages or delivery reports arrive. Send messages with Transceiver#send_mt.
51
53
 
52
54
  # connect to SMSC
53
- tx = EventMachine::run do
55
+ tx = EventMachine::run do
54
56
  $tx = EventMachine::connect(
55
- host,
56
- port,
57
- Smpp::Transceiver,
58
- config, # a property hash
57
+ host,
58
+ port,
59
+ Smpp::Transceiver,
60
+ config, # a property hash
59
61
  delegate # delegate class that will receive callbacks on MOs and DRs and other events
60
62
  end
61
-
63
+
62
64
  # send a message
63
65
  tx.send_mt(id, from, to, body)
64
66
 
data/Rakefile CHANGED
@@ -1,24 +1,4 @@
1
- require File.expand_path('../config/environment', __FILE__)
2
- require 'rake'
3
-
4
- begin
5
- require 'jeweler'
6
- Jeweler::Tasks.new do |gem|
7
- # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
8
- gem.name = "anjlab-ruby-smpp"
9
- gem.summary = %Q{Ruby implementation of the SMPP protocol, based on EventMachine.}
10
- gem.description = gem.summary + " SMPP is a protocol that allows ordinary people outside the mobile network to exchange SMS messages directly with mobile operators."
11
- gem.email = "raykrueger@gmail.com"
12
- gem.homepage = "http://github.com/raykrueger/ruby-smpp"
13
- gem.authors = ["Ray Krueger", "August Z. Flatby"]
14
- gem.rubyforge_project = gem.name
15
-
16
- gem.extra_rdoc_files = ["README.rdoc", "CHANGELOG", "CONTRIBUTORS.txt"]
17
- end
18
- Jeweler::GemcutterTasks.new
19
- rescue LoadError
20
- puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
21
- end
1
+ require "bundler/gem_tasks"
22
2
 
23
3
  require 'rake/testtask'
24
4
  Rake::TestTask.new(:test) do |test|
@@ -27,27 +7,4 @@ Rake::TestTask.new(:test) do |test|
27
7
  test.verbose = true
28
8
  end
29
9
 
30
- begin
31
- require 'rcov/rcovtask'
32
- Rcov::RcovTask.new do |test|
33
- test.libs << 'test'
34
- test.pattern = 'test/**/*_test.rb'
35
- test.verbose = true
36
- end
37
- rescue LoadError
38
- task :rcov do
39
- abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
40
- end
41
- end
42
-
43
10
  task :default => :test
44
-
45
- require 'rake/rdoctask'
46
- Rake::RDocTask.new do |rdoc|
47
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
-
49
- rdoc.rdoc_dir = 'rdoc'
50
- rdoc.title = "ruby-smpp #{version}"
51
- rdoc.rdoc_files.include('README*', "CHANGELOG", "CONTRIBUTORS.txt", "LICENSE")
52
- rdoc.rdoc_files.include('lib/**/*.rb')
53
- end
@@ -1,98 +1,26 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
- # -*- encoding: utf-8 -*-
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'smpp/version'
5
5
 
6
- Gem::Specification.new do |s|
7
- s.name = "anjlab-ruby-smpp"
8
- s.version = "0.6.3"
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "anjlab-ruby-smpp"
8
+ spec.version = Smpp::VERSION
9
+ spec.authors = ["Ray Krueger", "August Z. Flatby"]
10
+ spec.email = ["raykrueger@gmail.com"]
11
+ spec.description = %q{Ruby implementation of the SMPP protocol, based on EventMachine. SMPP is a protocol that allows ordinary people outside the mobile network to exchange SMS messages directly with mobile operators.}
12
+ spec.summary = %q{Ruby implementation of the SMPP protocol, based on EventMachine.}
13
+ spec.homepage = "http://github.com/raykrueger/ruby-smpp"
14
+ spec.license = "MIT"
9
15
 
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Ray Krueger", "August Z. Flatby"]
12
- s.date = "2012-06-27"
13
- s.description = "Ruby implementation of the SMPP protocol, based on EventMachine. SMPP is a protocol that allows ordinary people outside the mobile network to exchange SMS messages directly with mobile operators."
14
- s.email = "raykrueger@gmail.com"
15
- s.extra_rdoc_files = [
16
- "CHANGELOG",
17
- "CONTRIBUTORS.txt",
18
- "README.rdoc"
19
- ]
20
- s.files = [
21
- "CHANGELOG",
22
- "CONTRIBUTORS.txt",
23
- "Gemfile",
24
- "Gemfile.lock",
25
- "LICENSE",
26
- "README.rdoc",
27
- "Rakefile",
28
- "VERSION",
29
- "anjlab-ruby-smpp.gemspec",
30
- "config/environment.rb",
31
- "examples/PDU1.example",
32
- "examples/PDU2.example",
33
- "examples/sample_gateway.rb",
34
- "examples/sample_smsc.rb",
35
- "lib/smpp.rb",
36
- "lib/smpp/base.rb",
37
- "lib/smpp/encoding/utf8_encoder.rb",
38
- "lib/smpp/optional_parameter.rb",
39
- "lib/smpp/pdu/base.rb",
40
- "lib/smpp/pdu/bind_base.rb",
41
- "lib/smpp/pdu/bind_receiver.rb",
42
- "lib/smpp/pdu/bind_receiver_response.rb",
43
- "lib/smpp/pdu/bind_resp_base.rb",
44
- "lib/smpp/pdu/bind_transceiver.rb",
45
- "lib/smpp/pdu/bind_transceiver_response.rb",
46
- "lib/smpp/pdu/bind_transmitter.rb",
47
- "lib/smpp/pdu/bind_transmitter_response.rb",
48
- "lib/smpp/pdu/deliver_sm.rb",
49
- "lib/smpp/pdu/deliver_sm_response.rb",
50
- "lib/smpp/pdu/enquire_link.rb",
51
- "lib/smpp/pdu/enquire_link_response.rb",
52
- "lib/smpp/pdu/generic_nack.rb",
53
- "lib/smpp/pdu/submit_multi.rb",
54
- "lib/smpp/pdu/submit_multi_response.rb",
55
- "lib/smpp/pdu/submit_sm.rb",
56
- "lib/smpp/pdu/submit_sm_response.rb",
57
- "lib/smpp/pdu/unbind.rb",
58
- "lib/smpp/pdu/unbind_response.rb",
59
- "lib/smpp/receiver.rb",
60
- "lib/smpp/server.rb",
61
- "lib/smpp/transceiver.rb",
62
- "lib/sms.rb",
63
- "test/delegate.rb",
64
- "test/encoding_test.rb",
65
- "test/optional_parameter_test.rb",
66
- "test/pdu_parsing_test.rb",
67
- "test/receiver_test.rb",
68
- "test/responsive_delegate.rb",
69
- "test/server.rb",
70
- "test/smpp_test.rb",
71
- "test/submit_sm_test.rb",
72
- "test/transceiver_test.rb"
73
- ]
74
- s.homepage = "http://github.com/raykrueger/ruby-smpp"
75
- s.require_paths = ["lib"]
76
- s.rubyforge_project = "anjlab-ruby-smpp"
77
- s.rubygems_version = "1.8.17"
78
- s.summary = "Ruby implementation of the SMPP protocol, based on EventMachine."
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
79
20
 
80
- if s.respond_to? :specification_version then
81
- s.specification_version = 3
21
+ spec.add_dependency "eventmachine", ">= 0.10.0"
82
22
 
83
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
84
- s.add_runtime_dependency(%q<eventmachine>, [">= 0.10.0"])
85
- s.add_development_dependency(%q<jeweler>, [">= 0"])
86
- s.add_development_dependency(%q<rake>, [">= 0"])
87
- else
88
- s.add_dependency(%q<eventmachine>, [">= 0.10.0"])
89
- s.add_dependency(%q<jeweler>, [">= 0"])
90
- s.add_dependency(%q<rake>, [">= 0"])
91
- end
92
- else
93
- s.add_dependency(%q<eventmachine>, [">= 0.10.0"])
94
- s.add_dependency(%q<jeweler>, [">= 0"])
95
- s.add_dependency(%q<rake>, [">= 0"])
96
- end
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "test-unit"
97
26
  end
98
-
@@ -20,15 +20,15 @@ module KeyboardHandler
20
20
  def receive_line(data)
21
21
  sender, receiver, *body_parts = data.split
22
22
  unless sender && receiver && body_parts.size > 0
23
- puts "Syntax: <sender> <receiver> <message body>"
23
+ puts "Syntax: <sender> <receiver> <message body>"
24
24
  else
25
25
  body = body_parts.join(' ')
26
- puts "Sending MT from #{sender} to #{receiver}: #{body}"
26
+ puts "Sending MT from #{sender} to #{receiver}: #{body}"
27
27
  SampleGateway.send_mt(sender, receiver, body)
28
28
  end
29
29
  prompt
30
30
  end
31
-
31
+
32
32
  def prompt
33
33
  print "MT: "
34
34
  $stdout.flush
@@ -36,16 +36,16 @@ module KeyboardHandler
36
36
  end
37
37
 
38
38
  class SampleGateway
39
-
40
- # MT id counter.
39
+
40
+ # MT id counter.
41
41
  @@mt_id = 0
42
-
42
+
43
43
  # expose SMPP transceiver's send_mt method
44
44
  def self.send_mt(*args)
45
45
  @@mt_id += 1
46
46
  @@tx.send_mt(@@mt_id, *args)
47
47
  end
48
-
48
+
49
49
  def logger
50
50
  Smpp::Base.logger
51
51
  end
@@ -53,22 +53,22 @@ class SampleGateway
53
53
  def start(config)
54
54
  # The transceiver sends MT messages to the SMSC. It needs a storage with Hash-like
55
55
  # semantics to map SMSC message IDs to your own message IDs.
56
- pdr_storage = {}
56
+ pdr_storage = {}
57
57
 
58
58
  # Run EventMachine in loop so we can reconnect when the SMSC drops our connection.
59
59
  puts "Connecting to SMSC..."
60
60
  loop do
61
- EventMachine::run do
61
+ EventMachine::run do
62
62
  @@tx = EventMachine::connect(
63
- config[:host],
64
- config[:port],
65
- Smpp::Transceiver,
66
- config,
63
+ config[:host],
64
+ config[:port],
65
+ Smpp::Transceiver,
66
+ config,
67
67
  self # delegate that will receive callbacks on MOs and DRs and other events
68
- )
68
+ )
69
69
  print "MT: "
70
70
  $stdout.flush
71
-
71
+
72
72
  # Start consuming MT messages (in this case, from the console)
73
73
  # Normally, you'd hook this up to a message queue such as Starling
74
74
  # or ActiveMQ via STOMP.
@@ -78,8 +78,8 @@ class SampleGateway
78
78
  sleep 5
79
79
  end
80
80
  end
81
-
82
- # ruby-smpp delegate methods
81
+
82
+ # ruby-smpp delegate methods
83
83
 
84
84
  def mo_received(transceiver, pdu)
85
85
  logger.info "Delegate: mo_received: from #{pdu.source_addr} to #{pdu.destination_addr}: #{pdu.short_message}"
@@ -101,19 +101,19 @@ class SampleGateway
101
101
  logger.info "Delegate: transceiver bound"
102
102
  end
103
103
 
104
- def unbound(transceiver)
104
+ def unbound(transceiver)
105
105
  logger.info "Delegate: transceiver unbound"
106
106
  EventMachine::stop_event_loop
107
107
  end
108
-
108
+
109
109
  end
110
110
 
111
111
  # Start the Gateway
112
- begin
113
- puts "Starting SMS Gateway. Please check the log at #{LOGFILE}"
112
+ begin
113
+ puts "Starting SMS Gateway. Please check the log at #{LOGFILE}"
114
114
 
115
115
  # SMPP properties. These parameters work well with the Logica SMPP simulator.
116
- # Consult the SMPP spec or your mobile operator for the correct settings of
116
+ # Consult the SMPP spec or your mobile operator for the correct settings of
117
117
  # the other properties.
118
118
  config = {
119
119
  :host => '127.0.0.1',
@@ -129,9 +129,9 @@ begin
129
129
  :source_address_range => '',
130
130
  :destination_address_range => '',
131
131
  :enquire_link_delay_secs => 10
132
- }
132
+ }
133
133
  gw = SampleGateway.new
134
- gw.start(config)
134
+ gw.start(config)
135
135
  rescue Exception => ex
136
136
  puts "Exception in SMS Gateway: #{ex} at #{ex.backtrace.join("\n")}"
137
137
  end
@@ -1,5 +1,5 @@
1
1
  # SMPP v3.4 subset implementation.
2
- # SMPP is a short message peer-to-peer protocol typically used to communicate
2
+ # SMPP is a short message peer-to-peer protocol typically used to communicate
3
3
  # with SMS Centers (SMSCs) over TCP/IP.
4
4
  #
5
5
  # August Z. Flatby
@@ -10,6 +10,7 @@ require 'logger'
10
10
  $:.unshift(File.dirname(__FILE__))
11
11
  require 'smpp/base.rb'
12
12
  require 'smpp/transceiver.rb'
13
+ require 'smpp/transmitter.rb'
13
14
  require 'smpp/receiver.rb'
14
15
  require 'smpp/optional_parameter'
15
16
  require 'smpp/pdu/base.rb'
@@ -5,13 +5,13 @@ require 'eventmachine'
5
5
 
6
6
  module Smpp
7
7
  class InvalidStateException < Exception; end
8
-
8
+
9
9
  class Base < EventMachine::Connection
10
10
  include Smpp
11
-
11
+
12
12
  # :bound or :unbound
13
13
  attr_accessor :state
14
-
14
+
15
15
  def initialize(config, delegate)
16
16
  @state = :unbound
17
17
  @config = config
@@ -19,7 +19,7 @@ module Smpp
19
19
  @delegate = delegate
20
20
 
21
21
  # Array of un-acked MT message IDs indexed by sequence number.
22
- # As soon as we receive SubmitSmResponse we will use this to find the
22
+ # As soon as we receive SubmitSmResponse we will use this to find the
23
23
  # associated message ID, and then create a pending delivery report.
24
24
  @ack_ids = {}
25
25
 
@@ -31,11 +31,11 @@ module Smpp
31
31
  def unbound?
32
32
  @state == :unbound
33
33
  end
34
-
34
+
35
35
  def bound?
36
36
  @state == :bound
37
37
  end
38
-
38
+
39
39
  def Base.logger
40
40
  @@logger
41
41
  end
@@ -47,8 +47,8 @@ module Smpp
47
47
  def logger
48
48
  @@logger
49
49
  end
50
-
51
-
50
+
51
+
52
52
  # invoked by EventMachine when connected
53
53
  def post_init
54
54
  # send Bind PDU if we are a binder (eg
@@ -68,9 +68,10 @@ module Smpp
68
68
  # method named: periodic_call_method
69
69
  def start_enquire_link_timer(delay_secs)
70
70
  logger.info "Starting enquire link timer (with #{delay_secs}s interval)"
71
- EventMachine::PeriodicTimer.new(delay_secs) do
71
+ timer = EventMachine::PeriodicTimer.new(delay_secs) do
72
72
  if error?
73
73
  logger.warn "Link timer: Connection is in error state. Disconnecting."
74
+ timer.cancel
74
75
  close_connection
75
76
  elsif unbound?
76
77
  logger.warn "Link is unbound, waiting until next #{delay_secs} interval before querying again"
@@ -81,7 +82,7 @@ module Smpp
81
82
  rval = defined?(periodic_call_method) ? periodic_call_method : true
82
83
 
83
84
  # only send an OK if this worked
84
- write_pdu Pdu::EnquireLink.new if rval
85
+ write_pdu Pdu::EnquireLink.new if rval
85
86
  end
86
87
  end
87
88
  end
@@ -108,31 +109,33 @@ module Smpp
108
109
  process_pdu(pdu) if pdu
109
110
  rescue Exception => e
110
111
  logger.error "Error receiving data: #{e}\n#{e.backtrace.join("\n")}"
111
- if @delegate.respond_to?(:data_error)
112
- @delegate.data_error(e)
113
- end
112
+ run_callback(:data_error, e)
114
113
  end
115
114
 
116
115
  end
117
116
  end
118
-
117
+
119
118
  # EventMachine::Connection#unbind
120
119
  # Invoked by EM when connection is closed. Delegates should consider
121
120
  # breaking the event loop and reconnect when they receive this callback.
122
121
  def unbind
123
- if @delegate.respond_to?(:unbound)
124
- @delegate.unbound(self)
125
- end
122
+ run_callback(:unbound, self)
126
123
  end
127
-
124
+
128
125
  def send_unbind
129
126
  write_pdu Pdu::Unbind.new
130
127
  @state = :unbound
131
128
  end
132
129
 
130
+ def run_callback(cb, *args)
131
+ if @delegate.respond_to?(cb)
132
+ @delegate.send(cb, *args)
133
+ end
134
+ end
135
+
133
136
  # process common PDUs
134
137
  # returns true if no further processing necessary
135
- def process_pdu(pdu)
138
+ def process_pdu(pdu)
136
139
  case pdu
137
140
  when Pdu::EnquireLinkResponse
138
141
  # nop
@@ -142,7 +145,7 @@ module Smpp
142
145
  @state = :unbound
143
146
  write_pdu(Pdu::UnbindResponse.new(pdu.sequence_number, Pdu::Base::ESME_ROK))
144
147
  close_connection
145
- when Pdu::UnbindResponse
148
+ when Pdu::UnbindResponse
146
149
  logger.info "Unbound OK. Closing connection."
147
150
  close_connection
148
151
  when Pdu::GenericNack
@@ -154,15 +157,11 @@ module Smpp
154
157
  logger.debug "ESM CLASS #{pdu.esm_class}"
155
158
  if pdu.esm_class != 4
156
159
  # MO message
157
- if @delegate.respond_to?(:mo_received)
158
- @delegate.mo_received(self, pdu)
159
- end
160
+ run_callback(:mo_received, self, pdu)
160
161
  else
161
162
  # Delivery report
162
- if @delegate.respond_to?(:delivery_report_received)
163
- @delegate.delivery_report_received(self, pdu)
164
- end
165
- end
163
+ run_callback(:delivery_report_received, self, pdu)
164
+ end
166
165
  write_pdu(Pdu::DeliverSmResponse.new(pdu.sequence_number))
167
166
  rescue => e
168
167
  logger.warn "Send Receiver Temporary App Error due to #{e.inspect} raised in delegate"
@@ -173,19 +172,20 @@ module Smpp
173
172
  when Pdu::Base::ESME_ROK
174
173
  logger.debug "Bound OK."
175
174
  @state = :bound
176
- if @delegate.respond_to?(:bound)
177
- @delegate.bound(self)
178
- end
175
+ run_callback(:bound, self)
179
176
  when Pdu::Base::ESME_RINVPASWD
180
177
  logger.warn "Invalid password."
181
- # scheduele the connection to close, which eventually will cause the unbound() delegate
178
+ # schedule the connection to close, which eventually will cause the unbound() delegate
182
179
  # method to be invoked.
180
+ run_callback(:invalid_password, self)
183
181
  close_connection
184
182
  when Pdu::Base::ESME_RINVSYSID
185
183
  logger.warn "Invalid system id."
184
+ run_callback(:invalid_system_id, self)
186
185
  close_connection
187
186
  else
188
187
  logger.warn "Unexpected BindTransceiverResponse. Command status: #{pdu.command_status}"
188
+ run_callback(:unexpected_error, self)
189
189
  close_connection
190
190
  end
191
191
  when Pdu::SubmitSmResponse
@@ -195,14 +195,10 @@ module Smpp
195
195
  end
196
196
  if pdu.command_status != Pdu::Base::ESME_ROK
197
197
  logger.error "Error status in SubmitSmResponse: #{pdu.command_status}"
198
- if @delegate.respond_to?(:message_rejected)
199
- @delegate.message_rejected(self, mt_message_id, pdu)
200
- end
198
+ run_callback(:message_rejected, self, mt_message_id, pdu)
201
199
  else
202
200
  logger.info "Got OK SubmitSmResponse (#{pdu.message_id} -> #{mt_message_id})"
203
- if @delegate.respond_to?(:message_accepted)
204
- @delegate.message_accepted(self, mt_message_id, pdu)
205
- end
201
+ run_callback(:message_accepted, self, mt_message_id, pdu)
206
202
  end
207
203
  when Pdu::SubmitMultiResponse
208
204
  mt_message_id = @ack_ids[pdu.sequence_number]
@@ -211,42 +207,61 @@ module Smpp
211
207
  end
212
208
  if pdu.command_status != Pdu::Base::ESME_ROK
213
209
  logger.error "Error status in SubmitMultiResponse: #{pdu.command_status}"
214
- if @delegate.respond_to?(:message_rejected)
215
- @delegate.message_rejected(self, mt_message_id, pdu)
216
- end
210
+ run_callback(:message_rejected, self, mt_message_id, pdu)
217
211
  else
218
212
  logger.info "Got OK SubmitMultiResponse (#{pdu.message_id} -> #{mt_message_id})"
219
- if @delegate.respond_to?(:message_accepted)
220
- @delegate.message_accepted(self, mt_message_id, pdu)
221
- end
213
+ run_callback(:message_accepted, self, mt_message_id, pdu)
222
214
  end
223
215
  when Pdu::BindReceiverResponse
224
216
  case pdu.command_status
225
217
  when Pdu::Base::ESME_ROK
226
218
  logger.debug "Bound OK."
227
219
  @state = :bound
228
- if @delegate.respond_to?(:bound)
229
- @delegate.bound(self)
230
- end
220
+ run_callback(:bound, self)
221
+ when Pdu::Base::ESME_RINVPASWD
222
+ logger.warn "Invalid password."
223
+ run_callback(:invalid_password, self)
224
+ # scheduele the connection to close, which eventually will cause the unbound() delegate
225
+ # method to be invoked.
226
+ close_connection
227
+ when Pdu::Base::ESME_RINVSYSID
228
+ logger.warn "Invalid system id."
229
+ run_callback(:invalid_system_id, self)
230
+ close_connection
231
+ else
232
+ logger.warn "Unexpected BindReceiverResponse. Command status: #{pdu.command_status}"
233
+ run_callback(:unexpected_error, self)
234
+ close_connection
235
+ end
236
+ when Pdu::BindTransmitterResponse
237
+ case pdu.command_status
238
+ when Pdu::Base::ESME_ROK
239
+ logger.debug "Bound OK."
240
+ @state = :bound
241
+ run_callback(:bound, self)
231
242
  when Pdu::Base::ESME_RINVPASWD
232
243
  logger.warn "Invalid password."
233
- # scheduele the connection to close, which eventually will cause the unbound() delegate
244
+ run_callback(:invalid_password, self)
245
+ # schedule the connection to close, which eventually will cause the unbound() delegate
234
246
  # method to be invoked.
235
247
  close_connection
236
248
  when Pdu::Base::ESME_RINVSYSID
237
249
  logger.warn "Invalid system id."
250
+ run_callback(:invalid_system_id, self)
238
251
  close_connection
239
252
  else
240
253
  logger.warn "Unexpected BindReceiverResponse. Command status: #{pdu.command_status}"
254
+ run_callback(:unexpected_error, self)
241
255
  close_connection
242
256
  end
243
257
  else
244
258
  logger.warn "(#{self.class.name}) Received unexpected PDU: #{pdu.to_human}."
259
+ run_callback(:unexpected_pdu, self, pdu)
245
260
  close_connection
246
261
  end
247
262
  end
248
263
 
249
- private
264
+ private
250
265
  def write_pdu(pdu)
251
266
  logger.debug "<- #{pdu.to_human}"
252
267
  hex_debug pdu.data, "<- "
@@ -256,12 +271,12 @@ module Smpp
256
271
  def read_pdu(data)
257
272
  pdu = nil
258
273
  # we may either receive a new request or a response to a previous response.
259
- begin
274
+ begin
260
275
  pdu = Pdu::Base.create(data)
261
276
  if !pdu
262
277
  logger.warn "Not able to parse PDU!"
263
278
  else
264
- logger.debug "-> " + pdu.to_human
279
+ logger.debug "-> " + pdu.to_human
265
280
  end
266
281
  hex_debug data, "-> "
267
282
  rescue Exception => ex
@@ -278,7 +293,7 @@ module Smpp
278
293
  def Base.hex_debug(data, prefix = "")
279
294
  logger.debug do
280
295
  message = "Hex dump follows:\n"
281
- hexdump(data).each_line do |line|
296
+ hexdump(data).each_line do |line|
282
297
  message << (prefix + line.chomp + "\n")
283
298
  end
284
299
  message
@@ -303,6 +318,6 @@ module Smpp
303
318
  }
304
319
  output << ' '*(((2+width-ascii.size)*(2*group+1))/group.to_f).ceil+ascii
305
320
  output[1..-1]
306
- end
321
+ end
307
322
  end
308
323
  end
@@ -5,7 +5,7 @@ module Smpp
5
5
 
6
6
  # This class is not required by smpp.rb at all, you need to bring it in yourself.
7
7
  # This class also requires iconv, you'll need to ensure it is installed.
8
- class Utf8Encoder
8
+ class Utf8Encoder
9
9
 
10
10
  EURO_TOKEN = "_X_EURO_X_"
11
11
 
@@ -23,7 +23,10 @@ class Smpp::OptionalParameter
23
23
  tag, length, remaining_bytes = data.unpack('H4na*')
24
24
  tag = tag.hex
25
25
 
26
- raise "invalid data, cannot parse optional parameters" if tag == 0 or length.nil?
26
+ if tag == 0 || length.nil?
27
+ Smpp::Base.logger.error "invalid data, cannot parse optional parameters tag: #{tag} length:#{length}"
28
+ length = length.to_i
29
+ end
27
30
 
28
31
  value = remaining_bytes.slice!(0...length)
29
32
 
@@ -115,7 +115,7 @@ module Smpp::Pdu
115
115
  def Base.optional_parameters_to_buffer(optionals)
116
116
  output = ""
117
117
  optionals.each do |tag, optional_param|
118
- length = optional_param.value.length
118
+ length = optional_param.value.to_s.length
119
119
  buffer = []
120
120
  buffer += [tag >> 8, tag & 0xff]
121
121
  buffer += [length >> 8, length & 0xff]
@@ -114,12 +114,20 @@ class Smpp::Pdu::DeliverSm < Smpp::Pdu::Base
114
114
  # For example, Tele2 (Norway):
115
115
  # "<msisdn><shortcode>?id:10ea34755d3d4f7a20900cdb3349e549 sub:001 dlvrd:001 submit date:0611011228 done date:0611011230 stat:DELIVRD err:000 Text:abc'!10ea34755d3d4f7a20900cdb3349e549"
116
116
  if options[:esm_class] == 4
117
+ # id is in the mandatory part
117
118
  msg_ref_match = short_message.match(/id:([^ ]*)/)
119
+ # id is not found, search it in the optional part
120
+ msg_ref_match = remaining_bytes.match(/id:([^ ]*)/) if !msg_ref_match
121
+
118
122
  if msg_ref_match
119
123
  options[:msg_reference] = msg_ref_match[1]
120
124
  end
121
125
 
126
+ # stat is the mandatory part
122
127
  stat_match = short_message.match(/stat:([^ ]*)/)
128
+ # stat is not found, search it in the optional part
129
+ stat_match = remaining_bytes.match(/stat:([^ ]*)/) if !stat_match
130
+
123
131
  if stat_match
124
132
  options[:stat] = stat_match[1]
125
133
  end
@@ -13,7 +13,7 @@ class Smpp::Server < Smpp::Base
13
13
  # a proc to invoke for delivery reports,
14
14
  # and optionally a hash-like storage for pending delivery reports.
15
15
  def initialize(config, received_messages = [], sent_messages = [])
16
- super(config)
16
+ super(config, nil)
17
17
  @state = :unbound
18
18
  @received_messages = received_messages
19
19
  @sent_messages = sent_messages
@@ -0,0 +1,64 @@
1
+ # The SMPP Transmitter maintains a unidirectional connection to an SMSC.
2
+ # Provide a config hash with connection options to get started.
3
+ # See the sample_gateway.rb for examples of config values.
4
+
5
+ class Smpp::Transmitter < Smpp::Base
6
+
7
+ attr_reader :ack_ids
8
+
9
+ # Send an MT SMS message. Delegate will receive message_accepted callback when SMSC
10
+ # acknowledges, or the message_rejected callback upon error
11
+ def send_mt(message_id, source_addr, destination_addr, short_message, options={})
12
+ logger.debug "Sending MT: #{short_message}"
13
+ if @state == :bound
14
+ pdu = Pdu::SubmitSm.new(source_addr, destination_addr, short_message, options)
15
+ write_pdu(pdu)
16
+
17
+ # keep the message ID so we can associate the SMSC message ID with our message
18
+ # when the response arrives.
19
+ @ack_ids[pdu.sequence_number] = message_id
20
+ else
21
+ raise InvalidStateException, "Transmitter is unbound. Cannot send MT messages."
22
+ end
23
+ end
24
+
25
+ def send_concat_mt(message_id, source_addr, destination_addr, message, options = {})
26
+ if @state == :bound
27
+ # Split the message into parts of 134 characters.
28
+ parts = []
29
+ while message.size > 0 do
30
+ parts << message.slice!(0..133)
31
+ end
32
+ 0.upto(parts.size-1) do |i|
33
+ udh = sprintf("%c", 5) # UDH is 5 bytes.
34
+ udh << sprintf("%c%c", 0, 3) # This is a concatenated message
35
+ udh << sprintf("%c", message_id) # The ID for the entire concatenated message
36
+ udh << sprintf("%c", parts.size) # How many parts this message consists of
37
+
38
+ udh << sprintf("%c", i+1) # This is part i+1
39
+
40
+ combined_options = {
41
+ :esm_class => 64, # This message contains a UDH header.
42
+ :udh => udh
43
+ }.merge(options)
44
+
45
+ pdu = Smpp::Pdu::SubmitSm.new(source_addr, destination_addr, parts[i], combined_options)
46
+ write_pdu(pdu)
47
+ end
48
+ else
49
+ raise InvalidStateException, "Transmitter is unbound. Cannot send MT messages."
50
+ end
51
+ end
52
+
53
+ def send_bind
54
+ raise IOError, 'Transmitter already bound.' unless unbound?
55
+ pdu = Pdu::BindTransmitter.new(
56
+ @config[:system_id],
57
+ @config[:password],
58
+ @config[:system_type],
59
+ @config[:source_ton],
60
+ @config[:source_npi],
61
+ @config[:source_address_range])
62
+ write_pdu(pdu)
63
+ end
64
+ end
@@ -0,0 +1,3 @@
1
+ module Smpp
2
+ VERSION = "0.6.4"
3
+ end
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: anjlab-ruby-smpp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.3
5
- prerelease:
4
+ version: 0.6.4
6
5
  platform: ruby
7
6
  authors:
8
7
  - Ray Krueger
@@ -10,52 +9,75 @@ authors:
10
9
  autorequire:
11
10
  bindir: bin
12
11
  cert_chain: []
13
- date: 2012-06-27 00:00:00.000000000 Z
12
+ date: 2013-12-27 00:00:00.000000000 Z
14
13
  dependencies:
15
14
  - !ruby/object:Gem::Dependency
16
15
  name: eventmachine
17
- requirement: &70113962531680 !ruby/object:Gem::Requirement
18
- none: false
16
+ requirement: !ruby/object:Gem::Requirement
19
17
  requirements:
20
- - - ! '>='
18
+ - - ">="
21
19
  - !ruby/object:Gem::Version
22
20
  version: 0.10.0
23
21
  type: :runtime
24
22
  prerelease: false
25
- version_requirements: *70113962531680
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: 0.10.0
26
28
  - !ruby/object:Gem::Dependency
27
- name: jeweler
28
- requirement: &70113962531200 !ruby/object:Gem::Requirement
29
- none: false
29
+ name: bundler
30
+ requirement: !ruby/object:Gem::Requirement
30
31
  requirements:
31
- - - ! '>='
32
+ - - "~>"
32
33
  - !ruby/object:Gem::Version
33
- version: '0'
34
+ version: '1.3'
34
35
  type: :development
35
36
  prerelease: false
36
- version_requirements: *70113962531200
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '1.3'
37
42
  - !ruby/object:Gem::Dependency
38
43
  name: rake
39
- requirement: &70113962530720 !ruby/object:Gem::Requirement
40
- none: false
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: test-unit
58
+ requirement: !ruby/object:Gem::Requirement
41
59
  requirements:
42
- - - ! '>='
60
+ - - ">="
43
61
  - !ruby/object:Gem::Version
44
62
  version: '0'
45
63
  type: :development
46
64
  prerelease: false
47
- version_requirements: *70113962530720
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
48
70
  description: Ruby implementation of the SMPP protocol, based on EventMachine. SMPP
49
71
  is a protocol that allows ordinary people outside the mobile network to exchange
50
72
  SMS messages directly with mobile operators.
51
- email: raykrueger@gmail.com
73
+ email:
74
+ - raykrueger@gmail.com
52
75
  executables: []
53
76
  extensions: []
54
- extra_rdoc_files:
55
- - CHANGELOG
56
- - CONTRIBUTORS.txt
57
- - README.rdoc
77
+ extra_rdoc_files: []
58
78
  files:
79
+ - ".gitignore"
80
+ - ".travis.yml"
59
81
  - CHANGELOG
60
82
  - CONTRIBUTORS.txt
61
83
  - Gemfile
@@ -97,6 +119,8 @@ files:
97
119
  - lib/smpp/receiver.rb
98
120
  - lib/smpp/server.rb
99
121
  - lib/smpp/transceiver.rb
122
+ - lib/smpp/transmitter.rb
123
+ - lib/smpp/version.rb
100
124
  - lib/sms.rb
101
125
  - test/delegate.rb
102
126
  - test/encoding_test.rb
@@ -109,27 +133,37 @@ files:
109
133
  - test/submit_sm_test.rb
110
134
  - test/transceiver_test.rb
111
135
  homepage: http://github.com/raykrueger/ruby-smpp
112
- licenses: []
136
+ licenses:
137
+ - MIT
138
+ metadata: {}
113
139
  post_install_message:
114
140
  rdoc_options: []
115
141
  require_paths:
116
142
  - lib
117
143
  required_ruby_version: !ruby/object:Gem::Requirement
118
- none: false
119
144
  requirements:
120
- - - ! '>='
145
+ - - ">="
121
146
  - !ruby/object:Gem::Version
122
147
  version: '0'
123
148
  required_rubygems_version: !ruby/object:Gem::Requirement
124
- none: false
125
149
  requirements:
126
- - - ! '>='
150
+ - - ">="
127
151
  - !ruby/object:Gem::Version
128
152
  version: '0'
129
153
  requirements: []
130
- rubyforge_project: anjlab-ruby-smpp
131
- rubygems_version: 1.8.17
154
+ rubyforge_project:
155
+ rubygems_version: 2.2.0
132
156
  signing_key:
133
- specification_version: 3
157
+ specification_version: 4
134
158
  summary: Ruby implementation of the SMPP protocol, based on EventMachine.
135
- test_files: []
159
+ test_files:
160
+ - test/delegate.rb
161
+ - test/encoding_test.rb
162
+ - test/optional_parameter_test.rb
163
+ - test/pdu_parsing_test.rb
164
+ - test/receiver_test.rb
165
+ - test/responsive_delegate.rb
166
+ - test/server.rb
167
+ - test/smpp_test.rb
168
+ - test/submit_sm_test.rb
169
+ - test/transceiver_test.rb