Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
2d202dd
Fixed bug with hotmail skipping 1st contact of each page
glennfu Apr 12, 2010
8f4fdc6
Fixed yahoo bug with missing last page and also with reversed first/l…
glennfu Apr 12, 2010
39bf812
Added new gemspec
glennfu Apr 12, 2010
14b3d75
Merge with upstream
glennfu Aug 13, 2010
8ef5028
Fixes Yahoo returning nil instead of [] when finding 0 contacts
glennfu Aug 13, 2010
25794ec
Fixes broken hotmail imports
glennfu Sep 27, 2010
832d6d6
Merged with upstream
glennfu Sep 27, 2010
a7b6b2b
Thanks to mrich54907 - fixed hotmail import bug
glennfu Nov 30, 2010
364d76c
Attempts to fix some Yahoo issues... still failing incorrectly with w…
glennfu Nov 30, 2010
f0c8e68
Incorporated AOL fix from cbaker
glennfu May 12, 2011
ddce190
Fixed aol not recognizing bad password error
glennfu May 12, 2011
5193d46
No more hard-coding the AOL_NUM as it's changing too frequently, scra…
glennfu May 23, 2011
0806eb9
Whoops forgot to step the gem version
glennfu May 23, 2011
c8bf7e5
Fixes AOL import bug when a contact's name as a double-quote in it
glennfu Jun 13, 2011
da176e0
removing a ruby 1.9 incompatibility due to CSV::Reader deprecation
joshuaknox Jul 13, 2011
3026fca
new CSV.parse is a subset of former CSV::Reader.parse. doing the dis…
joshuaknox Jul 14, 2011
7cdb21d
adding contacts_data to make debugging the malformed CSV easier
joshuaknox Jul 14, 2011
29fc9b9
Merge remote branch 'glennfu/master'
joshuaknox Jul 14, 2011
edc4d05
real_connect_data to debug hotmail
joshuaknox Jul 14, 2011
cfa898b
disabling connect for debugging
joshuaknox Jul 14, 2011
cbbdd50
more debugging
joshuaknox Jul 14, 2011
a06ec76
whacking a superfluous "end"
joshuaknox Jul 14, 2011
2fab02b
more debugging.
joshuaknox Jul 14, 2011
6839ab0
updating contacts_data
joshuaknox Jul 14, 2011
1869c3a
AOL supports 2 emails for a single contact: display both
joshuaknox Jul 14, 2011
fc9dd82
contacts_data for yahoo
joshuaknox Jul 14, 2011
163ef04
I could've sworn implicit tuple worked. weird.
joshuaknox Jul 14, 2011
49868d6
dropped an "end"
joshuaknox Jul 14, 2011
c7a2546
eliminating dupe returns from Yahoo, making no_connect an optional param
joshuaknox Jul 14, 2011
3990f54
minor bit of cleanup: mostly whacking the superfluous FasterCSV
joshuaknox Jul 14, 2011
703cee8
correcting the conditionals
joshuaknox Jul 16, 2011
c449911
contacts_data for Plaxo
joshuaknox Jul 18, 2011
c39e2f8
grab all emails for a given Plaxo contact
joshuaknox Jul 18, 2011
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion contacts.gemspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Gem::Specification.new do |s|
s.name = "contacts"
s.version = "1.2.4"
s.version = "1.2.6"
s.date = "2010-07-06"
s.summary = "A universal interface to grab contact list information from various providers including Yahoo, AOL, Gmail, Hotmail, and Plaxo."
s.email = "[email protected]"
Expand Down
14 changes: 14 additions & 0 deletions joshuaknox-contacts.gemspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Gem::Specification.new do |s|
s.name = "joshuaknox-contacts"
s.version = "1.2.7"
s.date = "2011-07-14"
s.summary = "A universal interface to grab contact list information from various providers including Yahoo, AOL, Gmail, Hotmail, and Plaxo."
s.email = "[email protected]"
s.homepage = "http://github.com/joshuaknox/contacts"
s.description = "A universal interface to grab contact list information from various providers including Yahoo, AOL, Gmail, Hotmail, and Plaxo."
s.has_rdoc = false
s.authors = ["Joshua Knox", "Glenn Sidney", "Lucas Carlson"]
s.files = ["LICENSE", "Rakefile", "README", "examples/grab_contacts.rb", "lib/contacts.rb", "lib/contacts/base.rb", "lib/contacts/json_picker.rb", "lib/contacts/gmail.rb", "lib/contacts/aol.rb", "lib/contacts/hotmail.rb", "lib/contacts/plaxo.rb", "lib/contacts/yahoo.rb"]
s.add_dependency("json", ">= 1.1.1")
s.add_dependency('gdata', '>= 1.1.1')
end
79 changes: 66 additions & 13 deletions lib/contacts/aol.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ class Aol < Base
LOGIN_URL = "https://my.screenname.aol.com/_cqr/login/login.psp"
LOGIN_REFERER_URL = "http://webmail.aol.com/"
LOGIN_REFERER_PATH = "sitedomain=sns.webmail.aol.com&lang=en&locale=us&authLev=0&uitype=mini&loginId=&redirType=js&xchk=false"
AOL_NUM = "29970-343" # this seems to change each time they change the protocol

CONTACT_LIST_URL = "http://webmail.aol.com/#{AOL_NUM}/aim-2/en-us/Lite/ContactList.aspx?folder=Inbox&showUserFolders=False"
CONTACT_LIST_CSV_URL = "http://webmail.aol.com/#{AOL_NUM}/aim-2/en-us/Lite/ABExport.aspx?command=all"
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"

def real_connect
Expand Down Expand Up @@ -80,7 +76,7 @@ def real_connect
data, resp, cookies, forward, old_url = get(forward, cookies, old_url) + [forward]
end

if data.index("Invalid Username or Password. Please try again.")
if data.index("Incorrect Username or Password.")
raise AuthenticationError, "Username and password do not match"
elsif data.index("Required field must not be blank")
raise AuthenticationError, "Login and password must not be blank"
Expand All @@ -91,7 +87,11 @@ def real_connect
elsif cookies == ""
raise ConnectionError, PROTOCOL_ERROR
end


@aol_num = data.match(/URL=\"http:\/\/mail.aol.com\/(.*)\/aol-6\/en-us\/common\/error.aspx\?/)[1]
@contact_list_url = "http://mail.aol.com/#{@aol_num}/aol-6/en-us/Lite/ContactList.aspx?folder=Inbox&showUserFolders=False"
@contact_list_csv_url = "http://mail.aol.com/#{@aol_num}/aol-6/en-us/Lite/ABExport.aspx?command=all"

@cookies = cookies
end

Expand All @@ -103,7 +103,7 @@ def contacts

return @contacts if @contacts
if connected?
data, resp, cookies, forward, old_url = get(CONTACT_LIST_URL, @cookies, CONTACT_LIST_URL) + [CONTACT_LIST_URL]
data, resp, cookies, forward, old_url = get(@contact_list_url, @cookies, @contact_list_url) + [@contact_list_url]

until forward.nil?
data, resp, cookies, forward, old_url = get(forward, cookies, old_url) + [forward]
Expand All @@ -119,7 +119,7 @@ def contacts
postdata["user"] = input.attributes["value"] if input.attributes["name"] == "user"
end

data, resp, cookies, forward, old_url = get(CONTACT_LIST_CSV_URL, @cookies, CONTACT_LIST_URL) + [CONTACT_LIST_URL]
data, resp, cookies, forward, old_url = get(@contact_list_csv_url, @cookies, @contact_list_url) + [@contact_list_url]

until forward.nil?
data, resp, cookies, forward, old_url = get(forward, cookies, old_url) + [forward]
Expand All @@ -132,14 +132,67 @@ def contacts
parse data
end
end

def contacts_data
postdata = {
"file" => 'contacts',
"fileType" => 'csv'
}

if connected?
data, resp, cookies, forward, old_url = get(@contact_list_url, @cookies, @contact_list_url) + [@contact_list_url]

until forward.nil?
data, resp, cookies, forward, old_url = get(forward, cookies, old_url) + [forward]
end

if resp.code_type != Net::HTTPOK
raise ConnectionError, self.class.const_get(:PROTOCOL_ERROR)
end

# parse data and grab <input name="user" value="8QzMPIAKs2" type="hidden">
doc = Hpricot(data)
(doc/:input).each do |input|
postdata["user"] = input.attributes["value"] if input.attributes["name"] == "user"
end

data, resp, cookies, forward, old_url = get(@contact_list_csv_url, @cookies, @contact_list_url) + [@contact_list_url]

until forward.nil?
data, resp, cookies, forward, old_url = get(forward, cookies, old_url) + [forward]
end

if data.include?("error.gif")
raise AuthenticationError, "Account invalid"
end

data
end
end

private

def parse(data, options={})
data = CSV::Reader.parse(data)
if CSV.const_defined? :Reader
data = CSV::Reader.parse(data)
else
if data.is_a?(String)
data = CSV.parse(data)
else
data = CSV.read(data)
end
end
col_names = data.shift
@contacts = data.map do |person|
["#{person[0]} #{person[1]}", person[4]] if person[4] && !person[4].empty?
end.compact
@contacts = []
data.each do |person|
if person[4] && !person[4].empty?
@contacts << ["#{person[0]} #{person[1]}", person[4]]
end
if person[5] && !person[5].empty?
@contacts << ["#{person[0]} #{person[1]}", person[5]]
end
end
@contacts
end

def h_to_query_string(hash)
Expand All @@ -151,4 +204,4 @@ def h_to_query_string(hash)
end

TYPES[:aol] = Aol
end
end
8 changes: 4 additions & 4 deletions lib/contacts/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def initialize(login, password, options={})
@captcha_token = options[:captcha_token]
@captcha_response = options[:captcha_response]
@connections = {}
connect
connect unless options[:no_connect]
end

def connect
Expand Down Expand Up @@ -166,9 +166,9 @@ def get(url, cookies="", referer="")
data = uncompress(resp, data)
cookies = parse_cookies(resp.response['set-cookie'], cookies)
forward = resp.response['Location']
if (not forward.nil?) && URI.parse(forward).host.nil?
forward = url.scheme.to_s + "://" + url.host.to_s + forward
end
if (not forward.nil?) && URI.parse(forward).host.nil?
forward = url.scheme.to_s + "://" + url.host.to_s + forward
end
return data, resp, cookies, forward
end

Expand Down
62 changes: 58 additions & 4 deletions lib/contacts/hotmail.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,52 @@ class Hotmail < Base
PWDPAD = "IfYouAreReadingThisYouHaveTooMuchFreeTime"
MAX_HTTP_THREADS = 8

def real_connect_debug
data, resp, cookies, forward = get(URL)
old_url = URL
until forward.nil?
data, resp, cookies, forward, old_url = get(forward, cookies, old_url) + [forward]
end

postdata = "PPSX=%s&PwdPad=%s&login=%s&passwd=%s&LoginOptions=2&PPFT=%s" % [
CGI.escape(data.split("><").grep(/PPSX/).first[/=\S+$/][2..-3]),
PWDPAD[0...([email protected])],
CGI.escape(login),
CGI.escape(password),
CGI.escape(data.split("><").grep(/PPFT/).first[/=\S+$/][2..-3])
]

form_url = data.split("><").grep(/form/).first.split[5][8..-2]
data, resp, cookies, forward = post(form_url, postdata, cookies)

old_url = form_url
until cookies =~ /; PPAuth=/ || forward.nil?
data, resp, cookies, forward, old_url = get(forward, cookies, old_url) + [forward]
end
data

if data.index("The e-mail address or password is incorrect")
raise AuthenticationError, "Username and password do not match"
elsif cookies == ""
raise ConnectionError, PROTOCOL_ERROR
end

data, resp, cookies, forward = get("http://mail.live.com/mail", cookies)
until forward.nil?
data, resp, cookies, forward, old_url = get(forward, cookies, old_url) + [forward]
end


@domain = URI.parse(old_url).host
@cookies = cookies
rescue AuthenticationError => m
if @attempt == 1
retry
else
raise m
end
end

def real_connect
data, resp, cookies, forward = get(URL)
old_url = URL
Expand Down Expand Up @@ -45,6 +91,7 @@ def real_connect
data, resp, cookies, forward, old_url = get(forward, cookies, old_url) + [forward]
end


@domain = URI.parse(old_url).host
@cookies = cookies
rescue AuthenticationError => m
Expand All @@ -55,6 +102,13 @@ def real_connect
end
end

def debug_contacts
if connected?
url = URI.parse(contact_list_url)
data, resp, cookies, forward = get( contact_list_url, @cookies )
end
end

def contacts(options = {})
if connected?
url = URI.parse(contact_list_url)
Expand All @@ -76,7 +130,7 @@ def contacts(options = {})
resp, data = http.get(get_contact_list_url(index), "Cookie" => @cookies)

email_match_text_beginning = Regexp.escape("http://m.mail.live.com/?rru=compose&amp;to=")
email_match_text_end = Regexp.escape("&amp;")
email_match_text_end = Regexp.escape("&amp;ru=")

raw_html = resp.body.split("
").grep(/(?:e|dn)lk[0-9]+/)
Expand All @@ -89,7 +143,7 @@ def contacts(options = {})
# Grab info
case c_info[1]
when "e" # Email
build_contacts.last[1] = row.match(/#{email_match_text_beginning}(.*)#{email_match_text_end}/)[1]
build_contacts.last[1] = row.match(/#{email_match_text_beginning}(.*?)#{email_match_text_end}/)[1]
when "dn" # Name
build_contacts.last[0] = row.match(/<a[^>]*>(.+)<\/a>/)[1]
end
Expand All @@ -105,7 +159,7 @@ def contacts(options = {})
build_contacts.each do |contact|
unless contact[1].nil?
# Only return contacts with email addresses
contact[1] = CGI::unescape(contact[1])
contact[1] = CGI::unescape CGI::unescape(contact[1])
@contacts << contact
end
end
Expand All @@ -122,4 +176,4 @@ def get_contact_list_url(index)

TYPES[:hotmail] = Hotmail
end
end
end
5 changes: 2 additions & 3 deletions lib/contacts/json_picker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@

class Contacts
def self.parse_json( string )
if Object.const_defined?('ActiveSupport') and
ActiveSupport.const_defined?('JSON')
if Object.const_defined?('ActiveSupport') and ActiveSupport.const_defined?('JSON')
ActiveSupport::JSON.decode( string )
elsif Object.const_defined?('JSON')
JSON.parse( string )
else
raise 'Contacts requires JSON or Rails (with ActiveSupport::JSON)'
end
end
end
end
28 changes: 21 additions & 7 deletions lib/contacts/plaxo.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@ def contacts
parse data
end # contacts

def contacts_data
getdata = "&authInfo.authByEmail.email=%s" % CGI.escape(login)
getdata += "&authInfo.authByEmail.password=%s" % CGI.escape(password)
data, resp, cookies, forward = get(CONTACT_LIST_URL + getdata)

if resp.code_type != Net::HTTPOK
raise ConnectionError, PROTOCOL_ERROR
end

data
end

private
def parse(data, options={})
doc = REXML::Document.new(data)
Expand All @@ -39,14 +51,16 @@ def parse(data, options={})
elsif cont.elements['displayName']
cont.elements['displayName'].text
end
email = if cont.elements['email1']
cont.elements['email1'].text
end
if name || email
@contacts << [name, email]
i = 1
emails = []
while (cont.elements["email#{i}"] || cont.elements["workEmail#{i}"])
emails << cont.elements["email#{i}"].text if cont.elements["email#{i}"]
emails << cont.elements["workEmail#{i}"].text if cont.elements["workEmail#{i}"]
i += 1
end
emails.each {|email| @contacts << [name, email] }
end
@contacts
@contacts.uniq {|a,b| b}
else
raise ConnectionError, PROTOCOL_ERROR
end
Expand Down Expand Up @@ -127,4 +141,4 @@ def parse(data, options={})
<editCounter>3</editCounter>

</ns1:GetContactsResponse>
=end
=end
Loading