rubyforge 0.0.1 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,538 @@
1
+ # cookie.rb is redistributed file which is originally included in Webagent
2
+ # version 0.6.2 by TAKAHASHI `Maki' Masayoshi. And it contains some bug fixes.
3
+ # You can download the entire package of Webagent from
4
+ # http://www.rubycolor.org/arc/.
5
+
6
+
7
+ # Cookie class
8
+ #
9
+ # I refered to w3m's source to make these classes. Some comments
10
+ # are quoted from it. I'm thanksful for author(s) of it.
11
+ #
12
+ # w3m homepage: http://ei5nazha.yz.yamagata-u.ac.jp/~aito/w3m/eng/
13
+
14
+ require 'uri'
15
+
16
+ class WebAgent
17
+
18
+ module CookieUtils
19
+
20
+ def head_match?(str1, str2)
21
+ str1 == str2[0, str1.length]
22
+ end
23
+
24
+ def tail_match?(str1, str2)
25
+ if str1.length > 0
26
+ str1 == str2[-str1.length..-1].to_s
27
+ else
28
+ true
29
+ end
30
+ end
31
+
32
+ def domain_match(host, domain)
33
+ case domain
34
+ when /\d+\.\d+\.\d+\.\d+/
35
+ return (host == domain)
36
+ when '.'
37
+ return true
38
+ when /^\./
39
+ return tail_match?(domain, host)
40
+ else
41
+ return (host == domain)
42
+ end
43
+ end
44
+
45
+ def total_dot_num(string)
46
+ string.scan(/\./).length()
47
+ end
48
+
49
+ end
50
+
51
+ class Cookie
52
+ include CookieUtils
53
+
54
+ require 'parsedate'
55
+ include ParseDate
56
+
57
+ attr_accessor :name, :value
58
+ attr_accessor :domain, :path
59
+ attr_accessor :expires ## for Netscape Cookie
60
+ attr_accessor :url
61
+ attr_writer :use, :secure, :discard, :domain_orig, :path_orig, :override
62
+
63
+ USE = 1
64
+ SECURE = 2
65
+ DOMAIN = 4
66
+ PATH = 8
67
+ DISCARD = 16
68
+ OVERRIDE = 32
69
+ OVERRIDE_OK = 32
70
+
71
+ def initialize()
72
+ @discard = @use = @secure = @domain_orig = @path_orig = @override = nil
73
+ end
74
+
75
+ def discard?
76
+ @discard
77
+ end
78
+
79
+ def use?
80
+ @use
81
+ end
82
+
83
+ def secure?
84
+ @secure
85
+ end
86
+
87
+ def domain_orig?
88
+ @domain_orig
89
+ end
90
+
91
+ def path_orig?
92
+ @path_orig
93
+ end
94
+
95
+ def override?
96
+ @override
97
+ end
98
+
99
+ def flag
100
+ flg = 0
101
+ flg += USE if @use
102
+ flg += SECURE if @secure
103
+ flg += DOMAIN if @domain_orig
104
+ flg += PATH if @path_orig
105
+ flg += DISCARD if @discard
106
+ flg += OVERRIDE if @override
107
+ flg
108
+ end
109
+
110
+ def set_flag(flag)
111
+ flag = flag.to_i
112
+ @use = true if flag & USE > 0
113
+ @secure = true if flag & SECURE > 0
114
+ @domain_orig = true if flag & DOMAIN > 0
115
+ @path_orig = true if flag & PATH > 0
116
+ @discard = true if flag & DISCARD > 0
117
+ @override = true if flag & OVERRIDE > 0
118
+ end
119
+
120
+ def match?(url)
121
+ domainname = url.host
122
+ if (!domainname ||
123
+ !domain_match(domainname, @domain) ||
124
+ (@path && !head_match?(@path, url.path)) ||
125
+ (@secure && (url.scheme != 'https')) )
126
+ return false
127
+ else
128
+ return true
129
+ end
130
+ end
131
+
132
+ def join_quotedstr(array, sep)
133
+ ret = Array.new()
134
+ old_elem = nil
135
+ array.each{|elem|
136
+ if (elem.scan(/"/).length % 2) == 0
137
+ if old_elem
138
+ old_elem << sep << elem
139
+ else
140
+ ret << elem
141
+ old_elem = nil
142
+ end
143
+ else
144
+ if old_elem
145
+ old_elem << sep << elem
146
+ ret << old_elem
147
+ old_elem = nil
148
+ else
149
+ old_elem = elem.dup
150
+ end
151
+ end
152
+ }
153
+ ret
154
+ end
155
+
156
+ def parse(str, url)
157
+ @url = url
158
+ cookie_elem = str.split(/;/)
159
+ cookie_elem = join_quotedstr(cookie_elem, ';')
160
+ first_elem = cookie_elem.shift
161
+ if first_elem !~ /([^=]*)(\=(.*))?/
162
+ return
163
+ ## raise ArgumentError 'invalid cookie value'
164
+ end
165
+ @name = $1.strip
166
+ @value = $3
167
+ if @value
168
+ if @value =~ /^\s*"(.*)"\s*$/
169
+ @value = $1
170
+ else
171
+ @value.dup.strip!
172
+ end
173
+ end
174
+ cookie_elem.each{|pair|
175
+ key, value = pair.split(/=/) ## value may nil
176
+ key.strip!
177
+ if value
178
+ value = value.strip.sub(/\A"(.*)"\z/) { $1 }
179
+ end
180
+ case key.downcase
181
+ when 'domain'
182
+ @domain = value
183
+ when 'expires'
184
+ begin
185
+ @expires = Time.gm(*parsedate(value)[0,6])
186
+ rescue ArgumentError
187
+ @expires = nil
188
+ end
189
+ when 'path'
190
+ @path = value
191
+ when 'secure'
192
+ @secure = true ## value may nil, but must 'true'.
193
+ else
194
+ ## ignore
195
+ end
196
+ }
197
+ end
198
+
199
+ end
200
+
201
+ class CookieManager
202
+ include CookieUtils
203
+
204
+ ### errors
205
+ class Error < StandardError; end
206
+ class ErrorOverrideOK < Error; end
207
+ class SpecialError < Error; end
208
+ class NoDotError < ErrorOverrideOK; end
209
+
210
+ SPECIAL_DOMAIN = [".com",".edu",".gov",".mil",".net",".org",".int"]
211
+
212
+ attr_accessor :cookies
213
+ attr_accessor :cookies_file
214
+ attr_accessor :accept_domains, :reject_domains
215
+ attr_accessor :netscape_rule
216
+
217
+ def initialize(file=nil)
218
+ @cookies = Array.new()
219
+ @cookies_file = file
220
+ @is_saved = true
221
+ @reject_domains = Array.new()
222
+ @accept_domains = Array.new()
223
+ # for conformance to http://wp.netscape.com/newsref/std/cookie_spec.html
224
+ @netscape_rule = false
225
+ end
226
+
227
+ def save_all_cookies(force = nil, save_unused = true, save_discarded = true)
228
+ if @is_saved and !force
229
+ return
230
+ end
231
+ File.open(@cookies_file, 'w') do |f|
232
+ @cookies.each do |cookie|
233
+ if (cookie.use? or save_unused) and
234
+ (!cookie.discard? or save_discarded)
235
+ f.print(cookie.url.to_s,"\t",
236
+ cookie.name,"\t",
237
+ cookie.value,"\t",
238
+ cookie.expires.to_i,"\t",
239
+ cookie.domain,"\t",
240
+ cookie.path,"\t",
241
+ cookie.flag,"\n")
242
+ end
243
+ end
244
+ end
245
+ end
246
+
247
+ def save_cookies(force = nil)
248
+ save_all_cookies(force, false, false)
249
+ end
250
+
251
+ def check_expired_cookies()
252
+ @cookies.reject!{|cookie|
253
+ is_expired = (cookie.expires && (cookie.expires < Time.now.gmtime))
254
+ if is_expired && !cookie.discard?
255
+ @is_saved = false
256
+ end
257
+ is_expired
258
+ }
259
+ end
260
+
261
+ def parse(str, url)
262
+ cookie = WebAgent::Cookie.new()
263
+ cookie.parse(str, url)
264
+ add(cookie)
265
+ end
266
+
267
+ def make_cookie_str(cookie_list)
268
+ if cookie_list.empty?
269
+ return nil
270
+ end
271
+
272
+ ret = ''
273
+ c = cookie_list.shift
274
+ ret += "#{c.name}=#{c.value}"
275
+ cookie_list.each{|cookie|
276
+ ret += "; #{cookie.name}=#{cookie.value}"
277
+ }
278
+ return ret
279
+ end
280
+ private :make_cookie_str
281
+
282
+
283
+ def find(url)
284
+
285
+ check_expired_cookies()
286
+
287
+ cookie_list = Array.new()
288
+
289
+ @cookies.each{|cookie|
290
+ if cookie.use? && cookie.match?(url)
291
+ if cookie_list.select{|c1| c1.name == cookie.name}.empty?
292
+ cookie_list << cookie
293
+ end
294
+ end
295
+ }
296
+ return make_cookie_str(cookie_list)
297
+ end
298
+
299
+ def find_cookie_info(domain, path, name)
300
+ @cookies.find{|c|
301
+ c.domain == domain && c.path == path && c.name == name
302
+ }
303
+ end
304
+ private :find_cookie_info
305
+
306
+ def cookie_error(err, override)
307
+ if err.kind_of?(ErrorOverrideOK) || !override
308
+ raise err
309
+ end
310
+ end
311
+ private :cookie_error
312
+
313
+ def add(cookie)
314
+ url = cookie.url
315
+ name, value = cookie.name, cookie.value
316
+ expires, domain, path =
317
+ cookie.expires, cookie.domain, cookie.path
318
+ secure, domain_orig, path_orig =
319
+ cookie.secure?, cookie.domain_orig?, cookie.path_orig?
320
+ discard, override =
321
+ cookie.discard?, cookie.override?
322
+
323
+ domainname = url.host
324
+ domain_orig, path_orig = domain, path
325
+ use_security = override
326
+
327
+ if !domainname
328
+ cookie_error(NodotError.new(), override)
329
+ end
330
+
331
+ if domain
332
+
333
+ # [DRAFT 12] s. 4.2.2 (does not apply in the case that
334
+ # host name is the same as domain attribute for version 0
335
+ # cookie)
336
+ # I think that this rule has almost the same effect as the
337
+ # tail match of [NETSCAPE].
338
+ if domain !~ /^\./ && domainname != domain
339
+ domain = '.'+domain
340
+ end
341
+
342
+ n = total_dot_num(domain)
343
+ if n < 2
344
+ cookie_error(SpecialError.new(), override)
345
+ elsif @netscape_rule and n == 2
346
+ ## [NETSCAPE] rule
347
+ ok = SPECIAL_DOMAIN.select{|sdomain|
348
+ sdomain == domain[-(sdomain.length)..-1]
349
+ }
350
+ if ok.empty?
351
+ cookie_error(SpecialError.new(), override)
352
+ end
353
+ end
354
+
355
+ end
356
+
357
+ path ||= url.path.sub!(%r|/[^/]*|, '')
358
+ domain ||= domainname
359
+ cookie = find_cookie_info(domain, path, name)
360
+
361
+ if !cookie
362
+ cookie = WebAgent::Cookie.new()
363
+ cookie.use = true
364
+ @cookies << cookie
365
+ end
366
+
367
+ cookie.url = url
368
+ cookie.name = name
369
+ cookie.value = value
370
+ cookie.expires = expires
371
+ cookie.domain = domain
372
+ cookie.path = path
373
+
374
+ ## for flag
375
+ cookie.secure = secure
376
+ cookie.domain_orig = domain_orig
377
+ cookie.path_orig = path_orig
378
+ if discard || cookie.expires == nil
379
+ cookie.discard = true
380
+ else
381
+ cookie.discard = false
382
+ @is_saved = false
383
+ end
384
+
385
+ check_expired_cookies()
386
+ return false
387
+ end
388
+
389
+ def load_cookies()
390
+ return if !File.readable?(@cookies_file)
391
+ File.open(@cookies_file,'r'){|f|
392
+ while line = f.gets
393
+ cookie = WebAgent::Cookie.new()
394
+ @cookies << cookie
395
+ col = line.chomp.split(/\t/)
396
+ cookie.url = URI.parse(col[0])
397
+ cookie.name = col[1]
398
+ cookie.value = col[2]
399
+ cookie.expires = Time.at(col[3].to_i)
400
+ cookie.domain = col[4]
401
+ cookie.path = col[5]
402
+ cookie.set_flag(col[6])
403
+ end
404
+ }
405
+ end
406
+
407
+ def check_cookie_accept_domain(domain)
408
+ unless domain
409
+ return false
410
+ end
411
+ @accept_domains.each{|dom|
412
+ if domain_match(domain, dom)
413
+ return true
414
+ end
415
+ }
416
+ @reject_domains.each{|dom|
417
+ if domain_match(domain, dom)
418
+ return false
419
+ end
420
+ }
421
+ return true
422
+ end
423
+ end
424
+ end
425
+
426
+ __END__
427
+
428
+ =begin
429
+
430
+ == WebAgent::CookieManager Class
431
+
432
+ Load, save, parse and send cookies.
433
+
434
+ === Usage
435
+
436
+ ## initialize
437
+ cm = WebAgent::CookieManager.new("/home/foo/bar/cookie")
438
+
439
+ ## load cookie data
440
+ cm.load_cookies()
441
+
442
+ ## parse cookie from string (maybe "Set-Cookie:" header)
443
+ cm.parse(str)
444
+
445
+ ## send cookie data to url
446
+ f.write(cm.find(url))
447
+
448
+ ## save cookie to cookiefile
449
+ cm.save_cookies()
450
+
451
+
452
+ === Class Methods
453
+
454
+ -- CookieManager::new(file=nil)
455
+
456
+ create new CookieManager. If a file is provided,
457
+ use it as cookies' file.
458
+
459
+ === Methods
460
+
461
+ -- CookieManager#save_cookies(force = nil)
462
+
463
+ save cookies' data into file. if argument is true,
464
+ save data although data is not modified.
465
+
466
+ -- CookieManager#parse(str, url)
467
+
468
+ parse string and store cookie (to parse HTTP response header).
469
+
470
+ -- CookieManager#find(url)
471
+
472
+ get cookies and make into string (to send as HTTP request header).
473
+
474
+ -- CookieManager#add(cookie)
475
+
476
+ add new cookie.
477
+
478
+ -- CookieManager#load_cookies()
479
+
480
+ load cookies' data from file.
481
+
482
+
483
+ == WebAgent::CookieUtils Module
484
+
485
+ -- CookieUtils::head_match?(str1, str2)
486
+ -- CookieUtils::tail_match?(str1, str2)
487
+ -- CookieUtils::domain_match(host, domain)
488
+ -- CookieUtils::total_dot_num(str)
489
+
490
+
491
+ == WebAgent::Cookie Class
492
+
493
+ === Class Methods
494
+
495
+ -- Cookie::new()
496
+
497
+ create new cookie.
498
+
499
+ === Methods
500
+
501
+ -- Cookie#match?(url)
502
+
503
+ match cookie by url. if match, return true. otherwise,
504
+ return false.
505
+
506
+ -- Cookie#name
507
+ -- Cookie#name=(name)
508
+ -- Cookie#value
509
+ -- Cookie#value=(value)
510
+ -- Cookie#domain
511
+ -- Cookie#domain=(domain)
512
+ -- Cookie#path
513
+ -- Cookie#path=(path)
514
+ -- Cookie#expires
515
+ -- Cookie#expires=(expires)
516
+ -- Cookie#url
517
+ -- Cookie#url=(url)
518
+
519
+ accessor methods for cookie's items.
520
+
521
+ -- Cookie#discard?
522
+ -- Cookie#discard=(discard)
523
+ -- Cookie#use?
524
+ -- Cookie#use=(use)
525
+ -- Cookie#secure?
526
+ -- Cookie#secure=(secure)
527
+ -- Cookie#domain_orig?
528
+ -- Cookie#domain_orig=(domain_orig)
529
+ -- Cookie#path_orig?
530
+ -- Cookie#path_orig=(path_orig)
531
+ -- Cookie#override?
532
+ -- Cookie#override=(override)
533
+ -- Cookie#flag
534
+ -- Cookie#set_flag(flag_num)
535
+
536
+ accessor methods for flags.
537
+
538
+ =end