From be94858e1633d4bb879443f942b3abb7923e88de Mon Sep 17 00:00:00 2001 From: RadRussianRus Date: Sat, 18 Jan 2020 18:31:41 +0300 Subject: [PATCH] Merge checks --- Telegram/Resources/langs/lang.strings | 121 +- Telegram/Resources/langs/rewrites/ru.json | 98 +- Telegram/SourceFiles/_other/updater_linux.cpp | 4 +- Telegram/SourceFiles/_other/updater_osx.m | 2 +- Telegram/SourceFiles/api/api_sending.cpp | 1 - Telegram/SourceFiles/core/shortcuts.cpp | 6 + Telegram/SourceFiles/data/data_channel.cpp | 11 +- .../history/view/media/history_view_gif.cpp | 941 ++++- .../history/view/media/history_view_video.cpp | 656 ---- Telegram/SourceFiles/mtproto/connection.cpp | 3342 ----------------- Telegram/SourceFiles/mtproto/facade.h | 28 +- .../linux/notifications_manager_linux.cpp | 725 ++-- Telegram/SourceFiles/settings.cpp | 2 - Telegram/SourceFiles/settings.h | 6 +- .../SourceFiles/storage/file_download.cpp | 1277 +------ Telegram/SourceFiles/storage/file_download.h | 327 +- Telegram/SourceFiles/ui/twidget.cpp | 0 Telegram/ThirdParty/crl/.gitattributes | 63 - Telegram/ThirdParty/crl/.gitignore | 270 -- .../crl/src/crl/common/crl_common_config.h | 55 - .../crl/src/crl/common/crl_common_guards.h | 48 - .../crl/src/crl/common/crl_common_list.cpp | 67 - .../crl/src/crl/common/crl_common_list.h | 91 - .../crl/src/crl/common/crl_common_on_main.cpp | 64 - .../crl/src/crl/common/crl_common_on_main.h | 76 - .../crl/common/crl_common_on_main_guarded.h | 117 - .../crl/src/crl/common/crl_common_queue.cpp | 47 - .../crl/src/crl/common/crl_common_queue.h | 64 - .../crl/src/crl/common/crl_common_sync.h | 24 - .../crl/src/crl/common/crl_common_utils.h | 73 - Telegram/ThirdParty/crl/src/crl/crl.h | 15 - Telegram/ThirdParty/crl/src/crl/crl_async.h | 20 - .../crl/src/crl/crl_object_on_queue.h | 381 -- Telegram/ThirdParty/crl/src/crl/crl_on_main.h | 36 - Telegram/ThirdParty/crl/src/crl/crl_queue.h | 20 - .../ThirdParty/crl/src/crl/crl_semaphore.h | 20 - Telegram/ThirdParty/crl/src/crl/crl_time.cpp | 96 - Telegram/ThirdParty/crl/src/crl/crl_time.h | 43 - .../src/crl/dispatch/crl_dispatch_async.cpp | 46 - .../crl/src/crl/dispatch/crl_dispatch_async.h | 94 - .../src/crl/dispatch/crl_dispatch_on_main.h | 55 - .../src/crl/dispatch/crl_dispatch_queue.cpp | 57 - .../crl/src/crl/dispatch/crl_dispatch_queue.h | 87 - .../crl/dispatch/crl_dispatch_semaphore.cpp | 48 - .../src/crl/dispatch/crl_dispatch_semaphore.h | 48 - .../crl/src/crl/linux/crl_linux_time.cpp | 45 - .../crl/src/crl/mac/crl_mac_time.cpp | 47 - .../crl/src/crl/qt/crl_qt_async.cpp | 12 - .../ThirdParty/crl/src/crl/qt/crl_qt_async.h | 72 - .../ThirdParty/crl/src/crl/qt/crl_qt_guards.h | 117 - .../crl/src/crl/qt/crl_qt_semaphore.cpp | 12 - .../crl/src/crl/qt/crl_qt_semaphore.h | 41 - .../crl/src/crl/winapi/crl_winapi_async.cpp | 22 - .../crl/src/crl/winapi/crl_winapi_async.h | 51 - .../crl/src/crl/winapi/crl_winapi_dll.h | 67 - .../crl/src/crl/winapi/crl_winapi_list.cpp | 93 - .../crl/src/crl/winapi/crl_winapi_list.h | 102 - .../src/crl/winapi/crl_winapi_semaphore.cpp | 40 - .../crl/src/crl/winapi/crl_winapi_semaphore.h | 48 - .../crl/src/crl/winapi/crl_winapi_time.cpp | 51 - .../crl/src/crl/winapi/crl_winapi_windows_h.h | 14 - Telegram/ThirdParty/crl/src/test.cpp | 110 - Telegram/lib_ui | 2 +- lib/xdg/kotatogramdesktop.desktop | 2 +- lib/xdg/telegramdesktop.desktop | 1 - 65 files changed, 1211 insertions(+), 9410 deletions(-) delete mode 100644 Telegram/SourceFiles/history/view/media/history_view_video.cpp delete mode 100644 Telegram/SourceFiles/ui/twidget.cpp delete mode 100644 Telegram/ThirdParty/crl/.gitattributes delete mode 100644 Telegram/ThirdParty/crl/.gitignore delete mode 100644 Telegram/ThirdParty/crl/src/crl/common/crl_common_config.h delete mode 100644 Telegram/ThirdParty/crl/src/crl/common/crl_common_guards.h delete mode 100644 Telegram/ThirdParty/crl/src/crl/common/crl_common_list.cpp delete mode 100644 Telegram/ThirdParty/crl/src/crl/common/crl_common_list.h delete mode 100644 Telegram/ThirdParty/crl/src/crl/common/crl_common_on_main.cpp delete mode 100644 Telegram/ThirdParty/crl/src/crl/common/crl_common_on_main.h delete mode 100644 Telegram/ThirdParty/crl/src/crl/common/crl_common_on_main_guarded.h delete mode 100644 Telegram/ThirdParty/crl/src/crl/common/crl_common_queue.cpp delete mode 100644 Telegram/ThirdParty/crl/src/crl/common/crl_common_queue.h delete mode 100644 Telegram/ThirdParty/crl/src/crl/common/crl_common_sync.h delete mode 100644 Telegram/ThirdParty/crl/src/crl/common/crl_common_utils.h delete mode 100644 Telegram/ThirdParty/crl/src/crl/crl.h delete mode 100644 Telegram/ThirdParty/crl/src/crl/crl_async.h delete mode 100644 Telegram/ThirdParty/crl/src/crl/crl_object_on_queue.h delete mode 100644 Telegram/ThirdParty/crl/src/crl/crl_on_main.h delete mode 100644 Telegram/ThirdParty/crl/src/crl/crl_queue.h delete mode 100644 Telegram/ThirdParty/crl/src/crl/crl_semaphore.h delete mode 100644 Telegram/ThirdParty/crl/src/crl/crl_time.cpp delete mode 100644 Telegram/ThirdParty/crl/src/crl/crl_time.h delete mode 100644 Telegram/ThirdParty/crl/src/crl/dispatch/crl_dispatch_async.cpp delete mode 100644 Telegram/ThirdParty/crl/src/crl/dispatch/crl_dispatch_async.h delete mode 100644 Telegram/ThirdParty/crl/src/crl/dispatch/crl_dispatch_on_main.h delete mode 100644 Telegram/ThirdParty/crl/src/crl/dispatch/crl_dispatch_queue.cpp delete mode 100644 Telegram/ThirdParty/crl/src/crl/dispatch/crl_dispatch_queue.h delete mode 100644 Telegram/ThirdParty/crl/src/crl/dispatch/crl_dispatch_semaphore.cpp delete mode 100644 Telegram/ThirdParty/crl/src/crl/dispatch/crl_dispatch_semaphore.h delete mode 100644 Telegram/ThirdParty/crl/src/crl/linux/crl_linux_time.cpp delete mode 100644 Telegram/ThirdParty/crl/src/crl/mac/crl_mac_time.cpp delete mode 100644 Telegram/ThirdParty/crl/src/crl/qt/crl_qt_async.cpp delete mode 100644 Telegram/ThirdParty/crl/src/crl/qt/crl_qt_async.h delete mode 100644 Telegram/ThirdParty/crl/src/crl/qt/crl_qt_guards.h delete mode 100644 Telegram/ThirdParty/crl/src/crl/qt/crl_qt_semaphore.cpp delete mode 100644 Telegram/ThirdParty/crl/src/crl/qt/crl_qt_semaphore.h delete mode 100644 Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_async.cpp delete mode 100644 Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_async.h delete mode 100644 Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_dll.h delete mode 100644 Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_list.cpp delete mode 100644 Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_list.h delete mode 100644 Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_semaphore.cpp delete mode 100644 Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_semaphore.h delete mode 100644 Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_time.cpp delete mode 100644 Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_windows_h.h delete mode 100644 Telegram/ThirdParty/crl/src/test.cpp delete mode 100644 lib/xdg/telegramdesktop.desktop diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 4738da233..616a59af0 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2246,66 +2246,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL // Kotatogram keys "ktg_intro_about" = "Welcome to unofficial messaging app\nbased on Telegram Desktop."; - "ktg_about_text1" = "Experimental unofficial {tdesktop_link} fork."; "ktg_about_text1_tdesktop" = "Telegram Desktop"; "ktg_about_text3" = "Visit {channel_link} or {faq_link} for more info."; "ktg_about_text3_channel" = "Kotatogram channel"; -"ktg_copy_btn_callback" = "Copy callback data"; -"ktg_profile_copy_id" = "Copy ID"; -"ktg_profile_bot_id" = "Bot ID"; -"ktg_profile_user_id" = "User ID"; -"ktg_profile_group_id" = "Group ID"; -"ktg_profile_supergroup_id" = "Supergroup ID"; -"ktg_profile_channel_id" = "Channel ID"; -"ktg_pinned_message_show" = "Show pinned message"; -"ktg_pinned_message_hide" = "Hide pinned message"; - -"ktg_settings_show_json_settings" = "Show custom settings"; -"ktg_settings_restart" = "Restart Kotatogram"; - -"ktg_chat_status_subscribers#one" = "{count} subscriber"; -"ktg_chat_status_subscribers#other" = "{count} subscribers"; - -"ktg_profile_subscribers_section" = "Subscribers"; - -"ktg_settings_kotato" = "Kotatogram Settings"; -"ktg_settings_chats" = "Chats"; -"ktg_settings_sticker_height" = "Sticker height: {pixels}px"; -"ktg_settings_adaptive_baloons" = "Adaptive baloons"; -"ktg_settings_emoji_outline" = "Big emoji outline"; -"ktg_settings_always_show_scheduled" = "Always show scheduled"; -"ktg_settings_fonts" = "Change application fonts"; -"ktg_settings_network" = "Network"; -"ktg_settings_net_speed_boost" = "Speed boost"; -"ktg_settings_other" = "Other"; -"ktg_settings_show_phone_number" = "Show phone in drawer"; -"ktg_settings_show_chat_id" = "Show chat ID"; - -"ktg_fonts_title" = "Fonts"; -"ktg_fonts_reset" = "Reset"; -"ktg_fonts_about" = "If you want to use \"Bold\" font for your semibold, do not put \"Bold\" in font name, use \"Make semibold bold\" option.\n\nYou will need to restart app to apply and see changes."; - -"ktg_fonts_main" = "Main font"; -"ktg_fonts_semibold" = "Semibold font"; -"ktg_fonts_semibold_is_bold" = "Make semibold bold"; -"ktg_fonts_monospaced" = "Monospaced font"; - -"ktg_fonts_restart_new_fonts" = "You will need to restart app to apply new fonts.\n\nRestart now?"; -"ktg_fonts_restart_reset" = "You will need to restart app to reset fonts to default.\n\nRestart now?"; - -"ktg_fonts_restart" = "Restart"; - -"ktg_net_speed_boost_title" = "Network speed boost"; -"ktg_net_speed_boost_desc" = "Warning: changing this parameter to high values on slow networks can make even worse. Use at your own risk.\n\nYou'll need to restart app to save changes."; - -"ktg_net_speed_boost_default" = "Disabled"; -"ktg_net_speed_boost_slight" = "Slight"; -"ktg_net_speed_boost_medium" = "Medium"; -"ktg_net_speed_boost_big" = "Big"; - -"ktg_net_boost_restart_desc" = "You'll need to restart app to change network boost.\n\nRestart now?"; +// Replaces "ktg_open_from_tray" = "Open Kotatogram"; "ktg_quit_from_tray" = "Quit Kotatogram"; "ktg_tray_icon_text" = "Kotatogram is still running here,\nyou can change this from settings page.\nIf this icon disappears from tray menu,\nyou can drag it here from hidden icons."; @@ -2330,10 +2276,71 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "ktg_outdated_now" = "So that Kotatogram Desktop can update to newer versions."; "ktg_mac_menu_show" = "Show Kotatogram"; -"ktg_manage_peer_subscribers" = "Subscribers"; +"ktg_settings_kotato" = "Kotatogram Settings"; + +"ktg_settings_chats" = "Chats"; + +"ktg_settings_sticker_height" = "Sticker height: {pixels}px"; +"ktg_settings_emoji_outline" = "Big emoji outline"; + +"ktg_settings_always_show_scheduled" = "Always show scheduled"; +"ktg_settings_chat_list_compact" = "Compact chat list"; + +"ktg_fonts_title" = "Fonts"; +"ktg_settings_fonts" = "Change application fonts"; +"ktg_fonts_reset" = "Reset"; +"ktg_fonts_about" = "If you want to use \"Bold\" font for your semibold, do not put \"Bold\" in font name, use \"Make semibold bold\" option.\n\nYou will need to restart app to apply and see changes."; + +"ktg_fonts_main" = "Main font"; +"ktg_fonts_semibold" = "Semibold font"; +"ktg_fonts_semibold_is_bold" = "Make semibold bold"; +"ktg_fonts_monospaced" = "Monospaced font"; + +"ktg_fonts_restart_new_fonts" = "You will need to restart app to apply new fonts.\n\nRestart now?"; +"ktg_fonts_restart_reset" = "You will need to restart app to reset fonts to default.\n\nRestart now?"; + +"ktg_fonts_restart" = "Restart"; + +"ktg_settings_network" = "Network"; +"ktg_settings_net_speed_boost" = "Upload speed boost"; + +"ktg_net_speed_boost_title" = "Upload speed boost"; +"ktg_net_speed_boost_desc" = "Warning: changing this parameter to high values on slow networks can make even worse. Use at your own risk.\n\nYou'll need to restart app to save changes."; + +"ktg_net_speed_boost_default" = "Disabled"; +"ktg_net_speed_boost_slight" = "Slight"; +"ktg_net_speed_boost_medium" = "Medium"; +"ktg_net_speed_boost_big" = "Big"; + +"ktg_net_boost_restart_desc" = "You'll need to restart app to change upload boost.\n\nRestart now?"; + +"ktg_settings_other" = "Other"; +"ktg_settings_show_chat_id" = "Show chat ID"; +"ktg_profile_copy_id" = "Copy ID"; +"ktg_profile_bot_id" = "Bot ID"; +"ktg_profile_user_id" = "User ID"; +"ktg_profile_group_id" = "Group ID"; +"ktg_profile_supergroup_id" = "Supergroup ID"; +"ktg_profile_channel_id" = "Channel ID"; + +"ktg_settings_show_phone_number" = "Show phone in drawer"; "ktg_user_status_unaccessible" = "account inaccessible"; -"ktg_settings_chat_list_compact" = "Compact chat list"; +"ktg_chat_status_subscribers#one" = "{count} subscriber"; +"ktg_chat_status_subscribers#other" = "{count} subscribers"; + +"ktg_profile_subscribers_section" = "Subscribers"; +"ktg_manage_peer_subscribers" = "Subscribers"; + +"ktg_settings_show_json_settings" = "Show custom settings"; +"ktg_settings_restart" = "Restart Kotatogram"; + +"ktg_copy_btn_callback" = "Copy callback data"; + +"ktg_pinned_message_show" = "Show pinned message"; +"ktg_pinned_message_hide" = "Hide pinned message"; + +"ktg_settings_adaptive_baloons" = "Adaptive baloons"; // Keys finished diff --git a/Telegram/Resources/langs/rewrites/ru.json b/Telegram/Resources/langs/rewrites/ru.json index 11f871b8b..ace3fca5c 100644 --- a/Telegram/Resources/langs/rewrites/ru.json +++ b/Telegram/Resources/langs/rewrites/ru.json @@ -9,53 +9,6 @@ "ktg_about_text1": "Экспериментальный неофициальный форк {tdesktop_link}.", "ktg_about_text3": "Посетите {channel_link} или {faq_link} (англ.), чтобы узнать больше.", "ktg_about_text3_channel": "канал Kotatogram", - "ktg_copy_btn_callback": "Копировать callback-данные", - "ktg_profile_copy_id": "Копировать ID", - "ktg_profile_bot_id": "ID бота", - "ktg_profile_user_id": "ID пользователя", - "ktg_profile_group_id": "ID группы", - "ktg_profile_supergroup_id": "ID супергруппы", - "ktg_profile_channel_id": "ID канала", - "ktg_pinned_message_show": "Показать закреплённое сообщение", - "ktg_pinned_message_hide": "Скрыть закреплённое сообщение", - "ktg_settings_show_json_settings": "Показать файл настроек", - "ktg_settings_restart": "Перезапустить Kotatogram", - "ktg_chat_status_subscribers": { - "one": "{count} подписчик", - "few": "{count} подписчика", - "many": "{count} подписчиков", - "other": "{count} подписчиков" - }, - "ktg_profile_subscribers_section": "Подписчики", - "ktg_settings_kotato": "Настройки Kotatogram", - "ktg_settings_chats": "Чаты", - "ktg_settings_sticker_height": "Высота стикеров: {pixels} пикс.", - "ktg_settings_adaptive_baloons": "Адаптивные пузырьки сообщений", - "ktg_settings_emoji_outline": "Обводка у больших эмодзи", - "ktg_settings_always_show_scheduled": "Всегда показывать отложенные", - "ktg_settings_fonts": "Изменить шрифты приложения", - "ktg_settings_network": "Сеть", - "ktg_settings_net_speed_boost": "Ускорение загрузки", - "ktg_settings_other": "Прочие", - "ktg_settings_show_phone_number": "Показывать телефон в боковом меню", - "ktg_settings_show_chat_id": "Показывать ID чата", - "ktg_fonts_title": "Шрифты", - "ktg_fonts_reset": "Сброс", - "ktg_fonts_about": "Если вы хотите использовать жирное начертание для полужирного шрифта, не пишите «Bold» в названии, используйте настройку «Сделать полужирный жирным».\n\nДля применения и просмотра изменений требуется перезапуск.", - "ktg_fonts_main": "Основной шрифт", - "ktg_fonts_semibold": "Полужирный шрифт", - "ktg_fonts_semibold_is_bold": "Сделать полужирный жирным", - "ktg_fonts_monospaced": "Моноширинный шрифт", - "ktg_fonts_restart_new_fonts": "Для применения новых шрифтов требуется перезапуск.\n\nПерезапустить сейчас?", - "ktg_fonts_restart_reset": "Для сброса шрифтов к стандартным требуется перезапуск.\n\nПерезапустить сейчас?", - "ktg_fonts_restart": "Перезапустить", - "ktg_net_speed_boost_title": "Ускорение загрузки", - "ktg_net_speed_boost_desc": "Внимание: высокие значения параметра при слабых сетях могут сделать ещё хуже. Используйте на свой страх и риск.\n\nДля сохранения изменений требуется перезапуск.", - "ktg_net_speed_boost_default": "Отключено", - "ktg_net_speed_boost_slight": "Небольшое", - "ktg_net_speed_boost_medium": "Среднее", - "ktg_net_speed_boost_big": "Высокое", - "ktg_net_boost_restart_desc": "Для изменения ускорения загрузки требуется перезапуск.\n\nПерезапустить сейчас?", "ktg_open_from_tray": "Открыть Kotatogram", "ktg_quit_from_tray": "Закрыть Kotatogram", "ktg_tray_icon_text": "Kotatogram продолжает работать,\nВы можете изменить это в настройках.\nЕсли значок исчезнет из области уведомлений,\nего можно перенести из скрытых значков.", @@ -79,7 +32,54 @@ "ktg_outdated_soon": "Иначе приложение перестанет обновляться с {date}.", "ktg_outdated_now": "Чтобы приложение Kotatogram могло получать обновления.", "ktg_mac_menu_show": "Показать Kotatogram", - "ktg_manage_peer_subscribers": "Подписчики", + "ktg_settings_kotato": "Настройки Kotatogram", "ktg_user_status_unaccessible": "аккаунт недоступен", - "ktg_settings_chat_list_compact": "Компактный список чатов" + "ktg_chat_status_subscribers": { + "one": "{count} подписчик", + "few": "{count} подписчика", + "many": "{count} подписчиков", + "other": "{count} подписчиков" + }, + "ktg_profile_subscribers_section": "Подписчики", + "ktg_manage_peer_subscribers": "Подписчики", + "ktg_settings_show_json_settings": "Показать файл настроек", + "ktg_settings_restart": "Перезапустить Kotatogram", + "ktg_copy_btn_callback": "Копировать callback-данные", + "ktg_pinned_message_show": "Показать закреплённое сообщение", + "ktg_pinned_message_hide": "Скрыть закреплённое сообщение", + "ktg_settings_chats": "Чаты", + "ktg_settings_sticker_height": "Высота стикеров: {pixels} пикс.", + "ktg_settings_emoji_outline": "Обводка у больших эмодзи", + "ktg_settings_always_show_scheduled": "Всегда показывать отложенные", + "ktg_settings_chat_list_compact": "Компактный список чатов", + "ktg_fonts_title": "Шрифты", + "ktg_settings_fonts": "Изменить шрифты приложения", + "ktg_fonts_reset": "Сброс", + "ktg_fonts_about": "Если вы хотите использовать жирное начертание для полужирного шрифта, не пишите «Bold» в названии, используйте настройку «Сделать полужирный жирным».\n\nДля применения и просмотра изменений требуется перезапуск.", + "ktg_fonts_main": "Основной шрифт", + "ktg_fonts_semibold": "Полужирный шрифт", + "ktg_fonts_semibold_is_bold": "Сделать полужирный жирным", + "ktg_fonts_monospaced": "Моноширинный шрифт", + "ktg_fonts_restart_new_fonts": "Для применения новых шрифтов требуется перезапуск.\n\nПерезапустить сейчас?", + "ktg_fonts_restart_reset": "Для сброса шрифтов к стандартным требуется перезапуск.\n\nПерезапустить сейчас?", + "ktg_fonts_restart": "Перезапустить", + "ktg_settings_network": "Сеть", + "ktg_settings_net_speed_boost": "Ускорение загрузки на сервер", + "ktg_net_speed_boost_title": "Ускорение загрузки на сервер", + "ktg_net_speed_boost_desc": "Внимание: высокие значения параметра при слабых сетях могут сделать ещё хуже. Используйте на свой страх и риск.\n\nДля сохранения изменений требуется перезапуск.", + "ktg_net_speed_boost_default": "Отключено", + "ktg_net_speed_boost_slight": "Небольшое", + "ktg_net_speed_boost_medium": "Среднее", + "ktg_net_speed_boost_big": "Высокое", + "ktg_net_boost_restart_desc": "Для изменения ускорения загрузки на сервер требуется перезапуск.\n\nПерезапустить сейчас?", + "ktg_settings_other": "Прочие", + "ktg_settings_show_chat_id": "Показывать ID чата", + "ktg_profile_copy_id": "Копировать ID", + "ktg_profile_bot_id": "ID бота", + "ktg_profile_user_id": "ID пользователя", + "ktg_profile_group_id": "ID группы", + "ktg_profile_supergroup_id": "ID супергруппы", + "ktg_profile_channel_id": "ID канала", + "ktg_settings_show_phone_number": "Показывать телефон в боковом меню", + "ktg_settings_adaptive_baloons": "Адаптивные пузырьки сообщений" } diff --git a/Telegram/SourceFiles/_other/updater_linux.cpp b/Telegram/SourceFiles/_other/updater_linux.cpp index cef84d75d..4f2e576ae 100644 --- a/Telegram/SourceFiles/_other/updater_linux.cpp +++ b/Telegram/SourceFiles/_other/updater_linux.cpp @@ -266,7 +266,7 @@ bool update() { writeLog("Error: bad update, has Updater! '%s' equal '%s'", tofname.c_str(), updaterName.c_str()); delFolder(); return false; - } else if (equal(tofname, exePath + "Telegram") && exeName != "Telegram") { + } else if (equal(tofname, exePath + "Kotatogram") && exeName != "Kotatogram") { string fullBinaryPath = exePath + exeName; writeLog("Target binary found: '%s', changing to '%s'", tofname.c_str(), fullBinaryPath.c_str()); tofname = fullBinaryPath; @@ -482,7 +482,7 @@ int main(int argc, char *argv[]) { return 1; } - writeLog("Executed Telegram, closing log and quitting.."); + writeLog("Executed Kotatogram, closing log and quitting.."); closeLog(); return 0; diff --git a/Telegram/SourceFiles/_other/updater_osx.m b/Telegram/SourceFiles/_other/updater_osx.m index f8bb4b14f..bf8e70e6b 100644 --- a/Telegram/SourceFiles/_other/updater_osx.m +++ b/Telegram/SourceFiles/_other/updater_osx.m @@ -165,7 +165,7 @@ int main(int argc, const char * argv[]) { writeLog([@"Starting update files iteration, path: " stringByAppendingString: srcEnum]); - // Take the Updater (this currently running binary) from the place where it was placed by Telegram + // Take the Updater (this currently running binary) from the place where it was placed by Kotatogram // and copy it to the folder with the new version of the app (ready), // so it won't be deleted when we will clear the "Kotatogram.app/Contents" folder. NSString *oldVersionUpdaterPath = [appDirFull stringByAppendingString: @"/Contents/Frameworks/Updater" ]; diff --git a/Telegram/SourceFiles/api/api_sending.cpp b/Telegram/SourceFiles/api/api_sending.cpp index 121a54491..45476bfd5 100644 --- a/Telegram/SourceFiles/api/api_sending.cpp +++ b/Telegram/SourceFiles/api/api_sending.cpp @@ -38,7 +38,6 @@ void SendExistingMedia( const auto session = &history->session(); const auto api = &session->api(); - media->refreshFileReference("blabla"); message.action.clearDraft = false; message.action.generateLocal = true; api->sendAction(message.action); diff --git a/Telegram/SourceFiles/core/shortcuts.cpp b/Telegram/SourceFiles/core/shortcuts.cpp index 4886ddfc2..7d13cd9fa 100644 --- a/Telegram/SourceFiles/core/shortcuts.cpp +++ b/Telegram/SourceFiles/core/shortcuts.cpp @@ -75,6 +75,8 @@ const auto CommandByName = base::flat_map{ { qsl("last_chat") , Command::ChatLast }, { qsl("self_chat") , Command::ChatSelf }, + { qsl("show_archive") , Command::ShowArchive }, + { qsl("save_draft") , Command::SaveDraft }, }; @@ -99,6 +101,8 @@ const auto CommandNames = base::flat_map{ { Command::ChatLast , qsl("last_chat") }, { Command::ChatSelf , qsl("self_chat") }, + { Command::ShowArchive , qsl("show_archive") }, + { Command::SaveDraft , qsl("save_draft") }, }; @@ -336,6 +340,8 @@ void Manager::fillDefaults() { set(qsl("ctrl+0"), Command::ChatSelf); + set(qsl("ctrl+9"), Command::ShowArchive); + set(qsl("ctrl+s"), Command::SaveDraft); } diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index e72cf0ff0..d002653af 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -360,13 +360,14 @@ QString ChannelData::adminRank(not_null user) const { : QString(); } -QString ChannelData::unavailableReason() const { - return _unavailableReason; +auto ChannelData::unavailableReasons() const +-> const std::vector & { + return _unavailableReasons; } -void ChannelData::setUnavailableReason(const QString &text) { - if (_unavailableReason != text) { - _unavailableReason = text; +void ChannelData::setUnavailableReasons(std::vector &&reasons) { + if (_unavailableReasons != reasons) { + _unavailableReasons = std::move(reasons); Notify::peerUpdatedDelayed( this, Notify::PeerUpdate::Flag::UnavailableReasonChanged); diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp index ac60117be..af891bcad 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/audio/media_audio.h" #include "media/clip/media_clip_reader.h" #include "media/player/media_player_instance.h" +#include "media/streaming/media_streaming_instance.h" #include "media/streaming/media_streaming_player.h" #include "media/view/media_view_playback_progress.h" #include "boxes/confirm_box.h" @@ -22,12 +23,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "history/view/history_view_element.h" #include "history/view/history_view_cursor_state.h" +#include "history/view/media/history_view_media_common.h" #include "window/window_session_controller.h" #include "core/application.h" // Application::showDocument. #include "ui/image/image.h" +#include "ui/grouped_layout.h" #include "data/data_session.h" +#include "data/data_streaming.h" #include "data/data_document.h" #include "data/data_file_origin.h" +#include "mainwidget.h" #include "app.h" #include "styles/style_history.h" @@ -35,6 +40,7 @@ namespace HistoryView { namespace { constexpr auto kMaxGifForwardedBarLines = 4; +constexpr auto kUseNonBlurredThreshold = 160; int gifMaxStatusWidth(DocumentData *document) { auto result = st::normalFont->width(formatDownloadText(document->size, document->size)); @@ -44,19 +50,57 @@ int gifMaxStatusWidth(DocumentData *document) { } // namespace +struct Gif::Streamed { + Streamed( + std::shared_ptr<::Media::Streaming::Document> shared, + Fn waitingCallback); + ::Media::Streaming::Instance instance; + ::Media::Streaming::FrameRequest frozenRequest; + QImage frozenFrame; + QString frozenStatusText; +}; + +Gif::Streamed::Streamed( + std::shared_ptr<::Media::Streaming::Document> shared, + Fn waitingCallback) +: instance(std::move(shared), std::move(waitingCallback)) { +} + Gif::Gif( not_null parent, + not_null realParent, not_null document) -: File(parent, parent->data()) +: File(parent, realParent) , _data(document) -, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) { - const auto item = parent->data(); - setDocumentLinks(_data, item); +, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) +, _downloadSize(formatSizeText(_data->size)) { + setDocumentLinks(_data, realParent); setStatusSize(FileStatusSizeReady); - _caption = createCaption(item); - _data->loadThumbnail(item->fullId()); + refreshCaption(); + _data->loadThumbnail(realParent->fullId()); +} + +Gif::~Gif() { + if (_streamed) { + _data->owner().streaming().keepAlive(_data); + setStreamed(nullptr); + } +} + +QSize Gif::sizeForAspectRatio() const { + // We use size only for aspect ratio and we want to have it + // as close to the thumbnail as possible. + //if (!_data->dimensions.isEmpty()) { + // return _data->dimensions; + //} + if (const auto thumb = _data->thumbnail()) { + if (!thumb->size().isEmpty()) { + return thumb->size(); + } + } + return { 1, 1 }; } QSize Gif::countOptimalSize() { @@ -67,17 +111,13 @@ QSize Gif::countOptimalSize() { _parent->skipBlockWidth(), _parent->skipBlockHeight()); } - if (_gif && _gif->state() == ::Media::Clip::State::Error) { - if (!_gif->autoplay()) { - Ui::show(Box(tr::lng_gif_error(tr::now))); - } - setClipReader(::Media::Clip::ReaderPointer::Bad()); - } const auto captionWithPaddings = _caption.maxWidth() + st::msgPadding.left() + st::msgPadding.right(); - const auto maxSize = _data->isVideoMessage() + const auto maxSize = _data->isVideoFile() + ? st::maxMediaSize + : _data->isVideoMessage() ? st::maxVideoMessageSize : st::maxGifSize; const auto size = style::ConvertScale(videoSize()); @@ -102,7 +142,7 @@ QSize Gif::countOptimalSize() { auto maxWidth = qMax(tw, st::minPhotoSize); auto minHeight = qMax(th, st::minPhotoSize); accumulate_max(maxWidth, _parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); - if (!currentReader() && !activeRoundPlayer()) { + if (!activeCurrentStreamed()) { accumulate_max(maxWidth, gifMaxStatusWidth(_data) + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); } if (_parent->hasBubble()) { @@ -135,7 +175,9 @@ QSize Gif::countCurrentSize(int newWidth) { const auto captionWithPaddings = _caption.maxWidth() + st::msgPadding.left() + st::msgPadding.right(); - const auto maxSize = _data->isVideoMessage() + const auto maxSize = _data->isVideoFile() + ? st::maxMediaSize + : _data->isVideoMessage() ? st::maxVideoMessageSize : st::maxGifSize; const auto size = style::ConvertScale(videoSize()); @@ -166,34 +208,7 @@ QSize Gif::countCurrentSize(int newWidth) { newWidth = qMax(tw, st::minPhotoSize); auto newHeight = qMax(th, st::minPhotoSize); accumulate_max(newWidth, _parent->infoWidth() + 2 * st::msgDateImgDelta + st::msgDateImgPadding.x()); - const auto reader = activeRoundPlayer() ? nullptr : currentReader(); - if (reader) { - const auto own = (reader->mode() == ::Media::Clip::Reader::Mode::Gif); - if (own && !reader->started()) { - auto isRound = _data->isVideoMessage(); - auto inWebPage = (_parent->media() != this); - auto roundRadius = isRound - ? ImageRoundRadius::Ellipse - : inWebPage - ? ImageRoundRadius::Small - : ImageRoundRadius::Large; - auto roundCorners = (isRound || inWebPage) - ? RectPart::AllCorners - : ((isBubbleTop() - ? (RectPart::TopLeft | RectPart::TopRight) - : RectPart::None) - | ((isBubbleBottom() && _caption.isEmpty()) - ? (RectPart::BottomLeft | RectPart::BottomRight) - : RectPart::None)); - reader->start( - _thumbw, - _thumbh, - newWidth, - newHeight, - roundRadius, - roundCorners); - } - } else { + if (!activeCurrentStreamed()) { accumulate_max(newWidth, gifMaxStatusWidth(_data) + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); } if (_parent->hasBubble()) { @@ -232,10 +247,8 @@ QSize Gif::countCurrentSize(int newWidth) { } QSize Gif::videoSize() const { - if (const auto player = activeRoundPlayer()) { - return player->videoSize(); - } else if (const auto reader = currentReader()) { - return QSize(reader->width(), reader->height()); + if (const auto streamed = activeCurrentStreamed()) { + return streamed->player().videoSize(); } else if (!_data->dimensions.isEmpty()) { return _data->dimensions; } else if (const auto thumbnail = _data->thumbnail()) { @@ -245,26 +258,42 @@ QSize Gif::videoSize() const { } } +bool Gif::downloadInCorner() const { + return _data->isVideoFile() + && (_data->loading() || !autoplayEnabled()) + && _data->canBeStreamed() + && !_data->inappPlaybackFailed() + && IsServerMsgId(_parent->data()->id); +} + bool Gif::autoplayEnabled() const { - return history()->session().settings().autoplayGifs(); + return Data::AutoDownload::ShouldAutoPlay( + _data->session().settings().autoDownload(), + _realParent->history()->peer, + _data); } void Gif::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const { if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return; const auto item = _parent->data(); - _data->automaticLoad(_realParent->fullId(), item); - auto loaded = _data->loaded(); - auto displayLoading = (item->id < 0) || _data->displayLoading(); - auto selected = (selection == FullSelection); - - if (loaded - && autoplayEnabled() - && !_gif - && !_gif.isBad() - && !activeRoundPlayer()) { - _parent->delegate()->elementAnimationAutoplayAsync(_parent); + const auto displayLoading = item->isSending() || _data->displayLoading(); + const auto selected = (selection == FullSelection); + const auto autoPaused = App::wnd()->sessionController()->isGifPausedAtLeastFor(Window::GifPauseReason::Any); + const auto cornerDownload = downloadInCorner(); + const auto canBePlayed = _data->canBePlayed(); + const auto autoplay = autoplayEnabled() && canBePlayed; + const auto activeRoundPlaying = activeRoundStreamed(); + const auto startPlay = autoplay + && !_streamed + && !activeRoundPlaying; + if (startPlay) { + const_cast(this)->playAnimation(true); + } else { + checkStreamedIsStarted(); } + const auto streamingMode = _streamed || activeRoundPlaying || autoplay; + const auto activeOwnPlaying = activeOwnStreamed(); auto paintx = 0, painty = 0, paintw = width(), painth = height(); bool bubble = _parent->hasBubble(); @@ -275,18 +304,30 @@ void Gif::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms const auto isRound = _data->isVideoMessage(); auto displayMute = false; - const auto player = activeRoundPlayer(); - const auto reader = player ? nullptr : currentReader(); - const auto animating = player || (reader && reader->started()); + const auto streamed = activeRoundPlaying + ? activeRoundPlaying + : activeOwnPlaying + ? &activeOwnPlaying->instance + : nullptr; + const auto streamedForWaiting = activeRoundPlaying + ? activeRoundPlaying + : _streamed + ? &_streamed->instance + : nullptr; - if ((!animating || item->id < 0) && displayLoading) { + if (displayLoading + && (!streamedForWaiting + || item->isSending() + || _data->uploading() + || (cornerDownload && _data->loading()))) { ensureAnimation(); if (!_animation->radial.animating()) { _animation->radial.start(dataProgress()); } } updateStatusText(); - const auto radial = isRadialAnimation(); + const auto radial = isRadialAnimation() + || (streamedForWaiting && streamedForWaiting->waitingShown()); if (bubble) { if (!_caption.isEmpty()) { @@ -317,24 +358,39 @@ void Gif::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms auto roundRadius = isRound ? ImageRoundRadius::Ellipse : inWebPage ? ImageRoundRadius::Small : ImageRoundRadius::Large; auto roundCorners = (isRound || inWebPage) ? RectPart::AllCorners : ((isBubbleTop() ? (RectPart::TopLeft | RectPart::TopRight) : RectPart::None) | ((isBubbleBottom() && _caption.isEmpty()) ? (RectPart::BottomLeft | RectPart::BottomRight) : RectPart::None)); - if (animating) { - auto paused = App::wnd()->sessionController()->isGifPausedAtLeastFor(Window::GifPauseReason::Any); + if (streamed) { + auto paused = autoPaused; if (isRound) { - if (player) { + if (activeRoundStreamed()) { paused = false; } else { displayMute = true; } } - if (player) { - auto request = ::Media::Streaming::FrameRequest(); - request.outer = QSize(usew, painth) * cIntRetinaFactor(); - request.resize = QSize(_thumbw, _thumbh) * cIntRetinaFactor(); - request.corners = roundCorners; - request.radius = roundRadius; - p.drawImage(rthumb, player->frame(request)); + auto request = ::Media::Streaming::FrameRequest(); + request.outer = QSize(usew, painth) * cIntRetinaFactor(); + request.resize = QSize(_thumbw, _thumbh) * cIntRetinaFactor(); + request.corners = roundCorners; + request.radius = roundRadius; + if (!activeRoundPlaying && activeOwnPlaying->instance.playerLocked()) { + if (activeOwnPlaying->frozenFrame.isNull()) { + activeOwnPlaying->frozenRequest = request; + activeOwnPlaying->frozenFrame = streamed->frame(request); + activeOwnPlaying->frozenStatusText = _statusText; + } else if (activeOwnPlaying->frozenRequest != request) { + activeOwnPlaying->frozenRequest = request; + activeOwnPlaying->frozenFrame = streamed->frame(request); + } + p.drawImage(rthumb, activeOwnPlaying->frozenFrame); } else { - p.drawPixmap(rthumb.topLeft(), reader->current(_thumbw, _thumbh, usew, painth, roundRadius, roundCorners, paused ? 0 : ms)); + if (activeOwnPlaying) { + activeOwnPlaying->frozenFrame = QImage(); + activeOwnPlaying->frozenStatusText = QString(); + } + p.drawImage(rthumb, streamed->frame(request)); + if (!paused) { + streamed->markFrameShown(); + } } if (const auto playback = videoPlayback()) { @@ -369,7 +425,12 @@ void Gif::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms } const auto normal = _data->thumbnail(); if (normal && normal->loaded()) { - p.drawPixmap(rthumb.topLeft(), normal->pixSingle(_realParent->fullId(), _thumbw, _thumbh, usew, painth, roundRadius, roundCorners)); + if (normal->width() >= kUseNonBlurredThreshold + && normal->height() >= kUseNonBlurredThreshold) { + p.drawPixmap(rthumb.topLeft(), normal->pixSingle(_realParent->fullId(), _thumbw, _thumbh, usew, painth, roundRadius, roundCorners)); + } else { + p.drawPixmap(rthumb.topLeft(), normal->pixBlurredSingle(_realParent->fullId(), _thumbw, _thumbh, usew, painth, roundRadius, roundCorners)); + } } else if (const auto blurred = _data->thumbnailInline()) { p.drawPixmap(rthumb.topLeft(), blurred->pixBlurredSingle(_realParent->fullId(), _thumbw, _thumbh, usew, painth, roundRadius, roundCorners)); } else if (!isRound) { @@ -391,8 +452,16 @@ void Gif::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms App::complexOverlayRect(p, rthumb, roundRadius, roundCorners); } - if (radial || (!reader && !player && (_gif.isBad() || (!loaded && !_data->loading()) || !autoplayEnabled()))) { - auto radialOpacity = (radial && loaded && item->id > 0) ? _animation->radial.opacity() : 1.; + if (radial + || (!streamingMode + && ((!_data->loaded() && !_data->loading()) || !autoplay))) { + const auto radialOpacity = (item->isSending() || _data->uploading()) + ? 1. + : streamedForWaiting + ? streamedForWaiting->waitingOpacity() + : (radial && _data->loaded()) + ? _animation->radial.opacity() + : 1.; auto inner = QRect(rthumb.x() + (rthumb.width() - st::msgFileSize) / 2, rthumb.y() + (rthumb.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize); p.setPen(Qt::NoPen); if (selected) { @@ -412,11 +481,13 @@ void Gif::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms } p.setOpacity(radialOpacity); - auto icon = [&]() -> const style::icon * { - if (_data->loaded() && !radial) { + const auto icon = [&]() -> const style::icon * { + if (streamingMode && !_data->uploading()) { + return nullptr; + } else if ((_data->loaded() || canBePlayed) && (!radial || cornerDownload)) { return &(selected ? st::historyFileThumbPlaySelected : st::historyFileThumbPlay); } else if (radial || _data->loading()) { - if (item->id > 0 || _data->uploading()) { + if (!item->isSending() || _data->uploading()) { return &(selected ? st::historyFileThumbCancelSelected : st::historyFileThumbCancel); } return nullptr; @@ -426,21 +497,28 @@ void Gif::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms if (icon) { icon->paintInCenter(p, inner); } + p.setOpacity(1); if (radial) { - p.setOpacity(1); QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine))); - _animation->radial.draw(p, rinner, st::msgFileRadialLine, selected ? st::historyFileThumbRadialFgSelected : st::historyFileThumbRadialFg); - } - - if (!isRound && (!animating || item->id < 0)) { - auto statusX = paintx + st::msgDateImgDelta + st::msgDateImgPadding.x(); - auto statusY = painty + st::msgDateImgDelta + st::msgDateImgPadding.y(); - auto statusW = st::normalFont->width(_statusText) + 2 * st::msgDateImgPadding.x(); - auto statusH = st::normalFont->height + 2 * st::msgDateImgPadding.y(); - App::roundRect(p, style::rtlrect(statusX - st::msgDateImgPadding.x(), statusY - st::msgDateImgPadding.y(), statusW, statusH, width()), selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners); - p.setFont(st::normalFont); - p.setPen(st::msgDateImgFg); - p.drawTextLeft(statusX, statusY, width(), _statusText, statusW - 2 * st::msgDateImgPadding.x()); + const auto fg = selected + ? st::historyFileThumbRadialFgSelected + : st::historyFileThumbRadialFg; + if (streamedForWaiting && !_data->uploading()) { + Ui::InfiniteRadialAnimation::Draw( + p, + streamedForWaiting->waitingState(), + rinner.topLeft(), + rinner.size(), + width(), + fg, + st::msgFileRadialLine); + } else if (!cornerDownload) { + _animation->radial.draw( + p, + rinner, + st::msgFileRadialLine, + fg); + } } } if (displayMute) { @@ -452,6 +530,10 @@ void Gif::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms (selected ? st::historyVideoMessageMuteSelected : st::historyVideoMessageMute).paintInCenter(p, muteRect); } + if (!isRound) { + drawCornerStatus(p, selected, QPoint()); + } + if (!inWebPage && isRound) { auto mediaUnread = item->hasUnreadMediaFlag(); auto statusW = st::normalFont->width(_statusText) + 2 * st::msgDateImgPadding.x(); @@ -553,6 +635,72 @@ void Gif::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms } } +void Gif::drawCornerStatus(Painter &p, bool selected, QPoint position) const { + if (!needCornerStatusDisplay()) { + return; + } + const auto own = activeOwnStreamed(); + const auto text = (own && !own->frozenStatusText.isEmpty()) + ? own->frozenStatusText + : _statusText; + const auto padding = st::msgDateImgPadding; + const auto radial = _animation && _animation->radial.animating(); + const auto cornerDownload = downloadInCorner() && !_data->loaded() && !_data->loadedInMediaCache(); + const auto cornerMute = _streamed && _data->isVideoFile() && !cornerDownload; + const auto addLeft = cornerDownload ? (st::historyVideoDownloadSize + 2 * padding.y()) : 0; + const auto addRight = cornerMute ? st::historyVideoMuteSize : 0; + const auto downloadWidth = cornerDownload ? st::normalFont->width(_downloadSize) : 0; + const auto statusW = std::max(downloadWidth, st::normalFont->width(text)) + 2 * padding.x() + addLeft + addRight; + const auto statusH = cornerDownload ? (st::historyVideoDownloadSize + 2 * padding.y()) : (st::normalFont->height + 2 * padding.y()); + const auto statusX = position.x() + st::msgDateImgDelta + padding.x(); + const auto statusY = position.y() + st::msgDateImgDelta + padding.y(); + const auto around = style::rtlrect(statusX - padding.x(), statusY - padding.y(), statusW, statusH, width()); + const auto statusTextTop = statusY + (cornerDownload ? (((statusH - 2 * st::normalFont->height) / 3) - padding.y()) : 0); + App::roundRect(p, around, selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners); + p.setFont(st::normalFont); + p.setPen(st::msgDateImgFg); + p.drawTextLeft(statusX + addLeft, statusTextTop, width(), text, statusW - 2 * padding.x()); + if (cornerDownload) { + const auto downloadTextTop = statusY + st::normalFont->height + (2 * (statusH - 2 * st::normalFont->height) / 3) - padding.y(); + p.drawTextLeft(statusX + addLeft, downloadTextTop, width(), _downloadSize, statusW - 2 * padding.x()); + const auto inner = QRect(statusX + padding.y() - padding.x(), statusY, st::historyVideoDownloadSize, st::historyVideoDownloadSize); + const auto icon = [&]() -> const style::icon * { + if (_data->loading()) { + return &(selected ? st::historyVideoCancelSelected : st::historyVideoCancel); + } + return &(selected ? st::historyVideoDownloadSelected : st::historyVideoDownload); + }(); + if (icon) { + icon->paintInCenter(p, inner); + } + if (radial) { + QRect rinner(inner.marginsRemoved(QMargins(st::historyVideoRadialLine, st::historyVideoRadialLine, st::historyVideoRadialLine, st::historyVideoRadialLine))); + _animation->radial.draw(p, rinner, st::historyVideoRadialLine, selected ? st::historyFileThumbRadialFgSelected : st::historyFileThumbRadialFg); + } + } else if (cornerMute) { + (selected ? st::historyVideoMessageMuteSelected : st::historyVideoMessageMute).paint(p, statusX - padding.x() - padding.y() + statusW - addRight, statusY - padding.y() + (statusH - st::historyVideoMessageMute.height()) / 2, width()); + } +} + +TextState Gif::cornerStatusTextState( + QPoint point, + StateRequest request, + QPoint position) const { + auto result = TextState(_parent); + if (!needCornerStatusDisplay() || !downloadInCorner() || _data->loaded()) { + return result; + } + const auto padding = st::msgDateImgPadding; + const auto addWidth = st::historyVideoDownloadSize + 2 * padding.y() - padding.x(); + const auto statusX = position.x() + st::msgDateImgDelta + padding.x(); + const auto statusY = position.y() + st::msgDateImgDelta + padding.y(); + const auto inner = QRect(statusX + padding.y() - padding.x(), statusY, st::historyVideoDownloadSize, st::historyVideoDownloadSize); + if (inner.contains(point)) { + result.link = _data->loading() ? _cancell : _savel; + } + return result; +} + TextState Gif::textState(QPoint point, StateRequest request) const { auto result = TextState(_parent); @@ -651,17 +799,22 @@ TextState Gif::textState(QPoint point, StateRequest request) const { } } } - if (QRect(usex + paintx, painty, usew, painth).contains(point)) { - if (_data->uploading()) { - result.link = _cancell; - } else { - result.link = _data->loaded() - ? _openl : - _data->loading() - ? _cancell - : _savel; + if (!isRound) { + if (const auto state = cornerStatusTextState(point, request, QPoint()); state.link) { + return state; } } + if (QRect(usex + paintx, painty, usew, painth).contains(point)) { + result.link = _data->uploading() + ? _cancell + : !IsServerMsgId(_realParent->id) + ? nullptr + : (_data->loaded() || _data->canBePlayed()) + ? _openl + : _data->loading() + ? _cancell + : _savel; + } if (isRound || _caption.isEmpty()) { auto fullRight = usex + paintx + usew; auto fullBottom = painty + painth; @@ -705,6 +858,246 @@ TextForMimeData Gif::selectedText(TextSelection selection) const { return _caption.toTextForMimeData(selection); } +bool Gif::fullFeaturedGrouped(RectParts sides) const { + return (sides & RectPart::Left) && (sides & RectPart::Right); +} + +QSize Gif::sizeForGrouping() const { + return sizeForAspectRatio(); +} + +void Gif::drawGrouped( + Painter &p, + const QRect &clip, + TextSelection selection, + crl::time ms, + const QRect &geometry, + RectParts sides, + RectParts corners, + not_null cacheKey, + not_null cache) const { + const auto item = _parent->data(); + const auto displayLoading = (item->id < 0) || _data->displayLoading(); + const auto selected = (selection == FullSelection); + const auto autoPaused = App::wnd()->sessionController()->isGifPausedAtLeastFor(Window::GifPauseReason::Any); + const auto fullFeatured = fullFeaturedGrouped(sides); + const auto cornerDownload = fullFeatured && downloadInCorner(); + const auto canBePlayed = _data->canBePlayed(); + const auto autoplay = fullFeatured && autoplayEnabled() && canBePlayed; + const auto startPlay = autoplay && !_streamed; + if (startPlay) { + const_cast(this)->playAnimation(true); + } else { + checkStreamedIsStarted(); + } + const auto streamingMode = _streamed || autoplay; + const auto activeOwnPlaying = activeOwnStreamed(); + + auto paintx = geometry.x(), painty = geometry.y(), paintw = geometry.width(), painth = geometry.height(); + + auto displayMute = false; + const auto streamed = activeOwnPlaying + ? &activeOwnPlaying->instance + : nullptr; + const auto streamedForWaiting = _streamed + ? &_streamed->instance + : nullptr; + + if (displayLoading + && (!streamedForWaiting + || item->isSending() + || _data->uploading() + || (cornerDownload && _data->loading()))) { + ensureAnimation(); + if (!_animation->radial.animating()) { + _animation->radial.start(dataProgress()); + } + } + updateStatusText(); + const auto radial = isRadialAnimation() + || (streamedForWaiting && streamedForWaiting->waitingShown()); + + const auto roundRadius = ImageRoundRadius::Large; + + auto drawHighlighted = [&](const auto &painter, auto geometry, auto frame, bool isPixmap = false) { + const auto animms = _parent->delegate()->elementHighlightTime(_parent); + const auto realId = _realParent->id; + const auto mainWidget = App::main(); + const auto highlightedRealId = mainWidget->highlightedOriginalId(); + if (realId != highlightedRealId + && animms + && animms < st::activeFadeInDuration + st::activeFadeOutDuration) { + const auto dt = (animms <= st::activeFadeInDuration) + ? ((animms / float64(st::activeFadeInDuration))) + : (1. - (animms - st::activeFadeInDuration) + / float64(st::activeFadeOutDuration)); + const auto o = painter.opacity(); + painter.setOpacity(o - dt * 0.8); + (isPixmap ? painter.drawPixmap(geometry, frame) : painter.drawImage(geometry, frame) ); + painter.setOpacity(o); + } else { + (isPixmap ? painter.drawPixmap(geometry, frame) : painter.drawImage(geometry, frame) ); + } + }; + + if (streamed) { + const auto paused = autoPaused; + auto request = ::Media::Streaming::FrameRequest(); + const auto original = sizeForAspectRatio(); + const auto originalWidth = style::ConvertScale(original.width()); + const auto originalHeight = style::ConvertScale(original.height()); + const auto pixSize = Ui::GetImageScaleSizeForGeometry( + { originalWidth, originalHeight }, + { geometry.width(), geometry.height() }); + request.outer = geometry.size() * cIntRetinaFactor(); + request.resize = pixSize * cIntRetinaFactor(); + request.corners = corners; + request.radius = roundRadius; + if (activeOwnPlaying->instance.playerLocked()) { + if (activeOwnPlaying->frozenFrame.isNull()) { + activeOwnPlaying->frozenRequest = request; + activeOwnPlaying->frozenFrame = streamed->frame(request); + activeOwnPlaying->frozenStatusText = _statusText; + } else if (activeOwnPlaying->frozenRequest != request) { + activeOwnPlaying->frozenRequest = request; + activeOwnPlaying->frozenFrame = streamed->frame(request); + } + drawHighlighted(p, geometry, activeOwnPlaying->frozenFrame); + } else { + if (activeOwnPlaying) { + activeOwnPlaying->frozenFrame = QImage(); + activeOwnPlaying->frozenStatusText = QString(); + } + drawHighlighted(p, geometry, streamed->frame(request)); + if (!paused) { + streamed->markFrameShown(); + } + } + } else { + validateGroupedCache(geometry, corners, cacheKey, cache); + drawHighlighted(p, geometry, *cache, true); + } + + if (selected) { + App::complexOverlayRect(p, geometry, roundRadius, corners); + } + + if (radial + || (!streamingMode + && ((!_data->loaded() && !_data->loading()) || !autoplay))) { + const auto radialOpacity = (item->isSending() || _data->uploading()) + ? 1. + : streamedForWaiting + ? streamedForWaiting->waitingOpacity() + : (radial && _data->loaded()) + ? _animation->radial.opacity() + : 1.; + const auto radialSize = st::historyGroupRadialSize; + const auto inner = QRect( + geometry.x() + (geometry.width() - radialSize) / 2, + geometry.y() + (geometry.height() - radialSize) / 2, + radialSize, + radialSize); + p.setPen(Qt::NoPen); + if (selected) { + p.setBrush(st::msgDateImgBgSelected); + } else if (isThumbAnimation()) { + auto over = _animation->a_thumbOver.value(1.); + p.setBrush(anim::brush(st::msgDateImgBg, st::msgDateImgBgOver, over)); + } else { + auto over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel); + p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); + } + p.setOpacity(radialOpacity * p.opacity()); + + { + PainterHighQualityEnabler hq(p); + p.drawEllipse(inner); + } + + p.setOpacity(radialOpacity); + const auto icon = [&]() -> const style::icon * { + if (_data->waitingForAlbum()) { + return &(selected ? st::historyFileThumbWaitingSelected : st::historyFileThumbWaiting); + } else if (streamingMode && !_data->uploading()) { + return nullptr; + } else if ((_data->loaded() || canBePlayed) && (!radial || cornerDownload)) { + return &(selected ? st::historyFileThumbPlaySelected : st::historyFileThumbPlay); + } else if (radial || _data->loading()) { + if (!item->isSending() || _data->uploading()) { + return &(selected ? st::historyFileThumbCancelSelected : st::historyFileThumbCancel); + } + return nullptr; + } + return &(selected ? st::historyFileThumbDownloadSelected : st::historyFileThumbDownload); + }(); + const auto previous = [&]() -> const style::icon* { + if (_data->waitingForAlbum()) { + return &(selected ? st::historyFileThumbCancelSelected : st::historyFileThumbCancel); + } + return nullptr; + }(); + if (icon) { + if (previous && radialOpacity > 0. && radialOpacity < 1.) { + PaintInterpolatedIcon(p, *icon, *previous, radialOpacity, inner); + } else { + icon->paintInCenter(p, inner); + } + } + p.setOpacity(1); + if (radial) { + const auto line = st::historyGroupRadialLine; + const auto rinner = inner.marginsRemoved({ line, line, line, line }); + const auto fg = selected + ? st::historyFileThumbRadialFgSelected + : st::historyFileThumbRadialFg; + if (streamedForWaiting && !_data->uploading()) { + Ui::InfiniteRadialAnimation::Draw( + p, + streamedForWaiting->waitingState(), + rinner.topLeft(), + rinner.size(), + width(), + fg, + st::msgFileRadialLine); + } else if (!cornerDownload) { + _animation->radial.draw( + p, + rinner, + st::msgFileRadialLine, + fg); + } + } + } + if (fullFeatured) { + drawCornerStatus(p, selected, geometry.topLeft()); + } +} + +TextState Gif::getStateGrouped( + const QRect &geometry, + RectParts sides, + QPoint point, + StateRequest request) const { + if (!geometry.contains(point)) { + return {}; + } + if (fullFeaturedGrouped(sides)) { + if (const auto state = cornerStatusTextState(point, request, geometry.topLeft()); state.link) { + return state; + } + } + return TextState(_parent, _data->uploading() + ? _cancell + : !IsServerMsgId(_realParent->id) + ? nullptr + : (_data->loaded() || _data->canBePlayed()) + ? _openl + : _data->loading() + ? _cancell + : _savel); +} + bool Gif::uploading() const { return _data->uploading(); } @@ -744,16 +1137,79 @@ bool Gif::isSeparateRoundVideo() const { && !_parent->hasBubble(); } +void Gif::validateGroupedCache( + const QRect &geometry, + RectParts corners, + not_null cacheKey, + not_null cache) const { + using Option = Images::Option; + const auto good = _data->goodThumbnail(); + const auto useGood = (good && good->loaded()); + const auto thumb = _data->thumbnail(); + const auto useThumb = (thumb && thumb->loaded()); + const auto image = useGood + ? good + : useThumb + ? thumb + : _data->thumbnailInline(); + const auto blur = !useGood + && (!useThumb + || (thumb->width() < kUseNonBlurredThreshold) + || (thumb->height() < kUseNonBlurredThreshold)); + if (good && !useGood) { + good->load({}); + } + + const auto loadLevel = useGood ? 3 : useThumb ? 2 : image ? 1 : 0; + const auto width = geometry.width(); + const auto height = geometry.height(); + const auto options = Option::Smooth + | Option::RoundedLarge + | (blur ? Option(0) : Option::Blurred) + | ((corners & RectPart::TopLeft) ? Option::RoundedTopLeft : Option::None) + | ((corners & RectPart::TopRight) ? Option::RoundedTopRight : Option::None) + | ((corners & RectPart::BottomLeft) ? Option::RoundedBottomLeft : Option::None) + | ((corners & RectPart::BottomRight) ? Option::RoundedBottomRight : Option::None); + const auto key = (uint64(width) << 48) + | (uint64(height) << 32) + | (uint64(options) << 16) + | (uint64(loadLevel)); + if (*cacheKey == key) { + return; + } + + const auto original = sizeForAspectRatio(); + const auto originalWidth = style::ConvertScale(original.width()); + const auto originalHeight = style::ConvertScale(original.height()); + const auto pixSize = Ui::GetImageScaleSizeForGeometry( + { originalWidth, originalHeight }, + { width, height }); + const auto pixWidth = pixSize.width() * cIntRetinaFactor(); + const auto pixHeight = pixSize.height() * cIntRetinaFactor(); + + *cacheKey = key; + *cache = (image ? image : Image::BlankMedia().get())->pixNoCache( + _realParent->fullId(), + pixWidth, + pixHeight, + options, + width, + height); +} + void Gif::setStatusSize(int newSize) const { - if (_data->isVideoMessage()) { + if (newSize < 0) { _statusSize = newSize; - if (newSize < 0) { - _statusText = formatDurationText(-newSize - 1); - } else { - _statusText = formatDurationText(_data->getDuration()); - } + _statusText = formatDurationText(-newSize - 1); + } else if (_data->isVideoMessage()) { + _statusSize = newSize; + _statusText = formatDurationText(_data->getDuration()); } else { - File::setStatusSize(newSize, _data->size, -2, 0); + File::setStatusSize( + newSize, + _data->size, + _data->isVideoFile() ? _data->getDuration() : -2, + 0); } } @@ -765,27 +1221,30 @@ void Gif::updateStatusText() const { statusSize = FileStatusSizeFailed; } else if (_data->uploading()) { statusSize = _data->uploadingData->offset; - } else if (_data->loading()) { + } else if (!downloadInCorner() && _data->loading()) { statusSize = _data->loadOffset(); - } else if (_data->loaded()) { + } else if (_data->loaded() || _data->canBePlayed()) { statusSize = FileStatusSizeLoaded; - if (const auto video = activeRoundPlayer()) { - const auto state = video->prepareLegacyState(); - if (state.length) { - auto position = int64(0); - if (::Media::Player::IsStoppedAtEnd(state.state)) { - position = state.length; - } else if (!::Media::Player::IsStoppedOrStopping(state.state)) { - position = state.position; - } - statusSize = -1 - int((state.length - position) / state.frequency + 1); - } else { - statusSize = -1 - _data->getDuration(); - } - } } else { statusSize = FileStatusSizeReady; } + const auto round = activeRoundStreamed(); + const auto own = activeOwnStreamed(); + if (round || (own && own->frozenFrame.isNull() && _data->isVideoFile())) { + const auto streamed = round ? round : &own->instance; + const auto state = streamed->player().prepareLegacyState(); + if (state.length) { + auto position = int64(0); + if (::Media::Player::IsStoppedAtEnd(state.state)) { + position = state.length; + } else if (!::Media::Player::IsStoppedOrStopping(state.state)) { + position = state.position; + } + statusSize = -1 - int((state.length - position) / state.frequency + 1); + } else { + statusSize = -1 - _data->getDuration(); + } + } if (statusSize != _statusSize) { setStatusSize(statusSize); } @@ -800,14 +1259,33 @@ QString Gif::additionalInfoString() const { } bool Gif::isReadyForOpen() const { - return _data->loaded(); + return true; } void Gif::parentTextUpdated() { - _caption = (_parent->media() == this) - ? createCaption(_parent->data()) - : Ui::Text::String(); - history()->owner().requestViewResize(_parent); + if (_parent->media() == this) { + refreshCaption(); + history()->owner().requestViewResize(_parent); + } +} + +void Gif::refreshParentId(not_null realParent) { + if (_parent->media() == this) { + refreshCaption(); + } +} + +void Gif::refreshCaption() { + const auto timestampLinksDuration = _data->isVideoFile() + ? _data->getDuration() + : 0; + const auto timestampLinkBase = timestampLinksDuration + ? DocumentTimestampLinkBase(_data, _realParent->fullId()) + : QString(); + _caption = createCaption( + _parent->data(), + timestampLinksDuration, + timestampLinkBase); } int Gif::additionalWidth(const HistoryMessageVia *via, const HistoryMessageReply *reply, const HistoryMessageForwarded *forwarded) const { @@ -823,98 +1301,162 @@ int Gif::additionalWidth(const HistoryMessageVia *via, const HistoryMessageReply return result; } -::Media::Streaming::Player *Gif::activeRoundPlayer() const { - return ::Media::Player::instance()->roundVideoPlayer(_parent->data()); +::Media::Streaming::Instance *Gif::activeRoundStreamed() const { + return ::Media::Player::instance()->roundVideoStreamed(_parent->data()); } -::Media::Clip::Reader *Gif::currentReader() const { - return (_gif && _gif->ready()) ? _gif.get() : nullptr; +Gif::Streamed *Gif::activeOwnStreamed() const { + return (_streamed + && _streamed->instance.player().ready() + && !_streamed->instance.player().videoSize().isEmpty()) + ? _streamed.get() + : nullptr; +} + +::Media::Streaming::Instance *Gif::activeCurrentStreamed() const { + if (const auto streamed = activeRoundStreamed()) { + return streamed; + } else if (const auto owned = activeOwnStreamed()) { + return &owned->instance; + } + return nullptr; } ::Media::View::PlaybackProgress *Gif::videoPlayback() const { return ::Media::Player::instance()->roundVideoPlayback(_parent->data()); } -void Gif::clipCallback(::Media::Clip::Notification notification) { - using namespace ::Media::Clip; - - const auto reader = _gif.get(); - if (!reader) { - return; - } - switch (notification) { - case NotificationReinit: { - auto stopped = false; - if (reader->autoPausedGif()) { - auto amVisible = false; - history()->owner().queryItemVisibility().notify( - { _parent->data(), &amVisible }, - true); - if (!amVisible) { // Stop animation if it is not visible. - stopAnimation(); - stopped = true; - } - } - if (!stopped) { - history()->owner().requestViewResize(_parent); - } - } break; - - case NotificationRepaint: { - if (!reader->currentDisplayed()) { - history()->owner().requestViewRepaint(_parent); - } - } break; - } -} - void Gif::playAnimation(bool autoplay) { if (_data->isVideoMessage() && !autoplay) { return; - } else if (_gif && autoplay) { + } else if (_streamed && autoplay) { return; - } else if (_gif && autoplayEnabled()) { + } else if ((_streamed && autoplayEnabled()) + || (!autoplay && _data->isVideoFile())) { Core::App().showDocument(_data, _parent->data()); return; } - using Mode = ::Media::Clip::Reader::Mode; - if (_gif) { + if (_streamed) { stopAnimation(); - } else if (_data->loaded(DocumentData::FilePathResolve::Checked)) { + } else if (_data->canBePlayed()) { if (!autoplayEnabled()) { - history()->owner().stopAutoplayAnimations(); - } - setClipReader(::Media::Clip::MakeReader( - _data, - _parent->data()->fullId(), - [=](auto notification) { clipCallback(notification); }, - Mode::Gif)); - if (_gif && autoplay) { - _gif->setAutoplay(); + history()->owner().checkPlayingVideoFiles(); } + createStreamedPlayer(); } } +void Gif::createStreamedPlayer() { + auto shared = _data->owner().streaming().sharedDocument( + _data, + _realParent->fullId()); + if (!shared) { + return; + } + setStreamed(std::make_unique( + std::move(shared), + [=] { repaintStreamedContent(); })); + + _streamed->instance.player().updates( + ) | rpl::start_with_next_error([=](::Media::Streaming::Update &&update) { + handleStreamingUpdate(std::move(update)); + }, [=](::Media::Streaming::Error &&error) { + handleStreamingError(std::move(error)); + }, _streamed->instance.lifetime()); + + if (_streamed->instance.ready()) { + streamingReady(base::duplicate(_streamed->instance.info())); + } + checkStreamedIsStarted(); +} + +void Gif::startStreamedPlayer() const { + Expects(_streamed != nullptr); + + auto options = ::Media::Streaming::PlaybackOptions(); + options.audioId = AudioMsgId(_data, _realParent->fullId()); + options.waitForMarkAsShown = true; + //if (!_streamed->withSound) { + options.mode = ::Media::Streaming::Mode::Video; + options.loop = true; + //} + _streamed->instance.play(options); +} + +void Gif::checkStreamedIsStarted() const { + if (!_streamed || _streamed->instance.playerLocked()) { + return; + } else if (_streamed->instance.paused()) { + _streamed->instance.resume(); + } + if (!_streamed->instance.active() && !_streamed->instance.failed()) { + startStreamedPlayer(); + } +} + +void Gif::setStreamed(std::unique_ptr value) { + const auto removed = (_streamed && !value); + const auto set = (!_streamed && value); + if (removed) { + history()->owner().unregisterPlayingVideoFile(_parent); + } + _streamed = std::move(value); + if (set) { + history()->owner().registerPlayingVideoFile(_parent); + } +} + +void Gif::handleStreamingUpdate(::Media::Streaming::Update &&update) { + using namespace ::Media::Streaming; + + update.data.match([&](Information &update) { + streamingReady(std::move(update)); + }, [&](const PreloadedVideo &update) { + }, [&](const UpdateVideo &update) { + repaintStreamedContent(); + }, [&](const PreloadedAudio &update) { + }, [&](const UpdateAudio &update) { + }, [&](const WaitingForData &update) { + }, [&](MutedByOther) { + }, [&](Finished) { + }); +} + +void Gif::handleStreamingError(::Media::Streaming::Error &&error) { +} + +void Gif::repaintStreamedContent() { + const auto own = activeOwnStreamed(); + if (own && !own->frozenFrame.isNull()) { + return; + } + if (App::wnd()->sessionController()->isGifPausedAtLeastFor(Window::GifPauseReason::Any) + && !activeRoundStreamed()) { + return; + } + history()->owner().requestViewRepaint(_parent); +} + +void Gif::streamingReady(::Media::Streaming::Information &&info) { + history()->owner().requestViewResize(_parent); +} + void Gif::stopAnimation() { - if (_gif) { - clearClipReader(); + if (_streamed) { + setStreamed(nullptr); history()->owner().requestViewResize(_parent); _data->unload(); } } -void Gif::setClipReader(::Media::Clip::ReaderPointer gif) { - if (_gif) { - history()->owner().unregisterAutoplayAnimation(_gif.get()); +int Gif::checkAnimationCount() { + if (!_streamed) { + return 0; + } else if (autoplayEnabled()) { + return 1; } - _gif = std::move(gif); - if (_gif) { - history()->owner().registerAutoplayAnimation(_gif.get(), _parent); - } -} - -Gif::~Gif() { - clearClipReader(); + stopAnimation(); + return 0; } float64 Gif::dataProgress() const { @@ -934,7 +1476,14 @@ bool Gif::dataLoaded() const { } bool Gif::needInfoDisplay() const { - return (_parent->data()->id < 0 || _parent->isUnderCursor()); + return _parent->data()->isSending() + || _data->uploading() + || _parent->isUnderCursor(); +} + +bool Gif::needCornerStatusDisplay() const { + return _data->isVideoFile() + || needInfoDisplay(); } } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/media/history_view_video.cpp b/Telegram/SourceFiles/history/view/media/history_view_video.cpp deleted file mode 100644 index bcae354fc..000000000 --- a/Telegram/SourceFiles/history/view/media/history_view_video.cpp +++ /dev/null @@ -1,656 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include "history/view/media/history_view_video.h" - -#include "history/view/media/history_view_media_common.h" -#include "layout.h" -#include "history/history_item_components.h" -#include "history/history_item.h" -#include "history/history.h" -#include "history/view/history_view_element.h" -#include "history/view/history_view_cursor_state.h" -#include "ui/image/image.h" -#include "ui/grouped_layout.h" -#include "data/data_session.h" -#include "data/data_document.h" -#include "data/data_file_origin.h" -#include "mainwidget.h" -#include "app.h" -#include "styles/style_history.h" - -namespace HistoryView { - -Video::Video( - not_null parent, - not_null realParent, - not_null document) -: File(parent, realParent) -, _data(document) -, _thumbw(1) -, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) { - _caption = createCaption(realParent); - - setDocumentLinks(_data, realParent); - - setStatusSize(FileStatusSizeReady); - _downloadSize = formatSizeText(_data->size); - - _data->loadThumbnail(realParent->fullId()); -} - -QSize Video::sizeForAspectRatio() const { - // We use size only for aspect ratio and we want to have it - // as close to the thumbnail as possible. - //if (!_data->dimensions.isEmpty()) { - // return _data->dimensions; - //} - if (const auto thumb = _data->thumbnail()) { - if (!thumb->size().isEmpty()) { - return thumb->size(); - } - } - return { 1, 1 }; -} - -QSize Video::countOptimalDimensions() const { - const auto captionWithPaddings = _caption.maxWidth() - + st::msgPadding.left() - + st::msgPadding.right(); - auto inWebPage = (_parent->media() != this); - const auto desired = style::ConvertScale(_data->dimensions); - const auto size = desired.isEmpty() ? sizeForAspectRatio() : desired; - auto tw = size.width(); - auto th = size.height(); - if (!tw || !th) { - tw = th = 1; - } else if ((!cAdaptiveBaloons() || (captionWithPaddings <= st::maxMediaSize && !inWebPage)) && tw >= th && tw > st::maxMediaSize) { - th = qRound((st::maxMediaSize / float64(tw)) * th); - tw = st::maxMediaSize; - } else if (cAdaptiveBaloons() && tw >= th && captionWithPaddings > st::maxMediaSize && tw > captionWithPaddings) { - th = qRound((captionWithPaddings / float64(tw)) * th); - tw = captionWithPaddings; - } else if (tw < th && th > st::maxMediaSize) { - tw = qRound((st::maxMediaSize / float64(th)) * tw); - th = st::maxMediaSize; - } else if ((tw < st::msgVideoSize.width()) - && (tw * st::msgVideoSize.height() - >= th * st::msgVideoSize.width())) { - th = qRound((st::msgVideoSize.width() / float64(tw)) * th); - tw = st::msgVideoSize.width(); - } else if ((th < st::msgVideoSize.height()) - && (tw * st::msgVideoSize.height() - < th * st::msgVideoSize.width())) { - tw = qRound((st::msgVideoSize.height() / float64(th)) * tw); - th = st::msgVideoSize.height(); - } - return QSize(tw, th); -} - -QSize Video::countOptimalSize() { - if (_parent->media() != this) { - _caption = Ui::Text::String(); - } else if (_caption.hasSkipBlock()) { - _caption.updateSkipBlock( - _parent->skipBlockWidth(), - _parent->skipBlockHeight()); - } - - const auto size = countOptimalDimensions(); - const auto tw = size.width(); - const auto th = size.height(); - _thumbw = qMax(tw, 1); - _thumbh = qMax(th, 1); - - auto minWidth = qMax(st::minVideoSize, _parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); - minWidth = qMax(minWidth, documentMaxStatusWidth(_data) + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); - auto maxWidth = qMax(_thumbw, minWidth); - auto minHeight = qMax(th, st::minVideoSize); - if (_parent->hasBubble() && !_caption.isEmpty()) { - if (cAdaptiveBaloons()) { - maxWidth = qMax(maxWidth, _caption.maxWidth() + st::msgPadding.left() + st::msgPadding.right()); - } - const auto captionw = maxWidth - - st::msgPadding.left() - - st::msgPadding.right(); - minHeight += st::mediaCaptionSkip + _caption.countHeight(captionw); - if (isBubbleBottom()) { - minHeight += st::msgPadding.bottom(); - } - } - return { maxWidth, minHeight }; -} - -QSize Video::countCurrentSize(int newWidth) { - auto availableWidth = newWidth; - - const auto size = countOptimalDimensions(); - auto tw = size.width(); - auto th = size.height(); - if (newWidth < tw) { - th = qRound((newWidth / float64(tw)) * th); - tw = newWidth; - } - - _thumbw = qMax(tw, 1); - _thumbh = qMax(th, 1); - auto minWidth = qMax(st::minPhotoSize, _parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); - minWidth = qMax(minWidth, documentMaxStatusWidth(_data) + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); - newWidth = qMax(_thumbw, minWidth); - auto newHeight = qMax(th, st::minPhotoSize); - if (_parent->hasBubble() && !_caption.isEmpty()) { - if (cAdaptiveBaloons()) { - newWidth = qMax(newWidth, _caption.maxWidth() + st::msgPadding.left() + st::msgPadding.right()); - newWidth = qMin(newWidth, availableWidth); - } - const auto captionw = newWidth - - st::msgPadding.left() - - st::msgPadding.right(); - newHeight += st::mediaCaptionSkip + _caption.countHeight(captionw); - if (isBubbleBottom()) { - newHeight += st::msgPadding.bottom(); - } - } - return { newWidth, newHeight }; -} - -bool Video::downloadInCorner() const { - return _data->canBeStreamed() - && !_data->inappPlaybackFailed() - && IsServerMsgId(_parent->data()->id); -} - -void Video::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const { - if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return; - - _data->automaticLoad(_realParent->fullId(), _parent->data()); - bool loaded = _data->loaded(), displayLoading = _data->displayLoading(); - bool selected = (selection == FullSelection); - - auto paintx = 0, painty = 0, paintw = width(), painth = height(); - bool bubble = _parent->hasBubble(); - const auto cornerDownload = downloadInCorner(); - - int captionw = paintw - st::msgPadding.left() - st::msgPadding.right(); - - if (displayLoading) { - ensureAnimation(); - if (!_animation->radial.animating()) { - _animation->radial.start(_data->progress()); - } - } - updateStatusText(); - const auto radial = isRadialAnimation(); - - if (bubble) { - if (!_caption.isEmpty()) { - painth -= st::mediaCaptionSkip + _caption.countHeight(captionw); - if (isBubbleBottom()) { - painth -= st::msgPadding.bottom(); - } - } - } else { - App::roundShadow(p, 0, 0, paintw, painth, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners); - } - - auto inWebPage = (_parent->media() != this); - auto roundRadius = inWebPage ? ImageRoundRadius::Small : ImageRoundRadius::Large; - auto roundCorners = inWebPage ? RectPart::AllCorners : ((isBubbleTop() ? (RectPart::TopLeft | RectPart::TopRight) : RectPart::None) - | ((isBubbleBottom() && _caption.isEmpty()) ? (RectPart::BottomLeft | RectPart::BottomRight) : RectPart::None)); - QRect rthumb(style::rtlrect(paintx, painty, paintw, painth, width())); - - const auto good = _data->goodThumbnail(); - if (good && good->loaded()) { - p.drawPixmap(rthumb.topLeft(), good->pixSingle({}, _thumbw, _thumbh, paintw, painth, roundRadius, roundCorners)); - } else { - if (good) { - good->load({}); - } - const auto normal = _data->thumbnail(); - if (normal && normal->loaded()) { - p.drawPixmap(rthumb.topLeft(), normal->pixSingle(_realParent->fullId(), _thumbw, _thumbh, paintw, painth, roundRadius, roundCorners)); - } else if (const auto blurred = _data->thumbnailInline()) { - p.drawPixmap(rthumb.topLeft(), blurred->pixBlurredSingle(_realParent->fullId(), _thumbw, _thumbh, paintw, painth, roundRadius, roundCorners)); - } else { - const auto roundTop = (roundCorners & RectPart::TopLeft); - const auto roundBottom = (roundCorners & RectPart::BottomLeft); - const auto margin = inWebPage - ? st::buttonRadius - : st::historyMessageRadius; - const auto parts = roundCorners - | RectPart::NoTopBottom - | (roundTop ? RectPart::Top : RectPart::None) - | (roundBottom ? RectPart::Bottom : RectPart::None); - App::roundRect(p, rthumb.marginsAdded({ 0, roundTop ? 0 : margin, 0, roundBottom ? 0 : margin }), st::imageBg, roundRadius, parts); - } - } - if (selected) { - App::complexOverlayRect(p, rthumb, roundRadius, roundCorners); - } - - QRect inner(rthumb.x() + (rthumb.width() - st::msgFileSize) / 2, rthumb.y() + (rthumb.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize); - p.setPen(Qt::NoPen); - if (selected) { - p.setBrush(st::msgDateImgBgSelected); - } else if (isThumbAnimation()) { - auto over = _animation->a_thumbOver.value(1.); - p.setBrush(anim::brush(st::msgDateImgBg, st::msgDateImgBgOver, over)); - } else { - bool over = ClickHandler::showAsActive((_data->loading() || _data->uploading()) ? _cancell : _savel); - p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); - } - - { - PainterHighQualityEnabler hq(p); - p.drawEllipse(inner); - } - - const auto icon = [&]() -> const style::icon * { - if (!cornerDownload && (_data->loading() || _data->uploading())) { - return &(selected ? st::historyFileThumbCancelSelected : st::historyFileThumbCancel); - } else if (!IsServerMsgId(_parent->data()->id)) { - return nullptr; - } else if (loaded || _data->canBePlayed()) { - return &(selected ? st::historyFileThumbPlaySelected : st::historyFileThumbPlay); - } - return &(selected ? st::historyFileThumbDownloadSelected : st::historyFileThumbDownload); - }(); - if (icon) { - icon->paintInCenter(p, inner); - } - if (radial && !cornerDownload) { - QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine))); - _animation->radial.draw(p, rinner, st::msgFileRadialLine, selected ? st::historyFileThumbRadialFgSelected : st::historyFileThumbRadialFg); - } - - drawCornerStatus(p, selected); - - // date - if (!_caption.isEmpty()) { - auto outbg = _parent->hasOutLayout(); - p.setPen(outbg ? (selected ? st::historyTextOutFgSelected : st::historyTextOutFg) : (selected ? st::historyTextInFgSelected : st::historyTextInFg)); - _caption.draw(p, st::msgPadding.left(), painty + painth + st::mediaCaptionSkip, captionw, style::al_left, 0, -1, selection); - } else if (_parent->media() == this) { - auto fullRight = paintx + paintw, fullBottom = painty + painth; - _parent->drawInfo(p, fullRight, fullBottom, 2 * paintx + paintw, selected, InfoDisplayType::Image); - if (!bubble && _parent->displayRightAction()) { - auto fastShareLeft = (fullRight + st::historyFastShareLeft); - auto fastShareTop = (fullBottom - st::historyFastShareBottom - st::historyFastShareSize); - _parent->drawRightAction(p, fastShareLeft, fastShareTop, 2 * paintx + paintw); - } - } -} - -void Video::drawCornerStatus(Painter &p, bool selected) const { - const auto padding = st::msgDateImgPadding; - const auto radial = _animation && _animation->radial.animating(); - const auto cornerDownload = downloadInCorner() && !_data->loaded() && !_data->loadedInMediaCache(); - const auto addWidth = cornerDownload ? (st::historyVideoDownloadSize + 2 * padding.y()) : 0; - const auto downloadWidth = cornerDownload ? st::normalFont->width(_downloadSize) : 0; - const auto statusW = std::max(downloadWidth, st::normalFont->width(_statusText)) + 2 * padding.x() + addWidth; - const auto statusH = cornerDownload ? (st::historyVideoDownloadSize + 2 * padding.y()) : (st::normalFont->height + 2 * padding.y()); - const auto statusX = st::msgDateImgDelta + padding.x(); - const auto statusY = st::msgDateImgDelta + padding.y(); - const auto around = style::rtlrect(statusX - padding.x(), statusY - padding.y(), statusW, statusH, width()); - const auto statusTextTop = statusY + (cornerDownload ? (((statusH - 2 * st::normalFont->height) / 3) - padding.y()) : 0); - App::roundRect(p, around, selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners); - p.setFont(st::normalFont); - p.setPen(st::msgDateImgFg); - p.drawTextLeft(statusX + addWidth, statusTextTop, width(), _statusText, statusW - 2 * padding.x()); - if (cornerDownload) { - const auto downloadTextTop = statusY + st::normalFont->height + (2 * (statusH - 2 * st::normalFont->height) / 3) - padding.y(); - p.drawTextLeft(statusX + addWidth, downloadTextTop, width(), _downloadSize, statusW - 2 * padding.x()); - const auto inner = QRect(statusX + padding.y() - padding.x(), statusY, st::historyVideoDownloadSize, st::historyVideoDownloadSize); - const auto icon = [&]() -> const style::icon * { - if (_data->loading()) { - return &(selected ? st::historyVideoCancelSelected : st::historyVideoCancel); - } - return &(selected ? st::historyVideoDownloadSelected : st::historyVideoDownload); - }(); - if (icon) { - icon->paintInCenter(p, inner); - } - if (radial) { - QRect rinner(inner.marginsRemoved(QMargins(st::historyVideoRadialLine, st::historyVideoRadialLine, st::historyVideoRadialLine, st::historyVideoRadialLine))); - _animation->radial.draw(p, rinner, st::historyVideoRadialLine, selected ? st::historyFileThumbRadialFgSelected : st::historyFileThumbRadialFg); - } - } -} - -TextState Video::cornerStatusTextState( - QPoint point, - StateRequest request) const { - auto result = TextState(_parent); - if (!downloadInCorner() || _data->loaded()) { - return result; - } - const auto padding = st::msgDateImgPadding; - const auto addWidth = st::historyVideoDownloadSize + 2 * padding.y() - padding.x(); - const auto statusX = st::msgDateImgDelta + padding.x(), statusY = st::msgDateImgDelta + padding.y(); - const auto inner = QRect(statusX + padding.y() - padding.x(), statusY, st::historyVideoDownloadSize, st::historyVideoDownloadSize); - if (inner.contains(point)) { - result.link = _data->loading() ? _cancell : _savel; - } - return result; -} - -TextState Video::textState(QPoint point, StateRequest request) const { - if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) { - return {}; - } - - auto result = TextState(_parent); - - auto paintx = 0, painty = 0, paintw = width(), painth = height(); - bool bubble = _parent->hasBubble(); - - if (bubble && !_caption.isEmpty()) { - const auto captionw = paintw - - st::msgPadding.left() - - st::msgPadding.right(); - painth -= _caption.countHeight(captionw); - if (isBubbleBottom()) { - painth -= st::msgPadding.bottom(); - } - if (QRect(st::msgPadding.left(), painth, captionw, height() - painth).contains(point)) { - result = TextState(_parent, _caption.getState( - point - QPoint(st::msgPadding.left(), painth), - captionw, - request.forText())); - } - painth -= st::mediaCaptionSkip; - } - if (const auto state = cornerStatusTextState(point, request); state.link) { - return state; - } - if (QRect(paintx, painty, paintw, painth).contains(point)) { - if (!downloadInCorner() && (_data->loading() || _data->uploading())) { - result.link = _cancell; - } else if (!IsServerMsgId(_parent->data()->id)) { - } else if (_data->loaded() || _data->canBePlayed()) { - result.link = _openl; - } else { - result.link = _savel; - } - } - if (_caption.isEmpty() && _parent->media() == this) { - auto fullRight = paintx + paintw; - auto fullBottom = painty + painth; - if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayType::Image)) { - result.cursor = CursorState::Date; - } - if (!bubble && _parent->displayRightAction()) { - auto fastShareLeft = (fullRight + st::historyFastShareLeft); - auto fastShareTop = (fullBottom - st::historyFastShareBottom - st::historyFastShareSize); - if (QRect(fastShareLeft, fastShareTop, st::historyFastShareSize, st::historyFastShareSize).contains(point)) { - result.link = _parent->rightActionLink(); - } - } - } - return result; -} - -QSize Video::sizeForGrouping() const { - return sizeForAspectRatio(); -} - -void Video::drawGrouped( - Painter &p, - const QRect &clip, - TextSelection selection, - crl::time ms, - const QRect &geometry, - RectParts corners, - not_null cacheKey, - not_null cache) const { - _data->automaticLoad(_realParent->fullId(), _parent->data()); - - validateGroupedCache(geometry, corners, cacheKey, cache); - - const auto selected = (selection == FullSelection); - const auto loaded = _data->loaded(); - const auto displayLoading = _data->displayLoading(); - const auto bubble = _parent->hasBubble(); - - if (displayLoading) { - ensureAnimation(); - if (!_animation->radial.animating()) { - _animation->radial.start(_data->progress()); - } - } - const auto radial = isRadialAnimation(); - - if (!bubble) { -// App::roundShadow(p, 0, 0, paintw, painth, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners); - } - const auto animms = _parent->delegate()->elementHighlightTime(_parent); - const auto realId = _realParent->id; - const auto mainWidget = App::main(); - const auto highlightedRealId = mainWidget->highlightedOriginalId(); - if (realId != highlightedRealId - && animms - && animms < st::activeFadeInDuration + st::activeFadeOutDuration) { - const auto dt = (animms <= st::activeFadeInDuration) - ? ((animms / float64(st::activeFadeInDuration))) - : (1. - (animms - st::activeFadeInDuration) - / float64(st::activeFadeOutDuration)); - const auto o = p.opacity(); - p.setOpacity(o - dt * 0.8); - p.drawPixmap(geometry.topLeft(), *cache); - p.setOpacity(o); - } else { - p.drawPixmap(geometry.topLeft(), *cache); - } - if (selected) { - const auto roundRadius = ImageRoundRadius::Large; - App::complexOverlayRect(p, geometry, roundRadius, corners); - } - - const auto radialOpacity = radial - ? _animation->radial.opacity() - : 1.; - const auto backOpacity = (loaded && !_data->uploading()) - ? radialOpacity - : 1.; - const auto radialSize = st::historyGroupRadialSize; - const auto inner = QRect( - geometry.x() + (geometry.width() - radialSize) / 2, - geometry.y() + (geometry.height() - radialSize) / 2, - radialSize, - radialSize); - p.setPen(Qt::NoPen); - if (selected) { - p.setBrush(st::msgDateImgBgSelected); - } else if (isThumbAnimation()) { - auto over = _animation->a_thumbOver.value(1.); - p.setBrush(anim::brush(st::msgDateImgBg, st::msgDateImgBgOver, over)); - } else { - auto over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel); - p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); - } - - p.setOpacity(backOpacity * p.opacity()); - - { - PainterHighQualityEnabler hq(p); - p.drawEllipse(inner); - } - - auto icon = [&]() -> const style::icon * { - if (_data->waitingForAlbum()) { - return &(selected ? st::historyFileThumbWaitingSelected : st::historyFileThumbWaiting); - } else if (_data->loading() || _data->uploading()) { - return &(selected ? st::historyFileThumbCancelSelected : st::historyFileThumbCancel); - } else if (!IsServerMsgId(_realParent->id)) { - return nullptr; - } else if (loaded || _data->canBePlayed()) { - return &(selected ? st::historyFileThumbPlaySelected : st::historyFileThumbPlay); - } - return &(selected ? st::historyFileThumbDownloadSelected : st::historyFileThumbDownload); - }(); - const auto previous = [&]() -> const style::icon* { - if (_data->waitingForAlbum()) { - return &(selected ? st::historyFileThumbCancelSelected : st::historyFileThumbCancel); - } - return nullptr; - }(); - p.setOpacity(backOpacity); - if (icon) { - if (previous && radialOpacity > 0. && radialOpacity < 1.) { - PaintInterpolatedIcon(p, *icon, *previous, radialOpacity, inner); - } else { - icon->paintInCenter(p, inner); - } - } - p.setOpacity(1); - if (radial) { - const auto line = st::historyGroupRadialLine; - const auto rinner = inner.marginsRemoved({ line, line, line, line }); - const auto color = selected - ? st::historyFileThumbRadialFgSelected - : st::historyFileThumbRadialFg; - _animation->radial.draw(p, rinner, line, color); - } -} - -TextState Video::getStateGrouped( - const QRect &geometry, - QPoint point, - StateRequest request) const { - if (!geometry.contains(point)) { - return {}; - } - return TextState(_parent, (_data->loading() || _data->uploading()) - ? _cancell - : !IsServerMsgId(_realParent->id) - ? nullptr - : (_data->loaded() || _data->canBePlayed()) - ? _openl - : _savel); -} - -bool Video::uploading() const { - return _data->uploading(); -} - -float64 Video::dataProgress() const { - return _data->progress(); -} - -bool Video::dataFinished() const { - return !_data->loading() - && (!_data->uploading() || _data->waitingForAlbum()); -} - -bool Video::dataLoaded() const { - return _data->loaded(); -} - -void Video::validateGroupedCache( - const QRect &geometry, - RectParts corners, - not_null cacheKey, - not_null cache) const { - using Option = Images::Option; - const auto good = _data->goodThumbnail(); - const auto useGood = (good && good->loaded()); - const auto thumb = _data->thumbnail(); - const auto useThumb = (thumb && thumb->loaded()); - const auto image = useGood - ? good - : useThumb - ? thumb - : _data->thumbnailInline(); - if (good && !useGood) { - good->load({}); - } - - const auto loadLevel = useGood ? 3 : useThumb ? 2 : image ? 1 : 0; - const auto width = geometry.width(); - const auto height = geometry.height(); - const auto options = Option::Smooth - | Option::RoundedLarge - | (useGood ? Option(0) : Option::Blurred) - | ((corners & RectPart::TopLeft) ? Option::RoundedTopLeft : Option::None) - | ((corners & RectPart::TopRight) ? Option::RoundedTopRight : Option::None) - | ((corners & RectPart::BottomLeft) ? Option::RoundedBottomLeft : Option::None) - | ((corners & RectPart::BottomRight) ? Option::RoundedBottomRight : Option::None); - const auto key = (uint64(width) << 48) - | (uint64(height) << 32) - | (uint64(options) << 16) - | (uint64(loadLevel)); - if (*cacheKey == key) { - return; - } - - const auto original = sizeForAspectRatio(); - const auto originalWidth = style::ConvertScale(original.width()); - const auto originalHeight = style::ConvertScale(original.height()); - const auto pixSize = Ui::GetImageScaleSizeForGeometry( - { originalWidth, originalHeight }, - { width, height }); - const auto pixWidth = pixSize.width() * cIntRetinaFactor(); - const auto pixHeight = pixSize.height() * cIntRetinaFactor(); - - *cacheKey = key; - *cache = (image ? image : Image::BlankMedia().get())->pixNoCache( - _realParent->fullId(), - pixWidth, - pixHeight, - options, - width, - height); -} - -void Video::setStatusSize(int newSize) const { - File::setStatusSize(newSize, _data->size, _data->getDuration(), 0); -} - -TextForMimeData Video::selectedText(TextSelection selection) const { - return _caption.toTextForMimeData(selection); -} - -bool Video::needsBubble() const { - if (!_caption.isEmpty()) { - return true; - } - const auto item = _parent->data(); - return item->viaBot() - || item->Has() - || _parent->displayForwardedFrom() - || _parent->displayFromName(); - return false; -} - -void Video::parentTextUpdated() { - _caption = (_parent->media() == this) - ? createCaption(_parent->data()) - : Ui::Text::String(); - history()->owner().requestViewResize(_parent); -} - -void Video::updateStatusText() const { - auto showPause = false; - auto statusSize = 0; - auto realDuration = 0; - if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) { - statusSize = FileStatusSizeFailed; - } else if (_data->uploading()) { - statusSize = _data->uploadingData->offset; - } else if (!downloadInCorner() && _data->loading()) { - statusSize = _data->loadOffset(); - } else if (_data->canBePlayed()) { - statusSize = FileStatusSizeLoaded; - } else { - statusSize = FileStatusSizeReady; - } - if (statusSize != _statusSize) { - setStatusSize(statusSize); - } -} - -} // namespace HistoryView diff --git a/Telegram/SourceFiles/mtproto/connection.cpp b/Telegram/SourceFiles/mtproto/connection.cpp index 84410bc5a..e69de29bb 100644 --- a/Telegram/SourceFiles/mtproto/connection.cpp +++ b/Telegram/SourceFiles/mtproto/connection.cpp @@ -1,3342 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include "mtproto/connection.h" - -#include "mtproto/session.h" -#include "mtproto/rsa_public_key.h" -#include "mtproto/rpc_sender.h" -#include "mtproto/dc_options.h" -#include "mtproto/connection_abstract.h" -#include "zlib.h" -#include "core/application.h" -#include "core/launcher.h" -#include "lang/lang_keys.h" -#include "base/openssl_help.h" -#include "base/qthelp_url.h" -#include "base/unixtime.h" - -extern "C" { -#include -#include -#include -#include -#include -#include -} // extern "C" - -#ifdef small -#undef small -#endif // small - -namespace MTP { -namespace internal { -namespace { - -constexpr auto kRecreateKeyId = AuthKey::KeyId(0xFFFFFFFFFFFFFFFFULL); -constexpr auto kIntSize = static_cast(sizeof(mtpPrime)); -constexpr auto kMaxModExpSize = 256; -constexpr auto kWaitForBetterTimeout = crl::time(2000); -constexpr auto kMinConnectedTimeout = crl::time(1000); -constexpr auto kMaxConnectedTimeout = crl::time(8000); -constexpr auto kMinReceiveTimeout = crl::time(4000); -constexpr auto kMaxReceiveTimeout = crl::time(64000); -constexpr auto kMarkConnectionOldTimeout = crl::time(192000); -constexpr auto kPingDelayDisconnect = 60; -constexpr auto kPingSendAfter = crl::time(30000); -constexpr auto kPingSendAfterForce = crl::time(45000); -constexpr auto kTestModeDcIdShift = 10000; - -// If we can't connect for this time we will ask _instance to update config. -constexpr auto kRequestConfigTimeout = crl::time(8000); - -// Don't try to handle messages larger than this size. -constexpr auto kMaxMessageLength = 16 * 1024 * 1024; - -QString LogIdsVector(const QVector &ids) { - if (!ids.size()) return "[]"; - auto idsStr = QString("[%1").arg(ids.cbegin()->v); - for (const auto &id : ids) { - idsStr += QString(", %2").arg(id.v); - } - return idsStr + "]"; -} - -bool IsGoodModExpFirst( - const openssl::BigNum &modexp, - const openssl::BigNum &prime) { - const auto diff = openssl::BigNum::Sub(prime, modexp); - if (modexp.failed() || prime.failed() || diff.failed()) { - return false; - } - constexpr auto kMinDiffBitsCount = 2048 - 64; - if (diff.isNegative() - || diff.bitsSize() < kMinDiffBitsCount - || modexp.bitsSize() < kMinDiffBitsCount - || modexp.bytesSize() > kMaxModExpSize) { - return false; - } - return true; -} - -bool IsPrimeAndGoodCheck(const openssl::BigNum &prime, int g) { - constexpr auto kGoodPrimeBitsCount = 2048; - - if (prime.failed() - || prime.isNegative() - || prime.bitsSize() != kGoodPrimeBitsCount) { - LOG(("MTP Error: Bad prime bits count %1, expected %2." - ).arg(prime.bitsSize() - ).arg(kGoodPrimeBitsCount)); - return false; - } - - const auto context = openssl::Context(); - if (!prime.isPrime(context)) { - LOG(("MTP Error: Bad prime.")); - return false; - } - - switch (g) { - case 2: { - const auto mod8 = prime.countModWord(8); - if (mod8 != 7) { - LOG(("BigNum PT Error: bad g value: %1, mod8: %2").arg(g).arg(mod8)); - return false; - } - } break; - case 3: { - const auto mod3 = prime.countModWord(3); - if (mod3 != 2) { - LOG(("BigNum PT Error: bad g value: %1, mod3: %2").arg(g).arg(mod3)); - return false; - } - } break; - case 4: break; - case 5: { - const auto mod5 = prime.countModWord(5); - if (mod5 != 1 && mod5 != 4) { - LOG(("BigNum PT Error: bad g value: %1, mod5: %2").arg(g).arg(mod5)); - return false; - } - } break; - case 6: { - const auto mod24 = prime.countModWord(24); - if (mod24 != 19 && mod24 != 23) { - LOG(("BigNum PT Error: bad g value: %1, mod24: %2").arg(g).arg(mod24)); - return false; - } - } break; - case 7: { - const auto mod7 = prime.countModWord(7); - if (mod7 != 3 && mod7 != 5 && mod7 != 6) { - LOG(("BigNum PT Error: bad g value: %1, mod7: %2").arg(g).arg(mod7)); - return false; - } - } break; - default: { - LOG(("BigNum PT Error: bad g value: %1").arg(g)); - return false; - } break; - } - - if (!openssl::BigNum(prime).subWord(1).divWord(2).isPrime(context)) { - LOG(("MTP Error: Bad (prime - 1) / 2.")); - return false; - } - - return true; -} - -bool IsPrimeAndGood(bytes::const_span primeBytes, int g) { - static constexpr unsigned char GoodPrime[] = { - 0xC7, 0x1C, 0xAE, 0xB9, 0xC6, 0xB1, 0xC9, 0x04, 0x8E, 0x6C, 0x52, 0x2F, 0x70, 0xF1, 0x3F, 0x73, - 0x98, 0x0D, 0x40, 0x23, 0x8E, 0x3E, 0x21, 0xC1, 0x49, 0x34, 0xD0, 0x37, 0x56, 0x3D, 0x93, 0x0F, - 0x48, 0x19, 0x8A, 0x0A, 0xA7, 0xC1, 0x40, 0x58, 0x22, 0x94, 0x93, 0xD2, 0x25, 0x30, 0xF4, 0xDB, - 0xFA, 0x33, 0x6F, 0x6E, 0x0A, 0xC9, 0x25, 0x13, 0x95, 0x43, 0xAE, 0xD4, 0x4C, 0xCE, 0x7C, 0x37, - 0x20, 0xFD, 0x51, 0xF6, 0x94, 0x58, 0x70, 0x5A, 0xC6, 0x8C, 0xD4, 0xFE, 0x6B, 0x6B, 0x13, 0xAB, - 0xDC, 0x97, 0x46, 0x51, 0x29, 0x69, 0x32, 0x84, 0x54, 0xF1, 0x8F, 0xAF, 0x8C, 0x59, 0x5F, 0x64, - 0x24, 0x77, 0xFE, 0x96, 0xBB, 0x2A, 0x94, 0x1D, 0x5B, 0xCD, 0x1D, 0x4A, 0xC8, 0xCC, 0x49, 0x88, - 0x07, 0x08, 0xFA, 0x9B, 0x37, 0x8E, 0x3C, 0x4F, 0x3A, 0x90, 0x60, 0xBE, 0xE6, 0x7C, 0xF9, 0xA4, - 0xA4, 0xA6, 0x95, 0x81, 0x10, 0x51, 0x90, 0x7E, 0x16, 0x27, 0x53, 0xB5, 0x6B, 0x0F, 0x6B, 0x41, - 0x0D, 0xBA, 0x74, 0xD8, 0xA8, 0x4B, 0x2A, 0x14, 0xB3, 0x14, 0x4E, 0x0E, 0xF1, 0x28, 0x47, 0x54, - 0xFD, 0x17, 0xED, 0x95, 0x0D, 0x59, 0x65, 0xB4, 0xB9, 0xDD, 0x46, 0x58, 0x2D, 0xB1, 0x17, 0x8D, - 0x16, 0x9C, 0x6B, 0xC4, 0x65, 0xB0, 0xD6, 0xFF, 0x9C, 0xA3, 0x92, 0x8F, 0xEF, 0x5B, 0x9A, 0xE4, - 0xE4, 0x18, 0xFC, 0x15, 0xE8, 0x3E, 0xBE, 0xA0, 0xF8, 0x7F, 0xA9, 0xFF, 0x5E, 0xED, 0x70, 0x05, - 0x0D, 0xED, 0x28, 0x49, 0xF4, 0x7B, 0xF9, 0x59, 0xD9, 0x56, 0x85, 0x0C, 0xE9, 0x29, 0x85, 0x1F, - 0x0D, 0x81, 0x15, 0xF6, 0x35, 0xB1, 0x05, 0xEE, 0x2E, 0x4E, 0x15, 0xD0, 0x4B, 0x24, 0x54, 0xBF, - 0x6F, 0x4F, 0xAD, 0xF0, 0x34, 0xB1, 0x04, 0x03, 0x11, 0x9C, 0xD8, 0xE3, 0xB9, 0x2F, 0xCC, 0x5B }; - - if (!bytes::compare(bytes::make_span(GoodPrime), primeBytes)) { - if (g == 3 || g == 4 || g == 5 || g == 7) { - return true; - } - } - - return IsPrimeAndGoodCheck(openssl::BigNum(primeBytes), g); -} - -bytes::vector CreateAuthKey( - bytes::const_span firstBytes, - bytes::const_span randomBytes, - bytes::const_span primeBytes) { - using openssl::BigNum; - - const auto first = BigNum(firstBytes); - const auto prime = BigNum(primeBytes); - if (!IsGoodModExpFirst(first, prime)) { - LOG(("AuthKey Error: Bad first prime in CreateAuthKey().")); - return {}; - } - return BigNum::ModExp(first, BigNum(randomBytes), prime).getBytes(); -} - -ModExpFirst CreateModExp( - int g, - bytes::const_span primeBytes, - bytes::const_span randomSeed) { - Expects(randomSeed.size() == ModExpFirst::kRandomPowerSize); - - using namespace openssl; - - BigNum prime(primeBytes); - auto result = ModExpFirst(); - result.randomPower.resize(ModExpFirst::kRandomPowerSize); - while (true) { - bytes::set_random(result.randomPower); - for (auto i = 0; i != ModExpFirst::kRandomPowerSize; ++i) { - result.randomPower[i] ^= randomSeed[i]; - } - const auto modexp = BigNum::ModExp( - BigNum(g), - BigNum(result.randomPower), - prime); - if (IsGoodModExpFirst(modexp, prime)) { - result.modexp = modexp.getBytes(); - return result; - } - } -} - -void wrapInvokeAfter(SecureRequest &to, const SecureRequest &from, const RequestMap &haveSent, int32 skipBeforeRequest = 0) { - const auto afterId = *(mtpMsgId*)(from->after->data() + 4); - const auto i = afterId ? haveSent.constFind(afterId) : haveSent.cend(); - int32 size = to->size(), lenInInts = (tl::count_length(from) >> 2), headlen = 4, fulllen = headlen + lenInInts; - if (i == haveSent.constEnd()) { // no invoke after or such msg was not sent or was completed recently - to->resize(size + fulllen + skipBeforeRequest); - if (skipBeforeRequest) { - memcpy(to->data() + size, from->constData() + 4, headlen * sizeof(mtpPrime)); - memcpy(to->data() + size + headlen + skipBeforeRequest, from->constData() + 4 + headlen, lenInInts * sizeof(mtpPrime)); - } else { - memcpy(to->data() + size, from->constData() + 4, fulllen * sizeof(mtpPrime)); - } - } else { - to->resize(size + fulllen + skipBeforeRequest + 3); - memcpy(to->data() + size, from->constData() + 4, headlen * sizeof(mtpPrime)); - (*to)[size + 3] += 3 * sizeof(mtpPrime); - *((mtpTypeId*)&((*to)[size + headlen + skipBeforeRequest])) = mtpc_invokeAfterMsg; - memcpy(to->data() + size + headlen + skipBeforeRequest + 1, &afterId, 2 * sizeof(mtpPrime)); - memcpy(to->data() + size + headlen + skipBeforeRequest + 3, from->constData() + 4 + headlen, lenInInts * sizeof(mtpPrime)); - if (size + 3 != 7) (*to)[7] += 3 * sizeof(mtpPrime); - } -} - -bool parsePQ(const QByteArray &pqStr, QByteArray &pStr, QByteArray &qStr) { - if (pqStr.length() > 8) return false; // more than 64 bit pq - - uint64 pq = 0, p, q; - const uchar *pqChars = (const uchar*)pqStr.constData(); - for (uint32 i = 0, l = pqStr.length(); i < l; ++i) { - pq <<= 8; - pq |= (uint64)pqChars[i]; - } - uint64 pqSqrt = (uint64)sqrtl((long double)pq), ySqr, y; - while (pqSqrt * pqSqrt > pq) --pqSqrt; - while (pqSqrt * pqSqrt < pq) ++pqSqrt; - for (ySqr = pqSqrt * pqSqrt - pq; ; ++pqSqrt, ySqr = pqSqrt * pqSqrt - pq) { - y = (uint64)sqrtl((long double)ySqr); - while (y * y > ySqr) --y; - while (y * y < ySqr) ++y; - if (!ySqr || y + pqSqrt >= pq) return false; - if (y * y == ySqr) { - p = pqSqrt + y; - q = (pqSqrt > y) ? (pqSqrt - y) : (y - pqSqrt); - break; - } - } - if (p > q) std::swap(p, q); - - pStr.resize(4); - uchar *pChars = (uchar*)pStr.data(); - for (uint32 i = 0; i < 4; ++i) { - *(pChars + 3 - i) = (uchar)(p & 0xFF); - p >>= 8; - } - - qStr.resize(4); - uchar *qChars = (uchar*)qStr.data(); - for (uint32 i = 0; i < 4; ++i) { - *(qChars + 3 - i) = (uchar)(q & 0xFF); - q >>= 8; - } - - return true; -} - -} // namespace - -Connection::Connection(not_null instance) : _instance(instance) { -} - -void Connection::start(SessionData *sessionData, ShiftedDcId shiftedDcId) { - Expects(_thread == nullptr && _private == nullptr); - - _thread = std::make_unique(); - auto newData = std::make_unique( - _instance, - _thread.get(), - this, - sessionData, - shiftedDcId); - - // will be deleted in the thread::finished signal - _private = newData.release(); - _thread->start(); -} - -void Connection::kill() { - Expects(_private != nullptr && _thread != nullptr); - - _private->stop(); - _private = nullptr; - _thread->quit(); -} - -void Connection::waitTillFinish() { - Expects(_private == nullptr && _thread != nullptr); - - DEBUG_LOG(("Waiting for connectionThread to finish")); - _thread->wait(); - _thread.reset(); -} - -int32 Connection::state() const { - Expects(_private != nullptr && _thread != nullptr); - - return _private->getState(); -} - -QString Connection::transport() const { - Expects(_private != nullptr && _thread != nullptr); - - return _private->transport(); -} - -Connection::~Connection() { - Expects(_private == nullptr); - - if (_thread) { - waitTillFinish(); - } -} - -void ConnectionPrivate::appendTestConnection( - DcOptions::Variants::Protocol protocol, - const QString &ip, - int port, - const bytes::vector &protocolSecret) { - QWriteLocker lock(&stateConnMutex); - - const auto priority = (qthelp::is_ipv6(ip) ? 0 : 1) - + (protocol == DcOptions::Variants::Tcp ? 1 : 0) - + (protocolSecret.empty() ? 0 : 1); - _testConnections.push_back({ - AbstractConnection::Create( - _instance, - protocol, - thread(), - protocolSecret, - _connectionOptions->proxy), - priority - }); - const auto weak = _testConnections.back().data.get(); - connect(weak, &AbstractConnection::error, [=](int errorCode) { - onError(weak, errorCode); - }); - connect(weak, &AbstractConnection::receivedSome, [=] { - onReceivedSome(); - }); - firstSentAt = 0; - if (_oldConnection) { - _oldConnection = false; - DEBUG_LOG(("This connection marked as not old!")); - } - _oldConnectionTimer.callOnce(kMarkConnectionOldTimeout); - connect(weak, &AbstractConnection::connected, [=] { - onConnected(weak); - }); - connect(weak, &AbstractConnection::disconnected, [=] { - onDisconnected(weak); - }); - connect(weak, &AbstractConnection::syncTimeRequest, [=] { - InvokeQueued(_instance, [instance = _instance] { - instance->syncHttpUnixtime(); - }); - }); - - InvokeQueued(_testConnections.back().data, [=] { - weak->connectToServer(ip, port, protocolSecret, getProtocolDcId()); - }); -} - -int16 ConnectionPrivate::getProtocolDcId() const { - const auto dcId = BareDcId(_shiftedDcId); - const auto simpleDcId = isTemporaryDcId(dcId) - ? getRealIdFromTemporaryDcId(dcId) - : dcId; - const auto testedDcId = cTestMode() - ? (kTestModeDcIdShift + simpleDcId) - : simpleDcId; - return (_dcType == DcType::MediaDownload) - ? -testedDcId - : testedDcId; -} - -void ConnectionPrivate::destroyAllConnections() { - _waitForBetterTimer.cancel(); - _waitForReceivedTimer.cancel(); - _waitForConnectedTimer.cancel(); - _testConnections.clear(); - _connection = nullptr; -} - -ConnectionPrivate::ConnectionPrivate( - not_null instance, - not_null thread, - not_null owner, - not_null data, - ShiftedDcId shiftedDcId) -: QObject(nullptr) -, _instance(instance) -, _state(DisconnectedState) -, _shiftedDcId(shiftedDcId) -, _owner(owner) -, _retryTimer(thread, [=] { retryByTimer(); }) -, _oldConnectionTimer(thread, [=] { markConnectionOld(); }) -, _waitForConnectedTimer(thread, [=] { waitConnectedFailed(); }) -, _waitForReceivedTimer(thread, [=] { waitReceivedFailed(); }) -, _waitForBetterTimer(thread, [=] { waitBetterFailed(); }) -, _waitForReceived(kMinReceiveTimeout) -, _waitForConnected(kMinConnectedTimeout) -, _pingSender(thread, [=] { sendPingByTimer(); }) -, sessionData(data) { - Expects(_shiftedDcId != 0); - - moveToThread(thread); - - connect(thread, &QThread::started, this, [=] { connectToServer(); }); - connect(thread, &QThread::finished, this, [=] { finishAndDestroy(); }); - connect(this, SIGNAL(finished(internal::Connection*)), _instance, SLOT(connectionFinished(internal::Connection*)), Qt::QueuedConnection); - - connect(sessionData->owner(), SIGNAL(authKeyCreated()), this, SLOT(updateAuthKey()), Qt::QueuedConnection); - connect(sessionData->owner(), SIGNAL(needToRestart()), this, SLOT(restartNow()), Qt::QueuedConnection); - connect(this, SIGNAL(needToReceive()), sessionData->owner(), SLOT(tryToReceive()), Qt::QueuedConnection); - connect(this, SIGNAL(stateChanged(qint32)), sessionData->owner(), SLOT(onConnectionStateChange(qint32)), Qt::QueuedConnection); - connect(sessionData->owner(), SIGNAL(needToSend()), this, SLOT(tryToSend()), Qt::QueuedConnection); - connect(sessionData->owner(), SIGNAL(needToPing()), this, SLOT(onPingSendForce()), Qt::QueuedConnection); - connect(this, SIGNAL(sessionResetDone()), sessionData->owner(), SLOT(onResetDone()), Qt::QueuedConnection); - - static bool _registered = false; - if (!_registered) { - _registered = true; - qRegisterMetaType >("QVector"); - } - - connect(this, SIGNAL(needToSendAsync()), sessionData->owner(), SLOT(needToResumeAndSend()), Qt::QueuedConnection); - connect(this, SIGNAL(sendAnythingAsync(qint64)), sessionData->owner(), SLOT(sendAnything(qint64)), Qt::QueuedConnection); - connect(this, SIGNAL(sendHttpWaitAsync()), sessionData->owner(), SLOT(sendAnything()), Qt::QueuedConnection); - connect(this, SIGNAL(sendPongAsync(quint64,quint64)), sessionData->owner(), SLOT(sendPong(quint64,quint64)), Qt::QueuedConnection); - connect(this, SIGNAL(sendMsgsStateInfoAsync(quint64, QByteArray)), sessionData->owner(), SLOT(sendMsgsStateInfo(quint64,QByteArray)), Qt::QueuedConnection); - connect(this, SIGNAL(resendAsync(quint64,qint64,bool,bool)), sessionData->owner(), SLOT(resend(quint64,qint64,bool,bool)), Qt::QueuedConnection); - connect(this, SIGNAL(resendManyAsync(QVector,qint64,bool,bool)), sessionData->owner(), SLOT(resendMany(QVector,qint64,bool,bool)), Qt::QueuedConnection); - connect(this, SIGNAL(resendAllAsync()), sessionData->owner(), SLOT(resendAll()), Qt::QueuedConnection); -} - -void ConnectionPrivate::onConfigLoaded() { - connectToServer(true); -} - -void ConnectionPrivate::onCDNConfigLoaded() { - restart(); -} - -int32 ConnectionPrivate::getShiftedDcId() const { - return _shiftedDcId; -} - -int32 ConnectionPrivate::getState() const { - QReadLocker lock(&stateConnMutex); - int32 result = _state; - if (_state < 0) { - if (_retryTimer.isActive()) { - result = int32(crl::now() - _retryWillFinish); - if (result >= 0) { - result = -1; - } - } - } - return result; -} - -QString ConnectionPrivate::transport() const { - QReadLocker lock(&stateConnMutex); - if (!_connection || (_state < 0)) { - return QString(); - } - - Assert(_connectionOptions != nullptr); - return _connection->transport(); -} - -bool ConnectionPrivate::setState(int32 state, int32 ifState) { - if (ifState != Connection::UpdateAlways) { - QReadLocker lock(&stateConnMutex); - if (_state != ifState) return false; - } - QWriteLocker lock(&stateConnMutex); - if (_state == state) return false; - _state = state; - if (state < 0) { - _retryTimeout = -state; - _retryTimer.callOnce(_retryTimeout); - _retryWillFinish = crl::now() + _retryTimeout; - } - emit stateChanged(state); - return true; -} - -void ConnectionPrivate::resetSession() { // recreate all msg_id and msg_seqno - _needSessionReset = false; - - QWriteLocker locker1(sessionData->haveSentMutex()); - QWriteLocker locker2(sessionData->toResendMutex()); - QWriteLocker locker3(sessionData->toSendMutex()); - QWriteLocker locker4(sessionData->wereAckedMutex()); - auto &haveSent = sessionData->haveSentMap(); - auto &toResend = sessionData->toResendMap(); - auto &toSend = sessionData->toSendMap(); - auto &wereAcked = sessionData->wereAckedMap(); - - auto newId = base::unixtime::mtproto_msg_id(); - auto setSeqNumbers = RequestMap(); - auto replaces = QMap(); - for (auto i = haveSent.cbegin(), e = haveSent.cend(); i != e; ++i) { - if (!i.value().isSentContainer()) { - if (!*(mtpMsgId*)(i.value()->constData() + 4)) continue; - - mtpMsgId id = i.key(); - if (id > newId) { - while (true) { - if (toResend.constFind(newId) == toResend.cend() - && wereAcked.constFind(newId) == wereAcked.cend() - && haveSent.constFind(newId) == haveSent.cend()) { - break; - } - const auto m = base::unixtime::mtproto_msg_id(); - if (m <= newId) break; // wtf - - newId = m; - } - - MTP_LOG(_shiftedDcId, ("Replacing msgId %1 to %2!" - ).arg(id - ).arg(newId)); - replaces.insert(id, newId); - id = newId; - *(mtpMsgId*)(i.value()->data() + 4) = id; - } - setSeqNumbers.insert(id, i.value()); - } - } - // Collect all non-container requests. - for (auto i = toResend.cbegin(), e = toResend.cend(); i != e; ++i) { - const auto j = toSend.constFind(i.value()); - if (j == toSend.cend()) continue; - - if (!j.value().isSentContainer()) { - if (!*(mtpMsgId*)(j.value()->constData() + 4)) continue; - - mtpMsgId id = i.key(); - if (id > newId) { - while (true) { - if (toResend.constFind(newId) == toResend.cend() - && wereAcked.constFind(newId) == wereAcked.cend() - && haveSent.constFind(newId) == haveSent.cend()) { - break; - } - const auto m = base::unixtime::mtproto_msg_id(); - if (m <= newId) break; // wtf - - newId = m; - } - - MTP_LOG(_shiftedDcId, ("Replacing msgId %1 to %2!" - ).arg(id - ).arg(newId)); - replaces.insert(id, newId); - id = newId; - *(mtpMsgId*)(j.value()->data() + 4) = id; - } - setSeqNumbers.insert(id, j.value()); - } - } - - uint64 session = rand_value(); - DEBUG_LOG(("MTP Info: creating new session after bad_msg_notification, setting random server_session %1").arg(session)); - sessionData->setSession(session); - - for (auto i = setSeqNumbers.cbegin(), e = setSeqNumbers.cend(); i != e; ++i) { // generate new seq_numbers - bool wasNeedAck = (*(i.value()->data() + 6) & 1); - *(i.value()->data() + 6) = sessionData->nextRequestSeqNumber(wasNeedAck); - } - if (!replaces.isEmpty()) { - for (auto i = replaces.cbegin(), e = replaces.cend(); i != e; ++i) { // replace msgIds keys in all data structs - const auto j = haveSent.find(i.key()); - if (j != haveSent.cend()) { - const auto req = j.value(); - haveSent.erase(j); - haveSent.insert(i.value(), req); - } - const auto k = toResend.find(i.key()); - if (k != toResend.cend()) { - const auto req = k.value(); - toResend.erase(k); - toResend.insert(i.value(), req); - } - const auto l = wereAcked.find(i.key()); - if (l != wereAcked.cend()) { - DEBUG_LOG(("MTP Info: Replaced %1 with %2 in wereAcked." - ).arg(i.key() - ).arg(i.value())); - - const auto req = l.value(); - wereAcked.erase(l); - wereAcked.insert(i.value(), req); - } - } - for (auto i = haveSent.cbegin(), e = haveSent.cend(); i != e; ++i) { // replace msgIds in saved containers - if (i.value().isSentContainer()) { - mtpMsgId *ids = (mtpMsgId*)(i.value()->data() + 8); - for (uint32 j = 0, l = (i.value()->size() - 8) >> 1; j < l; ++j) { - const auto k = replaces.constFind(ids[j]); - if (k != replaces.cend()) { - ids[j] = k.value(); - } - } - } - } - } - - ackRequestData.clear(); - resendRequestData.clear(); - { - QWriteLocker locker5(sessionData->stateRequestMutex()); - sessionData->stateRequestMap().clear(); - } - - emit sessionResetDone(); -} - -mtpMsgId ConnectionPrivate::prepareToSend(SecureRequest &request, mtpMsgId currentLastId) { - if (request->size() < 9) return 0; - mtpMsgId msgId = *(mtpMsgId*)(request->constData() + 4); - if (msgId) { // resending this request - QWriteLocker locker(sessionData->toResendMutex()); - auto &toResend = sessionData->toResendMap(); - const auto i = toResend.find(msgId); - if (i != toResend.cend()) { - toResend.erase(i); - } - } else { - msgId = *(mtpMsgId*)(request->data() + 4) = currentLastId; - *(request->data() + 6) = sessionData->nextRequestSeqNumber(request.needAck()); - } - return msgId; -} - -mtpMsgId ConnectionPrivate::replaceMsgId(SecureRequest &request, mtpMsgId newId) { - if (request->size() < 9) return 0; - - mtpMsgId oldMsgId = *(mtpMsgId*)(request->constData() + 4); - if (oldMsgId != newId) { - if (oldMsgId) { - QWriteLocker locker(sessionData->toResendMutex()); - // haveSentMutex() and wereAckedMutex() were locked in tryToSend() - - auto &toResend = sessionData->toResendMap(); - auto &wereAcked = sessionData->wereAckedMap(); - auto &haveSent = sessionData->haveSentMap(); - - while (true) { - if (toResend.constFind(newId) == toResend.cend() && wereAcked.constFind(newId) == wereAcked.cend() && haveSent.constFind(newId) == haveSent.cend()) { - break; - } - const auto m = base::unixtime::mtproto_msg_id(); - if (m <= newId) break; // wtf - - newId = m; - } - - const auto i = toResend.find(oldMsgId); - if (i != toResend.cend()) { - const auto req = i.value(); - toResend.erase(i); - toResend.insert(newId, req); - } - - const auto j = wereAcked.find(oldMsgId); - if (j != wereAcked.cend()) { - const auto req = j.value(); - wereAcked.erase(j); - wereAcked.insert(newId, req); - } - - const auto k = haveSent.find(oldMsgId); - if (k != haveSent.cend()) { - const auto req = k.value(); - haveSent.erase(k); - haveSent.insert(newId, req); - } - - for (auto l = haveSent.begin(); l != haveSent.cend(); ++l) { - const auto req = l.value(); - if (req.isSentContainer()) { - const auto ids = (mtpMsgId *)(req->data() + 8); - for (uint32 i = 0, l = (req->size() - 8) >> 1; i < l; ++i) { - if (ids[i] == oldMsgId) { - ids[i] = newId; - } - } - } - } - } else { - *(request->data() + 6) = sessionData->nextRequestSeqNumber(request.needAck()); - } - *(mtpMsgId*)(request->data() + 4) = newId; - } - return newId; -} - -mtpMsgId ConnectionPrivate::placeToContainer(SecureRequest &toSendRequest, mtpMsgId &bigMsgId, mtpMsgId *&haveSentArr, SecureRequest &req) { - auto msgId = prepareToSend(req, bigMsgId); - if (msgId > bigMsgId) { - msgId = replaceMsgId(req, bigMsgId); - } - if (msgId >= bigMsgId) { - bigMsgId = base::unixtime::mtproto_msg_id(); - } - *(haveSentArr++) = msgId; - - uint32 from = toSendRequest->size(), len = req.messageSize(); - toSendRequest->resize(from + len); - memcpy(toSendRequest->data() + from, req->constData() + 4, len * sizeof(mtpPrime)); - - return msgId; -} - -void ConnectionPrivate::tryToSend() { - QReadLocker lockFinished(&sessionDataMutex); - if (!sessionData || !_connection) { - return; - } - - auto needsLayer = !_connectionOptions->inited; - auto state = getState(); - auto prependOnly = (state != ConnectedState); - auto pingRequest = SecureRequest(); - if (_shiftedDcId == BareDcId(_shiftedDcId)) { // main session - if (!prependOnly && !_pingIdToSend && !_pingId && _pingSendAt <= crl::now()) { - _pingIdToSend = rand_value(); - } - } - if (_pingIdToSend) { - if (prependOnly || _shiftedDcId != BareDcId(_shiftedDcId)) { - pingRequest = SecureRequest::Serialize(MTPPing( - MTP_long(_pingIdToSend) - )); - DEBUG_LOG(("MTP Info: sending ping, ping_id: %1" - ).arg(_pingIdToSend)); - } else { - pingRequest = SecureRequest::Serialize(MTPPing_delay_disconnect( - MTP_long(_pingIdToSend), - MTP_int(kPingDelayDisconnect))); - DEBUG_LOG(("MTP Info: sending ping_delay_disconnect, " - "ping_id: %1").arg(_pingIdToSend)); - } - - pingRequest->msDate = crl::now(); // > 0 - can send without container - _pingSendAt = pingRequest->msDate + kPingSendAfter; - pingRequest->requestId = 0; // dont add to haveSent / wereAcked maps - - if (_shiftedDcId == BareDcId(_shiftedDcId) && !prependOnly) { // main session - _pingSender.callOnce(kPingSendAfterForce); - } - - _pingId = _pingIdToSend; - _pingIdToSend = 0; - } else { - if (prependOnly) { - DEBUG_LOG(("MTP Info: dc %1 not sending, waiting for Connected state, state: %2").arg(_shiftedDcId).arg(state)); - return; // just do nothing, if is not connected yet - } else { - DEBUG_LOG(("MTP Info: dc %1 trying to send after ping, state: %2").arg(_shiftedDcId).arg(state)); - } - } - - SecureRequest ackRequest, resendRequest, stateRequest, httpWaitRequest; - if (!prependOnly && !ackRequestData.isEmpty()) { - ackRequest = SecureRequest::Serialize(MTPMsgsAck( - MTP_msgs_ack(MTP_vector(ackRequestData)))); - ackRequest->msDate = crl::now(); // > 0 - can send without container - ackRequest->requestId = 0; // dont add to haveSent / wereAcked maps - - ackRequestData.clear(); - } - if (!prependOnly && !resendRequestData.isEmpty()) { - resendRequest = SecureRequest::Serialize(MTPMsgResendReq( - MTP_msg_resend_req(MTP_vector(resendRequestData)))); - resendRequest->msDate = crl::now(); // > 0 - can send without container - resendRequest->requestId = 0; // dont add to haveSent / wereAcked maps - - resendRequestData.clear(); - } - if (!prependOnly) { - QVector stateReq; - { - QWriteLocker locker(sessionData->stateRequestMutex()); - auto &ids = sessionData->stateRequestMap(); - if (!ids.isEmpty()) { - stateReq.reserve(ids.size()); - for (auto i = ids.cbegin(), e = ids.cend(); i != e; ++i) { - stateReq.push_back(MTP_long(i.key())); - } - } - ids.clear(); - } - if (!stateReq.isEmpty()) { - stateRequest = SecureRequest::Serialize(MTPMsgsStateReq( - MTP_msgs_state_req(MTP_vector(stateReq)))); - stateRequest->msDate = crl::now(); // > 0 - can send without container - stateRequest->requestId = GetNextRequestId();// add to haveSent / wereAcked maps, but don't add to requestMap - } - if (_connection->usingHttpWait()) { - httpWaitRequest = SecureRequest::Serialize(MTPHttpWait( - MTP_http_wait(MTP_int(100), MTP_int(30), MTP_int(25000)))); - httpWaitRequest->msDate = crl::now(); // > 0 - can send without container - httpWaitRequest->requestId = 0; // dont add to haveSent / wereAcked maps - } - } - - MTPInitConnection initWrapper; - int32 initSize = 0, initSizeInInts = 0; - if (needsLayer) { - Assert(_connectionOptions != nullptr); - const auto systemLangCode = _connectionOptions->systemLangCode; - const auto cloudLangCode = _connectionOptions->cloudLangCode; - const auto langPackName = _connectionOptions->langPackName; - const auto deviceModel = (_dcType == DcType::Cdn) - ? "n/a" - : _instance->deviceModel(); - const auto systemVersion = (_dcType == DcType::Cdn) - ? "n/a" - : _instance->systemVersion(); -#if defined OS_MAC_STORE - const auto appVersion = QString::fromLatin1(AppKotatoVersionStr) - + " mac store"; -#elif defined OS_WIN_STORE // OS_MAC_STORE - const auto appVersion = QString::fromLatin1(AppKotatoVersionStr) - + " win store"; -#else // OS_MAC_STORE || OS_WIN_STORE - const auto appVersion = QString::fromLatin1(AppKotatoVersionStr); -#endif // OS_MAC_STORE || OS_WIN_STORE - const auto proxyType = _connectionOptions->proxy.type; - const auto mtprotoProxy = (proxyType == ProxyData::Type::Mtproto); - const auto clientProxyFields = mtprotoProxy - ? MTP_inputClientProxy( - MTP_string(_connectionOptions->proxy.host), - MTP_int(_connectionOptions->proxy.port)) - : MTPInputClientProxy(); - using Flag = MTPInitConnection::Flag; - initWrapper = MTPInitConnection( - MTP_flags(mtprotoProxy ? Flag::f_proxy : Flag(0)), - MTP_int(ApiId), - MTP_string(deviceModel), - MTP_string(systemVersion), - MTP_string(appVersion), - MTP_string(systemLangCode), - MTP_string(langPackName), - MTP_string(cloudLangCode), - clientProxyFields, - SecureRequest()); - initSizeInInts = (tl::count_length(initWrapper) >> 2) + 2; - initSize = initSizeInInts * sizeof(mtpPrime); - } - - bool needAnyResponse = false; - SecureRequest toSendRequest; - { - QWriteLocker locker1(sessionData->toSendMutex()); - - auto toSendDummy = PreRequestMap(); - auto &toSend = prependOnly ? toSendDummy : sessionData->toSendMap(); - if (prependOnly) locker1.unlock(); - - uint32 toSendCount = toSend.size(); - if (pingRequest) ++toSendCount; - if (ackRequest) ++toSendCount; - if (resendRequest) ++toSendCount; - if (stateRequest) ++toSendCount; - if (httpWaitRequest) ++toSendCount; - - if (!toSendCount) return; // nothing to send - - auto first = pingRequest ? pingRequest : (ackRequest ? ackRequest : (resendRequest ? resendRequest : (stateRequest ? stateRequest : (httpWaitRequest ? httpWaitRequest : toSend.cbegin().value())))); - if (toSendCount == 1 && first->msDate > 0) { // if can send without container - toSendRequest = first; - if (!prependOnly) { - toSend.clear(); - locker1.unlock(); - } - - const auto msgId = prepareToSend( - toSendRequest, - base::unixtime::mtproto_msg_id()); - if (pingRequest) { - _pingMsgId = msgId; - needAnyResponse = true; - } else if (resendRequest || stateRequest) { - needAnyResponse = true; - } - - if (toSendRequest->requestId) { - if (toSendRequest.needAck()) { - toSendRequest->msDate = toSendRequest.isStateRequest() ? 0 : crl::now(); - - QWriteLocker locker2(sessionData->haveSentMutex()); - auto &haveSent = sessionData->haveSentMap(); - haveSent.insert(msgId, toSendRequest); - - if (needsLayer && !toSendRequest->needsLayer) needsLayer = false; - if (toSendRequest->after) { - const auto toSendSize = tl::count_length(toSendRequest) >> 2; - auto wrappedRequest = SecureRequest::Prepare( - toSendSize, - toSendSize + 3); - wrappedRequest->resize(4); - memcpy(wrappedRequest->data(), toSendRequest->constData(), 4 * sizeof(mtpPrime)); - wrapInvokeAfter(wrappedRequest, toSendRequest, haveSent); - toSendRequest = std::move(wrappedRequest); - } - if (needsLayer) { - const auto noWrapSize = (tl::count_length(toSendRequest) >> 2); - const auto toSendSize = noWrapSize + initSizeInInts; - auto wrappedRequest = SecureRequest::Prepare(toSendSize); - memcpy(wrappedRequest->data(), toSendRequest->constData(), 7 * sizeof(mtpPrime)); // all except length - wrappedRequest->push_back(mtpc_invokeWithLayer); - wrappedRequest->push_back(internal::CurrentLayer); - initWrapper.write(*wrappedRequest); - wrappedRequest->resize(wrappedRequest->size() + noWrapSize); - memcpy(wrappedRequest->data() + wrappedRequest->size() - noWrapSize, toSendRequest->constData() + 8, noWrapSize * sizeof(mtpPrime)); - toSendRequest = std::move(wrappedRequest); - } - - needAnyResponse = true; - } else { - QWriteLocker locker3(sessionData->wereAckedMutex()); - sessionData->wereAckedMap().insert(msgId, toSendRequest->requestId); - } - } - } else { // send in container - bool willNeedInit = false; - uint32 containerSize = 1 + 1, idsWrapSize = (toSendCount << 1); // cons + vector size, idsWrapSize - size of "request-like" wrap for msgId vector - if (pingRequest) containerSize += pingRequest.messageSize(); - if (ackRequest) containerSize += ackRequest.messageSize(); - if (resendRequest) containerSize += resendRequest.messageSize(); - if (stateRequest) containerSize += stateRequest.messageSize(); - if (httpWaitRequest) containerSize += httpWaitRequest.messageSize(); - for (auto i = toSend.begin(), e = toSend.end(); i != e; ++i) { - containerSize += i.value().messageSize(); - if (needsLayer && i.value()->needsLayer) { - containerSize += initSizeInInts; - willNeedInit = true; - } - } - mtpBuffer initSerialized; - if (willNeedInit) { - initSerialized.reserve(initSizeInInts); - initSerialized.push_back(mtpc_invokeWithLayer); - initSerialized.push_back(internal::CurrentLayer); - initWrapper.write(initSerialized); - } - // prepare container + each in invoke after - toSendRequest = SecureRequest::Prepare( - containerSize, - containerSize + 3 * toSend.size()); - toSendRequest->push_back(mtpc_msg_container); - toSendRequest->push_back(toSendCount); - - // check for a valid container - auto bigMsgId = base::unixtime::mtproto_msg_id(); - - // the fact of this lock is used in replaceMsgId() - QWriteLocker locker2(sessionData->haveSentMutex()); - auto &haveSent = sessionData->haveSentMap(); - - // the fact of this lock is used in replaceMsgId() - QWriteLocker locker3(sessionData->wereAckedMutex()); - auto &wereAcked = sessionData->wereAckedMap(); - - // prepare "request-like" wrap for msgId vector - auto haveSentIdsWrap = SecureRequest::Prepare(idsWrapSize); - haveSentIdsWrap->requestId = 0; - haveSentIdsWrap->resize(haveSentIdsWrap->size() + idsWrapSize); - auto haveSentArr = (mtpMsgId*)(haveSentIdsWrap->data() + 8); - - if (pingRequest) { - _pingMsgId = placeToContainer(toSendRequest, bigMsgId, haveSentArr, pingRequest); - needAnyResponse = true; - } else if (resendRequest || stateRequest) { - needAnyResponse = true; - } - for (auto i = toSend.begin(), e = toSend.end(); i != e; ++i) { - auto &req = i.value(); - auto msgId = prepareToSend(req, bigMsgId); - if (msgId > bigMsgId) { - msgId = replaceMsgId(req, bigMsgId); - } - if (msgId >= bigMsgId) { - bigMsgId = base::unixtime::mtproto_msg_id(); - } - *(haveSentArr++) = msgId; - bool added = false; - if (req->requestId) { - if (req.needAck()) { - req->msDate = req.isStateRequest() ? 0 : crl::now(); - int32 reqNeedsLayer = (needsLayer && req->needsLayer) ? toSendRequest->size() : 0; - if (req->after) { - wrapInvokeAfter(toSendRequest, req, haveSent, reqNeedsLayer ? initSizeInInts : 0); - if (reqNeedsLayer) { - memcpy(toSendRequest->data() + reqNeedsLayer + 4, initSerialized.constData(), initSize); - *(toSendRequest->data() + reqNeedsLayer + 3) += initSize; - } - added = true; - } else if (reqNeedsLayer) { - toSendRequest->resize(reqNeedsLayer + initSizeInInts + req.messageSize()); - memcpy(toSendRequest->data() + reqNeedsLayer, req->constData() + 4, 4 * sizeof(mtpPrime)); - memcpy(toSendRequest->data() + reqNeedsLayer + 4, initSerialized.constData(), initSize); - memcpy(toSendRequest->data() + reqNeedsLayer + 4 + initSizeInInts, req->constData() + 8, tl::count_length(req)); - *(toSendRequest->data() + reqNeedsLayer + 3) += initSize; - added = true; - } - haveSent.insert(msgId, req); - - needAnyResponse = true; - } else { - wereAcked.insert(msgId, req->requestId); - } - } - if (!added) { - uint32 from = toSendRequest->size(), len = req.messageSize(); - toSendRequest->resize(from + len); - memcpy(toSendRequest->data() + from, req->constData() + 4, len * sizeof(mtpPrime)); - } - } - if (stateRequest) { - mtpMsgId msgId = placeToContainer(toSendRequest, bigMsgId, haveSentArr, stateRequest); - stateRequest->msDate = 0; // 0 for state request, do not request state of it - haveSent.insert(msgId, stateRequest); - } - if (resendRequest) placeToContainer(toSendRequest, bigMsgId, haveSentArr, resendRequest); - if (ackRequest) placeToContainer(toSendRequest, bigMsgId, haveSentArr, ackRequest); - if (httpWaitRequest) placeToContainer(toSendRequest, bigMsgId, haveSentArr, httpWaitRequest); - - mtpMsgId contMsgId = prepareToSend(toSendRequest, bigMsgId); - *(mtpMsgId*)(haveSentIdsWrap->data() + 4) = contMsgId; - (*haveSentIdsWrap)[6] = 0; // for container, msDate = 0, seqNo = 0 - haveSent.insert(contMsgId, haveSentIdsWrap); - toSend.clear(); - } - } - sendSecureRequest( - std::move(toSendRequest), - needAnyResponse, - lockFinished); -} - -void ConnectionPrivate::retryByTimer() { - QReadLocker lockFinished(&sessionDataMutex); - if (!sessionData) return; - - if (_retryTimeout < 3) { - ++_retryTimeout; - } else if (_retryTimeout == 3) { - _retryTimeout = 1000; - } else if (_retryTimeout < 64000) { - _retryTimeout *= 2; - } - if (keyId == kRecreateKeyId) { - if (sessionData->getKey()) { - unlockKey(); - - QWriteLocker lock(sessionData->keyMutex()); - sessionData->owner()->destroyKey(); - } - keyId = 0; - } - connectToServer(); -} - -void ConnectionPrivate::restartNow() { - _retryTimeout = 1; - _retryTimer.cancel(); - restart(); -} - -void ConnectionPrivate::connectToServer(bool afterConfig) { - if (_finished) { - DEBUG_LOG(("MTP Error: " - "connectToServer() called for finished connection!")); - return; - } - auto hasKey = true; - { - QReadLocker lockFinished(&sessionDataMutex); - if (!sessionData) { - DEBUG_LOG(("MTP Error: " - "connectToServer() called for stopped connection!")); - return; - } - _connectionOptions = std::make_unique( - sessionData->connectionOptions()); - hasKey = (sessionData->getKey() != nullptr); - } - auto bareDc = BareDcId(_shiftedDcId); - _dcType = _instance->dcOptions()->dcType(_shiftedDcId); - - // Use media_only addresses only if key for this dc is already created. - if (_dcType == DcType::MediaDownload && !hasKey) { - _dcType = DcType::Regular; - } else if (_dcType == DcType::Cdn && !_instance->isKeysDestroyer()) { - if (!_instance->dcOptions()->hasCDNKeysForDc(bareDc)) { - requestCDNConfig(); - return; - } - } - - if (afterConfig && (!_testConnections.empty() || _connection)) { - return; - } - - destroyAllConnections(); - if (_connectionOptions->proxy.type == ProxyData::Type::Mtproto) { - // host, port, secret for mtproto proxy are taken from proxy. - appendTestConnection(DcOptions::Variants::Tcp, {}, 0, {}); - } else { - using Variants = DcOptions::Variants; - const auto special = (_dcType == DcType::Temporary); - const auto variants = _instance->dcOptions()->lookup( - bareDc, - _dcType, - _connectionOptions->proxy.type != ProxyData::Type::None); - const auto useIPv4 = special ? true : _connectionOptions->useIPv4; - const auto useIPv6 = special ? false : _connectionOptions->useIPv6; - const auto useTcp = special ? true : _connectionOptions->useTcp; - const auto useHttp = special ? false : _connectionOptions->useHttp; - const auto skipAddress = !useIPv4 - ? Variants::IPv4 - : !useIPv6 - ? Variants::IPv6 - : Variants::AddressTypeCount; - const auto skipProtocol = !useTcp - ? Variants::Tcp - : !useHttp - ? Variants::Http - : Variants::ProtocolCount; - for (auto address = 0; address != Variants::AddressTypeCount; ++address) { - if (address == skipAddress) { - continue; - } - for (auto protocol = 0; protocol != Variants::ProtocolCount; ++protocol) { - if (protocol == skipProtocol) { - continue; - } - for (const auto &endpoint : variants.data[address][protocol]) { - appendTestConnection( - static_cast(protocol), - QString::fromStdString(endpoint.ip), - endpoint.port, - endpoint.secret); - } - } - } - } - if (_testConnections.empty()) { - if (_instance->isKeysDestroyer()) { - LOG(("MTP Error: DC %1 options for not found for auth key destruction!").arg(_shiftedDcId)); - emit _instance->keyDestroyed(_shiftedDcId); - return; - } else if (afterConfig) { - LOG(("MTP Error: DC %1 options for not found right after config load!").arg(_shiftedDcId)); - return restart(); - } - DEBUG_LOG(("MTP Info: DC %1 options not found, waiting for config").arg(_shiftedDcId)); - connect(_instance, SIGNAL(configLoaded()), this, SLOT(onConfigLoaded()), Qt::UniqueConnection); - InvokeQueued(_instance, [instance = _instance] { - instance->requestConfig(); - }); - return; - } - DEBUG_LOG(("Connection Info: Connecting to %1 with %2 test connections." - ).arg(_shiftedDcId - ).arg(_testConnections.size())); - - if (!_startedConnectingAt) { - _startedConnectingAt = crl::now(); - } else if (crl::now() - _startedConnectingAt > kRequestConfigTimeout) { - InvokeQueued(_instance, [instance = _instance] { - instance->requestConfigIfOld(); - }); - } - - _retryTimer.cancel(); - _waitForConnectedTimer.cancel(); - - setState(ConnectingState); - _pingId = _pingMsgId = _pingIdToSend = _pingSendAt = 0; - _pingSender.cancel(); - - _waitForConnectedTimer.callOnce(_waitForConnected); -} - -void ConnectionPrivate::restart() { - QReadLocker lockFinished(&sessionDataMutex); - if (!sessionData) return; - - DEBUG_LOG(("MTP Info: restarting Connection")); - - _waitForReceivedTimer.cancel(); - _waitForConnectedTimer.cancel(); - - auto key = sessionData->getKey(); - if (key) { - if (!sessionData->isCheckedKey()) { - // No destroying in case of an error. - // - //if (mayBeBadKey) { - // clearMessages(); - // keyId = kRecreateKeyId; -// retryTimeout = 1; // no ddos please - // LOG(("MTP Info: key may be bad and was not checked - but won't be destroyed, no log outs because of bad server right now...")); - //} - } else { - sessionData->setCheckedKey(false); - } - } - - lockFinished.unlock(); - doDisconnect(); - - lockFinished.relock(); - if (sessionData && _needSessionReset) { - resetSession(); - } - restarted = true; - if (_retryTimer.isActive()) return; - - DEBUG_LOG(("MTP Info: restart timeout: %1ms").arg(_retryTimeout)); - setState(-_retryTimeout); -} - -void ConnectionPrivate::onSentSome(uint64 size) { - if (!_waitForReceivedTimer.isActive()) { - auto remain = static_cast(_waitForReceived); - if (!_oldConnection) { - // 8kb / sec, so 512 kb give 64 sec - auto remainBySize = size * _waitForReceived / 8192; - remain = snap(remainBySize, remain, uint64(kMaxReceiveTimeout)); - if (remain != _waitForReceived) { - DEBUG_LOG(("Checking connect for request with size %1 bytes, delay will be %2").arg(size).arg(remain)); - } - } - if (isUploadDcId(_shiftedDcId)) { - remain *= cNetUploadSessionsCount(); - } else if (isDownloadDcId(_shiftedDcId)) { - remain *= cNetDownloadSessionsCount(); - } - _waitForReceivedTimer.callOnce(remain); - } - if (!firstSentAt) firstSentAt = crl::now(); -} - -void ConnectionPrivate::onReceivedSome() { - if (_oldConnection) { - _oldConnection = false; - DEBUG_LOG(("This connection marked as not old!")); - } - _oldConnectionTimer.callOnce(kMarkConnectionOldTimeout); - _waitForReceivedTimer.cancel(); - if (firstSentAt > 0) { - const auto ms = crl::now() - firstSentAt; - DEBUG_LOG(("MTP Info: response in %1ms, _waitForReceived: %2ms").arg(ms).arg(_waitForReceived)); - - if (ms > 0 && ms * 2 < _waitForReceived) { - _waitForReceived = qMax(ms * 2, kMinReceiveTimeout); - } - firstSentAt = -1; - } -} - -void ConnectionPrivate::markConnectionOld() { - _oldConnection = true; - _waitForReceived = kMinReceiveTimeout; - DEBUG_LOG(("This connection marked as old! _waitForReceived now %1ms").arg(_waitForReceived)); -} - -void ConnectionPrivate::sendPingByTimer() { - if (_pingId) { - // _pingSendAt: when to send next ping (lastPingAt + kPingSendAfter) - // could be equal to zero. - const auto now = crl::now(); - const auto mustSendTill = _pingSendAt - + kPingSendAfterForce - - kPingSendAfter; - if (mustSendTill < now + 1000) { - LOG(("Could not send ping for some seconds, restarting...")); - return restart(); - } else { - _pingSender.callOnce(mustSendTill - now); - } - } else { - emit needToSendAsync(); - } -} - -void ConnectionPrivate::onPingSendForce() { - if (!_pingId) { - _pingSendAt = 0; - DEBUG_LOG(("Will send ping!")); - tryToSend(); - } -} - -void ConnectionPrivate::waitReceivedFailed() { - Expects(_connectionOptions != nullptr); - - if (!_connectionOptions->useTcp) { - return; - } - - DEBUG_LOG(("MTP Info: bad connection, _waitForReceived: %1ms").arg(_waitForReceived)); - if (_waitForReceived < kMaxReceiveTimeout) { - _waitForReceived *= 2; - } - doDisconnect(); - restarted = true; - if (_retryTimer.isActive()) { - return; - } - - DEBUG_LOG(("MTP Info: immediate restart!")); - InvokeQueued(this, [=] { connectToServer(); }); -} - -void ConnectionPrivate::waitConnectedFailed() { - DEBUG_LOG(("MTP Info: can't connect in %1ms").arg(_waitForConnected)); - auto maxTimeout = kMaxConnectedTimeout; - for (const auto &connection : _testConnections) { - accumulate_max(maxTimeout, connection.data->fullConnectTimeout()); - } - if (_waitForConnected < maxTimeout) { - _waitForConnected = std::min(maxTimeout, 2 * _waitForConnected); - } - - connectingTimedOut(); - restarted = true; - - DEBUG_LOG(("MTP Info: immediate restart!")); - InvokeQueued(this, [=] { connectToServer(); }); -} - -void ConnectionPrivate::waitBetterFailed() { - confirmBestConnection(); -} - -void ConnectionPrivate::connectingTimedOut() { - for (const auto &connection : _testConnections) { - connection.data->timedOut(); - } - doDisconnect(); -} - -void ConnectionPrivate::doDisconnect() { - destroyAllConnections(); - - { - QReadLocker lockFinished(&sessionDataMutex); - if (sessionData) { - unlockKey(); - } - } - - clearAuthKeyData(); - - setState(DisconnectedState); - restarted = false; -} - -void ConnectionPrivate::finishAndDestroy() { - doDisconnect(); - _finished = true; - emit finished(_owner); - deleteLater(); -} - -void ConnectionPrivate::requestCDNConfig() { - connect( - _instance, - SIGNAL(cdnConfigLoaded()), - this, - SLOT(onCDNConfigLoaded()), - Qt::UniqueConnection); - InvokeQueued(_instance, [instance = _instance] { - instance->requestCDNConfig(); - }); -} - -void ConnectionPrivate::handleReceived() { - QReadLocker lockFinished(&sessionDataMutex); - if (!sessionData) return; - - onReceivedSome(); - - auto restartOnError = [this, &lockFinished] { - lockFinished.unlock(); - restart(); - }; - - ReadLockerAttempt lock(sessionData->keyMutex()); - if (!lock) { - DEBUG_LOG(("MTP Error: auth_key for dc %1 busy, cant lock").arg(_shiftedDcId)); - clearMessages(); - keyId = 0; - - return restartOnError(); - } - - auto key = sessionData->getKey(); - if (!key || key->keyId() != keyId) { - DEBUG_LOG(("MTP Error: auth_key id for dc %1 changed").arg(_shiftedDcId)); - return restartOnError(); - } - - while (!_connection->received().empty()) { - auto intsBuffer = std::move(_connection->received().front()); - _connection->received().pop_front(); - - constexpr auto kExternalHeaderIntsCount = 6U; // 2 auth_key_id, 4 msg_key - constexpr auto kEncryptedHeaderIntsCount = 8U; // 2 salt, 2 session, 2 msg_id, 1 seq_no, 1 length - constexpr auto kMinimalEncryptedIntsCount = kEncryptedHeaderIntsCount + 4U; // + 1 data + 3 padding - constexpr auto kMinimalIntsCount = kExternalHeaderIntsCount + kMinimalEncryptedIntsCount; - auto intsCount = uint32(intsBuffer.size()); - auto ints = intsBuffer.constData(); - if ((intsCount < kMinimalIntsCount) || (intsCount > kMaxMessageLength / kIntSize)) { - LOG(("TCP Error: bad message received, len %1").arg(intsCount * kIntSize)); - TCP_LOG(("TCP Error: bad message %1").arg(Logs::mb(ints, intsCount * kIntSize).str())); - - return restartOnError(); - } - if (keyId != *(uint64*)ints) { - LOG(("TCP Error: bad auth_key_id %1 instead of %2 received").arg(keyId).arg(*(uint64*)ints)); - TCP_LOG(("TCP Error: bad message %1").arg(Logs::mb(ints, intsCount * kIntSize).str())); - - return restartOnError(); - } - - auto encryptedInts = ints + kExternalHeaderIntsCount; - auto encryptedIntsCount = (intsCount - kExternalHeaderIntsCount) & ~0x03U; - auto encryptedBytesCount = encryptedIntsCount * kIntSize; - auto decryptedBuffer = QByteArray(encryptedBytesCount, Qt::Uninitialized); - auto msgKey = *(MTPint128*)(ints + 2); - -#ifdef TDESKTOP_MTPROTO_OLD - aesIgeDecrypt_oldmtp(encryptedInts, decryptedBuffer.data(), encryptedBytesCount, key, msgKey); -#else // TDESKTOP_MTPROTO_OLD - aesIgeDecrypt(encryptedInts, decryptedBuffer.data(), encryptedBytesCount, key, msgKey); -#endif // TDESKTOP_MTPROTO_OLD - - auto decryptedInts = reinterpret_cast(decryptedBuffer.constData()); - auto serverSalt = *(uint64*)&decryptedInts[0]; - auto session = *(uint64*)&decryptedInts[2]; - auto msgId = *(uint64*)&decryptedInts[4]; - auto seqNo = *(uint32*)&decryptedInts[6]; - auto needAck = ((seqNo & 0x01) != 0); - - auto messageLength = *(uint32*)&decryptedInts[7]; - if (messageLength > kMaxMessageLength) { - LOG(("TCP Error: bad messageLength %1").arg(messageLength)); - TCP_LOG(("TCP Error: bad message %1").arg(Logs::mb(ints, intsCount * kIntSize).str())); - - return restartOnError(); - - } - auto fullDataLength = kEncryptedHeaderIntsCount * kIntSize + messageLength; // Without padding. - - // Can underflow, but it is an unsigned type, so we just check the range later. - auto paddingSize = static_cast(encryptedBytesCount) - static_cast(fullDataLength); - -#ifdef TDESKTOP_MTPROTO_OLD - constexpr auto kMinPaddingSize_oldmtp = 0U; - constexpr auto kMaxPaddingSize_oldmtp = 15U; - auto badMessageLength = (/*paddingSize < kMinPaddingSize_oldmtp || */paddingSize > kMaxPaddingSize_oldmtp); - - auto hashedDataLength = badMessageLength ? encryptedBytesCount : fullDataLength; - auto sha1ForMsgKeyCheck = hashSha1(decryptedInts, hashedDataLength); - - constexpr auto kMsgKeyShift_oldmtp = 4U; - if (memcmp(&msgKey, sha1ForMsgKeyCheck.data() + kMsgKeyShift_oldmtp, sizeof(msgKey)) != 0) { - LOG(("TCP Error: bad SHA1 hash after aesDecrypt in message.")); - TCP_LOG(("TCP Error: bad message %1").arg(Logs::mb(encryptedInts, encryptedBytesCount).str())); - - return restartOnError(); - } -#else // TDESKTOP_MTPROTO_OLD - constexpr auto kMinPaddingSize = 12U; - constexpr auto kMaxPaddingSize = 1024U; - auto badMessageLength = (paddingSize < kMinPaddingSize || paddingSize > kMaxPaddingSize); - - std::array sha256Buffer = { { 0 } }; - - SHA256_CTX msgKeyLargeContext; - SHA256_Init(&msgKeyLargeContext); - SHA256_Update(&msgKeyLargeContext, key->partForMsgKey(false), 32); - SHA256_Update(&msgKeyLargeContext, decryptedInts, encryptedBytesCount); - SHA256_Final(sha256Buffer.data(), &msgKeyLargeContext); - - constexpr auto kMsgKeyShift = 8U; - if (memcmp(&msgKey, sha256Buffer.data() + kMsgKeyShift, sizeof(msgKey)) != 0) { - LOG(("TCP Error: bad SHA256 hash after aesDecrypt in message")); - TCP_LOG(("TCP Error: bad message %1").arg(Logs::mb(encryptedInts, encryptedBytesCount).str())); - - return restartOnError(); - } -#endif // TDESKTOP_MTPROTO_OLD - - if (badMessageLength || (messageLength & 0x03)) { - LOG(("TCP Error: bad msg_len received %1, data size: %2").arg(messageLength).arg(encryptedBytesCount)); - TCP_LOG(("TCP Error: bad message %1").arg(Logs::mb(encryptedInts, encryptedBytesCount).str())); - - return restartOnError(); - } - - TCP_LOG(("TCP Info: decrypted message %1,%2,%3 is %4 len").arg(msgId).arg(seqNo).arg(Logs::b(needAck)).arg(fullDataLength)); - - uint64 serverSession = sessionData->getSession(); - if (session != serverSession) { - LOG(("MTP Error: bad server session received")); - TCP_LOG(("MTP Error: bad server session %1 instead of %2 in message received").arg(session).arg(serverSession)); - - return restartOnError(); - } - - const auto serverTime = int32(msgId >> 32); - const auto clientTime = base::unixtime::now(); - const auto isReply = ((msgId & 0x03) == 1); - if (!isReply && ((msgId & 0x03) != 3)) { - LOG(("MTP Error: bad msg_id %1 in message received").arg(msgId)); - - return restartOnError(); - } - - bool badTime = false; - uint64 mySalt = sessionData->getSalt(); - if (serverTime > clientTime + 60 || serverTime + 300 < clientTime) { - DEBUG_LOG(("MTP Info: bad server time from msg_id: %1, my time: %2").arg(serverTime).arg(clientTime)); - badTime = true; - } - - bool wasConnected = (getState() == ConnectedState); - if (serverSalt != mySalt) { - if (!badTime) { - DEBUG_LOG(("MTP Info: other salt received... received: %1, my salt: %2, updating...").arg(serverSalt).arg(mySalt)); - sessionData->setSalt(serverSalt); - if (setState(ConnectedState, ConnectingState)) { // only connected - if (restarted) { - emit resendAllAsync(); - restarted = false; - } - } - } else { - DEBUG_LOG(("MTP Info: other salt received... received: %1, my salt: %2").arg(serverSalt).arg(mySalt)); - } - } else { - serverSalt = 0; // dont pass to handle method, so not to lock in setSalt() - } - - if (needAck) ackRequestData.push_back(MTP_long(msgId)); - - auto res = HandleResult::Success; // if no need to handle, then succeed - auto from = decryptedInts + kEncryptedHeaderIntsCount; - auto end = from + (messageLength / kIntSize); - auto sfrom = decryptedInts + 4U; // msg_id + seq_no + length + message - MTP_LOG(_shiftedDcId, ("Recv: ") + mtpTextSerialize(sfrom, end)); - - bool needToHandle = false; - { - QWriteLocker lock(sessionData->receivedIdsMutex()); - needToHandle = sessionData->receivedIdsSet().registerMsgId(msgId, needAck); - } - if (needToHandle) { - res = handleOneReceived(from, end, msgId, serverTime, serverSalt, badTime); - } - { - QWriteLocker lock(sessionData->receivedIdsMutex()); - sessionData->receivedIdsSet().shrink(); - } - - // send acks - uint32 toAckSize = ackRequestData.size(); - if (toAckSize) { - DEBUG_LOG(("MTP Info: will send %1 acks, ids: %2").arg(toAckSize).arg(LogIdsVector(ackRequestData))); - emit sendAnythingAsync(kAckSendWaiting); - } - - bool emitSignal = false; - { - QReadLocker locker(sessionData->haveReceivedMutex()); - emitSignal = !sessionData->haveReceivedResponses().isEmpty() || !sessionData->haveReceivedUpdates().isEmpty(); - if (emitSignal) { - DEBUG_LOG(("MTP Info: emitting needToReceive() - need to parse in another thread, %1 responses, %2 updates.").arg(sessionData->haveReceivedResponses().size()).arg(sessionData->haveReceivedUpdates().size())); - } - } - - if (emitSignal) { - emit needToReceive(); - } - - if (res != HandleResult::Success && res != HandleResult::Ignored) { - _needSessionReset = (res == HandleResult::ResetSession); - - return restartOnError(); - } - _retryTimeout = 1; // reset restart() timer - - if (!sessionData->isCheckedKey()) { - DEBUG_LOG(("MTP Info: marked auth key as checked")); - sessionData->setCheckedKey(true); - } - _startedConnectingAt = crl::time(0); - - if (!wasConnected) { - if (getState() == ConnectedState) { - emit needToSendAsync(); - } - } - } - if (_connection->needHttpWait()) { - emit sendHttpWaitAsync(); - } -} - -ConnectionPrivate::HandleResult ConnectionPrivate::handleOneReceived(const mtpPrime *from, const mtpPrime *end, uint64 msgId, int32 serverTime, uint64 serverSalt, bool badTime) { - const auto cons = mtpTypeId(*from); - - switch (cons) { - - case mtpc_gzip_packed: { - DEBUG_LOG(("Message Info: gzip container")); - mtpBuffer response = ungzip(++from, end); - if (response.empty()) { - return HandleResult::RestartConnection; - } - return handleOneReceived(response.data(), response.data() + response.size(), msgId, serverTime, serverSalt, badTime); - } - - case mtpc_msg_container: { - if (++from >= end) { - return HandleResult::ParseError; - } - - const mtpPrime *otherEnd; - const auto msgsCount = (uint32)*(from++); - DEBUG_LOG(("Message Info: container received, count: %1").arg(msgsCount)); - for (uint32 i = 0; i < msgsCount; ++i) { - if (from + 4 >= end) { - return HandleResult::ParseError; - } - otherEnd = from + 4; - - MTPlong inMsgId; - if (!inMsgId.read(from, otherEnd)) { - return HandleResult::ParseError; - } - bool isReply = ((inMsgId.v & 0x03) == 1); - if (!isReply && ((inMsgId.v & 0x03) != 3)) { - LOG(("Message Error: bad msg_id %1 in contained message received").arg(inMsgId.v)); - return HandleResult::RestartConnection; - } - - MTPint inSeqNo; - if (!inSeqNo.read(from, otherEnd)) { - return HandleResult::ParseError; - } - MTPint bytes; - if (!bytes.read(from, otherEnd)) { - return HandleResult::ParseError; - } - if ((bytes.v & 0x03) || bytes.v < 4) { - LOG(("Message Error: bad length %1 of contained message received").arg(bytes.v)); - return HandleResult::RestartConnection; - } - - bool needAck = (inSeqNo.v & 0x01); - if (needAck) ackRequestData.push_back(inMsgId); - - DEBUG_LOG(("Message Info: message from container, msg_id: %1, needAck: %2").arg(inMsgId.v).arg(Logs::b(needAck))); - - otherEnd = from + (bytes.v >> 2); - if (otherEnd > end) { - return HandleResult::ParseError; - } - - bool needToHandle = false; - { - QWriteLocker lock(sessionData->receivedIdsMutex()); - needToHandle = sessionData->receivedIdsSet().registerMsgId(inMsgId.v, needAck); - } - auto res = HandleResult::Success; // if no need to handle, then succeed - if (needToHandle) { - res = handleOneReceived(from, otherEnd, inMsgId.v, serverTime, serverSalt, badTime); - badTime = false; - } - if (res != HandleResult::Success) { - return res; - } - - from = otherEnd; - } - } return HandleResult::Success; - - case mtpc_msgs_ack: { - MTPMsgsAck msg; - if (!msg.read(from, end)) { - return HandleResult::ParseError; - } - auto &ids = msg.c_msgs_ack().vmsg_ids().v; - uint32 idsCount = ids.size(); - - DEBUG_LOG(("Message Info: acks received, ids: %1").arg(LogIdsVector(ids))); - if (!idsCount) return (badTime ? HandleResult::Ignored : HandleResult::Success); - - if (badTime) { - if (requestsFixTimeSalt(ids, serverTime, serverSalt)) { - badTime = false; - } else { - return HandleResult::Ignored; - } - } - requestsAcked(ids); - } return HandleResult::Success; - - case mtpc_bad_msg_notification: { - MTPBadMsgNotification msg; - if (!msg.read(from, end)) { - return HandleResult::ParseError; - } - const auto &data(msg.c_bad_msg_notification()); - LOG(("Message Info: bad message notification received (error_code %3) for msg_id = %1, seq_no = %2").arg(data.vbad_msg_id().v).arg(data.vbad_msg_seqno().v).arg(data.verror_code().v)); - - mtpMsgId resendId = data.vbad_msg_id().v; - if (resendId == _pingMsgId) { - _pingId = 0; - } - int32 errorCode = data.verror_code().v; - if (false - || errorCode == 16 - || errorCode == 17 - || errorCode == 32 - || errorCode == 33 - || errorCode == 64) { // can handle - const auto needResend = false - || (errorCode == 16) // bad msg_id - || (errorCode == 17) // bad msg_id - || (errorCode == 64); // bad container - if (errorCode == 64) { // bad container! - if (Logs::DebugEnabled()) { - SecureRequest request; - { - QWriteLocker locker(sessionData->haveSentMutex()); - auto &haveSent = sessionData->haveSentMap(); - - const auto i = haveSent.constFind(resendId); - if (i == haveSent.cend()) { - LOG(("Message Error: Container not found!")); - } else { - request = i.value(); - } - } - if (request) { - if (request.isSentContainer()) { - QStringList lst; - const auto ids = (const mtpMsgId*)(request->constData() + 8); - for (uint32 i = 0, l = (request->size() - 8) >> 1; i < l; ++i) { - lst.push_back(QString::number(ids[i])); - } - LOG(("Message Info: bad container received! messages: %1").arg(lst.join(','))); - } else { - LOG(("Message Error: bad container received, but request is not a container!")); - } - } - } - } - - if (!wasSent(resendId)) { - DEBUG_LOG(("Message Error: " - "such message was not sent recently %1").arg(resendId)); - return badTime - ? HandleResult::Ignored - : HandleResult::Success; - } - - if (needResend) { // bad msg_id or bad container - if (serverSalt) sessionData->setSalt(serverSalt); - base::unixtime::update(serverTime, true); - - DEBUG_LOG(("Message Info: unixtime updated, now %1, resending in container...").arg(serverTime)); - - resend(resendId, 0, true); - } else { // must create new session, because msg_id and msg_seqno are inconsistent - if (badTime) { - if (serverSalt) sessionData->setSalt(serverSalt); - base::unixtime::update(serverTime, true); - badTime = false; - } - LOG(("Message Info: bad message notification received, msgId %1, error_code %2").arg(data.vbad_msg_id().v).arg(errorCode)); - return HandleResult::ResetSession; - } - } else { // fatal (except 48, but it must not get here) - const auto badMsgId = mtpMsgId(data.vbad_msg_id().v); - const auto requestId = wasSent(resendId); - if (requestId) { - LOG(("Message Error: " - "bad message notification received, " - "msgId %1, error_code %2, fatal: clearing callbacks" - ).arg(badMsgId - ).arg(errorCode - )); - _instance->clearCallbacksDelayed({ 1, RPCCallbackClear( - requestId, - -errorCode) }); - } else { - DEBUG_LOG(("Message Error: " - "such message was not sent recently %1").arg(badMsgId)); - } - return badTime - ? HandleResult::Ignored - : HandleResult::Success; - } - } return HandleResult::Success; - - case mtpc_bad_server_salt: { - MTPBadMsgNotification msg; - if (!msg.read(from, end)) { - return HandleResult::ParseError; - } - const auto &data(msg.c_bad_server_salt()); - DEBUG_LOG(("Message Info: bad server salt received (error_code %4) for msg_id = %1, seq_no = %2, new salt: %3").arg(data.vbad_msg_id().v).arg(data.vbad_msg_seqno().v).arg(data.vnew_server_salt().v).arg(data.verror_code().v)); - - mtpMsgId resendId = data.vbad_msg_id().v; - if (resendId == _pingMsgId) { - _pingId = 0; - } else if (!wasSent(resendId)) { - DEBUG_LOG(("Message Error: such message was not sent recently %1").arg(resendId)); - return (badTime ? HandleResult::Ignored : HandleResult::Success); - } - - uint64 serverSalt = data.vnew_server_salt().v; - sessionData->setSalt(serverSalt); - base::unixtime::update(serverTime); - - if (setState(ConnectedState, ConnectingState)) { // maybe only connected - if (restarted) { - emit resendAllAsync(); - restarted = false; - } - } - - badTime = false; - - DEBUG_LOG(("Message Info: unixtime updated, now %1, server_salt updated, now %2, resending...").arg(serverTime).arg(serverSalt)); - resend(resendId); - } return HandleResult::Success; - - case mtpc_msgs_state_req: { - if (badTime) { - DEBUG_LOG(("Message Info: skipping with bad time...")); - return HandleResult::Ignored; - } - MTPMsgsStateReq msg; - if (!msg.read(from, end)) { - return HandleResult::ParseError; - } - auto &ids = msg.c_msgs_state_req().vmsg_ids().v; - auto idsCount = ids.size(); - DEBUG_LOG(("Message Info: msgs_state_req received, ids: %1").arg(LogIdsVector(ids))); - if (!idsCount) return HandleResult::Success; - - QByteArray info(idsCount, Qt::Uninitialized); - { - QReadLocker lock(sessionData->receivedIdsMutex()); - auto &receivedIds = sessionData->receivedIdsSet(); - auto minRecv = receivedIds.min(); - auto maxRecv = receivedIds.max(); - - QReadLocker locker(sessionData->wereAckedMutex()); - const auto &wereAcked = sessionData->wereAckedMap(); - const auto wereAckedEnd = wereAcked.cend(); - - for (uint32 i = 0, l = idsCount; i < l; ++i) { - char state = 0; - uint64 reqMsgId = ids[i].v; - if (reqMsgId < minRecv) { - state |= 0x01; - } else if (reqMsgId > maxRecv) { - state |= 0x03; - } else { - auto msgIdState = receivedIds.lookup(reqMsgId); - if (msgIdState == ReceivedMsgIds::State::NotFound) { - state |= 0x02; - } else { - state |= 0x04; - if (wereAcked.constFind(reqMsgId) != wereAckedEnd) { - state |= 0x80; // we know, that server knows, that we received request - } - if (msgIdState == ReceivedMsgIds::State::NeedsAck) { // need ack, so we sent ack - state |= 0x08; - } else { - state |= 0x10; - } - } - } - info[i] = state; - } - } - emit sendMsgsStateInfoAsync(msgId, info); - } return HandleResult::Success; - - case mtpc_msgs_state_info: { - MTPMsgsStateInfo msg; - if (!msg.read(from, end)) { - return HandleResult::ParseError; - } - auto &data = msg.c_msgs_state_info(); - - auto reqMsgId = data.vreq_msg_id().v; - auto &states = data.vinfo().v; - - DEBUG_LOG(("Message Info: msg state received, msgId %1, reqMsgId: %2, HEX states %3").arg(msgId).arg(reqMsgId).arg(Logs::mb(states.data(), states.length()).str())); - SecureRequest requestBuffer; - { // find this request in session-shared sent requests map - QReadLocker locker(sessionData->haveSentMutex()); - const auto &haveSent = sessionData->haveSentMap(); - const auto replyTo = haveSent.constFind(reqMsgId); - if (replyTo == haveSent.cend()) { // do not look in toResend, because we do not resend msgs_state_req requests - DEBUG_LOG(("Message Error: such message was not sent recently %1").arg(reqMsgId)); - return (badTime ? HandleResult::Ignored : HandleResult::Success); - } - if (badTime) { - if (serverSalt) sessionData->setSalt(serverSalt); // requestsFixTimeSalt with no lookup - base::unixtime::update(serverTime, true); - - DEBUG_LOG(("Message Info: unixtime updated from mtpc_msgs_state_info, now %1").arg(serverTime)); - - badTime = false; - } - requestBuffer = replyTo.value(); - } - QVector toAckReq(1, MTP_long(reqMsgId)), toAck; - requestsAcked(toAck, true); - - if (requestBuffer->size() < 9) { - LOG(("Message Error: bad request %1 found in requestMap, size: %2").arg(reqMsgId).arg(requestBuffer->size())); - return HandleResult::RestartConnection; - } - const mtpPrime *rFrom = requestBuffer->constData() + 8, *rEnd = requestBuffer->constData() + requestBuffer->size(); - if (mtpTypeId(*rFrom) == mtpc_msgs_state_req) { - MTPMsgsStateReq request; - if (!request.read(rFrom, rEnd)) { - LOG(("Message Error: could not parse sent msgs_state_req")); - return HandleResult::ParseError; - } - handleMsgsStates(request.c_msgs_state_req().vmsg_ids().v, states, toAck); - } else { - MTPMsgResendReq request; - if (!request.read(rFrom, rEnd)) { - LOG(("Message Error: could not parse sent msgs_state_req")); - return HandleResult::ParseError; - } - handleMsgsStates(request.c_msg_resend_req().vmsg_ids().v, states, toAck); - } - - requestsAcked(toAck); - } return HandleResult::Success; - - case mtpc_msgs_all_info: { - if (badTime) { - DEBUG_LOG(("Message Info: skipping with bad time...")); - return HandleResult::Ignored; - } - - MTPMsgsAllInfo msg; - if (!msg.read(from, end)) { - return HandleResult::ParseError; - } - auto &data = msg.c_msgs_all_info(); - auto &ids = data.vmsg_ids().v; - auto &states = data.vinfo().v; - - QVector toAck; - - DEBUG_LOG(("Message Info: msgs all info received, msgId %1, reqMsgIds: %2, states %3").arg(msgId).arg(LogIdsVector(ids)).arg(Logs::mb(states.data(), states.length()).str())); - handleMsgsStates(ids, states, toAck); - - requestsAcked(toAck); - } return HandleResult::Success; - - case mtpc_msg_detailed_info: { - MTPMsgDetailedInfo msg; - if (!msg.read(from, end)) { - return HandleResult::ParseError; - } - const auto &data(msg.c_msg_detailed_info()); - - DEBUG_LOG(("Message Info: msg detailed info, sent msgId %1, answerId %2, status %3, bytes %4").arg(data.vmsg_id().v).arg(data.vanswer_msg_id().v).arg(data.vstatus().v).arg(data.vbytes().v)); - - QVector ids(1, data.vmsg_id()); - if (badTime) { - if (requestsFixTimeSalt(ids, serverTime, serverSalt)) { - badTime = false; - } else { - DEBUG_LOG(("Message Info: error, such message was not sent recently %1").arg(data.vmsg_id().v)); - return HandleResult::Ignored; - } - } - requestsAcked(ids); - - bool received = false; - MTPlong resMsgId = data.vanswer_msg_id(); - { - QReadLocker lock(sessionData->receivedIdsMutex()); - received = (sessionData->receivedIdsSet().lookup(resMsgId.v) != ReceivedMsgIds::State::NotFound); - } - if (received) { - ackRequestData.push_back(resMsgId); - } else { - DEBUG_LOG(("Message Info: answer message %1 was not received, requesting...").arg(resMsgId.v)); - resendRequestData.push_back(resMsgId); - } - } return HandleResult::Success; - - case mtpc_msg_new_detailed_info: { - if (badTime) { - DEBUG_LOG(("Message Info: skipping msg_new_detailed_info with bad time...")); - return HandleResult::Ignored; - } - MTPMsgDetailedInfo msg; - if (!msg.read(from, end)) { - return HandleResult::ParseError; - } - const auto &data(msg.c_msg_new_detailed_info()); - - DEBUG_LOG(("Message Info: msg new detailed info, answerId %2, status %3, bytes %4").arg(data.vanswer_msg_id().v).arg(data.vstatus().v).arg(data.vbytes().v)); - - bool received = false; - MTPlong resMsgId = data.vanswer_msg_id(); - { - QReadLocker lock(sessionData->receivedIdsMutex()); - received = (sessionData->receivedIdsSet().lookup(resMsgId.v) != ReceivedMsgIds::State::NotFound); - } - if (received) { - ackRequestData.push_back(resMsgId); - } else { - DEBUG_LOG(("Message Info: answer message %1 was not received, requesting...").arg(resMsgId.v)); - resendRequestData.push_back(resMsgId); - } - } return HandleResult::Success; - - case mtpc_msg_resend_req: { - MTPMsgResendReq msg; - if (!msg.read(from, end)) { - return HandleResult::ParseError; - } - auto &ids = msg.c_msg_resend_req().vmsg_ids().v; - - auto idsCount = ids.size(); - DEBUG_LOG(("Message Info: resend of msgs requested, ids: %1").arg(LogIdsVector(ids))); - if (!idsCount) return (badTime ? HandleResult::Ignored : HandleResult::Success); - - QVector toResend(ids.size()); - for (int32 i = 0, l = ids.size(); i < l; ++i) { - toResend[i] = ids.at(i).v; - } - resendMany(toResend, 0, false, true); - } return HandleResult::Success; - - case mtpc_rpc_result: { - if (from + 3 > end) { - return HandleResult::ParseError; - } - auto response = SerializedMessage(); - - MTPlong reqMsgId; - if (!reqMsgId.read(++from, end)) { - return HandleResult::ParseError; - } - mtpTypeId typeId = from[0]; - - DEBUG_LOG(("RPC Info: response received for %1, queueing...").arg(reqMsgId.v)); - - QVector ids(1, reqMsgId); - if (badTime) { - if (requestsFixTimeSalt(ids, serverTime, serverSalt)) { - badTime = false; - } else { - DEBUG_LOG(("Message Info: error, such message was not sent recently %1").arg(reqMsgId.v)); - return HandleResult::Ignored; - } - } - requestsAcked(ids, true); - - if (typeId == mtpc_gzip_packed) { - DEBUG_LOG(("RPC Info: gzip container")); - response = ungzip(++from, end); - if (response.empty()) { - return HandleResult::RestartConnection; - } - typeId = response[0]; - } else { - response.resize(end - from); - memcpy(response.data(), from, (end - from) * sizeof(mtpPrime)); - } - if (typeId != mtpc_rpc_error) { - // An error could be some RPC_CALL_FAIL or other error inside - // the initConnection, so we're not sure yet that it was inited. - // Wait till a good response is received. - if (!_connectionOptions->inited) { - _connectionOptions->inited = true; - sessionData->notifyConnectionInited(*_connectionOptions); - } - } - - auto requestId = wasSent(reqMsgId.v); - if (requestId && requestId != mtpRequestId(0xFFFFFFFF)) { - // Save rpc_result for processing in the main thread. - QWriteLocker locker(sessionData->haveReceivedMutex()); - sessionData->haveReceivedResponses().insert(requestId, response); - } else { - DEBUG_LOG(("RPC Info: requestId not found for msgId %1").arg(reqMsgId.v)); - } - } return HandleResult::Success; - - case mtpc_new_session_created: { - const mtpPrime *start = from; - MTPNewSession msg; - if (!msg.read(from, end)) { - return HandleResult::ParseError; - } - const auto &data(msg.c_new_session_created()); - - if (badTime) { - if (requestsFixTimeSalt(QVector(1, data.vfirst_msg_id()), serverTime, serverSalt)) { - badTime = false; - } else { - DEBUG_LOG(("Message Info: error, such message was not sent recently %1").arg(data.vfirst_msg_id().v)); - return HandleResult::Ignored; - } - } - - DEBUG_LOG(("Message Info: new server session created, unique_id %1, first_msg_id %2, server_salt %3").arg(data.vunique_id().v).arg(data.vfirst_msg_id().v).arg(data.vserver_salt().v)); - sessionData->setSalt(data.vserver_salt().v); - - mtpMsgId firstMsgId = data.vfirst_msg_id().v; - QVector toResend; - { - QReadLocker locker(sessionData->haveSentMutex()); - const auto &haveSent = sessionData->haveSentMap(); - toResend.reserve(haveSent.size()); - for (auto i = haveSent.cbegin(), e = haveSent.cend(); i != e; ++i) { - if (i.key() >= firstMsgId) break; - if (i.value()->requestId) toResend.push_back(i.key()); - } - } - resendMany(toResend, 10, true); - - mtpBuffer update(from - start); - if (from > start) memcpy(update.data(), start, (from - start) * sizeof(mtpPrime)); - - // Notify main process about new session - need to get difference. - QWriteLocker locker(sessionData->haveReceivedMutex()); - sessionData->haveReceivedUpdates().push_back(SerializedMessage(update)); - } return HandleResult::Success; - - case mtpc_pong: { - MTPPong msg; - if (!msg.read(from, end)) { - return HandleResult::ParseError; - } - const auto &data(msg.c_pong()); - DEBUG_LOG(("Message Info: pong received, msg_id: %1, ping_id: %2").arg(data.vmsg_id().v).arg(data.vping_id().v)); - - if (!wasSent(data.vmsg_id().v)) { - DEBUG_LOG(("Message Error: such msg_id %1 ping_id %2 was not sent recently").arg(data.vmsg_id().v).arg(data.vping_id().v)); - return HandleResult::Ignored; - } - if (data.vping_id().v == _pingId) { - _pingId = 0; - } else { - DEBUG_LOG(("Message Info: just pong...")); - } - - QVector ids(1, data.vmsg_id()); - if (badTime) { - if (requestsFixTimeSalt(ids, serverTime, serverSalt)) { - badTime = false; - } else { - return HandleResult::Ignored; - } - } - requestsAcked(ids, true); - } return HandleResult::Success; - - } - - if (badTime) { - DEBUG_LOG(("Message Error: bad time in updates cons, must create new session")); - return HandleResult::ResetSession; - } - - if (_dcType == DcType::Regular) { - mtpBuffer update(end - from); - if (end > from) memcpy(update.data(), from, (end - from) * sizeof(mtpPrime)); - - // Notify main process about the new updates. - QWriteLocker locker(sessionData->haveReceivedMutex()); - sessionData->haveReceivedUpdates().push_back(SerializedMessage(update)); - - if (cons != mtpc_updatesTooLong - && cons != mtpc_updateShortMessage - && cons != mtpc_updateShortChatMessage - && cons != mtpc_updateShortSentMessage - && cons != mtpc_updateShort - && cons != mtpc_updatesCombined - && cons != mtpc_updates) { - // Maybe some new unknown update? - LOG(("Message Error: unknown constructor 0x%1").arg(cons, 0, 16)); - } - } else { - LOG(("Message Error: unexpected updates in dcType: %1").arg(static_cast(_dcType))); - } - - return HandleResult::Success; -} - -mtpBuffer ConnectionPrivate::ungzip(const mtpPrime *from, const mtpPrime *end) const { - mtpBuffer result; // * 4 because of mtpPrime type - result.resize(0); - - MTPstring packed; - if (!packed.read(from, end)) { // read packed string as serialized mtp string type - LOG(("RPC Error: could not read gziped bytes.")); - return result; - } - uint32 packedLen = packed.v.size(), unpackedChunk = packedLen, unpackedLen = 0; - - z_stream stream; - stream.zalloc = 0; - stream.zfree = 0; - stream.opaque = 0; - stream.avail_in = 0; - stream.next_in = 0; - int res = inflateInit2(&stream, 16 + MAX_WBITS); - if (res != Z_OK) { - LOG(("RPC Error: could not init zlib stream, code: %1").arg(res)); - return result; - } - stream.avail_in = packedLen; - stream.next_in = reinterpret_cast(packed.v.data()); - - stream.avail_out = 0; - while (!stream.avail_out) { - result.resize(result.size() + unpackedChunk); - stream.avail_out = unpackedChunk * sizeof(mtpPrime); - stream.next_out = (Bytef*)&result[result.size() - unpackedChunk]; - int res = inflate(&stream, Z_NO_FLUSH); - if (res != Z_OK && res != Z_STREAM_END) { - inflateEnd(&stream); - LOG(("RPC Error: could not unpack gziped data, code: %1").arg(res)); - DEBUG_LOG(("RPC Error: bad gzip: %1").arg(Logs::mb(packed.v.constData(), packedLen).str())); - return mtpBuffer(); - } - } - if (stream.avail_out & 0x03) { - uint32 badSize = result.size() * sizeof(mtpPrime) - stream.avail_out; - LOG(("RPC Error: bad length of unpacked data %1").arg(badSize)); - DEBUG_LOG(("RPC Error: bad unpacked data %1").arg(Logs::mb(result.data(), badSize).str())); - return mtpBuffer(); - } - result.resize(result.size() - (stream.avail_out >> 2)); - inflateEnd(&stream); - if (!result.size()) { - LOG(("RPC Error: bad length of unpacked data 0")); - } - return result; -} - -bool ConnectionPrivate::requestsFixTimeSalt(const QVector &ids, int32 serverTime, uint64 serverSalt) { - uint32 idsCount = ids.size(); - - for (uint32 i = 0; i < idsCount; ++i) { - if (wasSent(ids[i].v)) {// found such msg_id in recent acked requests or in recent sent requests - if (serverSalt) sessionData->setSalt(serverSalt); - base::unixtime::update(serverTime, true); - return true; - } - } - return false; -} - -void ConnectionPrivate::requestsAcked(const QVector &ids, bool byResponse) { - uint32 idsCount = ids.size(); - - DEBUG_LOG(("Message Info: requests acked, ids %1").arg(LogIdsVector(ids))); - - auto clearedBecauseTooOld = std::vector(); - QVector toAckMore; - { - QWriteLocker locker1(sessionData->wereAckedMutex()); - auto &wereAcked = sessionData->wereAckedMap(); - - { - QWriteLocker locker2(sessionData->haveSentMutex()); - auto &haveSent = sessionData->haveSentMap(); - - for (uint32 i = 0; i < idsCount; ++i) { - mtpMsgId msgId = ids[i].v; - const auto req = haveSent.find(msgId); - if (req != haveSent.cend()) { - if (!req.value()->msDate) { - DEBUG_LOG(("Message Info: container ack received, msgId %1").arg(ids[i].v)); - uint32 inContCount = ((*req)->size() - 8) / 2; - const mtpMsgId *inContId = (const mtpMsgId *)(req.value()->constData() + 8); - toAckMore.reserve(toAckMore.size() + inContCount); - for (uint32 j = 0; j < inContCount; ++j) { - toAckMore.push_back(MTP_long(*(inContId++))); - } - haveSent.erase(req); - } else { - mtpRequestId reqId = req.value()->requestId; - bool moveToAcked = byResponse; - if (!moveToAcked) { // ignore ACK, if we need a response (if we have a handler) - moveToAcked = !_instance->hasCallbacks(reqId); - } - if (moveToAcked) { - wereAcked.insert(msgId, reqId); - haveSent.erase(req); - } else { - DEBUG_LOG(("Message Info: ignoring ACK for msgId %1 because request %2 requires a response").arg(msgId).arg(reqId)); - } - } - } else { - DEBUG_LOG(("Message Info: msgId %1 was not found in recent sent, while acking requests, searching in resend...").arg(msgId)); - QWriteLocker locker3(sessionData->toResendMutex()); - auto &toResend = sessionData->toResendMap(); - const auto reqIt = toResend.find(msgId); - if (reqIt != toResend.cend()) { - const auto reqId = reqIt.value(); - bool moveToAcked = byResponse; - if (!moveToAcked) { // ignore ACK, if we need a response (if we have a handler) - moveToAcked = !_instance->hasCallbacks(reqId); - } - if (moveToAcked) { - QWriteLocker locker4(sessionData->toSendMutex()); - auto &toSend = sessionData->toSendMap(); - const auto req = toSend.find(reqId); - if (req != toSend.cend()) { - wereAcked.insert(msgId, req.value()->requestId); - if (req.value()->requestId != reqId) { - DEBUG_LOG(("Message Error: for msgId %1 found resent request, requestId %2, contains requestId %3").arg(msgId).arg(reqId).arg(req.value()->requestId)); - } else { - DEBUG_LOG(("Message Info: acked msgId %1 that was prepared to resend, requestId %2").arg(msgId).arg(reqId)); - } - toSend.erase(req); - } else { - DEBUG_LOG(("Message Info: msgId %1 was found in recent resent, requestId %2 was not found in prepared to send").arg(msgId)); - } - toResend.erase(reqIt); - } else { - DEBUG_LOG(("Message Info: ignoring ACK for msgId %1 because request %2 requires a response").arg(msgId).arg(reqId)); - } - } else { - DEBUG_LOG(("Message Info: msgId %1 was not found in recent resent either").arg(msgId)); - } - } - } - } - - uint32 ackedCount = wereAcked.size(); - if (ackedCount > kIdsBufferSize) { - DEBUG_LOG(("Message Info: removing some old acked sent msgIds %1").arg(ackedCount - kIdsBufferSize)); - clearedBecauseTooOld.reserve(ackedCount - kIdsBufferSize); - while (ackedCount-- > kIdsBufferSize) { - auto i = wereAcked.begin(); - clearedBecauseTooOld.push_back(RPCCallbackClear( - i.value(), - RPCError::TimeoutError)); - wereAcked.erase(i); - } - } - } - - if (!clearedBecauseTooOld.empty()) { - _instance->clearCallbacksDelayed(std::move(clearedBecauseTooOld)); - } - - if (toAckMore.size()) { - requestsAcked(toAckMore); - } -} - -void ConnectionPrivate::handleMsgsStates(const QVector &ids, const QByteArray &states, QVector &acked) { - uint32 idsCount = ids.size(); - if (!idsCount) { - DEBUG_LOG(("Message Info: void ids vector in handleMsgsStates()")); - return; - } - if (states.size() < idsCount) { - LOG(("Message Error: got less states than required ids count.")); - return; - } - - acked.reserve(acked.size() + idsCount); - for (uint32 i = 0, count = idsCount; i < count; ++i) { - char state = states[i]; - uint64 requestMsgId = ids[i].v; - { - QReadLocker locker(sessionData->haveSentMutex()); - const auto &haveSent = sessionData->haveSentMap(); - const auto haveSentEnd = haveSent.cend(); - if (haveSent.find(requestMsgId) == haveSentEnd) { - DEBUG_LOG(("Message Info: state was received for msgId %1, but request is not found, looking in resent requests...").arg(requestMsgId)); - QWriteLocker locker2(sessionData->toResendMutex()); - auto &toResend = sessionData->toResendMap(); - const auto reqIt = toResend.find(requestMsgId); - if (reqIt != toResend.cend()) { - if ((state & 0x07) != 0x04) { // was received - DEBUG_LOG(("Message Info: state was received for msgId %1, state %2, already resending in container").arg(requestMsgId).arg((int32)state)); - } else { - DEBUG_LOG(("Message Info: state was received for msgId %1, state %2, ack, cancelling resend").arg(requestMsgId).arg((int32)state)); - acked.push_back(MTP_long(requestMsgId)); // will remove from resend in requestsAcked - } - } else { - DEBUG_LOG(("Message Info: msgId %1 was not found in recent resent either").arg(requestMsgId)); - } - continue; - } - } - if ((state & 0x07) != 0x04) { // was received - DEBUG_LOG(("Message Info: state was received for msgId %1, state %2, resending in container").arg(requestMsgId).arg((int32)state)); - resend(requestMsgId, 10, true); - } else { - DEBUG_LOG(("Message Info: state was received for msgId %1, state %2, ack").arg(requestMsgId).arg((int32)state)); - acked.push_back(MTP_long(requestMsgId)); - } - } -} - -void ConnectionPrivate::resend(quint64 msgId, qint64 msCanWait, bool forceContainer, bool sendMsgStateInfo) { - if (msgId == _pingMsgId) return; - emit resendAsync(msgId, msCanWait, forceContainer, sendMsgStateInfo); -} - -void ConnectionPrivate::resendMany(QVector msgIds, qint64 msCanWait, bool forceContainer, bool sendMsgStateInfo) { - for (int32 i = 0, l = msgIds.size(); i < l; ++i) { - if (msgIds.at(i) == _pingMsgId) { - msgIds.remove(i); - --l; - } - } - emit resendManyAsync(msgIds, msCanWait, forceContainer, sendMsgStateInfo); -} - -void ConnectionPrivate::onConnected( - not_null connection) { - QReadLocker lockFinished(&sessionDataMutex); - if (!sessionData) return; - - disconnect(connection, &AbstractConnection::connected, nullptr, nullptr); - if (!connection->isConnected()) { - LOG(("Connection Error: not connected in onConnected(), " - "state: %1").arg(connection->debugState())); - - lockFinished.unlock(); - return restart(); - } - - _waitForConnected = kMinConnectedTimeout; - _waitForConnectedTimer.cancel(); - - const auto i = ranges::find( - _testConnections, - connection.get(), - [](const TestConnection &test) { return test.data.get(); }); - Assert(i != end(_testConnections)); - const auto my = i->priority; - const auto j = ranges::find_if( - _testConnections, - [&](const TestConnection &test) { return test.priority > my; }); - if (j != end(_testConnections)) { - DEBUG_LOG(("MTP Info: connection %1 succeed, " - "waiting for %2.").arg(i->data->tag()).arg(j->data->tag())); - _waitForBetterTimer.callOnce(kWaitForBetterTimeout); - } else { - DEBUG_LOG(("MTP Info: connection through IPv4 succeed.")); - _waitForBetterTimer.cancel(); - _connection = std::move(i->data); - _testConnections.clear(); - - lockFinished.unlock(); - updateAuthKey(); - } -} - -void ConnectionPrivate::onDisconnected( - not_null connection) { - removeTestConnection(connection); - - if (_testConnections.empty()) { - destroyAllConnections(); - restart(); - } else { - confirmBestConnection(); - } -} - -void ConnectionPrivate::confirmBestConnection() { - if (_waitForBetterTimer.isActive()) { - return; - } - const auto i = ranges::max_element( - _testConnections, - std::less<>(), - [](const TestConnection &test) { - return test.data->isConnected() ? test.priority : -1; - }); - Assert(i != end(_testConnections)); - if (!i->data->isConnected()) { - return; - } - - DEBUG_LOG(("MTP Info: can't connect through better, using %1." - ).arg(i->data->tag())); - - _connection = std::move(i->data); - _testConnections.clear(); - - updateAuthKey(); -} - -void ConnectionPrivate::removeTestConnection( - not_null connection) { - _testConnections.erase( - ranges::remove( - _testConnections, - connection.get(), - [](const TestConnection &test) { return test.data.get(); }), - end(_testConnections)); -} - -void ConnectionPrivate::updateAuthKey() { - QReadLocker lockFinished(&sessionDataMutex); - if (!sessionData || !_connection) return; - - DEBUG_LOG(("AuthKey Info: Connection updating key from Session, dc %1").arg(_shiftedDcId)); - uint64 newKeyId = 0; - { - ReadLockerAttempt lock(sessionData->keyMutex()); - if (!lock) { - DEBUG_LOG(("MTP Info: could not lock auth_key for read, waiting signal emit")); - clearMessages(); - keyId = newKeyId; - return; // some other connection is getting key - } - auto key = sessionData->getKey(); - newKeyId = key ? key->keyId() : 0; - } - if (keyId != newKeyId) { - clearMessages(); - keyId = newKeyId; - } - DEBUG_LOG(("AuthKey Info: Connection update key from Session, dc %1 result: %2").arg(_shiftedDcId).arg(Logs::mb(&keyId, sizeof(keyId)).str())); - if (keyId) { - return authKeyCreated(); - } - - DEBUG_LOG(("AuthKey Info: No key in updateAuthKey(), will be creating auth_key")); - lockKey(); - - auto &key = sessionData->getKey(); - if (key) { - if (keyId != key->keyId()) clearMessages(); - keyId = key->keyId(); - unlockKey(); - return authKeyCreated(); - } else if (_instance->isKeysDestroyer()) { - // We are here to destroy an old key, so we're done. - LOG(("MTP Error: No key %1 in updateAuthKey() for destroying.").arg(_shiftedDcId)); - emit _instance->keyDestroyed(_shiftedDcId); - return; - } - - _authKeyData = std::make_unique(); - _authKeyStrings = std::make_unique(); - const auto nonce = _authKeyData->nonce = rand_value(); - - connect(_connection, &AbstractConnection::receivedData, [=] { - pqAnswered(); - }); - - DEBUG_LOG(("AuthKey Info: sending Req_pq...")); - lockFinished.unlock(); - - sendNotSecureRequest(MTPReq_pq_multi(nonce)); -} - -void ConnectionPrivate::clearMessages() { - if (keyId && keyId != kRecreateKeyId && _connection) { - _connection->received().clear(); - } -} - -void ConnectionPrivate::pqAnswered() { - disconnect(_connection, &AbstractConnection::receivedData, nullptr, nullptr); - DEBUG_LOG(("AuthKey Info: receiving Req_pq answer...")); - - MTPReq_pq::ResponseType res_pq; - if (!readNotSecureResponse(res_pq)) { - return restart(); - } - - auto &res_pq_data = res_pq.c_resPQ(); - if (res_pq_data.vnonce() != _authKeyData->nonce) { - LOG(("AuthKey Error: received nonce <> sent nonce (in res_pq)!")); - DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&res_pq_data.vnonce(), 16).str()).arg(Logs::mb(&_authKeyData->nonce, 16).str())); - return restart(); - } - - auto rsaKey = internal::RSAPublicKey(); - if (!_instance->dcOptions()->getDcRSAKey(BareDcId(_shiftedDcId), res_pq.c_resPQ().vserver_public_key_fingerprints().v, &rsaKey)) { - if (_dcType == DcType::Cdn) { - LOG(("Warning: CDN public RSA key not found")); - requestCDNConfig(); - return; - } - LOG(("AuthKey Error: could not choose public RSA key")); - return restart(); - } - Assert(rsaKey.isValid()); - - _authKeyData->server_nonce = res_pq_data.vserver_nonce(); - _authKeyData->new_nonce = rand_value(); - - auto &pq = res_pq_data.vpq().v; - auto p = QByteArray(); - auto q = QByteArray(); - if (!internal::parsePQ(pq, p, q)) { - LOG(("AuthKey Error: could not factor pq!")); - DEBUG_LOG(("AuthKey Error: problematic pq: %1").arg(Logs::mb(pq.constData(), pq.length()).str())); - return restart(); - } - - auto p_q_inner = MTP_p_q_inner_data_dc( - res_pq_data.vpq(), - MTP_bytes(std::move(p)), - MTP_bytes(std::move(q)), - _authKeyData->nonce, - _authKeyData->server_nonce, - _authKeyData->new_nonce, - MTP_int(getProtocolDcId())); - auto dhEncString = encryptPQInnerRSA(p_q_inner, rsaKey); - if (dhEncString.empty()) { - return restart(); - } - - connect(_connection, &AbstractConnection::receivedData, [=] { - dhParamsAnswered(); - }); - - DEBUG_LOG(("AuthKey Info: sending Req_DH_params...")); - - sendNotSecureRequest(MTPReq_DH_params( - _authKeyData->nonce, - _authKeyData->server_nonce, - p_q_inner.c_p_q_inner_data_dc().vp(), - p_q_inner.c_p_q_inner_data_dc().vq(), - MTP_long(rsaKey.getFingerPrint()), - MTP_bytes(dhEncString))); -} - -bytes::vector ConnectionPrivate::encryptPQInnerRSA( - const MTPP_Q_inner_data &data, - const internal::RSAPublicKey &key) { - auto p_q_inner_size = tl::count_length(data); - auto encSize = (p_q_inner_size >> 2) + 6; - if (encSize >= 65) { - auto tmp = mtpBuffer(); - tmp.reserve(encSize); - data.write(tmp); - LOG(("AuthKey Error: too large data for RSA encrypt, size %1").arg(encSize * sizeof(mtpPrime))); - DEBUG_LOG(("AuthKey Error: bad data for RSA encrypt %1").arg(Logs::mb(&tmp[0], tmp.size() * 4).str())); - return {}; // can't be 255-byte string - } - - auto encBuffer = mtpBuffer(); - encBuffer.reserve(65); // 260 bytes - encBuffer.resize(6); - encBuffer[0] = 0; - data.write(encBuffer); - - hashSha1(&encBuffer[6], p_q_inner_size, &encBuffer[1]); - if (encSize < 65) { - encBuffer.resize(65); - memset_rand(&encBuffer[encSize], (65 - encSize) * sizeof(mtpPrime)); - } - - auto bytes = bytes::make_span(encBuffer); - auto bytesToEncrypt = bytes.subspan(3, 256); - return key.encrypt(bytesToEncrypt); -} - -void ConnectionPrivate::dhParamsAnswered() { - disconnect(_connection, &AbstractConnection::receivedData, nullptr, nullptr); - DEBUG_LOG(("AuthKey Info: receiving Req_DH_params answer...")); - - MTPReq_DH_params::ResponseType res_DH_params; - if (!readNotSecureResponse(res_DH_params)) { - return restart(); - } - - switch (res_DH_params.type()) { - case mtpc_server_DH_params_ok: { - const auto &encDH(res_DH_params.c_server_DH_params_ok()); - if (encDH.vnonce() != _authKeyData->nonce) { - LOG(("AuthKey Error: received nonce <> sent nonce (in server_DH_params_ok)!")); - DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&encDH.vnonce(), 16).str()).arg(Logs::mb(&_authKeyData->nonce, 16).str())); - return restart(); - } - if (encDH.vserver_nonce() != _authKeyData->server_nonce) { - LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in server_DH_params_ok)!")); - DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&encDH.vserver_nonce(), 16).str()).arg(Logs::mb(&_authKeyData->server_nonce, 16).str())); - return restart(); - } - - auto &encDHStr = encDH.vencrypted_answer().v; - uint32 encDHLen = encDHStr.length(), encDHBufLen = encDHLen >> 2; - if ((encDHLen & 0x03) || encDHBufLen < 6) { - LOG(("AuthKey Error: bad encrypted data length %1 (in server_DH_params_ok)!").arg(encDHLen)); - DEBUG_LOG(("AuthKey Error: received encrypted data %1").arg(Logs::mb(encDHStr.constData(), encDHLen).str())); - return restart(); - } - - uint32 nlen = tl::count_length(_authKeyData->new_nonce), slen = tl::count_length(_authKeyData->server_nonce); - uchar tmp_aes[1024], sha1ns[20], sha1sn[20], sha1nn[20]; - memcpy(tmp_aes, &_authKeyData->new_nonce, nlen); - memcpy(tmp_aes + nlen, &_authKeyData->server_nonce, slen); - memcpy(tmp_aes + nlen + slen, &_authKeyData->new_nonce, nlen); - memcpy(tmp_aes + nlen + slen + nlen, &_authKeyData->new_nonce, nlen); - hashSha1(tmp_aes, nlen + slen, sha1ns); - hashSha1(tmp_aes + nlen, nlen + slen, sha1sn); - hashSha1(tmp_aes + nlen + slen, nlen + nlen, sha1nn); - - mtpBuffer decBuffer; - decBuffer.resize(encDHBufLen); - - memcpy(_authKeyData->aesKey, sha1ns, 20); - memcpy(_authKeyData->aesKey + 20, sha1sn, 12); - memcpy(_authKeyData->aesIV, sha1sn + 12, 8); - memcpy(_authKeyData->aesIV + 8, sha1nn, 20); - memcpy(_authKeyData->aesIV + 28, &_authKeyData->new_nonce, 4); - - aesIgeDecryptRaw(encDHStr.constData(), &decBuffer[0], encDHLen, _authKeyData->aesKey, _authKeyData->aesIV); - - const mtpPrime *from(&decBuffer[5]), *to(from), *end(from + (encDHBufLen - 5)); - MTPServer_DH_inner_data dh_inner; - if (!dh_inner.read(to, end)) { - LOG(("AuthKey Error: could not decrypt server_DH_inner_data!")); - return restart(); - } - const auto &dh_inner_data(dh_inner.c_server_DH_inner_data()); - if (dh_inner_data.vnonce() != _authKeyData->nonce) { - LOG(("AuthKey Error: received nonce <> sent nonce (in server_DH_inner_data)!")); - DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&dh_inner_data.vnonce(), 16).str()).arg(Logs::mb(&_authKeyData->nonce, 16).str())); - return restart(); - } - if (dh_inner_data.vserver_nonce() != _authKeyData->server_nonce) { - LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in server_DH_inner_data)!")); - DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&dh_inner_data.vserver_nonce(), 16).str()).arg(Logs::mb(&_authKeyData->server_nonce, 16).str())); - return restart(); - } - uchar sha1Buffer[20]; - if (memcmp(&decBuffer[0], hashSha1(&decBuffer[5], (to - from) * sizeof(mtpPrime), sha1Buffer), 20)) { - LOG(("AuthKey Error: sha1 hash of encrypted part did not match!")); - DEBUG_LOG(("AuthKey Error: sha1 did not match, server_nonce: %1, new_nonce %2, encrypted data %3").arg(Logs::mb(&_authKeyData->server_nonce, 16).str()).arg(Logs::mb(&_authKeyData->new_nonce, 16).str()).arg(Logs::mb(encDHStr.constData(), encDHLen).str())); - return restart(); - } - base::unixtime::update(dh_inner_data.vserver_time().v); - - // check that dhPrime and (dhPrime - 1) / 2 are really prime - if (!IsPrimeAndGood(bytes::make_span(dh_inner_data.vdh_prime().v), dh_inner_data.vg().v)) { - LOG(("AuthKey Error: bad dh_prime primality!")); - return restart(); - } - - _authKeyStrings->dh_prime = bytes::make_vector( - dh_inner_data.vdh_prime().v); - _authKeyData->g = dh_inner_data.vg().v; - _authKeyStrings->g_a = bytes::make_vector(dh_inner_data.vg_a().v); - _authKeyData->retry_id = MTP_long(0); - _authKeyData->retries = 0; - } return dhClientParamsSend(); - - case mtpc_server_DH_params_fail: { - const auto &encDH(res_DH_params.c_server_DH_params_fail()); - if (encDH.vnonce() != _authKeyData->nonce) { - LOG(("AuthKey Error: received nonce <> sent nonce (in server_DH_params_fail)!")); - DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&encDH.vnonce(), 16).str()).arg(Logs::mb(&_authKeyData->nonce, 16).str())); - return restart(); - } - if (encDH.vserver_nonce() != _authKeyData->server_nonce) { - LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in server_DH_params_fail)!")); - DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&encDH.vserver_nonce(), 16).str()).arg(Logs::mb(&_authKeyData->server_nonce, 16).str())); - return restart(); - } - uchar sha1Buffer[20]; - if (encDH.vnew_nonce_hash() != *(MTPint128*)(hashSha1(&_authKeyData->new_nonce, 32, sha1Buffer) + 1)) { - LOG(("AuthKey Error: received new_nonce_hash did not match!")); - DEBUG_LOG(("AuthKey Error: received new_nonce_hash: %1, new_nonce: %2").arg(Logs::mb(&encDH.vnew_nonce_hash(), 16).str()).arg(Logs::mb(&_authKeyData->new_nonce, 32).str())); - return restart(); - } - LOG(("AuthKey Error: server_DH_params_fail received!")); - } return restart(); - - } - LOG(("AuthKey Error: unknown server_DH_params received, typeId = %1").arg(res_DH_params.type())); - return restart(); -} - -void ConnectionPrivate::dhClientParamsSend() { - if (++_authKeyData->retries > 5) { - LOG(("AuthKey Error: could not create auth_key for %1 retries").arg(_authKeyData->retries - 1)); - return restart(); - } - - // gen rand 'b' - auto randomSeed = bytes::vector(ModExpFirst::kRandomPowerSize); - bytes::set_random(randomSeed); - auto g_b_data = CreateModExp(_authKeyData->g, _authKeyStrings->dh_prime, randomSeed); - if (g_b_data.modexp.empty()) { - LOG(("AuthKey Error: could not generate good g_b.")); - return restart(); - } - - auto computedAuthKey = CreateAuthKey(_authKeyStrings->g_a, g_b_data.randomPower, _authKeyStrings->dh_prime); - if (computedAuthKey.empty()) { - LOG(("AuthKey Error: could not generate auth_key.")); - return restart(); - } - AuthKey::FillData(_authKeyStrings->auth_key, computedAuthKey); - - // count auth_key hashes - parts of sha1(auth_key) - auto auth_key_sha = hashSha1(_authKeyStrings->auth_key.data(), _authKeyStrings->auth_key.size()); - memcpy(&_authKeyData->auth_key_aux_hash, auth_key_sha.data(), 8); - memcpy(&_authKeyData->auth_key_hash, auth_key_sha.data() + 12, 8); - - auto client_dh_inner = MTP_client_DH_inner_data(_authKeyData->nonce, _authKeyData->server_nonce, _authKeyData->retry_id, MTP_bytes(g_b_data.modexp)); - - auto sdhEncString = encryptClientDHInner(client_dh_inner); - - connect(_connection, &AbstractConnection::receivedData, [=] { - dhClientParamsAnswered(); - }); - - DEBUG_LOG(("AuthKey Info: sending Req_client_DH_params...")); - sendNotSecureRequest(MTPSet_client_DH_params( - _authKeyData->nonce, - _authKeyData->server_nonce, - MTP_string(std::move(sdhEncString)))); -} - -std::string ConnectionPrivate::encryptClientDHInner(const MTPClient_DH_Inner_Data &data) { - auto client_dh_inner_size = tl::count_length(data); - auto encSize = (client_dh_inner_size >> 2) + 5; - auto encFullSize = encSize; - if (encSize & 0x03) { - encFullSize += 4 - (encSize & 0x03); - } - - auto encBuffer = mtpBuffer(); - encBuffer.reserve(encFullSize); - encBuffer.resize(5); - data.write(encBuffer); - - hashSha1(&encBuffer[5], client_dh_inner_size, &encBuffer[0]); - if (encSize < encFullSize) { - encBuffer.resize(encFullSize); - memset_rand(&encBuffer[encSize], (encFullSize - encSize) * sizeof(mtpPrime)); - } - - auto sdhEncString = std::string(encFullSize * 4, ' '); - - aesIgeEncryptRaw(&encBuffer[0], &sdhEncString[0], encFullSize * sizeof(mtpPrime), _authKeyData->aesKey, _authKeyData->aesIV); - - return sdhEncString; -} - -void ConnectionPrivate::dhClientParamsAnswered() { - QReadLocker lockFinished(&sessionDataMutex); - if (!sessionData) return; - - disconnect(_connection, &AbstractConnection::receivedData, nullptr, nullptr); - DEBUG_LOG(("AuthKey Info: receiving Req_client_DH_params answer...")); - - MTPSet_client_DH_params::ResponseType res_client_DH_params; - if (!readNotSecureResponse(res_client_DH_params)) { - lockFinished.unlock(); - return restart(); - } - - switch (res_client_DH_params.type()) { - case mtpc_dh_gen_ok: { - const auto &resDH(res_client_DH_params.c_dh_gen_ok()); - if (resDH.vnonce() != _authKeyData->nonce) { - LOG(("AuthKey Error: received nonce <> sent nonce (in dh_gen_ok)!")); - DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&resDH.vnonce(), 16).str()).arg(Logs::mb(&_authKeyData->nonce, 16).str())); - - lockFinished.unlock(); - return restart(); - } - if (resDH.vserver_nonce() != _authKeyData->server_nonce) { - LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in dh_gen_ok)!")); - DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&resDH.vserver_nonce(), 16).str()).arg(Logs::mb(&_authKeyData->server_nonce, 16).str())); - - lockFinished.unlock(); - return restart(); - } - _authKeyData->new_nonce_buf[32] = 1; - uchar sha1Buffer[20]; - if (resDH.vnew_nonce_hash1() != *(MTPint128*)(hashSha1(_authKeyData->new_nonce_buf, 41, sha1Buffer) + 1)) { - LOG(("AuthKey Error: received new_nonce_hash1 did not match!")); - DEBUG_LOG(("AuthKey Error: received new_nonce_hash1: %1, new_nonce_buf: %2").arg(Logs::mb(&resDH.vnew_nonce_hash1(), 16).str()).arg(Logs::mb(_authKeyData->new_nonce_buf, 41).str())); - - lockFinished.unlock(); - return restart(); - } - - uint64 salt1 = _authKeyData->new_nonce.l.l, salt2 = _authKeyData->server_nonce.l, serverSalt = salt1 ^ salt2; - sessionData->setSalt(serverSalt); - - auto authKey = std::make_shared(AuthKey::Type::Generated, BareDcId(_shiftedDcId), _authKeyStrings->auth_key); - - DEBUG_LOG(("AuthKey Info: auth key gen succeed, id: %1, server salt: %2").arg(authKey->keyId()).arg(serverSalt)); - - sessionData->owner()->notifyKeyCreated(std::move(authKey)); // slot will call authKeyCreated() - sessionData->clear(_instance); - unlockKey(); - } return; - - case mtpc_dh_gen_retry: { - const auto &resDH(res_client_DH_params.c_dh_gen_retry()); - if (resDH.vnonce() != _authKeyData->nonce) { - LOG(("AuthKey Error: received nonce <> sent nonce (in dh_gen_retry)!")); - DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&resDH.vnonce(), 16).str()).arg(Logs::mb(&_authKeyData->nonce, 16).str())); - - lockFinished.unlock(); - return restart(); - } - if (resDH.vserver_nonce() != _authKeyData->server_nonce) { - LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in dh_gen_retry)!")); - DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&resDH.vserver_nonce(), 16).str()).arg(Logs::mb(&_authKeyData->server_nonce, 16).str())); - - lockFinished.unlock(); - return restart(); - } - _authKeyData->new_nonce_buf[32] = 2; - uchar sha1Buffer[20]; - if (resDH.vnew_nonce_hash2() != *(MTPint128*)(hashSha1(_authKeyData->new_nonce_buf, 41, sha1Buffer) + 1)) { - LOG(("AuthKey Error: received new_nonce_hash2 did not match!")); - DEBUG_LOG(("AuthKey Error: received new_nonce_hash2: %1, new_nonce_buf: %2").arg(Logs::mb(&resDH.vnew_nonce_hash2(), 16).str()).arg(Logs::mb(_authKeyData->new_nonce_buf, 41).str())); - - lockFinished.unlock(); - return restart(); - } - _authKeyData->retry_id = _authKeyData->auth_key_aux_hash; - } return dhClientParamsSend(); - - case mtpc_dh_gen_fail: { - const auto &resDH(res_client_DH_params.c_dh_gen_fail()); - if (resDH.vnonce() != _authKeyData->nonce) { - LOG(("AuthKey Error: received nonce <> sent nonce (in dh_gen_fail)!")); - DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&resDH.vnonce(), 16).str()).arg(Logs::mb(&_authKeyData->nonce, 16).str())); - - lockFinished.unlock(); - return restart(); - } - if (resDH.vserver_nonce() != _authKeyData->server_nonce) { - LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in dh_gen_fail)!")); - DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&resDH.vserver_nonce(), 16).str()).arg(Logs::mb(&_authKeyData->server_nonce, 16).str())); - - lockFinished.unlock(); - return restart(); - } - _authKeyData->new_nonce_buf[32] = 3; - uchar sha1Buffer[20]; - if (resDH.vnew_nonce_hash3() != *(MTPint128*)(hashSha1(_authKeyData->new_nonce_buf, 41, sha1Buffer) + 1)) { - LOG(("AuthKey Error: received new_nonce_hash3 did not match!")); - DEBUG_LOG(("AuthKey Error: received new_nonce_hash3: %1, new_nonce_buf: %2").arg(Logs::mb(&resDH.vnew_nonce_hash3(), 16).str()).arg(Logs::mb(_authKeyData->new_nonce_buf, 41).str())); - - lockFinished.unlock(); - return restart(); - } - LOG(("AuthKey Error: dh_gen_fail received!")); - } - - lockFinished.unlock(); - return restart(); - - } - LOG(("AuthKey Error: unknown set_client_DH_params_answer received, typeId = %1").arg(res_client_DH_params.type())); - - lockFinished.unlock(); - return restart(); -} - -void ConnectionPrivate::authKeyCreated() { - clearAuthKeyData(); - - connect(_connection, &AbstractConnection::receivedData, [=] { - handleReceived(); - }); - - if (sessionData->getSalt()) { // else receive salt in bad_server_salt first, then try to send all the requests - setState(ConnectedState); - if (restarted) { - emit resendAllAsync(); - restarted = false; - } - } - - _pingIdToSend = rand_value(); // get server_salt - - emit needToSendAsync(); -} - -void ConnectionPrivate::clearAuthKeyData() { - auto zeroMemory = [](bytes::span bytes) { -#ifdef Q_OS_WIN2 - SecureZeroMemory(bytes.data(), bytes.size()); -#else // Q_OS_WIN - auto end = reinterpret_cast(bytes.data()) + bytes.size(); - for (volatile auto p = reinterpret_cast(bytes.data()); p != end; ++p) { - *p = 0; - } -#endif // Q_OS_WIN - }; - if (_authKeyData) { - zeroMemory(gsl::make_span(reinterpret_cast(_authKeyData.get()), sizeof(AuthKeyCreateData))); - _authKeyData.reset(); - } - if (_authKeyStrings) { - if (!_authKeyStrings->dh_prime.empty()) { - zeroMemory(_authKeyStrings->dh_prime); - } - if (!_authKeyStrings->g_a.empty()) { - zeroMemory(_authKeyStrings->g_a); - } - zeroMemory(_authKeyStrings->auth_key); - _authKeyStrings.reset(); - } -} - -void ConnectionPrivate::onError( - not_null connection, - qint32 errorCode) { - if (errorCode == -429) { - LOG(("Protocol Error: -429 flood code returned!")); - } else if (errorCode == -444) { - LOG(("Protocol Error: -444 bad dc_id code returned!")); - InvokeQueued(_instance, [instance = _instance] { - instance->badConfigurationError(); - }); - } - removeTestConnection(connection); - - if (_testConnections.empty()) { - handleError(errorCode); - } else { - confirmBestConnection(); - } -} - -void ConnectionPrivate::handleError(int errorCode) { - destroyAllConnections(); - _waitForConnectedTimer.cancel(); - - if (errorCode == -404) { - if (_instance->isKeysDestroyer()) { - LOG(("MTP Info: -404 error received on destroying key %1, assuming it is destroyed.").arg(_shiftedDcId)); - emit _instance->keyDestroyed(_shiftedDcId); - return; - } else if (_dcType == DcType::Cdn) { - LOG(("MTP Info: -404 error received in CDN dc %1, assuming it was destroyed, recreating.").arg(_shiftedDcId)); - clearMessages(); - keyId = kRecreateKeyId; - return restart(); - } - } - MTP_LOG(_shiftedDcId, ("Restarting after error in connection, error code: %1...").arg(errorCode)); - return restart(); -} - -void ConnectionPrivate::onReadyData() { -} - -template -void ConnectionPrivate::sendNotSecureRequest(const Request &request) { - auto packet = _connection->prepareNotSecurePacket( - request, - base::unixtime::mtproto_msg_id()); - - DEBUG_LOG(("AuthKey Info: sending request, size: %1, time: %3" - ).arg(packet.size() - 8 - ).arg(packet[5])); - - const auto bytesSize = packet.size() * sizeof(mtpPrime); - - _connection->sendData(std::move(packet)); - - onSentSome(bytesSize); -} - -template -bool ConnectionPrivate::readNotSecureResponse(Response &response) { - onReceivedSome(); - - if (_connection->received().empty()) { - LOG(("AuthKey Error: " - "trying to read response from empty received list")); - return false; - } - - const auto buffer = std::move(_connection->received().front()); - _connection->received().pop_front(); - - const auto answer = _connection->parseNotSecureResponse(buffer); - if (answer.empty()) { - return false; - } - auto from = answer.data(); - return response.read(from, from + answer.size()); -} - -bool ConnectionPrivate::sendSecureRequest( - SecureRequest &&request, - bool needAnyResponse, - QReadLocker &lockFinished) { - request.addPadding(_connection->requiresExtendedPadding()); - uint32 fullSize = request->size(); - if (fullSize < 9) { - return false; - } - - auto messageSize = request.messageSize(); - if (messageSize < 5 || fullSize < messageSize + 4) { - return false; - } - - auto lock = ReadLockerAttempt(sessionData->keyMutex()); - if (!lock) { - DEBUG_LOG(("MTP Info: could not lock key for read in sendBuffer(), dc %1, restarting...").arg(_shiftedDcId)); - - lockFinished.unlock(); - restart(); - return false; - } - - auto key = sessionData->getKey(); - if (!key || key->keyId() != keyId) { - DEBUG_LOG(("MTP Error: auth_key id for dc %1 changed").arg(_shiftedDcId)); - - lockFinished.unlock(); - restart(); - return false; - } - - auto session = sessionData->getSession(); - auto salt = sessionData->getSalt(); - - memcpy(request->data() + 0, &salt, 2 * sizeof(mtpPrime)); - memcpy(request->data() + 2, &session, 2 * sizeof(mtpPrime)); - - auto from = request->constData() + 4; - MTP_LOG(_shiftedDcId, ("Send: ") + mtpTextSerialize(from, from + messageSize)); - -#ifdef TDESKTOP_MTPROTO_OLD - uint32 padding = fullSize - 4 - messageSize; - - uchar encryptedSHA[20]; - MTPint128 &msgKey(*(MTPint128*)(encryptedSHA + 4)); - hashSha1( - request->constData(), - (fullSize - padding) * sizeof(mtpPrime), - encryptedSHA); - - auto packet = _connection->prepareSecurePacket(keyId, msgKey, fullSize); - const auto prefix = packet.size(); - packet.resize(prefix + fullSize); - - aesIgeEncrypt_oldmtp( - request->constData(), - &packet[prefix], - fullSize * sizeof(mtpPrime), - key, - msgKey); -#else // TDESKTOP_MTPROTO_OLD - uchar encryptedSHA256[32]; - MTPint128 &msgKey(*(MTPint128*)(encryptedSHA256 + 8)); - - SHA256_CTX msgKeyLargeContext; - SHA256_Init(&msgKeyLargeContext); - SHA256_Update(&msgKeyLargeContext, key->partForMsgKey(true), 32); - SHA256_Update(&msgKeyLargeContext, request->constData(), fullSize * sizeof(mtpPrime)); - SHA256_Final(encryptedSHA256, &msgKeyLargeContext); - - auto packet = _connection->prepareSecurePacket(keyId, msgKey, fullSize); - const auto prefix = packet.size(); - packet.resize(prefix + fullSize); - - aesIgeEncrypt( - request->constData(), - &packet[prefix], - fullSize * sizeof(mtpPrime), - key, - msgKey); -#endif // TDESKTOP_MTPROTO_OLD - - DEBUG_LOG(("MTP Info: sending request, size: %1, num: %2, time: %3").arg(fullSize + 6).arg((*request)[4]).arg((*request)[5])); - - _connection->setSentEncrypted(); - _connection->sendData(std::move(packet)); - - if (needAnyResponse) { - onSentSome((prefix + fullSize) * sizeof(mtpPrime)); - } - - return true; -} - -mtpRequestId ConnectionPrivate::wasSent(mtpMsgId msgId) const { - if (msgId == _pingMsgId) return mtpRequestId(0xFFFFFFFF); - { - QReadLocker locker(sessionData->haveSentMutex()); - const auto &haveSent = sessionData->haveSentMap(); - const auto i = haveSent.constFind(msgId); - if (i != haveSent.cend()) { - return i.value()->requestId - ? i.value()->requestId - : mtpRequestId(0xFFFFFFFF); - } - } - { - QReadLocker locker(sessionData->toResendMutex()); - const auto &toResend = sessionData->toResendMap(); - const auto i = toResend.constFind(msgId); - if (i != toResend.cend()) return i.value(); - } - { - QReadLocker locker(sessionData->wereAckedMutex()); - const auto &wereAcked = sessionData->wereAckedMap(); - const auto i = wereAcked.constFind(msgId); - if (i != wereAcked.cend()) return i.value(); - } - return 0; -} - -void ConnectionPrivate::lockKey() { - unlockKey(); - sessionData->keyMutex()->lockForWrite(); - myKeyLock = true; -} - -void ConnectionPrivate::unlockKey() { - if (myKeyLock) { - myKeyLock = false; - sessionData->keyMutex()->unlock(); - } -} - -ConnectionPrivate::~ConnectionPrivate() { - clearAuthKeyData(); - Assert(_finished && _connection == nullptr && _testConnections.empty()); -} - -void ConnectionPrivate::stop() { - QWriteLocker lockFinished(&sessionDataMutex); - if (sessionData) { - if (myKeyLock) { - sessionData->owner()->notifyKeyCreated(AuthKeyPtr()); // release key lock, let someone else create it - sessionData->keyMutex()->unlock(); - myKeyLock = false; - } - sessionData = nullptr; - } -} - -} // namespace internal - -bool IsPrimeAndGood(bytes::const_span primeBytes, int g) { - return internal::IsPrimeAndGood(primeBytes, g); -} - -bool IsGoodModExpFirst( - const openssl::BigNum &modexp, - const openssl::BigNum &prime) { - return internal::IsGoodModExpFirst(modexp, prime); -} - -ModExpFirst CreateModExp( - int g, - bytes::const_span primeBytes, - bytes::const_span randomSeed) { - return internal::CreateModExp(g, primeBytes, randomSeed); -} - -bytes::vector CreateAuthKey( - bytes::const_span firstBytes, - bytes::const_span randomBytes, - bytes::const_span primeBytes) { - return internal::CreateAuthKey(firstBytes, randomBytes, primeBytes); -} - -} // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/facade.h b/Telegram/SourceFiles/mtproto/facade.h index dfa3dab83..a074caf48 100644 --- a/Telegram/SourceFiles/mtproto/facade.h +++ b/Telegram/SourceFiles/mtproto/facade.h @@ -11,13 +11,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mtproto/mtp_instance.h" namespace MTP { -namespace internal { +namespace details { [[nodiscard]] bool paused(); void pause(); void unpause(); -} // namespace internal +} // namespace details // send(MTPhelp_GetConfig(), MTP::configDcId(dc)) - for dc enumeration constexpr ShiftedDcId configDcId(DcId dcId) { @@ -34,29 +34,28 @@ constexpr ShiftedDcId updaterDcId(DcId dcId) { return ShiftDcId(dcId, kUpdaterDcShift); } -constexpr auto kDownloadSessionsCount = 2; constexpr auto kUploadSessionsCount = 2; -constexpr auto kDownloadSessionsCountMax = 8; constexpr auto kUploadSessionsCountMax = 8; -namespace internal { +namespace details { constexpr ShiftedDcId downloadDcId(DcId dcId, int index) { - static_assert(kDownloadSessionsCountMax < kMaxMediaDcCount, "Too large MTPDownloadSessionsCount!"); + Expects(index < kMaxMediaDcCount); + return ShiftDcId(dcId, kBaseDownloadDcShift + index); }; -} // namespace internal +} // namespace details // send(req, callbacks, MTP::downloadDcId(dc, index)) - for download shifted dc id inline ShiftedDcId downloadDcId(DcId dcId, int index) { - Expects(index >= 0 && index < cNetDownloadSessionsCount()); - return internal::downloadDcId(dcId, index); + return details::downloadDcId(dcId, index); } inline constexpr bool isDownloadDcId(ShiftedDcId shiftedDcId) { - return (shiftedDcId >= internal::downloadDcId(0, 0)) && (shiftedDcId < internal::downloadDcId(0, kDownloadSessionsCountMax - 1) + kDcShift); + return (shiftedDcId >= details::downloadDcId(0, 0)) + && (shiftedDcId < details::downloadDcId(0, kMaxMediaDcCount - 1) + kDcShift); } inline bool isCdnDc(MTPDdcOption::Flags flags) { @@ -78,25 +77,26 @@ inline DcId getTemporaryIdFromRealDcId(ShiftedDcId shiftedDcId) { return (dcId < Instance::Config::kTemporaryMainDc) ? (dcId + Instance::Config::kTemporaryMainDc) : 0; } -namespace internal { +namespace details { constexpr ShiftedDcId uploadDcId(DcId dcId, int index) { static_assert(kUploadSessionsCountMax < kMaxMediaDcCount, "Too large MTPUploadSessionsCount!"); return ShiftDcId(dcId, kBaseUploadDcShift + index); }; -} // namespace internal +} // namespace details // send(req, callbacks, MTP::uploadDcId(index)) - for upload shifted dc id // uploading always to the main dc so BareDcId(result) == 0 inline ShiftedDcId uploadDcId(int index) { Expects(index >= 0 && index < cNetUploadSessionsCount()); - return internal::uploadDcId(0, index); + return details::uploadDcId(0, index); }; constexpr bool isUploadDcId(ShiftedDcId shiftedDcId) { - return (shiftedDcId >= internal::uploadDcId(0, 0)) && (shiftedDcId < internal::uploadDcId(0, kUploadSessionsCountMax - 1) + kDcShift); + return (shiftedDcId >= details::uploadDcId(0, 0)) + && (shiftedDcId < details::uploadDcId(0, kUploadSessionsCountMax - 1) + kDcShift); } inline ShiftedDcId destroyKeyNextDcId(ShiftedDcId shiftedDcId) { diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index 9b0dc1d65..f22209fe7 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -7,389 +7,312 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "platform/linux/notifications_manager_linux.h" -#include "window/notifications_utilities.h" -#include "platform/linux/linux_libnotify.h" -#include "platform/linux/linux_libs.h" #include "history/history.h" #include "lang/lang_keys.h" #include "facades.h" +#include +#include +#include +#include + namespace Platform { namespace Notifications { -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION namespace { -bool LibNotifyLoaded() { - return (Libs::notify_init != nullptr) - && (Libs::notify_uninit != nullptr) - && (Libs::notify_is_initted != nullptr) -// && (Libs::notify_get_app_name != nullptr) -// && (Libs::notify_set_app_name != nullptr) - && (Libs::notify_get_server_caps != nullptr) - && (Libs::notify_get_server_info != nullptr) - && (Libs::notify_notification_new != nullptr) -// && (Libs::notify_notification_update != nullptr) - && (Libs::notify_notification_show != nullptr) -// && (Libs::notify_notification_set_app_name != nullptr) - && (Libs::notify_notification_set_timeout != nullptr) -// && (Libs::notify_notification_set_category != nullptr) -// && (Libs::notify_notification_set_urgency != nullptr) -// && (Libs::notify_notification_set_icon_from_pixbuf != nullptr) - && (Libs::notify_notification_set_image_from_pixbuf != nullptr) -// && (Libs::notify_notification_set_hint != nullptr) -// && (Libs::notify_notification_set_hint_int32 != nullptr) -// && (Libs::notify_notification_set_hint_uint32 != nullptr) -// && (Libs::notify_notification_set_hint_double != nullptr) - && (Libs::notify_notification_set_hint_string != nullptr) -// && (Libs::notify_notification_set_hint_byte != nullptr) -// && (Libs::notify_notification_set_hint_byte_array != nullptr) -// && (Libs::notify_notification_clear_hints != nullptr) - && (Libs::notify_notification_add_action != nullptr) - && (Libs::notify_notification_clear_actions != nullptr) - && (Libs::notify_notification_close != nullptr) - && (Libs::notify_notification_get_closed_reason != nullptr) - && (Libs::g_object_ref_sink != nullptr) - && (Libs::g_object_unref != nullptr) - && (Libs::g_list_free_full != nullptr) - && (Libs::g_error_free != nullptr) - && (Libs::g_signal_connect_data != nullptr) - && (Libs::g_signal_handler_disconnect != nullptr) -// && (Libs::gdk_pixbuf_new_from_data != nullptr) - && (Libs::gdk_pixbuf_new_from_file != nullptr); +constexpr auto kService = str_const("org.freedesktop.Notifications"); +constexpr auto kObjectPath = str_const("/org/freedesktop/Notifications"); +constexpr auto kInterface = kService; + +std::vector GetServerInformation( + const std::shared_ptr ¬ificationInterface) { + std::vector serverInformation; + auto serverInformationReply = notificationInterface + ->call(qsl("GetServerInformation")); + + if (serverInformationReply.type() == QDBusMessage::ReplyMessage) { + for (const auto &arg : serverInformationReply.arguments()) { + if (static_cast(arg.type()) + == QMetaType::QString) { + serverInformation.push_back(arg.toString()); + } else { + LOG(("Native notification error: " + "all elements in GetServerInformation " + "should be strings")); + } + } + } else if (serverInformationReply.type() == QDBusMessage::ErrorMessage) { + LOG(("Native notification error: %1") + .arg(QDBusError(serverInformationReply).message())); + } else { + LOG(("Native notification error: " + "error while getting information about notification daemon")); + } + + return serverInformation; } -QString escapeHtml(const QString &text) { - auto result = QString(); - auto copyFrom = 0, textSize = text.size(); - auto data = text.constData(); - for (auto i = 0; i != textSize; ++i) { - auto ch = data[i]; - if (ch == '<' || ch == '>' || ch == '&') { - if (!copyFrom) { - result.reserve(textSize * 5); - } - if (i > copyFrom) { - result.append(data + copyFrom, i - copyFrom); - } - switch (ch.unicode()) { - case '<': result.append(qstr("<")); break; - case '>': result.append(qstr(">")); break; - case '&': result.append(qstr("&")); break; - } - copyFrom = i + 1; - } +std::vector GetCapabilities( + const std::shared_ptr ¬ificationInterface) { + QDBusReply capabilitiesReply = notificationInterface + ->call(qsl("GetCapabilities")); + + if (capabilitiesReply.isValid()) { + return capabilitiesReply.value().toVector().toStdVector(); + } else { + LOG(("Native notification error: %1") + .arg(capabilitiesReply.error().message())); } - if (copyFrom > 0) { - result.append(data + copyFrom, textSize - copyFrom); - return result; - } - return text; + + return std::vector(); } -class NotificationData { -public: - NotificationData(const std::shared_ptr &guarded, const QString &title, const QString &body, const QStringList &capabilities, PeerId peerId, MsgId msgId) - : _data(Libs::notify_notification_new(title.toUtf8().constData(), body.toUtf8().constData(), nullptr)) { - if (valid()) { - init(guarded, capabilities, peerId, msgId); - } - } - bool valid() const { - return (_data != nullptr); - } - NotificationData(const NotificationData &other) = delete; - NotificationData &operator=(const NotificationData &other) = delete; - NotificationData(NotificationData &&other) = delete; - NotificationData &operator=(NotificationData &&other) = delete; - - void setImage(const QString &imagePath) { - auto imagePathNative = QFile::encodeName(imagePath); - if (auto pixbuf = Libs::gdk_pixbuf_new_from_file(imagePathNative.constData(), nullptr)) { - Libs::notify_notification_set_image_from_pixbuf(_data, pixbuf); - Libs::g_object_unref(Libs::g_object_cast(pixbuf)); - } - } - bool show() { - if (valid()) { - GError *error = nullptr; - - Libs::notify_notification_show(_data, &error); - if (!error) { - return true; - } - - logError(error); - } - return false; +QVersionNumber ParseSpecificationVersion( + const std::vector &serverInformation) { + if (serverInformation.size() >= 4) { + return QVersionNumber::fromString(serverInformation[3]); + } else { + LOG(("Native notification error: " + "server information should have 4 elements")); } - bool close() { - if (valid()) { - GError *error = nullptr; - Libs::notify_notification_close(_data, &error); - if (!error) { - return true; - } - - logError(error); - } - return false; - } - - ~NotificationData() { - if (valid()) { -// if (_handlerId > 0) { -// Libs::g_signal_handler_disconnect(Libs::g_object_cast(_data), _handlerId); -// } -// Libs::notify_notification_clear_actions(_data); - Libs::g_object_unref(Libs::g_object_cast(_data)); - } - } - -private: - void init(const std::shared_ptr &guarded, const QStringList &capabilities, PeerId peerId, MsgId msgId) { - if (capabilities.contains(qsl("append"))) { - Libs::notify_notification_set_hint_string(_data, "append", "true"); - } else if (capabilities.contains(qsl("x-canonical-append"))) { - Libs::notify_notification_set_hint_string(_data, "x-canonical-append", "true"); - } - - Libs::notify_notification_set_hint_string(_data, "desktop-entry", "kotatogramdesktop"); - - auto signalReceiver = Libs::g_object_cast(_data); - auto signalHandler = G_CALLBACK(NotificationData::notificationClosed); - auto signalName = "closed"; - auto signalDataFreeMethod = &NotificationData::notificationDataFreeClosure; - auto signalData = new NotificationDataStruct(guarded, peerId, msgId); - _handlerId = Libs::g_signal_connect_helper(signalReceiver, signalName, signalHandler, signalData, signalDataFreeMethod); - - Libs::notify_notification_set_timeout(_data, Libs::NOTIFY_EXPIRES_DEFAULT); - - if ((*guarded)->hasActionsSupport()) { - auto label = tr::lng_notification_reply(tr::now).toUtf8(); - auto actionReceiver = _data; - auto actionHandler = &NotificationData::notificationClicked; - auto actionLabel = label.constData(); - auto actionName = "default"; - auto actionDataFreeMethod = &NotificationData::notificationDataFree; - auto actionData = new NotificationDataStruct(guarded, peerId, msgId); - Libs::notify_notification_add_action(actionReceiver, actionName, actionLabel, actionHandler, actionData, actionDataFreeMethod); - } - } - - void logError(GError *error) { - LOG(("LibNotify Error: domain %1, code %2, message '%3'").arg(error->domain).arg(error->code).arg(QString::fromUtf8(error->message))); - Libs::g_error_free(error); - } - - struct NotificationDataStruct { - NotificationDataStruct(const std::shared_ptr &guarded, PeerId peerId, MsgId msgId) - : weak(guarded) - , peerId(peerId) - , msgId(msgId) { - } - - std::weak_ptr weak; - PeerId peerId = 0; - MsgId msgId = 0; - }; - static void performOnMainQueue(NotificationDataStruct *data, FnMut task) { - const auto weak = data->weak; - crl::on_main(weak, [=, task = std::move(task)]() mutable { - task(*weak.lock()); - }); - } - static void notificationDataFree(gpointer data) { - auto notificationData = static_cast(data); - delete notificationData; - } - static void notificationDataFreeClosure(gpointer data, GClosure *closure) { - auto notificationData = static_cast(data); - delete notificationData; - } - static void notificationClosed(Libs::NotifyNotification *notification, gpointer data) { - auto closedReason = Libs::notify_notification_get_closed_reason(notification); - auto notificationData = static_cast(data); - performOnMainQueue(notificationData, [peerId = notificationData->peerId, msgId = notificationData->msgId](Manager *manager) { - manager->clearNotification(peerId, msgId); - }); - } - static void notificationClicked(Libs::NotifyNotification *notification, char *action, gpointer data) { - auto notificationData = static_cast(data); - performOnMainQueue(notificationData, [peerId = notificationData->peerId, msgId = notificationData->msgId](Manager *manager) { - manager->notificationActivated(peerId, msgId); - }); - } - - Libs::NotifyNotification *_data = nullptr; - gulong _handlerId = 0; - -}; - -using Notification = std::shared_ptr; - -QString GetServerName() { - if (!LibNotifyLoaded()) { - return QString(); - } - if (!Libs::notify_is_initted() && !Libs::notify_init("Kotatogram Desktop")) { - LOG(("LibNotify Error: failed to init!")); - return QString(); - } - - gchar *name = nullptr; - auto guard = gsl::finally([&name] { - if (name) Libs::g_free(name); - }); - - if (!Libs::notify_get_server_info(&name, nullptr, nullptr, nullptr)) { - LOG(("LibNotify Error: could not get server name!")); - return QString(); - } - if (!name) { - LOG(("LibNotify Error: successfully got empty server name!")); - return QString(); - } - - auto result = QString::fromUtf8(static_cast(name)); - LOG(("Notifications Server: %1").arg(result)); - - return result; + return QVersionNumber(); } -auto LibNotifyServerName = QString(); +} -} // namespace -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION +NotificationData::NotificationData( + const std::shared_ptr ¬ificationInterface, + const base::weak_ptr &manager, + const QString &title, const QString &subtitle, + const QString &msg, PeerId peerId, MsgId msgId) +: _notificationInterface(notificationInterface) +, _manager(manager) +, _title(title) +, _peerId(peerId) +, _msgId(msgId) { + auto capabilities = GetCapabilities(_notificationInterface); + auto capabilitiesEnd = capabilities.end(); + + if (ranges::find(capabilities, qsl("body-markup")) != capabilitiesEnd) { + _body = subtitle.isEmpty() + ? msg.toHtmlEscaped() + : qsl("%1\n%2").arg(subtitle.toHtmlEscaped()) + .arg(msg.toHtmlEscaped()); + } else { + _body = subtitle.isEmpty() + ? msg + : qsl("%1\n%2").arg(subtitle).arg(msg); + } + + if (ranges::find(capabilities, qsl("actions")) != capabilitiesEnd) { + _actions << qsl("default") << QString(); + + // icon name according to https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html + _actions << qsl("mail-reply-sender") + << tr::lng_notification_reply(tr::now); + + connect(_notificationInterface.get(), + SIGNAL(ActionInvoked(uint, QString)), + this, SLOT(notificationClicked(uint))); + } + + if (ranges::find(capabilities, qsl("action-icons")) != capabilitiesEnd) { + _hints["action-icons"] = true; + } + + // suppress system sound if telegram sound activated, otherwise use system sound + if (ranges::find(capabilities, qsl("sound")) != capabilitiesEnd) { + if (Global::SoundNotify()) { + _hints["suppress-sound"] = true; + } else { + // sound name according to http://0pointer.de/public/sound-naming-spec.html + _hints["sound-name"] = qsl("message-new-instant"); + } + } + + if (ranges::find(capabilities, qsl("x-canonical-append")) + != capabilitiesEnd) { + _hints["x-canonical-append"] = qsl("true"); + } + + _hints["category"] = qsl("im.received"); + +#ifdef TDESKTOP_LAUNCHER_FILENAME + _hints["desktop-entry"] = + qsl(MACRO_TO_STRING(TDESKTOP_LAUNCHER_FILENAME)) + .remove(QRegExp(qsl("\\.desktop$"), Qt::CaseInsensitive)); +#else + _hints["desktop-entry"] = qsl("kotatogramdesktop"); +#endif + + connect(_notificationInterface.get(), + SIGNAL(NotificationClosed(uint, uint)), + this, SLOT(notificationClosed(uint))); +} + +bool NotificationData::show() { + QDBusReply notifyReply = _notificationInterface->call(qsl("Notify"), + str_const_toString(AppName), uint(0), QString(), _title, _body, + _actions, _hints, -1); + + if (notifyReply.isValid()) { + _notificationId = notifyReply.value(); + } else { + LOG(("Native notification error: %1") + .arg(notifyReply.error().message())); + } + + return notifyReply.isValid(); +} + +bool NotificationData::close() { + QDBusReply closeReply = _notificationInterface + ->call(qsl("CloseNotification"), _notificationId); + + if (!closeReply.isValid()) { + LOG(("Native notification error: %1") + .arg(closeReply.error().message())); + } + + return closeReply.isValid(); +} + +void NotificationData::setImage(const QString &imagePath) { + auto specificationVersion = ParseSpecificationVersion( + GetServerInformation(_notificationInterface)); + + QString imageKey; + + if (!specificationVersion.isNull()) { + const auto majorVersion = specificationVersion.majorVersion(); + const auto minorVersion = specificationVersion.minorVersion(); + + if ((majorVersion == 1 && minorVersion >= 2) || majorVersion > 1) { + imageKey = qsl("image-data"); + } else if (majorVersion == 1 && minorVersion == 1) { + imageKey = qsl("image_data"); + } else if ((majorVersion == 1 && minorVersion < 1) + || majorVersion < 1) { + imageKey = qsl("icon_data"); + } else { + LOG(("Native notification error: unknown specification version")); + return; + } + } else { + LOG(("Native notification error: specification version is null")); + return; + } + + auto image = QImage(imagePath).convertToFormat(QImage::Format_RGBA8888); + QByteArray imageBytes((const char*)image.constBits(), + image.sizeInBytes()); + + ImageData imageData; + imageData.width = image.width(); + imageData.height = image.height(); + imageData.rowStride = image.bytesPerLine(); + imageData.hasAlpha = true; + imageData.bitsPerSample = 8; + imageData.channels = 4; + imageData.data = imageBytes; + + _hints[imageKey] = QVariant::fromValue(imageData); +} + +void NotificationData::notificationClosed(uint id) { + if (id == _notificationId) { + const auto manager = _manager; + crl::on_main(manager, [=] { + manager->clearNotification(_peerId, _msgId); + }); + } +} + +void NotificationData::notificationClicked(uint id) { + if (id == _notificationId) { + const auto manager = _manager; + crl::on_main(manager, [=] { + manager->notificationActivated(_peerId, _msgId); + }); + } +} + +QDBusArgument &operator<<(QDBusArgument &argument, + const NotificationData::ImageData &imageData) { + argument.beginStructure(); + argument << imageData.width + << imageData.height + << imageData.rowStride + << imageData.hasAlpha + << imageData.bitsPerSample + << imageData.channels + << imageData.data; + argument.endStructure(); + return argument; +} + +const QDBusArgument &operator>>(const QDBusArgument &argument, + NotificationData::ImageData &imageData) { + argument.beginStructure(); + argument >> imageData.width + >> imageData.height + >> imageData.rowStride + >> imageData.hasAlpha + >> imageData.bitsPerSample + >> imageData.channels + >> imageData.data; + argument.endStructure(); + return argument; +} bool Supported() { -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION - static auto Checked = false; - if (!Checked) { - Checked = true; - LibNotifyServerName = GetServerName(); - } + static auto Available = QDBusInterface( + str_const_toString(kService), + str_const_toString(kObjectPath), + str_const_toString(kInterface)).isValid(); - return !LibNotifyServerName.isEmpty(); -#else - return false; -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION + return Available; } -std::unique_ptr Create(Window::Notifications::System *system) { -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION +std::unique_ptr Create( + Window::Notifications::System *system) { if (Global::NativeNotifications() && Supported()) { return std::make_unique(system); } -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION return nullptr; } -void Finish() { -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION - if (Libs::notify_is_initted && Libs::notify_uninit) { - if (Libs::notify_is_initted()) { - Libs::notify_uninit(); - } - } -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION -} +Manager::Private::Private(Manager *manager, Type type) +: _cachedUserpics(type) +, _manager(manager) +, _notificationInterface(std::make_shared( + str_const_toString(kService), + str_const_toString(kObjectPath), + str_const_toString(kInterface))) { + qDBusRegisterMetaType(); -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION -class Manager::Private { -public: - using Type = Window::Notifications::CachedUserpics::Type; - explicit Private(Type type) - : _cachedUserpics(type) { + auto specificationVersion = ParseSpecificationVersion( + GetServerInformation(_notificationInterface)); + + auto capabilities = GetCapabilities(_notificationInterface); + + if (!specificationVersion.isNull()) { + LOG(("Notification daemon specification version: %1") + .arg(specificationVersion.toString())); } - void init(Manager *manager); + if (!capabilities.empty()) { + const auto capabilitiesString = std::accumulate( + capabilities.begin(), + capabilities.end(), + QString{}, + [](auto &s, auto &p) { + return s + (p + qstr(", ")); + }).chopped(2); - void showNotification( - not_null peer, - MsgId msgId, - const QString &title, - const QString &subtitle, - const QString &msg, - bool hideNameAndPhoto, - bool hideReplyButton); - void clearAll(); - void clearFromHistory(not_null history); - void clearNotification(PeerId peerId, MsgId msgId); - - bool hasPoorSupport() const { - return _poorSupported; + LOG(("Notification daemon capabilities: %1").arg(capabilitiesString)); } - bool hasActionsSupport() const { - return _actionsSupported; - } - - ~Private(); - -private: - QString escapeNotificationText(const QString &text) const; - void showNextNotification(); - - struct QueuedNotification { - PeerData *peer = nullptr; - MsgId msgId = 0; - QString title; - QString body; - bool hideNameAndPhoto = false; - }; - - QString _serverName; - QStringList _capabilities; - - using QueuedNotifications = QList; - QueuedNotifications _queuedNotifications; - - using Notifications = QMap>; - Notifications _notifications; - - Window::Notifications::CachedUserpics _cachedUserpics; - bool _actionsSupported = false; - bool _markupSupported = false; - bool _poorSupported = false; - - std::shared_ptr _guarded; - -}; -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION - -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION -void Manager::Private::init(Manager *manager) { - _guarded = std::make_shared(manager); - - if (auto capabilities = Libs::notify_get_server_caps()) { - for (auto capability = capabilities; capability; capability = capability->next) { - auto capabilityText = QString::fromUtf8(static_cast(capability->data)); - _capabilities.push_back(capabilityText); - } - Libs::g_list_free_full(capabilities, g_free); - - LOG(("LibNotify capabilities: %1").arg(_capabilities.join(qstr(", ")))); - if (_capabilities.contains(qsl("actions"))) { - _actionsSupported = true; - } else if (_capabilities.contains(qsl("body-markup"))) { - _markupSupported = true; - } - } else { - LOG(("LibNotify Error: could not get capabilities!")); - } - - // Unity and other Notify OSD users handle desktop notifications - // extremely poor, even without the ability to close() them. - _serverName = LibNotifyServerName; - Assert(!_serverName.isEmpty()); - if (_serverName == qstr("notify-osd")) { -// _poorSupported = true; - _actionsSupported = false; - } -} - -QString Manager::Private::escapeNotificationText(const QString &text) const { - return _markupSupported ? escapeHtml(text) : text; } void Manager::Private::showNotification( @@ -400,93 +323,44 @@ void Manager::Private::showNotification( const QString &msg, bool hideNameAndPhoto, bool hideReplyButton) { - auto titleText = escapeNotificationText(title); - auto subtitleText = escapeNotificationText(subtitle); - auto msgText = escapeNotificationText(msg); - if (_markupSupported && !subtitleText.isEmpty()) { - subtitleText = qstr("") + subtitleText + qstr(""); - } - auto bodyText = subtitleText.isEmpty() ? msgText : (subtitleText + '\n' + msgText); - - QueuedNotification notification; - notification.peer = peer; - notification.msgId = msgId; - notification.title = titleText; - notification.body = bodyText; - notification.hideNameAndPhoto = hideNameAndPhoto; - _queuedNotifications.push_back(notification); - - showNextNotification(); -} - -void Manager::Private::showNextNotification() { - // Show only one notification at a time in Unity / Notify OSD. - if (_poorSupported) { - for (auto b = _notifications.begin(); !_notifications.isEmpty() && b->isEmpty();) { - _notifications.erase(b); - } - if (!_notifications.isEmpty()) { - return; - } - } - - QueuedNotification data; - while (!_queuedNotifications.isEmpty()) { - data = _queuedNotifications.front(); - _queuedNotifications.pop_front(); - if (data.peer) { - break; - } - } - if (!data.peer) { - return; - } - - auto peerId = data.peer->id; - auto msgId = data.msgId; auto notification = std::make_shared( - _guarded, - data.title, - data.body, - _capabilities, - peerId, + _notificationInterface, + _manager, + title, + subtitle, + msg, + peer->id, msgId); - if (!notification->valid()) { - return; - } - const auto key = data.hideNameAndPhoto + const auto key = hideNameAndPhoto ? InMemoryKey() - : data.peer->userpicUniqueKey(); - notification->setImage(_cachedUserpics.get(key, data.peer)); + :peer->userpicUniqueKey(); + notification->setImage(_cachedUserpics.get(key, peer)); - auto i = _notifications.find(peerId); + auto i = _notifications.find(peer->id); if (i != _notifications.cend()) { auto j = i->find(msgId); if (j != i->cend()) { auto oldNotification = j.value(); i->erase(j); oldNotification->close(); - i = _notifications.find(peerId); + i = _notifications.find(peer->id); } } if (i == _notifications.cend()) { - i = _notifications.insert(peerId, QMap()); + i = _notifications.insert(peer->id, QMap()); } - _notifications[peerId].insert(msgId, notification); + _notifications[peer->id].insert(msgId, notification); if (!notification->show()) { - i = _notifications.find(peerId); + i = _notifications.find(peer->id); if (i != _notifications.cend()) { i->remove(msgId); if (i->isEmpty()) _notifications.erase(i); } - showNextNotification(); } } void Manager::Private::clearAll() { - _queuedNotifications.clear(); - auto temp = base::take(_notifications); for_const (auto ¬ifications, temp) { for_const (auto notification, notifications) { @@ -496,14 +370,6 @@ void Manager::Private::clearAll() { } void Manager::Private::clearFromHistory(not_null history) { - for (auto i = _queuedNotifications.begin(); i != _queuedNotifications.end();) { - if (i->peer == history->peer) { - i = _queuedNotifications.erase(i); - } else { - ++i; - } - } - auto i = _notifications.find(history->peer->id); if (i != _notifications.cend()) { auto temp = base::take(i.value()); @@ -513,8 +379,6 @@ void Manager::Private::clearFromHistory(not_null history) { notification->close(); } } - - showNextNotification(); } void Manager::Private::clearNotification(PeerId peerId, MsgId msgId) { @@ -525,31 +389,21 @@ void Manager::Private::clearNotification(PeerId peerId, MsgId msgId) { _notifications.erase(i); } } - - showNextNotification(); } Manager::Private::~Private() { clearAll(); } -Manager::Manager(Window::Notifications::System *system) : NativeManager(system) -, _private(std::make_unique(Private::Type::Rounded)) { - _private->init(this); +Manager::Manager(Window::Notifications::System *system) +: NativeManager(system) +, _private(std::make_unique(this, Private::Type::Rounded)) { } void Manager::clearNotification(PeerId peerId, MsgId msgId) { _private->clearNotification(peerId, msgId); } -bool Manager::hasPoorSupport() const { - return _private->hasPoorSupport(); -} - -bool Manager::hasActionsSupport() const { - return _private->hasActionsSupport(); -} - Manager::~Manager() = default; void Manager::doShowNativeNotification( @@ -577,7 +431,6 @@ void Manager::doClearAllFast() { void Manager::doClearFromHistory(not_null history) { _private->clearFromHistory(history); } -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION } // namespace Notifications } // namespace Platform diff --git a/Telegram/SourceFiles/settings.cpp b/Telegram/SourceFiles/settings.cpp index 7d5ce1318..132f6a000 100644 --- a/Telegram/SourceFiles/settings.cpp +++ b/Telegram/SourceFiles/settings.cpp @@ -238,9 +238,7 @@ bool gShowChatId = true; int gNetSpeedBoost = 0; int gNetRequestsCount = 2; -int gNetDownloadSessionsCount = 2; int gNetUploadSessionsCount = 2; -int gNetMaxFileQueries = 16; int gNetUploadRequestInterval = 500; bool gShowPhoneInDrawer = true; diff --git a/Telegram/SourceFiles/settings.h b/Telegram/SourceFiles/settings.h index 389724f7d..303027ed5 100644 --- a/Telegram/SourceFiles/settings.h +++ b/Telegram/SourceFiles/settings.h @@ -198,9 +198,7 @@ DeclareSetting(bool, ShowChatId); DeclareSetting(int, NetSpeedBoost); DeclareSetting(int, NetRequestsCount); -DeclareSetting(int, NetDownloadSessionsCount); DeclareSetting(int, NetUploadSessionsCount); -DeclareSetting(int, NetMaxFileQueries); DeclareSetting(int, NetUploadRequestInterval); inline void SetNetworkBoost(int boost) { @@ -213,9 +211,7 @@ inline void SetNetworkBoost(int boost) { } cSetNetRequestsCount(2 + (2 * cNetSpeedBoost())); - cSetNetDownloadSessionsCount(2 + (2 * cNetSpeedBoost())); cSetNetUploadSessionsCount(2 + (2 * cNetSpeedBoost())); - cSetNetMaxFileQueries(16 + (16 * cNetSpeedBoost())); cSetNetUploadRequestInterval(500 - (100 * cNetSpeedBoost())); } @@ -229,4 +225,4 @@ void ClearCustomScales(); void SetDialogListLines(int lines); [[nodiscard]] int DialogListLines(); -[[nodiscard]] rpl::producer DialogListLinesChanges(); \ No newline at end of file +[[nodiscard]] rpl::producer DialogListLinesChanges(); diff --git a/Telegram/SourceFiles/storage/file_download.cpp b/Telegram/SourceFiles/storage/file_download.cpp index d76c4b180..72ddd262d 100644 --- a/Telegram/SourceFiles/storage/file_download.cpp +++ b/Telegram/SourceFiles/storage/file_download.cpp @@ -15,7 +15,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/application.h" #include "storage/localstorage.h" #include "platform/platform_file_utilities.h" -#include "mtproto/connection.h" // for MTP::kAckSendWaiting #include "main/main_session.h" #include "apiwrap.h" #include "core/crash_reports.h" @@ -24,135 +23,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "facades.h" #include "app.h" -namespace Storage { -namespace { - -// How much time without download causes additional session kill. -constexpr auto kKillSessionTimeout = crl::time(5000); - -// Max 16 file parts downloaded at the same time, 128 KB each. -constexpr auto kMaxFileQueries = 16; - -// Max 8 http[s] files downloaded at the same time. -constexpr auto kMaxWebFileQueries = 8; - -// Different part sizes are not supported for now :( -// Because we start downloading with some part size -// and then we get a cdn-redirect where we support only -// fixed part size download for hash checking. -constexpr auto kPartSize = 128 * 1024; - -} // namespace - -Downloader::Downloader(not_null api) -: _api(api) -, _killDownloadSessionsTimer([=] { killDownloadSessions(); }) -, _queueForWeb(kMaxWebFileQueries) { -} - -void Downloader::clearPriorities() { - ++_priority; -} - -void Downloader::requestedAmountIncrement(MTP::DcId dcId, int index, int amount) { - Expects(index >= 0 && index < cNetDownloadSessionsCount()); - - using namespace rpl::mappers; - - auto it = _requestedBytesAmount.find(dcId); - if (it == _requestedBytesAmount.cend()) { - it = _requestedBytesAmount.emplace(dcId, RequestedInDc { { 0 } }).first; - } - it->second[index] += amount; - if (amount > 0) { - killDownloadSessionsStop(dcId); - } else if (ranges::find_if(it->second, _1 > 0) == end(it->second)) { - killDownloadSessionsStart(dcId); - } -} - -void Downloader::killDownloadSessionsStart(MTP::DcId dcId) { - if (!_killDownloadSessionTimes.contains(dcId)) { - _killDownloadSessionTimes.emplace( - dcId, - crl::now() + MTP::kAckSendWaiting + kKillSessionTimeout); - } - if (!_killDownloadSessionsTimer.isActive()) { - _killDownloadSessionsTimer.callOnce( - MTP::kAckSendWaiting + kKillSessionTimeout + 5); - } -} - -void Downloader::killDownloadSessionsStop(MTP::DcId dcId) { - _killDownloadSessionTimes.erase(dcId); - if (_killDownloadSessionTimes.empty() - && _killDownloadSessionsTimer.isActive()) { - _killDownloadSessionsTimer.cancel(); - } -} - -void Downloader::killDownloadSessions() { - auto ms = crl::now(), left = MTP::kAckSendWaiting + kKillSessionTimeout; - for (auto i = _killDownloadSessionTimes.begin(); i != _killDownloadSessionTimes.end(); ) { - if (i->second <= ms) { - for (int j = 0; j < cNetDownloadSessionsCount(); ++j) { - MTP::stopSession(MTP::downloadDcId(i->first, j)); - } - i = _killDownloadSessionTimes.erase(i); - } else { - if (i->second - ms < left) { - left = i->second - ms; - } - ++i; - } - } - if (!_killDownloadSessionTimes.empty()) { - _killDownloadSessionsTimer.callOnce(left); - } -} - -int Downloader::chooseDcIndexForRequest(MTP::DcId dcId) const { - auto result = 0; - auto it = _requestedBytesAmount.find(dcId); - if (it != _requestedBytesAmount.cend()) { - for (auto i = 1; i != cNetDownloadSessionsCount(); ++i) { - if (it->second[i] < it->second[result]) { - result = i; - } - } - } - return result; -} - -not_null Downloader::queueForDc(MTP::DcId dcId) { - const auto i = _queuesForDc.find(dcId); - const auto result = (i != end(_queuesForDc)) - ? i - : _queuesForDc.emplace(dcId, Queue(cNetMaxFileQueries())).first; - return &result->second; -} - -not_null Downloader::queueForWeb() { - return &_queueForWeb; -} - -Downloader::~Downloader() { - killDownloadSessions(); -} - -} // namespace Storage - -namespace { - -QThread *_webLoadThread = nullptr; -WebLoadManager *_webLoadManager = nullptr; -WebLoadManager *webLoadManager() { - return (_webLoadManager && _webLoadManager != FinishedWebLoadManager) ? _webLoadManager : nullptr; -} -WebLoadMainManager *_webLoadMainManager = nullptr; - -} // namespace - FileLoader::FileLoader( const QString &toFile, int32 size, @@ -161,7 +31,7 @@ FileLoader::FileLoader( LoadFromCloudSetting fromCloud, bool autoLoading, uint8 cacheTag) -: _downloader(&Auth().downloader()) +: _session(&Auth()) , _autoLoading(autoLoading) , _cacheTag(cacheTag) , _filename(toFile) @@ -173,8 +43,10 @@ FileLoader::FileLoader( Expects(!_filename.isEmpty() || (_size <= Storage::kMaxFileInMemory)); } +FileLoader::~FileLoader() = default; + Main::Session &FileLoader::session() const { - return _downloader->api().session(); + return *_session; } void FileLoader::finishWithBytes(const QByteArray &data) { @@ -200,7 +72,7 @@ void FileLoader::finishWithBytes(const QByteArray &data) { Platform::File::PostprocessDownloaded( QFileInfo(_file).absoluteFilePath()); } - _downloader->taskFinished().notify(); + Auth().downloaderTaskFinished().notify(); } QByteArray FileLoader::imageFormat(const QSize &shrinkBox) const { @@ -258,46 +130,7 @@ void FileLoader::permitLoadFromCloud() { } void FileLoader::notifyAboutProgress() { - const auto queue = _queue; emit progress(this); - LoadNextFromQueue(queue); -} - -void FileLoader::LoadNextFromQueue(not_null queue) { - if (queue->queriesCount >= queue->queriesLimit) { - return; - } - for (auto i = queue->start; i;) { - if (i->loadPart()) { - if (queue->queriesCount >= queue->queriesLimit) { - return; - } - } else { - i = i->_next; - } - } -} - -void FileLoader::removeFromQueue() { - if (!_inQueue) return; - if (_next) { - _next->_prev = _prev; - } - if (_prev) { - _prev->_next = _next; - } - if (_queue->end == this) { - _queue->end = _prev; - } - if (_queue->start == this) { - _queue->start = _next; - } - _next = _prev = nullptr; - _inQueue = false; -} - -FileLoader::~FileLoader() { - removeFromQueue(); } void FileLoader::localLoaded( @@ -332,71 +165,7 @@ void FileLoader::start() { return cancel(true); } } - - auto currentPriority = _downloader->currentPriority(); - FileLoader *before = nullptr, *after = nullptr; - if (_inQueue && _priority == currentPriority) { - if (!_next || _next->_priority < currentPriority) return startLoading(); - after = _next; - while (after->_next && after->_next->_priority == currentPriority) { - after = after->_next; - } - } else { - _priority = currentPriority; - if (_inQueue) { - if (_next && _next->_priority == currentPriority) { - after = _next; - } else if (_prev && _prev->_priority < currentPriority) { - before = _prev; - while (before->_prev && before->_prev->_priority < currentPriority) { - before = before->_prev; - } - } else { - return startLoading(); - } - } else { - if (_queue->start && _queue->start->_priority == currentPriority) { - after = _queue->start; - } else { - before = _queue->start; - } - } - if (after) { - while (after->_next && after->_next->_priority == currentPriority) { - after = after->_next; - } - } - } - - removeFromQueue(); - - _inQueue = true; - if (!_queue->start) { - _queue->start = _queue->end = this; - } else if (before) { - if (before != _next) { - _prev = before->_prev; - _next = before; - _next->_prev = this; - if (_prev) { - _prev->_next = this; - } - if (_queue->start->_prev) _queue->start = _queue->start->_prev; - } - } else if (after) { - if (after != _prev) { - _next = after->_next; - _prev = after; - after->_next = this; - if (_next) { - _next->_prev = this; - } - if (_queue->end->_next) _queue->end = _queue->end->_next; - } - } else { - LOG(("Queue Error: _start && !before && !after")); - } - return startLoading(); + startLoading(); } void FileLoader::loadLocal(const Storage::Cache::Key &key) { @@ -472,7 +241,9 @@ void FileLoader::cancel() { void FileLoader::cancel(bool fail) { const auto started = (currentOffset() > 0); - cancelRequests(); + + cancelHook(); + _cancelled = true; _finished = true; if (_fileIsOpen) { @@ -481,10 +252,7 @@ void FileLoader::cancel(bool fail) { _file.remove(); } _data = QByteArray(); - removeFromQueue(); - const auto queue = _queue; - const auto sessionGuard = &session(); const auto weak = QPointer(this); if (fail) { emit failed(this, started); @@ -495,16 +263,6 @@ void FileLoader::cancel(bool fail) { _filename = QString(); _file.setFileName(_filename); } - - // Current cancel() call could be made from ~Main::Session(). - crl::on_main(sessionGuard, [=] { LoadNextFromQueue(queue); }); -} - -void FileLoader::startLoading() { - if ((_queue->queriesCount >= _queue->queriesLimit) || _finished) { - return; - } - loadPart(); } int FileLoader::currentOffset() const { @@ -595,8 +353,6 @@ bool FileLoader::finalizeResult() { Platform::File::PostprocessDownloaded( QFileInfo(_file).absoluteFilePath()); } - removeFromQueue(); - if (_localStatus == LocalStatus::NotFound) { if (const auto key = fileLocationKey()) { if (!_filename.isEmpty()) { @@ -612,1019 +368,6 @@ bool FileLoader::finalizeResult() { _cacheTag)); } } - _downloader->taskFinished().notify(); + Auth().downloaderTaskFinished().notify(); return true; } - -mtpFileLoader::mtpFileLoader( - const StorageFileLocation &location, - Data::FileOrigin origin, - LocationType type, - const QString &to, - int32 size, - LoadToCacheSetting toCache, - LoadFromCloudSetting fromCloud, - bool autoLoading, - uint8 cacheTag) -: FileLoader( - to, - size, - type, - toCache, - fromCloud, - autoLoading, - cacheTag) -, _location(location) -, _origin(origin) { - _queue = _downloader->queueForDc(dcId()); -} - -mtpFileLoader::mtpFileLoader( - const WebFileLocation &location, - int32 size, - LoadFromCloudSetting fromCloud, - bool autoLoading, - uint8 cacheTag) -: FileLoader( - QString(), - size, - UnknownFileLocation, - LoadToCacheAsWell, - fromCloud, - autoLoading, - cacheTag) -, _location(location) { - _queue = _downloader->queueForDc(dcId()); -} - -mtpFileLoader::mtpFileLoader( - const GeoPointLocation &location, - int32 size, - LoadFromCloudSetting fromCloud, - bool autoLoading, - uint8 cacheTag) -: FileLoader( - QString(), - size, - UnknownFileLocation, - LoadToCacheAsWell, - fromCloud, - autoLoading, - cacheTag) -, _location(location) { - _queue = _downloader->queueForDc(dcId()); -} - -Data::FileOrigin mtpFileLoader::fileOrigin() const { - return _origin; -} - -uint64 mtpFileLoader::objId() const { - if (const auto storage = base::get_if(&_location)) { - return storage->objectId(); - } - return 0; -} - -void mtpFileLoader::refreshFileReferenceFrom( - const Data::UpdatedFileReferences &updates, - int requestId, - const QByteArray ¤t) { - if (const auto storage = base::get_if(&_location)) { - storage->refreshFileReference(updates); - if (storage->fileReference() == current) { - cancel(true); - return; - } - } else { - cancel(true); - return; - } - const auto offset = finishSentRequestGetOffset(requestId); - makeRequest(offset); -} - -bool mtpFileLoader::loadPart() { - if (_finished || _lastComplete || (!_sentRequests.empty() && !_size)) { - return false; - } else if (_size && _nextRequestOffset >= _size) { - return false; - } - - makeRequest(_nextRequestOffset); - _nextRequestOffset += Storage::kPartSize; - return true; -} - -MTP::DcId mtpFileLoader::dcId() const { - if (const auto storage = base::get_if(&_location)) { - return storage->dcId(); - } - return Global::WebFileDcId(); -} - -mtpFileLoader::RequestData mtpFileLoader::prepareRequest(int offset) const { - auto result = RequestData(); - result.dcId = _cdnDcId ? _cdnDcId : dcId(); - result.dcIndex = _size - ? _downloader->chooseDcIndexForRequest(result.dcId) - : 0; - result.offset = offset; - return result; -} - -mtpRequestId mtpFileLoader::sendRequest(const RequestData &requestData) { - const auto offset = requestData.offset; - const auto limit = Storage::kPartSize; - const auto shiftedDcId = MTP::downloadDcId( - requestData.dcId, - requestData.dcIndex); - if (_cdnDcId) { - Assert(requestData.dcId == _cdnDcId); - return MTP::send( - MTPupload_GetCdnFile( - MTP_bytes(_cdnToken), - MTP_int(offset), - MTP_int(limit)), - rpcDone(&mtpFileLoader::cdnPartLoaded), - rpcFail(&mtpFileLoader::cdnPartFailed), - shiftedDcId, - 50); - } - return _location.match([&](const WebFileLocation &location) { - return MTP::send( - MTPupload_GetWebFile( - MTP_inputWebFileLocation( - MTP_bytes(location.url()), - MTP_long(location.accessHash())), - MTP_int(offset), - MTP_int(limit)), - rpcDone(&mtpFileLoader::webPartLoaded), - rpcFail(&mtpFileLoader::partFailed), - shiftedDcId, - 50); - }, [&](const GeoPointLocation &location) { - return MTP::send( - MTPupload_GetWebFile( - MTP_inputWebFileGeoPointLocation( - MTP_inputGeoPoint( - MTP_double(location.lat), - MTP_double(location.lon)), - MTP_long(location.access), - MTP_int(location.width), - MTP_int(location.height), - MTP_int(location.zoom), - MTP_int(location.scale)), - MTP_int(offset), - MTP_int(limit)), - rpcDone(&mtpFileLoader::webPartLoaded), - rpcFail(&mtpFileLoader::partFailed), - shiftedDcId, - 50); - }, [&](const StorageFileLocation &location) { - return MTP::send( - MTPupload_GetFile( - MTP_flags(0), - location.tl(session().userId()), - MTP_int(offset), - MTP_int(limit)), - rpcDone(&mtpFileLoader::normalPartLoaded), - rpcFail( - &mtpFileLoader::normalPartFailed, - location.fileReference()), - shiftedDcId, - 50); - }); -} - -void mtpFileLoader::makeRequest(int offset) { - Expects(!_finished); - - auto requestData = prepareRequest(offset); - placeSentRequest(sendRequest(requestData), requestData); -} - -void mtpFileLoader::requestMoreCdnFileHashes() { - Expects(!_finished); - - if (_cdnHashesRequestId || _cdnUncheckedParts.empty()) { - return; - } - - auto offset = _cdnUncheckedParts.cbegin()->first; - auto requestData = RequestData(); - requestData.dcId = dcId(); - requestData.dcIndex = 0; - requestData.offset = offset; - auto shiftedDcId = MTP::downloadDcId( - requestData.dcId, - requestData.dcIndex); - auto requestId = _cdnHashesRequestId = MTP::send( - MTPupload_GetCdnFileHashes( - MTP_bytes(_cdnToken), - MTP_int(offset)), - rpcDone(&mtpFileLoader::getCdnFileHashesDone), - rpcFail(&mtpFileLoader::cdnPartFailed), - shiftedDcId); - placeSentRequest(requestId, requestData); -} - -void mtpFileLoader::normalPartLoaded( - const MTPupload_File &result, - mtpRequestId requestId) { - Expects(!_finished); - Expects(result.type() == mtpc_upload_fileCdnRedirect || result.type() == mtpc_upload_file); - - auto offset = finishSentRequestGetOffset(requestId); - if (result.type() == mtpc_upload_fileCdnRedirect) { - return switchToCDN(offset, result.c_upload_fileCdnRedirect()); - } - auto buffer = bytes::make_span(result.c_upload_file().vbytes().v); - return partLoaded(offset, buffer); -} - -void mtpFileLoader::webPartLoaded( - const MTPupload_WebFile &result, - mtpRequestId requestId) { - result.match([&](const MTPDupload_webFile &data) { - const auto offset = finishSentRequestGetOffset(requestId); - if (!_size) { - _size = data.vsize().v; - } else if (data.vsize().v != _size) { - LOG(("MTP Error: " - "Bad size provided by bot for webDocument: %1, real: %2" - ).arg(_size - ).arg(data.vsize().v)); - cancel(true); - return; - } - partLoaded(offset, bytes::make_span(data.vbytes().v)); - }); -} - -void mtpFileLoader::cdnPartLoaded(const MTPupload_CdnFile &result, mtpRequestId requestId) { - Expects(!_finished); - - const auto offset = finishSentRequestGetOffset(requestId); - result.match([&](const MTPDupload_cdnFileReuploadNeeded &data) { - auto requestData = RequestData(); - requestData.dcId = dcId(); - requestData.dcIndex = 0; - requestData.offset = offset; - const auto shiftedDcId = MTP::downloadDcId( - requestData.dcId, - requestData.dcIndex); - const auto requestId = MTP::send( - MTPupload_ReuploadCdnFile( - MTP_bytes(_cdnToken), - data.vrequest_token()), - rpcDone(&mtpFileLoader::reuploadDone), - rpcFail(&mtpFileLoader::cdnPartFailed), - shiftedDcId); - placeSentRequest(requestId, requestData); - }, [&](const MTPDupload_cdnFile &data) { - auto key = bytes::make_span(_cdnEncryptionKey); - auto iv = bytes::make_span(_cdnEncryptionIV); - Expects(key.size() == MTP::CTRState::KeySize); - Expects(iv.size() == MTP::CTRState::IvecSize); - - auto state = MTP::CTRState(); - auto ivec = bytes::make_span(state.ivec); - std::copy(iv.begin(), iv.end(), ivec.begin()); - - auto counterOffset = static_cast(offset) >> 4; - state.ivec[15] = static_cast(counterOffset & 0xFF); - state.ivec[14] = static_cast((counterOffset >> 8) & 0xFF); - state.ivec[13] = static_cast((counterOffset >> 16) & 0xFF); - state.ivec[12] = static_cast((counterOffset >> 24) & 0xFF); - - auto decryptInPlace = data.vbytes().v; - auto buffer = bytes::make_detached_span(decryptInPlace); - MTP::aesCtrEncrypt(buffer, key.data(), &state); - - switch (checkCdnFileHash(offset, buffer)) { - case CheckCdnHashResult::NoHash: { - _cdnUncheckedParts.emplace(offset, decryptInPlace); - requestMoreCdnFileHashes(); - } return; - - case CheckCdnHashResult::Invalid: { - LOG(("API Error: Wrong cdnFileHash for offset %1.").arg(offset)); - cancel(true); - } return; - - case CheckCdnHashResult::Good: { - partLoaded(offset, buffer); - } return; - } - Unexpected("Result of checkCdnFileHash()"); - }); -} - -mtpFileLoader::CheckCdnHashResult mtpFileLoader::checkCdnFileHash( - int offset, - bytes::const_span buffer) { - const auto cdnFileHashIt = _cdnFileHashes.find(offset); - if (cdnFileHashIt == _cdnFileHashes.cend()) { - return CheckCdnHashResult::NoHash; - } - const auto realHash = openssl::Sha256(buffer); - const auto receivedHash = bytes::make_span(cdnFileHashIt->second.hash); - if (bytes::compare(realHash, receivedHash)) { - return CheckCdnHashResult::Invalid; - } - return CheckCdnHashResult::Good; -} - -void mtpFileLoader::reuploadDone( - const MTPVector &result, - mtpRequestId requestId) { - auto offset = finishSentRequestGetOffset(requestId); - addCdnHashes(result.v); - makeRequest(offset); -} - -void mtpFileLoader::getCdnFileHashesDone( - const MTPVector &result, - mtpRequestId requestId) { - Expects(!_finished); - Expects(_cdnHashesRequestId == requestId); - - _cdnHashesRequestId = 0; - - const auto offset = finishSentRequestGetOffset(requestId); - addCdnHashes(result.v); - auto someMoreChecked = false; - for (auto i = _cdnUncheckedParts.begin(); i != _cdnUncheckedParts.cend();) { - const auto uncheckedOffset = i->first; - const auto uncheckedBytes = bytes::make_span(i->second); - - switch (checkCdnFileHash(uncheckedOffset, uncheckedBytes)) { - case CheckCdnHashResult::NoHash: { - ++i; - } break; - - case CheckCdnHashResult::Invalid: { - LOG(("API Error: Wrong cdnFileHash for offset %1.").arg(offset)); - cancel(true); - return; - } break; - - case CheckCdnHashResult::Good: { - someMoreChecked = true; - const auto goodOffset = uncheckedOffset; - const auto goodBytes = std::move(i->second); - const auto weak = QPointer(this); - i = _cdnUncheckedParts.erase(i); - if (!feedPart(goodOffset, bytes::make_span(goodBytes)) - || !weak) { - return; - } else if (_finished) { - notifyAboutProgress(); - return; - } - } break; - - default: Unexpected("Result of checkCdnFileHash()"); - } - } - if (someMoreChecked) { - const auto weak = QPointer(this); - notifyAboutProgress(); - if (weak) { - requestMoreCdnFileHashes(); - } - return; - } - LOG(("API Error: " - "Could not find cdnFileHash for offset %1 " - "after getCdnFileHashes request." - ).arg(offset)); - cancel(true); -} - -void mtpFileLoader::placeSentRequest( - mtpRequestId requestId, - const RequestData &requestData) { - Expects(!_finished); - - _downloader->requestedAmountIncrement( - requestData.dcId, - requestData.dcIndex, - Storage::kPartSize); - ++_queue->queriesCount; - _sentRequests.emplace(requestId, requestData); -} - -int mtpFileLoader::finishSentRequestGetOffset(mtpRequestId requestId) { - auto it = _sentRequests.find(requestId); - Assert(it != _sentRequests.cend()); - - auto requestData = it->second; - _downloader->requestedAmountIncrement( - requestData.dcId, - requestData.dcIndex, - -Storage::kPartSize); - - --_queue->queriesCount; - _sentRequests.erase(it); - - return requestData.offset; -} - -bool mtpFileLoader::feedPart(int offset, bytes::const_span buffer) { - if (!writeResultPart(offset, buffer)) { - return false; - } - if (buffer.empty() || (buffer.size() % 1024)) { // bad next offset - _lastComplete = true; - } - const auto finished = _sentRequests.empty() - && _cdnUncheckedParts.empty() - && (_lastComplete || (_size && _nextRequestOffset >= _size)); - if (finished && !finalizeResult()) { - return false; - } - return true; -} - -void mtpFileLoader::partLoaded(int offset, bytes::const_span buffer) { - if (feedPart(offset, buffer)) { - notifyAboutProgress(); - } -} - -bool mtpFileLoader::normalPartFailed( - QByteArray fileReference, - const RPCError &error, - mtpRequestId requestId) { - if (MTP::isDefaultHandledError(error)) { - return false; - } - if (error.code() == 400 - && error.type().startsWith(qstr("FILE_REFERENCE_"))) { - session().api().refreshFileReference( - _origin, - this, - requestId, - fileReference); - return true; - } - return partFailed(error, requestId); -} - - -bool mtpFileLoader::partFailed( - const RPCError &error, - mtpRequestId requestId) { - if (MTP::isDefaultHandledError(error)) { - return false; - } - cancel(true); - return true; -} - -bool mtpFileLoader::cdnPartFailed( - const RPCError &error, - mtpRequestId requestId) { - if (MTP::isDefaultHandledError(error)) { - return false; - } - - if (requestId == _cdnHashesRequestId) { - _cdnHashesRequestId = 0; - } - if (error.type() == qstr("FILE_TOKEN_INVALID") - || error.type() == qstr("REQUEST_TOKEN_INVALID")) { - auto offset = finishSentRequestGetOffset(requestId); - changeCDNParams( - offset, - 0, - QByteArray(), - QByteArray(), - QByteArray(), - QVector()); - return true; - } - return partFailed(error, requestId); -} - -void mtpFileLoader::cancelRequests() { - while (!_sentRequests.empty()) { - auto requestId = _sentRequests.begin()->first; - MTP::cancel(requestId); - finishSentRequestGetOffset(requestId); - } -} - -void mtpFileLoader::switchToCDN( - int offset, - const MTPDupload_fileCdnRedirect &redirect) { - changeCDNParams( - offset, - redirect.vdc_id().v, - redirect.vfile_token().v, - redirect.vencryption_key().v, - redirect.vencryption_iv().v, - redirect.vfile_hashes().v); -} - -void mtpFileLoader::addCdnHashes(const QVector &hashes) { - for (const auto &hash : hashes) { - hash.match([&](const MTPDfileHash &data) { - _cdnFileHashes.emplace( - data.voffset().v, - CdnFileHash{ data.vlimit().v, data.vhash().v }); - }); - } -} - -void mtpFileLoader::changeCDNParams( - int offset, - MTP::DcId dcId, - const QByteArray &token, - const QByteArray &encryptionKey, - const QByteArray &encryptionIV, - const QVector &hashes) { - if (dcId != 0 - && (encryptionKey.size() != MTP::CTRState::KeySize - || encryptionIV.size() != MTP::CTRState::IvecSize)) { - LOG(("Message Error: Wrong key (%1) / iv (%2) size in CDN params").arg(encryptionKey.size()).arg(encryptionIV.size())); - cancel(true); - return; - } - - auto resendAllRequests = (_cdnDcId != dcId - || _cdnToken != token - || _cdnEncryptionKey != encryptionKey - || _cdnEncryptionIV != encryptionIV); - _cdnDcId = dcId; - _cdnToken = token; - _cdnEncryptionKey = encryptionKey; - _cdnEncryptionIV = encryptionIV; - addCdnHashes(hashes); - - if (resendAllRequests && !_sentRequests.empty()) { - auto resendOffsets = std::vector(); - resendOffsets.reserve(_sentRequests.size()); - while (!_sentRequests.empty()) { - auto requestId = _sentRequests.begin()->first; - MTP::cancel(requestId); - auto resendOffset = finishSentRequestGetOffset(requestId); - resendOffsets.push_back(resendOffset); - } - for (auto resendOffset : resendOffsets) { - makeRequest(resendOffset); - } - } - makeRequest(offset); -} - -Storage::Cache::Key mtpFileLoader::cacheKey() const { - return _location.match([&](const WebFileLocation &location) { - return Data::WebDocumentCacheKey(location); - }, [&](const GeoPointLocation &location) { - return Data::GeoPointCacheKey(location); - }, [&](const StorageFileLocation &location) { - return location.cacheKey(); - }); -} - -std::optional mtpFileLoader::fileLocationKey() const { - if (_locationType != UnknownFileLocation) { - return mediaKey(_locationType, dcId(), objId()); - } - return std::nullopt; -} - -mtpFileLoader::~mtpFileLoader() { - cancelRequests(); -} - -webFileLoader::webFileLoader( - const QString &url, - const QString &to, - LoadFromCloudSetting fromCloud, - bool autoLoading, - uint8 cacheTag) -: FileLoader( - QString(), - 0, - UnknownFileLocation, - LoadToCacheAsWell, - fromCloud, - autoLoading, - cacheTag) -, _url(url) { - _queue = _downloader->queueForWeb(); -} - -bool webFileLoader::loadPart() { - if (_finished - || _requestSent - || _webLoadManager == FinishedWebLoadManager) { - return false; - } - if (!_webLoadManager) { - _webLoadMainManager = new WebLoadMainManager(); - - _webLoadThread = new QThread(); - _webLoadManager = new WebLoadManager(_webLoadThread); - - _webLoadThread->start(); - } - - _requestSent = true; - _webLoadManager->append(this, _url); - return false; -} - -int webFileLoader::currentOffset() const { - return _already; -} - -void webFileLoader::loadProgress(qint64 already, qint64 size) { - _size = size; - _already = already; - notifyAboutProgress(); -} - -void webFileLoader::loadFinished(const QByteArray &data) { - if (writeResultPart(0, bytes::make_span(data))) { - if (finalizeResult()) { - notifyAboutProgress(); - } - } -} - -void webFileLoader::loadError() { - cancel(true); -} - -Storage::Cache::Key webFileLoader::cacheKey() const { - return Data::UrlCacheKey(_url); -} - -std::optional webFileLoader::fileLocationKey() const { - return std::nullopt; -} - -void webFileLoader::cancelRequests() { - if (!webLoadManager()) return; - webLoadManager()->stop(this); -} - -webFileLoader::~webFileLoader() { -} - -class webFileLoaderPrivate { -public: - webFileLoaderPrivate(webFileLoader *loader, const QString &url) - : _interface(loader) - , _url(url) - , _redirectsLeft(kMaxHttpRedirects) { - } - - QNetworkReply *reply() { - return _reply; - } - - QNetworkReply *request(QNetworkAccessManager &manager, const QString &redirect) { - if (!redirect.isEmpty()) _url = redirect; - - QNetworkRequest req(_url); - QByteArray rangeHeaderValue = "bytes=" + QByteArray::number(_already) + "-"; - req.setRawHeader("Range", rangeHeaderValue); - _reply = manager.get(req); - return _reply; - } - - bool oneMoreRedirect() { - if (_redirectsLeft) { - --_redirectsLeft; - return true; - } - return false; - } - - void setData(const QByteArray &data) { - _data = data; - } - void addData(const QByteArray &data) { - _data.append(data); - } - const QByteArray &data() { - return _data; - } - void setProgress(qint64 already, qint64 size) { - _already = already; - _size = qMax(size, 0LL); - } - - qint64 size() const { - return _size; - } - qint64 already() const { - return _already; - } - -private: - static constexpr auto kMaxHttpRedirects = 5; - - webFileLoader *_interface = nullptr; - QUrl _url; - qint64 _already = 0; - qint64 _size = 0; - QNetworkReply *_reply = nullptr; - int32 _redirectsLeft = kMaxHttpRedirects; - QByteArray _data; - - friend class WebLoadManager; -}; - -void stopWebLoadManager() { - if (webLoadManager()) { - _webLoadThread->quit(); - DEBUG_LOG(("Waiting for webloadThread to finish")); - _webLoadThread->wait(); - delete _webLoadManager; - delete _webLoadMainManager; - delete _webLoadThread; - _webLoadThread = nullptr; - _webLoadMainManager = nullptr; - _webLoadManager = FinishedWebLoadManager; - } -} - -WebLoadManager::WebLoadManager(QThread *thread) { - moveToThread(thread); - _manager.moveToThread(thread); - connect(thread, SIGNAL(started()), this, SLOT(process())); - connect(thread, SIGNAL(finished()), this, SLOT(finish())); - connect(this, SIGNAL(processDelayed()), this, SLOT(process()), Qt::QueuedConnection); - - connect(this, SIGNAL(progress(webFileLoader*,qint64,qint64)), _webLoadMainManager, SLOT(progress(webFileLoader*,qint64,qint64))); - connect(this, SIGNAL(finished(webFileLoader*,QByteArray)), _webLoadMainManager, SLOT(finished(webFileLoader*,QByteArray))); - connect(this, SIGNAL(error(webFileLoader*)), _webLoadMainManager, SLOT(error(webFileLoader*))); - - connect(&_manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), this, SLOT(onFailed(QNetworkReply*))); -#ifndef OS_MAC_OLD - connect(&_manager, SIGNAL(sslErrors(QNetworkReply*,const QList&)), this, SLOT(onFailed(QNetworkReply*))); -#endif // OS_MAC_OLD -} - -void WebLoadManager::append(webFileLoader *loader, const QString &url) { - loader->_private = new webFileLoaderPrivate(loader, url); - - QMutexLocker lock(&_loaderPointersMutex); - _loaderPointers.insert(loader, loader->_private); - emit processDelayed(); -} - -void WebLoadManager::stop(webFileLoader *loader) { - QMutexLocker lock(&_loaderPointersMutex); - _loaderPointers.remove(loader); - emit processDelayed(); -} - -bool WebLoadManager::carries(webFileLoader *loader) const { - QMutexLocker lock(&_loaderPointersMutex); - return _loaderPointers.contains(loader); -} - -bool WebLoadManager::handleReplyResult(webFileLoaderPrivate *loader, WebReplyProcessResult result) { - QMutexLocker lock(&_loaderPointersMutex); - LoaderPointers::iterator it = _loaderPointers.find(loader->_interface); - if (it != _loaderPointers.cend() && it.key()->_private != loader) { - it = _loaderPointers.end(); // it is a new loader which was realloced in the same address - } - if (it == _loaderPointers.cend()) { - return false; - } - - if (result == WebReplyProcessProgress) { - if (loader->size() > Storage::kMaxFileInMemory) { - LOG(("API Error: too large file is loaded to cache: %1").arg(loader->size())); - result = WebReplyProcessError; - } - } - if (result == WebReplyProcessError) { - if (it != _loaderPointers.cend()) { - emit error(it.key()); - } - return false; - } - if (loader->already() < loader->size() || !loader->size()) { - emit progress(it.key(), loader->already(), loader->size()); - return true; - } - emit finished(it.key(), loader->data()); - return false; -} - -void WebLoadManager::onFailed(QNetworkReply::NetworkError error) { - onFailed(qobject_cast(QObject::sender())); -} - -void WebLoadManager::onFailed(QNetworkReply *reply) { - if (!reply) return; - reply->deleteLater(); - - Replies::iterator j = _replies.find(reply); - if (j == _replies.cend()) { // handled already - return; - } - webFileLoaderPrivate *loader = j.value(); - _replies.erase(j); - - LOG(("Network Error: Failed to request '%1', error %2 (%3)").arg(QString::fromLatin1(loader->_url.toEncoded())).arg(int(reply->error())).arg(reply->errorString())); - - if (!handleReplyResult(loader, WebReplyProcessError)) { - _loaders.remove(loader); - delete loader; - } -} - -void WebLoadManager::onProgress(qint64 already, qint64 size) { - const auto reply = qobject_cast(QObject::sender()); - if (!reply) return; - - const auto j = _replies.find(reply); - if (j == _replies.cend()) { // handled already - return; - } - const auto loader = j.value(); - - auto result = WebReplyProcessProgress; - const auto statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); - const auto status = statusCode.isValid() ? statusCode.toInt() : 200; - if (status != 200 && status != 206 && status != 416) { - if (status == 301 || status == 302) { - QString loc = reply->header(QNetworkRequest::LocationHeader).toString(); - if (!loc.isEmpty()) { - if (loader->oneMoreRedirect()) { - sendRequest(loader, loc); - return; - } else { - LOG(("Network Error: Too many HTTP redirects in onFinished() for web file loader: %1").arg(loc)); - result = WebReplyProcessError; - } - } - } else { - LOG(("Network Error: Bad HTTP status received in WebLoadManager::onProgress(): %1").arg(statusCode.toInt())); - result = WebReplyProcessError; - } - } else { - loader->setProgress(already, size); - QByteArray r = reply->readAll(); - if (!r.isEmpty()) { - loader->addData(r); - } - if (size == 0) { - LOG(("Network Error: Zero size received for HTTP download progress in WebLoadManager::onProgress(): %1 / %2").arg(already).arg(size)); - result = WebReplyProcessError; - } - } - if (!handleReplyResult(loader, result)) { - _replies.erase(j); - _loaders.remove(loader); - delete loader; - - reply->abort(); - reply->deleteLater(); - } -} - -void WebLoadManager::onMeta() { - const auto reply = qobject_cast(QObject::sender()); - if (!reply) return; - - const auto j = _replies.find(reply); - if (j == _replies.cend()) { // handled already - return; - } - const auto loader = j.value(); - - const auto pairs = reply->rawHeaderPairs(); - for (const auto &pair : pairs) { - if (QString::fromUtf8(pair.first).toLower() == "content-range") { - const auto m = QRegularExpression(qsl("/(\\d+)([^\\d]|$)")).match(QString::fromUtf8(pair.second)); - if (m.hasMatch()) { - loader->setProgress(qMax(qint64(loader->data().size()), loader->already()), m.captured(1).toLongLong()); - if (!handleReplyResult(loader, WebReplyProcessProgress)) { - _replies.erase(j); - _loaders.remove(loader); - delete loader; - - reply->abort(); - reply->deleteLater(); - } - } - } - } -} - -void WebLoadManager::process() { - Loaders newLoaders; - { - QMutexLocker lock(&_loaderPointersMutex); - for (LoaderPointers::iterator i = _loaderPointers.begin(), e = _loaderPointers.end(); i != e; ++i) { - Loaders::iterator it = _loaders.find(i.value()); - if (i.value()) { - if (it == _loaders.cend()) { - _loaders.insert(i.value()); - newLoaders.insert(i.value()); - } - i.value() = 0; - } - } - for (auto i = _loaders.begin(), e = _loaders.end(); i != e;) { - LoaderPointers::iterator it = _loaderPointers.find((*i)->_interface); - if (it != _loaderPointers.cend() && it.key()->_private != (*i)) { - it = _loaderPointers.end(); - } - if (it == _loaderPointers.cend()) { - if (QNetworkReply *reply = (*i)->reply()) { - _replies.remove(reply); - reply->abort(); - reply->deleteLater(); - } - delete (*i); - i = _loaders.erase(i); - } else { - ++i; - } - } - } - for_const (webFileLoaderPrivate *loader, newLoaders) { - if (_loaders.contains(loader)) { - sendRequest(loader); - } - } -} - -void WebLoadManager::sendRequest(webFileLoaderPrivate *loader, const QString &redirect) { - Replies::iterator j = _replies.find(loader->reply()); - if (j != _replies.cend()) { - QNetworkReply *r = j.key(); - _replies.erase(j); - - r->abort(); - r->deleteLater(); - } - - QNetworkReply *r = loader->request(_manager, redirect); - - // Those use QObject::sender, so don't just remove the receiver pointer! - connect(r, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(onProgress(qint64, qint64))); - connect(r, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onFailed(QNetworkReply::NetworkError))); - connect(r, SIGNAL(metaDataChanged()), this, SLOT(onMeta())); - - _replies.insert(r, loader); -} - -void WebLoadManager::finish() { - clear(); -} - -void WebLoadManager::clear() { - QMutexLocker lock(&_loaderPointersMutex); - for (auto i = _loaderPointers.begin(), e = _loaderPointers.end(); i != e; ++i) { - if (i.value()) { - i.key()->_private = nullptr; - } - } - _loaderPointers.clear(); - - for (const auto loader : _loaders) { - delete loader; - } - _loaders.clear(); - - for (auto i = _replies.begin(), e = _replies.end(); i != e; ++i) { - delete i.key(); - } - _replies.clear(); -} - -WebLoadManager::~WebLoadManager() { - clear(); -} - -void WebLoadMainManager::progress(webFileLoader *loader, qint64 already, qint64 size) { - if (webLoadManager() && webLoadManager()->carries(loader)) { - loader->loadProgress(already, size); - } -} - -void WebLoadMainManager::finished(webFileLoader *loader, QByteArray data) { - if (webLoadManager() && webLoadManager()->carries(loader)) { - loader->loadFinished(data); - } -} - -void WebLoadMainManager::error(webFileLoader *loader) { - if (webLoadManager() && webLoadManager()->carries(loader)) { - loader->loadError(); - } -} diff --git a/Telegram/SourceFiles/storage/file_download.h b/Telegram/SourceFiles/storage/file_download.h index ce492fc51..d68b81352 100644 --- a/Telegram/SourceFiles/storage/file_download.h +++ b/Telegram/SourceFiles/storage/file_download.h @@ -10,11 +10,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/observer.h" #include "base/timer.h" #include "base/binary_guard.h" -#include "data/data_file_origin.h" #include -class ApiWrap; +namespace Data { +struct FileOrigin; +} // namespace Data namespace Main { class Session; @@ -25,68 +26,22 @@ namespace Cache { struct Key; } // namespace Cache +// 10 MB max file could be hold in memory // This value is used in local cache database settings! -constexpr auto kMaxFileInMemory = 10 * 1024 * 1024; // 10 MB max file could be hold in memory +constexpr auto kMaxFileInMemory = 10 * 1024 * 1024; -constexpr auto kMaxVoiceInMemory = 2 * 1024 * 1024; // 2 MB audio is hold in memory and auto loaded -constexpr auto kMaxStickerInMemory = 2 * 1024 * 1024; // 2 MB stickers hold in memory, auto loaded and displayed inline +// 2 MB audio is hold in memory and auto loaded +constexpr auto kMaxVoiceInMemory = 2 * 1024 * 1024; + +// 2 MB stickers hold in memory, auto loaded and displayed inline +constexpr auto kMaxStickerInMemory = 2 * 1024 * 1024; + +// 10 MB GIF and mp4 animations held in memory while playing constexpr auto kMaxWallPaperInMemory = kMaxFileInMemory; -constexpr auto kMaxAnimationInMemory = kMaxFileInMemory; // 10 MB gif and mp4 animations held in memory while playing -constexpr auto kMaxWallPaperDimension = 4096; // 4096x4096 is max area. +constexpr auto kMaxAnimationInMemory = kMaxFileInMemory; -class Downloader final { -public: - struct Queue { - Queue(int queriesLimit) : queriesLimit(queriesLimit) { - } - int queriesCount = 0; - int queriesLimit = 0; - FileLoader *start = nullptr; - FileLoader *end = nullptr; - }; - - explicit Downloader(not_null api); - ~Downloader(); - - ApiWrap &api() const { - return *_api; - } - - int currentPriority() const { - return _priority; - } - void clearPriorities(); - - base::Observable &taskFinished() { - return _taskFinishedObservable; - } - - void requestedAmountIncrement(MTP::DcId dcId, int index, int amount); - int chooseDcIndexForRequest(MTP::DcId dcId) const; - - not_null queueForDc(MTP::DcId dcId); - not_null queueForWeb(); - -private: - void killDownloadSessionsStart(MTP::DcId dcId); - void killDownloadSessionsStop(MTP::DcId dcId); - void killDownloadSessions(); - - not_null _api; - - base::Observable _taskFinishedObservable; - int _priority = 1; - - using RequestedInDc = std::array; - std::map _requestedBytesAmount; - - base::flat_map _killDownloadSessionTimes; - base::Timer _killDownloadSessionsTimer; - - std::map _queuesForDc; - Queue _queueForWeb; - -}; +// 4096x4096 is max area. +constexpr auto kMaxWallPaperDimension = 4096; } // namespace Storage @@ -99,9 +54,6 @@ struct StorageImageSaved { }; -class mtpFileLoader; -class webFileLoader; - class FileLoader : public QObject { Q_OBJECT @@ -114,8 +66,9 @@ public: LoadFromCloudSetting fromCloud, bool autoLoading, uint8 cacheTag); + virtual ~FileLoader(); - Main::Session &session() const; + [[nodiscard]] Main::Session &session() const; bool finished() const { return _finished; @@ -135,7 +88,8 @@ public: QString fileName() const { return _filename; } - virtual Data::FileOrigin fileOrigin() const; + // Used in MainWidget::documentLoadFailed. + [[nodiscard]] virtual Data::FileOrigin fileOrigin() const; float64 currentProgress() const; virtual int currentOffset() const; int fullSize() const; @@ -146,12 +100,6 @@ public: void start(); void cancel(); - bool loading() const { - return _inQueue; - } - bool started() const { - return _inQueue; - } bool loadingLocal() const { return (_localStatus == LocalStatus::Loading); } @@ -159,10 +107,6 @@ public: return _autoLoading; } - virtual void stop() { - } - virtual ~FileLoader(); - void localLoaded( const StorageImageSaved &result, const QByteArray &imageFormat, @@ -173,8 +117,6 @@ signals: void failed(FileLoader *loader, bool started); protected: - using Queue = Storage::Downloader::Queue; - enum class LocalStatus { NotTried, NotFound, @@ -188,29 +130,21 @@ protected: void loadLocal(const Storage::Cache::Key &key); virtual Storage::Cache::Key cacheKey() const = 0; virtual std::optional fileLocationKey() const = 0; - virtual void cancelRequests() = 0; + virtual void cancelHook() = 0; + virtual void startLoading() = 0; - void startLoading(); - void removeFromQueue(); void cancel(bool failed); void notifyAboutProgress(); - static void LoadNextFromQueue(not_null queue); - virtual bool loadPart() = 0; bool writeResultPart(int offset, bytes::const_span buffer); bool finalizeResult(); [[nodiscard]] QByteArray readLoadedPartBack(int offset, int size); - not_null _downloader; - FileLoader *_prev = nullptr; - FileLoader *_next = nullptr; - int _priority = 0; - Queue *_queue = nullptr; + const not_null _session; bool _autoLoading = false; uint8 _cacheTag = 0; - bool _inQueue = false; bool _finished = false; bool _cancelled = false; mutable LocalStatus _localStatus = LocalStatus::NotTried; @@ -233,220 +167,3 @@ protected: mutable QImage _imageData; }; - -class StorageImageLocation; -class WebFileLocation; -class mtpFileLoader : public FileLoader, public RPCSender { -public: - mtpFileLoader( - const StorageFileLocation &location, - Data::FileOrigin origin, - LocationType type, - const QString &toFile, - int32 size, - LoadToCacheSetting toCache, - LoadFromCloudSetting fromCloud, - bool autoLoading, - uint8 cacheTag); - mtpFileLoader( - const WebFileLocation &location, - int32 size, - LoadFromCloudSetting fromCloud, - bool autoLoading, - uint8 cacheTag); - mtpFileLoader( - const GeoPointLocation &location, - int32 size, - LoadFromCloudSetting fromCloud, - bool autoLoading, - uint8 cacheTag); - - Data::FileOrigin fileOrigin() const override; - - uint64 objId() const override; - - void stop() override { - rpcInvalidate(); - } - void refreshFileReferenceFrom( - const Data::UpdatedFileReferences &updates, - int requestId, - const QByteArray ¤t); - - ~mtpFileLoader(); - -private: - struct RequestData { - MTP::DcId dcId = 0; - int dcIndex = 0; - int offset = 0; - }; - struct CdnFileHash { - CdnFileHash(int limit, QByteArray hash) : limit(limit), hash(hash) { - } - int limit = 0; - QByteArray hash; - }; - Storage::Cache::Key cacheKey() const override; - std::optional fileLocationKey() const override; - void cancelRequests() override; - - MTP::DcId dcId() const; - RequestData prepareRequest(int offset) const; - void makeRequest(int offset); - - bool loadPart() override; - void normalPartLoaded(const MTPupload_File &result, mtpRequestId requestId); - void webPartLoaded(const MTPupload_WebFile &result, mtpRequestId requestId); - void cdnPartLoaded(const MTPupload_CdnFile &result, mtpRequestId requestId); - void reuploadDone(const MTPVector &result, mtpRequestId requestId); - void requestMoreCdnFileHashes(); - void getCdnFileHashesDone(const MTPVector &result, mtpRequestId requestId); - - void partLoaded(int offset, bytes::const_span buffer); - bool feedPart(int offset, bytes::const_span buffer); - - bool partFailed(const RPCError &error, mtpRequestId requestId); - bool normalPartFailed(QByteArray fileReference, const RPCError &error, mtpRequestId requestId); - bool cdnPartFailed(const RPCError &error, mtpRequestId requestId); - - mtpRequestId sendRequest(const RequestData &requestData); - void placeSentRequest(mtpRequestId requestId, const RequestData &requestData); - int finishSentRequestGetOffset(mtpRequestId requestId); - void switchToCDN(int offset, const MTPDupload_fileCdnRedirect &redirect); - void addCdnHashes(const QVector &hashes); - void changeCDNParams(int offset, MTP::DcId dcId, const QByteArray &token, const QByteArray &encryptionKey, const QByteArray &encryptionIV, const QVector &hashes); - - enum class CheckCdnHashResult { - NoHash, - Invalid, - Good, - }; - CheckCdnHashResult checkCdnFileHash(int offset, bytes::const_span buffer); - - std::map _sentRequests; - - bool _lastComplete = false; - int32 _nextRequestOffset = 0; - - base::variant< - StorageFileLocation, - WebFileLocation, - GeoPointLocation> _location; - - Data::FileOrigin _origin; - - MTP::DcId _cdnDcId = 0; - QByteArray _cdnToken; - QByteArray _cdnEncryptionKey; - QByteArray _cdnEncryptionIV; - std::map _cdnFileHashes; - std::map _cdnUncheckedParts; - mtpRequestId _cdnHashesRequestId = 0; - -}; - -class webFileLoaderPrivate; - -class webFileLoader : public FileLoader { -public: - webFileLoader( - const QString &url, - const QString &to, - LoadFromCloudSetting fromCloud, - bool autoLoading, - uint8 cacheTag); - - int currentOffset() const override; - - void loadProgress(qint64 already, qint64 size); - void loadFinished(const QByteArray &data); - void loadError(); - - void stop() override { - cancelRequests(); - } - - ~webFileLoader(); - -protected: - void cancelRequests() override; - Storage::Cache::Key cacheKey() const override; - std::optional fileLocationKey() const override; - bool loadPart() override; - - QString _url; - - bool _requestSent = false; - int32 _already = 0; - - friend class WebLoadManager; - webFileLoaderPrivate *_private = nullptr; - -}; - -enum WebReplyProcessResult { - WebReplyProcessError, - WebReplyProcessProgress, - WebReplyProcessFinished, -}; - -class WebLoadManager : public QObject { - Q_OBJECT - -public: - WebLoadManager(QThread *thread); - - void append(webFileLoader *loader, const QString &url); - void stop(webFileLoader *reader); - bool carries(webFileLoader *reader) const; - - ~WebLoadManager(); - -signals: - void processDelayed(); - - void progress(webFileLoader *loader, qint64 already, qint64 size); - void finished(webFileLoader *loader, QByteArray data); - void error(webFileLoader *loader); - -public slots: - void onFailed(QNetworkReply *reply); - void onFailed(QNetworkReply::NetworkError error); - void onProgress(qint64 already, qint64 size); - void onMeta(); - - void process(); - void finish(); - -private: - void clear(); - void sendRequest(webFileLoaderPrivate *loader, const QString &redirect = QString()); - bool handleReplyResult(webFileLoaderPrivate *loader, WebReplyProcessResult result); - - QNetworkAccessManager _manager; - typedef QMap LoaderPointers; - LoaderPointers _loaderPointers; - mutable QMutex _loaderPointersMutex; - - typedef OrderedSet Loaders; - Loaders _loaders; - - typedef QMap Replies; - Replies _replies; - -}; - -class WebLoadMainManager : public QObject { - Q_OBJECT - -public slots: - void progress(webFileLoader *loader, qint64 already, qint64 size); - void finished(webFileLoader *loader, QByteArray data); - void error(webFileLoader *loader); - -}; - -static WebLoadManager * const FinishedWebLoadManager = SharedMemoryLocation(); - -void stopWebLoadManager(); diff --git a/Telegram/SourceFiles/ui/twidget.cpp b/Telegram/SourceFiles/ui/twidget.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/Telegram/ThirdParty/crl/.gitattributes b/Telegram/ThirdParty/crl/.gitattributes deleted file mode 100644 index 1ff0c4230..000000000 --- a/Telegram/ThirdParty/crl/.gitattributes +++ /dev/null @@ -1,63 +0,0 @@ -############################################################################### -# Set default behavior to automatically normalize line endings. -############################################################################### -* text=auto - -############################################################################### -# Set default behavior for command prompt diff. -# -# This is need for earlier builds of msysgit that does not have it on by -# default for csharp files. -# Note: This is only used by command line -############################################################################### -#*.cs diff=csharp - -############################################################################### -# Set the merge driver for project and solution files -# -# Merging from the command prompt will add diff markers to the files if there -# are conflicts (Merging from VS is not affected by the settings below, in VS -# the diff markers are never inserted). Diff markers may cause the following -# file extensions to fail to load in VS. An alternative would be to treat -# these files as binary and thus will always conflict and require user -# intervention with every merge. To do so, just uncomment the entries below -############################################################################### -#*.sln merge=binary -#*.csproj merge=binary -#*.vbproj merge=binary -#*.vcxproj merge=binary -#*.vcproj merge=binary -#*.dbproj merge=binary -#*.fsproj merge=binary -#*.lsproj merge=binary -#*.wixproj merge=binary -#*.modelproj merge=binary -#*.sqlproj merge=binary -#*.wwaproj merge=binary - -############################################################################### -# behavior for image files -# -# image files are treated as binary by default. -############################################################################### -#*.jpg binary -#*.png binary -#*.gif binary - -############################################################################### -# diff behavior for common document formats -# -# Convert binary document formats to text before diffing them. This feature -# is only available from the command line. Turn it on by uncommenting the -# entries below. -############################################################################### -#*.doc diff=astextplain -#*.DOC diff=astextplain -#*.docx diff=astextplain -#*.DOCX diff=astextplain -#*.dot diff=astextplain -#*.DOT diff=astextplain -#*.pdf diff=astextplain -#*.PDF diff=astextplain -#*.rtf diff=astextplain -#*.RTF diff=astextplain diff --git a/Telegram/ThirdParty/crl/.gitignore b/Telegram/ThirdParty/crl/.gitignore deleted file mode 100644 index c586f2903..000000000 --- a/Telegram/ThirdParty/crl/.gitignore +++ /dev/null @@ -1,270 +0,0 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. - -# User-specific files -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ - -# Visual Studio 2015 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUNIT -*.VisualState.xml -TestResult.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# DNX -project.lock.json -project.fragment.lock.json -artifacts/ - -*_i.c -*_p.c -*_i.h -*.ilk -*.meta -*.obj -*.pch -*.pdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# JustCode is a .NET coding add-in -.JustCode - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# TODO: Comment the next line if you want to checkin your web deploy settings -# but database connection strings (with potential passwords) will be unencrypted -#*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# The packages folder can be ignored because of Package Restore -**/packages/* -# except build/, which is used as an MSBuild target. -!**/packages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/packages/repositories.config -# NuGet v3's project.json files produces more ignoreable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -node_modules/ -orleans.codegen.cs - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm - -# SQL Server files -*.mdf -*.ldf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# JetBrains Rider -.idea/ -*.sln.iml - -# CodeRush -.cr/ - -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc - -# Qt builds -[Dd]ebugQt/ -[Rr]eleaseQt/ - -# Xcode project -xcuserdata -*.xcworkspace -.DS_Store diff --git a/Telegram/ThirdParty/crl/src/crl/common/crl_common_config.h b/Telegram/ThirdParty/crl/src/crl/common/crl_common_config.h deleted file mode 100644 index 9c20b3c46..000000000 --- a/Telegram/ThirdParty/crl/src/crl/common/crl_common_config.h +++ /dev/null @@ -1,55 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#if defined _MSC_VER -#define CRL_USE_WINAPI_TIME -#elif defined __APPLE__ // _MSC_VER -#define CRL_USE_MAC_TIME -#else // __APPLE__ -#define CRL_USE_LINUX_TIME -#endif // !_MSC_VER && !__APPLE__ - -#if defined _MSC_VER && !defined CRL_FORCE_QT - -#if defined _WIN64 -#define CRL_USE_WINAPI -#define CRL_WINAPI_X64 -#elif defined _M_IX86 // _WIN64 -#define CRL_USE_WINAPI -#define CRL_WINAPI_X86 -#else // _M_IX86 -#error "Configuration is not supported." -#endif // !_WIN64 && !_M_IX86 - -#ifdef CRL_FORCE_STD_LIST -#define CRL_USE_COMMON_LIST -#else // CRL_FORCE_STD_LIST -#define CRL_USE_WINAPI_LIST -#endif // !CRL_FORCE_STD_LIST - -#elif defined __APPLE__ && !defined CRL_FORCE_QT // _MSC_VER && !CRL_FORCE_QT - -#define CRL_USE_DISPATCH - -#ifdef CRL_USE_COMMON_QUEUE -#define CRL_USE_COMMON_LIST -#endif // CRL_USE_COMMON_QUEUE - -#elif __has_include() // __APPLE__ && !CRL_FORCE_QT - -#define CRL_USE_QT -#define CRL_USE_COMMON_LIST - -#else // Qt -#error "Configuration is not supported." -#endif // !_MSC_VER && !__APPLE__ && !Qt - -#if __has_include() -#define CRL_ENABLE_RPL_INTEGRATION -#endif // __has_include() diff --git a/Telegram/ThirdParty/crl/src/crl/common/crl_common_guards.h b/Telegram/ThirdParty/crl/src/crl/common/crl_common_guards.h deleted file mode 100644 index 52257e948..000000000 --- a/Telegram/ThirdParty/crl/src/crl/common/crl_common_guards.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include - -#include - -namespace crl { - -template -struct guard_traits; - - -template -struct guard_traits, void> { - static std::weak_ptr create(const std::weak_ptr &value) { - return value; - } - static std::weak_ptr create(std::weak_ptr &&value) { - return std::move(value); - } - static bool check(const std::weak_ptr &guard) { - return guard.lock() != nullptr; - } - -}; - -template -struct guard_traits, void> { - static std::weak_ptr create(const std::shared_ptr &value) { - return value; - } - static std::weak_ptr create(std::shared_ptr &&value) { - return value; - } - static bool check(const std::weak_ptr &guard) { - return guard.lock() != nullptr; - } - -}; - -} // namespace crl diff --git a/Telegram/ThirdParty/crl/src/crl/common/crl_common_list.cpp b/Telegram/ThirdParty/crl/src/crl/common/crl_common_list.cpp deleted file mode 100644 index e2a805de7..000000000 --- a/Telegram/ThirdParty/crl/src/crl/common/crl_common_list.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include - -#if defined CRL_USE_COMMON_LIST - -namespace crl::details { - -list::list() : _alive(new bool(true)) { -} - -auto list::ReverseList(BasicEntry *entry, BasicEntry *next) -> BasicEntry* { - entry->next = nullptr; - do { - auto third = next->next; - next->next = entry; - entry = next; - next = third; - } while (next); - return entry; -} - -bool list::push_entry(BasicEntry *entry) { - auto head = (BasicEntry*)nullptr; - while (true) { - if (_head.compare_exchange_weak(head, entry)) { - return (head == nullptr); - } - entry->next = head; - } -} - -bool list::empty() const { - return (_head == nullptr); -} - -bool list::process() { - if (auto entry = _head.exchange(nullptr)) { - const auto alive = _alive; - if (const auto next = entry->next) { - entry = ReverseList(entry, next); - } - do { - const auto basic = entry; - entry = entry->next; - basic->process(basic); - if (!*alive) { - delete alive; - return false; - } - } while (entry); - } - return true; -} - -list::~list() { - *_alive = false; -} - -} // namespace crl::details - -#endif // CRL_USE_COMMON_LIST diff --git a/Telegram/ThirdParty/crl/src/crl/common/crl_common_list.h b/Telegram/ThirdParty/crl/src/crl/common/crl_common_list.h deleted file mode 100644 index e2e3ca5fc..000000000 --- a/Telegram/ThirdParty/crl/src/crl/common/crl_common_list.h +++ /dev/null @@ -1,91 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include - -#if defined CRL_USE_WINAPI_LIST - -#include - -#elif defined CRL_USE_COMMON_LIST // CRL_USE_WINAPI_LIST - -#include -#include -#include - -namespace crl::details { - -class list { -public: - list(); - - template - bool push_is_first(Callable &&callable) { - return push_entry(AllocateEntry(std::forward(callable))); - } - bool process(); - bool empty() const; - - ~list(); - -private: - struct BasicEntry; - using ProcessEntryMethod = void(*)(BasicEntry *entry); - - struct BasicEntry { - BasicEntry(ProcessEntryMethod method) : process(method) { - } - - BasicEntry *next = nullptr; - ProcessEntryMethod process = nullptr; - }; - - template - struct Entry : BasicEntry { - Entry(Function &&function) - : BasicEntry(Entry::Process) - , function(std::move(function)) { - } - Entry(const Function &function) - : BasicEntry(Entry::Process) - , function(function) { - } - Function function; - - static void Process(BasicEntry *entry) { - auto full = static_cast(entry); - auto guard = details::finally([=] { delete full; }); - full->function(); - } - - }; - - template - static Entry> *AllocateEntry( - Callable &&callable) { - using Function = std::decay_t; - using Type = Entry; - - return new Type(std::forward(callable)); - } - - static BasicEntry *ReverseList(BasicEntry *entry, BasicEntry *next); - - bool push_entry(BasicEntry *entry); - - std::atomic _head = nullptr; - bool *_alive = nullptr; - -}; - -} // namespace crl::details - -#elif !defined CRL_USE_DISPATCH // CRL_USE_COMMON_LIST -#error "Configuration is not supported." -#endif // !CRL_USE_WINAPI_LIST && !CRL_USE_COMMON_LIST diff --git a/Telegram/ThirdParty/crl/src/crl/common/crl_common_on_main.cpp b/Telegram/ThirdParty/crl/src/crl/common/crl_common_on_main.cpp deleted file mode 100644 index db4309468..000000000 --- a/Telegram/ThirdParty/crl/src/crl/common/crl_common_on_main.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include - -#if defined CRL_USE_COMMON_QUEUE || !defined CRL_USE_DISPATCH - -#include - -namespace { - -crl::queue *Queue/* = nullptr*/; -std::atomic Counter/* = 0*/; -crl::details::main_queue_pointer Lifetime; - -} // namespace - -namespace crl::details { - -void main_queue_pointer::grab() { - auto counter = Counter.load(std::memory_order_acquire); - while (true) { - if (!counter) { - return; - } else if (Counter.compare_exchange_weak(counter, counter + 1)) { - _pointer = Queue; - return; - } - } -} - -void main_queue_pointer::ungrab() { - if (_pointer) { - if (--Counter == 0) { - delete _pointer; - } - _pointer = nullptr; - } -} - -void main_queue_pointer::create(main_queue_processor processor) { - if (Counter.load(std::memory_order_acquire) != 0) { - std::terminate(); - } - Queue = new queue(processor); - Counter.store(1, std::memory_order_release); - _pointer = Queue; -} - -} // namespace crl::details - -namespace crl { - -void init_main_queue(main_queue_processor processor) { - Lifetime.create(processor); -} - -} // namespace crl - -#endif // CRL_USE_COMMON_QUEUE || !CRL_USE_DISPATCH diff --git a/Telegram/ThirdParty/crl/src/crl/common/crl_common_on_main.h b/Telegram/ThirdParty/crl/src/crl/common/crl_common_on_main.h deleted file mode 100644 index c8fe74103..000000000 --- a/Telegram/ThirdParty/crl/src/crl/common/crl_common_on_main.h +++ /dev/null @@ -1,76 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include - -#if defined CRL_USE_COMMON_QUEUE || !defined CRL_USE_DISPATCH - -#include -#include - -namespace crl::details { - -extern queue *MainQueue; -extern std::atomic MainQueueCounter; - -class main_queue_pointer { -public: - main_queue_pointer() { - grab(); - } - - void create(main_queue_processor processor); - - explicit operator bool() const { - return _pointer != nullptr; - } - - queue *operator->() const { - return _pointer; - } - - ~main_queue_pointer() { - ungrab(); - } - -private: - void grab(); - void ungrab(); - - queue *_pointer = nullptr; - -}; - -} // namespace crl::details - -namespace crl { - -void init_main_queue(main_queue_processor processor); - -inline void wrap_main_queue(main_queue_wrapper wrapper) { - // If wrapping is needed here, it can be done inside processor. -} - -template -inline void on_main(Callable &&callable) { - if (const auto main = details::main_queue_pointer()) { - main->async(std::forward(callable)); - } -} - -template -inline void on_main_sync(Callable &&callable) { - if (const auto main = details::main_queue_pointer()) { - main->sync(std::forward(callable)); - } -} - -} // namespace crl - -#endif // CRL_USE_COMMON_QUEUE || !CRL_USE_DISPATCH diff --git a/Telegram/ThirdParty/crl/src/crl/common/crl_common_on_main_guarded.h b/Telegram/ThirdParty/crl/src/crl/common/crl_common_on_main_guarded.h deleted file mode 100644 index a335df5fc..000000000 --- a/Telegram/ThirdParty/crl/src/crl/common/crl_common_on_main_guarded.h +++ /dev/null @@ -1,117 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include - -#include -#include - -namespace crl::details { - -template -constexpr std::size_t dependent_zero = 0; - -} // namespace crl::details - -namespace crl { - -template -struct guard_traits; - -template -struct deduce_call_type_traits { - using type = decltype(&Callable::operator()); -}; - -template -using deduced_call_type = typename deduce_call_type_traits< - std::decay_t>::type; - -template -class guarded_wrap { -public: - using ClearCallable = std::decay_t; - using GuardTraits = guard_traits>; - using GuardType = decltype(GuardTraits::create(std::declval())); - - guarded_wrap(Guard &&object, Callable &&callable) - : _guard(GuardTraits::create(std::forward(object))) - , _callable(std::forward(callable)) { - } - - template < - typename ...OtherArgs, - typename Return = decltype( - std::declval()(std::declval()...))> - Return operator()(OtherArgs &&...args) { - return GuardTraits::check(_guard) - ? _callable(std::forward(args)...) - : Return(); - } - - template < - typename ...OtherArgs, - typename Return = decltype( - std::declval()(std::declval()...))> - Return operator()(OtherArgs &&...args) const { - return GuardTraits::check(_guard) - ? _callable(std::forward(args)...) - : Return(); - } - -private: - GuardType _guard; - ClearCallable _callable; - -}; - -template -struct deduce_call_type_traits> { - using type = deduced_call_type; -}; - -template < - typename Guard, - typename Callable, - typename GuardTraits = guard_traits>, - typename = std::enable_if_t< - sizeof(GuardTraits) != details::dependent_zero>> -inline auto guard(Guard &&object, Callable &&callable) --> guarded_wrap { - return { - std::forward(object), - std::forward(callable) - }; -} - -template < - typename Guard, - typename Callable, - typename GuardTraits = guard_traits>, - typename = std::enable_if_t< - sizeof(GuardTraits) != details::dependent_zero>> -inline void on_main(Guard &&object, Callable &&callable) { - return on_main(guard( - std::forward(object), - std::forward(callable))); -} - -template < - typename Guard, - typename Callable, - typename GuardTraits = guard_traits>, - typename = std::enable_if_t< - sizeof(GuardTraits) != details::dependent_zero>> -inline void on_main_sync(Guard &&object, Callable &&callable) { - return on_main_sync(guard( - std::forward(object), - std::forward(callable))); -} - -} // namespace crl diff --git a/Telegram/ThirdParty/crl/src/crl/common/crl_common_queue.cpp b/Telegram/ThirdParty/crl/src/crl/common/crl_common_queue.cpp deleted file mode 100644 index 7c942e16b..000000000 --- a/Telegram/ThirdParty/crl/src/crl/common/crl_common_queue.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include - -#if defined CRL_USE_COMMON_QUEUE || !defined CRL_USE_DISPATCH - -#include - -namespace crl { - -queue::queue() = default; - -queue::queue(main_queue_processor processor) : _main_processor(processor) { -} - -void queue::wake_async() { - auto expected = false; - if (_queued.compare_exchange_strong(expected, true)) { - (_main_processor ? _main_processor : details::async_plain)( - ProcessCallback, - static_cast(this)); - } -} - -void queue::process() { - if (!_list.process()) { - return; - } - _queued.store(false); - - if (!_list.empty()) { - wake_async(); - } -} - -void queue::ProcessCallback(void *that) { - static_cast(that)->process(); -} - -} // namespace crl - -#endif // CRL_USE_COMMON_QUEUE || !CRL_USE_DISPATCH diff --git a/Telegram/ThirdParty/crl/src/crl/common/crl_common_queue.h b/Telegram/ThirdParty/crl/src/crl/common/crl_common_queue.h deleted file mode 100644 index 9ccd6cde1..000000000 --- a/Telegram/ThirdParty/crl/src/crl/common/crl_common_queue.h +++ /dev/null @@ -1,64 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include - -#if defined CRL_USE_COMMON_QUEUE || !defined CRL_USE_DISPATCH - -#include -#include -#include - -namespace crl { -namespace details { -class main_queue_pointer; -} // namespace details - -class queue { -public: - queue(); - queue(const queue &other) = delete; - queue &operator=(const queue &other) = delete; - - template - void async(Callable &&callable) { - if (_list.push_is_first(std::forward(callable))) { - wake_async(); - } - } - - template - void sync(Callable &&callable) { - semaphore waiter; - async([&] { - const auto guard = details::finally([&] { waiter.release(); }); - callable(); - }); - waiter.acquire(); - } - -private: - friend class details::main_queue_pointer; - - static void ProcessCallback(void *that); - - queue(main_queue_processor processor); - - void wake_async(); - void process(); - - main_queue_processor _main_processor = nullptr; - details::list _list; - std::atomic _queued = false; - -}; - -} // namespace crl - -#endif // CRL_USE_COMMON_QUEUE || !CRL_USE_DISPATCH diff --git a/Telegram/ThirdParty/crl/src/crl/common/crl_common_sync.h b/Telegram/ThirdParty/crl/src/crl/common/crl_common_sync.h deleted file mode 100644 index 41f65c0c2..000000000 --- a/Telegram/ThirdParty/crl/src/crl/common/crl_common_sync.h +++ /dev/null @@ -1,24 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include - -namespace crl { - -template -inline void sync(Callable &&callable) { - semaphore waiter; - async([&] { - const auto guard = details::finally([&] { waiter.release(); }); - callable(); - }); - waiter.acquire(); -} - -} // namespace crl diff --git a/Telegram/ThirdParty/crl/src/crl/common/crl_common_utils.h b/Telegram/ThirdParty/crl/src/crl/common/crl_common_utils.h deleted file mode 100644 index 5950ed3a1..000000000 --- a/Telegram/ThirdParty/crl/src/crl/common/crl_common_utils.h +++ /dev/null @@ -1,73 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include -#include - -namespace crl { - -using main_queue_processor = void(*)(void (*callable)(void*), void *argument); -using main_queue_wrapper = void(*)(void (*callable)(void*), void *argument); - -} // namespace crl - -namespace crl::details { - -using true_t = char; -struct false_t { - char data[2]; -}; - -template -struct check_plain_function { - static false_t check(...); - static true_t check(Return(*)(Args...)); -}; - -template -constexpr bool is_plain_function_v = sizeof( - check_plain_function::check( - std::declval())) == sizeof(true_t); - -template -class finalizer { -public: - explicit finalizer(Callable &&callable) - : _callable(std::move(callable)) { - } - finalizer(const finalizer &other) = delete; - finalizer &operator=(const finalizer &other) = delete; - finalizer(finalizer &&other) - : _callable(std::move(other._callable)) - , _disabled(std::exchange(other._disabled, true)) { - } - finalizer &operator=(finalizer &&other) { - _callable = std::move(other._callable); - _disabled = std::exchange(other._disabled, true); - } - ~finalizer() { - if (!_disabled) { - _callable(); - } - } - -private: - Callable _callable; - bool _disabled = false; - -}; - -template < - typename Callable, - typename = std::enable_if_t>> -finalizer finally(Callable &&callable) { - return finalizer{ std::move(callable) }; -} - -} // namespace crl::details diff --git a/Telegram/ThirdParty/crl/src/crl/crl.h b/Telegram/ThirdParty/crl/src/crl/crl.h deleted file mode 100644 index 663350f1d..000000000 --- a/Telegram/ThirdParty/crl/src/crl/crl.h +++ /dev/null @@ -1,15 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include -#include -#include -#include -#include -#include diff --git a/Telegram/ThirdParty/crl/src/crl/crl_async.h b/Telegram/ThirdParty/crl/src/crl/crl_async.h deleted file mode 100644 index 40a272791..000000000 --- a/Telegram/ThirdParty/crl/src/crl/crl_async.h +++ /dev/null @@ -1,20 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include - -#if defined CRL_USE_WINAPI -#include -#elif defined CRL_USE_DISPATCH // CRL_USE_WINAPI -#include -#elif defined CRL_USE_QT // CRL_USE_DISPATCH -#include -#else // CRL_USE_QT -#error "Configuration is not supported." -#endif // !CRL_USE_WINAPI && !CRL_USE_DISPATCH && !CRL_USE_QT diff --git a/Telegram/ThirdParty/crl/src/crl/crl_object_on_queue.h b/Telegram/ThirdParty/crl/src/crl/crl_object_on_queue.h deleted file mode 100644 index bf31f3d02..000000000 --- a/Telegram/ThirdParty/crl/src/crl/crl_object_on_queue.h +++ /dev/null @@ -1,381 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include -#include -#include -#include - -#ifdef CRL_ENABLE_RPL_INTEGRATION -#include -#endif // CRL_ENABLE_RPL_INTEGRATION - -namespace crl { -namespace details { - -template -class object_on_queue_data final - : public std::enable_shared_from_this> { -public: - using Object = Type; - - template - void construct(Args &&...args); - - template - void with(Method &&method); - - template - void with(Method &&method) const; - - template < - typename Value, - typename = std::enable_if_t>> - void destroy(Value &&value) const; - - ~object_on_queue_data(); - -private: - template - void async(Callable &&callable) const; - - Type &value(); - const Type &value() const; - - std::aligned_storage_t _storage; - mutable crl::queue _queue; - -}; - -} // namespace details - -template -class weak_on_queue final { - using data = details::object_on_queue_data>; - using my_data = std::conditional_t< - std::is_const_v, - const data, - data>; - -public: - weak_on_queue() = default; - weak_on_queue(const std::shared_ptr &strong); - weak_on_queue(const weak_on_queue &other) = default; - weak_on_queue(weak_on_queue &&other) = default; - weak_on_queue &operator=(const weak_on_queue &other) = default; - weak_on_queue &operator=(weak_on_queue &&other) = default; - - template - void with(Method &&method) const; - - template < - typename Value, - typename = std::enable_if_t>> - void destroy(Value &&value) const; - - // Returns a lambda that runs arbitrary callable on the objects queue. - // const auto r = runner(); r([] { make_some_work_on_queue(); }); - auto runner() const { - return [weak = *this](auto &&method) { - weak.with([ - method = std::forward(method) - ](Type&) mutable { - std::move(method)(); - }); - }; - } - -#ifdef CRL_ENABLE_RPL_INTEGRATION - template < - typename Method, - typename Invoke, - typename Result = decltype( - std::declval()(std::declval()))> - Result producer(Method &&method, Invoke &&invoke) const; - - template < - typename Method, - typename Result = decltype( - std::declval()(std::declval()))> - Result producer_on_main(Method &&method) const; -#endif // CRL_ENABLE_RPL_INTEGRATION - -private: - std::weak_ptr _weak; - -}; - -template -class object_on_queue final { -public: - template - object_on_queue(Args &&...args); - - object_on_queue(const object_on_queue &other) = delete; - object_on_queue &operator=(const object_on_queue &other) = delete; - - template - void with(Method &&method); - - template - void with(Method &&method) const; - - template < - typename Value, - typename = std::enable_if_t>> - void destroy(Value &&value) const; - -#ifdef CRL_ENABLE_RPL_INTEGRATION - template < - typename Method, - typename Invoke, - typename Result = decltype( - std::declval()(std::declval()))> - Result producer(Method &&method, Invoke &&invoke) const; - - template < - typename Method, - typename Result = decltype( - std::declval()(std::declval()))> - Result producer_on_main(Method &&method) const; -#endif // CRL_ENABLE_RPL_INTEGRATION - - weak_on_queue weak(); - weak_on_queue weak() const; - - ~object_on_queue(); - -private: - using Data = details::object_on_queue_data; - std::shared_ptr _data; - -}; - -namespace details { - -template -template -void object_on_queue_data::async(Callable &&callable) const { - _queue.async([ - that = this->shared_from_this(), - what = std::forward(callable) - ]() mutable { - std::move(what)(); - }); -} - -template -Type &object_on_queue_data::value() { - return *reinterpret_cast(&_storage); -} - -template -const Type &object_on_queue_data::value() const { - return *reinterpret_cast(&_storage); -} - -template -template -void object_on_queue_data::construct(Args &&...args) { - async([arguments = std::make_tuple( - &_storage, - std::forward(args)... - )]() mutable { - const auto create = [](void *storage, Args &&...args) { - new (storage) Type(std::forward(args)...); - }; - std::apply(create, std::move(arguments)); - }); -} - -template -template -void object_on_queue_data::with(Method &&method) { - async([=, method = std::forward(method)]() mutable { - std::move(method)(value()); - }); -} - -template -template -void object_on_queue_data::with(Method &&method) const { - async([=, method = std::forward(method)]() mutable { - std::move(method)(value()); - }); -} - -template -template -void object_on_queue_data::destroy(Value &&value) const { - _queue.async([moved = std::move(value)]{}); -} - -template -object_on_queue_data::~object_on_queue_data() { - value().~Type(); -} - -} // namespace details - -template -weak_on_queue::weak_on_queue(const std::shared_ptr &strong) -: _weak(strong) { -} - -template -template -void weak_on_queue::with(Method &&method) const { - if (auto strong = _weak.lock()) { - strong->with(std::move(method)); - strong->destroy(std::move(strong)); - } -} - -template -template -void weak_on_queue::destroy(Value &&value) const { - if (auto strong = _weak.lock()) { - strong->destroy(std::move(value)); - strong->destroy(std::move(strong)); - } else { - [[maybe_unused]] const auto moved = std::move(value); - } -} - -#ifdef CRL_ENABLE_RPL_INTEGRATION -template -template -Result weak_on_queue::producer( - Method &&method, - Invoke &&invoke) const { - return [ - weak = *this, - method = std::forward(method), - invoke = std::forward(invoke) - ](auto consumer) mutable { - auto lifetime_on_queue = std::make_shared(); - weak.with([ - method = std::move(method), - invoke = std::move(invoke), - consumer = std::move(consumer), - lifetime_on_queue - ](const Type &that) mutable { - method( - that - ) | rpl::start_with_next_error_done([=](auto &&value) { - invoke([ - consumer, - value = std::forward(value) - ]() mutable { - consumer.put_next(std::move(value)); - }); - }, [=](auto &&error) { - invoke([ - consumer, - error = std::forward(error) - ]() mutable { - consumer.put_error(std::move(error)); - }); - }, [=] { - invoke([=] { - consumer.put_done(); - }); - }, *lifetime_on_queue); - }); - return rpl::lifetime([ - lifetime_on_queue = std::move(lifetime_on_queue), - weak = std::move(weak) - ]() mutable { - weak.destroy(std::move(lifetime_on_queue)); - }); - }; -} - -template -template -Result weak_on_queue::producer_on_main(Method &&method) const { - return producer(std::forward(method), [](auto &&callback) { - crl::on_main(std::forward(callback)); - }); -} -#endif // CRL_ENABLE_RPL_INTEGRATION - -template -template -object_on_queue::object_on_queue(Args &&...args) -: _data(std::make_shared()) { - constexpr auto plain_construct = std::is_constructible_v< - Type, - Args...>; - constexpr auto with_weak_construct = std::is_constructible_v< - Type, - weak_on_queue, - Args...>; - if constexpr (plain_construct) { - _data->construct(std::forward(args)...); - } else if constexpr (with_weak_construct) { - _data->construct(weak(), std::forward(args)...); - } else { - static_assert(false_t(args...), "Could not find a constructor."); - } -} - -template -template -void object_on_queue::with(Method &&method) { - _data->with(std::forward(method)); -} - -template -template -void object_on_queue::with(Method &&method) const { - const auto data = static_cast(_data.get()); - data->with(std::forward(method)); -} - -template -template -void object_on_queue::destroy(Value &&value) const { - _data->destroy(std::move(value)); -} - -#ifdef CRL_ENABLE_RPL_INTEGRATION -template -template -Result object_on_queue::producer( - Method &&method, - Callback &&callback) const { - return weak().producer( - std::forward(method), - std::forward(callback)); -} - -template -template -Result object_on_queue::producer_on_main(Method &&method) const { - return weak().producer_on_main(std::forward(method)); -} -#endif // CRL_ENABLE_RPL_INTEGRATION - -template -auto object_on_queue::weak() -> weak_on_queue { - return { _data }; -} - -template -auto object_on_queue::weak() const -> weak_on_queue { - return { _data }; -} - -template -object_on_queue::~object_on_queue() { - _data->destroy(std::move(_data)); -} - -} // namespace diff --git a/Telegram/ThirdParty/crl/src/crl/crl_on_main.h b/Telegram/ThirdParty/crl/src/crl/crl_on_main.h deleted file mode 100644 index 7abc1e4bd..000000000 --- a/Telegram/ThirdParty/crl/src/crl/crl_on_main.h +++ /dev/null @@ -1,36 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include - -#if defined CRL_USE_WINAPI || defined CRL_USE_COMMON_QUEUE -#include -#elif defined CRL_USE_DISPATCH // CRL_USE_WINAPI -#include -#elif defined CRL_USE_QT // CRL_USE_DISPATCH -#include -#else // CRL_USE_QT -#error "Configuration is not supported." -#endif // !CRL_USE_WINAPI && !CRL_USE_DISPATCH && !CRL_USE_QT - -#include -#include -#include - -#ifdef CRL_ENABLE_RPL_INTEGRATION - -#include - -namespace crl { - -rpl::producer<> on_main_update_requests(); - -} // namespace crl - -#endif // CRL_ENABLE_RPL_INTEGRATION diff --git a/Telegram/ThirdParty/crl/src/crl/crl_queue.h b/Telegram/ThirdParty/crl/src/crl/crl_queue.h deleted file mode 100644 index 69648eecf..000000000 --- a/Telegram/ThirdParty/crl/src/crl/crl_queue.h +++ /dev/null @@ -1,20 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include - -#if defined CRL_USE_WINAPI || defined CRL_USE_COMMON_QUEUE -#include -#elif defined CRL_USE_DISPATCH // CRL_USE_WINAPI -#include -#elif defined CRL_USE_QT // CRL_USE_DISPATCH -#include -#else // CRL_USE_QT -#error "Configuration is not supported." -#endif // !CRL_USE_WINAPI && !CRL_USE_DISPATCH && !CRL_USE_QT diff --git a/Telegram/ThirdParty/crl/src/crl/crl_semaphore.h b/Telegram/ThirdParty/crl/src/crl/crl_semaphore.h deleted file mode 100644 index a33191241..000000000 --- a/Telegram/ThirdParty/crl/src/crl/crl_semaphore.h +++ /dev/null @@ -1,20 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include - -#if defined CRL_USE_WINAPI -#include -#elif defined CRL_USE_DISPATCH // CRL_USE_WINAPI -#include -#elif defined CRL_USE_QT // CRL_USE_DISPATCH -#include -#else // CRL_USE_QT -#error "Configuration is not supported." -#endif // !CRL_USE_WINAPI && !CRL_USE_DISPATCH && !CRL_USE_QT diff --git a/Telegram/ThirdParty/crl/src/crl/crl_time.cpp b/Telegram/ThirdParty/crl/src/crl/crl_time.cpp deleted file mode 100644 index 86227a14c..000000000 --- a/Telegram/ThirdParty/crl/src/crl/crl_time.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include - -#include -#include -#include - -namespace crl { -namespace details { -namespace { - -time LastAdjustmentTime/* = 0*/; -std::time_t LastAdjustmentUnixtime/* = 0*/; - -using seconds_type = std::uint32_t; -std::atomic AdjustSeconds/* = 0*/; - -inner_time_type StartValue/* = 0*/; -inner_profile_type StartProfileValue/* = 0*/; - -struct StaticInit { - StaticInit(); -}; - -StaticInit::StaticInit() { - StartValue = current_value(); - StartProfileValue = current_profile_value(); - - init(); - - LastAdjustmentUnixtime = ::time(nullptr); -} - -StaticInit StaticInitObject; - -bool adjust_time() { - const auto now = crl::now(); - const auto delta = (now - LastAdjustmentTime); - const auto unixtime = ::time(nullptr); - const auto real = (unixtime - LastAdjustmentUnixtime); - const auto seconds = (time(real) * 1000 - delta) / 1000; - - LastAdjustmentUnixtime = unixtime; - LastAdjustmentTime = now; - - if (seconds <= 0) { - return false; - } - auto current = seconds_type(0); - static constexpr auto max = std::numeric_limits::max(); - while (true) { - if (time(current) + seconds > time(max)) { - return false; - } - const auto next = current + seconds_type(seconds); - if (AdjustSeconds.compare_exchange_weak(current, next)) { - return true; - } - } - return false; -} - -time compute_adjustment() { - return time(AdjustSeconds.load()) * 1000; -} - -profile_time compute_profile_adjustment() { - return compute_adjustment() * 1000; -} - -} // namespace -} // namespace details - -time now() { - const auto elapsed = details::current_value() - details::StartValue; - return details::convert(elapsed) + details::compute_adjustment(); -} - -profile_time profile() { - const auto elapsed = details::current_profile_value() - - details::StartProfileValue; - return details::convert_profile(elapsed) - + details::compute_profile_adjustment(); -} - -bool adjust_time() { - return details::adjust_time(); -} - -} // namespace crl diff --git a/Telegram/ThirdParty/crl/src/crl/crl_time.h b/Telegram/ThirdParty/crl/src/crl/crl_time.h deleted file mode 100644 index 959e2285f..000000000 --- a/Telegram/ThirdParty/crl/src/crl/crl_time.h +++ /dev/null @@ -1,43 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include - -#include - -// Use after main() started. - -namespace crl { - -using time = std::int64_t; -using profile_time = std::int64_t; - -namespace details { - -using inner_time_type = std::int64_t; -using inner_profile_type = std::int64_t; - -void init(); - -inner_time_type current_value(); -time convert(inner_time_type value); - -inner_profile_type current_profile_value(); -profile_time convert_profile(inner_profile_type); - -} // namespace details - -// Thread-safe. -time now(); -profile_time profile(); - -// Returns true if some adjustment was made. -bool adjust_time(); - -} // namespace crl diff --git a/Telegram/ThirdParty/crl/src/crl/dispatch/crl_dispatch_async.cpp b/Telegram/ThirdParty/crl/src/crl/dispatch/crl_dispatch_async.cpp deleted file mode 100644 index fd240e20e..000000000 --- a/Telegram/ThirdParty/crl/src/crl/dispatch/crl_dispatch_async.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include - -#ifdef CRL_USE_DISPATCH - -#include - -namespace crl::details { - -void empty_main_wrapper(void (*callable)(void*), void *argument) { - callable(argument); -} - -main_queue_wrapper _main_wrapper = &empty_main_wrapper; - -void *background_queue_dispatch() { - return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); -} - -void *main_queue_dispatch() { - return dispatch_get_main_queue(); -} - -void on_queue_async(void *queue, void (*callable)(void*), void *argument) { - dispatch_async_f( - static_cast(queue), - argument, - callable); -} - -void on_queue_sync(void *queue, void (*callable)(void*), void *argument) { - dispatch_sync_f( - static_cast(queue), - argument, - callable); -} - -} // namespace crl::details - -#endif // CRL_USE_DISPATCH diff --git a/Telegram/ThirdParty/crl/src/crl/dispatch/crl_dispatch_async.h b/Telegram/ThirdParty/crl/src/crl/dispatch/crl_dispatch_async.h deleted file mode 100644 index 511592cda..000000000 --- a/Telegram/ThirdParty/crl/src/crl/dispatch/crl_dispatch_async.h +++ /dev/null @@ -1,94 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include - -#ifdef CRL_USE_DISPATCH - -#include -#include - -namespace crl::details { - -void *background_queue_dispatch(); -void *main_queue_dispatch(); - -void on_queue_async(void *queue, void (*callable)(void*), void *argument); -void on_queue_sync(void *queue, void (*callable)(void*), void *argument); - -template < - typename Wrapper, - typename Invoker, - typename Callable, - typename Return = decltype(std::declval()())> -inline void on_queue_invoke( - void *queue, - Invoker invoker, - Callable &&callable) { - using Function = std::decay_t; - - if constexpr (details::is_plain_function_v) { - using Plain = Return(*)(); - static_assert(sizeof(Plain) <= sizeof(void*)); - const auto copy = static_cast(callable); - invoker(queue, [](void *passed) { - Wrapper::Invoke([](void *passed) { - const auto callable = reinterpret_cast(passed); - (*callable)(); - }, passed); - }, reinterpret_cast(copy)); - } else { - const auto copy = new Function(std::forward(callable)); - invoker(queue, [](void *passed) { - Wrapper::Invoke([](void *passed) { - const auto callable = static_cast(passed); - const auto guard = details::finally([=] { delete callable; }); - (*callable)(); - }, passed); - }, static_cast(copy)); - } -} - -struct EmptyWrapper { - template - static inline void Invoke(Callable &&callable, void *argument) { - callable(argument); - } -}; - -inline void async_plain(void (*callable)(void*), void *argument) { - return on_queue_async( - background_queue_dispatch(), - callable, - argument); -} - -} // namespace crl::details - -namespace crl { - -template -inline void async(Callable &&callable) { - return details::on_queue_invoke( - details::background_queue_dispatch(), - details::on_queue_async, - std::forward(callable)); -} - -template -inline void sync(Callable &&callable) { - return details::on_queue_invoke( - details::background_queue_dispatch(), - details::on_queue_sync, - std::forward(callable)); -} - -} // namespace crl - -#endif // CRL_USE_DISPATCH diff --git a/Telegram/ThirdParty/crl/src/crl/dispatch/crl_dispatch_on_main.h b/Telegram/ThirdParty/crl/src/crl/dispatch/crl_dispatch_on_main.h deleted file mode 100644 index 82dbdc788..000000000 --- a/Telegram/ThirdParty/crl/src/crl/dispatch/crl_dispatch_on_main.h +++ /dev/null @@ -1,55 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include - -#if defined CRL_USE_DISPATCH && !defined CRL_USE_COMMON_QUEUE - -#include -#include - -namespace crl { -namespace details { - -extern main_queue_wrapper _main_wrapper; - -struct MainQueueWrapper { - static inline void Invoke(void (*callable)(void*), void *argument) { - _main_wrapper(callable, argument); - } -}; - -} // namespace details - -inline void init_main_queue(main_queue_processor processor) { -} - -inline void wrap_main_queue(main_queue_wrapper wrapper) { - details::_main_wrapper = wrapper; -} - -template -inline void on_main(Callable &&callable) { - return details::on_queue_invoke( - details::main_queue_dispatch(), - details::on_queue_async, - std::forward(callable)); -} - -template -inline void on_main_sync(Callable &&callable) { - return details::on_queue_invoke( - details::main_queue_dispatch(), - details::on_queue_sync, - std::forward(callable)); -} - -} // namespace crl - -#endif // CRL_USE_DISPATCH && !CRL_USE_COMMON_QUEUE diff --git a/Telegram/ThirdParty/crl/src/crl/dispatch/crl_dispatch_queue.cpp b/Telegram/ThirdParty/crl/src/crl/dispatch/crl_dispatch_queue.cpp deleted file mode 100644 index 5a0049fd1..000000000 --- a/Telegram/ThirdParty/crl/src/crl/dispatch/crl_dispatch_queue.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include - -#if defined CRL_USE_DISPATCH && !defined CRL_USE_COMMON_QUEUE - -#include -#include - -namespace crl { -namespace { - -dispatch_queue_t Unwrap(void *value) { - return static_cast(value); -} - -} // namespace - -auto queue::implementation::create() -> pointer { - auto result = dispatch_queue_create(nullptr, DISPATCH_QUEUE_SERIAL); - if (!result) { - std::terminate(); - } - return result; -} - -void queue::implementation::operator()(pointer value) { - if (value) { - dispatch_release(Unwrap(value)); - } -}; - -queue::queue() : _handle(implementation::create()) { -} - -void queue::async_plain(void (*callable)(void*), void *argument) { - dispatch_async_f( - Unwrap(_handle.get()), - argument, - callable); -} - -void queue::sync_plain(void (*callable)(void*), void *argument) { - dispatch_sync_f( - Unwrap(_handle.get()), - argument, - callable); -} - -} // namespace crl - -#endif // CRL_USE_DISPATCH && !CRL_USE_COMMON_QUEUE diff --git a/Telegram/ThirdParty/crl/src/crl/dispatch/crl_dispatch_queue.h b/Telegram/ThirdParty/crl/src/crl/dispatch/crl_dispatch_queue.h deleted file mode 100644 index 5360fffde..000000000 --- a/Telegram/ThirdParty/crl/src/crl/dispatch/crl_dispatch_queue.h +++ /dev/null @@ -1,87 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include - -#if defined CRL_USE_DISPATCH && !defined CRL_USE_COMMON_QUEUE - -#include -#include -#include - -namespace crl { - -class queue { -public: - queue(); - - template < - typename Callable, - typename Return = decltype(std::declval()())> - void async(Callable &&callable) { - using Function = std::decay_t; - - if constexpr (details::is_plain_function_v) { - using Plain = Return(*)(); - const auto copy = static_cast(callable); - async_plain([](void *passed) { - const auto callable = reinterpret_cast(passed); - (*callable)(); - }, reinterpret_cast(copy)); - } else { - const auto copy = new Function(std::forward(callable)); - async_plain([](void *passed) { - const auto callable = static_cast(passed); - const auto guard = details::finally([=] { delete callable; }); - (*callable)(); - }, static_cast(copy)); - } - } - - template < - typename Callable, - typename Return = decltype(std::declval()())> - void sync(Callable &&callable) { - using Function = std::decay_t; - - if constexpr (details::is_plain_function_v) { - using Plain = Return(*)(); - const auto copy = static_cast(callable); - sync_plain([](void *passed) { - const auto callable = reinterpret_cast(passed); - (*callable)(); - }, reinterpret_cast(copy)); - } else { - const auto copy = new Function(std::forward(callable)); - sync_plain([](void *passed) { - const auto callable = static_cast(passed); - const auto guard = details::finally([=] { delete callable; }); - (*callable)(); - }, static_cast(copy)); - } - } - -private: - // Hide dispatch_queue_t - struct implementation { - using pointer = void*; - static pointer create(); - void operator()(pointer value); - }; - - void async_plain(void (*callable)(void*), void *argument); - void sync_plain(void (*callable)(void*), void *argument); - - std::unique_ptr _handle; - -}; - -} // namespace crl - -#endif // CRL_USE_DISPATCH && !CRL_USE_COMMON_QUEUE diff --git a/Telegram/ThirdParty/crl/src/crl/dispatch/crl_dispatch_semaphore.cpp b/Telegram/ThirdParty/crl/src/crl/dispatch/crl_dispatch_semaphore.cpp deleted file mode 100644 index 869416a2c..000000000 --- a/Telegram/ThirdParty/crl/src/crl/dispatch/crl_dispatch_semaphore.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include - -#ifdef CRL_USE_DISPATCH - -#include -#include - -namespace crl { -namespace { - -dispatch_semaphore_t Unwrap(void *value) { - return static_cast(value); -} - -} // namespace - -auto semaphore::implementation::create() -> pointer { - auto result = dispatch_semaphore_create(0); - if (!result) { - std::terminate(); - } - return result; -} - -void semaphore::implementation::operator()(pointer value) { - if (value) { - dispatch_release(Unwrap(value)); - } -}; - -void semaphore::acquire() { - dispatch_semaphore_wait(Unwrap(_handle.get()), DISPATCH_TIME_FOREVER); -} - -void semaphore::release() { - dispatch_semaphore_signal(Unwrap(_handle.get())); -} - -} // namespace crl - -#endif // CRL_USE_DISPATCH diff --git a/Telegram/ThirdParty/crl/src/crl/dispatch/crl_dispatch_semaphore.h b/Telegram/ThirdParty/crl/src/crl/dispatch/crl_dispatch_semaphore.h deleted file mode 100644 index 41870aec4..000000000 --- a/Telegram/ThirdParty/crl/src/crl/dispatch/crl_dispatch_semaphore.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include - -#ifdef CRL_USE_DISPATCH - -#include - -namespace crl { - -class semaphore { -public: - semaphore() : _handle(implementation::create()) { - } - semaphore(const semaphore &other) = delete; - semaphore &operator=(const semaphore &other) = delete; - semaphore(semaphore &&other) noexcept - : _handle(std::move(other._handle)) { - } - semaphore &operator=(semaphore &&other) noexcept { - _handle = std::move(other._handle); - return *this; - } - - void acquire(); - void release(); - -private: - // Hide dispatch_semaphore_t - struct implementation { - using pointer = void*; - static pointer create(); - void operator()(pointer value); - }; - std::unique_ptr _handle; - -}; - -} // namespace crl - -#endif // CRL_USE_DISPATCH diff --git a/Telegram/ThirdParty/crl/src/crl/linux/crl_linux_time.cpp b/Telegram/ThirdParty/crl/src/crl/linux/crl_linux_time.cpp deleted file mode 100644 index 8b2eca1d1..000000000 --- a/Telegram/ThirdParty/crl/src/crl/linux/crl_linux_time.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include - -#ifdef CRL_USE_LINUX_TIME - -#include - -namespace crl::details { - -void init() { -} - -inner_time_type current_value() { - timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - const auto seconds = inner_time_type(ts.tv_sec); - const auto milliseconds = inner_time_type(ts.tv_nsec) / 1000000; - return seconds * 1000 + milliseconds; -} - -time convert(inner_time_type value) { - return time(value); -} - -inner_profile_type current_profile_value() { - timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - const auto seconds = inner_profile_type(ts.tv_sec); - const auto milliseconds = inner_profile_type(ts.tv_nsec) / 1000; - return seconds * 1000000 + milliseconds; -} - -profile_time convert_profile(inner_profile_type value) { - return profile_time(value); -} - -} // namespace crl::details - -#endif // CRL_USE_LINUX_TIME diff --git a/Telegram/ThirdParty/crl/src/crl/mac/crl_mac_time.cpp b/Telegram/ThirdParty/crl/src/crl/mac/crl_mac_time.cpp deleted file mode 100644 index a10bec5ef..000000000 --- a/Telegram/ThirdParty/crl/src/crl/mac/crl_mac_time.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include - -#ifdef CRL_USE_MAC_TIME - -#include - -namespace crl::details { -namespace { - -double Frequency/* = 0.*/; -double ProfileFrequency/* = 0.*/; - -} // namespace - -void init() { - mach_timebase_info_data_t tb = { 0, 0 }; - mach_timebase_info(&tb); - Frequency = (double(tb.numer) / tb.denom) / 1000000.; - ProfileFrequency = (double(tb.numer) / tb.denom) / 1000.; -} - -inner_time_type current_value() { - return mach_absolute_time(); -} - -time convert(inner_time_type value) { - return time(value * Frequency); -} - -inner_profile_type current_profile_value() { - return mach_absolute_time(); -} - -profile_time convert_profile(inner_profile_type value) { - return profile_time(value * ProfileFrequency); -} - -} // namespace crl::details - -#endif // CRL_USE_MAC_TIME diff --git a/Telegram/ThirdParty/crl/src/crl/qt/crl_qt_async.cpp b/Telegram/ThirdParty/crl/src/crl/qt/crl_qt_async.cpp deleted file mode 100644 index aed623169..000000000 --- a/Telegram/ThirdParty/crl/src/crl/qt/crl_qt_async.cpp +++ /dev/null @@ -1,12 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include - -#ifdef CRL_USE_QT - -#endif // CRL_USE_QT diff --git a/Telegram/ThirdParty/crl/src/crl/qt/crl_qt_async.h b/Telegram/ThirdParty/crl/src/crl/qt/crl_qt_async.h deleted file mode 100644 index 9e9ef41a0..000000000 --- a/Telegram/ThirdParty/crl/src/crl/qt/crl_qt_async.h +++ /dev/null @@ -1,72 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include - -#ifdef CRL_USE_QT - -#include -#include -#include - -#include - -namespace crl::details { - -template -class Runnable : public QRunnable { -public: - Runnable(Callable &&callable) : _callable(std::move(callable)) { - } - - void run() override { - _callable(); - } - -private: - Callable _callable; - -}; - -template -inline auto create_runnable(Callable &&callable) { - if constexpr (std::is_reference_v) { - auto copy = callable; - return create_runnable(std::move(copy)); - } else { - return new Runnable(std::move(callable)); - } -} - -template -inline void async_any(Callable &&callable) { - QThreadPool::globalInstance()->start( - create_runnable(std::forward(callable))); -} - -inline void async_plain(void (*callable)(void*), void *argument) { - async_any([=] { - callable(argument); - }); -} - -} // namespace crl::details - -namespace crl { - -template < - typename Callable, - typename Return = decltype(std::declval()())> -inline void async(Callable &&callable) { - details::async_any(std::forward(callable)); -} - -} // namespace crl - -#endif // CRL_USE_QT diff --git a/Telegram/ThirdParty/crl/src/crl/qt/crl_qt_guards.h b/Telegram/ThirdParty/crl/src/crl/qt/crl_qt_guards.h deleted file mode 100644 index be8e2639a..000000000 --- a/Telegram/ThirdParty/crl/src/crl/qt/crl_qt_guards.h +++ /dev/null @@ -1,117 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include - -#if __has_include() - -class QObject; - -template -class QPointer; - -template -class QWeakPointer; - -template -class QSharedPointer; - -#if __has_include() - -namespace gsl { - -template -class not_null; - -} // namespace gsl - -#endif // gsl - -namespace crl { - -template -struct guard_traits; - -template -struct guard_traits, void> { - static QPointer create(const QPointer &value) { - return value; - } - static QPointer create(QPointer &&value) { - return std::move(value); - } - static bool check(const QPointer &guard) { - return guard.data() != nullptr; - } - -}; - -template -struct guard_traits< - T*, - std::enable_if_t< - std::is_base_of_v>>> { - static QPointer create(T *value) { - return value; - } - static bool check(const QPointer &guard) { - return guard.data() != nullptr; - } - -}; - -#if __has_include() - -template -struct guard_traits< - gsl::not_null, - std::enable_if_t< - std::is_base_of_v>>> { - static QPointer create(gsl::not_null value) { - return value.get(); - } - static bool check(const QPointer &guard) { - return guard.data() != nullptr; - } - -}; - -#endif // gsl - -template -struct guard_traits, void> { - static QWeakPointer create(const QWeakPointer &value) { - return value; - } - static QWeakPointer create(QWeakPointer &&value) { - return std::move(value); - } - static bool check(const QWeakPointer &guard) { - return guard.toStrongRef() != nullptr; - } - -}; - -template -struct guard_traits, void> { - static QWeakPointer create(const QSharedPointer &value) { - return value; - } - static QWeakPointer create(QSharedPointer &&value) { - return value; - } - static bool check(const QWeakPointer &guard) { - return guard.toStrongRef() != nullptr; - } - -}; - -} // namespace crl - -#endif // Qt diff --git a/Telegram/ThirdParty/crl/src/crl/qt/crl_qt_semaphore.cpp b/Telegram/ThirdParty/crl/src/crl/qt/crl_qt_semaphore.cpp deleted file mode 100644 index e6c335ec3..000000000 --- a/Telegram/ThirdParty/crl/src/crl/qt/crl_qt_semaphore.cpp +++ /dev/null @@ -1,12 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include - -#ifdef CRL_USE_QT - -#endif // CRL_USE_QT diff --git a/Telegram/ThirdParty/crl/src/crl/qt/crl_qt_semaphore.h b/Telegram/ThirdParty/crl/src/crl/qt/crl_qt_semaphore.h deleted file mode 100644 index f6aa19de1..000000000 --- a/Telegram/ThirdParty/crl/src/crl/qt/crl_qt_semaphore.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include - -#ifdef CRL_USE_QT - -#include -#include - -namespace crl { - -class semaphore { -public: - semaphore() = default; - semaphore(const semaphore &other) = delete; - semaphore &operator=(const semaphore &other) = delete; - semaphore(semaphore &&other) = delete; - semaphore &operator=(semaphore &&other) = delete; - - void acquire() { - _impl.acquire(); - } - void release() { - _impl.release(); - } - -private: - QSemaphore _impl; - -}; - -} // namespace crl - -#endif // CRL_USE_QT diff --git a/Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_async.cpp b/Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_async.cpp deleted file mode 100644 index 7f5ca8d8e..000000000 --- a/Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_async.cpp +++ /dev/null @@ -1,22 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include - -#ifdef CRL_USE_WINAPI - -#include - -namespace crl::details { - -void async_plain(void (*callable)(void*), void *argument) { - Concurrency::CurrentScheduler::ScheduleTask(callable, argument); -} - -} // namespace crl::details - -#endif // CRL_USE_WINAPI diff --git a/Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_async.h b/Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_async.h deleted file mode 100644 index 430b5e4f2..000000000 --- a/Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_async.h +++ /dev/null @@ -1,51 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include - -#ifdef CRL_USE_WINAPI - -#include -#include -#include - -namespace crl::details { - -void async_plain(void (*callable)(void*), void *argument); - -} // namespace crl::details - -namespace crl { - -template < - typename Callable, - typename Return = decltype(std::declval()())> -inline void async(Callable &&callable) { - using Function = std::decay_t; - - if constexpr (details::is_plain_function_v) { - using Plain = Return(*)(); - const auto copy = static_cast(callable); - details::async_plain([](void *passed) { - const auto callable = reinterpret_cast(passed); - (*callable)(); - }, reinterpret_cast(copy)); - } else { - const auto copy = new Function(std::forward(callable)); - details::async_plain([](void *passed) { - const auto callable = static_cast(passed); - const auto guard = details::finally([=] { delete callable; }); - (*callable)(); - }, static_cast(copy)); - } -} - -} // namespace crl - -#endif // CRL_USE_WINAPI diff --git a/Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_dll.h b/Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_dll.h deleted file mode 100644 index 6b28d72d3..000000000 --- a/Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_dll.h +++ /dev/null @@ -1,67 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include - -#ifdef CRL_USE_WINAPI - -#include -#include - -namespace crl::details { - -class dll { -public: - enum class own_policy { - owner, - load_and_leak, - use_existing, - }; - dll(LPCWSTR library, own_policy policy) - : _handle((policy == own_policy::use_existing) - ? GetModuleHandle(library) - : LoadLibrary(library)) - , _policy(policy) { - } - - template - bool try_load(Function &function, const char *name) const { - if (!_handle) { - return false; - } - function = reinterpret_cast(GetProcAddress(_handle, name)); - return (function != nullptr); - } - - template - void load(Function &function, const char *name) const { - if (!try_load(function, name)) { - Failed(); - } - } - - ~dll() { - if (_handle && _policy == own_policy::owner) { - FreeLibrary(_handle); - } - } - -private: - [[noreturn]] static void Failed() { - std::terminate(); - } - - HMODULE _handle = nullptr; - own_policy _policy = own_policy::use_existing; - -}; - -} // namespace crl::details - -#endif // CRL_USE_WINAPI diff --git a/Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_list.cpp b/Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_list.cpp deleted file mode 100644 index 3d64ea94e..000000000 --- a/Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_list.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include - -#ifdef CRL_USE_WINAPI_LIST - -#include -#include - -namespace crl::details { -namespace { - -PSLIST_HEADER UnwrapList(void *wrapped) { - return static_cast(wrapped); -} - -PSLIST_ENTRY UnwrapEntry(void *wrapped) { - return static_cast(wrapped); -} - -SLIST_ENTRY *ReverseList(SLIST_ENTRY *entry, SLIST_ENTRY *next) { - entry->Next = nullptr; - do { - auto third = next->Next; - next->Next = entry; - entry = next; - next = third; - } while (next); - return entry; -} - -PSLIST_ENTRY (NTAPI *RtlFirstEntrySList)(const SLIST_HEADER *ListHead) = nullptr; - -} // namespace - -list::list() -: _impl(std::make_unique()) -, _alive(new bool(true)) { - static auto initialize = [] { - const auto library = details::dll( - L"ntdll.dll", - details::dll::own_policy::load_and_leak); - library.load(RtlFirstEntrySList, "RtlFirstEntrySList"); - return true; - }(); // TODO crl::once?.. - - static_assert(alignof(lock_free_list) == MEMORY_ALLOCATION_ALIGNMENT); - static_assert(alignof(lock_free_list) >= alignof(SLIST_HEADER)); - static_assert(sizeof(lock_free_list) == sizeof(SLIST_HEADER)); - InitializeSListHead(UnwrapList(_impl.get())); -} - -bool list::push_entry(BasicEntry *entry) { - return (InterlockedPushEntrySList( - UnwrapList(_impl.get()), - UnwrapEntry(&entry->plain)) == nullptr); -} - -bool list::empty() const { - return RtlFirstEntrySList(UnwrapList(_impl.get())) == nullptr; -} - -bool list::process() { - if (auto entry = InterlockedFlushSList(UnwrapList(_impl.get()))) { - const auto alive = _alive; - if (const auto next = entry->Next) { - entry = ReverseList(entry, next); - } - do { - const auto basic = reinterpret_cast(entry); - entry = entry->Next; - basic->process(basic); - if (!*alive) { - delete alive; - return false; - } - } while (entry); - } - return true; -} - -list::~list() { - *_alive = false; -} - -} // namespace crl::details - -#endif // CRL_USE_WINAPI_LIST diff --git a/Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_list.h b/Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_list.h deleted file mode 100644 index adbbdb741..000000000 --- a/Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_list.h +++ /dev/null @@ -1,102 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include - -#ifdef CRL_USE_WINAPI_LIST - -#include -#include - -#ifndef CRL_USE_WINAPI -#error "This file should not be included by client-code directly." -#endif // CRL_USE_WINAPI - -namespace crl::details { - -class list { -public: - list(); - list(const list &other) = delete; - list &operator=(const list &other) = delete; - - template - bool push_is_first(Callable &&callable) { - return push_entry(AllocateEntry(std::forward(callable))); - } - bool process(); - bool empty() const; - - ~list(); - -private: -#if defined CRL_WINAPI_X64 - static constexpr auto kLockFreeAlignment = 16; -#elif defined CRL_WINAPI_X86 // CRL_WINAPI_X64 - static constexpr auto kLockFreeAlignment = 8; -#else // CRL_WINAPI_X86 -#error "Configuration is not supported." -#endif // !CRL_WINAPI_X86 && !CRL_WINAPI_X64 - - // Hide WinAPI SLIST_HEADER - struct alignas(kLockFreeAlignment) lock_free_list { - void *Next__; // Hide WinAPI SLIST_ENTRY - unsigned short Depth__; // Hide WinAPI WORD - unsigned short CpuId__; // Hide WinAPI WORD - }; - - struct alignas(kLockFreeAlignment) BasicEntry; - using ProcessEntryMethod = void(*)(BasicEntry *entry); - - struct alignas(kLockFreeAlignment) BasicEntry { - void *plain; // Hide WinAPI SLIST_ENTRY - ProcessEntryMethod process; - }; - - static_assert(std::is_pod_v); - static_assert(std::is_standard_layout_v); - static_assert(offsetof(BasicEntry, plain) == 0); - - template - struct Entry : BasicEntry { - Entry(Function &&function) : function(std::move(function)) { - } - Entry(const Function &function) : function(function) { - } - Function function; - - static void Process(BasicEntry *entry) { - auto full = static_cast(entry); - auto guard = details::finally([=] { delete full; }); - full->function(); - } - - }; - - template - static Entry> *AllocateEntry( - Callable &&callable) { - using Function = std::decay_t; - using Type = Entry; - - auto result = new Type(std::forward(callable)); - result->process = &Type::Process; - return result; - } - - bool push_entry(BasicEntry *entry); - - const std::unique_ptr _impl; - bool *_alive = nullptr; - -}; - -} // namespace crl::details - -#endif // CRL_USE_WINAPI_LIST diff --git a/Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_semaphore.cpp b/Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_semaphore.cpp deleted file mode 100644 index b974b4f45..000000000 --- a/Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_semaphore.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include - -#ifdef CRL_USE_WINAPI - -#include - -namespace crl { - -auto semaphore::implementation::create() -> pointer { - auto result = CreateSemaphore(nullptr, 0, 1, nullptr); - if (!result) { - std::terminate(); - } - return result; -} - -void semaphore::implementation::operator()(pointer value) { - if (value) { - CloseHandle(value); - } -}; - -void semaphore::acquire() { - WaitForSingleObject(_handle.get(), INFINITE); -} - -void semaphore::release() { - ReleaseSemaphore(_handle.get(), 1, nullptr); -} - -} // namespace crl - -#endif // CRL_USE_WINAPI diff --git a/Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_semaphore.h b/Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_semaphore.h deleted file mode 100644 index 4322aa63d..000000000 --- a/Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_semaphore.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include - -#ifdef CRL_USE_WINAPI - -#include - -namespace crl { - -class semaphore { -public: - semaphore() : _handle(implementation::create()) { - } - semaphore(const semaphore &other) = delete; - semaphore &operator=(const semaphore &other) = delete; - semaphore(semaphore &&other) noexcept - : _handle(std::move(other._handle)) { - } - semaphore &operator=(semaphore &&other) noexcept { - _handle = std::move(other._handle); - return *this; - } - - void acquire(); - void release(); - -private: - // Hide WinAPI HANDLE - struct implementation { - using pointer = void*; - static pointer create(); - void operator()(pointer value); - }; - std::unique_ptr _handle; - -}; - -} // namespace crl - -#endif // CRL_USE_WINAPI diff --git a/Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_time.cpp b/Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_time.cpp deleted file mode 100644 index a68c9b6ef..000000000 --- a/Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_time.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include - -#ifdef CRL_USE_WINAPI_TIME - -#include - -namespace crl::details { -namespace { - -double Frequency/* = 0.*/; -double ProfileFrequency/* = 0.*/; - -} // namespace - -void init() { - LARGE_INTEGER value; - QueryPerformanceFrequency(&value); - Frequency = 1000. / double(value.QuadPart); - ProfileFrequency = 1000000. / double(value.QuadPart); -} - -inner_time_type current_value() { - LARGE_INTEGER value; - QueryPerformanceCounter(&value); - return value.QuadPart; -} - -time convert(inner_time_type value) { - return time(value * Frequency); -} - -inner_profile_type current_profile_value() { - LARGE_INTEGER value; - QueryPerformanceCounter(&value); - return value.QuadPart; -} - -profile_time convert_profile(inner_profile_type value) { - return profile_time(value * ProfileFrequency); -} - -} // namespace crl::details - -#endif // CRL_USE_WINAPI_TIME diff --git a/Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_windows_h.h b/Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_windows_h.h deleted file mode 100644 index e06f131cc..000000000 --- a/Telegram/ThirdParty/crl/src/crl/winapi/crl_winapi_windows_h.h +++ /dev/null @@ -1,14 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include - -#ifdef small -#undef small -#endif // small diff --git a/Telegram/ThirdParty/crl/src/test.cpp b/Telegram/ThirdParty/crl/src/test.cpp deleted file mode 100644 index 49b2b27e4..000000000 --- a/Telegram/ThirdParty/crl/src/test.cpp +++ /dev/null @@ -1,110 +0,0 @@ -// crl_test.cpp : Defines the entry point for the console application. -// -#include -#include -#include -#include -#include - -void testOutput(crl::queue *queue) { - for (auto i = 0; i != 1000; ++i) { - queue->async([i] { std::cout << "Hi from serial queue: " << i << std::endl; }); - if ((i % 100) == 99) { - crl::async([i] { std::cout << "Hi from async: " << i << std::endl; }); - queue->sync([i] { std::cout << "Hi from queue sync: " << i << std::endl; }); - } - } - for (auto i = 0; i != 1000; ++i) { - crl::async([i] { std::cout << "Hi from crazy: " << i << std::endl; }); - } -} - -constexpr auto kQueueCount = 8; - -struct CacheLine { - static constexpr auto kSize = 128; // To be sure. - - int value; - char padding[kSize - sizeof(int)]; -}; - -int operator+(int already, const CacheLine &a) { - return already + a.value; -} - -std::atomic added = 0; -int testCounting(crl::queue (&queues)[kQueueCount]) { - CacheLine result[kQueueCount] = { 0 }; - for (auto i = 0; i != 100000; ++i) { - for (auto j = 0; j != kQueueCount; ++j) { - queues[j].async([&result, j] { ++result[j].value; }); - } - if ((i % 10000) == 9999) { - crl::async([i] { ++added; }); -// const auto j = ((i + 1) / 10000) % kQueueCount; -// queues[j].sync([j, &result] { ++result[j].value; }); - } - } - for (auto j = 0; j != kQueueCount; ++j) { - queues[j].sync([&result, j] { ++result[j].value; }); - } - return std::accumulate(std::begin(result), std::end(result), 0) + added; -} - -struct MainRequest { - void (*callable)(void*); - void *argument; -}; -std::deque MainRequests; - -void DrainMainRequests() { - while (!MainRequests.empty()) { - auto front = MainRequests.front(); - MainRequests.pop_front(); - front.callable(front.argument); - } -} - -void testMainQueue() { - auto count = 0; - auto add = [&] { - crl::on_main([&] { - count += 10; - crl::on_main([&] { - count += 20; - crl::on_main([&] { - count += 30; - }); - }); - }); - }; - add(); - crl::init_main_queue([](void (*callable)(void*), void *argument) { - MainRequests.push_back({ callable, argument }); - }); - DrainMainRequests(); - auto a = count; - add(); - auto b = count; - add(); - DrainMainRequests(); - auto c = count; - std::cout << "Should be (0, 0, 120): " << a << ", " << b << ", " << c << std::endl; -} - -int main() { - crl::queue testQueue[kQueueCount]; -// testOutput(&testQueue[0]); - for (int i = 0; i != 5; ++i) { - auto start_time = std::chrono::high_resolution_clock::now(); - auto result = testCounting(testQueue); - auto end_time = std::chrono::high_resolution_clock::now(); - auto ms = std::chrono::duration_cast(end_time - start_time); - std::cout << "Time: " << ms.count() / 1000. << " (" << result << ")" << std::endl; - } - testMainQueue(); - std::cout << "Finished." << std::endl; - int a = 0; - std::cin >> a; - return 0; -} diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 55aec1954..1d2bfb486 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 55aec1954e4239c8c1e85cf16791c96b0fa44624 +Subproject commit 1d2bfb486386aaff4d64900c800d72fb02d07e19 diff --git a/lib/xdg/kotatogramdesktop.desktop b/lib/xdg/kotatogramdesktop.desktop index e21852113..6d195be0e 100644 --- a/lib/xdg/kotatogramdesktop.desktop +++ b/lib/xdg/kotatogramdesktop.desktop @@ -7,7 +7,7 @@ Icon=kotatogram Terminal=false StartupWMClass=KotatogramDesktop Type=Application -Categories=Network;InstantMessaging;Qt; +Categories=Chat;Network;InstantMessaging;Qt; MimeType=x-scheme-handler/tg; Keywords=tg;chat;im;messaging;messenger;sms;tdesktop;kotatogram; X-GNOME-UsesNotifications=true diff --git a/lib/xdg/telegramdesktop.desktop b/lib/xdg/telegramdesktop.desktop deleted file mode 100644 index 8b1378917..000000000 --- a/lib/xdg/telegramdesktop.desktop +++ /dev/null @@ -1 +0,0 @@ -