http-cookie 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ module HTTP
2
+ class Cookie
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,233 @@
1
+ require 'http/cookie'
2
+
3
+ ##
4
+ # This class is used to manage the Cookies that have been returned from
5
+ # any particular website.
6
+
7
+ class HTTP::CookieJar
8
+ autoload :AbstractSaver, 'http/cookie_jar/abstract_saver'
9
+ autoload :AbstractStore, 'http/cookie_jar/abstract_store'
10
+
11
+ attr_reader :store
12
+
13
+ def initialize(store = :hash, options = nil)
14
+ case store
15
+ when Symbol
16
+ @store = AbstractStore.implementation(store).new(options)
17
+ when AbstractStore
18
+ options.empty? or
19
+ raise ArgumentError, 'wrong number of arguments (%d for 1)' % (1 + options.size)
20
+ @store = store
21
+ else
22
+ raise TypeError, 'wrong object given as cookie store: %s' % store.inspect
23
+ end
24
+ end
25
+
26
+ def initialize_copy(other)
27
+ @store = other.instance_eval { @store.dup }
28
+ end
29
+
30
+ # Add a +cookie+ to the jar and return self.
31
+ def add(cookie, *_)
32
+ _.empty? or
33
+ raise ArgumentError, 'HTTP::Cookie equivalent for Mechanize::CookieJar#add(uri, cookie) is #add(cookie) after setting cookie.origin = uri.'
34
+
35
+ if cookie.domain.nil? || cookie.path.nil?
36
+ raise ArgumentError, "a cookie with unknown domain or path cannot be added"
37
+ end
38
+
39
+ @store.add(cookie)
40
+ self
41
+ end
42
+ alias << add
43
+
44
+ # Used to exist in Mechanize::CookieJar. Use #add().
45
+ def add!(cookie)
46
+ raise NoMethodError, 'HTTP::Cookie equivalent for Mechanize::CookieJar#add!() is #add().'
47
+ end
48
+
49
+ # Fetch the cookies that should be used for the URL/URI.
50
+ def cookies(url)
51
+ now = Time.now
52
+ each(url).select { |cookie|
53
+ !cookie.expired? && (cookie.accessed_at = now)
54
+ }.sort
55
+ end
56
+
57
+ # Tests if the jar is empty. If url is given, tests if there is no
58
+ # cookie for the URL.
59
+ def empty?(url = nil)
60
+ if url
61
+ each(url) { return false }
62
+ return true
63
+ else
64
+ @store.empty?
65
+ end
66
+ end
67
+
68
+ # Iterates over all cookies that are not expired.
69
+ #
70
+ # Available option keywords are below:
71
+ #
72
+ # * +uri+
73
+ #
74
+ # Specify a URI/URL indicating the destination of the cookies
75
+ # being selected. Every cookie yielded should be good to send to
76
+ # the given URI, i.e. cookie.valid_for_uri?(uri) evaluates to
77
+ # true.
78
+ #
79
+ # If (and only if) this option is given, last access time of each
80
+ # cookie is updated to the current time.
81
+ def each(uri = nil, &block)
82
+ block_given? or return enum_for(__method__, uri)
83
+
84
+ if uri
85
+ block = proc { |cookie|
86
+ yield cookie if cookie.valid_for_uri?(uri)
87
+ }
88
+ end
89
+
90
+ @store.each(uri, &block)
91
+ self
92
+ end
93
+ include Enumerable
94
+
95
+ # call-seq:
96
+ # jar.save(filename_or_io, **options)
97
+ # jar.save(filename_or_io, format = :yaml, **options)
98
+ #
99
+ # Save the cookie jar into a file or an IO in the format specified
100
+ # and return self. If the given object responds to #write it is
101
+ # taken as an IO, or taken as a filename otherwise.
102
+ #
103
+ # Available option keywords are below:
104
+ #
105
+ # * +format+
106
+ # [<tt>:yaml</tt>]
107
+ # YAML structure (default)
108
+ # [<tt>:cookiestxt</tt>]
109
+ # Mozilla's cookies.txt format
110
+ # * +session+
111
+ # [+true+]
112
+ # Save session cookies as well.
113
+ # [+false+]
114
+ # Do not save session cookies. (default)
115
+ #
116
+ # All options given are passed through to the underlying cookie
117
+ # saver module.
118
+ def save(writable, *options)
119
+ opthash = {
120
+ :format => :yaml,
121
+ :session => false,
122
+ }
123
+ case options.size
124
+ when 0
125
+ when 1
126
+ case options = options.first
127
+ when Symbol
128
+ opthash[:format] = options
129
+ else
130
+ opthash.update(options) if options
131
+ end
132
+ when 2
133
+ opthash[:format], options = options
134
+ opthash.update(options) if options
135
+ else
136
+ raise ArgumentError, 'wrong number of arguments (%d for 1-3)' % (1 + options.size)
137
+ end
138
+
139
+ begin
140
+ saver = AbstractSaver.implementation(opthash[:format]).new(opthash)
141
+ rescue KeyError => e
142
+ raise ArgumentError, e.message
143
+ end
144
+
145
+ if writable.respond_to?(:write)
146
+ saver.save(writable, self)
147
+ else
148
+ File.open(writable, 'w') { |io|
149
+ saver.save(io, self)
150
+ }
151
+ end
152
+
153
+ self
154
+ end
155
+
156
+ # Used to exist in Mechanize::CookieJar. Use #save().
157
+ def save_as(*args)
158
+ raise NoMethodError, 'HTTP::Cookie equivalent for Mechanize::CookieJar#save_as() is #save().'
159
+ end
160
+
161
+ # call-seq:
162
+ # jar.load(filename_or_io, **options)
163
+ # jar.load(filename_or_io, format = :yaml, **options)
164
+ #
165
+ # Load cookies recorded in a file or an IO in the format specified
166
+ # into the jar and return self. If the given object responds to
167
+ # #read it is taken as an IO, or taken as a filename otherwise.
168
+ #
169
+ # Available option keywords are below:
170
+ #
171
+ # * +format+
172
+ # [<tt>:yaml</tt>]
173
+ # YAML structure (default)
174
+ # [<tt>:cookiestxt</tt>]
175
+ # Mozilla's cookies.txt format
176
+ #
177
+ # All options given are passed through to the underlying cookie
178
+ # saver module.
179
+ def load(readable, *options)
180
+ opthash = {
181
+ :format => :yaml,
182
+ :session => false,
183
+ }
184
+ case options.size
185
+ when 0
186
+ when 1
187
+ case options = options.first
188
+ when Symbol
189
+ opthash[:format] = options
190
+ else
191
+ opthash.update(options) if options
192
+ end
193
+ when 2
194
+ opthash[:format], options = options
195
+ opthash.update(options) if options
196
+ else
197
+ raise ArgumentError, 'wrong number of arguments (%d for 1-3)' % (1 + options.size)
198
+ end
199
+
200
+ begin
201
+ saver = AbstractSaver.implementation(opthash[:format]).new(opthash)
202
+ rescue KeyError => e
203
+ raise ArgumentError, e.message
204
+ end
205
+
206
+ if readable.respond_to?(:write)
207
+ saver.load(readable, self)
208
+ else
209
+ File.open(readable, 'r') { |io|
210
+ saver.load(io, self)
211
+ }
212
+ end
213
+
214
+ self
215
+ end
216
+
217
+ # Clear the cookie jar and return self.
218
+ def clear
219
+ @store.clear
220
+ self
221
+ end
222
+
223
+ # Used to exist in Mechanize::CookieJar. Use #clear().
224
+ def clear!(*args)
225
+ raise NoMethodError, 'HTTP::Cookie equivalent for Mechanize::CookieJar#clear!() is #clear().'
226
+ end
227
+
228
+ # Remove expired cookies and return self.
229
+ def cleanup(session = false)
230
+ @store.cleanup session
231
+ self
232
+ end
233
+ end
@@ -0,0 +1,53 @@
1
+ require 'http/cookie_jar'
2
+
3
+ class HTTP::CookieJar::AbstractSaver
4
+ class << self
5
+ @@class_map = {}
6
+
7
+ # Gets an implementation class by the name, optionally trying to
8
+ # load "http/cookie_jar/*_saver" if not found. If loading fails,
9
+ # KeyError is raised.
10
+ def implementation(symbol)
11
+ @@class_map.fetch(symbol)
12
+ rescue KeyError
13
+ begin
14
+ require 'http/cookie_jar/%s_saver' % symbol
15
+ @@class_map.fetch(symbol)
16
+ rescue LoadError, KeyError => e
17
+ raise KeyError, 'cookie saver unavailable: %s' % symbol.inspect
18
+ end
19
+ end
20
+
21
+ def inherited(subclass)
22
+ @@class_map[class_to_symbol(subclass)] = subclass
23
+ end
24
+
25
+ def class_to_symbol(klass)
26
+ klass.name[/[^:]+?(?=Saver$|$)/].downcase.to_sym
27
+ end
28
+ end
29
+
30
+ def default_options
31
+ {}
32
+ end
33
+ private :default_options
34
+
35
+ def initialize(options = nil)
36
+ options ||= {}
37
+ @logger = options[:logger]
38
+ @session = options[:session]
39
+ # Initializes each instance variable of the same name as option
40
+ # keyword.
41
+ default_options.each_pair { |key, default|
42
+ instance_variable_set("@#{key}", options.key?(key) ? options[key] : default)
43
+ }
44
+ end
45
+
46
+ def save(io, jar)
47
+ raise
48
+ end
49
+
50
+ def load(io, jar)
51
+ raise
52
+ end
53
+ end
@@ -0,0 +1,90 @@
1
+ require 'http/cookie_jar'
2
+
3
+ class HTTP::CookieJar::AbstractStore
4
+ class << self
5
+ @@class_map = {}
6
+
7
+ # Gets an implementation class by the name, optionally trying to
8
+ # load "http/cookie_jar/*_store" if not found. If loading fails,
9
+ # KeyError is raised.
10
+ def implementation(symbol)
11
+ @@class_map.fetch(symbol)
12
+ rescue KeyError
13
+ begin
14
+ require 'http/cookie_jar/%s_store' % symbol
15
+ @@class_map.fetch(symbol)
16
+ rescue LoadError, KeyError => e
17
+ raise KeyError, 'cookie store unavailable: %s' % symbol.inspect
18
+ end
19
+ end
20
+
21
+ def inherited(subclass)
22
+ @@class_map[class_to_symbol(subclass)] = subclass
23
+ end
24
+
25
+ def class_to_symbol(klass)
26
+ klass.name[/[^:]+?(?=Store$|$)/].downcase.to_sym
27
+ end
28
+ end
29
+
30
+ def default_options
31
+ {}
32
+ end
33
+ private :default_options
34
+
35
+ def initialize(options = nil)
36
+ options ||= {}
37
+ @logger = options[:logger]
38
+ # Initializes each instance variable of the same name as option
39
+ # keyword.
40
+ default_options.each_pair { |key, default|
41
+ instance_variable_set("@#{key}", options.key?(key) ? options[key] : default)
42
+ }
43
+ end
44
+
45
+ def initialize_copy(other)
46
+ raise
47
+ self
48
+ end
49
+
50
+ def add(cookie)
51
+ raise
52
+ self
53
+ end
54
+
55
+ # Iterates over all cookies that are not expired.
56
+ #
57
+ # Available option keywords are below:
58
+ #
59
+ # * +uri+
60
+ #
61
+ # Specify a URI object indicating the destination of the cookies
62
+ # being selected. Every cookie yielded should be good to send to
63
+ # the given URI, i.e. cookie.valid_for_uri?(uri) evaluates to
64
+ # true.
65
+ #
66
+ # If (and only if) this option is given, last access time of each
67
+ # cookie is updated to the current time.
68
+ def each(options = nil, &block)
69
+ raise
70
+ self
71
+ end
72
+ include Enumerable
73
+
74
+ def clear
75
+ raise
76
+ self
77
+ end
78
+
79
+ def cleanup(session = false)
80
+ if session
81
+ select { |cookie| cookie.session? || cookie.expired? }
82
+ else
83
+ select(&:expired?)
84
+ end.each { |cookie|
85
+ add(cookie.expire)
86
+ }
87
+ # subclasses can optionally remove over-the-limit cookies.
88
+ self
89
+ end
90
+ end
@@ -0,0 +1,74 @@
1
+ require 'http/cookie_jar'
2
+
3
+ # CookiestxtSaver saves and loads cookies in the cookies.txt format.
4
+ class HTTP::CookieJar::CookiestxtSaver < HTTP::CookieJar::AbstractSaver
5
+ True = "TRUE"
6
+ False = "FALSE"
7
+
8
+ def save(io, jar)
9
+ io.puts @header if @header
10
+ jar.each { |cookie|
11
+ next if !@session && cookie.session?
12
+ io.print cookie_to_record(cookie)
13
+ }
14
+ end
15
+
16
+ def load(io, jar)
17
+ io.each_line { |line|
18
+ cookie = parse_record(line) and jar.add(cookie)
19
+ }
20
+ end
21
+
22
+ private
23
+
24
+ def default_options
25
+ {
26
+ header: "# HTTP Cookie File",
27
+ linefeed: "\n",
28
+ }
29
+ end
30
+
31
+ # Serializes the cookie into a cookies.txt line.
32
+ def cookie_to_record(cookie)
33
+ cookie.instance_eval {
34
+ [
35
+ @domain,
36
+ @for_domain ? True : False,
37
+ @path,
38
+ @secure ? True : False,
39
+ @expires.to_i,
40
+ @name,
41
+ @value
42
+ ]
43
+ }.join("\t") << @linefeed
44
+ end
45
+
46
+ # Parses a line from cookies.txt and returns a cookie object if the
47
+ # line represents a cookie record or returns nil otherwise.
48
+ def parse_record(line)
49
+ return nil if line.match(/^#/)
50
+
51
+ domain,
52
+ s_for_domain, # Whether this cookie is for domain
53
+ path, # Path for which the cookie is relevant
54
+ s_secure, # Requires a secure connection
55
+ s_expires, # Time the cookie expires (Unix epoch time)
56
+ name, value = line.split("\t", 7)
57
+ return nil if value.nil?
58
+
59
+ value.chomp!
60
+
61
+ if (expires_seconds = s_expires.to_i).nonzero?
62
+ expires = Time.at(expires_seconds)
63
+ return nil if expires < Time.now
64
+ end
65
+
66
+ HTTP::Cookie.new(name, value,
67
+ :domain => domain,
68
+ :for_domain => s_for_domain == True,
69
+ :path => path,
70
+ :secure => s_secure == True,
71
+ :expires => expires,
72
+ :version => 0)
73
+ end
74
+ end