From 77d0297313ca7d0a26ec820381e93030a974c1a9 Mon Sep 17 00:00:00 2001 From: Claire Date: Sat, 17 Jul 2021 17:06:52 +0200 Subject: [PATCH 01/11] Fix replying from modal (#16516) Fixes #16515 Not using a router object somehow made `this.history` lag behind the real browser history whenever pushing a new history item in `replyCompose`. Not using the context-provided router in this case was an oversight made when porting glitch-soc changes in #16499. --- app/javascript/mastodon/components/modal_root.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/javascript/mastodon/components/modal_root.js b/app/javascript/mastodon/components/modal_root.js index 8c9409d01..755c46fd6 100644 --- a/app/javascript/mastodon/components/modal_root.js +++ b/app/javascript/mastodon/components/modal_root.js @@ -6,6 +6,10 @@ import { multiply } from 'color-blend'; export default class ModalRoot extends React.PureComponent { + static contextTypes = { + router: PropTypes.object, + }; + static propTypes = { children: PropTypes.node, onClose: PropTypes.func.isRequired, From 9c610ca0a41b1a317188a94d7532e9937dda47b8 Mon Sep 17 00:00:00 2001 From: Claire Date: Sun, 25 Jul 2021 01:13:46 +0200 Subject: [PATCH 02/11] =?UTF-8?q?Fix=20=E2=80=9Copen=E2=80=9D=20link=20of?= =?UTF-8?q?=20media=20modal=20not=20closing=20modal=20(#16524)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../features/picture_in_picture/components/footer.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/javascript/mastodon/features/picture_in_picture/components/footer.js b/app/javascript/mastodon/features/picture_in_picture/components/footer.js index 1ecb18bf8..f5ce50ac8 100644 --- a/app/javascript/mastodon/features/picture_in_picture/components/footer.js +++ b/app/javascript/mastodon/features/picture_in_picture/components/footer.js @@ -114,7 +114,11 @@ class Footer extends ImmutablePureComponent { return; } - const { status } = this.props; + const { status, onClose } = this.props; + + if (onClose) { + onClose(); + } router.history.push(`/statuses/${status.get('id')}`); } From 1bcb3daf7e4255662d6185a2fb74c2263f75060a Mon Sep 17 00:00:00 2001 From: Takeshi Umeda Date: Fri, 6 Aug 2021 19:14:13 +0900 Subject: [PATCH 03/11] Fix logout link not working in safari (#16574) --- .../features/compose/containers/navigation_container.js | 1 + app/javascript/mastodon/features/compose/index.js | 1 + .../features/ui/components/confirmation_modal.js | 9 ++++++++- .../mastodon/features/ui/components/link_footer.js | 1 + 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/javascript/mastodon/features/compose/containers/navigation_container.js b/app/javascript/mastodon/features/compose/containers/navigation_container.js index 8606a642e..654c14df9 100644 --- a/app/javascript/mastodon/features/compose/containers/navigation_container.js +++ b/app/javascript/mastodon/features/compose/containers/navigation_container.js @@ -21,6 +21,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ dispatch(openModal('CONFIRM', { message: intl.formatMessage(messages.logoutMessage), confirm: intl.formatMessage(messages.logoutConfirm), + closeWhenConfirm: false, onConfirm: () => logOut(), })); }, diff --git a/app/javascript/mastodon/features/compose/index.js b/app/javascript/mastodon/features/compose/index.js index e2de8b0e6..c1bce0a3f 100644 --- a/app/javascript/mastodon/features/compose/index.js +++ b/app/javascript/mastodon/features/compose/index.js @@ -74,6 +74,7 @@ class Compose extends React.PureComponent { dispatch(openModal('CONFIRM', { message: intl.formatMessage(messages.logoutMessage), confirm: intl.formatMessage(messages.logoutConfirm), + closeWhenConfirm: false, onConfirm: () => logOut(), })); diff --git a/app/javascript/mastodon/features/ui/components/confirmation_modal.js b/app/javascript/mastodon/features/ui/components/confirmation_modal.js index 1227fa453..65d97ca16 100644 --- a/app/javascript/mastodon/features/ui/components/confirmation_modal.js +++ b/app/javascript/mastodon/features/ui/components/confirmation_modal.js @@ -13,15 +13,22 @@ class ConfirmationModal extends React.PureComponent { onConfirm: PropTypes.func.isRequired, secondary: PropTypes.string, onSecondary: PropTypes.func, + closeWhenConfirm: PropTypes.bool, intl: PropTypes.object.isRequired, }; + static defaultProps = { + closeWhenConfirm: true, + }; + componentDidMount() { this.button.focus(); } handleClick = () => { - this.props.onClose(); + if (this.props.closeWhenConfirm) { + this.props.onClose(); + } this.props.onConfirm(); } diff --git a/app/javascript/mastodon/features/ui/components/link_footer.js b/app/javascript/mastodon/features/ui/components/link_footer.js index 3c4cff9f9..00977d94c 100644 --- a/app/javascript/mastodon/features/ui/components/link_footer.js +++ b/app/javascript/mastodon/features/ui/components/link_footer.js @@ -17,6 +17,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ dispatch(openModal('CONFIRM', { message: intl.formatMessage(messages.logoutMessage), confirm: intl.formatMessage(messages.logoutConfirm), + closeWhenConfirm: false, onConfirm: () => logOut(), })); }, From e65ede1ac538501883f7cb68516e9bada2ef25fe Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 16 Nov 2021 21:36:28 +0100 Subject: [PATCH 04/11] Fix upload of remote media with OpenStack Swift sometimes failing (#16998) Under certain conditions, files fetched from remotes trigger an error when being uploaded using OpenStack Swift. This is because in some cases, the remote server will not return a content-length, so our ResponseWithLimitAdapter will hold a `nil` value for `#size`, which will lead to an invalid value for the Content-Length header of the Swift API call. This commit fixes that by taking the size from the actually-downloaded file size rather than the upstream-provided Content-Length header value. --- lib/paperclip/response_with_limit_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/paperclip/response_with_limit_adapter.rb b/lib/paperclip/response_with_limit_adapter.rb index 17a2abd25..deb89717a 100644 --- a/lib/paperclip/response_with_limit_adapter.rb +++ b/lib/paperclip/response_with_limit_adapter.rb @@ -17,9 +17,9 @@ module Paperclip def cache_current_values @original_filename = filename_from_content_disposition.presence || filename_from_path.presence || 'data' - @size = @target.response.content_length @tempfile = copy_to_tempfile(@target) @content_type = ContentTypeDetector.new(@tempfile.path).detect + @size = File.size(@tempfile) end def copy_to_tempfile(source) From 22cd1e6ab593830dc667ef09235faed0b7547891 Mon Sep 17 00:00:00 2001 From: Claire Date: Sun, 14 Nov 2021 21:55:40 +0100 Subject: [PATCH 05/11] Fix confusing error when webfinger request returns empty document (#16986) For some reason, some misconfigured servers return an empty document when queried over webfinger. Since an empty document does not lead to a parse error, the error is not caught properly and triggers uncaught exceptions later on. This PR fixes that by immediately erroring out with `Webfinger::Error` on getting an empty response. --- app/lib/webfinger.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/lib/webfinger.rb b/app/lib/webfinger.rb index e0e022cea..1ffb5b4bf 100644 --- a/app/lib/webfinger.rb +++ b/app/lib/webfinger.rb @@ -46,7 +46,9 @@ class Webfinger def body_from_webfinger(url = standard_url, use_fallback = true) webfinger_request(url).perform do |res| if res.code == 200 - res.body_with_limit + body = res.body_with_limit + raise Webfinger::Error, "Request for #{@uri} returned empty response" if body.empty? + body elsif res.code == 404 && use_fallback body_from_host_meta elsif res.code == 410 From e5113a8cad23f686129ef04e908afee9ad0f6ada Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 19 Nov 2021 18:22:49 +0100 Subject: [PATCH 06/11] Fix overflow of long profile fields in admin view (#17010) --- app/javascript/styles/mastodon/admin.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss index 25ebc19b0..2744c6c6c 100644 --- a/app/javascript/styles/mastodon/admin.scss +++ b/app/javascript/styles/mastodon/admin.scss @@ -829,6 +829,7 @@ a.name-tag, padding: 0 5px; margin-bottom: 10px; flex: 1 0 50%; + max-width: 100%; } .account__header__fields, From 3c18311d860e829a3bbefe34f166f701242f925a Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 24 Nov 2021 17:41:03 +0100 Subject: [PATCH 07/11] Fix error when suspending user with an already-existing canonical email block (#17036) * Fix error when suspending user with an already-existing canonical email block Fixes #17033 While attempting to create a `CanonicalEmailBlock` with an existing hash would raise an `ActiveRecord::RecordNotUnique` error, this being done within a transaction would cancel the whole transaction. For this reason, checking for uniqueness in Rails would query the database within the transaction and avoid invalidating the whole transaction for this reason. A race condition is still possible, where multiple accounts sharing a canonical email would be blocked in concurrent transactions, in which only one would succeed, but that is way less likely to happen that the current issue, and can always be retried after the first failure, unlike the current situation. * Add tests --- app/models/canonical_email_block.rb | 2 +- spec/models/account_spec.rb | 31 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/app/models/canonical_email_block.rb b/app/models/canonical_email_block.rb index a8546d65a..be8c45bfe 100644 --- a/app/models/canonical_email_block.rb +++ b/app/models/canonical_email_block.rb @@ -15,7 +15,7 @@ class CanonicalEmailBlock < ApplicationRecord belongs_to :reference_account, class_name: 'Account' - validates :canonical_email_hash, presence: true + validates :canonical_email_hash, presence: true, uniqueness: true def email=(email) self.canonical_email_hash = email_to_canonical_email_hash(email) diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb index 03d6f5fb0..65e6714c0 100644 --- a/spec/models/account_spec.rb +++ b/spec/models/account_spec.rb @@ -5,6 +5,37 @@ RSpec.describe Account, type: :model do let(:bob) { Fabricate(:account, username: 'bob') } subject { Fabricate(:account) } + describe '#suspend!' do + it 'marks the account as suspended' do + subject.suspend! + expect(subject.suspended?).to be true + end + + it 'creates a deletion request' do + subject.suspend! + expect(AccountDeletionRequest.where(account: subject).exists?).to be true + end + + context 'when the account is of a local user' do + let!(:subject) { Fabricate(:account, user: Fabricate(:user, email: 'foo+bar@domain.org')) } + + it 'creates a canonical domain block' do + subject.suspend! + expect(CanonicalEmailBlock.block?(subject.user_email)).to be true + end + + context 'when a canonical domain block already exists for that email' do + before do + Fabricate(:canonical_email_block, email: subject.user_email) + end + + it 'does not raise an error' do + expect { subject.suspend! }.not_to raise_error + end + end + end + end + describe '#follow!' do it 'creates a follow' do follow = subject.follow!(bob) From 5e4b04de8853822a9b0c6b124d0427ac9df2327e Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 25 Nov 2021 23:46:39 +0100 Subject: [PATCH 08/11] Fix handling of recursive toots in WebUI (#17041) --- app/javascript/mastodon/features/status/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/javascript/mastodon/features/status/index.js b/app/javascript/mastodon/features/status/index.js index 69cd245fb..907236bb8 100644 --- a/app/javascript/mastodon/features/status/index.js +++ b/app/javascript/mastodon/features/status/index.js @@ -83,7 +83,7 @@ const makeMapStateToProps = () => { ancestorsIds = ancestorsIds.withMutations(mutable => { let id = statusId; - while (id) { + while (id && !mutable.includes(id)) { mutable.unshift(id); id = inReplyTos.get(id); } @@ -101,7 +101,7 @@ const makeMapStateToProps = () => { const ids = [statusId]; while (ids.length > 0) { - let id = ids.shift(); + let id = ids.pop(); const replies = contextReplies.get(id); if (statusId !== id) { @@ -110,7 +110,7 @@ const makeMapStateToProps = () => { if (replies) { replies.reverse().forEach(reply => { - ids.unshift(reply); + if (!ids.includes(reply) && !descendantsIds.includes(reply) && statusId !== reply) ids.push(reply); }); } } From f264cca1d20dcd1d80343d31f7c960f1579b57ad Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 25 Nov 2021 23:46:30 +0100 Subject: [PATCH 09/11] Fix filtering DMs from non-followed users (#17042) --- app/services/notify_service.rb | 43 +++++++++++++++++++++++++++- spec/services/notify_service_spec.rb | 17 +++++++++-- 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/app/services/notify_service.rb b/app/services/notify_service.rb index fc187db40..e78c74d1e 100644 --- a/app/services/notify_service.rb +++ b/app/services/notify_service.rb @@ -67,8 +67,49 @@ class NotifyService < BaseService message? && @notification.target_status.direct_visibility? end + # Returns true if the sender has been mentionned by the recipient up the thread def response_to_recipient? - @notification.target_status.in_reply_to_account_id == @recipient.id && @notification.target_status.thread&.direct_visibility? + return false if @notification.target_status.in_reply_to_id.nil? + + # Using an SQL CTE to avoid unneeded back-and-forth with SQL server in case of long threads + !Status.count_by_sql([<<-SQL.squish, id: @notification.target_status.in_reply_to_id, recipient_id: @recipient.id, sender_id: @notification.from_account.id]).zero? + WITH RECURSIVE ancestors(id, in_reply_to_id, replying_to_sender) AS ( + SELECT + s.id, s.in_reply_to_id, (CASE + WHEN s.account_id = :recipient_id THEN + EXISTS ( + SELECT * + FROM mentions m + WHERE m.silent = FALSE AND m.account_id = :sender_id AND m.status_id = s.id + ) + ELSE + FALSE + END) + FROM statuses s + WHERE s.id = :id + UNION ALL + SELECT + s.id, + s.in_reply_to_id, + (CASE + WHEN s.account_id = :recipient_id THEN + EXISTS ( + SELECT * + FROM mentions m + WHERE m.silent = FALSE AND m.account_id = :sender_id AND m.status_id = s.id + ) + ELSE + FALSE + END) + FROM ancestors st + JOIN statuses s ON s.id = st.in_reply_to_id + WHERE st.replying_to_sender IS FALSE + ) + SELECT COUNT(*) + FROM ancestors st + JOIN statuses s ON s.id = st.id + WHERE st.replying_to_sender IS TRUE AND s.visibility = 3 + SQL end def from_staff? diff --git a/spec/services/notify_service_spec.rb b/spec/services/notify_service_spec.rb index f2cb22c5e..7433866b7 100644 --- a/spec/services/notify_service_spec.rb +++ b/spec/services/notify_service_spec.rb @@ -64,8 +64,9 @@ RSpec.describe NotifyService, type: :service do is_expected.to_not change(Notification, :count) end - context 'if the message chain initiated by recipient, but is not direct message' do + context 'if the message chain is initiated by recipient, but is not direct message' do let(:reply_to) { Fabricate(:status, account: recipient) } + let!(:mention) { Fabricate(:mention, account: sender, status: reply_to) } let(:activity) { Fabricate(:mention, account: recipient, status: Fabricate(:status, account: sender, visibility: :direct, thread: reply_to)) } it 'does not notify' do @@ -73,8 +74,20 @@ RSpec.describe NotifyService, type: :service do end end - context 'if the message chain initiated by recipient and is direct message' do + context 'if the message chain is initiated by recipient, but without a mention to the sender, even if the sender sends multiple messages in a row' do + let(:reply_to) { Fabricate(:status, account: recipient) } + let!(:mention) { Fabricate(:mention, account: sender, status: reply_to) } + let(:dummy_reply) { Fabricate(:status, account: sender, visibility: :direct, thread: reply_to) } + let(:activity) { Fabricate(:mention, account: recipient, status: Fabricate(:status, account: sender, visibility: :direct, thread: dummy_reply)) } + + it 'does not notify' do + is_expected.to_not change(Notification, :count) + end + end + + context 'if the message chain is initiated by the recipient with a mention to the sender' do let(:reply_to) { Fabricate(:status, account: recipient, visibility: :direct) } + let!(:mention) { Fabricate(:mention, account: sender, status: reply_to) } let(:activity) { Fabricate(:mention, account: recipient, status: Fabricate(:status, account: sender, visibility: :direct, thread: reply_to)) } it 'does notify' do From 4cd33a2c712acf038d856caaad077e34398306d2 Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 11 Nov 2021 14:00:30 +0100 Subject: [PATCH 10/11] Fix "bundle exec rails mastodon:setup" crashing in some circumstances (#16976) Fix regression from #16896 --- lib/tasks/mastodon.rake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/tasks/mastodon.rake b/lib/tasks/mastodon.rake index 9146f78e1..d905a07da 100644 --- a/lib/tasks/mastodon.rake +++ b/lib/tasks/mastodon.rake @@ -350,11 +350,11 @@ namespace :mastodon do end end.join("\n") - generated_header = "# Generated with mastodon:setup on #{Time.now.utc}\n\n" + generated_header = "# Generated with mastodon:setup on #{Time.now.utc}\n\n".dup if incompatible_syntax - generated_header << "Some variables in this file will be interpreted differently whether you are\n" - generated_header << "using docker-compose or not.\n\n" + generated_header << "# Some variables in this file will be interpreted differently whether you are\n" + generated_header << "# using docker-compose or not.\n\n" end File.write(Rails.root.join('.env.production'), "#{generated_header}#{env_contents}\n") From fd868f8ca0b13afdb3f6fb9a3be063ccec7e1349 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 26 Nov 2021 01:27:08 +0100 Subject: [PATCH 11/11] Bump version to 3.4.4 --- CHANGELOG.md | 17 +++++++++++++++++ lib/mastodon/version.rb | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0c6c5315..deca765fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,23 @@ Changelog All notable changes to this project will be documented in this file. +## [3.4.4] - 2021-11-26 +### Fixed + +- Fix error when suspending user with an already blocked canonical email ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17036)) +- Fix overflow of long profile fields in admin UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17010)) +- Fix confusing error when WebFinger request returns empty document ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16986)) +- Fix upload of remote media with OpenStack Swift sometimes failing ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16998)) +- Fix logout link not working in Safari ([noellabo](https://github.com/mastodon/mastodon/pull/16574)) +- Fix “open” link of media modal not closing modal in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16524)) +- Fix replying from modal in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16516)) +- Fix `mastodon:setup` command crashing in some circumstances ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16976)) + +### Security + +- Fix filtering DMs from non-followed users ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17042)) +- Fix handling of recursive toots in WebUI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17041)) + ## [3.4.3] - 2021-11-06 ### Fixed diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 37f08ad93..493479977 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -13,7 +13,7 @@ module Mastodon end def patch - 3 + 4 end def flags