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
This commit is contained in:
		| @@ -13,7 +13,7 @@ module AccountsHelper | ||||
|     if account.local? | ||||
|       "@#{account.acct}@#{Rails.configuration.x.local_domain}" | ||||
|     else | ||||
|       "@#{account.acct}" | ||||
|       "@#{account.pretty_acct}" | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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) | ||||
|       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 | ||||
|  | ||||
|     it 'creates a mention' do | ||||
|       expect(remote_user.mentions.where(status: status).count).to eq 1 | ||||
|     end | ||||
|     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") } | ||||
|  | ||||
|     it 'sends activity to the inbox' do | ||||
|       expect(a_request(:post, remote_user.inbox_url)).to have_been_made.once | ||||
|       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) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user