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