rfetion 0.3.0

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.
data/README.textile ADDED
@@ -0,0 +1,56 @@
1
+ h1. rfetion
2
+
3
+ rfetion is a ruby gem for China Mobile fetion service that you can send SMS free.
4
+
5
+ **************************************************************************
6
+
7
+ h2. Demo
8
+
9
+ see "http://fetion.heroku.com":http://fetion.heroku.com
10
+
11
+ **************************************************************************
12
+
13
+ h2. Install
14
+
15
+ <pre><code>
16
+ gem sources -a http://gemcutter.org
17
+ gem install rfetion
18
+ </code></pre>
19
+
20
+ **************************************************************************
21
+
22
+ h2. Usage
23
+
24
+ send sms to yourself
25
+ <pre><code>
26
+ Fetion.send_sms_to_self(mobile_no, password, content)
27
+ </code></pre>
28
+
29
+ send sms to friends
30
+ <pre><code>
31
+ Fetion.send_sms_to_friends(mobile_no, password, friend_mobile_no or array of friends_mobile_no, content)
32
+ </code></pre>
33
+
34
+ add friend
35
+ <pre><code>
36
+ Fetion.add_buddy(mobile_no, password, friend_mobile)
37
+ </code></pre>
38
+
39
+ **************************************************************************
40
+
41
+ h2. Shell command
42
+
43
+ you can use it in shell command directly
44
+
45
+ <pre><code>
46
+ rfetion -h
47
+
48
+ Usage: rfetion [options]
49
+ Example: rfetion -m mobile -p password -f friend_mobile -c sms_content
50
+ rfetion -m mobile -p password -a friend_mobile
51
+ -m, --mobile MOBILE Fetion mobile number
52
+ -p, --password PASSWORD Fetion password
53
+ -c, --content CONTENT Fetion message content
54
+ -f, --friends MOBILE1,MOBILE2 (optional) Fetion friends mobile number, if no friends mobile number, send message to yourself
55
+ -a, --add_buddy MOBILE Add friend mobile as fetion friend
56
+ </code></pre>
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require 'jeweler'
2
+
3
+ Jeweler::Tasks.new do |gemspec|
4
+ gemspec.name = 'rfetion'
5
+ gemspec.summary = 'rfetion is a ruby gem for China Mobile fetion service that you can send SMS free.'
6
+ gemspec.description = 'rfetion is a ruby gem for China Mobile fetion service that you can send SMS free.'
7
+ gemspec.email = 'flyerhzm@gmail.com'
8
+ gemspec.homepage = ''
9
+ gemspec.authors = ['Richard Huang']
10
+ gemspec.files.exclude '.gitignore'
11
+ gemspec.add_dependency 'uuid'
12
+ gemspec.executables << 'rfetion'
13
+ end
14
+ Jeweler::GemcutterTasks.new
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.3.0
data/bin/rfetion ADDED
@@ -0,0 +1,5 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'rfetion'
5
+ require 'rfetion/command'
@@ -0,0 +1,50 @@
1
+ require 'optparse'
2
+
3
+ options = {}
4
+
5
+ OptionParser.new do |opts|
6
+ # Set a banner, displayed at the top of the help screen.
7
+ opts.banner = <<EOF
8
+ Usage: rfetion [options]
9
+ Example: rfetion -m mobile -p password -f friend_mobile -c sms_content
10
+ rfetion -m mobile -p password -a friend_mobile
11
+ EOF
12
+
13
+ opts.on('-m', '--mobile MOBILE', 'Fetion mobile number') do |mobile|
14
+ options[:mobile_no] = mobile
15
+ end
16
+
17
+ opts.on('-p', '--password PASSWORD', 'Fetion password') do |f|
18
+ options[:password] = f
19
+ end
20
+
21
+ opts.on('-c', '--content CONTENT', 'Fetion message content') do |f|
22
+ options[:content] = f
23
+ end
24
+
25
+ options[:friends_mobile] = []
26
+ opts.on('-f', '--friends MOBILE1,MOBILE2', Array, '(optional) Fetion friends mobile number, if no friends mobile number, send message to yourself') do |f|
27
+ options[:friends_mobile] = f
28
+ end
29
+
30
+ opts.on('-a', '--add_buddy MOBILE', 'Add friend mobile as fetion friend') do |f|
31
+ options[:add_mobile] = f
32
+ end
33
+
34
+ opts.parse!
35
+ end
36
+
37
+ if options[:add_mobile]
38
+ if options[:mobile_no] and options[:password]
39
+ Fetion.add_buddy(options[:mobile_no], options[:password], options[:add_mobile])
40
+ end
41
+ return
42
+ end
43
+
44
+ if options[:mobile_no] and options[:password] and options[:content]
45
+ if options[:friends_mobile].empty?
46
+ Fetion.send_sms_to_self(options[:mobile_no], options[:password], options[:content])
47
+ else
48
+ Fetion.send_sms_to_friends(options[:mobile_no], options[:password], options[:friends_mobile], options[:content])
49
+ end
50
+ end
@@ -0,0 +1,247 @@
1
+ class FetionException < Exception
2
+ end
3
+
4
+ class Fetion
5
+ attr_accessor :mobile_no, :password
6
+ attr_reader :uri, :contacts
7
+
8
+ FETION_URL = 'http://221.130.44.194/ht/sd.aspx'
9
+ FETION_LOGIN_URL = 'https://nav.fetion.com.cn/ssiportal/SSIAppSignIn.aspx'
10
+ FETION_CONFIG_URL = 'http://nav.fetion.com.cn/nav/getsystemconfig.aspx'
11
+ FETION_SIPP = 'SIPP'
12
+ GUID = UUID.new.generate
13
+ @nonce = nil
14
+
15
+ def initialize
16
+ @next_call = 0
17
+ @seq = 0
18
+ @buddies = []
19
+ @contacts = []
20
+ @logger = Logger.new(STDOUT)
21
+ @logger.level = Logger::INFO
22
+ end
23
+
24
+ def logger_level=(level)
25
+ @logger.level = level
26
+ end
27
+
28
+ def Fetion.send_sms_to_self(mobile_no, password, content)
29
+ fetion = Fetion.new
30
+ fetion.mobile_no = mobile_no
31
+ fetion.password = password
32
+ fetion.login
33
+ fetion.register
34
+ fetion.send_sms(fetion.uri, content)
35
+ fetion.logout
36
+ end
37
+
38
+ def Fetion.send_sms_to_friends(mobile_no, password, friend_mobiles, content)
39
+ friend_mobiles = Array(friend_mobiles)
40
+ friend_mobiles.collect! {|mobile| mobile.to_i}
41
+ fetion = Fetion.new
42
+ fetion.mobile_no = mobile_no
43
+ fetion.password = password
44
+ fetion.login
45
+ fetion.register
46
+ fetion.get_buddy_list
47
+ fetion.get_contacts_info
48
+ fetion.contacts.each do |contact|
49
+ if friend_mobiles.include? contact[:mobile_no].to_i
50
+ fetion.send_sms(contact[:sip], content)
51
+ end
52
+ end
53
+ fetion.logout
54
+ end
55
+
56
+ def Fetion.add_buddy(mobile_no, password, friend_mobile)
57
+ fetion = Fetion.new
58
+ fetion.mobile_no = mobile_no
59
+ fetion.password = password
60
+ fetion.login
61
+ fetion.register
62
+ fetion.add_buddy(friend_mobile)
63
+ fetion.logout
64
+ end
65
+
66
+ def login
67
+ @logger.info "fetion login"
68
+ uri = URI.parse(FETION_LOGIN_URL + "?mobileno=#{@mobile_no}&pwd=#{@password}")
69
+ http = Net::HTTP.new(uri.host, uri.port)
70
+ http.use_ssl = true
71
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
72
+ headers = {'Content-Type' => 'application/oct-stream', 'Pragma' => "xz4BBcV#{GUID}", 'User-Agent' => 'IIC2.0/PC 3.2.0540'}
73
+ response = http.request_get(uri.request_uri, headers)
74
+
75
+ raise FetionException.new('Fetion Error: Login failed.') unless response.is_a? Net::HTTPSuccess
76
+ raise FetionException.new('Fetion Error: No ssic found in cookie.') unless response['set-cookie'] =~ /ssic=(.*);/
77
+
78
+ @ssic = $1
79
+ doc = REXML::Document.new(response.body)
80
+ results = doc.root
81
+ @status_code = results.attributes["status-code"]
82
+ user = results.children.first
83
+ @user_status = user.attributes['user-status']
84
+ @uri = user.attributes['uri']
85
+ @mobile_no = user.attributes['mobile-no']
86
+ @user_id = user.attributes['user-id']
87
+ if @uri =~ /sip:(\d+)@(.+);/
88
+ @sid = $1
89
+ @domain = $2
90
+ end
91
+ @logger.debug "ssic: " + @ssic
92
+ @logger.debug "status_code: " + @status_code
93
+ @logger.debug "user_status: " + @user_status
94
+ @logger.debug "uri: " + @uri
95
+ @logger.debug "mobile_no: " + @mobile_no
96
+ @logger.debug "user_id: " + @user_id
97
+ @logger.debug "sid: " + @sid
98
+ @logger.debug "domain: " + @domain
99
+ @logger.info "fetion login success"
100
+ end
101
+
102
+ def register
103
+ @logger.info "fetion http register"
104
+ arg = '<args><device type="PC" version="44" client-version="3.2.0540" /><caps value="simple-im;im-session;temp-group;personal-group" /><events value="contact;permission;system-message;personal-group" /><user-info attributes="all" /><presence><basic value="400" desc="" /></presence></args>'
105
+
106
+ call = next_call
107
+ curl_exec(next_url, @ssic, FETION_SIPP)
108
+
109
+ msg = sip_create("R fetion.com.cn SIP-C/2.0", {'F' => @sid, 'I' => call, 'Q' => '1 R'}, arg) + FETION_SIPP
110
+ curl_exec(next_url('i'), @ssic, msg)
111
+
112
+ response = curl_exec(next_url, @ssic, FETION_SIPP)
113
+ raise FetionException.new("Fetion Error: no nonce found") unless response.body =~ /nonce="(\w+)"/
114
+
115
+ @nonce = $1
116
+ @salt = "777A6D03"
117
+ @cnonce = calc_cnonce
118
+ @response = calc_response
119
+
120
+ @logger.debug "nonce: #{@nonce}"
121
+ @logger.debug "salt: #{@salt}"
122
+ @logger.debug "cnonce: #{@cnonce}"
123
+ @logger.debug "response: #{@response}"
124
+
125
+ msg = sip_create('R fetion.com.cn SIP-C/2.0', {'F' => @sid, 'I' => call, 'Q' => '2 R', 'A' => "Digest algorithm=\"SHA1-sess\",response=\"#{@response}\",cnonce=\"#{@cnonce}\",salt=\"#{@salt}\""}, arg) + FETION_SIPP
126
+ curl_exec(next_url, @ssic, msg)
127
+ response = curl_exec(next_url, @ssic, FETION_SIPP)
128
+
129
+ @logger.info "fetion http register success"
130
+ response.is_a? Net::HTTPSuccess
131
+ end
132
+
133
+ def get_buddy_list
134
+ @logger.info "fetion get buddy list"
135
+ arg = '<args><contacts><buddy-lists /><buddies attributes="all" /><mobile-buddies attributes="all" /><chat-friends /><blacklist /></contacts></args>'
136
+ msg = sip_create('S fetion.com.cn SIP-C/2.0', {'F' => @sid, 'I' => next_call, 'Q' => '1 S', 'N' => 'GetContactList'}, arg) + FETION_SIPP
137
+ curl_exec(next_url, @ssic, msg)
138
+ response = curl_exec(next_url, @ssic, FETION_SIPP)
139
+ raise FetionException.new("Fetion Error: No buddy list found") unless response.body =~ /.*?\r\n\r\n(.*)#{FETION_SIPP}\s*$/i
140
+
141
+ response.body.scan(/uri="([^"]+)"/).each do |buddy|
142
+ @buddies << {:uri => buddy[0]}
143
+ end
144
+ @logger.debug @buddies.inspect
145
+ @logger.info "fetion get buddy list success"
146
+ end
147
+
148
+ def get_contacts_info
149
+ @logger.info "fetion get contacts info"
150
+ arg = '<args><contacts attributes="all">'
151
+ @buddies.each do |buddy|
152
+ arg += "<contact uri=\"#{buddy[:uri]}\" />"
153
+ end
154
+ arg += '</contacts></args>'
155
+
156
+ msg = sip_create('S fetion.com.cn SIP-C/2.0', {'F' => @sid, 'I' => next_call, 'Q' => '1 S', 'N' => 'GetContactsInfo'}, arg) + FETION_SIPP
157
+ curl_exec(next_url, @ssic, msg)
158
+ response = curl_exec(next_url, @ssic, FETION_SIPP)
159
+ response.body.scan(/uri="([^"]+)".*?mobile-no="([^"]+)"/).each do |contact|
160
+ @contacts << {:sip => contact[0], :mobile_no => contact[1]}
161
+ end
162
+ @logger.debug @contacts.inspect
163
+ @logger.info "fetion get contacts info success"
164
+ end
165
+
166
+ def send_sms(to, content)
167
+ @logger.info "fetion send sms to #{to}"
168
+ msg = sip_create('M fetion.com.cn SIP-C/2.0', {'F' => @sid, 'I' => next_call, 'Q' => '1 M', 'T' => to, 'N' => 'SendSMS'}, content) + FETION_SIPP
169
+ response = curl_exec(next_url, @ssic, msg)
170
+
171
+ @logger.info "fetion send sms to #{to} success"
172
+ response.is_a? Net::HTTPSuccess
173
+ end
174
+
175
+ def add_buddy(mobile, nickname = nil)
176
+ @logger.info "fetion send request to add #{mobile} as friend"
177
+ arg = %Q{<args><contacts><buddies><buddy uri="tel:#{mobile}" local-name="#{nickname}" buddy-lists="1" expose-mobile-no="1" expose-name="1" /></buddies></contacts></args>}
178
+ msg = sip_create('S fetion.com.cn SIP-C/2.0', {'F' => @sid, 'I' => next_call, 'Q' => '1 S', 'N' => 'AddBuddy'}, arg) + FETION_SIPP
179
+ response = curl_exec(next_url, @ssic, msg)
180
+
181
+ @logger.info "fetion send request to add #{mobile} as friend success"
182
+ response.is_a? Net::HTTPSuccess
183
+ end
184
+
185
+ def logout
186
+ @logger.info "fetion logout"
187
+ msg = sip_create('R fetion.com.cn SIP-C/2.0', {'F' => @sid, 'I' => next_call, 'Q' => '2 R', 'X' => 0}, '') + FETION_SIPP
188
+ curl_exec(next_url, @ssic, msg)
189
+ response = curl_exec(next_url, @ssic, FETION_SIPP)
190
+ @logger.info "fetion logout success"
191
+ end
192
+
193
+ def curl_exec(url, ssic, body)
194
+ @logger.debug "fetion curl exec"
195
+ @logger.debug "url: #{url}"
196
+ @logger.debug "ssic: #{ssic}"
197
+ @logger.debug "body: #{body}"
198
+ uri = URI.parse(url)
199
+ http = Net::HTTP.new(uri.host, uri.port)
200
+ headers = {'Content-Type' => 'application/oct-stream', 'Pragma' => "xz4BBcV#{GUID}", 'User-Agent' => 'IIC2.0/PC 3.2.0540', 'Cookie' => "ssic=#{@ssic}"}
201
+ response = http.request_post(uri.request_uri, body, headers)
202
+
203
+ @logger.debug "response: #{response.inspect}"
204
+ @logger.debug "response body: #{response.body}"
205
+ @logger.debug "fetion curl exec complete"
206
+ response
207
+ end
208
+
209
+ def sip_create(invite, fields, arg)
210
+ sip = invite + "\r\n"
211
+ fields.each {|k, v| sip += "#{k}: #{v}\r\n"}
212
+ sip += "L: #{arg.size}\r\n\r\n#{arg}"
213
+ @logger.debug "sip message: #{sip}"
214
+ sip
215
+ end
216
+
217
+ def calc_response
218
+ str = [hash_password[8..-1]].pack("H*")
219
+ key = Digest::SHA1.digest("#{@sid}:#{@domain}:#{str}")
220
+
221
+ h1 = Digest::MD5.hexdigest("#{key}:#{@nonce}:#{@cnonce}").upcase
222
+ h2 = Digest::MD5.hexdigest("REGISTER:#{@sid}").upcase
223
+
224
+ Digest::MD5.hexdigest("#{h1}:#{@nonce}:#{h2}").upcase
225
+ end
226
+
227
+ def calc_cnonce
228
+ Digest::MD5.hexdigest(UUID.new.generate).upcase
229
+ end
230
+
231
+ def hash_password
232
+ salt = "#{0x77.chr}#{0x7A.chr}#{0x6D.chr}#{0x03.chr}"
233
+ src = salt + Digest::SHA1.digest(@password)
234
+ '777A6D03' + Digest::SHA1.hexdigest(src).upcase
235
+ end
236
+
237
+ def next_url(t = 's')
238
+ @seq += 1
239
+ FETION_URL + "?t=#{t}&i=#{@seq}"
240
+ end
241
+
242
+ def next_call
243
+ @next_call += 1
244
+ @next_call
245
+ end
246
+ end
247
+
data/lib/rfetion.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'uuid'
3
+ require 'net/http'
4
+ require 'net/https'
5
+ require 'rexml/document'
6
+ require 'digest/sha1'
7
+ require 'logger'
8
+ require 'rfetion/fetion'
data/rfetion.gemspec ADDED
@@ -0,0 +1,47 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{rfetion}
8
+ s.version = "0.3.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Richard Huang"]
12
+ s.date = %q{2009-10-11}
13
+ s.description = %q{rfetion is a ruby gem for China Mobile fetion service that you can send SMS free.}
14
+ s.email = %q{flyerhzm@gmail.com}
15
+ s.executables = ["rfetion", "rfetion"]
16
+ s.extra_rdoc_files = [
17
+ "README.textile"
18
+ ]
19
+ s.files = [
20
+ "README.textile",
21
+ "Rakefile",
22
+ "VERSION",
23
+ "bin/rfetion",
24
+ "lib/rfetion.rb",
25
+ "lib/rfetion/command.rb",
26
+ "lib/rfetion/fetion.rb",
27
+ "rfetion.gemspec"
28
+ ]
29
+ s.homepage = %q{}
30
+ s.rdoc_options = ["--charset=UTF-8"]
31
+ s.require_paths = ["lib"]
32
+ s.rubygems_version = %q{1.3.5}
33
+ s.summary = %q{rfetion is a ruby gem for China Mobile fetion service that you can send SMS free.}
34
+
35
+ if s.respond_to? :specification_version then
36
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
37
+ s.specification_version = 3
38
+
39
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
40
+ s.add_runtime_dependency(%q<uuid>, [">= 0"])
41
+ else
42
+ s.add_dependency(%q<uuid>, [">= 0"])
43
+ end
44
+ else
45
+ s.add_dependency(%q<uuid>, [">= 0"])
46
+ end
47
+ end
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rfetion
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Richard Huang
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-11 00:00:00 +08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: uuid
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ description: rfetion is a ruby gem for China Mobile fetion service that you can send SMS free.
26
+ email: flyerhzm@gmail.com
27
+ executables:
28
+ - rfetion
29
+ - rfetion
30
+ extensions: []
31
+
32
+ extra_rdoc_files:
33
+ - README.textile
34
+ files:
35
+ - README.textile
36
+ - Rakefile
37
+ - VERSION
38
+ - bin/rfetion
39
+ - lib/rfetion.rb
40
+ - lib/rfetion/command.rb
41
+ - lib/rfetion/fetion.rb
42
+ - rfetion.gemspec
43
+ has_rdoc: true
44
+ homepage: ""
45
+ licenses: []
46
+
47
+ post_install_message:
48
+ rdoc_options:
49
+ - --charset=UTF-8
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0"
63
+ version:
64
+ requirements: []
65
+
66
+ rubyforge_project:
67
+ rubygems_version: 1.3.5
68
+ signing_key:
69
+ specification_version: 3
70
+ summary: rfetion is a ruby gem for China Mobile fetion service that you can send SMS free.
71
+ test_files: []
72
+