contacts 1.0.18 → 1.0.19
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/contacts.rb +2 -1
- data/lib/contacts/aol.rb +148 -0
- data/lib/contacts/base.rb +1 -1
- data/lib/contacts/gmail.rb +1 -2
- data/lib/contacts/hotmail.rb +6 -7
- data/lib/contacts/yahoo.rb +53 -15
- metadata +2 -1
data/lib/contacts.rb
CHANGED
data/lib/contacts/aol.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
class Hash
|
2
|
+
def to_query_string
|
3
|
+
u = ERB::Util.method(:u)
|
4
|
+
map { |k, v|
|
5
|
+
u.call(k) + "=" + u.call(v)
|
6
|
+
}.join("&")
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class Contacts
|
11
|
+
require 'hpricot'
|
12
|
+
class Aol < Base
|
13
|
+
URL = "http://www.aol.com/"
|
14
|
+
LOGIN_URL = "https://my.screenname.aol.com/_cqr/login/login.psp"
|
15
|
+
LOGIN_REFERER_URL = "http://webmail.aol.com/"
|
16
|
+
LOGIN_REFERER_PATH = "sitedomain=sns.webmail.aol.com&lang=en&locale=us&authLev=0&uitype=mini&loginId=&redirType=js&xchk=false"
|
17
|
+
AOL_NUM = "29970-343" # this seems to change each time they change the protocol
|
18
|
+
|
19
|
+
CONTACT_LIST_URL = "http://webmail.aol.com/#{AOL_NUM}/aim-2/en-us/Lite/ContactList.aspx?folder=Inbox&showUserFolders=False"
|
20
|
+
CONTACT_LIST_CSV_URL = "http://webmail.aol.com/#{AOL_NUM}/aim-2/en-us/Lite/ABExport.aspx?command=all"
|
21
|
+
PROTOCOL_ERROR = "AOL 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"
|
22
|
+
|
23
|
+
def real_connect
|
24
|
+
|
25
|
+
postdata = {
|
26
|
+
"loginId" => login,
|
27
|
+
"password" => password,
|
28
|
+
"rememberMe" => "on",
|
29
|
+
"_sns_fg_color_" => "",
|
30
|
+
"_sns_err_color_" => "",
|
31
|
+
"_sns_link_color_" => "",
|
32
|
+
"_sns_width_" => "",
|
33
|
+
"_sns_height_" => "",
|
34
|
+
"offerId" => "mail-second-en-us",
|
35
|
+
"_sns_bg_color_" => "",
|
36
|
+
"sitedomain" => "sns.webmail.aol.com",
|
37
|
+
"regPromoCode" => "",
|
38
|
+
"mcState" => "initialized",
|
39
|
+
"uitype" => "std",
|
40
|
+
"siteId" => "",
|
41
|
+
"lang" => "en",
|
42
|
+
"locale" => "us",
|
43
|
+
"authLev" => "0",
|
44
|
+
"siteState" => "",
|
45
|
+
"isSiteStateEncoded" => "false",
|
46
|
+
"use_aam" => "0",
|
47
|
+
"seamless" => "novl",
|
48
|
+
"aolsubmit" => CGI.escape("Sign In"),
|
49
|
+
"idType" => "SN",
|
50
|
+
"usrd" => "",
|
51
|
+
"doSSL" => "",
|
52
|
+
"redirType" => "",
|
53
|
+
"xchk" => "false"
|
54
|
+
}
|
55
|
+
|
56
|
+
# Get this cookie and stick it in the form to confirm to Aol that your cookies work
|
57
|
+
data, resp, cookies, forward = get(URL)
|
58
|
+
postdata["stips"] = cookie_hash_from_string(cookies)["stips"]
|
59
|
+
postdata["tst"] = cookie_hash_from_string(cookies)["tst"]
|
60
|
+
|
61
|
+
data, resp, cookies, forward, old_url = get(LOGIN_REFERER_URL, cookies) + [URL]
|
62
|
+
until forward.nil?
|
63
|
+
data, resp, cookies, forward, old_url = get(forward, cookies, old_url) + [forward]
|
64
|
+
end
|
65
|
+
|
66
|
+
data, resp, cookies, forward, old_url = get("#{LOGIN_URL}?#{LOGIN_REFERER_PATH}", cookies) + [LOGIN_REFERER_URL]
|
67
|
+
until forward.nil?
|
68
|
+
data, resp, cookies, forward, old_url = get(forward, cookies, old_url) + [forward]
|
69
|
+
end
|
70
|
+
|
71
|
+
doc = Hpricot(data)
|
72
|
+
(doc/:input).each do |input|
|
73
|
+
postdata["usrd"] = input.attributes["value"] if input.attributes["name"] == "usrd"
|
74
|
+
end
|
75
|
+
# parse data for <input name="usrd" value="2726212" type="hidden"> and add it to the postdata
|
76
|
+
|
77
|
+
postdata["SNS_SC"] = cookie_hash_from_string(cookies)["SNS_SC"]
|
78
|
+
postdata["SNS_LDC"] = cookie_hash_from_string(cookies)["SNS_LDC"]
|
79
|
+
postdata["LTState"] = cookie_hash_from_string(cookies)["LTState"]
|
80
|
+
# raise data.inspect
|
81
|
+
|
82
|
+
data, resp, cookies, forward, old_url = post(LOGIN_URL, postdata.to_query_string, cookies, LOGIN_REFERER_URL) + [LOGIN_REFERER_URL]
|
83
|
+
|
84
|
+
until forward.nil?
|
85
|
+
data, resp, cookies, forward, old_url = get(forward, cookies, old_url) + [forward]
|
86
|
+
end
|
87
|
+
|
88
|
+
if data.index("Invalid Screen Name or Password.")
|
89
|
+
raise AuthenticationError, "Username and password do not match"
|
90
|
+
elsif data.index("Required field must not be blank")
|
91
|
+
raise AuthenticationError, "Login and password must not be blank"
|
92
|
+
elsif data.index("errormsg_0_logincaptcha")
|
93
|
+
raise AuthenticationError, "Captcha error"
|
94
|
+
elsif data.index("Invalid request")
|
95
|
+
raise ConnectionError, PROTOCOL_ERROR
|
96
|
+
elsif cookies == ""
|
97
|
+
raise ConnectionError, PROTOCOL_ERROR
|
98
|
+
end
|
99
|
+
|
100
|
+
@cookies = cookies
|
101
|
+
end
|
102
|
+
|
103
|
+
def contacts
|
104
|
+
postdata = {
|
105
|
+
"file" => 'contacts',
|
106
|
+
"fileType" => 'csv'
|
107
|
+
}
|
108
|
+
|
109
|
+
return @contacts if @contacts
|
110
|
+
if connected?
|
111
|
+
data, resp, cookies, forward, old_url = get(CONTACT_LIST_URL, @cookies, CONTACT_LIST_URL) + [CONTACT_LIST_URL]
|
112
|
+
|
113
|
+
until forward.nil?
|
114
|
+
data, resp, cookies, forward, old_url = get(forward, cookies, old_url) + [forward]
|
115
|
+
end
|
116
|
+
|
117
|
+
if resp.code_type != Net::HTTPOK
|
118
|
+
raise ConnectionError, self.class.const_get(:PROTOCOL_ERROR)
|
119
|
+
end
|
120
|
+
|
121
|
+
# parse data and grab <input name="user" value="8QzMPIAKs2" type="hidden">
|
122
|
+
doc = Hpricot(data)
|
123
|
+
(doc/:input).each do |input|
|
124
|
+
postdata["user"] = input.attributes["value"] if input.attributes["name"] == "user"
|
125
|
+
end
|
126
|
+
|
127
|
+
data, resp, cookies, forward, old_url = get(CONTACT_LIST_CSV_URL, @cookies, CONTACT_LIST_URL) + [CONTACT_LIST_URL]
|
128
|
+
|
129
|
+
if forward.nil?
|
130
|
+
parse data
|
131
|
+
else
|
132
|
+
raise AuthenticationError, "Account cancelled"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
private
|
137
|
+
|
138
|
+
def parse(data, options={})
|
139
|
+
data = CSV.parse(data)
|
140
|
+
col_names = data.shift
|
141
|
+
@contacts = data.map do |person|
|
142
|
+
["#{person[0]} #{person[1]}", person[4]] unless person[4].empty?
|
143
|
+
end.compact
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
TYPES[:aol] = Aol
|
148
|
+
end
|
data/lib/contacts/base.rb
CHANGED
data/lib/contacts/gmail.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# Use ActiveSupport's version of JSON if available
|
2
2
|
if Object.const_defined?('ActiveSupport') && ActiveSupport.const_defined?('JSON') && ActiveSupport::JSON.is_a?(Class)
|
3
|
-
puts JSON.class
|
4
3
|
class JSON
|
5
4
|
def self.parse(i)
|
6
5
|
ActiveSupport::JSON.decode(i)
|
@@ -81,7 +80,7 @@ class Contacts
|
|
81
80
|
# Default format.
|
82
81
|
# ['Name', 'Email1', 'Email2', ...]
|
83
82
|
if @contacts != nil
|
84
|
-
@contacts = @contacts.
|
83
|
+
@contacts = @contacts.select {|c| !c["Emails"].nil?}.map do |c|
|
85
84
|
name, emails = c.values_at "Name", "Emails"
|
86
85
|
# emails are returned in a form of
|
87
86
|
# [{"Address"=>"home.email@gmail.com"}, {"Type"=>{"Id"=>"WORK"}, "Address"=>"work.email@gmail.com"}]
|
data/lib/contacts/hotmail.rb
CHANGED
@@ -27,6 +27,11 @@ class Contacts
|
|
27
27
|
form_url = data.split("><").grep(/form/).first.split[5][8..-2]
|
28
28
|
data, resp, cookies, forward = post(form_url, postdata, cookies)
|
29
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
|
+
|
30
35
|
if data.index("The e-mail address or password is incorrect")
|
31
36
|
raise AuthenticationError, "Username and password do not match"
|
32
37
|
elsif data != ""
|
@@ -34,13 +39,7 @@ class Contacts
|
|
34
39
|
elsif cookies == ""
|
35
40
|
raise ConnectionError, PROTOCOL_ERROR
|
36
41
|
end
|
37
|
-
|
38
|
-
old_url = form_url
|
39
|
-
|
40
|
-
until forward.nil?
|
41
|
-
data, resp, cookies, forward, old_url = get(forward, cookies, old_url) + [forward]
|
42
|
-
end
|
43
|
-
|
42
|
+
|
44
43
|
data, resp, cookies, forward = get("http://mail.live.com/mail", cookies)
|
45
44
|
until forward.nil?
|
46
45
|
data, resp, cookies, forward, old_url = get(forward, cookies, old_url) + [forward]
|
data/lib/contacts/yahoo.rb
CHANGED
@@ -1,14 +1,23 @@
|
|
1
|
-
|
1
|
+
# Use ActiveSupport's version of JSON if available
|
2
|
+
if Object.const_defined?('ActiveSupport') && ActiveSupport.const_defined?('JSON') && ActiveSupport::JSON.is_a?(Class)
|
3
|
+
class JSON
|
4
|
+
def self.parse(i)
|
5
|
+
ActiveSupport::JSON.decode(i)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
else
|
9
|
+
require 'json/add/rails'
|
10
|
+
end
|
2
11
|
|
3
12
|
class Contacts
|
4
13
|
class Yahoo < Base
|
5
14
|
URL = "http://mail.yahoo.com/"
|
6
15
|
LOGIN_URL = "https://login.yahoo.com/config/login"
|
7
|
-
ADDRESS_BOOK_URL = "http://address.mail.yahoo.com
|
8
|
-
CONTACT_LIST_URL = "http://address.yahoo.com
|
16
|
+
ADDRESS_BOOK_URL = "http://address.mail.yahoo.com/?.rand=430244936"
|
17
|
+
CONTACT_LIST_URL = "http://address.mail.yahoo.com/?_src=&_crumb=crumb&sortfield=3&bucket=1&scroll=1&VPC=social_list&.r=time"
|
9
18
|
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"
|
10
19
|
|
11
|
-
def real_connect
|
20
|
+
def real_connect
|
12
21
|
postdata = ".tries=2&.src=ym&.md5=&.hash=&.js=&.last=&promo=&.intl=us&.bypass="
|
13
22
|
postdata += "&.partner=&.u=4eo6isd23l8r3&.v=0&.challenge=gsMsEcoZP7km3N3NeI4mX"
|
14
23
|
postdata += "kGB7zMV&.yplus=&.emailCode=&pkg=&stepid=&.ev=&hasMsgr=1&.chkP=Y&."
|
@@ -48,32 +57,61 @@ class Contacts
|
|
48
57
|
if resp.code_type != Net::HTTPOK
|
49
58
|
raise ConnectionError, self.class.const_get(:PROTOCOL_ERROR)
|
50
59
|
end
|
51
|
-
|
52
|
-
crumb = data.to_s[/
|
60
|
+
|
61
|
+
crumb = data.to_s[/dotCrumb: '(.*?)'/][13...-1]
|
53
62
|
|
54
63
|
# now proceed with the new ".crumb" parameter to get the csv data
|
55
|
-
url = URI.parse("
|
64
|
+
url = URI.parse(contact_list_url.sub("_crumb=crumb","_crumb=#{crumb}").sub("time", Time.now.to_f.to_s.sub(".","")[0...-2]))
|
56
65
|
http = open_http(url)
|
57
|
-
resp,
|
58
|
-
"Cookie" => @cookies
|
66
|
+
resp, more_data = http.get("#{url.path}?#{url.query}",
|
67
|
+
"Cookie" => @cookies,
|
68
|
+
"X-Requested-With" => "XMLHttpRequest",
|
69
|
+
"Referer" => address_book_url
|
59
70
|
)
|
60
71
|
|
61
72
|
if resp.code_type != Net::HTTPOK
|
62
73
|
raise ConnectionError, self.class.const_get(:PROTOCOL_ERROR)
|
63
74
|
end
|
64
|
-
|
75
|
+
|
65
76
|
parse data
|
77
|
+
|
78
|
+
parse more_data
|
79
|
+
|
80
|
+
if more_data =~ /"TotalABContacts":(\d+)/
|
81
|
+
total = $1.to_i
|
82
|
+
((total / 50)).times do |i|
|
83
|
+
# now proceed with the new ".crumb" parameter to get the csv data
|
84
|
+
url = URI.parse(contact_list_url.sub("bucket=1","bucket=#{i+2}").sub("_crumb=crumb","_crumb=#{crumb}").sub("time", Time.now.to_f.to_s.sub(".","")[0...-2]))
|
85
|
+
http = open_http(url)
|
86
|
+
resp, more_data = http.get("#{url.path}?#{url.query}",
|
87
|
+
"Cookie" => @cookies,
|
88
|
+
"X-Requested-With" => "XMLHttpRequest",
|
89
|
+
"Referer" => address_book_url
|
90
|
+
)
|
91
|
+
|
92
|
+
if resp.code_type != Net::HTTPOK
|
93
|
+
raise ConnectionError, self.class.const_get(:PROTOCOL_ERROR)
|
94
|
+
end
|
95
|
+
|
96
|
+
parse more_data
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
@contacts
|
66
101
|
end
|
67
102
|
end
|
68
103
|
|
69
104
|
private
|
70
105
|
|
71
106
|
def parse(data, options={})
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
107
|
+
@contacts ||= []
|
108
|
+
if data =~ /var InitialContacts = (\[.*?\])/
|
109
|
+
@contacts += JSON.parse($1).select{|contact|!contact["email"].to_s.empty?}.map{|contact|[contact["contactName"], contact["email"]]}
|
110
|
+
elsif data =~ /^\{"response":/
|
111
|
+
@contacts += JSON.parse(data)["response"]["ResultSet"]["Contacts"].to_a.select{|contact|!contact["email"].to_s.empty?}.map{|contact|[contact["contactName"], contact["email"]]}
|
112
|
+
else
|
113
|
+
@contacts
|
114
|
+
end
|
77
115
|
end
|
78
116
|
|
79
117
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: contacts
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.19
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lucas Carlson
|
@@ -38,6 +38,7 @@ files:
|
|
38
38
|
- lib/contacts.rb
|
39
39
|
- lib/contacts/base.rb
|
40
40
|
- lib/contacts/gmail.rb
|
41
|
+
- lib/contacts/aol.rb
|
41
42
|
- lib/contacts/hotmail.rb
|
42
43
|
- lib/contacts/plaxo.rb
|
43
44
|
- lib/contacts/yahoo.rb
|