Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

405 lines (381 sloc) 11.626 kb
--- lib/webagent/cookie.rb 2002-09-30 01:00:39.000000000 +0900
+++ ../lib/httpclient/cookie.rb 2011-03-22 14:40:02.158455015 +0900
@@ -1,2 +1,8 @@
+# cookie.rb is redistributed file which is originally included in Webagent
+# version 0.6.2 by TAKAHASHI `Maki' Masayoshi. And it contains some bug fixes.
+# You can download the entire package of Webagent from
+# http://www.rubycolor.org/arc/.
+
+
# Cookie class
#
@@ -7,4 +13,6 @@
require 'uri'
+require 'time'
+require 'monitor'
class WebAgent
@@ -25,13 +33,16 @@ class WebAgent
def domain_match(host, domain)
+ domainname = domain.sub(/\.\z/, '').downcase
+ hostname = host.sub(/\.\z/, '').downcase
case domain
when /\d+\.\d+\.\d+\.\d+/
- return (host == domain)
+ return (hostname == domainname)
when '.'
return true
when /^\./
- return tail_match?(domain, host)
+ # allows; host == rubyforge.org, domain == .rubyforge.org
+ return tail_match?(domainname, '.' + hostname)
else
- return (host == domain)
+ return (hostname == domainname)
end
end
@@ -46,12 +57,9 @@ class WebAgent
include CookieUtils
- require 'parsedate'
- include ParseDate
-
attr_accessor :name, :value
attr_accessor :domain, :path
attr_accessor :expires ## for Netscape Cookie
attr_accessor :url
- attr_writer :use, :secure, :discard, :domain_orig, :path_orig, :override
+ attr_writer :use, :secure, :http_only, :discard, :domain_orig, :path_orig, :override
USE = 1
@@ -62,6 +70,11 @@ class WebAgent
OVERRIDE = 32
OVERRIDE_OK = 32
+ HTTP_ONLY = 64
def initialize()
+ @name = @value = @domain = @path = nil
+ @expires = nil
+ @url = nil
+ @use = @secure = @http_only = @discard = @domain_orig = @path_orig = @override = nil
end
@@ -78,4 +91,8 @@ class WebAgent
end
+ def http_only?
+ @http_only
+ end
+
def domain_orig?
@domain_orig
@@ -94,4 +111,5 @@ class WebAgent
flg += USE if @use
flg += SECURE if @secure
+ flg += HTTP_ONLY if @http_only
flg += DOMAIN if @domain_orig
flg += PATH if @path_orig
@@ -105,4 +123,5 @@ class WebAgent
@use = true if flag & USE > 0
@secure = true if flag & SECURE > 0
+ @http_only = true if flag & HTTP_ONLY > 0
@domain_orig = true if flag & DOMAIN > 0
@path_orig = true if flag & PATH > 0
@@ -149,6 +168,8 @@ class WebAgent
def parse(str, url)
@url = url
+ # TODO: should not depend on join_quotedstr. scan with escape like CSV.
cookie_elem = str.split(/;/)
cookie_elem = join_quotedstr(cookie_elem, ';')
+ cookie_elem -= [""] # del empty elements, a cookie might included ";;"
first_elem = cookie_elem.shift
if first_elem !~ /([^=]*)(\=(.*))?/
@@ -157,27 +178,17 @@ class WebAgent
end
@name = $1.strip
- @value = $3
- if @value
- if @value =~ /^\s*"(.*)"\s*$/
- @value = $1
- else
- @value.dup.strip!
- end
- end
+ @value = normalize_cookie_value($3)
cookie_elem.each{|pair|
- key, value = pair.split(/=/) ## value may nil
+ key, value = pair.split(/=/, 2) ## value may nil
key.strip!
- value.strip!
- if value && value =~ /^"(.*)"$/
- value = $1
- end
+ value = normalize_cookie_value(value)
case key.downcase
when 'domain'
@domain = value
when 'expires'
+ @expires = nil
begin
- @expires = Time.gm(*parsedate(value)[0,6])
+ @expires = Time.parse(value).gmtime() if value
rescue ArgumentError
- @expires = nil
end
when 'path'
@@ -185,4 +196,6 @@ class WebAgent
when 'secure'
@secure = true ## value may nil, but must 'true'.
+ when 'httponly'
+ @http_only = true ## value may nil, but must 'true'.
else
## ignore
@@ -191,4 +204,12 @@ class WebAgent
end
+ def normalize_cookie_value(value)
+ if value
+ value = value.strip.sub(/\A"(.*)"\z/) { $1 }
+ value = nil if value.empty?
+ end
+ value
+ end
+ private :normalize_cookie_value
end
@@ -200,46 +221,63 @@ class WebAgent
class ErrorOverrideOK < Error; end
class SpecialError < Error; end
- class NoDotError < ErrorOverrideOK; end
-
- SPECIAL_DOMAIN = [".com",".edu",".gov",".mil",".net",".org",".int"]
- attr_accessor :cookies
+ attr_reader :cookies
attr_accessor :cookies_file
attr_accessor :accept_domains, :reject_domains
+ # for conformance to http://wp.netscape.com/newsref/std/cookie_spec.html
+ attr_accessor :netscape_rule
+ SPECIAL_DOMAIN = [".com",".edu",".gov",".mil",".net",".org",".int"]
+
def initialize(file=nil)
@cookies = Array.new()
+ @cookies.extend(MonitorMixin)
@cookies_file = file
@is_saved = true
@reject_domains = Array.new()
@accept_domains = Array.new()
+ @netscape_rule = false
end
- def save_cookies(force = nil)
- if @is_saved && !force
- return
+ def cookies=(cookies)
+ @cookies = cookies
+ @cookies.extend(MonitorMixin)
+ end
+
+ def save_all_cookies(force = nil, save_unused = true, save_discarded = true)
+ @cookies.synchronize do
+ check_expired_cookies()
+ if @is_saved and !force
+ return
+ end
+ File.open(@cookies_file, 'w') do |f|
+ @cookies.each do |cookie|
+ if (cookie.use? or save_unused) and
+ (!cookie.discard? or save_discarded)
+ f.print(cookie.url.to_s,"\t",
+ cookie.name,"\t",
+ cookie.value,"\t",
+ cookie.expires.to_i,"\t",
+ cookie.domain,"\t",
+ cookie.path,"\t",
+ cookie.flag,"\n")
+ end
+ end
+ end
end
- File.open(@cookies_file,'w'){|f|
- @cookies.each{|cookie|
- if cookie.use? && (!cookie.discard?)
- f.print(cookie.url.to_s,"\t",
- cookie.name,"\t",
- cookie.value,"\t",
- cookie.expires.to_i,"\t",
- cookie.domain,"\t",
- cookie.path,"\t",
- cookie.flag,"\n")
- end
- }
- }
+ @is_saved = true
+ end
+
+ def save_cookies(force = nil)
+ save_all_cookies(force, false, false)
end
def check_expired_cookies()
@cookies.reject!{|cookie|
- is_expired = (cookie.expires && (cookie.expires < Time.now.gmtime))
- if is_expired && !cookie.discard?
- @is_saved = false
- end
- is_expired
+ is_expired = (cookie.expires && (cookie.expires < Time.now.gmtime))
+ if is_expired && !cookie.discard?
+ @is_saved = false
+ end
+ is_expired
}
end
@@ -268,15 +306,14 @@ class WebAgent
def find(url)
-
- check_expired_cookies()
+ return nil if @cookies.empty?
cookie_list = Array.new()
-
@cookies.each{|cookie|
- if cookie.use? && cookie.match?(url)
- if cookie_list.select{|c1| c1.name == cookie.name}.empty?
- cookie_list << cookie
- end
- end
+ is_expired = (cookie.expires && (cookie.expires < Time.now.gmtime))
+ if cookie.use? && !is_expired && cookie.match?(url)
+ if cookie_list.select{|c1| c1.name == cookie.name}.empty?
+ cookie_list << cookie
+ end
+ end
}
return make_cookie_str(cookie_list)
@@ -290,6 +327,7 @@ class WebAgent
private :find_cookie_info
+ # not tested well; used only netscape_rule = true.
def cookie_error(err, override)
- if err.kind_of?(ErrorOverrideOK) || !override
+ if !err.kind_of?(ErrorOverrideOK) || !override
raise err
end
@@ -302,6 +340,6 @@ class WebAgent
expires, domain, path =
cookie.expires, cookie.domain, cookie.path
- secure, domain_orig, path_orig =
- cookie.secure?, cookie.domain_orig?, cookie.path_orig?
+ secure, http_only, domain_orig, path_orig =
+ cookie.secure?, cookie.http_only?, cookie.domain_orig?, cookie.path_orig?
discard, override =
cookie.discard?, cookie.override?
@@ -309,9 +347,4 @@ class WebAgent
domainname = url.host
domain_orig, path_orig = domain, path
- use_security = override
-
- if !domainname
- cookie_error(NodotError.new(), override)
- end
if domain
@@ -326,27 +359,36 @@ class WebAgent
end
- ## [NETSCAPE] rule
- n = total_dot_num(domain)
- if n < 2
- cookie_error(SpecialError.new(), override)
- elsif n == 2
- ok = SPECIAL_DOMAIN.select{|sdomain|
- sdomain == domain[-(sdomain.length)..-1]
- }
- if ok.empty?
- cookie_error(SpecialError.new(), override)
- end
- end
-
+ # [NETSCAPE] rule
+ if @netscape_rule
+ n = total_dot_num(domain)
+ if n < 2
+ cookie_error(SpecialError.new(), override)
+ elsif n == 2
+ ## [NETSCAPE] rule
+ ok = SPECIAL_DOMAIN.select{|sdomain|
+ sdomain == domain[-(sdomain.length)..-1]
+ }
+ if ok.empty?
+ cookie_error(SpecialError.new(), override)
+ end
+ end
+ end
+
+ # this implementation does not check RFC2109 4.3.2 case 2;
+ # the portion of host not in domain does not contain a dot.
+ # according to nsCookieService.cpp in Firefox 3.0.4, Firefox 3.0.4
+ # and IE does not check, too.
end
- path ||= url.path.sub!(%r|/[^/]*|, '')
+ path ||= url.path.sub(%r|/[^/]*\z|, '')
domain ||= domainname
- cookie = find_cookie_info(domain, path, name)
-
- if !cookie
- cookie = WebAgent::Cookie.new()
- cookie.use = true
- @cookies << cookie
+ @cookies.synchronize do
+ cookie = find_cookie_info(domain, path, name)
+ if !cookie
+ cookie = WebAgent::Cookie.new()
+ cookie.use = true
+ @cookies << cookie
+ end
+ check_expired_cookies()
end
@@ -360,4 +402,5 @@ class WebAgent
## for flag
cookie.secure = secure
+ cookie.http_only = http_only
cookie.domain_orig = domain_orig
cookie.path_orig = path_orig
@@ -368,25 +411,29 @@ class WebAgent
@is_saved = false
end
-
- check_expired_cookies()
- return false
end
def load_cookies()
return if !File.readable?(@cookies_file)
- File.open(@cookies_file,'r'){|f|
- while line = f.gets
- cookie = WebAgent::Cookie.new()
- @cookies << cookie
- col = line.chomp.split(/\t/)
- cookie.url = URI.parse(col[0])
- cookie.name = col[1]
- cookie.value = col[2]
- cookie.expires = Time.at(col[3].to_i)
- cookie.domain = col[4]
- cookie.path = col[5]
- cookie.set_flag(col[6])
- end
- }
+ @cookies.synchronize do
+ @cookies.clear
+ File.open(@cookies_file,'r'){|f|
+ while line = f.gets
+ cookie = WebAgent::Cookie.new()
+ @cookies << cookie
+ col = line.chomp.split(/\t/)
+ cookie.url = URI.parse(col[0])
+ cookie.name = col[1]
+ cookie.value = col[2]
+ if col[3].empty? or col[3] == '0'
+ cookie.expires = nil
+ else
+ cookie.expires = Time.at(col[3].to_i).gmtime
+ end
+ cookie.domain = col[4]
+ cookie.path = col[5]
+ cookie.set_flag(col[6])
+ end
+ }
+ end
end
@@ -511,4 +558,6 @@ Load, save, parse and send cookies.
-- Cookie#secure?
-- Cookie#secure=(secure)
+ -- Cookie#http_only?
+ -- Cookie#http_only=(http_only)
-- Cookie#domain_orig?
-- Cookie#domain_orig=(domain_orig)
Jump to Line
Something went wrong with that request. Please try again.