contacts_cn_19 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,154 @@
1
+ class Contacts
2
+ require 'hpricot'
3
+ require 'csv'
4
+ class Aol < Base
5
+ URL = "http://www.aol.com/"
6
+ LOGIN_URL = "https://my.screenname.aol.com/_cqr/login/login.psp"
7
+ LOGIN_REFERER_URL = "http://webmail.aol.com/"
8
+ LOGIN_REFERER_PATH = "sitedomain=sns.webmail.aol.com&lang=en&locale=us&authLev=0&uitype=mini&loginId=&redirType=js&xchk=false"
9
+ AOL_NUM = "29970-343" # this seems to change each time they change the protocol
10
+
11
+ CONTACT_LIST_URL = "http://webmail.aol.com/#{AOL_NUM}/aim-2/en-us/Lite/ContactList.aspx?folder=Inbox&showUserFolders=False"
12
+ CONTACT_LIST_CSV_URL = "http://webmail.aol.com/#{AOL_NUM}/aim-2/en-us/Lite/ABExport.aspx?command=all"
13
+ 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"
14
+
15
+ def real_connect
16
+ if login.strip =~ /^(.+)@aol\.com$/ # strip off the @aol.com for AOL logins
17
+ login = $1
18
+ end
19
+
20
+ postdata = {
21
+ "loginId" => login,
22
+ "password" => password,
23
+ "rememberMe" => "on",
24
+ "_sns_fg_color_" => "",
25
+ "_sns_err_color_" => "",
26
+ "_sns_link_color_" => "",
27
+ "_sns_width_" => "",
28
+ "_sns_height_" => "",
29
+ "offerId" => "mail-second-en-us",
30
+ "_sns_bg_color_" => "",
31
+ "sitedomain" => "sns.webmail.aol.com",
32
+ "regPromoCode" => "",
33
+ "mcState" => "initialized",
34
+ "uitype" => "std",
35
+ "siteId" => "",
36
+ "lang" => "en",
37
+ "locale" => "us",
38
+ "authLev" => "0",
39
+ "siteState" => "",
40
+ "isSiteStateEncoded" => "false",
41
+ "use_aam" => "0",
42
+ "seamless" => "novl",
43
+ "aolsubmit" => CGI.escape("Sign In"),
44
+ "idType" => "SN",
45
+ "usrd" => "",
46
+ "doSSL" => "",
47
+ "redirType" => "",
48
+ "xchk" => "false"
49
+ }
50
+
51
+ # Get this cookie and stick it in the form to confirm to Aol that your cookies work
52
+ data, resp, cookies, forward = get(URL)
53
+ postdata["stips"] = cookie_hash_from_string(cookies)["stips"]
54
+ postdata["tst"] = cookie_hash_from_string(cookies)["tst"]
55
+
56
+ data, resp, cookies, forward, old_url = get(LOGIN_REFERER_URL, cookies) + [URL]
57
+ until forward.nil?
58
+ data, resp, cookies, forward, old_url = get(forward, cookies, old_url) + [forward]
59
+ end
60
+
61
+ data, resp, cookies, forward, old_url = get("#{LOGIN_URL}?#{LOGIN_REFERER_PATH}", cookies) + [LOGIN_REFERER_URL]
62
+ until forward.nil?
63
+ data, resp, cookies, forward, old_url = get(forward, cookies, old_url) + [forward]
64
+ end
65
+
66
+ doc = Hpricot(data)
67
+ (doc/:input).each do |input|
68
+ postdata["usrd"] = input.attributes["value"] if input.attributes["name"] == "usrd"
69
+ end
70
+ # parse data for <input name="usrd" value="2726212" type="hidden"> and add it to the postdata
71
+
72
+ postdata["SNS_SC"] = cookie_hash_from_string(cookies)["SNS_SC"]
73
+ postdata["SNS_LDC"] = cookie_hash_from_string(cookies)["SNS_LDC"]
74
+ postdata["LTState"] = cookie_hash_from_string(cookies)["LTState"]
75
+ # raise data.inspect
76
+
77
+ data, resp, cookies, forward, old_url = post(LOGIN_URL, h_to_query_string(postdata), cookies, LOGIN_REFERER_URL) + [LOGIN_REFERER_URL]
78
+
79
+ until forward.nil?
80
+ data, resp, cookies, forward, old_url = get(forward, cookies, old_url) + [forward]
81
+ end
82
+
83
+ if data.index("Invalid Username or Password. Please try again.")
84
+ raise AuthenticationError, "Username and password do not match"
85
+ elsif data.index("Required field must not be blank")
86
+ raise AuthenticationError, "Login and password must not be blank"
87
+ elsif data.index("errormsg_0_logincaptcha")
88
+ raise AuthenticationError, "Captcha error"
89
+ elsif data.index("Invalid request")
90
+ raise ConnectionError, PROTOCOL_ERROR
91
+ elsif cookies == ""
92
+ raise ConnectionError, PROTOCOL_ERROR
93
+ end
94
+
95
+ @cookies = cookies
96
+ end
97
+
98
+ def contacts
99
+ postdata = {
100
+ "file" => 'contacts',
101
+ "fileType" => 'csv'
102
+ }
103
+
104
+ return @contacts if @contacts
105
+ if connected?
106
+ data, resp, cookies, forward, old_url = get(CONTACT_LIST_URL, @cookies, CONTACT_LIST_URL) + [CONTACT_LIST_URL]
107
+
108
+ until forward.nil?
109
+ data, resp, cookies, forward, old_url = get(forward, cookies, old_url) + [forward]
110
+ end
111
+
112
+ if resp.code_type != Net::HTTPOK
113
+ raise ConnectionError, self.class.const_get(:PROTOCOL_ERROR)
114
+ end
115
+
116
+ # parse data and grab <input name="user" value="8QzMPIAKs2" type="hidden">
117
+ doc = Hpricot(data)
118
+ (doc/:input).each do |input|
119
+ postdata["user"] = input.attributes["value"] if input.attributes["name"] == "user"
120
+ end
121
+
122
+ data, resp, cookies, forward, old_url = get(CONTACT_LIST_CSV_URL, @cookies, CONTACT_LIST_URL) + [CONTACT_LIST_URL]
123
+
124
+ until forward.nil?
125
+ data, resp, cookies, forward, old_url = get(forward, cookies, old_url) + [forward]
126
+ end
127
+
128
+ if data.include?("error.gif")
129
+ raise AuthenticationError, "Account invalid"
130
+ end
131
+
132
+ parse data
133
+ end
134
+ end
135
+ private
136
+
137
+ def parse(data, options={})
138
+ data = CSV::Reader.parse(data)
139
+ col_names = data.shift
140
+ @contacts = data.map do |person|
141
+ ["#{person[0]} #{person[1]}", person[4]] if person[4] && !person[4].empty?
142
+ end.compact
143
+ end
144
+
145
+ def h_to_query_string(hash)
146
+ u = ERB::Util.method(:u)
147
+ hash.map { |k, v|
148
+ u.call(k) + "=" + u.call(v)
149
+ }.join("&")
150
+ end
151
+ end
152
+
153
+ TYPES[:aol] = Aol
154
+ end
@@ -0,0 +1,232 @@
1
+ require "cgi"
2
+ require "net/http"
3
+ require "net/https"
4
+ require "uri"
5
+ require "zlib"
6
+ require "stringio"
7
+ require "thread"
8
+ require "erb"
9
+
10
+ class Contacts
11
+ TYPES = {}
12
+ VERSION = "1.2.8"
13
+
14
+ class Base
15
+ def initialize(login, password, options={})
16
+ @login = login
17
+ @password = password
18
+ @captcha_token = options[:captcha_token]
19
+ @captcha_response = options[:captcha_response]
20
+ @connections = {}
21
+ connect
22
+ end
23
+
24
+ def connect
25
+ raise AuthenticationError, "Login and password must not be nil, login: #{@login.inspect}, password: #{@password.inspect}" if @login.nil? || @login.empty? || @password.nil? || @password.empty?
26
+ real_connect
27
+ end
28
+
29
+ def connected?
30
+ @cookies && !@cookies.empty?
31
+ end
32
+
33
+ def contacts(options = {})
34
+ return @contacts if @contacts
35
+ if connected?
36
+ url = URI.parse(contact_list_url)
37
+ http = open_http(url)
38
+ resp = http.get("#{url.path}?#{url.query}",
39
+ "Cookie" => @cookies
40
+ )
41
+ data = resp.body
42
+
43
+ if resp.code_type != Net::HTTPOK
44
+ raise ConnectionError, self.class.const_get(:PROTOCOL_ERROR)
45
+ end
46
+
47
+ parse(data, options)
48
+ end
49
+ end
50
+
51
+ def login
52
+ @attempt ||= 0
53
+ @attempt += 1
54
+
55
+ if @attempt == 1
56
+ @login
57
+ else
58
+ if @login.include?("@#{domain}")
59
+ @login.sub("@#{domain}","")
60
+ else
61
+ "#{@login}@#{domain}"
62
+ end
63
+ end
64
+ end
65
+
66
+ def login_with_domain
67
+ @login.include?("@#{domain}") ? "#{@login}" : "#{@login}@#{domain}"
68
+ end
69
+
70
+ def login_without_domain
71
+ @login.include?("@#{domain}") ? @login.sub("@#{domain}",'') : "#{@login}"
72
+ end
73
+
74
+ def password
75
+ @password
76
+ end
77
+
78
+ private
79
+
80
+ def domain
81
+ @d ||= self.class.const_get(:DOMAIN) rescue nil
82
+ @d ||= URI.parse(self.class.const_get(:URL)).host.sub(/^www\./,'')
83
+ end
84
+
85
+ def contact_list_url
86
+ self.class.const_get(:CONTACT_LIST_URL)
87
+ end
88
+
89
+ def address_book_url
90
+ self.class.const_get(:ADDRESS_BOOK_URL)
91
+ end
92
+
93
+ def open_http(url)
94
+ c = @connections[Thread.current.object_id] ||= {}
95
+ http = c["#{url.host}:#{url.port}"]
96
+ unless http
97
+ http = Net::HTTP.new(url.host, url.port)
98
+ if url.port == 443
99
+ http.use_ssl = true
100
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
101
+ end
102
+ c["#{url.host}:#{url.port}"] = http
103
+ end
104
+ http.start unless http.started?
105
+ http
106
+ end
107
+
108
+ def cookie_hash_from_string(cookie_string)
109
+ cookie_string.split(";").map{|i|i.split("=", 2).map{|j|j.strip}}.inject({}){|h,i|h[i[0]]=i[1];h}
110
+ end
111
+
112
+ def parse_cookies(data, existing="")
113
+ return existing if data.nil?
114
+
115
+ cookies = cookie_hash_from_string(existing)
116
+
117
+ data.gsub!(/ ?[\w]+=EXPIRED;/,'')
118
+ data.gsub!(/ ?expires=(.*?, .*?)[;,$]/i, ';')
119
+ data.gsub!(/ ?(domain|path)=[\S]*?[;,$]/i,';')
120
+ data.gsub!(/[,;]?\s*(secure|httponly)/i,'')
121
+ data.gsub!(/(;\s*){2,}/,', ')
122
+ data.gsub!(/(,\s*){2,}/,', ')
123
+ data.sub!(/^,\s*/,'')
124
+ data.sub!(/\s*,$/,'')
125
+
126
+ data.split(", ").map{|t|t.to_s.split(";").first}.each do |data|
127
+ k, v = data.split("=", 2).map{|j|j.strip}
128
+ if cookies[k] && v.empty?
129
+ cookies.delete(k)
130
+ elsif v && !v.empty?
131
+ cookies[k] = v
132
+ end
133
+ end
134
+
135
+ cookies.map{|k,v| "#{k}=#{v}"}.join("; ")
136
+ end
137
+
138
+ def remove_cookie(cookie, cookies)
139
+ parse_cookies("#{cookie}=", cookies)
140
+ end
141
+
142
+ def post(url, postdata, cookies="", referer="")
143
+ url = URI.parse(url)
144
+ http = open_http(url)
145
+ resp = http.post(url.path, postdata,
146
+ "User-Agent" => "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0",
147
+ "Accept-Encoding" => "gzip",
148
+ "Cookie" => cookies,
149
+ "Referer" => referer,
150
+ "Content-Type" => 'application/x-www-form-urlencoded'
151
+ )
152
+ data = resp.body
153
+ data = uncompress(resp, data)
154
+ cookies = parse_cookies(resp.response['set-cookie'], cookies)
155
+ forward = resp.response['Location']
156
+ forward ||= (data =~ /<meta.*?url='([^']+)'/ ? CGI.unescapeHTML($1) : nil)
157
+ if (not forward.nil?) && URI.parse(forward).host.nil?
158
+ forward = url.scheme.to_s + "://" + url.host.to_s + forward
159
+ end
160
+ return data, resp, cookies, forward
161
+ end
162
+
163
+ def get(url, cookies="", referer="")
164
+ url = URI.parse(url)
165
+ http = open_http(url)
166
+ resp = http.get("#{url.path}?#{url.query}",
167
+ "User-Agent" => "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0",
168
+ "Accept-Encoding" => "gzip",
169
+ "Cookie" => cookies,
170
+ "Referer" => referer
171
+ )
172
+ data = resp.body
173
+ data = uncompress(resp, data)
174
+ cookies = parse_cookies(resp.response['set-cookie'], cookies)
175
+ forward = resp.response['Location']
176
+ if (not forward.nil?) && URI.parse(forward).host.nil?
177
+ forward = url.scheme.to_s + "://" + url.host.to_s + forward
178
+ end
179
+ return data, resp, cookies, forward
180
+ end
181
+
182
+ def uncompress(resp, data)
183
+ case resp.response['content-encoding']
184
+ when 'gzip'
185
+ gz = Zlib::GzipReader.new(StringIO.new(data))
186
+ data = gz.read
187
+ gz.close
188
+ resp.response['content-encoding'] = nil
189
+ # FIXME: Not sure what Hotmail was feeding me with their 'deflate',
190
+ # but the headers definitely were not right
191
+ when 'deflate'
192
+ data = Zlib::Inflate.inflate(data)
193
+ resp.response['content-encoding'] = nil
194
+ end
195
+
196
+ data
197
+ end
198
+ end
199
+
200
+ class ContactsError < StandardError
201
+ end
202
+
203
+ class AuthenticationError < ContactsError
204
+ end
205
+
206
+ class ConnectionError < ContactsError
207
+ end
208
+
209
+ class TypeNotFound < ContactsError
210
+ end
211
+
212
+ class MailServerError < ContactsError
213
+ end
214
+
215
+ def self.new(type, login, password, options={})
216
+ if TYPES.include?(type.to_s.intern)
217
+ TYPES[type.to_s.intern].new(login, password, options)
218
+ else
219
+ raise TypeNotFound, "#{type.inspect} is not a valid type, please choose one of the following: #{TYPES.keys.inspect}"
220
+ end
221
+ end
222
+
223
+ def self.guess(login, password, options={})
224
+ TYPES.inject([]) do |a, t|
225
+ begin
226
+ a + t[1].new(login, password, options).contacts
227
+ rescue AuthenticationError
228
+ a
229
+ end
230
+ end.uniq
231
+ end
232
+ end
@@ -0,0 +1,35 @@
1
+ require 'gdata'
2
+
3
+ class Contacts
4
+ class Gmail < Base
5
+
6
+ CONTACTS_SCOPE = 'http://www.google.com/m8/feeds/'
7
+ CONTACTS_FEED = CONTACTS_SCOPE + 'contacts/default/full/?max-results=1000'
8
+
9
+ def contacts
10
+ return @contacts if @contacts
11
+ end
12
+
13
+ def real_connect
14
+ @client = GData::Client::Contacts.new
15
+ @client.clientlogin(@login, @password, @captcha_token, @captcha_response)
16
+
17
+ feed = @client.get(CONTACTS_FEED).to_xml
18
+
19
+ @contacts = feed.elements.to_a('entry').collect do |entry|
20
+ title, email = entry.elements['title'].text, nil
21
+ entry.elements.each('gd:email') do |e|
22
+ email = e.attribute('address').value if e.attribute('primary')
23
+ end
24
+ [title, email] unless email.nil?
25
+ end
26
+ @contacts.compact!
27
+ rescue GData::Client::AuthorizationError => e
28
+ raise AuthenticationError, "Username or password are incorrect"
29
+ end
30
+
31
+ private
32
+
33
+ TYPES[:gmail] = Gmail
34
+ end
35
+ end
@@ -0,0 +1,9 @@
1
+ require 'erb'
2
+ class Hash
3
+ def to_query_string
4
+ u = ERB::Util.method(:u)
5
+ map { |k, v|
6
+ u.call(k) + "=" + u.call(v)
7
+ }.join("&")
8
+ end
9
+ end
@@ -0,0 +1,93 @@
1
+ require 'rubygems'
2
+ require 'iconv'
3
+
4
+ class Contacts
5
+ class Hotmail < Base
6
+ URL = "https://login.live.com/login.srf?id=2"
7
+ CONTACT_LIST_URL = "https://mail.live.com/mail/GetContacts.aspx"
8
+ 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"
9
+ PWDPAD = "IfYouAreReadingThisYouHaveTooMuchFreeTime"
10
+
11
+ def real_connect
12
+
13
+ data, resp, cookies, forward = get(URL)
14
+ old_url = URL
15
+ until forward.nil?
16
+ data, resp, cookies, forward, old_url = get(forward, cookies, old_url) + [forward]
17
+ end
18
+
19
+ postdata = "PPSX=%s&PwdPad=%s&login=%s&passwd=%s&LoginOptions=2&PPFT=%s" % [
20
+ CGI.escape(data.split("><").grep(/PPSX/).first[/=\S+$/][2..-3]),
21
+ PWDPAD[0...(PWDPAD.length-@password.length)],
22
+ CGI.escape(login),
23
+ CGI.escape(password),
24
+ CGI.escape(data.split("><").grep(/PPFT/).first[/=\S+$/][2..-3])
25
+ ]
26
+
27
+ form_url = data.split("><").grep(/form/).first.split[5][8..-2]
28
+ data, resp, cookies, forward = post(form_url, postdata, cookies)
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
+
35
+ if data.index("The e-mail address or password is incorrect")
36
+ raise AuthenticationError, "Username and password do not match"
37
+ elsif data != ""
38
+ raise AuthenticationError, "Required field must not be blank"
39
+ elsif cookies == ""
40
+ raise ConnectionError, PROTOCOL_ERROR
41
+ end
42
+
43
+ data, resp, cookies, forward = get("http://mail.live.com/mail", cookies)
44
+ until forward.nil?
45
+ data, resp, cookies, forward, old_url = get(forward, cookies, old_url) + [forward]
46
+ end
47
+
48
+
49
+ @domain = URI.parse(old_url).host
50
+ @cookies = cookies
51
+ rescue AuthenticationError => m
52
+ if @attempt == 1
53
+ retry
54
+ else
55
+ raise m
56
+ end
57
+ end
58
+
59
+ def contacts(options = {})
60
+ if @contacts.nil? && connected?
61
+ url = URI.parse(contact_list_url)
62
+ data, resp, cookies, forward = get(get_contact_list_url, @cookies )
63
+ data = Iconv.conv('UTF-8//IGNORE','UTF-8',data)
64
+ data.gsub!(";",",")
65
+ data.gsub!("'","")
66
+ # data = data.gsub(/[\x80-\xff]/n,"")
67
+
68
+ @contacts = CSV.parse(data, {:headers => true, :col_sep => ','}).map do |row|
69
+ name = ""
70
+ name = row["First Name"] if !row["First Name"].nil?
71
+ name << " #{row["Last Name"]}" if !row["Last Name"].nil?
72
+ email = row["E-mail Address"] || ""
73
+ [name, email]
74
+ end
75
+ @contacts.delete_if{|x| x[1].blank?}
76
+ else
77
+ @contacts || []
78
+ end
79
+ end
80
+
81
+ private
82
+
83
+ TYPES[:hotmail] = Hotmail
84
+
85
+ # the contacts url is dynamic
86
+ # luckily it tells us where to find it
87
+ def get_contact_list_url
88
+ data = get(CONTACT_LIST_URL, @cookies)[0]
89
+ html_doc = Nokogiri::HTML(data)
90
+ html_doc.xpath("//a")[0]["href"]
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,17 @@
1
+ if !Object.const_defined?('ActiveSupport')
2
+ require 'json'
3
+ end
4
+
5
+ class Contacts
6
+ def self.parse_json( string )
7
+ string = string.gsub("'",'"')
8
+ if Object.const_defined?('ActiveSupport') and
9
+ ActiveSupport.const_defined?('JSON')
10
+ ActiveSupport::JSON.decode( string )
11
+ elsif Object.const_defined?('JSON')
12
+ JSON.parse( string )
13
+ else
14
+ raise 'Contacts requires JSON or Rails (with ActiveSupport::JSON)'
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,118 @@
1
+ class Contacts
2
+ class NetEase < Base
3
+ URL = "http://www.163.com"
4
+ LOGIN_URL = "https://reg.163.com/logins.jsp"
5
+ LoginData = {
6
+ :url2 => {
7
+ :wy163 => 'http://mail.163.com/errorpage/err_163.htm',
8
+ :wy126 => 'http://mail.126.com/errorpage/err_126.htm',
9
+ :yeah => 'http://mail.yeah.net/errorpage/err_yeah.htm'
10
+ },
11
+ :url => {
12
+ :wy163 => 'http://entry.mail.163.com/coremail/fcg/ntesdoor2?lightweight=1&verifycookie=1&language=-1&style=-1&username=%s',
13
+ :wy126 => 'http://entry.mail.126.com/cgi/ntesdoor?hid=10010102&lightweight=1&verifycookie=1&language=0&style=-1&username=%s',
14
+ :yeah => 'http://entry.mail.yeah.net/cgi/ntesdoor?lightweight=1&verifycookie=1&style=-1&username=%s'
15
+ },
16
+ :product => {
17
+ :wy163 => 'mail163',
18
+ :wy126 => 'mail126',
19
+ :yeah => 'mailyeah'
20
+ }
21
+ }
22
+ ENTER_MAIL_URL = {
23
+ :wy163 => "http://entry.mail.163.com/coremail/fcg/ntesdoor2?lightweight=1&verifycookie=1&language=-1&style=-1&username=%s",
24
+ :wy126 => "http://entry.mail.126.com/cgi/ntesdoor?hid=10010102&lightweight=1&verifycookie=1&language=0&style=-1&username=%s",
25
+ :yeah => "http://entry.mail.yeah.net/cgi/ntesdoor?lightweight=1&verifycookie=1&style=-1&username=%s"
26
+ }
27
+
28
+ CONTACT_LIST_URL = "%ss?sid=%s&func=global:sequential"
29
+ PROTOCOL_ERROR = "netease has changed its protocols, please upgrade this library first. you can also contact kamechb@gmail.com"
30
+
31
+ def initialize(login, password, options={})
32
+ @mail_type = get_mail_type(login)
33
+ super(login,password,options)
34
+ end
35
+
36
+ def real_connect
37
+ login_for_cookies
38
+ enter_mail_server
39
+ end
40
+
41
+ def contacts
42
+ return @contacts if @contacts
43
+ if connected?
44
+ url = URI.parse(CONTACT_LIST_URL % [@mail_server,@sid])
45
+ http = open_http(url)
46
+ postdata = '<?xml version="1.0"?><object><array name="items"><object><string name="func">pab:searchContacts</string><object name="var"><array name="order"><object><string name="field">FN</string><boolean name="ignoreCase">true</boolean></object></array></object></object><object><string name="func">user:getSignatures</string></object><object><string name="func">pab:getAllGroups</string></object></array></object>'
47
+ set_header = {"Cookie" => @cookies,'Accept' => 'text/javascript','Content-Type' => 'application/xml; charset=UTF-8'}
48
+ resp = http.post("#{url.path}?#{url.query}",postdata,set_header)
49
+ data = resp.body
50
+ if resp.code_type != Net::HTTPOK
51
+ raise ConnectionError, self.class.const_get(:PROTOCOL_ERROR)
52
+ end
53
+ parse(data)
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def get_mail_type(username)
60
+ if username.include?("@126.com")
61
+ :wy126
62
+ elsif username.include?("@163.com")
63
+ :wy163
64
+ elsif username.include?("@yeah.net")
65
+ :yeah
66
+ else
67
+ raise MailServerError, "there are only three mail servers that 126.com, 163.com and yeah.net. please add domain after username"
68
+ end
69
+ end
70
+
71
+ def parse(data)
72
+ json_data = Contacts.parse_json(data)
73
+ json_data['var'][0]['var'].map{|contactor|
74
+ [contactor['FN'],contactor['EMAIL;PREF']]
75
+ }
76
+ end
77
+
78
+ def login_for_cookies
79
+ data = {
80
+ :type => '1',
81
+ :url => LoginData[:url][@mail_type],
82
+ :username => @login,
83
+ :password => @password,
84
+ :selType => '-1',
85
+ :remUser => '1',
86
+ :secure => 'on',
87
+ :verifycookie => '1',
88
+ :style => '-1',
89
+ :product => LoginData[:product][@mail_type],
90
+ :savelogin => '',
91
+ :url2 => LoginData[:url2][@mail_type]
92
+ }
93
+ postdata = data.to_query_string
94
+ #login and get cookie
95
+ data, resp, cookies, forward = post(LOGIN_URL,postdata)
96
+ @cookies = cookies
97
+ if data.index(LoginData[:url2][@mail_type])
98
+ raise AuthenticationError, "Username or password error"
99
+ end
100
+ end
101
+
102
+ def enter_mail_server
103
+ #get mail server and sid
104
+ enter_mail_url = ENTER_MAIL_URL[@mail_type] % @login
105
+ data, resp, cookies, forward = get(enter_mail_url,@cookies)
106
+ location = resp['Location']
107
+ data_reg = /<a.*?(http.*?)main.jsp\?sid=(.*?)\">/
108
+ location_reg = /(http.*?)main.jsp\?sid=(.*)/
109
+ unless data.match(data_reg) || location.match(location_reg)
110
+ raise ConnectionError, self.class.const_get(:PROTOCOL_ERROR)
111
+ end
112
+ @cookies = cookies
113
+ @mail_server = $1
114
+ @sid = $2
115
+ end
116
+ TYPES[:net_ease] = NetEase
117
+ end
118
+ 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,92 @@
1
+ class Contacts
2
+ class Sina < Base
3
+ URL = "http://mail.sina.com.cn"
4
+ LOGIN_URL = {
5
+ :sina_cn => "https://mail.sina.com.cn/cgi-bin/cnlogin.php",
6
+ :sina_com => "https://mail.sina.com.cn/cgi-bin/login.php"
7
+ }
8
+ LOGIN_COOKIE = {
9
+ :sina_cn => "sina_cn_mail_recid=true",
10
+ :sina_com => "sina_free_mail_recid=true; sina_free_mail_ltype=uid; sina_vip_mail_recid=false"
11
+ }
12
+ DOMAIN = {
13
+ :sina_cn => 'sina.cn',
14
+ :sina_com => 'sina.com'
15
+ }
16
+ PROTOCOL_ERROR = "sina has changed its protocols, please upgrade this library first. you can also contact kamechb@gmail.com"
17
+
18
+ def initialize(login, password, options={})
19
+ @mail_type = get_mail_type(login)
20
+ super(login,password,options)
21
+ end
22
+
23
+ def real_connect
24
+ login_for_cookies
25
+ redirect_for_location
26
+ end
27
+
28
+ def contacts
29
+ return @contacts if @contacts
30
+ if connected?
31
+ data, resp, cookies, forward = get(@mail_url,@cookies)
32
+ if resp.code_type != Net::HTTPOK
33
+ raise ConnectionError, self.class.const_get(:PROTOCOL_ERROR)
34
+ end
35
+ parse(data)
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def get_mail_type(username)
42
+ if username.include?("@sina.com")
43
+ :sina_com
44
+ elsif username.include?("@sina.cn")
45
+ :sina_cn
46
+ else
47
+ raise MailServerError, "there are only two mail servers that sina.com and sina.cn. please add domain after username"
48
+ end
49
+ end
50
+
51
+ def parse(data)
52
+ #data =~ /conf.*?contacts:.*?(\{.*?\}),\s*groups:/m
53
+ data =~ /conf.*?contacts.*?(\{.*\}).*?groups/m
54
+ contacts = $1.gsub("&quot;",'')
55
+ contacts = ActiveSupport::JSON.decode(contacts)
56
+ contacts['contact'].map{|contactor|
57
+ [contactor['name'],contactor['email']]
58
+ }
59
+ end
60
+
61
+ def login_for_cookies
62
+ data = {
63
+ :domain => DOMAIN[@mail_type],
64
+ :logintype => 'uid',
65
+ :u => @login,
66
+ :psw => @password,
67
+ :savelogin => 'on',
68
+ :sshchk => 'on',
69
+ :ssl => 'on'
70
+ }
71
+ data, resp, cookies, forward = post(LOGIN_URL[@mail_type],data.to_query_string,LOGIN_COOKIE[@mail_type])
72
+ login_faile_flag = %r{form.*?action.*?http.*?mail.sina.com.cn/cgi-bin/.*?login.php}m
73
+ if data.match(login_faile_flag)
74
+ raise AuthenticationError, "Username or password error"
75
+ end
76
+ data.match(/URL=(http:\/\/.*?)'>/)
77
+ @redirect_url = $1
78
+ @mail_server = @redirect_url.match(/(http:\/\/.*\..*?)\//)
79
+ @cookies = cookies
80
+ end
81
+
82
+ def redirect_for_location
83
+ data, resp, cookies, forward = get(@redirect_url,@cookies)
84
+ location = resp['Location']
85
+ @mail_url = location.index("http://") ? location : "#{@mail_server}#{location}"
86
+ @cookies = cookies
87
+ end
88
+
89
+ TYPES[:sina] = Sina
90
+ end
91
+
92
+ end
@@ -0,0 +1,59 @@
1
+ class Contacts
2
+ class Sohu < Base
3
+ URL = "http://mail.sohu.com"
4
+ DOMAIN = "sohu.com"
5
+ LOGIN_URL = "https://passport.sohu.com/sso/login.jsp"
6
+ LOGIN_COOKIE = "IPLOC=CN3301; SUV=1008301317090277"
7
+ MAIL_URL = "http://mail.sohu.com/bapp/117/main"
8
+ PROTOCOL_ERROR = "sohu has changed its protocols, please upgrade this library first. you can also contact kamechb@gmail.com"
9
+
10
+ def real_connect
11
+ login_for_cookies
12
+ end
13
+
14
+ def contacts
15
+ return @contacts if @contacts
16
+ if connected?
17
+ data, resp, cookies, forward = get(MAIL_URL,@cookies)
18
+ if resp.code_type != Net::HTTPOK
19
+ raise ConnectionError, self.class.const_get(:PROTOCOL_ERROR)
20
+ end
21
+ parse(data)
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+
28
+ def parse(data)
29
+ data.match(/ADDRESSES.*?'(\{.*?\})';/m)
30
+ contacts = ActiveSupport::JSON.decode($1)
31
+ contacts['contact'].map{|contactor|
32
+ [contactor['nickname'],contactor['email']]
33
+ }
34
+ end
35
+
36
+ def login_for_cookies
37
+ data = {
38
+ :userid => @login,
39
+ :password => @password,
40
+ :appid => '1000',
41
+ :persistentcookie => '0',
42
+ :s => '1283173792650',
43
+ :b => '2',
44
+ :w => '1280',
45
+ :pwdtype => '0',
46
+ :v => '26'
47
+ }
48
+ data, resp, cookies, forward = get("#{LOGIN_URL}?#{data.to_query_string}",LOGIN_COOKIE)
49
+ login_faile_flag = %r{login_status.*?error}
50
+ if data.match(login_faile_flag)
51
+ raise AuthenticationError, "Username or password error"
52
+ end
53
+ @cookies = cookies
54
+ end
55
+
56
+ TYPES[:sohu] = Sohu
57
+ end
58
+
59
+ end
@@ -0,0 +1,103 @@
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
+ if connected?
39
+ # first, get the addressbook site with the new crumb parameter
40
+ url = URI.parse(address_book_url)
41
+ http = open_http(url)
42
+ resp = http.get("#{url.path}?#{url.query}",
43
+ "Cookie" => @cookies
44
+ )
45
+ data = resp.body
46
+ if resp.code_type != Net::HTTPOK
47
+ raise ConnectionError, self.class.const_get(:PROTOCOL_ERROR)
48
+ end
49
+
50
+ crumb = data.to_s[/dotCrumb: '(.*?)'/][13...-1]
51
+
52
+ # now proceed with the new ".crumb" parameter to get the csv data
53
+ url = URI.parse(contact_list_url.sub("_crumb=crumb","_crumb=#{crumb}").sub("time", Time.now.to_f.to_s.sub(".","")[0...-2]))
54
+ http = open_http(url)
55
+ resp = http.get("#{url.path}?#{url.query}",
56
+ "Cookie" => @cookies,
57
+ "X-Requested-With" => "XMLHttpRequest",
58
+ "Referer" => address_book_url
59
+ )
60
+ data = resp.body
61
+ if resp.code_type != Net::HTTPOK
62
+ raise ConnectionError, self.class.const_get(:PROTOCOL_ERROR)
63
+ end
64
+
65
+ if data =~ /"TotalABContacts":(\d+)/
66
+ total = $1.to_i
67
+ ((total / 50.0).ceil).times do |i|
68
+ # now proceed with the new ".crumb" parameter to get the csv data
69
+ 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]))
70
+ http = open_http(url)
71
+ resp = http.get("#{url.path}?#{url.query}",
72
+ "Cookie" => @cookies,
73
+ "X-Requested-With" => "XMLHttpRequest",
74
+ "Referer" => address_book_url
75
+ )
76
+ data = resp.body
77
+ if resp.code_type != Net::HTTPOK
78
+ raise ConnectionError, self.class.const_get(:PROTOCOL_ERROR)
79
+ end
80
+
81
+ parse data
82
+ end
83
+ end
84
+
85
+ @contacts
86
+ end
87
+ end
88
+
89
+ private
90
+
91
+ def parse(data, options={})
92
+ @contacts ||= []
93
+ @contacts += Contacts.parse_json(data)["response"]["ResultSet"]["Contacts"].to_a.select{|contact|!contact["email"].to_s.empty?}.map do |contact|
94
+ name = contact["contactName"].split(",")
95
+ [[name.pop, name.join(",")].join(" ").strip, contact["email"]]
96
+ end if data =~ /^\{"response":/
97
+ @contacts
98
+ end
99
+
100
+ end
101
+
102
+ TYPES[:yahoo] = Yahoo
103
+ end
@@ -1,9 +1,6 @@
1
1
  $:.unshift(File.dirname(__FILE__)+"/contacts/")
2
2
 
3
3
  require 'rubygems'
4
- unless Object.const_defined?('ActiveSupport')
5
- require 'activesupport'
6
- end
7
4
  require 'base'
8
5
  require 'gmail'
9
6
  require 'hotmail'
@@ -14,5 +11,4 @@ require 'net_ease'
14
11
  require 'sina'
15
12
  require 'sohu'
16
13
  require 'json_picker'
17
- require 'hash_ext'
18
- require 'iconv'
14
+ require 'hash_ext'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: contacts_cn_19
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -18,6 +18,17 @@ extensions: []
18
18
  extra_rdoc_files: []
19
19
  files:
20
20
  - lib/contacts_cn_19.rb
21
+ - lib/contacts/aol.rb
22
+ - lib/contacts/sohu.rb
23
+ - lib/contacts/yahoo.rb
24
+ - lib/contacts/json_picker.rb
25
+ - lib/contacts/base.rb
26
+ - lib/contacts/gmail.rb
27
+ - lib/contacts/sina.rb
28
+ - lib/contacts/hash_ext.rb
29
+ - lib/contacts/hotmail.rb
30
+ - lib/contacts/plaxo.rb
31
+ - lib/contacts/net_ease.rb
21
32
  homepage: https://github.com/wxluckly/contacts_cn_19
22
33
  licenses: []
23
34
  post_install_message: