contacts 1.0.1 → 1.0.2

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.
@@ -137,7 +137,7 @@ Class <a href="Contacts/Yahoo.html" class="link">Contacts::Yahoo</a><br />
137
137
  <tr class="top-aligned-row context-row">
138
138
  <td class="context-item-name">VERSION</td>
139
139
  <td>=</td>
140
- <td class="context-item-value">&quot;1.0.1&quot;</td>
140
+ <td class="context-item-value">&quot;1.0.2&quot;</td>
141
141
  </tr>
142
142
  </table>
143
143
  </div>
@@ -10,7 +10,7 @@
10
10
  <link rel="stylesheet" href="../.././rdoc-style.css" type="text/css" media="screen" />
11
11
  </head>
12
12
  <body class="standalone-code">
13
- <pre><span class="ruby-comment cmt"># File lib/contacts/base.rb, line 154</span>
13
+ <pre><span class="ruby-comment cmt"># File lib/contacts/base.rb, line 182</span>
14
14
  <span class="ruby-keyword kw">def</span> <span class="ruby-keyword kw">self</span>.<span class="ruby-identifier">new</span>(<span class="ruby-identifier">type</span>, <span class="ruby-identifier">login</span>, <span class="ruby-identifier">password</span>)
15
15
  <span class="ruby-keyword kw">if</span> <span class="ruby-constant">TYPES</span>.<span class="ruby-identifier">include?</span>(<span class="ruby-identifier">type</span>.<span class="ruby-identifier">to_s</span>.<span class="ruby-identifier">intern</span>)
16
16
  <span class="ruby-constant">TYPES</span>[<span class="ruby-identifier">type</span>.<span class="ruby-identifier">to_s</span>.<span class="ruby-identifier">intern</span>].<span class="ruby-identifier">new</span>(<span class="ruby-identifier">login</span>, <span class="ruby-identifier">password</span>)
@@ -10,7 +10,7 @@
10
10
  <link rel="stylesheet" href="../.././rdoc-style.css" type="text/css" media="screen" />
11
11
  </head>
12
12
  <body class="standalone-code">
13
- <pre><span class="ruby-comment cmt"># File lib/contacts/base.rb, line 162</span>
13
+ <pre><span class="ruby-comment cmt"># File lib/contacts/base.rb, line 190</span>
14
14
  <span class="ruby-keyword kw">def</span> <span class="ruby-keyword kw">self</span>.<span class="ruby-identifier">guess</span>(<span class="ruby-identifier">login</span>, <span class="ruby-identifier">password</span>)
15
15
  <span class="ruby-constant">TYPES</span>.<span class="ruby-identifier">inject</span>([]) <span class="ruby-keyword kw">do</span> <span class="ruby-operator">|</span><span class="ruby-identifier">a</span>, <span class="ruby-identifier">t</span><span class="ruby-operator">|</span>
16
16
  <span class="ruby-keyword kw">begin</span>
@@ -10,10 +10,11 @@
10
10
  <link rel="stylesheet" href="../../.././rdoc-style.css" type="text/css" media="screen" />
11
11
  </head>
12
12
  <body class="standalone-code">
13
- <pre><span class="ruby-comment cmt"># File lib/contacts/base.rb, line 11</span>
13
+ <pre><span class="ruby-comment cmt"># File lib/contacts/base.rb, line 14</span>
14
14
  <span class="ruby-keyword kw">def</span> <span class="ruby-identifier">initialize</span>(<span class="ruby-identifier">login</span>, <span class="ruby-identifier">password</span>)
15
15
  <span class="ruby-ivar">@login</span> = <span class="ruby-identifier">login</span>
16
16
  <span class="ruby-ivar">@password</span> = <span class="ruby-identifier">password</span>
17
+ <span class="ruby-ivar">@connections</span> = {}
17
18
  <span class="ruby-identifier">connect</span>
18
19
  <span class="ruby-keyword kw">end</span></pre>
19
20
  </body>
@@ -10,7 +10,7 @@
10
10
  <link rel="stylesheet" href="../../.././rdoc-style.css" type="text/css" media="screen" />
11
11
  </head>
12
12
  <body class="standalone-code">
13
- <pre><span class="ruby-comment cmt"># File lib/contacts/base.rb, line 17</span>
13
+ <pre><span class="ruby-comment cmt"># File lib/contacts/base.rb, line 21</span>
14
14
  <span class="ruby-keyword kw">def</span> <span class="ruby-identifier">connect</span>
15
15
  <span class="ruby-identifier">raise</span> <span class="ruby-constant">AuthenticationError</span>, <span class="ruby-node">&quot;Login and password must not be nil, login: #{@login.inspect}, password: #{@password.inspect}&quot;</span> <span class="ruby-keyword kw">if</span> <span class="ruby-ivar">@login</span>.<span class="ruby-identifier">nil?</span> <span class="ruby-operator">||</span> <span class="ruby-ivar">@password</span>.<span class="ruby-identifier">nil?</span>
16
16
  <span class="ruby-identifier">real_connect</span>
@@ -10,7 +10,7 @@
10
10
  <link rel="stylesheet" href="../../.././rdoc-style.css" type="text/css" media="screen" />
11
11
  </head>
12
12
  <body class="standalone-code">
13
- <pre><span class="ruby-comment cmt"># File lib/contacts/base.rb, line 22</span>
13
+ <pre><span class="ruby-comment cmt"># File lib/contacts/base.rb, line 26</span>
14
14
  <span class="ruby-keyword kw">def</span> <span class="ruby-identifier">connected?</span>
15
15
  <span class="ruby-ivar">@cookies</span> <span class="ruby-operator">&amp;&amp;</span> <span class="ruby-operator">!</span><span class="ruby-ivar">@cookies</span>.<span class="ruby-identifier">empty?</span>
16
16
  <span class="ruby-keyword kw">end</span></pre>
@@ -10,16 +10,12 @@
10
10
  <link rel="stylesheet" href="../../.././rdoc-style.css" type="text/css" media="screen" />
11
11
  </head>
12
12
  <body class="standalone-code">
13
- <pre><span class="ruby-comment cmt"># File lib/contacts/base.rb, line 26</span>
13
+ <pre><span class="ruby-comment cmt"># File lib/contacts/base.rb, line 30</span>
14
14
  <span class="ruby-keyword kw">def</span> <span class="ruby-identifier">contacts</span>
15
15
  <span class="ruby-keyword kw">return</span> <span class="ruby-ivar">@contacts</span> <span class="ruby-keyword kw">if</span> <span class="ruby-ivar">@contacts</span>
16
16
  <span class="ruby-keyword kw">if</span> <span class="ruby-identifier">connected?</span>
17
17
  <span class="ruby-identifier">url</span> = <span class="ruby-constant">URI</span>.<span class="ruby-identifier">parse</span>(<span class="ruby-identifier">contact_list_url</span>)
18
- <span class="ruby-identifier">http</span> = <span class="ruby-constant">Net</span><span class="ruby-operator">::</span><span class="ruby-constant">HTTP</span>.<span class="ruby-identifier">new</span>(<span class="ruby-identifier">url</span>.<span class="ruby-identifier">host</span>, <span class="ruby-identifier">url</span>.<span class="ruby-identifier">port</span>)
19
- <span class="ruby-keyword kw">if</span> <span class="ruby-identifier">url</span>.<span class="ruby-identifier">port</span> <span class="ruby-operator">==</span> <span class="ruby-value">443</span>
20
- <span class="ruby-identifier">http</span>.<span class="ruby-identifier">use_ssl</span> = <span class="ruby-keyword kw">true</span>
21
- <span class="ruby-identifier">http</span>.<span class="ruby-identifier">verify_mode</span> = <span class="ruby-constant">OpenSSL</span><span class="ruby-operator">::</span><span class="ruby-constant">SSL</span><span class="ruby-operator">::</span><span class="ruby-constant">VERIFY_NONE</span>
22
- <span class="ruby-keyword kw">end</span>
18
+ <span class="ruby-identifier">http</span> = <span class="ruby-identifier">open_http</span>(<span class="ruby-identifier">url</span>)
23
19
  <span class="ruby-identifier">resp</span>, <span class="ruby-identifier">data</span> = <span class="ruby-identifier">http</span>.<span class="ruby-identifier">get</span>(<span class="ruby-node">&quot;#{url.path}?#{url.query}&quot;</span>,
24
20
  <span class="ruby-value str">&quot;Cookie&quot;</span> =<span class="ruby-operator">&gt;</span> <span class="ruby-ivar">@cookies</span>
25
21
  )
@@ -115,6 +115,11 @@
115
115
  <td>=</td>
116
116
  <td class="context-item-value">&quot;http://%s/cgi-bin/addresses&quot;</td>
117
117
  </tr>
118
+ <tr class="top-aligned-row context-row">
119
+ <td class="context-item-name">COMPOSE_URL</td>
120
+ <td>=</td>
121
+ <td class="context-item-value">&quot;http://%s/cgi-bin/compose?&quot;</td>
122
+ </tr>
118
123
  <tr class="top-aligned-row context-row">
119
124
  <td class="context-item-name">PROTOCOL_ERROR</td>
120
125
  <td>=</td>
@@ -125,6 +130,11 @@
125
130
  <td>=</td>
126
131
  <td class="context-item-value">&quot;IfYouAreReadingThisYouHaveTooMuchFreeTime&quot;</td>
127
132
  </tr>
133
+ <tr class="top-aligned-row context-row">
134
+ <td class="context-item-name">MAX_HTTP_THREADS</td>
135
+ <td>=</td>
136
+ <td class="context-item-value">8</td>
137
+ </tr>
128
138
  </table>
129
139
  </div>
130
140
  </div>
@@ -10,7 +10,7 @@
10
10
  <link rel="stylesheet" href="../../.././rdoc-style.css" type="text/css" media="screen" />
11
11
  </head>
12
12
  <body class="standalone-code">
13
- <pre><span class="ruby-comment cmt"># File lib/contacts/hotmail.rb, line 8</span>
13
+ <pre><span class="ruby-comment cmt"># File lib/contacts/hotmail.rb, line 10</span>
14
14
  <span class="ruby-keyword kw">def</span> <span class="ruby-identifier">real_connect</span>
15
15
  <span class="ruby-identifier">data</span>, <span class="ruby-identifier">resp</span>, <span class="ruby-identifier">cookies</span>, <span class="ruby-identifier">forward</span> = <span class="ruby-identifier">get</span>(<span class="ruby-constant">URL</span>)
16
16
  <span class="ruby-identifier">data</span>, <span class="ruby-identifier">resp</span>, <span class="ruby-identifier">cookies</span>, <span class="ruby-identifier">forward</span> = <span class="ruby-identifier">get</span>(<span class="ruby-identifier">forward</span>, <span class="ruby-identifier">cookies</span>, <span class="ruby-constant">URL</span>)
data/doc/created.rid CHANGED
@@ -1 +1 @@
1
- Thu Dec 07 13:33:56 PST 2006
1
+ Fri Mar 30 10:28:09 PDT 2007
@@ -56,7 +56,7 @@
56
56
  </tr>
57
57
  <tr class="top-aligned-row">
58
58
  <td><strong>Last Update:</strong></td>
59
- <td>Thu Dec 07 10:27:59 PST 2006</td>
59
+ <td>Fri Mar 30 10:27:18 PDT 2007</td>
60
60
  </tr>
61
61
  </table>
62
62
  </div>
@@ -77,6 +77,9 @@
77
77
  net/http&nbsp;&nbsp;
78
78
  net/https&nbsp;&nbsp;
79
79
  uri&nbsp;&nbsp;
80
+ zlib&nbsp;&nbsp;
81
+ stringio&nbsp;&nbsp;
82
+ thread&nbsp;&nbsp;
80
83
  </div>
81
84
  </div>
82
85
 
@@ -56,7 +56,7 @@
56
56
  </tr>
57
57
  <tr class="top-aligned-row">
58
58
  <td><strong>Last Update:</strong></td>
59
- <td>Thu Dec 07 10:57:58 PST 2006</td>
59
+ <td>Fri Mar 30 10:23:35 PDT 2007</td>
60
60
  </tr>
61
61
  </table>
62
62
  </div>
data/lib/contacts/base.rb CHANGED
@@ -2,15 +2,19 @@ require "cgi"
2
2
  require "net/http"
3
3
  require "net/https"
4
4
  require "uri"
5
+ require "zlib"
6
+ require "stringio"
7
+ require "thread"
5
8
 
6
9
  class Contacts
7
10
  TYPES = {}
8
- VERSION = "1.0.1"
11
+ VERSION = "1.0.2"
9
12
 
10
13
  class Base
11
14
  def initialize(login, password)
12
15
  @login = login
13
16
  @password = password
17
+ @connections = {}
14
18
  connect
15
19
  end
16
20
 
@@ -27,11 +31,7 @@ class Contacts
27
31
  return @contacts if @contacts
28
32
  if connected?
29
33
  url = URI.parse(contact_list_url)
30
- http = Net::HTTP.new(url.host, url.port)
31
- if url.port == 443
32
- http.use_ssl = true
33
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
34
- end
34
+ http = open_http(url)
35
35
  resp, data = http.get("#{url.path}?#{url.query}",
36
36
  "Cookie" => @cookies
37
37
  )
@@ -72,6 +72,21 @@ class Contacts
72
72
  def contact_list_url
73
73
  self.class.const_get(:CONTACT_LIST_URL)
74
74
  end
75
+
76
+ def open_http(url)
77
+ c = @connections[Thread.current.object_id] ||= {}
78
+ http = c["#{url.host}:#{url.port}"]
79
+ unless http
80
+ http = Net::HTTP.new(url.host, url.port)
81
+ if url.port == 443
82
+ http.use_ssl = true
83
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
84
+ end
85
+ c["#{url.host}:#{url.port}"] = http
86
+ end
87
+ http.start unless http.started?
88
+ http
89
+ end
75
90
 
76
91
  def parse_cookies(data, existing="")
77
92
  return existing if data.nil?
@@ -105,17 +120,15 @@ class Contacts
105
120
 
106
121
  def post(url, postdata, cookies="", referer="")
107
122
  url = URI.parse(url)
108
- http = Net::HTTP.new(url.host, url.port)
109
- if url.port == 443
110
- http.use_ssl = true
111
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
112
- end
123
+ http = open_http(url)
113
124
  resp, data = http.post(url.path, postdata,
114
125
  "User-Agent" => "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0",
126
+ "Accept-Encoding" => "gzip",
115
127
  "Cookie" => cookies,
116
128
  "Referer" => referer,
117
129
  "Content-Type" => 'application/x-www-form-urlencoded'
118
130
  )
131
+ data = uncompress(resp, data)
119
132
  cookies = parse_cookies(resp.response['set-cookie'], cookies)
120
133
  forward = resp.response['Location']
121
134
  return data, resp, cookies, forward
@@ -123,20 +136,35 @@ class Contacts
123
136
 
124
137
  def get(url, cookies="", referer="")
125
138
  url = URI.parse(url)
126
- http = Net::HTTP.new(url.host, url.port)
127
- if url.port == 443
128
- http.use_ssl = true
129
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
130
- end
139
+ http = open_http(url)
131
140
  resp, data = http.get("#{url.path}?#{url.query}",
132
141
  "User-Agent" => "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0",
142
+ "Accept-Encoding" => "gzip",
133
143
  "Cookie" => cookies,
134
144
  "Referer" => referer
135
145
  )
146
+ data = uncompress(resp, data)
136
147
  cookies = parse_cookies(resp.response['set-cookie'], cookies)
137
148
  forward = resp.response['Location']
138
149
  return data, resp, cookies, forward
139
150
  end
151
+
152
+ def uncompress(resp, data)
153
+ case resp.response['content-encoding']
154
+ when 'gzip':
155
+ gz = Zlib::GzipReader.new(StringIO.new(data))
156
+ data = gz.read
157
+ gz.close
158
+ resp.response['content-encoding'] = nil
159
+ # FIXME: Not sure what Hotmail was feeding me with their 'deflate',
160
+ # but the headers definitely were not right
161
+ when 'deflate':
162
+ data = Zlib::Inflate.inflate(data)
163
+ resp.response['content-encoding'] = nil
164
+ end
165
+
166
+ data
167
+ end
140
168
  end
141
169
 
142
170
  class ContactsError < StandardError
@@ -2,8 +2,10 @@ class Contacts
2
2
  class Hotmail < Base
3
3
  URL = "http://www.hotmail.com/"
4
4
  CONTACT_LIST_URL = "http://%s/cgi-bin/addresses"
5
+ COMPOSE_URL = "http://%s/cgi-bin/compose?"
5
6
  PROTOCOL_ERROR = "Hotmail has changed its protocols, please upgrade this library first. If that does not work, contact lucas@rufy.com with this error"
6
7
  PWDPAD = "IfYouAreReadingThisYouHaveTooMuchFreeTime"
8
+ MAX_HTTP_THREADS = 8
7
9
 
8
10
  def real_connect
9
11
  data, resp, cookies, forward = get(URL)
@@ -60,15 +62,59 @@ class Contacts
60
62
  CONTACT_LIST_URL % @domain
61
63
  end
62
64
 
65
+ def follow_email(data, id, contacts_slot)
66
+ compose_url = COMPOSE_URL % @domain
67
+ postdata = "HrsTest=&to=#{id}&mailto=1&ref=addresses"
68
+ postdata += "&curmbox=00000000-0000-0000-0000-000000000001"
69
+
70
+ a = data.split(/>\s*<input\s+/i).grep(/\s+name="a"/i)
71
+ return nil if a.empty?
72
+
73
+ a = a[0].match(/\s+value="([a-f0-9]+)"/i) or return nil
74
+ postdata += "&a=#{a[1]}"
75
+
76
+ data, resp, @cookies, forward = post(compose_url, postdata, @cookies)
77
+ e = data.split(/>\s*<input\s+/i).grep(/\s+name="to"/i)
78
+ return nil if e.empty?
79
+
80
+ e = e[0].match(/\s+value="([^"]+)"/i) or return nil
81
+ @contacts[contacts_slot][1] = e[1] if e[1].match(/@/)
82
+ end
83
+
63
84
  def parse(data)
64
85
  chunks = data.split('id="hotmail"')
65
- chunks.delete_at(0)
86
+ prev = chunks.delete_at(0)
66
87
 
67
- @contacts = chunks.map do |chunk|
88
+ queue = Queue.new
89
+ threads = []
90
+ @contacts = []
91
+ chunks.each do |chunk|
68
92
  name = chunk.split('return false;">')[1].split('</a>')[0] rescue nil
69
93
  email = chunk.split('return false;">')[2].split('</a>')[0] rescue nil
70
- [name, email] if email && email != "more"
71
- end.compact
94
+ unless email && email != "more"
95
+ prev = chunk
96
+ next
97
+ end
98
+ next_slot = @contacts.size
99
+ @contacts[next_slot] = [name, email]
100
+ if email.match(/\.\.\.$/)
101
+ if m = prev.match(/\s+id="([A-Za-z0-9\-]+)"/)
102
+ queue.push([m[1], next_slot])
103
+ if threads.size < MAX_HTTP_THREADS
104
+ threads.push(Thread.new {
105
+ while x = queue.pop
106
+ follow_email(data, *x)
107
+ end
108
+ })
109
+ end
110
+ end
111
+ end
112
+ prev = chunk
113
+ end
114
+
115
+ threads.each { queue.push(nil) }
116
+ threads.each { |t| t.join }
117
+ @contacts
72
118
  end
73
119
 
74
120
  end
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: contacts
5
5
  version: !ruby/object:Gem::Version
6
- version: 1.0.1
7
- date: 2006-12-07 00:00:00 -08:00
6
+ version: 1.0.2
7
+ date: 2007-03-30 00:00:00 -07:00
8
8
  summary: Ridiculously easy contact list information from various providers including Yahoo, Gmail, and Hotmail
9
9
  require_paths:
10
10
  - lib