rfetion 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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
+