127 lines
3.8 KiB
Ruby
127 lines
3.8 KiB
Ruby
require File.join(Rails.root, 'lib/hcard')
|
|
require File.join(Rails.root, 'lib/webfinger_profile')
|
|
|
|
class EMWebfinger
|
|
TIMEOUT = 5
|
|
REDIRECTS = 3
|
|
OPTS = {:timeout => TIMEOUT, :redirects => REDIRECTS}
|
|
def initialize(account)
|
|
@account = account.strip.gsub('acct:','').to_s
|
|
@callbacks = []
|
|
@ssl = true
|
|
Rails.logger.info("event=EMWebfinger status=initialized target=#{account}")
|
|
# Raise an error if identifier has a port number
|
|
#raise "Identifier is invalid" if(@account.strip.match(/\:\d+$/))
|
|
# Raise an error if identifier is not a valid email (generous regexp)
|
|
#raise "Identifier is invalid" if !(@account=~ /^[a-zA-Z][\w\.-]*[a-zA-Z0-9]@[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]$/)
|
|
end
|
|
def fetch
|
|
if @callbacks.empty?
|
|
Rails.logger.info("event=EMWebfinger status=abort target=#{@account} callbacks=empty")
|
|
raise 'you need to set a callback before calling fetch'
|
|
end
|
|
person = Person.by_account_identifier(@account)
|
|
if person
|
|
Rails.logger.info("event=EMWebfinger status=local target=#{@account}")
|
|
process_callbacks person
|
|
else
|
|
Rails.logger.info("event=EMWebfinger status=remote target=#{@account}")
|
|
get_xrd
|
|
end
|
|
end
|
|
|
|
def on_person(&block)
|
|
@callbacks << block
|
|
self.fetch
|
|
end
|
|
|
|
private
|
|
|
|
def get_xrd
|
|
http = EventMachine::HttpRequest.new(xrd_url).get OPTS
|
|
http.callback {
|
|
profile_url = webfinger_profile_url(http.response)
|
|
if profile_url
|
|
get_webfinger_profile(profile_url)
|
|
elsif @ssl
|
|
@ssl = false
|
|
get_xrd
|
|
else
|
|
process_callbacks I18n.t('webfinger.not_enabled', :account => @account)
|
|
end
|
|
}
|
|
|
|
http.errback {
|
|
if @ssl
|
|
@ssl = false
|
|
get_xrd
|
|
else
|
|
process_callbacks I18n.t('webfinger.xrd_fetch_failed', :account => @account)
|
|
end }
|
|
end
|
|
|
|
|
|
def get_webfinger_profile(profile_url)
|
|
http = EventMachine::HttpRequest.new(profile_url).get OPTS
|
|
http.callback{ make_person_from_webfinger(http.response) }
|
|
http.errback{ process_callbacks I18n.t('webfinger.fetch_failed', :profile_url => profile_url) }
|
|
end
|
|
|
|
def make_person_from_webfinger(webfinger_profile)
|
|
unless webfinger_profile.strip == ""
|
|
|
|
begin
|
|
wf_profile = WebfingerProfile.new(@account, webfinger_profile)
|
|
|
|
|
|
http = EventMachine::HttpRequest.new(wf_profile.hcard).get OPTS
|
|
http.callback{
|
|
begin
|
|
hcard = HCard.build http.response
|
|
p = Person.build_from_webfinger(wf_profile, hcard)
|
|
process_callbacks(p)
|
|
rescue
|
|
process_callbacks I18n.t 'webfinger.no_person_constructed'
|
|
end
|
|
}
|
|
http.errback{
|
|
process_callbacks I18n.t('webfinger.hcard_fetch_failed', :account => @account) }
|
|
|
|
rescue
|
|
process_callbacks "No person could be constructed from this webfinger profile."
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
def process_callbacks(person)
|
|
Rails.logger.info("event=EMWebfinger status=callbacks_started target=#{@account} response='#{person.is_a?(String) ? person : person.id}'")
|
|
@callbacks.each { |c|
|
|
begin
|
|
c.call(person)
|
|
rescue Exception => e
|
|
Rails.logger.info("event=EMWebfinger status=error_on_callback error='#{e.inspect}'")
|
|
end
|
|
}
|
|
Rails.logger.info("event=EMWebfinger status=complete target=#{@account}")
|
|
end
|
|
|
|
|
|
##helpers
|
|
private
|
|
|
|
def webfinger_profile_url(xrd_response)
|
|
doc = Nokogiri::XML::Document.parse(xrd_response)
|
|
return nil if doc.namespaces["xmlns"] != "http://docs.oasis-open.org/ns/xri/xrd-1.0"
|
|
swizzle doc.at('Link[rel=lrdd]').attribute('template').value
|
|
end
|
|
|
|
def xrd_url
|
|
domain = @account.split('@')[1]
|
|
"http#{'s' if @ssl}://#{domain}/.well-known/host-meta"
|
|
end
|
|
|
|
def swizzle(template)
|
|
template.gsub '{uri}', @account
|
|
end
|
|
end
|