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/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