discodactyl 0.3.0
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/AUTHORS +1 -0
- data/CHANGELOG +452 -0
- data/COPYING +662 -0
- data/Gemfile +8 -0
- data/INSTALL +26 -0
- data/MANIFEST +30 -0
- data/NEWS +31 -0
- data/README +63 -0
- data/Rakefile +23 -0
- data/TODO +24 -0
- data/bin/webfinger +62 -0
- data/discodactyl.gemspec +23 -0
- data/lib/discodactyl.rb +9 -0
- data/lib/discodactyl/acct_uri.rb +82 -0
- data/lib/discodactyl/host_meta.rb +34 -0
- data/lib/discodactyl/link_header.rb +45 -0
- data/lib/discodactyl/resource_discovery.rb +102 -0
- data/lib/discodactyl/uri_template.rb +32 -0
- data/lib/discodactyl/xrd.rb +2 -0
- data/lib/discodactyl/xrd/document.rb +75 -0
- data/lib/discodactyl/xrd/link.rb +47 -0
- data/test/test_acct_uri.rb +57 -0
- data/test/test_helper.rb +17 -0
- data/test/test_host_meta.rb +37 -0
- data/test/test_link_header.rb +32 -0
- data/test/test_resource_discovery.rb +65 -0
- data/test/test_uri_template.rb +32 -0
- data/test/test_xrd_append.rb +93 -0
- data/test/test_xrd_link_parse.rb +97 -0
- data/test/test_xrd_parse.rb +146 -0
- metadata +164 -0
data/Gemfile
ADDED
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
|
data/discodactyl.gemspec
ADDED
@@ -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
|
data/lib/discodactyl.rb
ADDED
@@ -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
|