Fixes and general progress

shrike
Eugen Rochko 2016-02-22 18:10:30 +01:00
parent 709c6685a9
commit 1dad72bf13
15 changed files with 133 additions and 34 deletions

View File

@ -7,7 +7,7 @@ class XrdController < ApplicationController
def webfinger
@account = Account.find_by!(username: username_from_resource, domain: nil)
@canonical_account_uri = "acct:#{@account.username}#{LOCAL_DOMAIN}"
@canonical_account_uri = "acct:#{@account.username}@#{LOCAL_DOMAIN}"
@magic_key = pem_to_magic_key(@account.keypair.public_key)
end

View File

@ -1,12 +1,12 @@
module ApplicationHelper
include GrapeRouteHelpers::NamedRouteMatcher
include RoutingHelper
def unique_tag(date, id, type)
"tag:#{LOCAL_DOMAIN},#{date.strftime('%Y-%m-%d')}:objectId=#{id}:objectType=#{type}"
end
def subscription_url(account)
add_base_url_prefix subscription_path(id: account.id, format: '')
add_base_url_prefix subscriptions_path(id: account.id, format: '')
end
def salmon_url(account)
@ -14,6 +14,6 @@ module ApplicationHelper
end
def add_base_url_prefix(suffix)
"#{root_url}api#{suffix}"
File.join(root_url, "api", suffix)
end
end

View File

@ -0,0 +1,11 @@
module RoutingHelper
extend ActiveSupport::Concern
include Rails.application.routes.url_helpers
include GrapeRouteHelpers::NamedRouteMatcher
included do
def default_url_options
ActionMailer::Base.default_url_options
end
end
end

View File

@ -29,6 +29,18 @@ class Account < ActiveRecord::Base
self.domain.nil?
end
def acct
local? ? self.username : "#{self.username}@#{self.domain}"
end
def object_type
:person
end
def subscribed?
!(self.secret.blank? || self.verify_token.blank?)
end
def keypair
self.private_key.nil? ? OpenSSL::PKey::RSA.new(self.public_key) : OpenSSL::PKey::RSA.new(self.private_key)
end

View File

@ -2,6 +2,28 @@ class Follow < ActiveRecord::Base
belongs_to :account
belongs_to :target_account, class_name: 'Account'
validates :account, :target_account, presence: true
def verb
:follow
end
def object_type
:person
end
def target
self.target_account
end
def content
"#{self.account.acct} started following #{self.target_account.acct}"
end
def title
content
end
after_create do
self.account.stream_entries.create!(activity: self)
end

View File

@ -1,6 +1,24 @@
class Status < ActiveRecord::Base
belongs_to :account, inverse_of: :statuses
validates :account, presence: true
def verb
:post
end
def object_type
:note
end
def content
self.text
end
def title
content.truncate(80, omission: "...")
end
after_create do
self.account.stream_entries.create!(activity: self)
end

View File

@ -2,32 +2,29 @@ class StreamEntry < ActiveRecord::Base
belongs_to :account, inverse_of: :stream_entries
belongs_to :activity, polymorphic: true
validates :account, :activity, presence: true
def object_type
case self.activity_type
when 'Status'
:note
when 'Follow'
:person
end
self.activity.object_type
end
def verb
case self.activity_type
when 'Status'
:post
when 'Follow'
:follow
end
self.activity.verb
end
def targeted?
[:follow].include? self.verb
end
def target
case self.activity_type
when 'Follow'
self.activity.target_account
end
self.activity.target
end
def title
self.activity.title
end
def content
self.activity.text if self.activity_type == 'Status'
self.activity.content
end
end

View File

@ -1,3 +1,5 @@
class User < ActiveRecord::Base
belongs_to :account, inverse_of: :user
validates :account, presence: true
end

View File

@ -5,10 +5,13 @@ class FollowRemoteAccountService
username, domain = uri.split('@')
account = Account.where(username: username, domain: domain).first
return account unless account.nil?
if account.nil?
account = Account.new(username: username, domain: domain)
elsif account.subscribed?
return account
end
account = Account.new(username: username, domain: domain)
data = Goldfinger.finger("acct:#{uri}")
data = Goldfinger.finger("acct:#{uri}")
account.remote_url = data.link('http://schemas.google.com/g/2010#updates-from').href
account.salmon_url = data.link('salmon').href
@ -21,16 +24,20 @@ class FollowRemoteAccountService
feed = get_feed(account.remote_url)
hubs = feed.xpath('//xmlns:link[@rel="hub"]')
return false if hubs.empty? || hubs.first.attribute('href').nil? || feed.at_xpath('/xmlns:author/xmlns:uri').nil?
return nil if hubs.empty? || hubs.first.attribute('href').nil? || feed.at_xpath('/xmlns:feed/xmlns:author/xmlns:uri').nil?
account.uri = feed.at_xpath('/xmlns:author/xmlns:uri').content
account.uri = feed.at_xpath('/xmlns:feed/xmlns:author/xmlns:uri').content
account.hub_url = hubs.first.attribute('href').value
get_profile(feed, account)
account.save!
subscription = account.subscription(subscription_url(account))
subscription.subscribe
return account
rescue Goldfinger::Error, HTTP::Error => e
false
nil
end
private
@ -40,6 +47,20 @@ class FollowRemoteAccountService
Nokogiri::XML(response)
end
def get_profile(xml, account)
author = xml.at_xpath('/xmlns:feed/xmlns:author')
if author.at_xpath('./poco:displayName').nil?
account.display_name = account.username
else
account.display_name = author.at_xpath('./poco:displayName').content
end
unless author.at_xpath('./poco:note').nil?
account.note = author.at_xpath('./poco:note').content
end
end
def magic_key_to_pem(magic_key)
_, modulus, exponent = magic_key.split('.')
modulus, exponent = [modulus, exponent].map { |n| Base64.urlsafe_decode64(n).bytes.inject(0) { |num, byte| (num << 8) | byte } }

View File

@ -1,7 +1,7 @@
class FollowService
def call(source_account, uri)
target_account = follow_remote_account_service.(uri)
source_account.follow!(target_account)
source_account.follow!(target_account) unless target_account.nil?
end
private

View File

@ -15,20 +15,29 @@ Nokogiri::XML::Builder.new do |xml|
end
xml.link(rel: 'alternate', type: 'text/html', href: profile_url(name: @account.username))
xml.link(rel: 'hub', href: '')
xml.link(rel: 'hub', href: HUB_URL)
xml.link(rel: 'salmon', href: salmon_url(@account))
xml.link(rel: 'self', type: 'application/atom+xml', href: atom_user_stream_url(id: @account.id))
@account.stream_entries.each do |stream_entry|
xml.entry do
xml.id_ unique_tag(stream_entry.created_at, stream_entry.activity_id, stream_entry.activity_type)
xml.published stream_entry.activity.created_at.iso8601
xml.updated stream_entry.activity.updated_at.iso8601
xml.content({ type: 'html' }, stream_entry.content)
xml.title
xml.title stream_entry.title
xml.content({ type: 'html' }, stream_entry.content)
xml['activity'].send('verb', "http://activitystrea.ms/schema/1.0/#{stream_entry.verb}")
xml['activity'].send('object-type', "http://activitystrea.ms/schema/1.0/#{stream_entry.object_type}")
if stream_entry.targeted?
xml['activity'].send('object') do
xml['activity'].send('object-type', "http://activitystrea.ms/schema/1.0/#{stream_entry.target.object_type}")
xml.id_ stream_entry.target.uri
end
else
xml['activity'].send('object-type', "http://activitystrea.ms/schema/1.0/#{stream_entry.object_type}")
end
end
end
end

View File

@ -1,6 +1,8 @@
Nokogiri::XML::Builder.new do |xml|
xml.XRD(xmlns: 'http://docs.oasis-open.org/ns/xri/xrd-1.0') do
xml.Subject @canonical_account_uri
xml.Alias profile_url(name: @account.username)
xml.Link(rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: profile_url(name: @account.username))
xml.Link(rel: 'http://schemas.google.com/g/2010#updates-from', type: 'application/atom+xml', href: atom_user_stream_url(id: @account.id))
xml.Link(rel: 'salmon', href: salmon_url(@account))
xml.Link(rel: 'magic-public-key', href: @magic_key)

View File

@ -6,6 +6,8 @@ require 'rails/all'
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
Dotenv::Railtie.load
module Mastodon
class Application < Rails::Application
# Settings in config/environments/* take precedence over those specified here.

View File

@ -38,6 +38,4 @@ Rails.application.configure do
# Raises error for missing translations
# config.action_view.raise_on_missing_translations = true
config.action_mailer.default_url_options = { host: ENV['NGROK_HOST'] }
end

View File

@ -1 +1,6 @@
LOCAL_DOMAIN = ENV['LOCAL_DOMAIN'] || 'localhost'
HUB_URL = ENV['HUB_URL'] || 'https://pubsubhubbub.superfeedr.com'
Rails.application.configure do
config.action_mailer.default_url_options = { host: LOCAL_DOMAIN }
end