diamond-mechanize 2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +718 -0
- data/EXAMPLES.rdoc +187 -0
- data/FAQ.rdoc +11 -0
- data/GUIDE.rdoc +163 -0
- data/LICENSE.rdoc +20 -0
- data/Manifest.txt +159 -0
- data/README.rdoc +64 -0
- data/Rakefile +49 -0
- data/lib/mechanize.rb +1079 -0
- data/lib/mechanize/content_type_error.rb +13 -0
- data/lib/mechanize/cookie.rb +232 -0
- data/lib/mechanize/cookie_jar.rb +194 -0
- data/lib/mechanize/download.rb +59 -0
- data/lib/mechanize/element_matcher.rb +36 -0
- data/lib/mechanize/file.rb +65 -0
- data/lib/mechanize/file_connection.rb +17 -0
- data/lib/mechanize/file_request.rb +26 -0
- data/lib/mechanize/file_response.rb +74 -0
- data/lib/mechanize/file_saver.rb +39 -0
- data/lib/mechanize/form.rb +543 -0
- data/lib/mechanize/form/button.rb +6 -0
- data/lib/mechanize/form/check_box.rb +12 -0
- data/lib/mechanize/form/field.rb +54 -0
- data/lib/mechanize/form/file_upload.rb +21 -0
- data/lib/mechanize/form/hidden.rb +3 -0
- data/lib/mechanize/form/image_button.rb +19 -0
- data/lib/mechanize/form/keygen.rb +34 -0
- data/lib/mechanize/form/multi_select_list.rb +94 -0
- data/lib/mechanize/form/option.rb +50 -0
- data/lib/mechanize/form/radio_button.rb +55 -0
- data/lib/mechanize/form/reset.rb +3 -0
- data/lib/mechanize/form/select_list.rb +44 -0
- data/lib/mechanize/form/submit.rb +3 -0
- data/lib/mechanize/form/text.rb +3 -0
- data/lib/mechanize/form/textarea.rb +3 -0
- data/lib/mechanize/headers.rb +23 -0
- data/lib/mechanize/history.rb +82 -0
- data/lib/mechanize/http.rb +8 -0
- data/lib/mechanize/http/agent.rb +1004 -0
- data/lib/mechanize/http/auth_challenge.rb +59 -0
- data/lib/mechanize/http/auth_realm.rb +31 -0
- data/lib/mechanize/http/content_disposition_parser.rb +188 -0
- data/lib/mechanize/http/www_authenticate_parser.rb +155 -0
- data/lib/mechanize/monkey_patch.rb +16 -0
- data/lib/mechanize/page.rb +440 -0
- data/lib/mechanize/page/base.rb +7 -0
- data/lib/mechanize/page/frame.rb +27 -0
- data/lib/mechanize/page/image.rb +30 -0
- data/lib/mechanize/page/label.rb +20 -0
- data/lib/mechanize/page/link.rb +98 -0
- data/lib/mechanize/page/meta_refresh.rb +68 -0
- data/lib/mechanize/parser.rb +173 -0
- data/lib/mechanize/pluggable_parsers.rb +144 -0
- data/lib/mechanize/redirect_limit_reached_error.rb +19 -0
- data/lib/mechanize/redirect_not_get_or_head_error.rb +21 -0
- data/lib/mechanize/response_code_error.rb +21 -0
- data/lib/mechanize/response_read_error.rb +27 -0
- data/lib/mechanize/robots_disallowed_error.rb +28 -0
- data/lib/mechanize/test_case.rb +663 -0
- data/lib/mechanize/unauthorized_error.rb +3 -0
- data/lib/mechanize/unsupported_scheme_error.rb +6 -0
- data/lib/mechanize/util.rb +101 -0
- data/test/data/htpasswd +1 -0
- data/test/data/server.crt +16 -0
- data/test/data/server.csr +12 -0
- data/test/data/server.key +15 -0
- data/test/data/server.pem +15 -0
- data/test/htdocs/alt_text.html +10 -0
- data/test/htdocs/bad_form_test.html +9 -0
- data/test/htdocs/button.jpg +0 -0
- data/test/htdocs/canonical_uri.html +9 -0
- data/test/htdocs/dir with spaces/foo.html +1 -0
- data/test/htdocs/empty_form.html +6 -0
- data/test/htdocs/file_upload.html +26 -0
- data/test/htdocs/find_link.html +41 -0
- data/test/htdocs/form_multi_select.html +16 -0
- data/test/htdocs/form_multival.html +37 -0
- data/test/htdocs/form_no_action.html +18 -0
- data/test/htdocs/form_no_input_name.html +16 -0
- data/test/htdocs/form_order_test.html +11 -0
- data/test/htdocs/form_select.html +16 -0
- data/test/htdocs/form_set_fields.html +14 -0
- data/test/htdocs/form_test.html +188 -0
- data/test/htdocs/frame_referer_test.html +10 -0
- data/test/htdocs/frame_test.html +30 -0
- data/test/htdocs/google.html +13 -0
- data/test/htdocs/index.html +6 -0
- data/test/htdocs/link with space.html +5 -0
- data/test/htdocs/meta_cookie.html +11 -0
- data/test/htdocs/no_title_test.html +6 -0
- data/test/htdocs/noindex.html +9 -0
- data/test/htdocs/rails_3_encoding_hack_form_test.html +27 -0
- data/test/htdocs/relative/tc_relative_links.html +21 -0
- data/test/htdocs/robots.html +8 -0
- data/test/htdocs/robots.txt +2 -0
- data/test/htdocs/tc_bad_charset.html +9 -0
- data/test/htdocs/tc_bad_links.html +5 -0
- data/test/htdocs/tc_base_link.html +8 -0
- data/test/htdocs/tc_blank_form.html +11 -0
- data/test/htdocs/tc_charset.html +6 -0
- data/test/htdocs/tc_checkboxes.html +19 -0
- data/test/htdocs/tc_encoded_links.html +5 -0
- data/test/htdocs/tc_field_precedence.html +11 -0
- data/test/htdocs/tc_follow_meta.html +8 -0
- data/test/htdocs/tc_form_action.html +48 -0
- data/test/htdocs/tc_links.html +19 -0
- data/test/htdocs/tc_meta_in_body.html +9 -0
- data/test/htdocs/tc_pretty_print.html +17 -0
- data/test/htdocs/tc_referer.html +16 -0
- data/test/htdocs/tc_relative_links.html +19 -0
- data/test/htdocs/tc_textarea.html +23 -0
- data/test/htdocs/test_click.html +11 -0
- data/test/htdocs/unusual______.html +5 -0
- data/test/test_mechanize.rb +1164 -0
- data/test/test_mechanize_cookie.rb +451 -0
- data/test/test_mechanize_cookie_jar.rb +483 -0
- data/test/test_mechanize_download.rb +43 -0
- data/test/test_mechanize_file.rb +61 -0
- data/test/test_mechanize_file_connection.rb +21 -0
- data/test/test_mechanize_file_request.rb +19 -0
- data/test/test_mechanize_file_saver.rb +21 -0
- data/test/test_mechanize_form.rb +875 -0
- data/test/test_mechanize_form_check_box.rb +38 -0
- data/test/test_mechanize_form_encoding.rb +114 -0
- data/test/test_mechanize_form_field.rb +63 -0
- data/test/test_mechanize_form_file_upload.rb +20 -0
- data/test/test_mechanize_form_image_button.rb +12 -0
- data/test/test_mechanize_form_keygen.rb +32 -0
- data/test/test_mechanize_form_multi_select_list.rb +84 -0
- data/test/test_mechanize_form_option.rb +55 -0
- data/test/test_mechanize_form_radio_button.rb +78 -0
- data/test/test_mechanize_form_select_list.rb +76 -0
- data/test/test_mechanize_form_textarea.rb +52 -0
- data/test/test_mechanize_headers.rb +35 -0
- data/test/test_mechanize_history.rb +103 -0
- data/test/test_mechanize_http_agent.rb +1225 -0
- data/test/test_mechanize_http_auth_challenge.rb +39 -0
- data/test/test_mechanize_http_auth_realm.rb +49 -0
- data/test/test_mechanize_http_content_disposition_parser.rb +118 -0
- data/test/test_mechanize_http_www_authenticate_parser.rb +146 -0
- data/test/test_mechanize_link.rb +80 -0
- data/test/test_mechanize_page.rb +118 -0
- data/test/test_mechanize_page_encoding.rb +182 -0
- data/test/test_mechanize_page_frame.rb +16 -0
- data/test/test_mechanize_page_link.rb +390 -0
- data/test/test_mechanize_page_meta_refresh.rb +127 -0
- data/test/test_mechanize_parser.rb +289 -0
- data/test/test_mechanize_pluggable_parser.rb +52 -0
- data/test/test_mechanize_redirect_limit_reached_error.rb +24 -0
- data/test/test_mechanize_redirect_not_get_or_head_error.rb +14 -0
- data/test/test_mechanize_subclass.rb +22 -0
- data/test/test_mechanize_util.rb +103 -0
- data/test/test_multi_select.rb +119 -0
- metadata +216 -0
@@ -0,0 +1,13 @@
|
|
1
|
+
##
|
2
|
+
# This error is raised when a pluggable parser tries to parse a content type
|
3
|
+
# that it does not know how to handle. For example if Mechanize::Page were to
|
4
|
+
# try to parse a PDF, a ContentTypeError would be thrown.
|
5
|
+
|
6
|
+
class Mechanize::ContentTypeError < Mechanize::Error
|
7
|
+
attr_reader :content_type
|
8
|
+
|
9
|
+
def initialize(content_type)
|
10
|
+
@content_type = content_type
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
@@ -0,0 +1,232 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'webrick/cookie'
|
3
|
+
require 'domain_name'
|
4
|
+
|
5
|
+
# This class is used to represent an HTTP Cookie.
|
6
|
+
class Mechanize::Cookie
|
7
|
+
attr_reader :name
|
8
|
+
attr_accessor :value, :version
|
9
|
+
attr_accessor :domain, :path, :secure
|
10
|
+
attr_accessor :comment, :max_age
|
11
|
+
|
12
|
+
attr_accessor :session
|
13
|
+
|
14
|
+
attr_accessor :created_at
|
15
|
+
attr_accessor :accessed_at
|
16
|
+
|
17
|
+
# :call-seq:
|
18
|
+
# new(name, value)
|
19
|
+
# new(name, value, attr_hash)
|
20
|
+
# new(attr_hash)
|
21
|
+
#
|
22
|
+
# Creates a cookie object. For each key of +attr_hash+, the setter
|
23
|
+
# is called if defined. Each key can be either a symbol or a
|
24
|
+
# string, downcased or not.
|
25
|
+
#
|
26
|
+
# e.g.
|
27
|
+
# new("uid", "a12345")
|
28
|
+
# new("uid", "a12345", :domain => 'example.org',
|
29
|
+
# :for_domain => true, :expired => Time.now + 7*86400)
|
30
|
+
# new("name" => "uid", "value" => "a12345", "Domain" => 'www.example.org')
|
31
|
+
#
|
32
|
+
def initialize(*args)
|
33
|
+
@version = 0 # Netscape Cookie
|
34
|
+
|
35
|
+
@domain = @path = @secure = @comment = @max_age =
|
36
|
+
@expires = @comment_url = @discard = @port = nil
|
37
|
+
|
38
|
+
@created_at = @accessed_at = Time.now
|
39
|
+
case args.size
|
40
|
+
when 2
|
41
|
+
@name, @value = *args
|
42
|
+
@for_domain = false
|
43
|
+
return
|
44
|
+
when 3
|
45
|
+
@name, @value, attr_hash = *args
|
46
|
+
when 1
|
47
|
+
attr_hash = args.first
|
48
|
+
else
|
49
|
+
raise ArgumentError, "wrong number of arguments (#{args.size} for 1-3)"
|
50
|
+
end
|
51
|
+
for_domain = false
|
52
|
+
attr_hash.each_pair { |key, val|
|
53
|
+
skey = key.to_s.downcase
|
54
|
+
skey.sub!(/[!?]\z/, '')
|
55
|
+
case skey
|
56
|
+
when 'for_domain'
|
57
|
+
for_domain = !!val
|
58
|
+
when 'name'
|
59
|
+
@name = val
|
60
|
+
when 'value'
|
61
|
+
@value = val
|
62
|
+
else
|
63
|
+
setter = :"#{skey}="
|
64
|
+
send(setter, val) if respond_to?(setter)
|
65
|
+
end
|
66
|
+
}
|
67
|
+
@for_domain = for_domain
|
68
|
+
end
|
69
|
+
|
70
|
+
# If this flag is true, this cookie will be sent to any host in the
|
71
|
+
# +domain+. If it is false, this cookie will be sent only to the
|
72
|
+
# host indicated by the +domain+.
|
73
|
+
attr_accessor :for_domain
|
74
|
+
alias for_domain? for_domain
|
75
|
+
|
76
|
+
class << self
|
77
|
+
# Parses a Set-Cookie header line +str+ sent from +uri+ into an
|
78
|
+
# array of Cookie objects. Note that this array may contain
|
79
|
+
# nil's when some of the cookie-pairs are malformed.
|
80
|
+
def parse(uri, str, log = Mechanize.log)
|
81
|
+
return str.split(/,(?=[^;,]*=)|,$/).map { |c|
|
82
|
+
cookie_elem = c.split(/;+/)
|
83
|
+
first_elem = cookie_elem.shift
|
84
|
+
first_elem.strip!
|
85
|
+
key, value = first_elem.split(/\=/, 2)
|
86
|
+
|
87
|
+
begin
|
88
|
+
cookie = new(key, value.dup)
|
89
|
+
rescue
|
90
|
+
log.warn("Couldn't parse key/value: #{first_elem}") if log
|
91
|
+
next
|
92
|
+
end
|
93
|
+
|
94
|
+
cookie_elem.each do |pair|
|
95
|
+
pair.strip!
|
96
|
+
key, value = pair.split(/\=/, 2)
|
97
|
+
next unless key
|
98
|
+
value = WEBrick::HTTPUtils.dequote(value.strip) if value
|
99
|
+
|
100
|
+
case key.downcase
|
101
|
+
when 'domain'
|
102
|
+
begin
|
103
|
+
cookie.domain = value
|
104
|
+
cookie.for_domain = true
|
105
|
+
rescue
|
106
|
+
log.warn("Couldn't parse domain: #{value}") if log
|
107
|
+
end
|
108
|
+
when 'path'
|
109
|
+
cookie.path = value
|
110
|
+
when 'expires'
|
111
|
+
if value.empty?
|
112
|
+
cookie.session = true
|
113
|
+
next
|
114
|
+
end
|
115
|
+
|
116
|
+
begin
|
117
|
+
cookie.expires = Time::parse(value)
|
118
|
+
rescue
|
119
|
+
log.warn("Couldn't parse expires: #{value}") if log
|
120
|
+
end
|
121
|
+
when 'max-age'
|
122
|
+
begin
|
123
|
+
cookie.max_age = Integer(value)
|
124
|
+
rescue
|
125
|
+
log.warn("Couldn't parse max age '#{value}'") if log
|
126
|
+
end
|
127
|
+
when 'comment'
|
128
|
+
cookie.comment = value
|
129
|
+
when 'version'
|
130
|
+
begin
|
131
|
+
cookie.version = Integer(value)
|
132
|
+
rescue
|
133
|
+
log.warn("Couldn't parse version '#{value}'") if log
|
134
|
+
cookie.version = nil
|
135
|
+
end
|
136
|
+
when 'secure'
|
137
|
+
cookie.secure = true
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
cookie.path ||= (uri + './').path
|
142
|
+
cookie.secure ||= false
|
143
|
+
cookie.domain ||= uri.host
|
144
|
+
# Move this in to the cookie jar
|
145
|
+
yield cookie if block_given?
|
146
|
+
|
147
|
+
cookie
|
148
|
+
}
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
alias set_domain domain=
|
153
|
+
|
154
|
+
# Sets the domain attribute. A leading dot in +domain+ implies
|
155
|
+
# turning the +for_domain?+ flag on.
|
156
|
+
def domain=(domain)
|
157
|
+
if DomainName === domain
|
158
|
+
@domain_name = domain
|
159
|
+
else
|
160
|
+
domain.is_a?(String) or
|
161
|
+
(domain.respond_to?(:to_str) && (domain = domain.to_str).is_a?(String)) or
|
162
|
+
raise TypeError, "#{domain.class} is not a String"
|
163
|
+
if domain.start_with?('.')
|
164
|
+
@for_domain = true
|
165
|
+
domain = domain[1..-1]
|
166
|
+
end
|
167
|
+
# Do we really need to support this?
|
168
|
+
if domain.match(/\A([^:]+):[0-9]+\z/)
|
169
|
+
domain = $1
|
170
|
+
end
|
171
|
+
@domain_name = DomainName.new(domain)
|
172
|
+
end
|
173
|
+
set_domain(@domain_name.hostname)
|
174
|
+
end
|
175
|
+
|
176
|
+
def expires=(t)
|
177
|
+
@expires = t && (t.is_a?(Time) ? t.httpdate : t.to_s)
|
178
|
+
end
|
179
|
+
|
180
|
+
def expires
|
181
|
+
@expires && Time.parse(@expires)
|
182
|
+
end
|
183
|
+
|
184
|
+
def expired?
|
185
|
+
return false unless expires
|
186
|
+
Time.now > expires
|
187
|
+
end
|
188
|
+
|
189
|
+
alias secure? secure
|
190
|
+
|
191
|
+
def acceptable_from_uri?(uri)
|
192
|
+
host = DomainName.new(uri.host)
|
193
|
+
|
194
|
+
# RFC 6265 5.3
|
195
|
+
# When the user agent "receives a cookie":
|
196
|
+
return host.hostname == domain unless @for_domain
|
197
|
+
|
198
|
+
if host.cookie_domain?(@domain_name)
|
199
|
+
true
|
200
|
+
elsif host.hostname == domain
|
201
|
+
@for_domain = false
|
202
|
+
true
|
203
|
+
else
|
204
|
+
false
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def valid_for_uri?(uri)
|
209
|
+
return false if secure? && uri.scheme != 'https'
|
210
|
+
acceptable_from_uri?(uri) && uri.path.start_with?(path)
|
211
|
+
end
|
212
|
+
|
213
|
+
def to_s
|
214
|
+
"#{@name}=#{@value}"
|
215
|
+
end
|
216
|
+
|
217
|
+
def init_with(coder)
|
218
|
+
yaml_initialize(coder.tag, coder.map)
|
219
|
+
end
|
220
|
+
|
221
|
+
def yaml_initialize(tag, map)
|
222
|
+
@for_domain = true # for forward compatibility
|
223
|
+
map.each { |key, value|
|
224
|
+
case key
|
225
|
+
when 'domain'
|
226
|
+
self.domain = value # ditto
|
227
|
+
else
|
228
|
+
instance_variable_set(:"@#{key}", value)
|
229
|
+
end
|
230
|
+
}
|
231
|
+
end
|
232
|
+
end
|
@@ -0,0 +1,194 @@
|
|
1
|
+
##
|
2
|
+
# This class is used to manage the Cookies that have been returned from
|
3
|
+
# any particular website.
|
4
|
+
|
5
|
+
class Mechanize::CookieJar
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
# add_cookie wants something resembling a URI.
|
9
|
+
|
10
|
+
attr_reader :jar
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@jar = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize_copy other # :nodoc:
|
17
|
+
@jar = Marshal.load Marshal.dump other.jar
|
18
|
+
end
|
19
|
+
|
20
|
+
# Add a +cookie+ to the jar if it is considered acceptable from
|
21
|
+
# +uri+. Return nil if the cookie was not added, otherwise return
|
22
|
+
# the cookie added.
|
23
|
+
def add(uri, cookie)
|
24
|
+
return nil unless cookie.acceptable_from_uri?(uri)
|
25
|
+
add!(cookie)
|
26
|
+
cookie
|
27
|
+
end
|
28
|
+
|
29
|
+
# Add a +cookie+ to the jar and return self.
|
30
|
+
def add!(cookie)
|
31
|
+
normal_domain = cookie.domain.downcase
|
32
|
+
|
33
|
+
@jar[normal_domain] ||= {} unless @jar.has_key?(normal_domain)
|
34
|
+
|
35
|
+
@jar[normal_domain][cookie.path] ||= {}
|
36
|
+
@jar[normal_domain][cookie.path][cookie.name] = cookie
|
37
|
+
|
38
|
+
self
|
39
|
+
end
|
40
|
+
alias << add!
|
41
|
+
|
42
|
+
# Fetch the cookies that should be used for the URI object passed in.
|
43
|
+
def cookies(url)
|
44
|
+
cleanup
|
45
|
+
url.path = '/' if url.path.empty?
|
46
|
+
now = Time.now
|
47
|
+
|
48
|
+
select { |cookie|
|
49
|
+
!cookie.expired? && cookie.valid_for_uri?(url) && (cookie.accessed_at = now)
|
50
|
+
}.sort_by { |cookie|
|
51
|
+
# RFC 6265 5.4
|
52
|
+
# Precedence: 1. longer path 2. older creation
|
53
|
+
[-cookie.path.length, cookie.created_at]
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
def empty?(url)
|
58
|
+
cookies(url).length > 0 ? false : true
|
59
|
+
end
|
60
|
+
|
61
|
+
def each
|
62
|
+
block_given? or return enum_for(__method__)
|
63
|
+
cleanup
|
64
|
+
@jar.each { |domain, paths|
|
65
|
+
paths.each { |path, hash|
|
66
|
+
hash.each_value { |cookie|
|
67
|
+
yield cookie
|
68
|
+
}
|
69
|
+
}
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
# Save the cookie jar to a file in the format specified.
|
74
|
+
#
|
75
|
+
# Available formats:
|
76
|
+
# :yaml <- YAML structure
|
77
|
+
# :cookiestxt <- Mozilla's cookies.txt format
|
78
|
+
def save_as(file, format = :yaml)
|
79
|
+
jar = dup
|
80
|
+
jar.cleanup true
|
81
|
+
|
82
|
+
open(file, 'w') { |f|
|
83
|
+
case format
|
84
|
+
when :yaml then
|
85
|
+
load_yaml
|
86
|
+
|
87
|
+
YAML.dump(jar.jar, f)
|
88
|
+
when :cookiestxt then
|
89
|
+
jar.dump_cookiestxt(f)
|
90
|
+
else
|
91
|
+
raise ArgumentError, "Unknown cookie jar file format"
|
92
|
+
end
|
93
|
+
}
|
94
|
+
|
95
|
+
self
|
96
|
+
end
|
97
|
+
|
98
|
+
# Load cookie jar from a file in the format specified.
|
99
|
+
#
|
100
|
+
# Available formats:
|
101
|
+
# :yaml <- YAML structure.
|
102
|
+
# :cookiestxt <- Mozilla's cookies.txt format
|
103
|
+
def load(file, format = :yaml)
|
104
|
+
@jar = open(file) { |f|
|
105
|
+
case format
|
106
|
+
when :yaml then
|
107
|
+
load_yaml
|
108
|
+
|
109
|
+
YAML.load(f)
|
110
|
+
when :cookiestxt then
|
111
|
+
load_cookiestxt(f)
|
112
|
+
else
|
113
|
+
raise ArgumentError, "Unknown cookie jar file format"
|
114
|
+
end
|
115
|
+
}
|
116
|
+
|
117
|
+
cleanup
|
118
|
+
|
119
|
+
self
|
120
|
+
end
|
121
|
+
|
122
|
+
def load_yaml # :nodoc:
|
123
|
+
begin
|
124
|
+
require 'psych'
|
125
|
+
rescue LoadError
|
126
|
+
end
|
127
|
+
|
128
|
+
require 'yaml'
|
129
|
+
end
|
130
|
+
|
131
|
+
# Clear the cookie jar
|
132
|
+
def clear!
|
133
|
+
@jar = {}
|
134
|
+
end
|
135
|
+
|
136
|
+
# Read cookies from Mozilla cookies.txt-style IO stream
|
137
|
+
def load_cookiestxt(io)
|
138
|
+
now = Time.now
|
139
|
+
|
140
|
+
io.each_line do |line|
|
141
|
+
line.chomp!
|
142
|
+
line.gsub!(/#.+/, '')
|
143
|
+
fields = line.split("\t")
|
144
|
+
|
145
|
+
next if fields.length != 7
|
146
|
+
|
147
|
+
expires_seconds = fields[4].to_i
|
148
|
+
expires = (expires_seconds == 0) ? nil : Time.at(expires_seconds)
|
149
|
+
next if expires and (expires < now)
|
150
|
+
|
151
|
+
c = Mechanize::Cookie.new(fields[5], fields[6])
|
152
|
+
c.domain = fields[0]
|
153
|
+
c.for_domain = (fields[1] == "TRUE") # Whether this cookie is for domain
|
154
|
+
c.path = fields[2] # Path for which the cookie is relevant
|
155
|
+
c.secure = (fields[3] == "TRUE") # Requires a secure connection
|
156
|
+
c.expires = expires # Time the cookie expires.
|
157
|
+
c.version = 0 # Conforms to Netscape cookie spec.
|
158
|
+
|
159
|
+
add!(c)
|
160
|
+
end
|
161
|
+
|
162
|
+
@jar
|
163
|
+
end
|
164
|
+
|
165
|
+
# Write cookies to Mozilla cookies.txt-style IO stream
|
166
|
+
def dump_cookiestxt(io)
|
167
|
+
to_a.each do |cookie|
|
168
|
+
io.puts([
|
169
|
+
cookie.domain,
|
170
|
+
cookie.for_domain? ? "TRUE" : "FALSE",
|
171
|
+
cookie.path,
|
172
|
+
cookie.secure ? "TRUE" : "FALSE",
|
173
|
+
cookie.expires.to_i.to_s,
|
174
|
+
cookie.name,
|
175
|
+
cookie.value
|
176
|
+
].join("\t"))
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
protected
|
181
|
+
|
182
|
+
# Remove expired cookies
|
183
|
+
def cleanup session = false
|
184
|
+
@jar.each do |domain, paths|
|
185
|
+
paths.each do |path, names|
|
186
|
+
names.each do |cookie_name, cookie|
|
187
|
+
paths[path].delete(cookie_name) if
|
188
|
+
cookie.expired? or (session and cookie.session)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
@@ -0,0 +1,59 @@
|
|
1
|
+
##
|
2
|
+
# Download is a pluggable parser for downloading files without loading them
|
3
|
+
# into memory first. You may subclass this class to handle content types you
|
4
|
+
# do not wish to load into memory first.
|
5
|
+
#
|
6
|
+
# See Mechanize::PluggableParser for instructions on using this class.
|
7
|
+
|
8
|
+
class Mechanize::Download
|
9
|
+
|
10
|
+
include Mechanize::Parser
|
11
|
+
|
12
|
+
##
|
13
|
+
# Accessor for the IO-like that contains the body
|
14
|
+
|
15
|
+
attr_reader :body_io
|
16
|
+
|
17
|
+
alias content body_io
|
18
|
+
|
19
|
+
##
|
20
|
+
# Creates a new download retrieved from the given +uri+ and +response+
|
21
|
+
# object. The +body_io+ is an IO-like containing the HTTP response body and
|
22
|
+
# +code+ is the HTTP status.
|
23
|
+
|
24
|
+
def initialize uri = nil, response = nil, body_io = nil, code = nil
|
25
|
+
@uri = uri
|
26
|
+
@body_io = body_io
|
27
|
+
@code = code
|
28
|
+
|
29
|
+
@full_path = false unless defined? @full_path
|
30
|
+
|
31
|
+
fill_header response
|
32
|
+
extract_filename
|
33
|
+
|
34
|
+
yield self if block_given?
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# Saves a copy of the body_io to +filename+
|
39
|
+
|
40
|
+
def save filename = nil
|
41
|
+
filename = find_free_name filename
|
42
|
+
|
43
|
+
dirname = File.dirname filename
|
44
|
+
FileUtils.mkdir_p dirname
|
45
|
+
|
46
|
+
# Ruby 1.8.7 implements StringIO#path, can't use respond_to? :path
|
47
|
+
if StringIO === @body_io then
|
48
|
+
open filename, 'wb' do |io|
|
49
|
+
until @body_io.eof? do
|
50
|
+
io.write @body_io.read 16384
|
51
|
+
end
|
52
|
+
end
|
53
|
+
else
|
54
|
+
FileUtils.mv @body_io.path, filename
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|