--- lib/webagent/cookie.rb 2002-09-30 01:00:39.000000000 +0900 +++ ../lib/httpclient/cookie.rb 2008-12-19 15:55:42.000000000 +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,7 +57,4 @@ class WebAgent include CookieUtils - require 'parsedate' - include ParseDate - attr_accessor :name, :value attr_accessor :domain, :path @@ -64,4 +72,6 @@ class WebAgent def initialize() + @discard = @use = @secure = @domain_orig = @path_orig = @override = nil + @path = nil end @@ -168,8 +178,7 @@ class WebAgent key, value = pair.split(/=/) ## value may nil key.strip! - value.strip! - if value && value =~ /^"(.*)"$/ - value = $1 - end + if value + value = value.strip.sub(/\A"(.*)"\z/) { $1 } + end case key.downcase when 'domain' @@ -177,5 +186,5 @@ class WebAgent when 'expires' begin - @expires = Time.gm(*parsedate(value)[0,6]) + @expires = Time.parse(value) rescue ArgumentError @expires = nil @@ -200,46 +209,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 +294,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 +315,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 @@ -311,8 +337,4 @@ class WebAgent use_security = override - if !domainname - cookie_error(NodotError.new(), override) - end - if domain @@ -326,27 +348,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|/[^/]*|, '') 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 @@ -368,25 +399,24 @@ 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 + 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 + } + end end