im_contacts 1.2.7
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/LICENSE +20 -0
- data/README +61 -0
- data/Rakefile +91 -0
- data/examples/grab_contacts.rb +23 -0
- data/lib/contacts/aol.rb +154 -0
- data/lib/contacts/base.rb +229 -0
- data/lib/contacts/gmail.rb +35 -0
- data/lib/contacts/hash_ext.rb +9 -0
- data/lib/contacts/hotmail.rb +125 -0
- data/lib/contacts/json_picker.rb +16 -0
- data/lib/contacts/net_ease.rb +117 -0
- data/lib/contacts/plaxo.rb +130 -0
- data/lib/contacts/sina.rb +91 -0
- data/lib/contacts/sohu.rb +59 -0
- data/lib/contacts/yahoo.rb +103 -0
- data/lib/im_contacts.rb +17 -0
- data/test/example_accounts.yml +101 -0
- data/test/test_helper.rb +31 -0
- data/test/test_suite.rb +4 -0
- data/test/unit/aol_contact_importer_test.rb +39 -0
- data/test/unit/gmail_contact_importer_test.rb +39 -0
- data/test/unit/hotmail_contact_importer_test.rb +41 -0
- data/test/unit/net_ease_contact_importer_test.rb +60 -0
- data/test/unit/sina_contact_importer_test.rb +59 -0
- data/test/unit/sohu_contact_importer_test.rb +39 -0
- data/test/unit/test_accounts_test.rb +23 -0
- data/test/unit/yahoo_csv_contact_importer_test.rb +35 -0
- metadata +124 -0
@@ -0,0 +1,125 @@
|
|
1
|
+
class Contacts
|
2
|
+
class Hotmail < Base
|
3
|
+
URL = "https://login.live.com/login.srf?id=2"
|
4
|
+
OLD_CONTACT_LIST_URL = "http://%s/cgi-bin/addresses"
|
5
|
+
NEW_CONTACT_LIST_URL = "http://%s/mail/GetContacts.aspx"
|
6
|
+
CONTACT_LIST_URL = "http://mpeople.live.com/default.aspx?pg=0"
|
7
|
+
COMPOSE_URL = "http://%s/cgi-bin/compose?"
|
8
|
+
PROTOCOL_ERROR = "Hotmail has changed its protocols, please upgrade this library first. If that does not work, report this error at http://rubyforge.org/forum/?group_id=2693"
|
9
|
+
PWDPAD = "IfYouAreReadingThisYouHaveTooMuchFreeTime"
|
10
|
+
MAX_HTTP_THREADS = 8
|
11
|
+
|
12
|
+
def real_connect
|
13
|
+
data, resp, cookies, forward = get(URL)
|
14
|
+
old_url = URL
|
15
|
+
until forward.nil?
|
16
|
+
data, resp, cookies, forward, old_url = get(forward, cookies, old_url) + [forward]
|
17
|
+
end
|
18
|
+
|
19
|
+
postdata = "PPSX=%s&PwdPad=%s&login=%s&passwd=%s&LoginOptions=2&PPFT=%s" % [
|
20
|
+
CGI.escape(data.split("><").grep(/PPSX/).first[/=\S+$/][2..-3]),
|
21
|
+
PWDPAD[0...(PWDPAD.length-@password.length)],
|
22
|
+
CGI.escape(login),
|
23
|
+
CGI.escape(password),
|
24
|
+
CGI.escape(data.split("><").grep(/PPFT/).first[/=\S+$/][2..-3])
|
25
|
+
]
|
26
|
+
|
27
|
+
form_url = data.split("><").grep(/form/).first.split[5][8..-2]
|
28
|
+
data, resp, cookies, forward = post(form_url, postdata, cookies)
|
29
|
+
|
30
|
+
old_url = form_url
|
31
|
+
until cookies =~ /; PPAuth=/ || forward.nil?
|
32
|
+
data, resp, cookies, forward, old_url = get(forward, cookies, old_url) + [forward]
|
33
|
+
end
|
34
|
+
|
35
|
+
if data.index("The e-mail address or password is incorrect")
|
36
|
+
raise AuthenticationError, "Username and password do not match"
|
37
|
+
elsif data != ""
|
38
|
+
raise AuthenticationError, "Required field must not be blank"
|
39
|
+
elsif cookies == ""
|
40
|
+
raise ConnectionError, PROTOCOL_ERROR
|
41
|
+
end
|
42
|
+
|
43
|
+
data, resp, cookies, forward = get("http://mail.live.com/mail", cookies)
|
44
|
+
until forward.nil?
|
45
|
+
data, resp, cookies, forward, old_url = get(forward, cookies, old_url) + [forward]
|
46
|
+
end
|
47
|
+
|
48
|
+
@domain = URI.parse(old_url).host
|
49
|
+
@cookies = cookies
|
50
|
+
rescue AuthenticationError => m
|
51
|
+
if @attempt == 1
|
52
|
+
retry
|
53
|
+
else
|
54
|
+
raise m
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def contacts(options = {})
|
59
|
+
if connected?
|
60
|
+
url = URI.parse(contact_list_url)
|
61
|
+
data, resp, cookies, forward = get( contact_list_url, @cookies )
|
62
|
+
|
63
|
+
if resp.code_type != Net::HTTPOK
|
64
|
+
raise ConnectionError, self.class.const_get(:PROTOCOL_ERROR)
|
65
|
+
end
|
66
|
+
|
67
|
+
@contacts = []
|
68
|
+
build_contacts = []
|
69
|
+
go = true
|
70
|
+
index = 0
|
71
|
+
|
72
|
+
while(go) do
|
73
|
+
go = false
|
74
|
+
url = URI.parse(get_contact_list_url(index))
|
75
|
+
http = open_http(url)
|
76
|
+
resp, data = http.get(get_contact_list_url(index), "Cookie" => @cookies)
|
77
|
+
|
78
|
+
email_match_text_beginning = Regexp.escape("http://m.mail.live.com/?rru=compose&to=")
|
79
|
+
email_match_text_end = Regexp.escape("&")
|
80
|
+
|
81
|
+
raw_html = resp.body.split("
|
82
|
+
").grep(/(?:e|dn)lk[0-9]+/)
|
83
|
+
raw_html.inject(-1) do |memo, row|
|
84
|
+
c_info = row.match(/(e|dn)lk([0-9])+/)
|
85
|
+
|
86
|
+
# Same contact, or different?
|
87
|
+
build_contacts << [] if memo != c_info[2]
|
88
|
+
|
89
|
+
# Grab info
|
90
|
+
case c_info[1]
|
91
|
+
when "e" # Email
|
92
|
+
build_contacts.last[1] = row.match(/#{email_match_text_beginning}(.*)#{email_match_text_end}/)[1]
|
93
|
+
when "dn" # Name
|
94
|
+
build_contacts.last[0] = row.match(/<a[^>]*>(.+)<\/a>/)[1]
|
95
|
+
end
|
96
|
+
|
97
|
+
# Set memo to contact id
|
98
|
+
c_info[2]
|
99
|
+
end
|
100
|
+
|
101
|
+
go = resp.body.include?("ContactList_next")
|
102
|
+
index += 1
|
103
|
+
end
|
104
|
+
|
105
|
+
build_contacts.each do |contact|
|
106
|
+
unless contact[1].nil?
|
107
|
+
# Only return contacts with email addresses
|
108
|
+
contact[1] = CGI::unescape(contact[1])
|
109
|
+
@contacts << contact
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
return @contacts
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def get_contact_list_url(index)
|
118
|
+
"http://mpeople.live.com/default.aspx?pg=#{index}"
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
TYPES[:hotmail] = Hotmail
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
if !Object.const_defined?('ActiveSupport')
|
2
|
+
require 'json'
|
3
|
+
end
|
4
|
+
|
5
|
+
class Contacts
|
6
|
+
def self.parse_json( string )
|
7
|
+
if Object.const_defined?('ActiveSupport') and
|
8
|
+
ActiveSupport.const_defined?('JSON')
|
9
|
+
ActiveSupport::JSON.decode( string )
|
10
|
+
elsif Object.const_defined?('JSON')
|
11
|
+
JSON.parse( string )
|
12
|
+
else
|
13
|
+
raise 'Contacts requires JSON or Rails (with ActiveSupport::JSON)'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
class Contacts
|
2
|
+
class NetEase < Base
|
3
|
+
URL = "http://www.163.com"
|
4
|
+
LOGIN_URL = "https://reg.163.com/logins.jsp"
|
5
|
+
LoginData = {
|
6
|
+
:url2 => {
|
7
|
+
:wy163 => 'http://mail.163.com/errorpage/err_163.htm',
|
8
|
+
:wy126 => 'http://mail.126.com/errorpage/err_126.htm',
|
9
|
+
:yeah => 'http://mail.yeah.net/errorpage/err_yeah.htm'
|
10
|
+
},
|
11
|
+
:url => {
|
12
|
+
:wy163 => 'http://entry.mail.163.com/coremail/fcg/ntesdoor2?lightweight=1&verifycookie=1&language=-1&style=-1&username=%s',
|
13
|
+
:wy126 => 'http://entry.mail.126.com/cgi/ntesdoor?hid=10010102&lightweight=1&verifycookie=1&language=0&style=-1&username=%s',
|
14
|
+
:yeah => 'http://entry.mail.yeah.net/cgi/ntesdoor?lightweight=1&verifycookie=1&style=-1&username=%s'
|
15
|
+
},
|
16
|
+
:product => {
|
17
|
+
:wy163 => 'mail163',
|
18
|
+
:wy126 => 'mail126',
|
19
|
+
:yeah => 'mailyeah'
|
20
|
+
}
|
21
|
+
}
|
22
|
+
ENTER_MAIL_URL = {
|
23
|
+
:wy163 => "http://entry.mail.163.com/coremail/fcg/ntesdoor2?lightweight=1&verifycookie=1&language=-1&style=-1&username=%s",
|
24
|
+
:wy126 => "http://entry.mail.126.com/cgi/ntesdoor?hid=10010102&lightweight=1&verifycookie=1&language=0&style=-1&username=%s",
|
25
|
+
:yeah => "http://entry.mail.yeah.net/cgi/ntesdoor?lightweight=1&verifycookie=1&style=-1&username=%s"
|
26
|
+
}
|
27
|
+
|
28
|
+
CONTACT_LIST_URL = "%ss?sid=%s&func=global:sequential"
|
29
|
+
PROTOCOL_ERROR = "netease has changed its protocols, please upgrade this library first. you can also contact kamechb@gmail.com"
|
30
|
+
|
31
|
+
def initialize(login, password, options={})
|
32
|
+
@mail_type = get_mail_type(login)
|
33
|
+
super(login,password,options)
|
34
|
+
end
|
35
|
+
|
36
|
+
def real_connect
|
37
|
+
login_for_cookies
|
38
|
+
enter_mail_server
|
39
|
+
end
|
40
|
+
|
41
|
+
def contacts
|
42
|
+
return @contacts if @contacts
|
43
|
+
if connected?
|
44
|
+
url = URI.parse(CONTACT_LIST_URL % [@mail_server,@sid])
|
45
|
+
http = open_http(url)
|
46
|
+
postdata = '<?xml version="1.0"?><object><array name="items"><object><string name="func">pab:searchContacts</string><object name="var"><array name="order"><object><string name="field">FN</string><boolean name="ignoreCase">true</boolean></object></array></object></object><object><string name="func">user:getSignatures</string></object><object><string name="func">pab:getAllGroups</string></object></array></object>'
|
47
|
+
set_header = {"Cookie" => @cookies,'Accept' => 'text/javascript','Content-Type' => 'application/xml; charset=UTF-8'}
|
48
|
+
resp, data = http.post("#{url.path}?#{url.query}",postdata,set_header)
|
49
|
+
if resp.code_type != Net::HTTPOK
|
50
|
+
raise ConnectionError, self.class.const_get(:PROTOCOL_ERROR)
|
51
|
+
end
|
52
|
+
parse(data)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def get_mail_type(username)
|
59
|
+
if username.include?("@126.com")
|
60
|
+
:wy126
|
61
|
+
elsif username.include?("@163.com")
|
62
|
+
:wy163
|
63
|
+
elsif username.include?("@yeah.net")
|
64
|
+
:yeah
|
65
|
+
else
|
66
|
+
raise MailServerError, "there are only three mail servers that 126.com, 163.com and yeah.net. please add domain after username"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def parse(data)
|
71
|
+
json_data = Contacts.parse_json(data)
|
72
|
+
json_data['var'][0]['var'].map{|contactor|
|
73
|
+
[contactor['FN'],contactor['EMAIL;PREF']]
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
def login_for_cookies
|
78
|
+
data = {
|
79
|
+
:type => '1',
|
80
|
+
:url => LoginData[:url][@mail_type],
|
81
|
+
:username => @login,
|
82
|
+
:password => @password,
|
83
|
+
:selType => '-1',
|
84
|
+
:remUser => '1',
|
85
|
+
:secure => 'on',
|
86
|
+
:verifycookie => '1',
|
87
|
+
:style => '-1',
|
88
|
+
:product => LoginData[:product][@mail_type],
|
89
|
+
:savelogin => '',
|
90
|
+
:url2 => LoginData[:url2][@mail_type]
|
91
|
+
}
|
92
|
+
postdata = data.to_query_string
|
93
|
+
#login and get cookie
|
94
|
+
data, resp, cookies, forward = post(LOGIN_URL,postdata)
|
95
|
+
@cookies = cookies
|
96
|
+
if data.index(LoginData[:url2][@mail_type])
|
97
|
+
raise AuthenticationError, "Username or password error"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def enter_mail_server
|
102
|
+
#get mail server and sid
|
103
|
+
enter_mail_url = ENTER_MAIL_URL[@mail_type] % @login
|
104
|
+
data, resp, cookies, forward = get(enter_mail_url,@cookies)
|
105
|
+
location = resp['Location']
|
106
|
+
data_reg = /<a.*?(http.*?)main.jsp\?sid=(.*?)\">/
|
107
|
+
location_reg = /(http.*?)main.jsp\?sid=(.*)/
|
108
|
+
unless data.match(data_reg) || location.match(location_reg)
|
109
|
+
raise ConnectionError, self.class.const_get(:PROTOCOL_ERROR)
|
110
|
+
end
|
111
|
+
@cookies = cookies
|
112
|
+
@mail_server = $1
|
113
|
+
@sid = $2
|
114
|
+
end
|
115
|
+
TYPES[:net_ease] = NetEase
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
|
3
|
+
class Contacts
|
4
|
+
class Plaxo < Base
|
5
|
+
URL = "http://www.plaxo.com/"
|
6
|
+
LOGIN_URL = "https://www.plaxo.com/signin"
|
7
|
+
ADDRESS_BOOK_URL = "http://www.plaxo.com/po3/?module=ab&operation=viewFull&mode=normal"
|
8
|
+
CONTACT_LIST_URL = "http://www.plaxo.com/axis/soap/contact?_action=getContacts&_format=xml"
|
9
|
+
PROTOCOL_ERROR = "Plaxo has changed its protocols, please upgrade this library first. If that does not work, dive into the code and submit a patch at http://github.com/cardmagic/contacts"
|
10
|
+
|
11
|
+
def real_connect
|
12
|
+
|
13
|
+
end # real_connect
|
14
|
+
|
15
|
+
def contacts
|
16
|
+
getdata = "&authInfo.authByEmail.email=%s" % CGI.escape(login)
|
17
|
+
getdata += "&authInfo.authByEmail.password=%s" % CGI.escape(password)
|
18
|
+
data, resp, cookies, forward = get(CONTACT_LIST_URL + getdata)
|
19
|
+
|
20
|
+
if resp.code_type != Net::HTTPOK
|
21
|
+
raise ConnectionError, PROTOCOL_ERROR
|
22
|
+
end
|
23
|
+
|
24
|
+
parse data
|
25
|
+
end # contacts
|
26
|
+
|
27
|
+
private
|
28
|
+
def parse(data, options={})
|
29
|
+
doc = REXML::Document.new(data)
|
30
|
+
code = doc.elements['//response/code'].text
|
31
|
+
|
32
|
+
if code == '401'
|
33
|
+
raise AuthenticationError, "Username and password do not match"
|
34
|
+
elsif code == '200'
|
35
|
+
@contacts = []
|
36
|
+
doc.elements.each('//contact') do |cont|
|
37
|
+
name = if cont.elements['fullName']
|
38
|
+
cont.elements['fullName'].text
|
39
|
+
elsif cont.elements['displayName']
|
40
|
+
cont.elements['displayName'].text
|
41
|
+
end
|
42
|
+
email = if cont.elements['email1']
|
43
|
+
cont.elements['email1'].text
|
44
|
+
end
|
45
|
+
if name || email
|
46
|
+
@contacts << [name, email]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
@contacts
|
50
|
+
else
|
51
|
+
raise ConnectionError, PROTOCOL_ERROR
|
52
|
+
end
|
53
|
+
|
54
|
+
end # parse
|
55
|
+
|
56
|
+
end # Plaxo
|
57
|
+
|
58
|
+
TYPES[:plaxo] = Plaxo
|
59
|
+
|
60
|
+
end # Contacts
|
61
|
+
|
62
|
+
|
63
|
+
# sample contacts responses
|
64
|
+
=begin
|
65
|
+
Bad email
|
66
|
+
=========
|
67
|
+
<?xml version="1.0" encoding="utf-8" ?>
|
68
|
+
<ns1:GetContactsResponse xmlns:ns1="Plaxo">
|
69
|
+
<response>
|
70
|
+
<code>401</code>
|
71
|
+
<subCode>1</subCode>
|
72
|
+
<message>User not found.</message>
|
73
|
+
</response>
|
74
|
+
</ns1:GetContactsResponse>
|
75
|
+
|
76
|
+
|
77
|
+
Bad password
|
78
|
+
============
|
79
|
+
<?xml version="1.0" encoding="utf-8" ?>
|
80
|
+
<ns1:GetContactsResponse xmlns:ns1="Plaxo">
|
81
|
+
<response>
|
82
|
+
<code>401</code>
|
83
|
+
<subCode>4</subCode>
|
84
|
+
<message>Bad password or security token.</message>
|
85
|
+
</response>
|
86
|
+
</ns1:GetContactsResponse>
|
87
|
+
|
88
|
+
|
89
|
+
Success
|
90
|
+
=======
|
91
|
+
<?xml version="1.0" encoding="utf-8" ?>
|
92
|
+
<ns1:GetContactsResponse xmlns:ns1="Plaxo">
|
93
|
+
|
94
|
+
<response>
|
95
|
+
<code>200</code>
|
96
|
+
<message>OK</message>
|
97
|
+
<userId>77311236242</userId>
|
98
|
+
</response>
|
99
|
+
|
100
|
+
<contacts>
|
101
|
+
|
102
|
+
<contact>
|
103
|
+
<itemId>61312569</itemId>
|
104
|
+
<displayName>Joe Blow1</displayName>
|
105
|
+
<fullName>Joe Blow1</fullName>
|
106
|
+
<firstName>Joe</firstName>
|
107
|
+
<lastName>Blow1</lastName>
|
108
|
+
<homeEmail1>joeblow1@mailinator.com</homeEmail1>
|
109
|
+
<email1>joeblow1@mailinator.com</email1>
|
110
|
+
<folderId>5291351</folderId>
|
111
|
+
</contact>
|
112
|
+
|
113
|
+
<contact>
|
114
|
+
<itemId>61313159</itemId>
|
115
|
+
<displayName>Joe Blow2</displayName>
|
116
|
+
<fullName>Joe Blow2</fullName>
|
117
|
+
<firstName>Joe</firstName>
|
118
|
+
<lastName>Blow2</lastName>
|
119
|
+
<homeEmail1>joeblow2@mailinator.com</homeEmail1>
|
120
|
+
<email1>joeblow2@mailinator.com</email1>
|
121
|
+
<folderId>5291351</folderId>
|
122
|
+
</contact>
|
123
|
+
|
124
|
+
</contacts>
|
125
|
+
|
126
|
+
<totalCount>2</totalCount>
|
127
|
+
<editCounter>3</editCounter>
|
128
|
+
|
129
|
+
</ns1:GetContactsResponse>
|
130
|
+
=end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
class Contacts
|
2
|
+
class Sina < Base
|
3
|
+
URL = "http://mail.sina.com.cn"
|
4
|
+
LOGIN_URL = {
|
5
|
+
:sina_cn => "https://mail.sina.com.cn/cgi-bin/cnlogin.php",
|
6
|
+
:sina_com => "https://mail.sina.com.cn/cgi-bin/login.php"
|
7
|
+
}
|
8
|
+
LOGIN_COOKIE = {
|
9
|
+
:sina_cn => "sina_cn_mail_id=nonobo_t; sina_cn_mail_recid=true",
|
10
|
+
:sina_com => "sina_free_mail_id=fangs2; sina_free_mail_recid=true; sina_free_mail_ltype=uid; sina_vip_mail_recid=false"
|
11
|
+
}
|
12
|
+
DOMAIN = {
|
13
|
+
:sina_cn => 'sina.cn',
|
14
|
+
:sina_com => 'sina.com'
|
15
|
+
}
|
16
|
+
PROTOCOL_ERROR = "sina has changed its protocols, please upgrade this library first. you can also contact kamechb@gmail.com"
|
17
|
+
|
18
|
+
def initialize(login, password, options={})
|
19
|
+
@mail_type = get_mail_type(login)
|
20
|
+
super(login,password,options)
|
21
|
+
end
|
22
|
+
|
23
|
+
def real_connect
|
24
|
+
login_for_cookies
|
25
|
+
redirect_for_location
|
26
|
+
end
|
27
|
+
|
28
|
+
def contacts
|
29
|
+
return @contacts if @contacts
|
30
|
+
if connected?
|
31
|
+
data, resp, cookies, forward = get(@mail_url,@cookies)
|
32
|
+
if resp.code_type != Net::HTTPOK
|
33
|
+
raise ConnectionError, self.class.const_get(:PROTOCOL_ERROR)
|
34
|
+
end
|
35
|
+
parse(data)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def get_mail_type(username)
|
42
|
+
if username.include?("@sina.com")
|
43
|
+
:sina_com
|
44
|
+
elsif username.include?("@sina.cn")
|
45
|
+
:sina_cn
|
46
|
+
else
|
47
|
+
raise MailServerError, "there are only two mail servers that sina.com and sina.cn. please add domain after username"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def parse(data)
|
52
|
+
data =~ /conf.*?contacts:.*?(\{.*?\}),\s*groups:/m
|
53
|
+
contacts = $1.gsub(""",'')
|
54
|
+
contacts = ActiveSupport::JSON.decode(contacts)
|
55
|
+
contacts['contact'].map{|contactor|
|
56
|
+
[contactor['name'],contactor['email']]
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
def login_for_cookies
|
61
|
+
data = {
|
62
|
+
:domain => DOMAIN[@mail_type],
|
63
|
+
:logintype => 'uid',
|
64
|
+
:u => @login,
|
65
|
+
:psw => @password,
|
66
|
+
:savelogin => 'on',
|
67
|
+
:sshchk => 'on',
|
68
|
+
:ssl => 'on'
|
69
|
+
}
|
70
|
+
data, resp, cookies, forward = post(LOGIN_URL[@mail_type],data.to_query_string,LOGIN_COOKIE[@mail_type])
|
71
|
+
login_faile_flag = %r{form.*?action.*?http.*?mail.sina.com.cn/cgi-bin/.*?login.php}m
|
72
|
+
if data.match(login_faile_flag)
|
73
|
+
raise AuthenticationError, "Username or password error"
|
74
|
+
end
|
75
|
+
data.match(/URL=(http:\/\/.*?)'>/)
|
76
|
+
@redirect_url = $1
|
77
|
+
@mail_server = @redirect_url.match(/(http:\/\/.*\..*?)\//)
|
78
|
+
@cookies = cookies
|
79
|
+
end
|
80
|
+
|
81
|
+
def redirect_for_location
|
82
|
+
data, resp, cookies, forward = get(@redirect_url,@cookies)
|
83
|
+
location = resp['Location']
|
84
|
+
@mail_url = location.index("http://") ? location : "#{@mail_server}#{location}"
|
85
|
+
@cookies = cookies
|
86
|
+
end
|
87
|
+
|
88
|
+
TYPES[:sina] = Sina
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
class Contacts
|
2
|
+
class Sohu < Base
|
3
|
+
URL = "http://mail.sohu.com"
|
4
|
+
DOMAIN = "sohu.com"
|
5
|
+
LOGIN_URL = "https://passport.sohu.com/sso/login.jsp"
|
6
|
+
LOGIN_COOKIE = "IPLOC=CN3301; SUV=1008301317090277"
|
7
|
+
MAIL_URL = "http://mail.sohu.com/bapp/81/main"
|
8
|
+
PROTOCOL_ERROR = "sohu has changed its protocols, please upgrade this library first. you can also contact kamechb@gmail.com"
|
9
|
+
|
10
|
+
def real_connect
|
11
|
+
login_for_cookies
|
12
|
+
end
|
13
|
+
|
14
|
+
def contacts
|
15
|
+
return @contacts if @contacts
|
16
|
+
if connected?
|
17
|
+
data, resp, cookies, forward = get(MAIL_URL,@cookies)
|
18
|
+
if resp.code_type != Net::HTTPOK
|
19
|
+
raise ConnectionError, self.class.const_get(:PROTOCOL_ERROR)
|
20
|
+
end
|
21
|
+
parse(data)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
|
28
|
+
def parse(data)
|
29
|
+
data.match(/ADDRESSES.*?'(\{.*?\})';/m)
|
30
|
+
contacts = ActiveSupport::JSON.decode($1)
|
31
|
+
contacts['contact'].map{|contactor|
|
32
|
+
[contactor['nickname'],contactor['email']]
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def login_for_cookies
|
37
|
+
data = {
|
38
|
+
:userid => @login,
|
39
|
+
:password => @password,
|
40
|
+
:appid => '1000',
|
41
|
+
:persistentcookie => '0',
|
42
|
+
:s => '1283173792650',
|
43
|
+
:b => '2',
|
44
|
+
:w => '1280',
|
45
|
+
:pwdtype => '0',
|
46
|
+
:v => '26'
|
47
|
+
}
|
48
|
+
data, resp, cookies, forward = get("#{LOGIN_URL}?#{data.to_query_string}",LOGIN_COOKIE)
|
49
|
+
login_faile_flag = %r{login_status.*?error}
|
50
|
+
if data.match(login_faile_flag)
|
51
|
+
raise AuthenticationError, "Username or password error"
|
52
|
+
end
|
53
|
+
@cookies = cookies
|
54
|
+
end
|
55
|
+
|
56
|
+
TYPES[:sohu] = Sohu
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
class Contacts
|
2
|
+
class Yahoo < Base
|
3
|
+
URL = "http://mail.yahoo.com/"
|
4
|
+
LOGIN_URL = "https://login.yahoo.com/config/login"
|
5
|
+
ADDRESS_BOOK_URL = "http://address.mail.yahoo.com/?.rand=430244936"
|
6
|
+
CONTACT_LIST_URL = "http://address.mail.yahoo.com/?_src=&_crumb=crumb&sortfield=3&bucket=1&scroll=1&VPC=social_list&.r=time"
|
7
|
+
PROTOCOL_ERROR = "Yahoo has changed its protocols, please upgrade this library first. If that does not work, dive into the code and submit a patch at http://github.com/cardmagic/contacts"
|
8
|
+
|
9
|
+
def real_connect
|
10
|
+
postdata = ".tries=2&.src=ym&.md5=&.hash=&.js=&.last=&promo=&.intl=us&.bypass="
|
11
|
+
postdata += "&.partner=&.u=4eo6isd23l8r3&.v=0&.challenge=gsMsEcoZP7km3N3NeI4mX"
|
12
|
+
postdata += "kGB7zMV&.yplus=&.emailCode=&pkg=&stepid=&.ev=&hasMsgr=1&.chkP=Y&."
|
13
|
+
postdata += "done=#{CGI.escape(URL)}&login=#{CGI.escape(login)}&passwd=#{CGI.escape(password)}"
|
14
|
+
|
15
|
+
data, resp, cookies, forward = post(LOGIN_URL, postdata)
|
16
|
+
|
17
|
+
if data.index("Invalid ID or password") || data.index("This ID is not yet taken")
|
18
|
+
raise AuthenticationError, "Username and password do not match"
|
19
|
+
elsif data.index("Sign in") && data.index("to Yahoo!")
|
20
|
+
raise AuthenticationError, "Required field must not be blank"
|
21
|
+
elsif !data.match(/uncompressed\/chunked/)
|
22
|
+
raise ConnectionError, PROTOCOL_ERROR
|
23
|
+
elsif cookies == ""
|
24
|
+
raise ConnectionError, PROTOCOL_ERROR
|
25
|
+
end
|
26
|
+
|
27
|
+
data, resp, cookies, forward = get(forward, cookies, LOGIN_URL)
|
28
|
+
|
29
|
+
if resp.code_type != Net::HTTPOK
|
30
|
+
raise ConnectionError, PROTOCOL_ERROR
|
31
|
+
end
|
32
|
+
|
33
|
+
@cookies = cookies
|
34
|
+
end
|
35
|
+
|
36
|
+
def contacts
|
37
|
+
return @contacts if @contacts
|
38
|
+
if connected?
|
39
|
+
# first, get the addressbook site with the new crumb parameter
|
40
|
+
url = URI.parse(address_book_url)
|
41
|
+
http = open_http(url)
|
42
|
+
resp, data = http.get("#{url.path}?#{url.query}",
|
43
|
+
"Cookie" => @cookies
|
44
|
+
)
|
45
|
+
|
46
|
+
if resp.code_type != Net::HTTPOK
|
47
|
+
raise ConnectionError, self.class.const_get(:PROTOCOL_ERROR)
|
48
|
+
end
|
49
|
+
|
50
|
+
crumb = data.to_s[/dotCrumb: '(.*?)'/][13...-1]
|
51
|
+
|
52
|
+
# now proceed with the new ".crumb" parameter to get the csv data
|
53
|
+
url = URI.parse(contact_list_url.sub("_crumb=crumb","_crumb=#{crumb}").sub("time", Time.now.to_f.to_s.sub(".","")[0...-2]))
|
54
|
+
http = open_http(url)
|
55
|
+
resp, more_data = http.get("#{url.path}?#{url.query}",
|
56
|
+
"Cookie" => @cookies,
|
57
|
+
"X-Requested-With" => "XMLHttpRequest",
|
58
|
+
"Referer" => address_book_url
|
59
|
+
)
|
60
|
+
|
61
|
+
if resp.code_type != Net::HTTPOK
|
62
|
+
raise ConnectionError, self.class.const_get(:PROTOCOL_ERROR)
|
63
|
+
end
|
64
|
+
|
65
|
+
if more_data =~ /"TotalABContacts":(\d+)/
|
66
|
+
total = $1.to_i
|
67
|
+
((total / 50.0).ceil).times do |i|
|
68
|
+
# now proceed with the new ".crumb" parameter to get the csv data
|
69
|
+
url = URI.parse(contact_list_url.sub("bucket=1","bucket=#{i}").sub("_crumb=crumb","_crumb=#{crumb}").sub("time", Time.now.to_f.to_s.sub(".","")[0...-2]))
|
70
|
+
http = open_http(url)
|
71
|
+
resp, more_data = http.get("#{url.path}?#{url.query}",
|
72
|
+
"Cookie" => @cookies,
|
73
|
+
"X-Requested-With" => "XMLHttpRequest",
|
74
|
+
"Referer" => address_book_url
|
75
|
+
)
|
76
|
+
|
77
|
+
if resp.code_type != Net::HTTPOK
|
78
|
+
raise ConnectionError, self.class.const_get(:PROTOCOL_ERROR)
|
79
|
+
end
|
80
|
+
|
81
|
+
parse more_data
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
@contacts
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def parse(data, options={})
|
92
|
+
@contacts ||= []
|
93
|
+
@contacts += Contacts.parse_json(data)["response"]["ResultSet"]["Contacts"].to_a.select{|contact|!contact["email"].to_s.empty?}.map do |contact|
|
94
|
+
name = contact["contactName"].split(",")
|
95
|
+
[[name.pop, name.join(",")].join(" ").strip, contact["email"]]
|
96
|
+
end if data =~ /^\{"response":/
|
97
|
+
@contacts
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
TYPES[:yahoo] = Yahoo
|
103
|
+
end
|
data/lib/im_contacts.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)+"/contacts/")
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
unless Object.const_defined?('ActiveSupport')
|
5
|
+
require 'active_support'
|
6
|
+
end
|
7
|
+
require 'base'
|
8
|
+
require 'gmail'
|
9
|
+
require 'hotmail'
|
10
|
+
require 'yahoo'
|
11
|
+
require 'plaxo'
|
12
|
+
require 'aol'
|
13
|
+
require 'net_ease'
|
14
|
+
require 'sina'
|
15
|
+
require 'sohu'
|
16
|
+
require 'json_picker'
|
17
|
+
require 'hash_ext'
|