From 7ec1af5e50d481eff78963c281d71602b8995937 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 1 Nov 2022 16:49:37 +0400 Subject: [PATCH] Forbid saving / copying of extended media. Fixes #25227. --- .../history/history_inner_widget.cpp | 30 +++++++++++++---- .../history/history_inner_widget.h | 3 ++ Telegram/SourceFiles/history/history_item.cpp | 9 +++++ Telegram/SourceFiles/history/history_item.h | 1 + .../view/history_view_context_menu.cpp | 10 +++--- .../history/view/history_view_list_widget.cpp | 33 +++++++++++++++++++ .../history/view/history_view_list_widget.h | 8 +++++ .../view/history_view_pinned_section.cpp | 5 +++ .../view/history_view_pinned_section.h | 2 ++ .../view/history_view_replies_section.cpp | 5 +++ .../view/history_view_replies_section.h | 2 ++ .../view/history_view_scheduled_section.cpp | 12 +++++++ .../view/history_view_scheduled_section.h | 2 ++ .../media/view/media_view_overlay_widget.cpp | 18 +++++----- .../media/view/media_view_overlay_widget.h | 4 +-- 15 files changed, 122 insertions(+), 22 deletions(-) diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index a00887234..030268347 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -2154,7 +2154,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { const auto addPhotoActions = [&](not_null photo, HistoryItem *item) { const auto media = photo->activeMediaView(); const auto itemId = item ? item->fullId() : FullMsgId(); - if (!photo->isNull() && media && media->loaded() && !hasCopyRestriction(item)) { + if (!photo->isNull() && media && media->loaded() && !hasCopyMediaRestriction(item)) { _menu->addAction(tr::lng_context_save_image(tr::now), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] { savePhotoToFile(photo); }), &st::menuIconSaveImage); @@ -2194,7 +2194,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { openContextGif(itemId); }, &st::menuIconShowInChat); } - if (!hasCopyRestriction(item)) { + if (!hasCopyMediaRestriction(item)) { _menu->addAction(tr::lng_context_save_gif(tr::now), [=] { saveContextGif(itemId); }, &st::menuIconGif); @@ -2205,7 +2205,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { showContextInFolder(document); }, &st::menuIconShowInFolder); } - if (item && !hasCopyRestriction(item)) { + if (item && !hasCopyMediaRestriction(item)) { HistoryView::AddSaveSoundForNotifications( _menu, item, @@ -2373,7 +2373,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { Api::ToggleFavedSticker(controller, document, itemId); }, isFaved ? &st::menuIconUnfave : &st::menuIconFave); } - if (!hasCopyRestriction(item)) { + if (!hasCopyMediaRestriction(item)) { _menu->addAction(tr::lng_context_save_image(tr::now), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] { saveDocumentToFile(itemId, document); }), &st::menuIconDownload); @@ -2533,6 +2533,11 @@ bool HistoryInner::hasCopyRestriction(HistoryItem *item) const { return !_peer->allowsForwarding() || (item && item->forbidsForward()); } +bool HistoryInner::hasCopyMediaRestriction( + not_null item) const { + return hasCopyRestriction(item) || item->forbidsSaving(); +} + bool HistoryInner::showCopyRestriction(HistoryItem *item) { if (!hasCopyRestriction(item)) { return false; @@ -2546,6 +2551,19 @@ bool HistoryInner::showCopyRestriction(HistoryItem *item) { return true; } +bool HistoryInner::showCopyMediaRestriction(not_null item) { + if (!hasCopyMediaRestriction(item)) { + return false; + } + Ui::ShowMultilineToast({ + .parentOverride = Window::Show(_controller).toastParent(), + .text = { _peer->isBroadcast() + ? tr::lng_error_nocopy_channel(tr::now) + : tr::lng_error_nocopy_group(tr::now) }, + }); + return true; +} + bool HistoryInner::hasCopyRestrictionForSelected() const { if (hasCopyRestriction()) { return true; @@ -2601,7 +2619,7 @@ void HistoryInner::copyContextImage( const auto media = photo->activeMediaView(); if (photo->isNull() || !media || !media->loaded()) { return; - } else if (!showCopyRestriction(item)) { + } else if (!showCopyMediaRestriction(item)) { const auto image = media->image(Data::PhotoSize::Large)->original(); QGuiApplication::clipboard()->setImage(image); } @@ -2643,7 +2661,7 @@ void HistoryInner::openContextGif(FullMsgId itemId) { void HistoryInner::saveContextGif(FullMsgId itemId) { if (const auto item = session().data().message(itemId)) { - if (!hasCopyRestriction(item)) { + if (!hasCopyMediaRestriction(item)) { if (const auto media = item->media()) { if (const auto document = media->document()) { Api::ToggleSavedGif( diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h index f9a66a113..5a4962f14 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.h +++ b/Telegram/SourceFiles/history/history_inner_widget.h @@ -403,7 +403,10 @@ private: void setupSharingDisallowed(); [[nodiscard]] bool hasCopyRestriction(HistoryItem *item = nullptr) const; + [[nodiscard]] bool hasCopyMediaRestriction( + not_null item) const; bool showCopyRestriction(HistoryItem *item = nullptr); + bool showCopyMediaRestriction(not_null item); [[nodiscard]] bool hasCopyRestrictionForSelected() const; bool showCopyRestrictionForSelected(); [[nodiscard]] bool hasSelectRestriction() const; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 87a663dc2..f10e85ffd 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -833,6 +833,15 @@ bool HistoryItem::forbidsForward() const { return (_flags & MessageFlag::NoForwards); } +bool HistoryItem::forbidsSaving() const { + if (forbidsForward()) { + return true; + } else if (const auto invoice = _media ? _media->invoice() : nullptr) { + return (invoice->extendedMedia != nullptr); + } + return false; +} + bool HistoryItem::canDelete() const { if (isSponsored()) { return false; diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 504db9fe1..590eafa44 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -345,6 +345,7 @@ public: [[nodiscard]] bool canBeEdited() const; [[nodiscard]] bool canStopPoll() const; [[nodiscard]] bool forbidsForward() const; + [[nodiscard]] bool forbidsSaving() const; [[nodiscard]] virtual bool allowsSendNow() const; [[nodiscard]] virtual bool allowsForward() const; [[nodiscard]] virtual bool allowsEdit(TimeId now) const; diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index 75ac5d4c8..3771bfa47 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -147,7 +147,7 @@ void AddPhotoActions( HistoryItem *item, not_null list) { const auto contextId = item ? item->fullId() : FullMsgId(); - if (!list->hasCopyRestriction(item)) { + if (!list->hasCopyMediaRestriction(item)) { menu->addAction( tr::lng_context_save_image(tr::now), App::LambdaDelayed( @@ -157,7 +157,7 @@ void AddPhotoActions( &st::menuIconSaveImage); menu->addAction(tr::lng_context_copy_image(tr::now), [=] { const auto item = photo->owner().message(contextId); - if (!list->showCopyRestriction(item)) { + if (!list->showCopyMediaRestriction(item)) { CopyImage(photo); } }, &st::menuIconCopy); @@ -214,7 +214,7 @@ void AddSaveDocumentAction( HistoryItem *item, not_null document, not_null list) { - if (list->hasCopyRestriction(item)) { + if (list->hasCopyMediaRestriction(item)) { return; } const auto origin = item ? item->fullId() : FullMsgId(); @@ -269,7 +269,7 @@ void AddDocumentActions( } }, &st::menuIconShowInChat); } - if (!list->hasCopyRestriction(item)) { + if (!list->hasCopyMediaRestriction(item)) { menu->addAction(tr::lng_context_save_gif(tr::now), [=] { SaveGif(list->controller(), contextId); }, &st::menuIconGif); @@ -309,7 +309,7 @@ void AddDocumentActions( std::move(callback), &st::menuIconStickers); } - if (item && !list->hasCopyRestriction(item)) { + if (item && !list->hasCopyMediaRestriction(item)) { const auto controller = list->controller(); AddSaveSoundForNotifications(menu, item, document, controller); } diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 16fddacf8..f220a262a 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -1335,6 +1335,11 @@ bool ListWidget::hasCopyRestriction(HistoryItem *item) const { != CopyRestrictionType::None; } +bool ListWidget::hasCopyMediaRestriction(not_null item) const { + return _delegate->listCopyMediaRestrictionType(item) + != CopyRestrictionType::None; +} + bool ListWidget::showCopyRestriction(HistoryItem *item) { const auto type = _delegate->listCopyRestrictionType(item); if (type == CopyRestrictionType::None) { @@ -1349,6 +1354,20 @@ bool ListWidget::showCopyRestriction(HistoryItem *item) { return true; } +bool ListWidget::showCopyMediaRestriction(not_null item) { + const auto type = _delegate->listCopyMediaRestrictionType(item); + if (type == CopyRestrictionType::None) { + return false; + } + Ui::ShowMultilineToast({ + .parentOverride = Window::Show(_controller).toastParent(), + .text = { (type == CopyRestrictionType::Channel) + ? tr::lng_error_nocopy_channel(tr::now) + : tr::lng_error_nocopy_group(tr::now) }, + }); + return true; +} + bool ListWidget::hasCopyRestrictionForSelected() const { if (hasCopyRestriction()) { return true; @@ -3867,6 +3886,20 @@ CopyRestrictionType CopyRestrictionTypeFor( : CopyRestrictionType::Group; } +CopyRestrictionType CopyMediaRestrictionTypeFor( + not_null peer, + not_null item) { + if (const auto all = CopyRestrictionTypeFor(peer, item) + ; all != CopyRestrictionType::None) { + return all; + } + return !item->forbidsSaving() + ? CopyRestrictionType::None + : peer->isBroadcast() + ? CopyRestrictionType::Channel + : CopyRestrictionType::Group; +} + CopyRestrictionType SelectRestrictionTypeFor( not_null peer) { if (const auto chat = peer->asChat()) { diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index aadc9fdff..eb33b0a19 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -125,6 +125,8 @@ public: CopyRestrictionType listCopyRestrictionType() { return listCopyRestrictionType(nullptr); } + virtual CopyRestrictionType listCopyMediaRestrictionType( + not_null item) = 0; virtual CopyRestrictionType listSelectRestrictionType() = 0; virtual auto listAllowedReactionsValue() -> rpl::producer = 0; @@ -244,7 +246,10 @@ public: [[nodiscard]] bool isEmpty() const; [[nodiscard]] bool hasCopyRestriction(HistoryItem *item = nullptr) const; + [[nodiscard]] bool hasCopyMediaRestriction( + not_null item) const; [[nodiscard]] bool showCopyRestriction(HistoryItem *item = nullptr); + [[nodiscard]] bool showCopyMediaRestriction(not_null item); [[nodiscard]] bool hasCopyRestrictionForSelected() const; [[nodiscard]] bool showCopyRestrictionForSelected(); [[nodiscard]] bool hasSelectRestriction() const; @@ -733,6 +738,9 @@ void ConfirmSendNowSelectedItems(not_null widget); [[nodiscard]] CopyRestrictionType CopyRestrictionTypeFor( not_null peer, HistoryItem *item = nullptr); +[[nodiscard]] CopyRestrictionType CopyMediaRestrictionTypeFor( + not_null peer, + not_null item); [[nodiscard]] CopyRestrictionType SelectRestrictionTypeFor( not_null peer); diff --git a/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp b/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp index 041345d74..1947696c4 100644 --- a/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp @@ -583,6 +583,11 @@ CopyRestrictionType PinnedWidget::listCopyRestrictionType( return CopyRestrictionTypeFor(_history->peer, item); } +CopyRestrictionType PinnedWidget::listCopyMediaRestrictionType( + not_null item) { + return CopyMediaRestrictionTypeFor(_history->peer, item); +} + CopyRestrictionType PinnedWidget::listSelectRestrictionType() { return SelectRestrictionTypeFor(_history->peer); } diff --git a/Telegram/SourceFiles/history/view/history_view_pinned_section.h b/Telegram/SourceFiles/history/view/history_view_pinned_section.h index 37ad4dd66..05c9d103d 100644 --- a/Telegram/SourceFiles/history/view/history_view_pinned_section.h +++ b/Telegram/SourceFiles/history/view/history_view_pinned_section.h @@ -110,6 +110,8 @@ public: void listHandleViaClick(not_null bot) override; not_null listChatTheme() override; CopyRestrictionType listCopyRestrictionType(HistoryItem *item) override; + CopyRestrictionType listCopyMediaRestrictionType( + not_null item) override; CopyRestrictionType listSelectRestrictionType() override; auto listAllowedReactionsValue() -> rpl::producer override; diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index e99748fae..bddb56baa 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -2436,6 +2436,11 @@ CopyRestrictionType RepliesWidget::listCopyRestrictionType( return CopyRestrictionTypeFor(_history->peer, item); } +CopyRestrictionType RepliesWidget::listCopyMediaRestrictionType( + not_null item) { + return CopyMediaRestrictionTypeFor(_history->peer, item); +} + CopyRestrictionType RepliesWidget::listSelectRestrictionType() { return SelectRestrictionTypeFor(_history->peer); } diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.h b/Telegram/SourceFiles/history/view/history_view_replies_section.h index a0915f530..3f7b7e4d0 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.h +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.h @@ -154,6 +154,8 @@ public: void listHandleViaClick(not_null bot) override; not_null listChatTheme() override; CopyRestrictionType listCopyRestrictionType(HistoryItem *item) override; + CopyRestrictionType listCopyMediaRestrictionType( + not_null item) override; CopyRestrictionType listSelectRestrictionType() override; auto listAllowedReactionsValue() ->rpl::producer override; diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index e2b7baedf..7df6c18bc 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -1250,6 +1250,18 @@ CopyRestrictionType ScheduledWidget::listCopyRestrictionType( return CopyRestrictionType::None; } +CopyRestrictionType ScheduledWidget::listCopyMediaRestrictionType( + not_null item) { + if (const auto media = item->media()) { + if (const auto invoice = media->invoice()) { + if (invoice->extendedMedia) { + return CopyMediaRestrictionTypeFor(_history->peer, item); + } + } + } + return CopyRestrictionType::None; +} + CopyRestrictionType ScheduledWidget::listSelectRestrictionType() { return CopyRestrictionType::None; } diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h index a7e06a272..a439bfc8d 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h @@ -133,6 +133,8 @@ public: void listHandleViaClick(not_null bot) override; not_null listChatTheme() override; CopyRestrictionType listCopyRestrictionType(HistoryItem *item) override; + CopyRestrictionType listCopyMediaRestrictionType( + not_null item) override; CopyRestrictionType listSelectRestrictionType() override; auto listAllowedReactionsValue() -> rpl::producer override; diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 5d00623b6..abcb11af2 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -563,13 +563,13 @@ QSize OverlayWidget::flipSizeByRotation(QSize size) const { return FlipSizeByRotation(size, _rotation); } -bool OverlayWidget::hasCopyRestriction() const { +bool OverlayWidget::hasCopyMediaRestriction() const { return (_history && !_history->peer->allowsForwarding()) - || (_message && _message->forbidsForward()); + || (_message && _message->forbidsSaving()); } -bool OverlayWidget::showCopyRestriction() { - if (!hasCopyRestriction()) { +bool OverlayWidget::showCopyMediaRestriction() { + if (!hasCopyMediaRestriction()) { return false; } Ui::ShowMultilineToast({ @@ -739,7 +739,7 @@ void OverlayWidget::refreshNavVisibility() { } bool OverlayWidget::contentCanBeSaved() const { - if (hasCopyRestriction()) { + if (hasCopyMediaRestriction()) { return false; } else if (_photo) { return _photo->hasVideo() || _photoMedia->loaded(); @@ -932,7 +932,7 @@ void OverlayWidget::fillContextMenuActions(const MenuCallback &addAction) { [=] { showInFolder(); }, &st::mediaMenuIconShowInFolder); } - if (!hasCopyRestriction()) { + if (!hasCopyMediaRestriction()) { if ((_document && documentContentShown()) || (_photo && _photoMedia->loaded())) { addAction( tr::lng_mediaview_copy(tr::now), @@ -976,7 +976,7 @@ void OverlayWidget::fillContextMenuActions(const MenuCallback &addAction) { [=] { deleteMedia(); }, &st::mediaMenuIconDelete); } - if (!hasCopyRestriction()) { + if (!hasCopyMediaRestriction()) { addAction( tr::lng_mediaview_save_as(tr::now), [=] { saveAs(); }, @@ -1602,7 +1602,7 @@ void OverlayWidget::notifyFileDialogShown(bool shown) { } void OverlayWidget::saveAs() { - if (showCopyRestriction()) { + if (showCopyMediaRestriction()) { return; } QString file; @@ -1938,7 +1938,7 @@ void OverlayWidget::showMediaOverview() { } void OverlayWidget::copyMedia() { - if (showCopyRestriction()) { + if (showCopyMediaRestriction()) { return; } _dropdown->hideAnimated(Ui::DropdownMenu::HideOption::IgnoreShow); diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h index 9d133a8a6..5df8b584c 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h @@ -409,8 +409,8 @@ private: void validatePhotoImage(Image *image, bool blurred); void validatePhotoCurrentImage(); - [[nodiscard]] bool hasCopyRestriction() const; - [[nodiscard]] bool showCopyRestriction(); + [[nodiscard]] bool hasCopyMediaRestriction() const; + [[nodiscard]] bool showCopyMediaRestriction(); [[nodiscard]] QSize flipSizeByRotation(QSize size) const;