httpclient 2.5.3.3 → 2.6.0

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.
@@ -1,448 +1,220 @@
1
- # cookie.rb is redistributed file which is originally included in Webagent
2
- # version 0.6.2 by TAKAHASHI `Maki' Masayoshi. And it contains some bug fixes.
3
- # You can download the entire package of Webagent from
4
- # http://www.rubycolor.org/arc/.
1
+ # do not override if httpclient/webagent-cookie is loaded already
2
+ unless defined?(HTTPClient::CookieManager)
3
+ begin # for catching LoadError and load webagent-cookie instead
5
4
 
5
+ require 'http-cookie'
6
6
 
7
- # Cookie class
8
- #
9
- # I refered to w3m's source to make these classes. Some comments
10
- # are quoted from it. I'm thanksful for author(s) of it.
11
- #
12
- # w3m homepage: http://ei5nazha.yz.yamagata-u.ac.jp/~aito/w3m/eng/
13
-
14
- require 'time'
15
- require 'monitor'
16
- require 'httpclient/util'
17
-
18
- class WebAgent
19
-
20
- module CookieUtils
7
+ class HTTPClient
8
+ class CookieManager
9
+ attr_reader :format
10
+ attr_accessor :cookies_file
21
11
 
22
- def head_match?(str1, str2)
23
- str1 == str2[0, str1.length]
12
+ def initialize(cookies_file = nil, format = WebAgentSaver)
13
+ @cookies_file = cookies_file
14
+ @format = format
15
+ @jar = HTTP::CookieJar.new
16
+ load_cookies if @cookies_file
24
17
  end
25
18
 
26
- def tail_match?(str1, str2)
27
- if str1.length > 0
28
- str1 == str2[-str1.length..-1].to_s
29
- else
30
- true
31
- end
19
+ def load_cookies
20
+ check_cookies_file
21
+ @jar.clear
22
+ @jar.load(@cookies_file, :format => @format)
23
+ end
24
+
25
+ def save_cookies(session = false)
26
+ check_cookies_file
27
+ @jar.save(@cookies_file, :format => @format, :session => session)
28
+ end
29
+
30
+ def cookies(uri = nil)
31
+ cookies = @jar.cookies(uri)
32
+ # TODO: return HTTP::Cookie in the future
33
+ cookies.map { |cookie|
34
+ WebAgent::Cookie.new(
35
+ :name => cookie.name,
36
+ :value => cookie.value,
37
+ :domain => cookie.domain,
38
+ :path => cookie.path,
39
+ :origin => cookie.origin,
40
+ :for_domain => cookie.for_domain,
41
+ :expires => cookie.expires,
42
+ :httponly => cookie.httponly,
43
+ :secure => cookie.secure
44
+ )
45
+ }
32
46
  end
33
47
 
34
- def domain_match(host, domain)
35
- return false if domain.nil?
36
- domainname = domain.sub(/\.\z/, '').downcase
37
- hostname = host.sub(/\.\z/, '').downcase
38
- case domain
39
- when /\d+\.\d+\.\d+\.\d+/
40
- return (hostname == domainname)
41
- when '.'
42
- return true
43
- when /^\./
44
- # allows; host == rubyforge.org, domain == .rubyforge.org
45
- return tail_match?(domainname, '.' + hostname)
46
- else
47
- return (hostname == domainname)
48
+ def cookie_value(uri)
49
+ cookies = self.cookies(uri)
50
+ unless cookies.empty?
51
+ HTTP::Cookie.cookie_value(cookies)
48
52
  end
49
53
  end
50
- end
51
-
52
- class Cookie
53
- include CookieUtils
54
-
55
- attr_accessor :name, :value
56
- attr_accessor :domain, :path
57
- attr_accessor :expires ## for Netscape Cookie
58
- attr_accessor :url
59
- attr_writer :use, :secure, :http_only, :discard, :domain_orig, :path_orig, :override
60
-
61
- USE = 1
62
- SECURE = 2
63
- DOMAIN = 4
64
- PATH = 8
65
- DISCARD = 16
66
- OVERRIDE = 32
67
- OVERRIDE_OK = 32
68
- HTTP_ONLY = 64
69
-
70
- def initialize
71
- @name = @value = @domain = @path = nil
72
- @expires = nil
73
- @url = nil
74
- @use = @secure = @http_only = @discard = @domain_orig = @path_orig = @override = nil
75
- end
76
54
 
77
- def discard?
78
- @discard
55
+ def parse(value, uri)
56
+ @jar.parse(value, uri)
79
57
  end
80
58
 
81
- def use?
82
- @use
83
- end
84
-
85
- def secure?
86
- @secure
87
- end
88
-
89
- def http_only?
90
- @http_only
91
- end
92
-
93
- def domain_orig?
94
- @domain_orig
59
+ def cookies=(cookies)
60
+ @jar.clear
61
+ cookies.each do |cookie|
62
+ add(cookie)
63
+ end
95
64
  end
96
65
 
97
- def path_orig?
98
- @path_orig
66
+ def add(cookie)
67
+ @jar.add(cookie)
99
68
  end
100
69
 
101
- def override?
102
- @override
70
+ def find(uri)
71
+ warn('CookieManager#find is deprecated and will be removed in near future. Use HTTP::Cookie.cookie_value(CookieManager#cookies) instead')
72
+ if cookie = cookies(uri)
73
+ HTTP::Cookie.cookie_value(cookie)
74
+ end
103
75
  end
104
76
 
105
- def flag
106
- flg = 0
107
- flg += USE if @use
108
- flg += SECURE if @secure
109
- flg += HTTP_ONLY if @http_only
110
- flg += DOMAIN if @domain_orig
111
- flg += PATH if @path_orig
112
- flg += DISCARD if @discard
113
- flg += OVERRIDE if @override
114
- flg
115
- end
77
+ private
116
78
 
117
- def set_flag(flag)
118
- flag = flag.to_i
119
- @use = true if flag & USE > 0
120
- @secure = true if flag & SECURE > 0
121
- @http_only = true if flag & HTTP_ONLY > 0
122
- @domain_orig = true if flag & DOMAIN > 0
123
- @path_orig = true if flag & PATH > 0
124
- @discard = true if flag & DISCARD > 0
125
- @override = true if flag & OVERRIDE > 0
79
+ def check_cookies_file
80
+ unless @cookies_file
81
+ raise ArgumentError.new('Cookies file not specified')
82
+ end
126
83
  end
84
+ end
127
85
 
128
- def match?(url)
129
- domainname = url.host
130
- if (!domainname ||
131
- !domain_match(domainname, @domain) ||
132
- (@path && !head_match?(@path, url.path.empty? ? '/' : url.path)) ||
133
- (@secure && (url.scheme != 'https')) )
134
- return false
135
- else
136
- return true
137
- end
86
+ class WebAgentSaver < HTTP::CookieJar::AbstractSaver
87
+ # no option
88
+ def default_options
89
+ {}
138
90
  end
139
91
 
140
- def join_quotedstr(array, sep)
141
- ret = Array.new
142
- old_elem = nil
143
- array.each{|elem|
144
- if (elem.scan(/"/).length % 2) == 0
145
- if old_elem
146
- old_elem << sep << elem
147
- else
148
- ret << elem
149
- old_elem = nil
150
- end
151
- else
152
- if old_elem
153
- old_elem << sep << elem
154
- ret << old_elem
155
- old_elem = nil
156
- else
157
- old_elem = elem.dup
158
- end
159
- end
92
+ # same as HTTP::CookieJar::CookiestxtSaver
93
+ def save(io, jar)
94
+ jar.each { |cookie|
95
+ next if !@session && cookie.session?
96
+ io.print cookie_to_record(cookie)
160
97
  }
161
- ret
162
98
  end
163
99
 
164
- def parse(str, url)
165
- @url = url
166
- # TODO: should not depend on join_quotedstr. scan with escape like CSV.
167
- cookie_elem = str.split(/;/)
168
- cookie_elem = join_quotedstr(cookie_elem, ';')
169
- cookie_elem -= [""] # del empty elements, a cookie might included ";;"
170
- first_elem = cookie_elem.shift
171
- if first_elem !~ /([^=]*)(\=(.*))?/
172
- return
173
- ## raise ArgumentError 'invalid cookie value'
174
- end
175
- @name = $1.strip
176
- @value = normalize_cookie_value($3)
177
- cookie_elem.each{|pair|
178
- key, value = pair.split(/=/, 2) ## value may nil
179
- key.strip!
180
- value = normalize_cookie_value(value)
181
- case key.downcase
182
- when 'domain'
183
- @domain = value
184
- when 'expires'
185
- @expires = nil
186
- begin
187
- @expires = Time.parse(value).gmtime if value
188
- rescue ArgumentError
189
- end
190
- when 'path'
191
- @path = value
192
- when 'secure'
193
- @secure = true ## value may nil, but must 'true'.
194
- when 'httponly'
195
- @http_only = true ## value may nil, but must 'true'.
196
- else
197
- warn("Unknown key: #{key} = #{value}")
198
- end
100
+ # same as HTTP::CookieJar::CookiestxtSaver
101
+ def load(io, jar)
102
+ io.each_line { |line|
103
+ cookie = parse_record(line) and jar.add(cookie)
199
104
  }
200
105
  end
201
106
 
202
- private
107
+ private
203
108
 
204
- def normalize_cookie_value(value)
205
- if value
206
- value = value.strip.sub(/\A"(.*)"\z/) { $1 }
207
- value = nil if value.empty?
208
- end
209
- value
109
+ def cookie_to_record(cookie)
110
+ [
111
+ cookie.origin,
112
+ cookie.name,
113
+ cookie.value,
114
+ cookie.expires.to_i,
115
+ cookie.dot_domain,
116
+ cookie.path,
117
+ self.class.flag(cookie)
118
+ ].join("\t") + "\n"
210
119
  end
211
- end
212
-
213
- ##
214
- # An Array class that already includes the MonitorMixin module.
215
- #
216
- class SynchronizedArray < Array
217
- include MonitorMixin
218
- end
219
-
220
- class CookieManager
221
- include CookieUtils
222
120
 
223
- ### errors
224
- class Error < StandardError; end
225
- class ErrorOverrideOK < Error; end
226
- class SpecialError < Error; end
227
-
228
- attr_reader :cookies
229
- attr_accessor :cookies_file
230
- attr_accessor :accept_domains, :reject_domains
231
-
232
- def initialize(file=nil)
233
- @cookies = SynchronizedArray.new
234
- @cookies_file = file
235
- @is_saved = true
236
- @reject_domains = Array.new
237
- @accept_domains = Array.new
238
- @netscape_rule = false
239
- end
121
+ def parse_record(line)
122
+ return nil if /\A#/ =~ line
123
+ col = line.chomp.split(/\t/)
240
124
 
241
- def cookies=(cookies)
242
- if cookies.is_a?(SynchronizedArray)
243
- @cookies = cookies
125
+ origin = col[0]
126
+ name = col[1]
127
+ value = col[2]
128
+ value.chomp!
129
+ if col[3].empty? or col[3] == '0'
130
+ expires = nil
244
131
  else
245
- @cookies = SynchronizedArray.new(cookies)
132
+ expires = Time.at(col[3].to_i)
133
+ return nil if expires < Time.now
246
134
  end
247
- end
135
+ domain = col[4]
136
+ path = col[5]
248
137
 
249
- def save_all_cookies(force = nil, save_unused = true, save_discarded = true)
250
- @cookies.synchronize do
251
- check_expired_cookies
252
- if @is_saved and !force
253
- return
254
- end
255
- File.open(@cookies_file, 'w') do |f|
256
- @cookies.each do |cookie|
257
- if (cookie.use? or save_unused) and
258
- (!cookie.discard? or save_discarded)
259
- f.print(cookie.url.to_s,"\t",
260
- cookie.name,"\t",
261
- cookie.value,"\t",
262
- cookie.expires.to_i,"\t",
263
- cookie.domain,"\t",
264
- cookie.path,"\t",
265
- cookie.flag,"\n")
266
- end
267
- end
268
- end
269
- end
270
- @is_saved = true
138
+ cookie = WebAgent::Cookie.new(name, value,
139
+ :origin => origin,
140
+ :domain => domain,
141
+ :path => path,
142
+ :expires => expires
143
+ )
144
+ self.class.set_flag(cookie, col[6].to_i)
145
+ cookie
271
146
  end
272
147
 
273
- def save_cookies(force = nil)
274
- save_all_cookies(force, false, false)
275
- end
148
+ USE = 1
149
+ SECURE = 2
150
+ DOMAIN = 4
151
+ PATH = 8
152
+ HTTP_ONLY = 64
276
153
 
277
- def check_expired_cookies
278
- @cookies.reject!{|cookie|
279
- is_expired = (cookie.expires && (cookie.expires < Time.now.gmtime))
280
- if is_expired && !cookie.discard?
281
- @is_saved = false
282
- end
283
- is_expired
284
- }
154
+ def self.flag(cookie)
155
+ flg = 0
156
+ flg += USE # not used
157
+ flg += SECURE if cookie.secure?
158
+ flg += DOMAIN if cookie.for_domain?
159
+ flg += HTTP_ONLY if cookie.httponly?
160
+ flg += PATH if cookie.path # not used
161
+ flg
285
162
  end
286
163
 
287
- def parse(str, url)
288
- cookie = WebAgent::Cookie.new
289
- cookie.parse(str, url)
290
- add(cookie)
164
+ def self.set_flag(cookie, flag)
165
+ cookie.secure = true if flag & SECURE > 0
166
+ cookie.for_domain = true if flag & DOMAIN > 0
167
+ cookie.httponly = true if flag & HTTP_ONLY > 0
291
168
  end
169
+ end
170
+ end
292
171
 
293
- def find(url)
294
- return nil if @cookies.empty?
295
-
296
- cookie_list = Array.new
297
- @cookies.each{|cookie|
298
- is_expired = (cookie.expires && (cookie.expires < Time.now.gmtime))
299
- if cookie.use? && !is_expired && cookie.match?(url)
300
- if cookie_list.select{|c1| c1.name == cookie.name}.empty?
301
- cookie_list << cookie
302
- end
303
- end
304
- }
305
- return make_cookie_str(cookie_list)
306
- end
172
+ # for backward compatibility
173
+ class WebAgent
174
+ CookieManager = ::HTTPClient::CookieManager
307
175
 
308
- def add(given)
309
- check_domain(given.domain, given.url.host, given.override?)
310
-
311
- domain = given.domain || given.url.host
312
- path = given.path || given.url.path.sub(%r|/[^/]*\z|, '')
313
-
314
- cookie = nil
315
- @cookies.synchronize do
316
- check_expired_cookies
317
- cookie = @cookies.find { |c|
318
- c.domain == domain && c.path == path && c.name == given.name
319
- }
320
- if !cookie
321
- cookie = WebAgent::Cookie.new
322
- cookie.use = true
323
- @cookies << cookie
324
- end
325
- end
176
+ class Cookie < HTTP::Cookie
177
+ @@warned = false
326
178
 
327
- cookie.domain = domain
328
- cookie.path = path
329
- cookie.url = given.url
330
- cookie.name = given.name
331
- cookie.value = given.value
332
- cookie.expires = given.expires
333
- cookie.secure = given.secure?
334
- cookie.http_only = given.http_only?
335
- cookie.domain_orig = given.domain
336
- cookie.path_orig = given.path
337
-
338
- if cookie.discard? || cookie.expires == nil
339
- cookie.discard = true
340
- else
341
- cookie.discard = false
342
- @is_saved = false
343
- end
179
+ def url
180
+ deprecated('url', 'origin')
181
+ self.origin
344
182
  end
345
183
 
346
- def load_cookies
347
- return if !File.readable?(@cookies_file)
348
- @cookies.synchronize do
349
- @cookies.clear
350
- File.open(@cookies_file,'r'){|f|
351
- while line = f.gets
352
- cookie = WebAgent::Cookie.new
353
- @cookies << cookie
354
- col = line.chomp.split(/\t/)
355
- cookie.url = HTTPClient::Util.urify(col[0])
356
- cookie.name = col[1]
357
- cookie.value = col[2]
358
- if col[3].empty? or col[3] == '0'
359
- cookie.expires = nil
360
- else
361
- cookie.expires = Time.at(col[3].to_i).gmtime
362
- end
363
- cookie.domain = col[4]
364
- cookie.path = col[5]
365
- cookie.set_flag(col[6])
366
- end
367
- }
368
- end
184
+ def url=(url)
185
+ deprecated('url=', 'origin=')
186
+ self.origin = url
369
187
  end
370
188
 
371
- # Who use it?
372
- def check_cookie_accept_domain(domain)
373
- unless domain
374
- return false
375
- end
376
- @accept_domains.each{|dom|
377
- if domain_match(domain, dom)
378
- return true
379
- end
380
- }
381
- @reject_domains.each{|dom|
382
- if domain_match(domain, dom)
383
- return false
384
- end
385
- }
386
- return true
189
+ def http_only?
190
+ deprecated('http_only?', 'httponly?')
191
+ self.httponly?
387
192
  end
388
193
 
389
- private
390
-
391
- def make_cookie_str(cookie_list)
392
- if cookie_list.empty?
393
- return nil
394
- end
194
+ alias original_domain domain
395
195
 
396
- ret = ''
397
- c = cookie_list.shift
398
- ret += "#{c.name}=#{c.value}"
399
- cookie_list.each{|cookie|
400
- ret += "; #{cookie.name}=#{cookie.value}"
401
- }
402
- return ret
196
+ def domain
197
+ warn('Cookie#domain returns dot-less domain name now. Use Cookie#dot_domain if you need "." at the beginning.')
198
+ self.original_domain
403
199
  end
404
200
 
405
- # for conformance to http://wp.netscape.com/newsref/std/cookie_spec.html
406
- attr_accessor :netscape_rule
407
- SPECIAL_DOMAIN = [".com",".edu",".gov",".mil",".net",".org",".int"]
408
-
409
- def check_domain(domain, hostname, override)
410
- return unless domain
411
-
412
- # [DRAFT 12] s. 4.2.2 (does not apply in the case that
413
- # host name is the same as domain attribute for version 0
414
- # cookie)
415
- # I think that this rule has almost the same effect as the
416
- # tail match of [NETSCAPE].
417
- if domain !~ /^\./ && hostname != domain
418
- domain = '.'+domain
419
- end
420
- # [NETSCAPE] rule
421
- if @netscape_rule
422
- n = domain.scan(/\./).length
423
- if n < 2
424
- cookie_error(SpecialError.new, override)
425
- elsif n == 2
426
- ## [NETSCAPE] rule
427
- ok = SPECIAL_DOMAIN.select{|sdomain|
428
- sdomain == domain[-(sdomain.length)..-1]
429
- }
430
- if ok.empty?
431
- cookie_error(SpecialError.new, override)
432
- end
433
- end
434
- end
435
- # this implementation does not check RFC2109 4.3.2 case 2;
436
- # the portion of host not in domain does not contain a dot.
437
- # according to nsCookieService.cpp in Firefox 3.0.4, Firefox 3.0.4
438
- # and IE does not check, too.
201
+ def flag
202
+ deprecated('flag', 'secure, for_domain, etc.')
203
+ HTTPClient::WebAgentSaver.flag(self)
439
204
  end
440
205
 
441
- # not tested well; used only netscape_rule = true.
442
- def cookie_error(err, override)
443
- if !err.kind_of?(ErrorOverrideOK) || !override
444
- raise err
206
+ private
207
+
208
+ def deprecated(old, new)
209
+ unless @@warned
210
+ warn("WebAgent::Cookie is deprecated and will be replaced with HTTP::Cookie in the near future. Please use Cookie##{new} instead of Cookie##{old} for the replacement.")
211
+ @@warned = true
445
212
  end
446
213
  end
447
214
  end
448
215
  end
216
+
217
+ rescue LoadError
218
+ require 'httpclient/webagent-cookie'
219
+ end
220
+ end