data_document_resolver.cpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "data/data_document_resolver.h"
  8. #include "base/options.h"
  9. #include "base/platform/base_platform_info.h"
  10. #include "boxes/abstract_box.h" // Ui::show().
  11. #include "chat_helpers/ttl_media_layer_widget.h"
  12. #include "core/application.h"
  13. #include "core/core_settings.h"
  14. #include "core/mime_type.h"
  15. #include "data/data_document.h"
  16. #include "data/data_document_media.h"
  17. #include "data/data_file_click_handler.h"
  18. #include "data/data_session.h"
  19. #include "history/history.h"
  20. #include "history/history_item.h"
  21. #include "history/view/media/history_view_gif.h"
  22. #include "lang/lang_keys.h"
  23. #include "media/player/media_player_instance.h"
  24. #include "platform/platform_file_utilities.h"
  25. #include "ui/boxes/confirm_box.h"
  26. #include "ui/chat/chat_theme.h"
  27. #include "ui/text/text_utilities.h"
  28. #include "ui/widgets/checkbox.h"
  29. #include "ui/wrap/slide_wrap.h"
  30. #include "window/window_session_controller.h"
  31. #include "styles/style_layers.h"
  32. #include <QtCore/QBuffer>
  33. #include <QtCore/QMimeType>
  34. #include <QtCore/QMimeDatabase>
  35. namespace Data {
  36. namespace {
  37. base::options::toggle OptionExternalVideoPlayer({
  38. .id = kOptionExternalVideoPlayer,
  39. .name = "External video player",
  40. .description = "Use system video player instead of the internal one. "
  41. "This disabes video playback in messages.",
  42. });
  43. void ConfirmDontWarnBox(
  44. not_null<Ui::GenericBox*> box,
  45. rpl::producer<TextWithEntities> &&text,
  46. rpl::producer<QString> &&check,
  47. rpl::producer<QString> &&confirm,
  48. Fn<void(bool)> callback) {
  49. auto checkbox = object_ptr<Ui::Checkbox>(
  50. box.get(),
  51. std::move(check),
  52. false,
  53. st::defaultBoxCheckbox);
  54. const auto weak = Ui::MakeWeak(checkbox.data());
  55. auto confirmed = crl::guard(weak, [=, callback = std::move(callback)] {
  56. const auto checked = weak->checked();
  57. box->closeBox();
  58. callback(checked);
  59. });
  60. Ui::ConfirmBox(box, {
  61. .text = std::move(text),
  62. .confirmed = std::move(confirmed),
  63. .confirmText = std::move(confirm),
  64. });
  65. auto padding = st::boxPadding;
  66. padding.setTop(padding.bottom());
  67. box->addRow(std::move(checkbox), std::move(padding));
  68. box->addRow(object_ptr<Ui::SlideWrap<Ui::FlatLabel>>(
  69. box,
  70. object_ptr<Ui::FlatLabel>(
  71. box,
  72. tr::lng_launch_dont_ask_settings(),
  73. st::boxLabel)
  74. ))->toggleOn(weak->checkedValue());
  75. }
  76. void LaunchWithWarning(
  77. // not_null<Window::Controller*> controller,
  78. const QString &name,
  79. HistoryItem *item) {
  80. const auto nameType = Core::DetectNameType(name);
  81. const auto isIpReveal = (nameType != Core::NameType::Executable)
  82. && Core::IsIpRevealingPath(name);
  83. const auto extension = Core::FileExtension(name).toLower();
  84. auto &app = Core::App();
  85. auto &settings = app.settings();
  86. const auto warn = [&] {
  87. if (item && item->history()->peer->isVerified()) {
  88. return false;
  89. }
  90. return (isIpReveal && settings.ipRevealWarning())
  91. || ((nameType == Core::NameType::Executable
  92. || nameType == Core::NameType::Unknown)
  93. && !settings.noWarningExtensions().contains(extension));
  94. }();
  95. if (extension.isEmpty()) {
  96. // If you launch a file without extension, like "test", in case
  97. // there is an executable file with the same name in this folder,
  98. // like "test.bat", the executable file will be launched.
  99. //
  100. // Now we always force an Open With dialog box for such files.
  101. //
  102. // Let's force it for all platforms for files without extension.
  103. crl::on_main([=] {
  104. Platform::File::UnsafeShowOpenWith(name);
  105. });
  106. return;
  107. } else if (!warn) {
  108. File::Launch(name);
  109. return;
  110. }
  111. const auto callback = [=, &app, &settings](bool checked) {
  112. if (checked) {
  113. if (isIpReveal) {
  114. settings.setIpRevealWarning(false);
  115. } else {
  116. auto copy = settings.noWarningExtensions();
  117. copy.emplace(extension);
  118. settings.setNoWarningExtensions(std::move(copy));
  119. }
  120. app.saveSettingsDelayed();
  121. }
  122. File::Launch(name);
  123. };
  124. auto text = isIpReveal
  125. ? tr::lng_launch_svg_warning(Ui::Text::WithEntities)
  126. : ((nameType == Core::NameType::Executable)
  127. ? tr::lng_launch_exe_warning
  128. : tr::lng_launch_other_warning)(
  129. lt_extension,
  130. rpl::single(Ui::Text::Bold('.' + extension)),
  131. Ui::Text::WithEntities);
  132. auto check = (isIpReveal
  133. ? tr::lng_launch_exe_dont_ask
  134. : tr::lng_launch_dont_ask)();
  135. auto confirm = ((nameType == Core::NameType::Executable)
  136. ? tr::lng_launch_exe_sure
  137. : tr::lng_launch_other_sure)();
  138. Ui::show(Box(
  139. ConfirmDontWarnBox,
  140. std::move(text),
  141. std::move(check),
  142. std::move(confirm),
  143. callback));
  144. }
  145. } // namespace
  146. const char kOptionExternalVideoPlayer[] = "external-video-player";
  147. base::binary_guard ReadBackgroundImageAsync(
  148. not_null<Data::DocumentMedia*> media,
  149. FnMut<QImage(QImage)> postprocess,
  150. FnMut<void(QImage&&)> done) {
  151. auto result = base::binary_guard();
  152. const auto gzipSvg = media->owner()->isPatternWallPaperSVG();
  153. crl::async([
  154. gzipSvg,
  155. bytes = media->bytes(),
  156. path = media->owner()->filepath(),
  157. postprocess = std::move(postprocess),
  158. guard = result.make_guard(),
  159. callback = std::move(done)
  160. ]() mutable {
  161. auto image = Ui::ReadBackgroundImage(path, bytes, gzipSvg);
  162. if (postprocess) {
  163. image = postprocess(std::move(image));
  164. }
  165. crl::on_main(std::move(guard), [
  166. image = std::move(image),
  167. callback = std::move(callback)
  168. ]() mutable {
  169. callback(std::move(image));
  170. });
  171. });
  172. return result;
  173. }
  174. void ResolveDocument(
  175. Window::SessionController *controller,
  176. not_null<DocumentData*> document,
  177. HistoryItem *item,
  178. MsgId topicRootId) {
  179. if (document->isNull()) {
  180. return;
  181. }
  182. const auto msgId = item ? item->fullId() : FullMsgId();
  183. const auto showDocument = [&] {
  184. if (OptionExternalVideoPlayer.value()
  185. && document->isVideoFile()
  186. && !document->filepath().isEmpty()) {
  187. File::Launch(document->location(false).fname);
  188. } else if (controller) {
  189. controller->openDocument(
  190. document,
  191. true,
  192. { msgId, topicRootId });
  193. }
  194. };
  195. const auto media = document->createMediaView();
  196. const auto openImageInApp = [&] {
  197. if (document->size >= Images::kReadBytesLimit) {
  198. return false;
  199. }
  200. const auto &location = document->location(true);
  201. const auto mime = u"image/"_q;
  202. if (!location.isEmpty() && location.accessEnable()) {
  203. const auto guard = gsl::finally([&] {
  204. location.accessDisable();
  205. });
  206. const auto path = location.name();
  207. if (Core::MimeTypeForFile(QFileInfo(path)).name().startsWith(mime)
  208. && QImageReader(path).canRead()) {
  209. showDocument();
  210. return true;
  211. }
  212. } else if (document->mimeString().startsWith(mime)
  213. && !media->bytes().isEmpty()) {
  214. auto bytes = media->bytes();
  215. auto buffer = QBuffer(&bytes);
  216. if (QImageReader(&buffer).canRead()) {
  217. showDocument();
  218. return true;
  219. }
  220. }
  221. return false;
  222. };
  223. const auto &location = document->location(true);
  224. if (document->isTheme() && media->loaded(true)) {
  225. showDocument();
  226. location.accessDisable();
  227. } else if (media->canBePlayed(item)) {
  228. if (document->isAudioFile()
  229. || document->isVoiceMessage()
  230. || document->isVideoMessage()) {
  231. ::Media::Player::instance()->playPause({ document, msgId });
  232. if (controller
  233. && item
  234. && item->media()
  235. && item->media()->ttlSeconds()) {
  236. ChatHelpers::ShowTTLMediaLayerWidget(controller, item);
  237. }
  238. } else {
  239. showDocument();
  240. }
  241. } else {
  242. document->saveFromDataSilent();
  243. if (!openImageInApp()) {
  244. if (!document->filepath(true).isEmpty()) {
  245. LaunchWithWarning(location.name(), item);
  246. } else if (document->status == FileReady
  247. || document->status == FileDownloadFailed) {
  248. DocumentSaveClickHandler::Save(
  249. item ? item->fullId() : Data::FileOrigin(),
  250. document);
  251. }
  252. }
  253. }
  254. }
  255. } // namespace Data