about summary refs log tree commit diff
path: root/app/lib
diff options
context:
space:
mode:
Diffstat (limited to 'app/lib')
-rw-r--r--app/lib/activitypub/activity.rb9
-rw-r--r--app/lib/activitypub/activity/add.rb5
-rw-r--r--app/lib/activitypub/activity/announce.rb10
-rw-r--r--app/lib/activitypub/activity/block.rb2
-rw-r--r--app/lib/activitypub/activity/create.rb7
-rw-r--r--app/lib/activitypub/activity/follow.rb2
-rw-r--r--app/lib/activitypub/activity/remove.rb2
-rw-r--r--app/lib/activitypub/tag_manager.rb8
-rw-r--r--app/lib/formatter.rb12
-rw-r--r--app/lib/ostatus/activity/creation.rb10
-rw-r--r--app/lib/ostatus/atom_serializer.rb3
-rw-r--r--app/lib/request.rb15
-rw-r--r--app/lib/user_settings_decorator.rb5
13 files changed, 61 insertions, 29 deletions
diff --git a/app/lib/activitypub/activity.rb b/app/lib/activitypub/activity.rb
index 84d4b1752..03476920b 100644
--- a/app/lib/activitypub/activity.rb
+++ b/app/lib/activitypub/activity.rb
@@ -118,4 +118,13 @@ class ActivityPub::Activity
   def delete_later!(uri)
     redis.setex("delete_upon_arrival:#{@account.id}:#{uri}", 6.hours.seconds, uri)
   end
+
+  def fetch_remote_original_status
+    if object_uri.start_with?('http')
+      return if ActivityPub::TagManager.instance.local_uri?(object_uri)
+      ActivityPub::FetchRemoteStatusService.new.call(object_uri, id: true, on_behalf_of: @account.followers.local.first)
+    elsif @object['url'].present?
+      ::FetchRemoteStatusService.new.call(@object['url'])
+    end
+  end
 end
diff --git a/app/lib/activitypub/activity/add.rb b/app/lib/activitypub/activity/add.rb
index ea94d2f98..688ab00b3 100644
--- a/app/lib/activitypub/activity/add.rb
+++ b/app/lib/activitypub/activity/add.rb
@@ -4,9 +4,10 @@ class ActivityPub::Activity::Add < ActivityPub::Activity
   def perform
     return unless @json['target'].present? && value_or_id(@json['target']) == @account.featured_collection_url
 
-    status = status_from_uri(object_uri)
+    status   = status_from_uri(object_uri)
+    status ||= fetch_remote_original_status
 
-    return unless status.account_id == @account.id && !@account.pinned?(status)
+    return unless !status.nil? && status.account_id == @account.id && !@account.pinned?(status)
 
     StatusPin.create!(account: @account, status: status)
   end
diff --git a/app/lib/activitypub/activity/announce.rb b/app/lib/activitypub/activity/announce.rb
index 7e146ea8c..1147a4481 100644
--- a/app/lib/activitypub/activity/announce.rb
+++ b/app/lib/activitypub/activity/announce.rb
@@ -26,16 +26,6 @@ class ActivityPub::Activity::Announce < ActivityPub::Activity
 
   private
 
-  def fetch_remote_original_status
-    if object_uri.start_with?('http')
-      return if ActivityPub::TagManager.instance.local_uri?(object_uri)
-
-      ActivityPub::FetchRemoteStatusService.new.call(object_uri, id: true)
-    elsif @object['url'].present?
-      ::FetchRemoteStatusService.new.call(@object['url'])
-    end
-  end
-
   def announceable?(status)
     status.account_id == @account.id || status.public_visibility? || status.unlisted_visibility?
   end
diff --git a/app/lib/activitypub/activity/block.rb b/app/lib/activitypub/activity/block.rb
index f630d5db2..26da8bdf5 100644
--- a/app/lib/activitypub/activity/block.rb
+++ b/app/lib/activitypub/activity/block.rb
@@ -7,6 +7,6 @@ class ActivityPub::Activity::Block < ActivityPub::Activity
     return if target_account.nil? || !target_account.local? || delete_arrived_first?(@json['id']) || @account.blocking?(target_account)
 
     UnfollowService.new.call(target_account, @account) if target_account.following?(@account)
-    @account.block!(target_account)
+    @account.block!(target_account, uri: @json['id'])
   end
 end
diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb
index 8d17a4ebe..869749f1e 100644
--- a/app/lib/activitypub/activity/create.rb
+++ b/app/lib/activitypub/activity/create.rb
@@ -11,6 +11,8 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
       if lock.acquired?
         @status = find_existing_status
         process_status if @status.nil?
+      else
+        raise Mastodon::RaceConditionError
       end
     end
 
@@ -76,9 +78,12 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
     return if tag['name'].blank?
 
     hashtag = tag['name'].gsub(/\A#/, '').mb_chars.downcase
-    hashtag = Tag.where(name: hashtag).first_or_initialize(name: hashtag)
+    hashtag = Tag.where(name: hashtag).first_or_create(name: hashtag)
+
+    return if status.tags.include?(hashtag)
 
     status.tags << hashtag
+    TrendingTags.record_use!(hashtag, status.account, status.created_at)
   rescue ActiveRecord::RecordInvalid
     nil
   end
diff --git a/app/lib/activitypub/activity/follow.rb b/app/lib/activitypub/activity/follow.rb
index 8adbbb9c3..fbbf358a8 100644
--- a/app/lib/activitypub/activity/follow.rb
+++ b/app/lib/activitypub/activity/follow.rb
@@ -12,7 +12,7 @@ class ActivityPub::Activity::Follow < ActivityPub::Activity
       return
     end
 
-    follow_request = FollowRequest.create!(account: @account, target_account: target_account)
+    follow_request = FollowRequest.create!(account: @account, target_account: target_account, uri: @json['id'])
 
     if target_account.locked?
       NotifyService.new.call(target_account, follow_request)
diff --git a/app/lib/activitypub/activity/remove.rb b/app/lib/activitypub/activity/remove.rb
index 62a1e3196..f523ead9f 100644
--- a/app/lib/activitypub/activity/remove.rb
+++ b/app/lib/activitypub/activity/remove.rb
@@ -6,7 +6,7 @@ class ActivityPub::Activity::Remove < ActivityPub::Activity
 
     status = status_from_uri(object_uri)
 
-    return unless status.account_id == @account.id
+    return unless !status.nil? && status.account_id == @account.id
 
     pin = StatusPin.find_by(account: @account, status: status)
     pin&.destroy!
diff --git a/app/lib/activitypub/tag_manager.rb b/app/lib/activitypub/tag_manager.rb
index fa2a8f7d3..95d1cf9f3 100644
--- a/app/lib/activitypub/tag_manager.rb
+++ b/app/lib/activitypub/tag_manager.rb
@@ -38,6 +38,10 @@ class ActivityPub::TagManager
     end
   end
 
+  def generate_uri_for(_target)
+    URI.join(root_url, 'payloads', SecureRandom.uuid)
+  end
+
   def activity_uri_for(target)
     raise ArgumentError, 'target must be a local activity' unless %i(note comment activity).include?(target.object_type) && target.local?
 
@@ -82,6 +86,8 @@ class ActivityPub::TagManager
   end
 
   def local_uri?(uri)
+    return false if uri.nil?
+
     uri  = Addressable::URI.parse(uri)
     host = uri.normalized_host
     host = "#{host}:#{uri.port}" if uri.port
@@ -95,6 +101,8 @@ class ActivityPub::TagManager
   end
 
   def uri_to_resource(uri, klass)
+    return if uri.nil?
+
     if local_uri?(uri)
       case klass.name
       when 'Account'
diff --git a/app/lib/formatter.rb b/app/lib/formatter.rb
index 050c651ee..e1ab05cc0 100644
--- a/app/lib/formatter.rb
+++ b/app/lib/formatter.rb
@@ -67,9 +67,17 @@ class Formatter
     html.html_safe # rubocop:disable Rails/OutputSafety
   end
 
-  def format_field(account, str)
+  def format_display_name(account, **options)
+    html = encode(account.display_name.presence || account.username)
+    html = encode_custom_emojis(html, account.emojis) if options[:custom_emojify]
+    html.html_safe # rubocop:disable Rails/OutputSafety
+  end
+
+  def format_field(account, str, **options)
     return reformat(str).html_safe unless account.local? # rubocop:disable Rails/OutputSafety
-    encode_and_link_urls(str, me: true).html_safe # rubocop:disable Rails/OutputSafety
+    html = encode_and_link_urls(str, me: true)
+    html = encode_custom_emojis(html, account.emojis) if options[:custom_emojify]
+    html.html_safe # rubocop:disable Rails/OutputSafety
   end
 
   def linkify(text)
diff --git a/app/lib/ostatus/activity/creation.rb b/app/lib/ostatus/activity/creation.rb
index 1e7f47029..d3a303a0c 100644
--- a/app/lib/ostatus/activity/creation.rb
+++ b/app/lib/ostatus/activity/creation.rb
@@ -15,6 +15,8 @@ class OStatus::Activity::Creation < OStatus::Activity::Base
         @status = find_status(id)
         return [@status, false] unless @status.nil?
         @status = process_status
+      else
+        raise Mastodon::RaceConditionError
       end
     end
 
@@ -46,7 +48,8 @@ class OStatus::Activity::Creation < OStatus::Activity::Base
         visibility: visibility_scope,
         conversation: find_or_create_conversation,
         thread: thread? ? find_status(thread.first) || find_activitypub_status(thread.first, thread.second) : nil,
-        media_attachment_ids: media_attachments.map(&:id)
+        media_attachment_ids: media_attachments.map(&:id),
+        sensitive: sensitive?
       )
 
       save_mentions(status)
@@ -105,6 +108,11 @@ class OStatus::Activity::Creation < OStatus::Activity::Base
 
   private
 
+  def sensitive?
+    # OStatus-specific convention (not standard)
+    @xml.xpath('./xmlns:category', xmlns: OStatus::TagManager::XMLNS).any? { |category| category['term'] == 'nsfw' }
+  end
+
   def find_or_create_conversation
     uri = @xml.at_xpath('./ostatus:conversation', ostatus: OStatus::TagManager::OS_XMLNS)&.attribute('ref')&.content
     return if uri.nil?
diff --git a/app/lib/ostatus/atom_serializer.rb b/app/lib/ostatus/atom_serializer.rb
index 7c66f2066..5c6ff4f9b 100644
--- a/app/lib/ostatus/atom_serializer.rb
+++ b/app/lib/ostatus/atom_serializer.rb
@@ -354,7 +354,7 @@ class OStatus::AtomSerializer
     append_element(entry, 'summary', status.spoiler_text, 'xml:lang': status.language) if status.spoiler_text?
     append_element(entry, 'content', Formatter.instance.format(status).to_str, type: 'html', 'xml:lang': status.language)
 
-    status.mentions.order(:id).each do |mentioned|
+    status.mentions.sort_by(&:id).each do |mentioned|
       append_element(entry, 'link', nil, rel: :mentioned, 'ostatus:object-type': OStatus::TagManager::TYPES[:person], href: OStatus::TagManager.instance.uri_for(mentioned.account))
     end
 
@@ -368,6 +368,7 @@ class OStatus::AtomSerializer
       append_element(entry, 'link', nil, rel: :enclosure, type: media.file_content_type, length: media.file_file_size, href: full_asset_url(media.file.url(:original, false)))
     end
 
+    append_element(entry, 'category', nil, term: 'nsfw') if status.sensitive? && status.media_attachments.any?
     append_element(entry, 'mastodon:scope', status.visibility)
 
     status.emojis.each do |emoji|
diff --git a/app/lib/request.rb b/app/lib/request.rb
index 00f94dacf..397614fac 100644
--- a/app/lib/request.rb
+++ b/app/lib/request.rb
@@ -57,10 +57,11 @@ class Request
   private
 
   def set_common_headers!
-    @headers[REQUEST_TARGET] = "#{@verb} #{@url.path}"
-    @headers['User-Agent']   = user_agent
-    @headers['Host']         = @url.host
-    @headers['Date']         = Time.now.utc.httpdate
+    @headers[REQUEST_TARGET]    = "#{@verb} #{@url.path}"
+    @headers['User-Agent']      = Mastodon::Version.user_agent
+    @headers['Host']            = @url.host
+    @headers['Date']            = Time.now.utc.httpdate
+    @headers['Accept-Encoding'] = 'gzip' if @verb != :head
   end
 
   def set_digest!
@@ -82,10 +83,6 @@ class Request
     @headers.keys.join(' ').downcase
   end
 
-  def user_agent
-    @user_agent ||= "#{HTTP::Request::USER_AGENT} (Mastodon/#{Mastodon::Version}; +#{root_url})"
-  end
-
   def key_id
     case @key_id_format
     when :acct
@@ -100,7 +97,7 @@ class Request
   end
 
   def http_client
-    @http_client ||= HTTP.timeout(:per_operation, timeout).follow(max_hops: 2)
+    @http_client ||= HTTP.use(:auto_inflate).timeout(:per_operation, timeout).follow(max_hops: 2)
   end
 
   def use_proxy?
diff --git a/app/lib/user_settings_decorator.rb b/app/lib/user_settings_decorator.rb
index 78b3aa77c..f8bacb036 100644
--- a/app/lib/user_settings_decorator.rb
+++ b/app/lib/user_settings_decorator.rb
@@ -30,6 +30,7 @@ class UserSettingsDecorator
     user.settings['noindex']                 = noindex_preference if change?('setting_noindex')
     user.settings['flavour']                 = flavour_preference if change?('setting_flavour')
     user.settings['skin']                    = skin_preference if change?('setting_skin')
+    user.settings['hide_network']            = hide_network_preference if change?('setting_hide_network')
   end
 
   def merged_notification_emails
@@ -92,6 +93,10 @@ class UserSettingsDecorator
     settings['setting_skin']
   end
 
+  def hide_network_preference
+    boolean_cast_setting 'setting_hide_network'
+  end
+
   def boolean_cast_setting(key)
     ActiveModel::Type::Boolean.new.cast(settings[key])
   end