foco-contacts 1.2.18
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +10 -0
- data/README.rdoc +68 -0
- data/Rakefile +41 -0
- data/examples/grab_contacts.rb +12 -0
- data/lib/contacts.rb +18 -0
- data/lib/contacts/aol.rb +157 -0
- data/lib/contacts/base.rb +249 -0
- data/lib/contacts/gmail.rb +45 -0
- data/lib/contacts/gmx.rb +63 -0
- data/lib/contacts/hotmail.rb +92 -0
- data/lib/contacts/inbox_lt.rb +90 -0
- data/lib/contacts/json_picker.rb +16 -0
- data/lib/contacts/mailru.rb +69 -0
- data/lib/contacts/onelt.rb +78 -0
- data/lib/contacts/plaxo.rb +130 -0
- data/lib/contacts/seznam.rb +78 -0
- data/lib/contacts/tonline_de.rb +79 -0
- data/lib/contacts/web_de.rb +79 -0
- data/lib/contacts/yahoo.rb +106 -0
- metadata +118 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'gdata'
|
2
|
+
|
3
|
+
class Contacts
|
4
|
+
class Gmail < Base
|
5
|
+
|
6
|
+
DETECTED_DOMAINS = [ /gmail.com/i, /googlemail.com/i ]
|
7
|
+
CONTACTS_SCOPE = 'http://www.google.com/m8/feeds/'
|
8
|
+
CONTACTS_FEED = CONTACTS_SCOPE + 'contacts/default/full/?max-results=1000'
|
9
|
+
|
10
|
+
def contacts
|
11
|
+
return @contacts if @contacts
|
12
|
+
end
|
13
|
+
|
14
|
+
def real_connect
|
15
|
+
@client = GData::Client::Contacts.new
|
16
|
+
@client.clientlogin(@login, @password, @captcha_token, @captcha_response)
|
17
|
+
|
18
|
+
feed = @client.get(CONTACTS_FEED).to_xml
|
19
|
+
|
20
|
+
@contacts = feed.elements.to_a('entry').collect do |entry|
|
21
|
+
title, email = entry.elements['title'].text, nil
|
22
|
+
primary_email = nil
|
23
|
+
|
24
|
+
entry.elements.each('gd:email') do |e|
|
25
|
+
if e.attribute('primary')
|
26
|
+
primary_email = e.attribute('address').value
|
27
|
+
else
|
28
|
+
email = e.attribute('address').value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
email = primary_email unless primary_email.nil?
|
33
|
+
|
34
|
+
[title, email] unless email.nil?
|
35
|
+
end
|
36
|
+
@contacts.compact!
|
37
|
+
rescue GData::Client::AuthorizationError => e
|
38
|
+
# raise AuthenticationError, "Username or password are incorrect"
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
TYPES[:gmail] = Gmail
|
44
|
+
end
|
45
|
+
end
|
data/lib/contacts/gmx.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
class Contacts
|
2
|
+
class Gmx < Base
|
3
|
+
DETECTED_DOMAINS = [ /gmx.de/i, /gmx.at/i, /gmx.ch/i, /gmx.net/i ]
|
4
|
+
LOGIN_URL = "https://service.gmx.net/de/cgi/login"
|
5
|
+
ADDRESS_BOOK_URL = "https://service.gmx.net/de/cgi/g.fcgi/addressbook/cab?cc=subnavi_adressbuch&sid="
|
6
|
+
EXPORT_URL = "https://adressbuch.gmx.net/exportcontacts"
|
7
|
+
|
8
|
+
attr_accessor :cookies, :sid
|
9
|
+
|
10
|
+
def real_connect
|
11
|
+
|
12
|
+
postdata = "AREA=1&EXT=redirect&EXT2=&dlevel=c&id=%s&p=%s&uinguserid=__uuid__" % [
|
13
|
+
CGI.escape(login),
|
14
|
+
CGI.escape(password)
|
15
|
+
]
|
16
|
+
|
17
|
+
data, resp, self.cookies, forward = post(LOGIN_URL, postdata, "")
|
18
|
+
|
19
|
+
if data.index("lose/password")
|
20
|
+
raise AuthenticationError, "Username and password do not match"
|
21
|
+
elsif !forward.nil? && forward.index("login-failed")
|
22
|
+
raise AuthenticationError, "Username and password do not match"
|
23
|
+
elsif cookies == "" or data == ""
|
24
|
+
raise ConnectionError, PROTOCOL_ERROR
|
25
|
+
end
|
26
|
+
|
27
|
+
data, resp, self.cookies, forward = get(forward, self.cookies)
|
28
|
+
|
29
|
+
self.sid = data.match(/sid=([a-z0-9\.]+)/)[1]
|
30
|
+
end
|
31
|
+
|
32
|
+
def contacts
|
33
|
+
data, resp, cookies, forward = get(ADDRESS_BOOK_URL + self.sid, self.cookies)
|
34
|
+
data, resp, cookies, forward = get(forward, self.cookies)
|
35
|
+
|
36
|
+
session = forward.match(/session=([^&]+)/)[1]
|
37
|
+
|
38
|
+
postdata = "language=eng&raw_format=csv_Outlook2003&what=PERSON&session=" + session
|
39
|
+
|
40
|
+
data, resp, cookies, forward = post(EXPORT_URL, postdata, self.cookies)
|
41
|
+
|
42
|
+
@contacts = []
|
43
|
+
|
44
|
+
CSV.parse(data) do |row|
|
45
|
+
@contacts << ["#{row[2]} #{row[0]}", row[9]] unless header_row?(row)
|
46
|
+
end
|
47
|
+
|
48
|
+
@contacts
|
49
|
+
end
|
50
|
+
|
51
|
+
def skip_gzip?
|
52
|
+
false
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def header_row?(row)
|
58
|
+
row[0] == 'Last Name'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
TYPES[:gmx] = Gmx
|
63
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'csv'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'nokogiri'
|
4
|
+
|
5
|
+
class Contacts
|
6
|
+
class Hotmail < Base
|
7
|
+
DETECTED_DOMAINS = [ /hotmail/i, /live/i, /msn/i, /chaishop/i ]
|
8
|
+
URL = "https://login.live.com/login.srf?id=2"
|
9
|
+
CONTACT_LIST_URL = "https://mail.live.com/mail/GetContacts.aspx"
|
10
|
+
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"
|
11
|
+
PWDPAD = "IfYouAreReadingThisYouHaveTooMuchFreeTime"
|
12
|
+
|
13
|
+
def real_connect
|
14
|
+
|
15
|
+
data, resp, cookies, forward = get(URL)
|
16
|
+
old_url = URL
|
17
|
+
until forward.nil?
|
18
|
+
data, resp, cookies, forward, old_url = get(forward, cookies, old_url) + [forward]
|
19
|
+
end
|
20
|
+
|
21
|
+
postdata = "PPSX=%s&PwdPad=%s&login=%s&passwd=%s&LoginOptions=2&PPFT=%s" % [
|
22
|
+
CGI.escape(data.split("><").grep(/PPSX/).first[/=\S+$/][2..-3]),
|
23
|
+
PWDPAD[0...(PWDPAD.length-@password.length)],
|
24
|
+
CGI.escape(login),
|
25
|
+
CGI.escape(password),
|
26
|
+
CGI.escape(data.split("><").grep(/PPFT/).first[/=\S+$/][2..-3])
|
27
|
+
]
|
28
|
+
|
29
|
+
form_url = data.split("><").grep(/form/).first.split[5][8..-2]
|
30
|
+
data, resp, cookies, forward = post(form_url, postdata, cookies)
|
31
|
+
|
32
|
+
old_url = form_url
|
33
|
+
until cookies =~ /; PPAuth=/ || forward.nil?
|
34
|
+
data, resp, cookies, forward, old_url = get(forward, cookies, old_url) + [forward]
|
35
|
+
end
|
36
|
+
|
37
|
+
if data.index("The e-mail address or password is incorrect")
|
38
|
+
# raise AuthenticationError, "Username and password do not match"
|
39
|
+
elsif data != ""
|
40
|
+
# raise AuthenticationError, "Required field must not be blank"
|
41
|
+
elsif cookies == ""
|
42
|
+
# raise ConnectionError, PROTOCOL_ERROR
|
43
|
+
end
|
44
|
+
|
45
|
+
data, resp, cookies, forward = get("http://mail.live.com/mail", cookies)
|
46
|
+
until forward.nil?
|
47
|
+
data, resp, cookies, forward, old_url = get(forward, cookies, old_url) + [forward]
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
@domain = URI.parse(old_url).host
|
52
|
+
@cookies = cookies
|
53
|
+
rescue AuthenticationError => m
|
54
|
+
if @attempt == 1
|
55
|
+
retry
|
56
|
+
else
|
57
|
+
# raise m
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def contacts(options = {})
|
62
|
+
if @contacts.nil? && connected?
|
63
|
+
url = URI.parse(contact_list_url)
|
64
|
+
data, resp, cookies, forward = get(get_contact_list_url, @cookies )
|
65
|
+
|
66
|
+
|
67
|
+
data.force_encoding('ISO-8859-1')
|
68
|
+
|
69
|
+
@contacts = CSV.parse(data, {:headers => true, :col_sep => data[7]}).map do |row|
|
70
|
+
name = ""
|
71
|
+
name = row["First Name"] if !row["First Name"].nil?
|
72
|
+
name << " #{row["Last Name"]}" if !row["Last Name"].nil?
|
73
|
+
[name, row["E-mail Address"] || ""]
|
74
|
+
end
|
75
|
+
else
|
76
|
+
@contacts || []
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
TYPES[:hotmail] = Hotmail
|
83
|
+
|
84
|
+
# the contacts url is dynamic
|
85
|
+
# luckily it tells us where to find it
|
86
|
+
def get_contact_list_url
|
87
|
+
data = get(CONTACT_LIST_URL, @cookies)[0]
|
88
|
+
html_doc = Nokogiri::HTML(data)
|
89
|
+
html_doc.xpath("//a")[0]["href"]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
class Contacts
|
2
|
+
class InboxLt < Base
|
3
|
+
DETECTED_DOMAINS = [ /inbox\.lt/i ]
|
4
|
+
URL = "http://www.inbox.lt/?language=lt"
|
5
|
+
LOGIN_URL = "https://login.inbox.lt/redirect.php"
|
6
|
+
ADDRESS_BOOK_URL = "http://mail.inbox.lt/horde/turba/?char=&sort=name&page=%d"
|
7
|
+
PROTOCOL_ERROR = "inbox.lt has changed its protocols"
|
8
|
+
|
9
|
+
attr_accessor :cookies
|
10
|
+
|
11
|
+
def real_connect
|
12
|
+
data, resp, self.cookies, forward = get(URL, "")
|
13
|
+
|
14
|
+
doc = Nokogiri(data)
|
15
|
+
|
16
|
+
salt_el = doc.at('input[name=salt]')
|
17
|
+
|
18
|
+
if salt_el.nil?
|
19
|
+
raise ConnectionError, PROTOCOL_ERROR
|
20
|
+
end
|
21
|
+
|
22
|
+
salt = salt_el['value']
|
23
|
+
|
24
|
+
postdata = "language=lt&passhash=&salt=%s&redirect_url=%s&redirect_vars=imapuser,usessl&imapuser=%s&pass=%s&usessl=1" % [
|
25
|
+
CGI.escape(salt),
|
26
|
+
CGI.escape('http://www.inbox.lt/index.php?actionID=imp_login'),
|
27
|
+
CGI.escape(username),
|
28
|
+
CGI.escape(password)
|
29
|
+
]
|
30
|
+
|
31
|
+
data, resp, self.cookies, forward = post(LOGIN_URL, postdata, "")
|
32
|
+
|
33
|
+
if forward.nil? || !forward.match("logged_in=1")
|
34
|
+
raise AuthenticationError, "Username and password do not match"
|
35
|
+
end
|
36
|
+
|
37
|
+
until forward.nil?
|
38
|
+
data, resp, self.cookies, forward = get(forward, self.cookies)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def contacts
|
43
|
+
@contacts = []
|
44
|
+
page = 0
|
45
|
+
|
46
|
+
until page.nil?
|
47
|
+
url = ADDRESS_BOOK_URL % page
|
48
|
+
|
49
|
+
data, resp, self.cookies, forward = get(url, self.cookies)
|
50
|
+
|
51
|
+
doc = Nokogiri(data)
|
52
|
+
|
53
|
+
(doc/"form#contactsForm table table[1] tr").each do |tr|
|
54
|
+
name_td = tr.at('td[2]')
|
55
|
+
email_td = tr.at('td[3]')
|
56
|
+
|
57
|
+
next if name_td.nil? || email_td.nil?
|
58
|
+
|
59
|
+
name = name_td.text.strip
|
60
|
+
email = email_td.text.strip
|
61
|
+
|
62
|
+
next unless email.match('@')
|
63
|
+
|
64
|
+
@contacts << [ name, email ]
|
65
|
+
end
|
66
|
+
|
67
|
+
page+= 1
|
68
|
+
page = nil unless data.match("&page=#{page}")
|
69
|
+
end
|
70
|
+
|
71
|
+
@contacts
|
72
|
+
end
|
73
|
+
|
74
|
+
def skip_gzip?
|
75
|
+
false
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def username
|
81
|
+
@login.split('@').first
|
82
|
+
end
|
83
|
+
|
84
|
+
def domain
|
85
|
+
@login.split('@').last
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
TYPES[:inbox_lt] = InboxLt
|
90
|
+
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,69 @@
|
|
1
|
+
require 'csv'
|
2
|
+
|
3
|
+
class Contacts
|
4
|
+
class Mailru < Base
|
5
|
+
DETECTED_DOMAINS = [ /list\.ru/i, /inbox\.ru/i, /bk\.ru/i, /mail\.ru/i ]
|
6
|
+
LOGIN_URL = "https://auth.mail.ru/cgi-bin/auth"
|
7
|
+
ADDRESS_BOOK_URL = "http://e.mail.ru/cgi-bin/abexport/addressbook.csv"
|
8
|
+
|
9
|
+
attr_accessor :cookies
|
10
|
+
|
11
|
+
def real_connect
|
12
|
+
username = login
|
13
|
+
|
14
|
+
postdata = "Login=%s&Domain=%s&Password=%s" % [
|
15
|
+
CGI.escape(username),
|
16
|
+
CGI.escape(domain_param(username)),
|
17
|
+
CGI.escape(password)
|
18
|
+
]
|
19
|
+
|
20
|
+
data, resp, self.cookies, forward = post(LOGIN_URL, postdata, "")
|
21
|
+
|
22
|
+
if data.index("fail=1")
|
23
|
+
raise AuthenticationError, "Username and password do not match"
|
24
|
+
elsif cookies == "" or data == ""
|
25
|
+
raise ConnectionError, PROTOCOL_ERROR
|
26
|
+
end
|
27
|
+
|
28
|
+
data, resp, cookies, forward = get(login_token_link(data), login_cookies.join(';'))
|
29
|
+
end
|
30
|
+
|
31
|
+
def contacts
|
32
|
+
postdata = "confirm=1&abtype=6"
|
33
|
+
data, resp, cookies, forward = post(ADDRESS_BOOK_URL, postdata, login_cookies.join(';'))
|
34
|
+
|
35
|
+
@contacts = []
|
36
|
+
CSV.parse(data) do |row|
|
37
|
+
@contacts << [row[0], row[4]] unless header_row?(row)
|
38
|
+
end
|
39
|
+
|
40
|
+
@contacts
|
41
|
+
end
|
42
|
+
|
43
|
+
def skip_gzip?
|
44
|
+
true
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
def login_token_link(data)
|
49
|
+
data.match(/url=(.+)\">/)[1]
|
50
|
+
end
|
51
|
+
|
52
|
+
def login_cookies
|
53
|
+
self.cookies.split(';').collect{|c| c if (c.include?('t=') or c.include?('Mpop='))}.compact.collect{|c| c.strip}
|
54
|
+
end
|
55
|
+
|
56
|
+
def header_row?(row)
|
57
|
+
row[0] == 'AB-Name'
|
58
|
+
end
|
59
|
+
|
60
|
+
def domain_param(login)
|
61
|
+
login.include?('@') ?
|
62
|
+
login.match(/.+@(.+)/)[1] :
|
63
|
+
'mail.ru'
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
TYPES[:mailru] = Mailru
|
69
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
class Contacts
|
2
|
+
class Onelt < Base
|
3
|
+
DETECTED_DOMAINS = [ /one\.lt/i ]
|
4
|
+
LOGIN_URL = "http://w33.one.lt/logonSubsite.do?subsite=pastas"
|
5
|
+
EMAIL_URL = "http://email.one.lt/"
|
6
|
+
ADDRESS_BOOK_URL = "http://w33.one.lt/resinRedir.do?pane=email"
|
7
|
+
PROTOCOL_ERROR = "One.lt 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"
|
8
|
+
|
9
|
+
attr_accessor :cookies
|
10
|
+
|
11
|
+
def real_connect
|
12
|
+
data, resp, self.cookies, forward = get(LOGIN_URL)
|
13
|
+
|
14
|
+
postdata = "username=%s&password=%s&subsite=pastas" % [
|
15
|
+
CGI.escape(username),
|
16
|
+
CGI.escape(password)
|
17
|
+
]
|
18
|
+
|
19
|
+
data, resp, self.cookies, forward, old_url = post(LOGIN_URL, postdata, self.cookies)
|
20
|
+
|
21
|
+
if data.index("f-login")
|
22
|
+
raise AuthenticationError, "Username and password do not match"
|
23
|
+
elsif !forward.nil? && forward.match('brutecheck')
|
24
|
+
raise AuthenticationError, "Got captcha"
|
25
|
+
elsif forward.nil? || !forward.match('tkn')
|
26
|
+
raise ConnectionError, PROTOCOL_ERROR
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def contacts
|
31
|
+
data, resp, self.cookies, forward = get(ADDRESS_BOOK_URL, self.cookies)
|
32
|
+
|
33
|
+
doc = Nokogiri(data)
|
34
|
+
int = nil
|
35
|
+
|
36
|
+
(doc/"input[name=int]").each do |input|
|
37
|
+
int = input['value']
|
38
|
+
end
|
39
|
+
|
40
|
+
postdata = "action=LoginUser&pane=contacts&int=#{int}"
|
41
|
+
data, resp, self.cookies, forward = post(EMAIL_URL, postdata, self.cookies)
|
42
|
+
|
43
|
+
contacts = []
|
44
|
+
page = 1
|
45
|
+
|
46
|
+
until page.nil?
|
47
|
+
url = "http://email.one.lt/index.html?pane=contacts&page-number=%d" % page
|
48
|
+
|
49
|
+
data, resp, self.cookies, forward = get(url, self.cookies)
|
50
|
+
|
51
|
+
doc = Nokogiri(data)
|
52
|
+
|
53
|
+
(doc/'form[name=contacts_items]//table[2]//tr[class=whiteBg]').each do |tr|
|
54
|
+
name = tr.at('td[2]').text.strip
|
55
|
+
email = tr.at('td[4]').text.strip
|
56
|
+
|
57
|
+
next if email.empty?
|
58
|
+
|
59
|
+
contacts << [ name, email ]
|
60
|
+
end
|
61
|
+
|
62
|
+
page+= 1
|
63
|
+
page = nil unless data.match("&page-number=#{page}")
|
64
|
+
end
|
65
|
+
|
66
|
+
contacts
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def username
|
72
|
+
login.split('@').first
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
TYPES[:onelt] = Onelt
|
78
|
+
end
|