discodactyl 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source :rubygems
2
+
3
+ gem 'actionpack', '~>2.3.8'
4
+ gem 'feedzirra', '~>0.0.23'
5
+ gem 'mofo', '~>0.2.16'
6
+ gem 'nokogiri', '~>1.4.2'
7
+ gem 'rake', '~>0.8.7'
8
+ gem 'rr', '~>0.10.11'
data/INSTALL ADDED
@@ -0,0 +1,26 @@
1
+ = Installing Discodactyl
2
+
3
+ RubyGems is the preferred easy install method for Discodactyl.
4
+
5
+ == Installing the Gem
6
+
7
+ Discodactyl is intended to be installed via the
8
+ RubyGems[http://rubygems.org/] system. To get the latest
9
+ version, simply enter the following into your command prompt:
10
+
11
+ $ gem install discodactyl
12
+
13
+ You must have RubyGems[http://rubygems.org/] installed for
14
+ the above to work.
15
+
16
+ If you want to build the gem locally, run the following command:
17
+
18
+ $ gem build discodactyl.gemspec
19
+
20
+ == Running the Tests
21
+
22
+ If you would like to run Discodactyl's test suite on your system before
23
+ installing and you have Rake installed, just issue the following command from
24
+ the root of the project directory:
25
+
26
+ $ rake
data/MANIFEST ADDED
@@ -0,0 +1,30 @@
1
+ AUTHORS
2
+ CHANGELOG
3
+ COPYING
4
+ Gemfile
5
+ INSTALL
6
+ MANIFEST
7
+ NEWS
8
+ README
9
+ Rakefile
10
+ TODO
11
+ bin/webfinger
12
+ discodactyl.gemspec
13
+ lib/discodactyl.rb
14
+ lib/discodactyl/acct_uri.rb
15
+ lib/discodactyl/host_meta.rb
16
+ lib/discodactyl/link_header.rb
17
+ lib/discodactyl/resource_discovery.rb
18
+ lib/discodactyl/uri_template.rb
19
+ lib/discodactyl/xrd.rb
20
+ lib/discodactyl/xrd/document.rb
21
+ lib/discodactyl/xrd/link.rb
22
+ test/test_acct_uri.rb
23
+ test/test_helper.rb
24
+ test/test_host_meta.rb
25
+ test/test_link_header.rb
26
+ test/test_resource_discovery.rb
27
+ test/test_uri_template.rb
28
+ test/test_xrd_append.rb
29
+ test/test_xrd_link_parse.rb
30
+ test/test_xrd_parse.rb
data/NEWS ADDED
@@ -0,0 +1,31 @@
1
+ === 0.3.0 / 2010-07-08
2
+
3
+ New Features
4
+
5
+ * Gem installation
6
+ * XRD support for adding & removing links
7
+ * Useful, plain text output from webfinger script
8
+
9
+ Breaking Changes
10
+
11
+ * Webfinger script no longer outputs raw XRD
12
+
13
+ === 0.2.0 / 2010-03-19
14
+
15
+ New Features
16
+
17
+ * URI.parse support for acct: URIs
18
+ * URI Template parsing
19
+ * XRD parsing and query support
20
+ * Host Meta and in-document discovery
21
+ * Webfinger example script
22
+
23
+ Breaking Changes
24
+
25
+ * XRD format has completely changed to comply with new specifications
26
+
27
+ === 0.0.0 / 2009-07-25
28
+
29
+ New Features
30
+
31
+ * Add Link Header discovery
data/README ADDED
@@ -0,0 +1,63 @@
1
+ = Discodactyl
2
+
3
+ * http://dactylo.us
4
+ * http://github.com/josephholsten/discodactyl
5
+ * http://josephholsten.lighthouseapp.com/projects/36040-discodactyl
6
+ * mailto:joseph@josephholsten.com
7
+
8
+ == DESCRIPTION:
9
+
10
+ Discodactyl is an experimental toolkit for XRD service discovery documents and related protocols. It includes implementations of XRD URITemplate Link-Patterns, basic site-meta support, HTTP Link header parsing, acct: URIs and a webfinger poking stick.
11
+
12
+ == FEATURES/PROBLEMS:
13
+
14
+ * Webfinger client.
15
+ * XRD parsing, querying and modification.
16
+ * LRDD discovery.
17
+ * HTTP Link header parsing.
18
+ * acct: URI parsing.
19
+ * Minimal URI Template parsing and application.
20
+ * Probably filled with bugs. Don't forget your bug powder.
21
+
22
+ == SYNOPSIS:
23
+
24
+ # All of WebFinger! In very few, if long and pointy, lines!
25
+ require 'discodactyl'
26
+
27
+ acct = URI.parse 'acct:bradfitz@gmail.com'
28
+
29
+ webfinger_uris = Discodactyl::ResourceDiscovery.get_uris_by_rel(acct, 'lrdd', 'uri' => acct)
30
+ disco_doc = Discodactyl::XRD::Document.parse(open(webfinger_uris.first))
31
+
32
+ == REQUIREMENTS:
33
+
34
+ * nokogiri 1.4
35
+ * actionpack 2.3
36
+ * feedzirra 0.0.23
37
+ * mofo 0.2
38
+
39
+ == INSTALLING:
40
+
41
+ See the INSTALL file for details.
42
+
43
+ == LICENSE:
44
+
45
+ This software is also available in a permissive license. Please contact Joseph Holsten <joseph@josephholsten.com> for more information.
46
+
47
+ (The GNU Affero General Public License)
48
+
49
+ Copyright (c) 2009, 2010 Joseph Anthony Pasquale Holsten
50
+
51
+ This program is free software: you can redistribute it and/or modify
52
+ it under the terms of the GNU Affero General Public License as published by
53
+ the Free Software Foundation, either version 3 of the License, or
54
+ (at your option) any later version.
55
+
56
+ This program is distributed in the hope that it will be useful,
57
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
58
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
59
+ GNU Affero General Public License for more details.
60
+
61
+ You should have received a copy of the GNU Affero General Public License
62
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
63
+
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require "rake/testtask"
4
+ Rake::TestTask.new do |test|
5
+ test.test_files = Dir['test/**/test_*.rb']
6
+ test.verbose = true
7
+ end
8
+ task :default => :test
9
+
10
+ require "rake/clean"
11
+ file 'MANIFEST.tmp' do
12
+ sh %{find . -type f | sed 's/\\.\\///' | grep -v '.git' | sort > MANIFEST.tmp}
13
+ end
14
+ CLEAN << 'MANIFEST.tmp'
15
+
16
+ desc "Check the manifest against current files"
17
+ task :check_manifest => [:clean, 'MANIFEST', 'MANIFEST.tmp'] do
18
+ puts `diff -du MANIFEST MANIFEST.tmp`
19
+ end
20
+
21
+ CLEAN << '.rake_tasks'
22
+
23
+ # vim: syntax=ruby
data/TODO ADDED
@@ -0,0 +1,24 @@
1
+ * release as rip
2
+ * write man page webfinger.1
3
+ * add CONTRIBUTING
4
+ * play nice with gem man
5
+ * semver
6
+ * document everything as tested
7
+ * nodoc everything without docs
8
+ * see if redfinger's docs map at all
9
+ * prettify the script output
10
+ * ensure security
11
+ * check hm:Host
12
+ * check final /XRD/Subject
13
+ * test 3** redirects
14
+ * REXML support
15
+ * collect common rel values into a enum module
16
+ * put license info in every file
17
+
18
+ Reference Specs
19
+ * host meta http://tools.ietf.org/html/draft-hammer-hostmeta-05
20
+ * webfinger
21
+ * xrd http://www.oasis-open.org/committees/download.php/36542/xrd-1.0-wd15.html
22
+ * lrdd http://tools.ietf.org/html/draft-hammer-discovery-04
23
+ * link header http://tools.ietf.org/html/draft-nottingham-http-link-header-08
24
+ * versioning-rels http://tools.ietf.org/html/draft-brown-versioning-link-relations-07
data/bin/webfinger ADDED
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env ruby
2
+ libdir = File.expand_path('../../lib', __FILE__)
3
+ $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
4
+
5
+ require 'discodactyl'
6
+ require 'open-uri'
7
+ require 'action_view'
8
+ require 'mofo'
9
+ require 'feedzirra'
10
+
11
+ if ARGV.length == 0
12
+ puts "Usage: #{__FILE__} user@host"
13
+ exit 0
14
+ end
15
+
16
+ # parse identifier
17
+ acct = URI::ACCT.parse(ARGV[0])
18
+
19
+ # Perform LRDD discovery on acct with the webfinger relation
20
+ finger_rel = "lrdd"
21
+ begin
22
+ webfinger_uris = Discodactyl::ResourceDiscovery.get_uris_by_rel(acct, finger_rel, 'uri' => acct)
23
+ rescue Discodactyl::HostMetaHTTPError => error
24
+ if error.io.status[1] == 'Not Found'
25
+ abort "No host meta webfinger information found at #{error.io.base_uri}"
26
+ else
27
+ raise
28
+ end
29
+ end
30
+
31
+ abort "URI didn't have any linked webfinger URIs" if webfinger_uris.empty?
32
+
33
+ # retrieve user disco doc
34
+ disco_res = open(webfinger_uris.first)
35
+
36
+ # parse disco doc
37
+ disco = Discodactyl::XRD::Document.parse(disco_res)
38
+
39
+ hcard_uri = disco.uris_by_rel('http://microformats.org/profile/hcard').first
40
+ hcard = hCard.find(:first => hcard_uri)
41
+ if hcard
42
+ puts "Name: #{hcard.fn.gsub(/\s+/, ' ')}" if hcard.properties.include?('fn')
43
+ puts "Title: #{hcard.title}" if hcard.properties.include?('title')
44
+ puts "Organization: #{hcard.org}" if hcard.properties.include?('org')
45
+ end
46
+
47
+ openid = disco.uris_by_rel('http://specs.openid.net/auth/').first
48
+ puts "OpenID: #{openid}" if openid
49
+
50
+ profile = disco.uris_by_rel('http://webfinger.net/rel/profile-page').first
51
+ puts "Profile: #{profile}" if profile
52
+
53
+ contacts = disco.uris_by_rel('http://portablecontacts.net/spec/1.0').first
54
+ puts "Contacts: #{contacts}" if contacts
55
+
56
+ activities = disco.uris_by_rel('http://schemas.google.com/g/2010#updates-from').first
57
+ if activities
58
+ feed = Feedzirra::Feed.fetch_and_parse(activities)
59
+ entry = feed.entries.first
60
+ include ActionView::Helpers::DateHelper
61
+ puts "Status: #{Loofah::Helpers.strip_tags(entry.content)} #{time_ago_in_words(entry.published)} ago"
62
+ end
@@ -0,0 +1,23 @@
1
+ lib = File.expand_path("../lib/discodactyl.rb", __FILE__)
2
+ version = File.read(lib)[/^\s*VERSION\s*=\s*(['"])(\d\.\d\.\d)\1/, 2]
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = "discodactyl"
6
+ spec.author = "Joseph Anthony Pasquale Holsten"
7
+ spec.email = "joseph@josephholsten.com"
8
+ spec.homepage = "http://dactylo.us"
9
+ spec.description = %q{Discodactyl is an experimental toolkit for XRD service discovery documents and related protocols. It includes implementations of XRD URITemplate Link-Patterns, basic site-meta support, HTTP Link header parsing, acct: URIs and a webfinger poking stick.}
10
+ spec.extra_rdoc_files = %w[ AUTHORS CHANGELOG COPYING INSTALL NEWS README TODO ]
11
+ spec.rdoc_options << "--charset=UTF-8" <<
12
+ "--title" << "Discodactyl Documentation" <<
13
+ "--main" << "README"
14
+ spec.version = version
15
+ spec.summary = spec.description.split(/\.\s+/).first
16
+ spec.files = File.read("MANIFEST").split(/\r?\n\r?/)
17
+ spec.executables = spec.files.grep(/^bin/) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(/^test\/.*test_.*\.rb$/)
19
+ spec.add_runtime_dependency 'nokogiri', '~>1.4.0'
20
+ spec.add_runtime_dependency 'actionpack', '~>2.3.0'
21
+ spec.add_runtime_dependency 'feedzirra', '~>0.0.23'
22
+ spec.add_runtime_dependency 'mofo', '~>0.2.0'
23
+ end
@@ -0,0 +1,9 @@
1
+ require 'discodactyl/acct_uri'
2
+ require 'discodactyl/host_meta'
3
+ require 'discodactyl/resource_discovery'
4
+ require 'discodactyl/uri_template'
5
+ require 'discodactyl/xrd'
6
+
7
+ module Discodactyl # :nodoc:
8
+ VERSION = '0.3.0'
9
+ end
@@ -0,0 +1,82 @@
1
+ require 'uri'
2
+
3
+ module URI # :nodoc:
4
+ class ACCT < Generic
5
+
6
+
7
+ COMPONENT = [
8
+ :scheme,
9
+ :local_part, :host
10
+ ].freeze
11
+
12
+ def self.parse(str)
13
+ /^(acct:)?(.*)$/ =~ str
14
+ scheme = 'acct'
15
+ opaque = $2
16
+ self.new(scheme, nil, nil, nil, nil, nil, opaque, nil, nil)
17
+ end
18
+
19
+
20
+ #
21
+ # == Description
22
+ #
23
+ # Create a new URI::ACCT object from components, with syntax checking.
24
+ #
25
+ # The components accepted are local-part and host.
26
+ #
27
+ # The components should be provided either as an Array, or as a Hash
28
+ # with keys formed by preceding the component names with a colon.
29
+ #
30
+ # If an Array is used, the components must be passed in the order
31
+ # [local-part, host].
32
+ #
33
+ # Example:
34
+ #
35
+ # newuri = URI::ACCT.build({:host => 'example.com',
36
+ # :local_part> => 'bob'})
37
+ #
38
+ # newuri = URI::ACCT.build(['bob', "example.com"])
39
+ #
40
+ def self.build(args)
41
+ tmp = Util::make_components_hash(self, args)
42
+ if tmp[:local_part] && tmp[:host]
43
+ tmp[:opaque] = tmp[:local_part]+'@'+tmp[:host]
44
+ tmp[:host] = nil
45
+ end
46
+ return super(tmp)
47
+ end
48
+
49
+ def initialize(*arg)
50
+ super(*arg)
51
+ parse_opaque
52
+ end
53
+
54
+ def parse_opaque
55
+ if /^([^@]*)@(.*)$/ =~ @opaque
56
+ @local_part = $1
57
+ @host = $2
58
+ end
59
+ end
60
+
61
+ attr_reader :local_part
62
+
63
+ def set_local_part(val)
64
+ @local_part = val
65
+ end
66
+ protected :set_local_part
67
+
68
+ def to_s
69
+ "#{@scheme}:#{@local_part}@#{@host}"
70
+ end
71
+
72
+ def id
73
+ "#{@local_part}@#{@host}"
74
+ end
75
+
76
+ def heirarchical?
77
+ false
78
+ end
79
+ end
80
+
81
+ @@schemes['ACCT'] = ACCT
82
+ end
@@ -0,0 +1,34 @@
1
+ require 'discodactyl/xrd'
2
+ require 'open-uri'
3
+
4
+ module Discodactyl # :nodoc
5
+ # Convienient access to host metadata and individual resources controlled by
6
+ # the host. See also: http://tools.ietf.org/html/draft-hammer-hostmeta
7
+ class HostMeta < XRD::Document
8
+ class << self
9
+ # Take a URI and return the URI for its HostMeta document
10
+ def get_uri_from_uri(uri)
11
+ host = uri.respond_to?('host') ? uri.host : uri
12
+ URI.parse('http://'+ host + '/.well-known/host-meta')
13
+ end
14
+
15
+ # Take a URI and retrieve its HostMeta document
16
+ def from_uri(uri)
17
+ uri = get_uri_from_uri(uri)
18
+ begin
19
+ raw = uri.open
20
+ rescue OpenURI::HTTPError => error
21
+ if error.io.status[1] == 'Not Found'
22
+ error.io.base_uri = uri
23
+ raise HostMetaHTTPError.new(error.message, error.io)
24
+ else
25
+ raise
26
+ end
27
+ end
28
+ self.parse raw
29
+ end
30
+ end
31
+ end
32
+ class HostMetaHTTPError < OpenURI::HTTPError # :nodoc:
33
+ end
34
+ end
@@ -0,0 +1,45 @@
1
+ require 'active_support/core_ext/object/misc'
2
+
3
+ module Discodactyl # :nodoc:
4
+ # Access to web links stored in HTTP Link header-fields. See also:
5
+ # http://tools.ietf.org/html/draft-nottingham-http-link-header
6
+ class LinkHeader < Hash
7
+ class << self
8
+ # Take the link header value as a string and parse it into a Hash
9
+ #
10
+ # For example, the raw link header
11
+ # Link: </>; rel="http://example.net/foo"
12
+ # is parsed by saying
13
+ # link = LinkHeader.parse('</>; rel="http://example.net/foo"')
14
+ # link[:href] # '/'
15
+ # link[:rel] # ['http://example.net/foo']
16
+ def parse(string)
17
+ returning(params = LinkHeader.new) do
18
+ if string =~ /^<([^>]+)>(.*)$/
19
+ params[:href] = $1
20
+ $2.split(/;\s*/).each do |part|
21
+ param, value = part.split '='
22
+ value = value[1...-1] if value =~ /^".*"$/
23
+ if param =~ /rel/i
24
+ insert_name_into_hash(params, param.to_sym, value)
25
+ elsif !param.nil?
26
+ params[param.to_sym] = value
27
+ end
28
+ end
29
+ else
30
+ raise "malformed link header: #{string}"
31
+ end
32
+ end
33
+ end
34
+
35
+ private
36
+ def insert_name_into_hash(hash, name, value)
37
+ value = value.split(/\s+/)
38
+ hash[name] ||= []
39
+ hash[name] << value
40
+ hash[name].flatten!
41
+ hash
42
+ end
43
+ end
44
+ end
45
+ end