group_open_id 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,3 @@
1
+ == 0.1 / 2007-08-07
2
+
3
+ * Birthday!
data/Manifest.txt ADDED
@@ -0,0 +1,10 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ lib/group_open_id.rb
6
+ lib/group_open_id/client.rb
7
+ lib/group_open_id/fetcher.rb
8
+ lib/group_open_id/uri.rb
9
+ spec/group_open_id_spec.rb
10
+ vendor/rest-open-uri.rb
data/README.txt ADDED
@@ -0,0 +1,64 @@
1
+ = Group Open ID
2
+
3
+ * Homepage: http://deepblue.springnote.com/pages/406396
4
+ * Author: Bryan Kang (http://myruby.net)
5
+
6
+ == DESCRIPTION:
7
+
8
+ Wrapper library for myID.net's Group ID API
9
+
10
+ == FEATURES/PROBLEMS:
11
+
12
+ * TBD
13
+
14
+ == SYNOPSIS:
15
+
16
+ require 'group_open_id'
17
+
18
+ # Initialize a client
19
+ GroupOpenID::Client.app_key = 'your_application_key'
20
+ client = GroupOpenID::Client.new('user_open_id_url', 'user_key')
21
+ group_id = GroupOpenID::URI.new('http://ruby.myid.net', client)
22
+
23
+ # Get the membership location
24
+ puts group_id.membership_location # => 'http://some.url/'
25
+
26
+ # Get member lists
27
+ puts group_id.members # => array of GroupOpenID::Member
28
+
29
+ # Determine where a given open_id is the member of a group id
30
+ puts group_id.member?('http://deepblue.myid.net') # => true
31
+
32
+ == REQUIREMENTS:
33
+
34
+ * rest_open_uri
35
+ * Hpricot
36
+
37
+ == INSTALL:
38
+
39
+ * sudo gem install group_open_id
40
+
41
+ == LICENSE:
42
+
43
+ (The MIT License)
44
+
45
+ Copyright (c) 2007 Bryan Kang
46
+
47
+ Permission is hereby granted, free of charge, to any person obtaining
48
+ a copy of this software and associated documentation files (the
49
+ 'Software'), to deal in the Software without restriction, including
50
+ without limitation the rights to use, copy, modify, merge, publish,
51
+ distribute, sublicense, and/or sell copies of the Software, and to
52
+ permit persons to whom the Software is furnished to do so, subject to
53
+ the following conditions:
54
+
55
+ The above copyright notice and this permission notice shall be
56
+ included in all copies or substantial portions of the Software.
57
+
58
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
59
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
60
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
61
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
62
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
63
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
64
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+ require 'hoe'
3
+ require './lib/group_open_id/version'
4
+
5
+ Hoe.new('group_open_id', GroupOpenID::VERSION) do |p|
6
+ p.rubyforge_name = 'springnote'
7
+ p.summary = "Wrapper library for myID.net's Group ID API"
8
+ p.author = 'Bryan Kang'
9
+ p.email = 'byblue@gmail.com'
10
+ p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
11
+ p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
12
+ p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
13
+ p.need_zip = true
14
+ p.extra_deps << ['hpricot', [">= 0.6"]]
15
+ end
@@ -0,0 +1,47 @@
1
+ require 'hpricot'
2
+ require File.dirname(__FILE__) + '/fetcher'
3
+
4
+ module GroupOpenID
5
+ class Client
6
+ attr_accessor :user_key, :user_openid
7
+
8
+ class <<self
9
+ attr_accessor :app_key
10
+ end
11
+
12
+ def initialize(user_openid = '', user_key = '')
13
+ @user_key, @user_openid = user_key, user_openid
14
+ end
15
+
16
+ def membership_location(group)
17
+ fetcher.head(group)['x-myid-membership-location']
18
+ end
19
+ alias_method :group_id?, :membership_location
20
+
21
+ def members(group)
22
+ (url = membership_location(group)) or raise InvalidGroupID
23
+ url += '?format=xml'
24
+ p url
25
+ parse_members(fetcher.get(url))
26
+ end
27
+
28
+ def member?(group, member)
29
+ (url = membership_location(group)) or raise InvalidGroupID
30
+ url += '?openid=' + CGI.escape(::URI.parse(member).normalize.to_s)
31
+ fetcher.head(url)['x-myid-membership'].to_s == 'true'
32
+ end
33
+
34
+ attr_writer :fetcher
35
+ def fetcher
36
+ @fetcher ||= Fetcher.new(self)
37
+ end
38
+
39
+ def parse_members(doc)
40
+ Hpricot(doc).search('//relation').map do |rel|
41
+ member = Member.new
42
+ member.members.each{|sym| member.send("#{sym}=", rel.at(sym).inner_html)}
43
+ member
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,27 @@
1
+ module GroupOpenID
2
+ class Fetcher
3
+ def initialize(config = {})
4
+ @config = config
5
+ end
6
+
7
+ def username
8
+ CGI.escape(@config.user_openid)
9
+ end
10
+
11
+ def password
12
+ [@config.user_key, GroupOpenID::Client.app_key].join('.')
13
+ end
14
+
15
+ def default_params
16
+ { :http_basic_authentication => [username, password] }
17
+ end
18
+
19
+ def head(url)
20
+ open(url, default_params.merge(:method => :head)).meta
21
+ end
22
+
23
+ def get(url)
24
+ open(url, default_params).read
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,19 @@
1
+ module GroupOpenID
2
+ class URI
3
+ def initialize(uri, client)
4
+ @uri, @client = uri, client
5
+ end
6
+
7
+ def membership_location
8
+ @client.membership_location(@uri)
9
+ end
10
+
11
+ def members
12
+ @client.members(@uri)
13
+ end
14
+
15
+ def member?(openid)
16
+ @client.member?(@uri, openid)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ require 'cgi'
3
+ require File.dirname(__FILE__) + '/../vendor/rest-open-uri'
4
+ require File.dirname(__FILE__) + '/group_open_id/version'
5
+ require File.dirname(__FILE__) + '/group_open_id/client'
6
+ require File.dirname(__FILE__) + '/group_open_id/uri'
7
+
8
+ module GroupOpenID
9
+ Member = Struct.new(:openid, :name, :email)
10
+
11
+ class InvalidGroupID < Exception; end
12
+ end
@@ -0,0 +1,53 @@
1
+ require File.dirname(__FILE__) + '/../lib/group_open_id'
2
+
3
+ describe GroupOpenID do
4
+ it "should return nil unless it can't discovers a membership location" do
5
+ client = GroupOpenID::Client.new
6
+ client.fetcher = fetcher(:head => {})
7
+ client.membership_location('').should == nil
8
+ end
9
+
10
+ it "should discover the membership location" do
11
+ client = GroupOpenID::Client.new
12
+ client.fetcher = fetcher(:head => {'x-myid-membership-location' => 'http://member.url'})
13
+ client.membership_location('').should == 'http://member.url'
14
+ end
15
+
16
+ it "should return where given openid is a member of the specific group id" do
17
+ client = GroupOpenID::Client.new
18
+ client.fetcher = fetcher(:head => {'x-myid-membership-location' => 'http://member.url', 'x-myid-membership' => 'true'})
19
+ client.member?('', 'http://user.myid.net').should be_true
20
+ end
21
+
22
+ it "should return member lists" do
23
+ sample = <<-EOF
24
+ <relations>
25
+ <relation>
26
+ <name>user1</name>
27
+ <openid>http://user1.myid.net/</openid>
28
+ <email>user1@mail.com</email>
29
+ </relation>
30
+ <relation>
31
+ <name>user2</name>
32
+ <openid>http://user2.myid.net/</openid>
33
+ <email>user2@mail.com</email>
34
+ </relation>
35
+ </relations>
36
+ EOF
37
+
38
+ client = GroupOpenID::Client.new
39
+ client.fetcher = fetcher(:head => {'x-myid-membership-location' => 'http://member.url'}, :get => sample)
40
+ members = client.members('')
41
+
42
+ members.length.should == 2
43
+ members[0].name.should == 'user1'
44
+ members[0].openid.should == 'http://user1.myid.net/'
45
+ members[0].email.should == 'user1@mail.com'
46
+ end
47
+
48
+ def fetcher(ret)
49
+ fetcher = mock('Fetcher')
50
+ ret.each { |k,v| fetcher.should_receive(k).any_number_of_times.with(any_args).and_return(v) }
51
+ fetcher
52
+ end
53
+ end
@@ -0,0 +1,727 @@
1
+ # original source from rest-open-uri.rubyforge.org
2
+ #
3
+ # modified by deepblue
4
+ # * set verify_mode NONE because myid.net's root ca can't be verified in many cases.
5
+ # * We should fix this problem in the near future
6
+
7
+ # This is a hack of (and drop-in replacement for) open-uri that
8
+ # supports entity-bodies, and HTTP methods other than GET. This makes
9
+ # it easy to build clients for REST web services.
10
+
11
+ require 'uri'
12
+ require 'stringio'
13
+ require 'time'
14
+ require 'net/http'
15
+
16
+ module Kernel
17
+ private
18
+ alias open_uri_original_open open # :nodoc:
19
+
20
+ # makes possible to open various resources including URIs.
21
+ # If the first argument respond to `open' method,
22
+ # the method is called with the rest arguments.
23
+ #
24
+ # If the first argument is a string which begins with xxx://,
25
+ # it is parsed by URI.parse. If the parsed object respond to `open' method,
26
+ # the method is called with the rest arguments.
27
+ #
28
+ # Otherwise original open is called.
29
+ #
30
+ # Since open-uri.rb provides URI::HTTP#open, URI::HTTPS#open and
31
+ # URI::FTP#open,
32
+ # Kernel[#.]open can accepts such URIs and strings which begins with
33
+ # http://, https:// and ftp://.
34
+ # In these case, the opened file object is extended by OpenURI::Meta.
35
+ def open(name, *rest, &block) # :doc:
36
+ if name.respond_to?(:open)
37
+ name.open(*rest, &block)
38
+ elsif name.respond_to?(:to_str) &&
39
+ %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://} =~ name &&
40
+ (uri = URI.parse(name)).respond_to?(:open)
41
+ uri.open(*rest, &block)
42
+ else
43
+ open_uri_original_open(name, *rest, &block)
44
+ end
45
+ end
46
+ module_function :open
47
+ end
48
+
49
+ # OpenURI is an easy-to-use wrapper for net/http, net/https and net/ftp.
50
+ #
51
+ #== Example
52
+ #
53
+ # It is possible to open http/https/ftp URL as usual like opening a file:
54
+ #
55
+ # open("http://www.ruby-lang.org/") {|f|
56
+ # f.each_line {|line| p line}
57
+ # }
58
+ #
59
+ # The opened file has several methods for meta information as follows since
60
+ # it is extended by OpenURI::Meta.
61
+ #
62
+ # open("http://www.ruby-lang.org/en") {|f|
63
+ # f.each_line {|line| p line}
64
+ # p f.base_uri # <URI::HTTP:0x40e6ef2 URL:http://www.ruby-lang.org/en/>
65
+ # p f.content_type # "text/html"
66
+ # p f.charset # "iso-8859-1"
67
+ # p f.content_encoding # []
68
+ # p f.last_modified # Thu Dec 05 02:45:02 UTC 2002
69
+ # }
70
+ #
71
+ # Additional header fields can be specified by an optional hash argument.
72
+ #
73
+ # open("http://www.ruby-lang.org/en/",
74
+ # "User-Agent" => "Ruby/#{RUBY_VERSION}",
75
+ # "From" => "foo@bar.invalid",
76
+ # "Referer" => "http://www.ruby-lang.org/") {|f|
77
+ # # ...
78
+ # }
79
+ #
80
+ # The environment variables such as http_proxy, https_proxy and ftp_proxy
81
+ # are in effect by default. :proxy => nil disables proxy.
82
+ #
83
+ # open("http://www.ruby-lang.org/en/raa.html", :proxy => nil) {|f|
84
+ # # ...
85
+ # }
86
+ #
87
+ # URI objects can be opened in a similar way.
88
+ #
89
+ # uri = URI.parse("http://www.ruby-lang.org/en/")
90
+ # uri.open {|f|
91
+ # # ...
92
+ # }
93
+ #
94
+ # URI objects can be read directly. The returned string is also extended by
95
+ # OpenURI::Meta.
96
+ #
97
+ # str = uri.read
98
+ # p str.base_uri
99
+ #
100
+ # Author:: Tanaka Akira <akr@m17n.org>
101
+
102
+ module OpenURI
103
+ Options = {
104
+ :proxy => true,
105
+ :method => true,
106
+ :progress_proc => true,
107
+ :content_length_proc => true,
108
+ :http_basic_authentication => true,
109
+ :body => true
110
+ }
111
+
112
+ # xxx: I'd like a better way of representing this trivial mapping.
113
+ Methods = {
114
+ :copy => Net::HTTP::Copy,
115
+ :delete => Net::HTTP::Delete,
116
+ :get => Net::HTTP::Get,
117
+ :head => Net::HTTP::Head,
118
+ :lock => Net::HTTP::Lock,
119
+ :mkcol => Net::HTTP::Mkcol,
120
+ :options => Net::HTTP::Options,
121
+ :post => Net::HTTP::Post,
122
+ :propfind => Net::HTTP::Propfind,
123
+ :proppatch => Net::HTTP::Proppatch,
124
+ :put => Net::HTTP::Put,
125
+ :trace => Net::HTTP::Trace,
126
+ :unlock => Net::HTTP::Unlock,
127
+ }
128
+
129
+
130
+ def OpenURI.check_options(options) # :nodoc:
131
+ options.each {|k, v|
132
+ next unless Symbol === k
133
+ unless Options.include? k
134
+ raise ArgumentError, "unrecognized option: #{k}"
135
+ end
136
+ }
137
+
138
+ m = options[:method]
139
+ if m && !Methods[m]
140
+ raise ArgumentError, "unrecognized HTTP method symbol: #{m}"
141
+ end
142
+ end
143
+
144
+ def OpenURI.scan_open_optional_arguments(*rest) # :nodoc:
145
+ if !rest.empty? && (String === rest.first || Integer === rest.first)
146
+ mode = rest.shift
147
+ if !rest.empty? && Integer === rest.first
148
+ perm = rest.shift
149
+ end
150
+ end
151
+ return mode, perm, rest
152
+ end
153
+
154
+ def OpenURI.open_uri(name, *rest) # :nodoc:
155
+ uri = URI::Generic === name ? name : URI.parse(name)
156
+ mode, perm, rest = OpenURI.scan_open_optional_arguments(*rest)
157
+ options = rest.shift if !rest.empty? && Hash === rest.first
158
+ raise ArgumentError.new("extra arguments") if !rest.empty?
159
+ options ||= {}
160
+ OpenURI.check_options(options)
161
+
162
+ unless mode == nil ||
163
+ mode == 'r' || mode == 'rb' ||
164
+ mode == File::RDONLY
165
+ raise ArgumentError.new("invalid access mode #{mode} (#{uri.class} resource is read only.)")
166
+ end
167
+
168
+ io = open_loop(uri, options)
169
+ if block_given?
170
+ begin
171
+ yield io
172
+ ensure
173
+ io.close
174
+ end
175
+ else
176
+ io
177
+ end
178
+ end
179
+
180
+ def OpenURI.open_loop(uri, options) # :nodoc:
181
+ case opt_proxy = options.fetch(:proxy, true)
182
+ when true
183
+ find_proxy = lambda {|u| u.find_proxy}
184
+ when nil, false
185
+ find_proxy = lambda {|u| nil}
186
+ when String
187
+ opt_proxy = URI.parse(opt_proxy)
188
+ find_proxy = lambda {|u| opt_proxy}
189
+ when URI::Generic
190
+ find_proxy = lambda {|u| opt_proxy}
191
+ else
192
+ raise ArgumentError.new("Invalid proxy option: #{opt_proxy}")
193
+ end
194
+
195
+ uri_set = {}
196
+ buf = nil
197
+ while true
198
+ redirect = catch(:open_uri_redirect) {
199
+ buf = Buffer.new
200
+ uri.buffer_open(buf, find_proxy.call(uri), options)
201
+ nil
202
+ }
203
+ if redirect
204
+ if redirect.relative?
205
+ # Although it violates RFC2616, Location: field may have relative
206
+ # URI. It is converted to absolute URI using uri as a base URI.
207
+ redirect = uri + redirect
208
+ end
209
+ unless OpenURI.redirectable?(uri, redirect)
210
+ raise "redirection forbidden: #{uri} -> #{redirect}"
211
+ end
212
+ if options.include? :http_basic_authentication
213
+ # send authentication only for the URI directly specified.
214
+ options = options.dup
215
+ options.delete :http_basic_authentication
216
+ end
217
+ uri = redirect
218
+ raise "HTTP redirection loop: #{uri}" if uri_set.include? uri.to_s
219
+ uri_set[uri.to_s] = true
220
+ else
221
+ break
222
+ end
223
+ end
224
+ io = buf.io
225
+ io.base_uri = uri
226
+ io
227
+ end
228
+
229
+ def OpenURI.redirectable?(uri1, uri2) # :nodoc:
230
+ # This test is intended to forbid a redirection from http://... to
231
+ # file:///etc/passwd.
232
+ # However this is ad hoc. It should be extensible/configurable.
233
+ uri1.scheme.downcase == uri2.scheme.downcase ||
234
+ (/\A(?:http|ftp)\z/i =~ uri1.scheme && /\A(?:http|ftp)\z/i =~ uri2.scheme)
235
+ end
236
+
237
+ def OpenURI.open_http(buf, target, proxy, options) # :nodoc:
238
+ if proxy
239
+ raise "Non-HTTP proxy URI: #{proxy}" if proxy.class != URI::HTTP
240
+ end
241
+
242
+ if target.userinfo && "1.9.0" <= RUBY_VERSION
243
+ # don't raise for 1.8 because compatibility.
244
+ raise ArgumentError, "userinfo not supported. [RFC3986]"
245
+ end
246
+
247
+ klass = Net::HTTP
248
+ if URI::HTTP === target
249
+ # HTTP or HTTPS
250
+ if proxy
251
+ klass = Net::HTTP::Proxy(proxy.host, proxy.port)
252
+ end
253
+ target_host = target.host
254
+ target_port = target.port
255
+ request_uri = target.request_uri
256
+ else
257
+ # FTP over HTTP proxy
258
+ target_host = proxy.host
259
+ target_port = proxy.port
260
+ request_uri = target.to_s
261
+ end
262
+
263
+ http = klass.new(target_host, target_port)
264
+ if target.class == URI::HTTPS
265
+ require 'net/https'
266
+ http.use_ssl = true
267
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE # modified by deepblue
268
+ store = OpenSSL::X509::Store.new
269
+ store.set_default_paths
270
+ http.cert_store = store
271
+ end
272
+
273
+ header = {}
274
+ options.each {|k, v| header[k] = v if String === k }
275
+
276
+ resp = nil
277
+ http.start {
278
+ if target.class == URI::HTTPS
279
+ # xxx: information hiding violation
280
+ sock = http.instance_variable_get(:@socket)
281
+ if sock.respond_to?(:io)
282
+ sock = sock.io # 1.9
283
+ else
284
+ sock = sock.instance_variable_get(:@socket) # 1.8
285
+ end
286
+ sock.post_connection_check(target_host)
287
+ end
288
+
289
+ methodclass = Methods[options[:method] || :get]
290
+ req = methodclass.new(request_uri, header)
291
+ req.body = options[:body] if methodclass::REQUEST_HAS_BODY
292
+
293
+ if options.include? :http_basic_authentication
294
+ user, pass = options[:http_basic_authentication]
295
+ req.basic_auth user, pass
296
+ end
297
+ http.request(req) {|response|
298
+ resp = response
299
+ if options[:content_length_proc] && Net::HTTPSuccess === resp
300
+ if resp.key?('Content-Length')
301
+ options[:content_length_proc].call(resp['Content-Length'].to_i)
302
+ else
303
+ options[:content_length_proc].call(nil)
304
+ end
305
+ end
306
+ resp.read_body {|str|
307
+ buf << str
308
+ if options[:progress_proc] && Net::HTTPSuccess === resp
309
+ options[:progress_proc].call(buf.size)
310
+ end
311
+ }
312
+ }
313
+ }
314
+ io = buf.io
315
+ io.rewind
316
+ io.status = [resp.code, resp.message]
317
+ resp.each {|name,value| buf.io.meta_add_field name, value }
318
+ case resp
319
+ when Net::HTTPSuccess
320
+ when Net::HTTPMovedPermanently, # 301
321
+ Net::HTTPFound, # 302
322
+ Net::HTTPSeeOther, # 303
323
+ Net::HTTPTemporaryRedirect # 307
324
+ throw :open_uri_redirect, URI.parse(resp['location'])
325
+ else
326
+ raise OpenURI::HTTPError.new(io.status.join(' '), io)
327
+ end
328
+ end
329
+
330
+ class HTTPError < StandardError
331
+ def initialize(message, io)
332
+ super(message)
333
+ @io = io
334
+ end
335
+ attr_reader :io
336
+ end
337
+
338
+ class Buffer # :nodoc:
339
+ def initialize
340
+ @io = StringIO.new
341
+ @size = 0
342
+ end
343
+ attr_reader :size
344
+
345
+ StringMax = 10240
346
+ def <<(str)
347
+ @io << str
348
+ @size += str.length
349
+ if StringIO === @io && StringMax < @size
350
+ require 'tempfile'
351
+ io = Tempfile.new('open-uri')
352
+ io.binmode
353
+ Meta.init io, @io if @io.respond_to? :meta
354
+ io << @io.string
355
+ @io = io
356
+ end
357
+ end
358
+
359
+ def io
360
+ Meta.init @io unless @io.respond_to? :meta
361
+ @io
362
+ end
363
+ end
364
+
365
+ # Mixin for holding meta-information.
366
+ module Meta
367
+ def Meta.init(obj, src=nil) # :nodoc:
368
+ obj.extend Meta
369
+ obj.instance_eval {
370
+ @base_uri = nil
371
+ @meta = {}
372
+ }
373
+ if src
374
+ obj.status = src.status
375
+ obj.base_uri = src.base_uri
376
+ src.meta.each {|name, value|
377
+ obj.meta_add_field(name, value)
378
+ }
379
+ end
380
+ end
381
+
382
+ # returns an Array which consists status code and message.
383
+ attr_accessor :status
384
+
385
+ # returns a URI which is base of relative URIs in the data.
386
+ # It may differ from the URI supplied by a user because redirection.
387
+ attr_accessor :base_uri
388
+
389
+ # returns a Hash which represents header fields.
390
+ # The Hash keys are downcased for canonicalization.
391
+ attr_reader :meta
392
+
393
+ def meta_add_field(name, value) # :nodoc:
394
+ @meta[name.downcase] = value
395
+ end
396
+
397
+ # returns a Time which represents Last-Modified field.
398
+ def last_modified
399
+ if v = @meta['last-modified']
400
+ Time.httpdate(v)
401
+ else
402
+ nil
403
+ end
404
+ end
405
+
406
+ RE_LWS = /[\r\n\t ]+/n
407
+ RE_TOKEN = %r{[^\x00- ()<>@,;:\\"/\[\]?={}\x7f]+}n
408
+ RE_QUOTED_STRING = %r{"(?:[\r\n\t !#-\[\]-~\x80-\xff]|\\[\x00-\x7f])*"}n
409
+ RE_PARAMETERS = %r{(?:;#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?=#{RE_LWS}?(?:#{RE_TOKEN}|#{RE_QUOTED_STRING})#{RE_LWS}?)*}n
410
+
411
+ def content_type_parse # :nodoc:
412
+ v = @meta['content-type']
413
+ # The last (?:;#{RE_LWS}?)? matches extra ";" which violates RFC2045.
414
+ if v && %r{\A#{RE_LWS}?(#{RE_TOKEN})#{RE_LWS}?/(#{RE_TOKEN})#{RE_LWS}?(#{RE_PARAMETERS})(?:;#{RE_LWS}?)?\z}no =~ v
415
+ type = $1.downcase
416
+ subtype = $2.downcase
417
+ parameters = []
418
+ $3.scan(/;#{RE_LWS}?(#{RE_TOKEN})#{RE_LWS}?=#{RE_LWS}?(?:(#{RE_TOKEN})|(#{RE_QUOTED_STRING}))/no) {|att, val, qval|
419
+ val = qval.gsub(/[\r\n\t !#-\[\]-~\x80-\xff]+|(\\[\x00-\x7f])/) { $1 ? $1[1,1] : $& } if qval
420
+ parameters << [att.downcase, val]
421
+ }
422
+ ["#{type}/#{subtype}", *parameters]
423
+ else
424
+ nil
425
+ end
426
+ end
427
+
428
+ # returns "type/subtype" which is MIME Content-Type.
429
+ # It is downcased for canonicalization.
430
+ # Content-Type parameters are stripped.
431
+ def content_type
432
+ type, *parameters = content_type_parse
433
+ type || 'application/octet-stream'
434
+ end
435
+
436
+ # returns a charset parameter in Content-Type field.
437
+ # It is downcased for canonicalization.
438
+ #
439
+ # If charset parameter is not given but a block is given,
440
+ # the block is called and its result is returned.
441
+ # It can be used to guess charset.
442
+ #
443
+ # If charset parameter and block is not given,
444
+ # nil is returned except text type in HTTP.
445
+ # In that case, "iso-8859-1" is returned as defined by RFC2616 3.7.1.
446
+ def charset
447
+ type, *parameters = content_type_parse
448
+ if pair = parameters.assoc('charset')
449
+ pair.last.downcase
450
+ elsif block_given?
451
+ yield
452
+ elsif type && %r{\Atext/} =~ type &&
453
+ @base_uri && /\Ahttp\z/i =~ @base_uri.scheme
454
+ "iso-8859-1" # RFC2616 3.7.1
455
+ else
456
+ nil
457
+ end
458
+ end
459
+
460
+ # returns a list of encodings in Content-Encoding field
461
+ # as an Array of String.
462
+ # The encodings are downcased for canonicalization.
463
+ def content_encoding
464
+ v = @meta['content-encoding']
465
+ if v && %r{\A#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?(?:,#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?)*}o =~ v
466
+ v.scan(RE_TOKEN).map {|content_coding| content_coding.downcase}
467
+ else
468
+ []
469
+ end
470
+ end
471
+ end
472
+
473
+ # Mixin for HTTP and FTP URIs.
474
+ module OpenRead
475
+ # OpenURI::OpenRead#open provides `open' for URI::HTTP and URI::FTP.
476
+ #
477
+ # OpenURI::OpenRead#open takes optional 3 arguments as:
478
+ # OpenURI::OpenRead#open([mode [, perm]] [, options]) [{|io| ... }]
479
+ #
480
+ # `mode', `perm' is same as Kernel#open.
481
+ #
482
+ # However, `mode' must be read mode because OpenURI::OpenRead#open doesn't
483
+ # support write mode (yet).
484
+ # Also `perm' is just ignored because it is meaningful only for file
485
+ # creation.
486
+ #
487
+ # `options' must be a hash.
488
+ #
489
+ # Each pairs which key is a string in the hash specify a extra header
490
+ # field for HTTP.
491
+ # I.e. it is ignored for FTP without HTTP proxy.
492
+ #
493
+ # The hash may include other options which key is a symbol:
494
+ #
495
+ # [:proxy]
496
+ # Synopsis:
497
+ # :proxy => "http://proxy.foo.com:8000/"
498
+ # :proxy => URI.parse("http://proxy.foo.com:8000/")
499
+ # :proxy => true
500
+ # :proxy => false
501
+ # :proxy => nil
502
+ #
503
+ # If :proxy option is specified, the value should be String, URI,
504
+ # boolean or nil.
505
+ # When String or URI is given, it is treated as proxy URI.
506
+ # When true is given or the option itself is not specified,
507
+ # environment variable `scheme_proxy' is examined.
508
+ # `scheme' is replaced by `http', `https' or `ftp'.
509
+ # When false or nil is given, the environment variables are ignored and
510
+ # connection will be made to a server directly.
511
+ #
512
+ # [:http_basic_authentication]
513
+ # Synopsis:
514
+ # :http_basic_authentication=>[user, password]
515
+ #
516
+ # If :http_basic_authentication is specified,
517
+ # the value should be an array which contains 2 strings:
518
+ # username and password.
519
+ # It is used for HTTP Basic authentication defined by RFC 2617.
520
+ #
521
+ # [:content_length_proc]
522
+ # Synopsis:
523
+ # :content_length_proc => lambda {|content_length| ... }
524
+ #
525
+ # If :content_length_proc option is specified, the option value procedure
526
+ # is called before actual transfer is started.
527
+ # It takes one argument which is expected content length in bytes.
528
+ #
529
+ # If two or more transfer is done by HTTP redirection, the procedure
530
+ # is called only one for a last transfer.
531
+ #
532
+ # When expected content length is unknown, the procedure is called with
533
+ # nil.
534
+ # It is happen when HTTP response has no Content-Length header.
535
+ #
536
+ # [:progress_proc]
537
+ # Synopsis:
538
+ # :progress_proc => lambda {|size| ...}
539
+ #
540
+ # If :progress_proc option is specified, the proc is called with one
541
+ # argument each time when `open' gets content fragment from network.
542
+ # The argument `size' `size' is a accumulated transfered size in bytes.
543
+ #
544
+ # If two or more transfer is done by HTTP redirection, the procedure
545
+ # is called only one for a last transfer.
546
+ #
547
+ # :progress_proc and :content_length_proc are intended to be used for
548
+ # progress bar.
549
+ # For example, it can be implemented as follows using Ruby/ProgressBar.
550
+ #
551
+ # pbar = nil
552
+ # open("http://...",
553
+ # :content_length_proc => lambda {|t|
554
+ # if t && 0 < t
555
+ # pbar = ProgressBar.new("...", t)
556
+ # pbar.file_transfer_mode
557
+ # end
558
+ # },
559
+ # :progress_proc => lambda {|s|
560
+ # pbar.set s if pbar
561
+ # }) {|f| ... }
562
+ #
563
+ # OpenURI::OpenRead#open returns an IO like object if block is not given.
564
+ # Otherwise it yields the IO object and return the value of the block.
565
+ # The IO object is extended with OpenURI::Meta.
566
+ def open(*rest, &block)
567
+ OpenURI.open_uri(self, *rest, &block)
568
+ end
569
+
570
+ # OpenURI::OpenRead#read([options]) reads a content referenced by self and
571
+ # returns the content as string.
572
+ # The string is extended with OpenURI::Meta.
573
+ # The argument `options' is same as OpenURI::OpenRead#open.
574
+ def read(options={})
575
+ self.open(options) {|f|
576
+ str = f.read
577
+ Meta.init str, f
578
+ str
579
+ }
580
+ end
581
+ end
582
+ end
583
+
584
+ module URI
585
+ class Generic
586
+ # returns a proxy URI.
587
+ # The proxy URI is obtained from environment variables such as http_proxy,
588
+ # ftp_proxy, no_proxy, etc.
589
+ # If there is no proper proxy, nil is returned.
590
+ #
591
+ # Note that capitalized variables (HTTP_PROXY, FTP_PROXY, NO_PROXY, etc.)
592
+ # are examined too.
593
+ #
594
+ # But http_proxy and HTTP_PROXY is treated specially under CGI environment.
595
+ # It's because HTTP_PROXY may be set by Proxy: header.
596
+ # So HTTP_PROXY is not used.
597
+ # http_proxy is not used too if the variable is case insensitive.
598
+ # CGI_HTTP_PROXY can be used instead.
599
+ def find_proxy
600
+ name = self.scheme.downcase + '_proxy'
601
+ proxy_uri = nil
602
+ if name == 'http_proxy' && ENV.include?('REQUEST_METHOD') # CGI?
603
+ # HTTP_PROXY conflicts with *_proxy for proxy settings and
604
+ # HTTP_* for header information in CGI.
605
+ # So it should be careful to use it.
606
+ pairs = ENV.reject {|k, v| /\Ahttp_proxy\z/i !~ k }
607
+ case pairs.length
608
+ when 0 # no proxy setting anyway.
609
+ proxy_uri = nil
610
+ when 1
611
+ k, v = pairs.shift
612
+ if k == 'http_proxy' && ENV[k.upcase] == nil
613
+ # http_proxy is safe to use because ENV is case sensitive.
614
+ proxy_uri = ENV[name]
615
+ else
616
+ proxy_uri = nil
617
+ end
618
+ else # http_proxy is safe to use because ENV is case sensitive.
619
+ proxy_uri = ENV[name]
620
+ end
621
+ if !proxy_uri
622
+ # Use CGI_HTTP_PROXY. cf. libwww-perl.
623
+ proxy_uri = ENV["CGI_#{name.upcase}"]
624
+ end
625
+ elsif name == 'http_proxy'
626
+ unless proxy_uri = ENV[name]
627
+ if proxy_uri = ENV[name.upcase]
628
+ warn 'The environment variable HTTP_PROXY is discouraged. Use http_proxy.'
629
+ end
630
+ end
631
+ else
632
+ proxy_uri = ENV[name] || ENV[name.upcase]
633
+ end
634
+
635
+ if proxy_uri && self.host
636
+ require 'socket'
637
+ begin
638
+ addr = IPSocket.getaddress(self.host)
639
+ proxy_uri = nil if /\A127\.|\A::1\z/ =~ addr
640
+ rescue SocketError
641
+ end
642
+ end
643
+
644
+ if proxy_uri
645
+ proxy_uri = URI.parse(proxy_uri)
646
+ name = 'no_proxy'
647
+ if no_proxy = ENV[name] || ENV[name.upcase]
648
+ no_proxy.scan(/([^:,]*)(?::(\d+))?/) {|host, port|
649
+ if /(\A|\.)#{Regexp.quote host}\z/i =~ self.host &&
650
+ (!port || self.port == port.to_i)
651
+ proxy_uri = nil
652
+ break
653
+ end
654
+ }
655
+ end
656
+ proxy_uri
657
+ else
658
+ nil
659
+ end
660
+ end
661
+ end
662
+
663
+ class HTTP
664
+ def buffer_open(buf, proxy, options) # :nodoc:
665
+ OpenURI.open_http(buf, self, proxy, options)
666
+ end
667
+
668
+ include OpenURI::OpenRead
669
+ end
670
+
671
+ class FTP
672
+ def buffer_open(buf, proxy, options) # :nodoc:
673
+ if proxy
674
+ OpenURI.open_http(buf, self, proxy, options)
675
+ return
676
+ end
677
+ require 'net/ftp'
678
+
679
+ directories = self.path.split(%r{/}, -1)
680
+ directories.shift if directories[0] == '' # strip a field before leading slash
681
+ directories.each {|d|
682
+ d.gsub!(/%([0-9A-Fa-f][0-9A-Fa-f])/) { [$1].pack("H2") }
683
+ }
684
+ unless filename = directories.pop
685
+ raise ArgumentError, "no filename: #{self.inspect}"
686
+ end
687
+ directories.each {|d|
688
+ if /[\r\n]/ =~ d
689
+ raise ArgumentError, "invalid directory: #{d.inspect}"
690
+ end
691
+ }
692
+ if /[\r\n]/ =~ filename
693
+ raise ArgumentError, "invalid filename: #{filename.inspect}"
694
+ end
695
+ typecode = self.typecode
696
+ if typecode && /\A[aid]\z/ !~ typecode
697
+ raise ArgumentError, "invalid typecode: #{typecode.inspect}"
698
+ end
699
+
700
+ # The access sequence is defined by RFC 1738
701
+ ftp = Net::FTP.open(self.host)
702
+ # todo: extract user/passwd from .netrc.
703
+ user = 'anonymous'
704
+ passwd = nil
705
+ user, passwd = self.userinfo.split(/:/) if self.userinfo
706
+ ftp.login(user, passwd)
707
+ directories.each {|cwd|
708
+ ftp.voidcmd("CWD #{cwd}")
709
+ }
710
+ if typecode
711
+ # xxx: typecode D is not handled.
712
+ ftp.voidcmd("TYPE #{typecode.upcase}")
713
+ end
714
+ if options[:content_length_proc]
715
+ options[:content_length_proc].call(ftp.size(filename))
716
+ end
717
+ ftp.retrbinary("RETR #{filename}", 4096) { |str|
718
+ buf << str
719
+ options[:progress_proc].call(buf.size) if options[:progress_proc]
720
+ }
721
+ ftp.close
722
+ buf.io.rewind
723
+ end
724
+
725
+ include OpenURI::OpenRead
726
+ end
727
+ end
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.4
3
+ specification_version: 1
4
+ name: group_open_id
5
+ version: !ruby/object:Gem::Version
6
+ version: "0.1"
7
+ date: 2007-08-08 00:00:00 +09:00
8
+ summary: Wrapper library for myID.net's Group ID API
9
+ require_paths:
10
+ - lib
11
+ email: byblue@gmail.com
12
+ homepage:
13
+ rubyforge_project: springnote
14
+ description: "== DESCRIPTION: Wrapper library for myID.net's Group ID API == FEATURES/PROBLEMS: * TBD == SYNOPSIS: require 'group_open_id' # Initialize a client GroupOpenID::Client.app_key = 'your_application_key' client = GroupOpenID::Client.new('user_open_id_url', 'user_key') group_id = GroupOpenID::URI.new('http://ruby.myid.net', client) # Get the membership location puts group_id.membership_location # => 'http://some.url/' # Get member lists puts group_id.members # => array of GroupOpenID::Member # Determine where a given open_id is the member of a group id puts group_id.member?('http://deepblue.myid.net') # => true"
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Bryan Kang
31
+ files:
32
+ - History.txt
33
+ - Manifest.txt
34
+ - README.txt
35
+ - Rakefile
36
+ - lib/group_open_id.rb
37
+ - lib/group_open_id/client.rb
38
+ - lib/group_open_id/fetcher.rb
39
+ - lib/group_open_id/uri.rb
40
+ - spec/group_open_id_spec.rb
41
+ - vendor/rest-open-uri.rb
42
+ test_files: []
43
+
44
+ rdoc_options:
45
+ - --main
46
+ - README.txt
47
+ extra_rdoc_files:
48
+ - History.txt
49
+ - Manifest.txt
50
+ - README.txt
51
+ executables: []
52
+
53
+ extensions: []
54
+
55
+ requirements: []
56
+
57
+ dependencies:
58
+ - !ruby/object:Gem::Dependency
59
+ name: hpricot
60
+ version_requirement:
61
+ version_requirements: !ruby/object:Gem::Version::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: "0.6"
66
+ version:
67
+ - !ruby/object:Gem::Dependency
68
+ name: hoe
69
+ version_requirement:
70
+ version_requirements: !ruby/object:Gem::Version::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: 1.2.2
75
+ version: