--- 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
 
