group_open_id 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: