muck-contacts 2.6.1

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.
@@ -0,0 +1,68 @@
1
+ require 'csv'
2
+
3
+ class Contacts
4
+ class Mailru < Base
5
+ LOGIN_URL = "https://auth.mail.ru/cgi-bin/auth"
6
+ ADDRESS_BOOK_URL = "http://win.mail.ru/cgi-bin/abexport/addressbook.csv"
7
+
8
+ attr_accessor :cookies
9
+
10
+ def real_connect
11
+ username = login
12
+
13
+ postdata = "Login=%s&Domain=%s&Password=%s" % [
14
+ CGI.escape(username),
15
+ CGI.escape(domain_param(username)),
16
+ CGI.escape(password)
17
+ ]
18
+
19
+ data, resp, self.cookies, forward = post(LOGIN_URL, postdata, "")
20
+
21
+ if data.index("fail=1")
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, cookies, forward = get(login_token_link(data), login_cookies.join(';'))
28
+ end
29
+
30
+ def contacts
31
+ postdata = "confirm=1&abtype=6"
32
+ data, resp, cookies, forward = post(ADDRESS_BOOK_URL, postdata, login_cookies.join(';'))
33
+
34
+ @contacts = []
35
+ CSV.parse(data) do |row|
36
+ @contacts << [row[0], row[4]] unless header_row?(row)
37
+ end
38
+
39
+ @contacts
40
+ end
41
+
42
+ def skip_gzip?
43
+ true
44
+ end
45
+
46
+ private
47
+ def login_token_link(data)
48
+ data.match(/url=(.+)\">/)[1]
49
+ end
50
+
51
+ def login_cookies
52
+ self.cookies.split(';').collect{|c| c if (c.include?('t=') or c.include?('Mpop='))}.compact.collect{|c| c.strip}
53
+ end
54
+
55
+ def header_row?(row)
56
+ row[0] == 'AB-Name'
57
+ end
58
+
59
+ def domain_param(login)
60
+ login.include?('@') ?
61
+ login.match(/.+@(.+)/)[1] :
62
+ 'mail.ru'
63
+ end
64
+
65
+ end
66
+
67
+ TYPES[:mailru] = Mailru
68
+ end
@@ -0,0 +1,59 @@
1
+ require 'csv'
2
+
3
+ class Contacts
4
+ class Outlook < Base
5
+
6
+ def initialize(file)
7
+ @contact_file = Array.new
8
+ file = file.respond_to?(:read) ? file.read : file
9
+ file.each_line do |line|
10
+ @contact_file << CSV.parse(line)[0]
11
+ end
12
+ @full_name = false
13
+ @header_indexes = Hash.new
14
+ @header_indexes[:email_address] = Array.new
15
+
16
+ headers = @contact_file[0]
17
+
18
+ @contact_file = @contact_file[1, @contact_file.length]
19
+
20
+ headers.each_with_index do |header, i|
21
+ if header.match(/^Name$/)
22
+ @full_name = true
23
+ @header_indexes[:full_name] = i
24
+ elsif header.match(/^First Name/)
25
+ @header_indexes[:first_name] = i
26
+ elsif header.match(/^Last Name/)
27
+ @header_indexes[:last_name] = i
28
+ elsif header.match(/E-mail/)
29
+ @header_indexes[:email_address] << i
30
+ end
31
+ end
32
+ end
33
+
34
+ def contacts
35
+
36
+ contacts = Array.new
37
+
38
+ @contact_file.each_with_index do |line, i|
39
+ contacts[i] = Array.new unless contacts[i]
40
+ if(@full_name)
41
+ contacts[i][0] = line[@header_indexes[:full_name]]
42
+ else
43
+ contacts[i][0] = "#{line[@header_indexes[:first_name]]} #{line[@header_indexes[:last_name]]}"
44
+ end
45
+ @header_indexes[:email_address].each do |index|
46
+ if line[index] && !contacts[i][1]
47
+ contacts[i][1] = line[index]
48
+ end
49
+ end
50
+ end
51
+
52
+ contacts
53
+ end
54
+
55
+ end
56
+
57
+ private
58
+ FILETYPES[:outlook] = Outlook
59
+ 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,25 @@
1
+ class Contacts
2
+ class Vcf < Base
3
+
4
+ def initialize(file)
5
+ @contact_file = file
6
+ end
7
+
8
+ def contacts
9
+ contacts = Array.new
10
+ i = 0
11
+ @contact_file.each do |line|
12
+ contacts[i] = Array.new unless contacts[i]
13
+ if line.match(/FN:/)
14
+ contacts[i] << line.gsub(/FN:/, '').strip
15
+ elsif line.match(/EMAIL;/)
16
+ contacts[i] << line.gsub(/^.*:/, '').strip
17
+ elsif line.match(/END:VCARD/)
18
+ i += 1
19
+ end
20
+ end
21
+ contacts
22
+ end
23
+ end
24
+ FILETYPES[:vcf] = Vcf
25
+ end
@@ -0,0 +1,104 @@
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
+ @contacts = []
39
+
40
+ if connected?
41
+ # first, get the addressbook site with the new crumb parameter
42
+ url = URI.parse(address_book_url)
43
+ http = open_http(url)
44
+ resp, data = http.get("#{url.path}?#{url.query}",
45
+ "Cookie" => @cookies
46
+ )
47
+
48
+ if resp.code_type != Net::HTTPOK
49
+ raise ConnectionError, self.class.const_get(:PROTOCOL_ERROR)
50
+ end
51
+
52
+ crumb = data.to_s[/dotCrumb: '(.*?)'/][13...-1]
53
+
54
+ # now proceed with the new ".crumb" parameter to get the csv data
55
+ url = URI.parse(contact_list_url.sub("_crumb=crumb","_crumb=#{crumb}").sub("time", Time.now.to_f.to_s.sub(".","")[0...-2]))
56
+ http = open_http(url)
57
+ resp, more_data = http.get("#{url.path}?#{url.query}",
58
+ "Cookie" => @cookies,
59
+ "X-Requested-With" => "XMLHttpRequest",
60
+ "Referer" => address_book_url
61
+ )
62
+
63
+ if resp.code_type != Net::HTTPOK
64
+ raise ConnectionError, self.class.const_get(:PROTOCOL_ERROR)
65
+ end
66
+
67
+ if more_data =~ /"TotalABContacts":(\d+)/
68
+ total = $1.to_i
69
+ ((total / 50.0).ceil).times do |i|
70
+ # now proceed with the new ".crumb" parameter to get the csv data
71
+ 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]))
72
+ http = open_http(url)
73
+ resp, more_data = http.get("#{url.path}?#{url.query}",
74
+ "Cookie" => @cookies,
75
+ "X-Requested-With" => "XMLHttpRequest",
76
+ "Referer" => address_book_url
77
+ )
78
+
79
+ if resp.code_type != Net::HTTPOK
80
+ raise ConnectionError, self.class.const_get(:PROTOCOL_ERROR)
81
+ end
82
+
83
+ parse more_data
84
+ end
85
+ end
86
+
87
+ @contacts
88
+ end
89
+ end
90
+
91
+ private
92
+
93
+ def parse(data, options={})
94
+ @contacts ||= []
95
+ @contacts += Contacts.parse_json(data)["response"]["ResultSet"]["Contacts"].to_a.select{|contact|!contact["email"].to_s.empty?}.map do |contact|
96
+ name = contact["contactName"].split(",")
97
+ [[name.pop, name.join(",")].join(" ").strip, contact["email"]]
98
+ end if data =~ /^\{"response":/
99
+ @contacts
100
+ end
101
+ end
102
+
103
+ TYPES[:yahoo] = Yahoo
104
+ end
@@ -0,0 +1,73 @@
1
+ gmail:
2
+ username: <changeme>
3
+ password: <changeme>
4
+ contacts:
5
+ -
6
+ name: "FirstName1 LastName1"
7
+ email_address: "firstname1@example.com"
8
+ -
9
+ name: "FirstName2 LastName2"
10
+ email_address: "firstname2@example.com"
11
+ yahoo:
12
+ username: <changeme>
13
+ password: <changeme>
14
+ contacts:
15
+ -
16
+ name: "FirstName1 LastName1"
17
+ email_address: "firstname1@example.com"
18
+ -
19
+ name: "FirstName2 LastName2"
20
+ email_address: "firstname2@example.com"
21
+ hotmail:
22
+ username: <changeme>
23
+ password: <changeme>
24
+ contacts:
25
+ -
26
+ name: "FirstName1 LastName1"
27
+ email_address: "firstname1@example.com"
28
+ -
29
+ name: "FirstName2 LastName2"
30
+ email_address: "firstname2@example.com"
31
+ aol:
32
+ username: <changeme>
33
+ password: <changeme>
34
+ contacts:
35
+ -
36
+ name: "FirstName1 LastName1"
37
+ email_address: "firstname1@example.com"
38
+ -
39
+ name: "FirstName2 LastName2"
40
+ email_address: "firstname2@example.com"
41
+ mailru:
42
+ username: <changeme>
43
+ password: <changeme>
44
+ contacts:
45
+ -
46
+ name: "FirstName1 LastName1"
47
+ email_address: "firstname1@example.com"
48
+ -
49
+ name: "FirstName2 LastName2"
50
+ email_address: "firstname2@example.com"
51
+ facebook:
52
+ username: <facebook uid>
53
+ password: <access token from facebook app oauth2>
54
+ contacts:
55
+ -
56
+ name: "FirstName1 LastName1"
57
+ account_id: "facebook_uid"
58
+ -
59
+ name: "FirstName2 LastName2"
60
+ account_id: "facebook_uid"
61
+ linked_in:
62
+ username: <oauth token for user>
63
+ password: <oauth secret for user>
64
+ app_id: <consumer token from linked_in app>
65
+ app_secret: <consumer secret from linked_in app>
66
+ contacts:
67
+ -
68
+ name: "FirstName1 LastName1"
69
+ account_id: "linked_in_uid"
70
+ -
71
+ name: "FirstName2 LastName2"
72
+ account_id: "linked_in_uid"
73
+