rfetion 0.4.8 → 0.5.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 +2 -2
- data/Rakefile +20 -0
- data/VERSION +1 -1
- data/lib/rfetion/contact.rb +7 -7
- data/lib/rfetion/fetion.rb +260 -292
- data/lib/rfetion/sipc_message.rb +104 -0
- data/lib/rfetion.rb +3 -1
- data/rfetion.gemspec +13 -3
- data/spec/rfetion/fetion_spec.rb +369 -0
- data/spec/rfetion/sipc_message_spec.rb +93 -0
- data/spec/spec.opts +8 -0
- data/spec/spec_helper.rb +8 -0
- metadata +11 -4
data/lib/rfetion/fetion.rb
CHANGED
@@ -1,41 +1,53 @@
|
|
1
|
-
require 'rubygems'
|
2
1
|
require 'guid'
|
3
2
|
require 'time'
|
4
3
|
require 'net/http'
|
5
4
|
require 'net/https'
|
6
5
|
require 'nokogiri'
|
7
6
|
require 'digest/sha1'
|
8
|
-
require '
|
7
|
+
require 'openssl'
|
9
8
|
require 'logger'
|
10
9
|
|
11
|
-
class FetionException < Exception
|
12
|
-
end
|
13
|
-
|
14
10
|
class Fetion
|
15
11
|
attr_accessor :mobile_no, :sid, :password
|
16
|
-
attr_reader :uri, :contacts
|
12
|
+
attr_reader :user_id, :uri, :contacts, :response, :nickname
|
13
|
+
|
14
|
+
FETION_URL = 'http://221.176.31.39/ht/sd.aspx'
|
15
|
+
FETION_LOGIN_URL = 'https://uid.fetion.com.cn/ssiportal/SSIAppSignInV4.aspx?mobileno=%mobileno%sid=%sid%&domains=fetion.com.cn;m161.com.cn;www.ikuwa.cn&v4digest-type=1&v4digest=%digest%'
|
17
16
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
@nonce = nil
|
17
|
+
SIPP = 'SIPP'
|
18
|
+
USER_AGENT = "IIC2.0/PC 3.6.2020"
|
19
|
+
VERSION = "3.6.2020"
|
20
|
+
DOMAIN = "fetion.com.cn"
|
23
21
|
|
24
22
|
def initialize
|
25
|
-
@
|
23
|
+
@call = 0
|
24
|
+
@alive = 0
|
26
25
|
@seq = 0
|
27
26
|
@buddies = []
|
28
27
|
@contacts = []
|
29
28
|
@logger = Logger.new(STDOUT)
|
30
29
|
@logger.level = Logger::INFO
|
31
|
-
@
|
32
|
-
@guid = ::Guid.new.to_s
|
30
|
+
@guid = Guid.new.to_s
|
33
31
|
end
|
34
32
|
|
35
33
|
def logger_level=(level)
|
36
34
|
@logger.level = level
|
37
35
|
end
|
38
36
|
|
37
|
+
def Fetion.open(options, &block)
|
38
|
+
fetion = Fetion.new
|
39
|
+
fetion.logger_level = options.delete(:logger_level) || Logger::INFO
|
40
|
+
fetion.mobile_no = options.delete(:mobile_no)
|
41
|
+
fetion.sid = options.delete(:sid)
|
42
|
+
fetion.password = options.delete(:password)
|
43
|
+
fetion.login
|
44
|
+
fetion.register
|
45
|
+
|
46
|
+
fetion.instance_eval &block
|
47
|
+
|
48
|
+
fetion.logout
|
49
|
+
end
|
50
|
+
|
39
51
|
# options
|
40
52
|
# mobile_no
|
41
53
|
# sid
|
@@ -44,30 +56,23 @@ class Fetion
|
|
44
56
|
# content
|
45
57
|
# logger_level
|
46
58
|
def Fetion.send_sms(options)
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
receivers.collect! {|receiver| receiver.to_s}
|
59
|
-
fetion.get_buddy_list
|
60
|
-
fetion.get_contacts_info
|
61
|
-
fetion.contacts.each do |contact|
|
62
|
-
if receivers.include? contact.mobile_no.to_s or receivers.any? { |receiver| contact.uri.index(receiver) }
|
63
|
-
fetion.send_sms(contact.uri, content)
|
59
|
+
Fetion.open(options) do
|
60
|
+
receivers = options.delete(:receivers)
|
61
|
+
content = options.delete(:content)
|
62
|
+
if receivers
|
63
|
+
receivers = Array(receivers)
|
64
|
+
receivers.collect! {|receiver| receiver.to_s}
|
65
|
+
get_contacts
|
66
|
+
contacts.each do |contact|
|
67
|
+
if receivers.include? contact.mobile_no.to_s or receivers.any? { |receiver| contact.uri.index(receiver) }
|
68
|
+
send_sms(contact.uri, content)
|
69
|
+
end
|
64
70
|
end
|
71
|
+
send_sms(uri, content) if receivers.any? { |receiver| self? receiver }
|
72
|
+
else
|
73
|
+
send_sms(uri, content)
|
65
74
|
end
|
66
|
-
fetion.send_sms(fetion.uri, content) if receivers.any? { |receiver| fetion.self? receiver }
|
67
|
-
else
|
68
|
-
fetion.send_sms(fetion.uri, content)
|
69
75
|
end
|
70
|
-
fetion.logout
|
71
76
|
end
|
72
77
|
|
73
78
|
# options
|
@@ -78,30 +83,23 @@ class Fetion
|
|
78
83
|
# content
|
79
84
|
# logger_level
|
80
85
|
def Fetion.send_msg(options)
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
receivers.collect! {|receiver| receiver.to_s}
|
93
|
-
fetion.get_buddy_list
|
94
|
-
fetion.get_contacts_info
|
95
|
-
fetion.contacts.each do |contact|
|
96
|
-
if receivers.include? contact.mobile_no.to_s or receivers.any? { |receiver| contact.uri.index(receiver) }
|
97
|
-
fetion.send_msg(contact.uri, content)
|
86
|
+
Fetion.open(options) do
|
87
|
+
receivers = options.delete(:receivers)
|
88
|
+
content = options.delete(:content)
|
89
|
+
if receivers
|
90
|
+
receivers = Array(receivers)
|
91
|
+
receivers.collect! {|receiver| receiver.to_s}
|
92
|
+
get_contacts
|
93
|
+
contacts.each do |contact|
|
94
|
+
if receivers.include? contact.mobile_no.to_s or receivers.any? { |receiver| contact.uri.index(receiver) }
|
95
|
+
send_msg(contact.uri, content)
|
96
|
+
end
|
98
97
|
end
|
98
|
+
send_msg(uri, content) if receivers.any? { |receiver| self? receiver }
|
99
|
+
else
|
100
|
+
send_msg(uri, content)
|
99
101
|
end
|
100
|
-
fetion.send_msg(fetion.uri, content) if receivers.any? { |receiver| fetion.self? receiver }
|
101
|
-
else
|
102
|
-
fetion.send_msg(fetion.uri, content)
|
103
102
|
end
|
104
|
-
fetion.logout
|
105
103
|
end
|
106
104
|
|
107
105
|
# options
|
@@ -112,33 +110,26 @@ class Fetion
|
|
112
110
|
# content
|
113
111
|
# time
|
114
112
|
# logger_level
|
115
|
-
def Fetion.
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
end
|
135
|
-
end.compact!
|
136
|
-
new_receivers << fetion.uri if receivers.any? { |receiver| fetion.self? receiver }
|
137
|
-
fetion.schedule_sms(new_receivers, content, time)
|
138
|
-
else
|
139
|
-
fetion.schedule_sms([fetion.uri], content, time)
|
113
|
+
def Fetion.set_schedule_sms(options)
|
114
|
+
Fetion.open(options) do
|
115
|
+
receivers = options.delete(:receivers)
|
116
|
+
content = options.delete(:content)
|
117
|
+
time = options.delete(:time)
|
118
|
+
get_contacts
|
119
|
+
if receivers
|
120
|
+
receivers = Array(receivers)
|
121
|
+
receivers.collect! {|receiver| receiver.to_s}
|
122
|
+
new_receivers = contacts.collect do |contact|
|
123
|
+
if receivers.include? contact.mobile_no.to_s or receivers.any? { |receiver| contact.uri.index(receiver) }
|
124
|
+
contact.uri
|
125
|
+
end
|
126
|
+
end.compact!
|
127
|
+
new_receivers << uri if receivers.any? { |receiver| self? receiver }
|
128
|
+
set_schedule_sms(new_receivers, content, time)
|
129
|
+
else
|
130
|
+
set_schedule_sms([fetion.uri], content, time)
|
131
|
+
end
|
140
132
|
end
|
141
|
-
fetion.logout
|
142
133
|
end
|
143
134
|
|
144
135
|
# options
|
@@ -149,213 +140,110 @@ class Fetion
|
|
149
140
|
# friend_sip
|
150
141
|
# logger_level
|
151
142
|
def Fetion.add_buddy(options)
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
fetion.sid = options[:sid]
|
156
|
-
fetion.password = options[:password]
|
157
|
-
fetion.login
|
158
|
-
fetion.register
|
159
|
-
fetion.get_personal_info
|
160
|
-
fetion.add_buddy(options)
|
161
|
-
fetion.logout
|
143
|
+
Fetion.open(options) do
|
144
|
+
add_buddy(options)
|
145
|
+
end
|
162
146
|
end
|
163
147
|
|
164
148
|
def login
|
165
|
-
@logger.info "fetion login"
|
166
149
|
if @mobile_no
|
167
|
-
|
150
|
+
url = FETION_LOGIN_URL.sub('%mobileno%', @mobile_no).sub('sid=%sid%', '')
|
168
151
|
else
|
169
|
-
|
152
|
+
url = FETION_LOGIN_URL.sub('%sid%', @sid).sub('mobileno=%mobileno%', '')
|
170
153
|
end
|
154
|
+
uri = URI.parse(url.sub('%digest%', Digest::SHA1.hexdigest("#{DOMAIN}:#{@password}")))
|
171
155
|
http = Net::HTTP.new(uri.host, uri.port)
|
172
156
|
http.use_ssl = true
|
173
157
|
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
174
|
-
headers = {'
|
158
|
+
headers = {'User-Agent' => USER_AGENT}
|
175
159
|
response = http.request_get(uri.request_uri, headers)
|
176
160
|
|
177
161
|
raise FetionException.new('Fetion Error: Login failed.') unless response.is_a? Net::HTTPSuccess
|
178
|
-
raise FetionException.new('Fetion Error: No ssic found in cookie.') unless response['set-cookie'] =~ /ssic=(.*);/
|
179
162
|
|
180
|
-
|
181
|
-
@logger.debug response.body
|
182
|
-
doc = Nokogiri::XML(response.body)
|
183
|
-
results = doc.root
|
184
|
-
@status_code = results["status-code"]
|
185
|
-
user = results.children.first
|
186
|
-
@user_status = user['user-status']
|
187
|
-
@uri = user['uri']
|
188
|
-
@mobile_no = user['mobile-no']
|
189
|
-
@user_id = user['user-id']
|
190
|
-
if @uri =~ /sip:(\d+)@(.+);/
|
191
|
-
@sid = $1
|
192
|
-
@domain = $2
|
193
|
-
end
|
194
|
-
@logger.debug "ssic: " + @ssic
|
195
|
-
@logger.debug "status_code: " + @status_code
|
196
|
-
@logger.debug "user_status: " + @user_status
|
197
|
-
@logger.debug "uri: " + @uri
|
198
|
-
@logger.debug "mobile_no: " + @mobile_no
|
199
|
-
@logger.debug "user_id: " + @user_id
|
200
|
-
@logger.debug "sid: " + @sid
|
201
|
-
@logger.debug "domain: " + @domain
|
202
|
-
@logger.info "fetion login success"
|
163
|
+
parse_login_response(response)
|
203
164
|
end
|
204
165
|
|
205
166
|
def register
|
206
|
-
@logger.info "fetion http register"
|
207
167
|
call = next_call
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
begin
|
212
|
-
register_first(call, arg)
|
213
|
-
rescue FetionException
|
214
|
-
sleep 16
|
215
|
-
register_first(call, arg)
|
216
|
-
end
|
217
|
-
|
218
|
-
begin
|
219
|
-
register_second(call, arg)
|
220
|
-
rescue FetionException
|
221
|
-
sleep 16
|
222
|
-
register_second(call, arg)
|
223
|
-
end
|
224
|
-
@logger.info "fetion http register success"
|
168
|
+
register_first
|
169
|
+
register_second
|
170
|
+
call = next_call
|
225
171
|
end
|
226
172
|
|
227
|
-
def register_first
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
msg = sip_create("R fetion.com.cn SIP-C/2.0", {'F' => @sid, 'I' => call, 'Q' => '1 R'}, arg) + FETION_SIPP
|
233
|
-
curl_exec(next_url('i'), @ssic, msg)
|
234
|
-
|
235
|
-
response = curl_exec(next_url, @ssic, FETION_SIPP)
|
236
|
-
raise FetionException.new("Fetion Error: no nonce found") unless response.body =~ /nonce="(\w+)"/
|
173
|
+
def register_first
|
174
|
+
curl_exec(SIPP, next_url('i'))
|
175
|
+
curl_exec(SipcMessage.register_first(self))
|
176
|
+
response = pulse
|
177
|
+
raise Fetion::NoNonceException.new("Fetion Error: no nonce found") unless response.body =~ /nonce="(.*?)",key="(.*?)",signature="(.*?)"/
|
237
178
|
|
238
179
|
@nonce = $1
|
239
|
-
@
|
240
|
-
@
|
180
|
+
@key = $2
|
181
|
+
@signature = $3
|
241
182
|
@response = calc_response
|
242
183
|
|
243
184
|
@logger.debug "nonce: #{@nonce}"
|
244
|
-
@logger.debug "
|
245
|
-
@logger.debug "
|
185
|
+
@logger.debug "key: #{@key}"
|
186
|
+
@logger.debug "signature: #{@signature}"
|
246
187
|
@logger.debug "response: #{@response}"
|
247
|
-
@logger.debug "fetion http register first success"
|
248
188
|
end
|
249
189
|
|
250
|
-
def register_second
|
251
|
-
@
|
252
|
-
|
253
|
-
|
254
|
-
curl_exec(next_url, @ssic, msg)
|
255
|
-
response = curl_exec(next_url, @ssic, FETION_SIPP)
|
190
|
+
def register_second
|
191
|
+
body = %Q|<args><device machine-code="B04B5DA2F5F1B8D01A76C0EBC841414C" /><caps value="ff" /><events value="7f" /><user-info mobile-no="#{@mobile_no}" user-id="#{@user_id}"><personal version="0" attributes="v4default" /><custom-config version="0" /><contact-list version="0" buddy-attributes="v4default" /></user-info><credentials domains="fetion.com.cn;m161.com.cn;www.ikuwa.cn;games.fetion.com.cn" /><presence><basic value="400" desc="" /></presence></args>|
|
192
|
+
curl_exec(SipcMessage.register_second(self))
|
193
|
+
response = pulse
|
256
194
|
|
257
195
|
raise FetionException.new('Fetion Error: Register failed.') unless response.is_a? Net::HTTPSuccess
|
258
|
-
@logger.debug "fetion http register second success"
|
259
|
-
end
|
260
196
|
|
261
|
-
|
262
|
-
|
263
|
-
arg = '<args><contacts><buddy-lists /><buddies attributes="all" /><mobile-buddies attributes="all" /><chat-friends /><blacklist /><allow-list /></contacts></args>'
|
264
|
-
msg = sip_create('S fetion.com.cn SIP-C/2.0', {'F' => @sid, 'I' => next_call, 'Q' => '1 S', 'N' => 'GetContactList'}, arg) + FETION_SIPP
|
265
|
-
curl_exec(next_url, @ssic, msg)
|
266
|
-
response = curl_exec(next_url, @ssic, FETION_SIPP)
|
267
|
-
raise FetionException.new("Fetion Error: Get buddy list error") unless response.is_a? Net::HTTPSuccess
|
268
|
-
|
269
|
-
response.body.scan(%r{<results>.*?</results>}).each do |results|
|
270
|
-
doc = Nokogiri::XML(results)
|
271
|
-
doc.root.xpath("/results/contacts/allow-list/contact").each do |contact|
|
272
|
-
@buddies << {:uri => contact["uri"]}
|
273
|
-
end
|
274
|
-
end
|
275
|
-
@logger.debug "buddies: #{@buddies.inspect}"
|
276
|
-
@logger.info "fetion get buddy list success"
|
197
|
+
parse_info(response.body)
|
198
|
+
parse_buddies(response.body)
|
277
199
|
end
|
278
200
|
|
279
|
-
def
|
280
|
-
|
281
|
-
arg = '<args><contacts attributes="provisioning;impresa;mobile-no;nickname;name;gender;portrait-crc;ivr-enabled" extended-attributes="score-level">'
|
282
|
-
@buddies.each do |buddy|
|
283
|
-
arg += "<contact uri=\"#{buddy[:uri]}\" />"
|
284
|
-
end
|
285
|
-
arg += '</contacts></args>'
|
286
|
-
|
287
|
-
msg = sip_create('S fetion.com.cn SIP-C/2.0', {'F' => @sid, 'I' => next_call, 'Q' => '1 S', 'N' => 'GetContactsInfo'}, arg) + FETION_SIPP
|
288
|
-
curl_exec(next_url, @ssic, msg)
|
289
|
-
while true do
|
290
|
-
sleep 1
|
291
|
-
response = curl_exec(next_url, @ssic, FETION_SIPP)
|
292
|
-
raise FetionException.new("Fetion Error: Get contacts info error") unless response.is_a? Net::HTTPSuccess
|
293
|
-
break if response.body.size > FETION_SIPP.size
|
294
|
-
end
|
201
|
+
def get_contacts
|
202
|
+
curl_exec(SipcMessage.get_group_list(self))
|
295
203
|
|
296
|
-
response.
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
@logger.debug @contacts.inspect
|
304
|
-
@logger.info "fetion get contacts info success"
|
204
|
+
response = curl_exec(SipcMessage.presence(self))
|
205
|
+
response = curl_exec(SipcMessage.get_group_topic(self))
|
206
|
+
raise FetionException.new('Fetion Error: get contacts failed.') unless response.is_a? Net::HTTPSuccess
|
207
|
+
parse_contacts(response.body)
|
208
|
+
|
209
|
+
curl_exec(SipcMessage.get_address_list(self))
|
210
|
+
pulse
|
305
211
|
end
|
306
212
|
|
307
213
|
def send_msg(receiver, content)
|
308
|
-
@logger.info "fetion
|
309
|
-
|
310
|
-
|
311
|
-
response = curl_exec(next_url, @ssic, FETION_SIPP)
|
214
|
+
@logger.info "fetion send msg to #{receiver}"
|
215
|
+
curl_exec(SipcMessage.send_msg(self, receiver, content))
|
216
|
+
response = pulse
|
312
217
|
|
313
|
-
raise
|
314
|
-
@logger.info "fetion
|
218
|
+
raise SendMsgException.new("Fetion Error: Send sms error") unless response.is_a? Net::HTTPSuccess
|
219
|
+
@logger.info "fetion send msg to #{receiver} success"
|
315
220
|
end
|
316
221
|
|
317
222
|
def send_sms(receiver, content)
|
318
|
-
@logger.info "fetion
|
319
|
-
|
320
|
-
|
321
|
-
response = curl_exec(next_url, @ssic, FETION_SIPP)
|
223
|
+
@logger.info "fetion send cat sms to #{receiver}"
|
224
|
+
curl_exec(SipcMessage.send_cat_sms(self, receiver, content))
|
225
|
+
response = pulse
|
322
226
|
|
323
|
-
raise
|
324
|
-
@logger.info "fetion
|
227
|
+
raise SendSmsException.new("Fetion Error: Send cat sms error") unless response.is_a? Net::HTTPSuccess
|
228
|
+
@logger.info "fetion send cat sms to #{receiver} success"
|
325
229
|
end
|
326
230
|
|
327
|
-
def
|
231
|
+
def set_schedule_sms(receivers, content, time)
|
328
232
|
receivers = Array(receivers)
|
329
233
|
time = time.is_a?(Time) ? time : Time.parse(time)
|
330
234
|
now = Time.now
|
331
235
|
one_year = Time.local(now.year + 1, now.month, now.day, now.hour, now.min, now.sec)
|
332
|
-
raise
|
333
|
-
raise
|
236
|
+
raise SetScheduleSmsException.new("Can't schedule send sms to more than 64 receivers") if receivers.size > 64
|
237
|
+
raise SetScheduleSmsException.new("Schedule time must between #{(now + 600).strftime('%Y-%m-%d %H:%M:%S')} and #{one_year.strftime('%Y-%m-%d %H:%M:%S')}") if time < (now + 600) or time > one_year
|
334
238
|
@logger.info "fetion schedule send sms to #{receivers.join(', ')}"
|
335
239
|
|
336
|
-
|
337
|
-
|
338
|
-
msg = sip_create('S fetion.com.cn SIP-C/2.0', {'F' => @sid, 'I' => next_call, 'Q' => '1 S', 'N' => 'SSSetScheduleCatSms'}, arg) + FETION_SIPP
|
339
|
-
curl_exec(next_url, @ssic, msg)
|
340
|
-
response = curl_exec(next_url, @ssic, FETION_SIPP)
|
240
|
+
curl_exec(SipcMessage.set_schedule_sms(self, receivers, content, time.strftime('%Y-%m-%d %H:%M:%S')))
|
241
|
+
response = pulse
|
341
242
|
|
342
|
-
raise
|
243
|
+
raise SetScheduleSmsException.new("Fetion Error: Set schedule sms error") unless response.is_a? Net::HTTPSuccess
|
343
244
|
@logger.info "fetion schedule send sms to #{receivers.join(', ')} success"
|
344
245
|
end
|
345
246
|
|
346
|
-
def get_personal_info
|
347
|
-
@logger.info "fetion get personal info"
|
348
|
-
arg = %Q{<args><personal attributes="all" /><services version="" attributes="all" /><config version="96" attributes="all" /><mobile-device attributes="all" /></args>}
|
349
|
-
msg = sip_create('S fetion.com.cn SIP-C/2.0', {'F' => @sid, 'I' => next_call, 'Q' => '1 S', 'N' => 'GetPersonalInfo'}, arg) + FETION_SIPP
|
350
|
-
curl_exec(next_url, @ssic, msg)
|
351
|
-
response = curl_exec(next_url, @ssic, FETION_SIPP)
|
352
|
-
raise FetionException.new("Fetion Error: Get personal info error") unless response.is_a? Net::HTTPSuccess
|
353
|
-
|
354
|
-
doc = Nokogiri::XML(response.body.chomp(FETION_SIPP))
|
355
|
-
@person = doc.root.xpath('/results/personal').first
|
356
|
-
@logger.info "fetion get personal info success"
|
357
|
-
end
|
358
|
-
|
359
247
|
# options
|
360
248
|
# friend_mobile
|
361
249
|
# friend_sip
|
@@ -363,44 +251,103 @@ class Fetion
|
|
363
251
|
uri = options[:friend_mobile] ? "tel:#{options[:friend_mobile]}" : "sip:#{options[:friend_sip]}"
|
364
252
|
|
365
253
|
@logger.info "fetion send request to add #{uri} as friend"
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
raise FetionException.new("Fetion Error: Add buddy error") unless response.is_a? Net::HTTPSuccess
|
371
|
-
|
372
|
-
if response.body =~ /No Subscription/
|
373
|
-
raise FetionException.new("Fetion Error: No #{uri}") if options[:friend_sip]
|
374
|
-
|
375
|
-
arg = %Q{<args><contacts><mobile-buddies><mobile-buddy uri="#{uri}" local-name="" buddy-lists="1" desc="#{@person['nickname']}" expose-mobile-no="1" expose-name="1" /></mobile-buddies></contacts></args>}
|
376
|
-
msg = sip_create('S fetion.com.cn SIP-C/2.0', {'F' => @sid, 'I' => next_call, 'Q' => '1 S', 'N' => 'AddMobileBuddy'}, arg) + FETION_SIPP
|
377
|
-
curl_exec(next_url, @ssic, msg)
|
378
|
-
response = curl_exec(next_url, @ssic, FETION_SIPP)
|
379
|
-
raise FetionException.new("Fetion Error: Add buddy error") unless response.is_a? Net::HTTPSuccess
|
380
|
-
|
381
|
-
raise FetionException.new("Fetion Error: No #{uri}") if response.body =~ /Not Found/
|
382
|
-
end
|
254
|
+
curl_exec(SipcMessage.add_buddy(self, options))
|
255
|
+
response = pulse
|
256
|
+
raise AddBuddyException.new("Fetion Error: Add buddy error") unless response.is_a? Net::HTTPSuccess
|
257
|
+
|
383
258
|
@logger.info "fetion send request to add #{uri} as friend success"
|
384
259
|
end
|
385
260
|
|
261
|
+
# options
|
262
|
+
# mobile_no
|
263
|
+
# sip
|
264
|
+
def get_contact_info(options)
|
265
|
+
uri = options[:mobile_no] ? "tel:#{options[:mobile_no]}" : "sip:#{options[:sip]}"
|
266
|
+
|
267
|
+
@logger.info "fetion get contact info of #{uri}"
|
268
|
+
curl_exec(SipcMessage.get_contact_info(self, options))
|
269
|
+
response = pulse
|
270
|
+
|
271
|
+
sipc_response = SipcMessage.sipc_response(response.body)
|
272
|
+
raise Fetion::NoUserException.new("Fetion Error: get contact info #{uri} with #{sipc_response.to_s}") unless SipcMessage::OK === sipc_response
|
273
|
+
|
274
|
+
@logger.info "fetion get contact info of #{uri} success"
|
275
|
+
end
|
276
|
+
|
386
277
|
def logout
|
387
|
-
|
388
|
-
|
389
|
-
curl_exec(next_url, @ssic, msg)
|
390
|
-
response = curl_exec(next_url, @ssic, FETION_SIPP)
|
278
|
+
curl_exec(SipcMessage.logout(self))
|
279
|
+
response = pulse
|
391
280
|
|
392
281
|
# raise FetionException.new("Fetion Error: Logout error") unless response.is_a? Net::HTTPSuccess
|
393
|
-
@logger.info "fetion logout success"
|
394
282
|
end
|
395
283
|
|
396
|
-
def
|
284
|
+
def parse_login_response(response)
|
285
|
+
raise Fetion::LoginException.new('Fetion Error: No ssic found in cookie.') unless response['set-cookie'] =~ /ssic=(.*);/
|
286
|
+
|
287
|
+
@ssic = $1
|
288
|
+
@logger.debug response.body
|
289
|
+
doc = Nokogiri::XML(response.body)
|
290
|
+
results = doc.root
|
291
|
+
@status_code = results["status-code"]
|
292
|
+
user = results.children.first
|
293
|
+
@user_status = user['user-status']
|
294
|
+
@uri = user['uri']
|
295
|
+
@mobile_no = user['mobile-no']
|
296
|
+
@user_id = user['user-id']
|
297
|
+
if @uri =~ /sip:(\d+)@(.+);/
|
298
|
+
@sid = $1
|
299
|
+
end
|
300
|
+
|
301
|
+
@logger.debug "ssic: " + @ssic
|
302
|
+
@logger.debug "status_code: " + @status_code
|
303
|
+
@logger.debug "user_status: " + @user_status
|
304
|
+
@logger.debug "uri: " + @uri
|
305
|
+
@logger.debug "mobile_no: " + @mobile_no
|
306
|
+
@logger.debug "user_id: " + @user_id
|
307
|
+
@logger.debug "sid: " + @sid
|
308
|
+
end
|
309
|
+
|
310
|
+
def parse_info(response_body)
|
311
|
+
response_body.scan(%r{<results>.*?</results>}).each do |results|
|
312
|
+
doc = Nokogiri::XML(results)
|
313
|
+
personal = doc.root.xpath("/results/user-info/personal").first
|
314
|
+
@nickname = personal['nickname']
|
315
|
+
end
|
316
|
+
|
317
|
+
@logger.debug "nickname: #@nickname"
|
318
|
+
end
|
319
|
+
|
320
|
+
def parse_buddies(response_body)
|
321
|
+
response_body.scan(%r{<results>.*?</results>}).each do |results|
|
322
|
+
doc = Nokogiri::XML(results)
|
323
|
+
doc.root.xpath("/results//buddies/b").each do |buddy|
|
324
|
+
@buddies << {:uri => buddy["u"]}
|
325
|
+
end
|
326
|
+
end
|
327
|
+
@logger.debug "buddies: #{@buddies.inspect}"
|
328
|
+
end
|
329
|
+
|
330
|
+
def parse_contacts(response_body)
|
331
|
+
response_body.scan(%r{<events>.*?</events>}).each do |results|
|
332
|
+
doc = Nokogiri::XML(results)
|
333
|
+
doc.root.xpath("/events//c/p").each do |person|
|
334
|
+
@contacts << Contact.new(person) if person['sid']
|
335
|
+
end
|
336
|
+
end
|
337
|
+
@logger.debug "contacts: #{@contacts.inspect}"
|
338
|
+
end
|
339
|
+
|
340
|
+
def pulse
|
341
|
+
curl_exec(SIPP)
|
342
|
+
end
|
343
|
+
|
344
|
+
def curl_exec(body='', url=next_url)
|
397
345
|
@logger.debug "fetion curl exec"
|
398
346
|
@logger.debug "url: #{url}"
|
399
|
-
@logger.debug "ssic: #{ssic}"
|
400
347
|
@logger.debug "body: #{body}"
|
401
348
|
uri = URI.parse(url)
|
402
349
|
http = Net::HTTP.new(uri.host, uri.port)
|
403
|
-
headers = {'Content-Type' => 'application/oct-stream', 'Pragma' => "xz4BBcV#{@guid}", 'User-Agent' =>
|
350
|
+
headers = {'Content-Type' => 'application/oct-stream', 'Pragma' => "xz4BBcV#{@guid}", 'User-Agent' => USER_AGENT, 'Cookie' => "ssic=#{@ssic}", 'Content-Length' => body.length.to_s}
|
404
351
|
response = http.request_post(uri.request_uri, body, headers)
|
405
352
|
|
406
353
|
@logger.debug "response: #{response.inspect}"
|
@@ -409,49 +356,70 @@ class Fetion
|
|
409
356
|
response
|
410
357
|
end
|
411
358
|
|
412
|
-
def
|
413
|
-
|
414
|
-
fields.each {|k, v| sip += "#{k}: #{v}\r\n"}
|
415
|
-
sip += "L: #{arg.size}\r\n\r\n#{arg}"
|
416
|
-
@logger.debug "sip message: #{sip}"
|
417
|
-
sip
|
418
|
-
end
|
419
|
-
|
420
|
-
def calc_response
|
421
|
-
str = [hash_password[8..-1]].pack("H*")
|
422
|
-
key = Digest::SHA1.digest("#{@sid}:#{@domain}:#{str}")
|
423
|
-
|
424
|
-
h1 = Digest::MD5.hexdigest("#{key}:#{@nonce}:#{@cnonce}").upcase
|
425
|
-
h2 = Digest::MD5.hexdigest("REGISTER:#{@sid}").upcase
|
426
|
-
|
427
|
-
Digest::MD5.hexdigest("#{h1}:#{@nonce}:#{h2}").upcase
|
359
|
+
def next_url(t = 's')
|
360
|
+
FETION_URL + "?t=#{t}&i=#{next_seq}"
|
428
361
|
end
|
429
362
|
|
430
|
-
def
|
431
|
-
|
363
|
+
def next_call
|
364
|
+
@call += 1
|
432
365
|
end
|
433
366
|
|
434
|
-
def
|
435
|
-
|
436
|
-
src = salt + Digest::SHA1.digest(@password)
|
437
|
-
'777A6D03' + Digest::SHA1.hexdigest(src).upcase
|
367
|
+
def next_seq
|
368
|
+
@seq += 1
|
438
369
|
end
|
439
370
|
|
440
|
-
def
|
441
|
-
@
|
442
|
-
FETION_URL + "?t=#{t}&i=#{@seq}"
|
371
|
+
def next_alive
|
372
|
+
@alive += 1
|
443
373
|
end
|
444
374
|
|
445
|
-
def
|
446
|
-
@
|
375
|
+
def calc_response
|
376
|
+
encrypted_password = Digest::SHA1.hexdigest([@user_id.to_i].pack("V*") + [Digest::SHA1.hexdigest("#{DOMAIN}:#{@password}")].pack("H*"))
|
377
|
+
rsa_result = "4A026855890197CFDF768597D07200B346F3D676411C6F87368B5C2276DCEDD2"
|
378
|
+
str = @nonce + [encrypted_password].pack("H*") + [rsa_result].pack("H*")
|
379
|
+
rsa_key = OpenSSL::PKey::RSA.new
|
380
|
+
exponent = OpenSSL::BN.new @key[-6..-1].hex.to_s
|
381
|
+
modulus = OpenSSL::BN.new @key[0...-6].hex.to_s
|
382
|
+
rsa_key.e = exponent
|
383
|
+
rsa_key.n = modulus
|
384
|
+
|
385
|
+
rsa_key.public_encrypt(str).unpack("H*").first.upcase
|
447
386
|
end
|
448
387
|
|
449
|
-
def send_command
|
450
|
-
@cat ? 'SendCatSMS' : 'SendSMS'
|
451
|
-
end
|
452
|
-
|
453
388
|
def self?(mobile_or_sid)
|
454
389
|
mobile_or_sid == @mobile_no or mobile_or_sid == @sid
|
455
390
|
end
|
391
|
+
|
392
|
+
[:login, :register, :get_contacts, :logout].each do |method|
|
393
|
+
class_eval <<-EOF
|
394
|
+
alias_method :origin_#{method}, :#{method}
|
395
|
+
|
396
|
+
def #{method}
|
397
|
+
@logger.info "fetion #{method.to_s.gsub(/_/, ' ')}"
|
398
|
+
origin_#{method}
|
399
|
+
@logger.info "fetion #{method.to_s.gsub(/_/, ' ')} success"
|
400
|
+
end
|
401
|
+
EOF
|
402
|
+
end
|
403
|
+
|
404
|
+
[:register_first, :register_second].each do |method|
|
405
|
+
class_eval <<-EOF
|
406
|
+
alias_method :origin_#{method}, :#{method}
|
407
|
+
|
408
|
+
def #{method}
|
409
|
+
@logger.debug "fetion #{method.to_s.gsub(/_/, ' ')}"
|
410
|
+
origin_#{method}
|
411
|
+
@logger.debug "fetion #{method.to_s.gsub(/_/, ' ')} success"
|
412
|
+
end
|
413
|
+
EOF
|
414
|
+
end
|
456
415
|
end
|
457
416
|
|
417
|
+
class FetionException < Exception; end
|
418
|
+
class Fetion::LoginException < FetionException; end
|
419
|
+
class Fetion::NoNonceException < FetionException; end
|
420
|
+
class Fetion::RegisterException < FetionException; end
|
421
|
+
class Fetion::SendSmsException < FetionException; end
|
422
|
+
class Fetion::SendMsgException < FetionException; end
|
423
|
+
class Fetion::SetScheduleSmsException < FetionException; end
|
424
|
+
class Fetion::AddBuddyException < FetionException; end
|
425
|
+
class Fetion::NoUserException < FetionException; end
|