From 7e074610a6651d499267e976f45aadce8cd60d96 Mon Sep 17 00:00:00 2001 From: "chr v1.x" Date: Sun, 29 Dec 2019 18:17:54 -0800 Subject: Fix backups stopping due to read timeouts (#12281) * Make BackupService resilient to read timeouts If an attachment read times out, assume that the resources is inaccessible and continue the backup without it. This fixes #12280. * Both errors on one line --- app/services/backup_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/services') diff --git a/app/services/backup_service.rb b/app/services/backup_service.rb index fba2d19a0..d1090dff1 100644 --- a/app/services/backup_service.rb +++ b/app/services/backup_service.rb @@ -165,7 +165,7 @@ class BackupService < BaseService io.write(buffer) end end - rescue Errno::ENOENT + rescue Errno::ENOENT, Seahorse::Client::NetworkingError Rails.logger.warn "Could not backup file #{filename}: file not found" end end -- cgit From f86ee4b59f25727d248609e0afe277a4f69f6be7 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 30 Dec 2019 19:20:43 +0100 Subject: Fix IDN mentions not being processed, IDN domains not being rendered (#12715) This changes the REST API to return unicode domains in the `acct` attribute instead of punycode, and to render unicode instead of punycode on public HTML pages as well. Fix #7812, fix #12246 --- app/helpers/accounts_helper.rb | 2 +- app/models/account.rb | 6 +++- app/serializers/rest/account_serializer.rb | 4 +++ app/services/process_mentions_service.rb | 11 +++++- spec/services/process_mentions_service_spec.rb | 46 +++++++++++++++++--------- 5 files changed, 50 insertions(+), 19 deletions(-) (limited to 'app/services') diff --git a/app/helpers/accounts_helper.rb b/app/helpers/accounts_helper.rb index 99815be7b..53939adfc 100644 --- a/app/helpers/accounts_helper.rb +++ b/app/helpers/accounts_helper.rb @@ -13,7 +13,7 @@ module AccountsHelper if account.local? "@#{account.acct}@#{Rails.configuration.x.local_domain}" else - "@#{account.acct}" + "@#{account.pretty_acct}" end end diff --git a/app/models/account.rb b/app/models/account.rb index 884332e5a..feaf273c1 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -50,7 +50,7 @@ class Account < ApplicationRecord USERNAME_RE = /[a-z0-9_]+([a-z0-9_\.-]+[a-z0-9_]+)?/i - MENTION_RE = /(?<=^|[^\/[:word:]])@((#{USERNAME_RE})(?:@[a-z0-9\.\-]+[a-z0-9]+)?)/i + MENTION_RE = /(?<=^|[^\/[:word:]])@((#{USERNAME_RE})(?:@[[:word:]\.\-]+[a-z0-9]+)?)/i include AccountAssociations include AccountAvatar @@ -164,6 +164,10 @@ class Account < ApplicationRecord local? ? username : "#{username}@#{domain}" end + def pretty_acct + local? ? username : "#{username}@#{Addressable::IDNA.to_unicode(domain)}" + end + def local_username_and_domain "#{username}@#{Rails.configuration.x.local_domain}" end diff --git a/app/serializers/rest/account_serializer.rb b/app/serializers/rest/account_serializer.rb index 5fec75673..fd361a34c 100644 --- a/app/serializers/rest/account_serializer.rb +++ b/app/serializers/rest/account_serializer.rb @@ -24,6 +24,10 @@ class REST::AccountSerializer < ActiveModel::Serializer object.id.to_s end + def acct + object.pretty_acct + end + def note Formatter.instance.simplified_format(object) end diff --git a/app/services/process_mentions_service.rb b/app/services/process_mentions_service.rb index 2f7a9e985..b2d868165 100644 --- a/app/services/process_mentions_service.rb +++ b/app/services/process_mentions_service.rb @@ -14,7 +14,16 @@ class ProcessMentionsService < BaseService mentions = [] status.text = status.text.gsub(Account::MENTION_RE) do |match| - username, domain = Regexp.last_match(1).split('@') + username, domain = Regexp.last_match(1).split('@') + + domain = begin + if TagManager.instance.local_domain?(domain) + nil + else + TagManager.instance.normalize_domain(domain) + end + end + mentioned_account = Account.find_remote(username, domain) if mention_undeliverable?(mentioned_account) diff --git a/spec/services/process_mentions_service_spec.rb b/spec/services/process_mentions_service_spec.rb index b1abd79b0..c30de8eeb 100644 --- a/spec/services/process_mentions_service_spec.rb +++ b/spec/services/process_mentions_service_spec.rb @@ -5,11 +5,11 @@ RSpec.describe ProcessMentionsService, type: :service do let(:visibility) { :public } let(:status) { Fabricate(:status, account: account, text: "Hello @#{remote_user.acct}", visibility: visibility) } + subject { ProcessMentionsService.new } + context 'OStatus with public toot' do let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :ostatus, domain: 'example.com', salmon_url: 'http://salmon.example.com') } - subject { ProcessMentionsService.new } - before do stub_request(:post, remote_user.salmon_url) subject.call(status) @@ -24,8 +24,6 @@ RSpec.describe ProcessMentionsService, type: :service do let(:visibility) { :private } let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :ostatus, domain: 'example.com', salmon_url: 'http://salmon.example.com') } - subject { ProcessMentionsService.new } - before do stub_request(:post, remote_user.salmon_url) subject.call(status) @@ -41,29 +39,45 @@ RSpec.describe ProcessMentionsService, type: :service do end context 'ActivityPub' do - let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') } + context do + let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') } - subject { ProcessMentionsService.new } + before do + stub_request(:post, remote_user.inbox_url) + subject.call(status) + end - before do - stub_request(:post, remote_user.inbox_url) - subject.call(status) - end + it 'creates a mention' do + expect(remote_user.mentions.where(status: status).count).to eq 1 + end - it 'creates a mention' do - expect(remote_user.mentions.where(status: status).count).to eq 1 + it 'sends activity to the inbox' do + expect(a_request(:post, remote_user.inbox_url)).to have_been_made.once + end end - it 'sends activity to the inbox' do - expect(a_request(:post, remote_user.inbox_url)).to have_been_made.once + context 'with an IDN domain' do + let(:remote_user) { Fabricate(:account, username: 'sneak', protocol: :activitypub, domain: 'xn--hresiar-mxa.ch', inbox_url: 'http://example.com/inbox') } + let(:status) { Fabricate(:status, account: account, text: "Hello @sneak@hæresiar.ch") } + + before do + stub_request(:post, remote_user.inbox_url) + subject.call(status) + end + + it 'creates a mention' do + expect(remote_user.mentions.where(status: status).count).to eq 1 + end + + it 'sends activity to the inbox' do + expect(a_request(:post, remote_user.inbox_url)).to have_been_made.once + end end end context 'Temporarily-unreachable ActivityPub user' do let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox', last_webfingered_at: nil) } - subject { ProcessMentionsService.new } - before do stub_request(:get, "https://example.com/.well-known/host-meta").to_return(status: 404) stub_request(:get, "https://example.com/.well-known/webfinger?resource=acct:remote_user@example.com").to_return(status: 500) -- cgit From e4d75f238b751329f1d9729046cc65b7b363c7d8 Mon Sep 17 00:00:00 2001 From: ThibG Date: Fri, 3 Jan 2020 05:01:45 +0100 Subject: Fix URL search not returning private toots user has access to (#12742) --- app/services/resolve_url_service.rb | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'app/services') diff --git a/app/services/resolve_url_service.rb b/app/services/resolve_url_service.rb index 79b1bad0c..1a2b0d60c 100644 --- a/app/services/resolve_url_service.rb +++ b/app/services/resolve_url_service.rb @@ -12,6 +12,8 @@ class ResolveURLService < BaseService process_local_url elsif !fetched_resource.nil? process_url + elsif @on_behalf_of.present? + process_url_from_db end end @@ -24,15 +26,19 @@ class ResolveURLService < BaseService status = FetchRemoteStatusService.new.call(resource_url, body) authorize_with @on_behalf_of, status, :show? unless status.nil? status - elsif fetched_resource.nil? && @on_behalf_of.present? - # It may happen that the resource is a private toot, and thus not fetchable, - # but we can return the toot if we already know about it. - status = Status.find_by(uri: @url) || Status.find_by(url: @url) - authorize_with @on_behalf_of, status, :show? unless status.nil? - status end end + def process_url_from_db + # It may happen that the resource is a private toot, and thus not fetchable, + # but we can return the toot if we already know about it. + status = Status.find_by(uri: @url) || Status.find_by(url: @url) + authorize_with @on_behalf_of, status, :show? unless status.nil? + status + rescue Mastodon::NotPermittedError + nil + end + def fetched_resource @fetched_resource ||= FetchResourceService.new.call(@url) end -- cgit From c306978190032dd0097d2e48edace33569ddb072 Mon Sep 17 00:00:00 2001 From: ThibG Date: Fri, 10 Jan 2020 00:10:29 +0100 Subject: Use quality values in Accept field to explicitly prefer JSON over HTML (#12806) --- app/services/fetch_resource_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/services') diff --git a/app/services/fetch_resource_service.rb b/app/services/fetch_resource_service.rb index 34382d279..abe7766d4 100644 --- a/app/services/fetch_resource_service.rb +++ b/app/services/fetch_resource_service.rb @@ -3,7 +3,7 @@ class FetchResourceService < BaseService include JsonLdHelper - ACCEPT_HEADER = 'application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams", text/html' + ACCEPT_HEADER = 'application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams", text/html;q=0.1' def call(url) return if url.blank? -- cgit From 206dfd7daddb888a8115804f2c88794b4230592a Mon Sep 17 00:00:00 2001 From: Daigo 3 Dango Date: Fri, 10 Jan 2020 10:57:05 -1000 Subject: Clarify keyword arguments with ** (#12769) This change is to suppress the warning below on on ruby-2.7.0: - warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/ --- app/helpers/accounts_helper.rb | 2 +- app/lib/activitypub/activity.rb | 2 +- app/services/activitypub/process_collection_service.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'app/services') diff --git a/app/helpers/accounts_helper.rb b/app/helpers/accounts_helper.rb index 53939adfc..e02bc2447 100644 --- a/app/helpers/accounts_helper.rb +++ b/app/helpers/accounts_helper.rb @@ -3,7 +3,7 @@ module AccountsHelper def display_name(account, **options) if options[:custom_emojify] - Formatter.instance.format_display_name(account, options) + Formatter.instance.format_display_name(account, **options) else account.display_name.presence || account.username end diff --git a/app/lib/activitypub/activity.rb b/app/lib/activitypub/activity.rb index 49b1dc9cd..ee35e1e8d 100644 --- a/app/lib/activitypub/activity.rb +++ b/app/lib/activitypub/activity.rb @@ -21,7 +21,7 @@ class ActivityPub::Activity class << self def factory(json, account, **options) @json = json - klass&.new(json, account, options) + klass&.new(json, account, **options) end private diff --git a/app/services/activitypub/process_collection_service.rb b/app/services/activitypub/process_collection_service.rb index a2a2e7071..e6ccaccc9 100644 --- a/app/services/activitypub/process_collection_service.rb +++ b/app/services/activitypub/process_collection_service.rb @@ -37,7 +37,7 @@ class ActivityPub::ProcessCollectionService < BaseService end def process_item(item) - activity = ActivityPub::Activity.factory(item, @account, @options) + activity = ActivityPub::Activity.factory(item, @account, **@options) activity&.perform end -- cgit From d386d89179ccc2b86894a8639b658f4ede24c5f6 Mon Sep 17 00:00:00 2001 From: ThibG Date: Sun, 12 Jan 2020 14:17:03 +0100 Subject: Fix invalid votes from the API being accepted (#12601) * Fix invalid votes from the API being accepted Fixes #12556 - Ensure `choice` is an integer instead of silently converting to 0 - Ensure `choice` corresponds to an actual choice of the poll * Please CodeClimate --- app/services/vote_service.rb | 2 +- app/validators/vote_validator.rb | 8 ++++++++ config/locales/en.yml | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) (limited to 'app/services') diff --git a/app/services/vote_service.rb b/app/services/vote_service.rb index cb7dce6e8..19e453332 100644 --- a/app/services/vote_service.rb +++ b/app/services/vote_service.rb @@ -20,7 +20,7 @@ class VoteService < BaseService ApplicationRecord.transaction do @choices.each do |choice| - @votes << @poll.votes.create!(account: @account, choice: choice) + @votes << @poll.votes.create!(account: @account, choice: Integer(choice)) end end else diff --git a/app/validators/vote_validator.rb b/app/validators/vote_validator.rb index 2e1818bdb..b1692562d 100644 --- a/app/validators/vote_validator.rb +++ b/app/validators/vote_validator.rb @@ -4,10 +4,18 @@ class VoteValidator < ActiveModel::Validator def validate(vote) vote.errors.add(:base, I18n.t('polls.errors.expired')) if vote.poll.expired? + vote.errors.add(:base, I18n.t('polls.errors.invalid_choice')) if invalid_choice?(vote) + if vote.poll.multiple? && vote.poll.votes.where(account: vote.account, choice: vote.choice).exists? vote.errors.add(:base, I18n.t('polls.errors.already_voted')) elsif !vote.poll.multiple? && vote.poll.votes.where(account: vote.account).exists? vote.errors.add(:base, I18n.t('polls.errors.already_voted')) end end + + private + + def invalid_choice?(vote) + vote.choice.negative? || vote.choice >= vote.poll.options.size + end end diff --git a/config/locales/en.yml b/config/locales/en.yml index 62f2226e3..d768cef33 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -922,6 +922,7 @@ en: duration_too_long: is too far into the future duration_too_short: is too soon expired: The poll has already ended + invalid_choice: The chosen vote option does not exist over_character_limit: cannot be longer than %{max} characters each too_few_options: must have more than one item too_many_options: can't contain more than %{max} items -- cgit