about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--app/controllers/xrd_controller.rb2
-rw-r--r--app/helpers/application_helper.rb6
-rw-r--r--app/helpers/routing_helper.rb11
-rw-r--r--app/models/account.rb12
-rw-r--r--app/models/follow.rb22
-rw-r--r--app/models/status.rb18
-rw-r--r--app/models/stream_entry.rb31
-rw-r--r--app/models/user.rb2
-rw-r--r--app/services/follow_remote_account_service.rb33
-rw-r--r--app/services/follow_service.rb2
-rw-r--r--app/views/atom/user_stream.xml.ruby17
-rw-r--r--app/views/xrd/webfinger.xml.ruby2
-rw-r--r--config/application.rb2
-rw-r--r--config/environments/development.rb2
-rw-r--r--config/initializers/ostatus.rb5
15 files changed, 133 insertions, 34 deletions
diff --git a/app/controllers/xrd_controller.rb b/app/controllers/xrd_controller.rb
index 4c8e958e6..6fda75a03 100644
--- a/app/controllers/xrd_controller.rb
+++ b/app/controllers/xrd_controller.rb
@@ -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
 
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 29e444a32..ed7b59165 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -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
diff --git a/app/helpers/routing_helper.rb b/app/helpers/routing_helper.rb
new file mode 100644
index 000000000..655e6bc26
--- /dev/null
+++ b/app/helpers/routing_helper.rb
@@ -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
diff --git a/app/models/account.rb b/app/models/account.rb
index 90e8d7610..fac835168 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -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
diff --git a/app/models/follow.rb b/app/models/follow.rb
index eec01b9ba..203215947 100644
--- a/app/models/follow.rb
+++ b/app/models/follow.rb
@@ -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
diff --git a/app/models/status.rb b/app/models/status.rb
index d98297643..c0b0ca9d9 100644
--- a/app/models/status.rb
+++ b/app/models/status.rb
@@ -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
diff --git a/app/models/stream_entry.rb b/app/models/stream_entry.rb
index cee151a07..7a182bb5d 100644
--- a/app/models/stream_entry.rb
+++ b/app/models/stream_entry.rb
@@ -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
diff --git a/app/models/user.rb b/app/models/user.rb
index ccfa54e4f..d23f5d608 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1,3 +1,5 @@
 class User < ActiveRecord::Base
   belongs_to :account, inverse_of: :user
+
+  validates :account, presence: true
 end
diff --git a/app/services/follow_remote_account_service.rb b/app/services/follow_remote_account_service.rb
index 41f8fa4a0..bd3c760d7 100644
--- a/app/services/follow_remote_account_service.rb
+++ b/app/services/follow_remote_account_service.rb
@@ -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 } }
diff --git a/app/services/follow_service.rb b/app/services/follow_service.rb
index fc606730b..ea868b544 100644
--- a/app/services/follow_service.rb
+++ b/app/services/follow_service.rb
@@ -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
diff --git a/app/views/atom/user_stream.xml.ruby b/app/views/atom/user_stream.xml.ruby
index d418ea0ec..d058d5cb4 100644
--- a/app/views/atom/user_stream.xml.ruby
+++ b/app/views/atom/user_stream.xml.ruby
@@ -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
diff --git a/app/views/xrd/webfinger.xml.ruby b/app/views/xrd/webfinger.xml.ruby
index 7a1e9a1d3..ce21f148a 100644
--- a/app/views/xrd/webfinger.xml.ruby
+++ b/app/views/xrd/webfinger.xml.ruby
@@ -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)
diff --git a/config/application.rb b/config/application.rb
index a2f778dc3..dddf4905b 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -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.
diff --git a/config/environments/development.rb b/config/environments/development.rb
index c3377aac8..b55e2144b 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -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
diff --git a/config/initializers/ostatus.rb b/config/initializers/ostatus.rb
index 64204870b..6f00ba7ed 100644
--- a/config/initializers/ostatus.rb
+++ b/config/initializers/ostatus.rb
@@ -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