contacts 1.0.18 → 1.0.19

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,4 +5,5 @@ require 'base'
5
5
  require 'gmail'
6
6
  require 'hotmail'
7
7
  require 'yahoo'
8
- require 'plaxo'
8
+ require 'plaxo'
9
+ require 'aol'
@@ -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
@@ -9,7 +9,7 @@ require "erb"
9
9
 
10
10
  class Contacts
11
11
  TYPES = {}
12
- VERSION = "1.0.18"
12
+ VERSION = "1.0.19"
13
13
 
14
14
  class Base
15
15
  def initialize(login, password)
@@ -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.delete_if {|c| c["Emails"].nil?}.map do |c|
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"}]
@@ -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]
@@ -1,14 +1,23 @@
1
- require 'csv'
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/?1&VPC=import_export"
8
- CONTACT_LIST_URL = "http://address.yahoo.com/index.php?VPC=import_export&A=B&submit[action_export_yahoo]=Export%20Now"
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[/id="crumb2" value="(.*?)"/][19...-1]
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("#{contact_list_url}&.crumb=#{crumb}")
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, data = http.get("#{url.path}?#{url.query}",
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
- data = CSV.parse(data)
73
- col_names = data.shift
74
- @contacts = data.map do |person|
75
- [[person[0], person[1], person[2]].delete_if{|i|i.empty?}.join(" "), person[4]] unless person[4].empty?
76
- end.compact
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.18
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