Merge commit 'e2fd1c13e9a0fbb21aa2992b7b2b2b4a7e66204a'
Conflicts: td/telegram/WebPagesManager.cpp tddb/td/db/SqliteKeyValue.cpp
This commit is contained in:
commit
eab65191d9
@ -716,7 +716,7 @@ Changes in 1.2.0:
|
||||
* Added method `searchInstalledStickerSets` to search by title and name for installed sticker sets.
|
||||
* Added methods for handling connected websites: `getConnectedWebsites`, `disconnectWebsite` and
|
||||
`disconnectAllWebsites`.
|
||||
* Added method `getCountryCode`, which uses current user IP to identify their country.
|
||||
* Added method `getCountryCode`, which uses current user IP address to identify their country.
|
||||
* Added option `t_me_url`.
|
||||
* Fixed `BlackBerry` spelling in `deviceTokenBlackBerryPush`.
|
||||
* Fixed return type of `getChatMessageByDate` method, which is `Message` and not `Messages`.
|
||||
|
@ -356,7 +356,6 @@ set(TL_DOTNET_SCHEME_SOURCE
|
||||
|
||||
set(TDLIB_SOURCE
|
||||
td/mtproto/AuthData.cpp
|
||||
td/mtproto/crypto.cpp
|
||||
td/mtproto/DhHandshake.cpp
|
||||
td/mtproto/Handshake.cpp
|
||||
td/mtproto/HandshakeActor.cpp
|
||||
@ -367,6 +366,7 @@ set(TDLIB_SOURCE
|
||||
td/mtproto/PingConnection.cpp
|
||||
td/mtproto/ProxySecret.cpp
|
||||
td/mtproto/RawConnection.cpp
|
||||
td/mtproto/RSA.cpp
|
||||
td/mtproto/SessionConnection.cpp
|
||||
td/mtproto/TcpTransport.cpp
|
||||
td/mtproto/TlsInit.cpp
|
||||
@ -494,7 +494,6 @@ set(TDLIB_SOURCE
|
||||
|
||||
td/mtproto/AuthData.h
|
||||
td/mtproto/AuthKey.h
|
||||
td/mtproto/crypto.h
|
||||
td/mtproto/CryptoStorer.h
|
||||
td/mtproto/DhHandshake.h
|
||||
td/mtproto/Handshake.h
|
||||
@ -511,6 +510,7 @@ set(TDLIB_SOURCE
|
||||
td/mtproto/ProxySecret.h
|
||||
td/mtproto/Query.h
|
||||
td/mtproto/RawConnection.h
|
||||
td/mtproto/RSA.h
|
||||
td/mtproto/SessionConnection.h
|
||||
td/mtproto/TcpTransport.h
|
||||
td/mtproto/TlsInit.h
|
||||
|
@ -52,7 +52,6 @@ for a list of all available `TDLib` [methods](https://core.telegram.org/tdlib/do
|
||||
* gperf (build only)
|
||||
* CMake (3.0.2+, build only)
|
||||
* PHP (optional, for documentation generation)
|
||||
* Doxygen (optional, for documentation generation)
|
||||
|
||||
<a name="building"></a>
|
||||
## Building
|
||||
|
@ -14,21 +14,22 @@
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(DEBUG));
|
||||
td::VERBOSITY_NAME(fd) = VERBOSITY_NAME(INFO);
|
||||
|
||||
std::string url = (argc > 1 ? argv[1] : "https://telegram.org");
|
||||
td::string url = (argc > 1 ? argv[1] : "https://telegram.org");
|
||||
auto timeout = 10;
|
||||
auto ttl = 3;
|
||||
auto prefer_ipv6 = (argc > 2 && std::string(argv[2]) == "-6");
|
||||
auto prefer_ipv6 = (argc > 2 && td::string(argv[2]) == "-6");
|
||||
auto scheduler = td::make_unique<td::ConcurrentScheduler>();
|
||||
scheduler->init(0);
|
||||
scheduler
|
||||
->create_actor_unsafe<td::Wget>(0, "Client",
|
||||
td::PromiseCreator::lambda([](td::Result<td::unique_ptr<td::HttpQuery>> res) {
|
||||
if (res.is_error()) {
|
||||
LOG(FATAL) << res.error();
|
||||
}
|
||||
LOG(ERROR) << *res.ok();
|
||||
td::Scheduler::instance()->finish();
|
||||
}),
|
||||
|
@ -162,9 +162,14 @@ See [erl-tdlib](https://github.com/lattenwald/erl-tdlib) for an example of TDLib
|
||||
<a name="php"></a>
|
||||
## Using TDLib in PHP projects
|
||||
|
||||
TDLib can be used from the PHP programming language by wrapping its functionality in a PHP extension.
|
||||
If you use modern PHP >= 7.4, you can use TDLib via a PHP FFI extension. For example, take a look at [ffi-tdlib](https://github.com/aurimasniekis/php-ffi-tdlib) - an FFI-based TDLib wrapper.
|
||||
|
||||
See also [tdlib-schema](https://github.com/aurimasniekis/php-tdlib-schema) - a generator for TDLib API classes.
|
||||
|
||||
For older PHP versions you can use TDLib by wrapping its functionality in a PHP extension.
|
||||
|
||||
See [phptdlib](https://github.com/yaroslavche/phptdlib), [tdlib](https://github.com/aurimasniekis/php-ext-tdlib) or [PIF-TDPony](https://github.com/danog/pif-tdpony) for examples of such extensions which provide access to TDLib from PHP.
|
||||
|
||||
See [tdlib-bundle](https://github.com/yaroslavche/tdlib-bundle) – a Symfony bundle based on [phptdlib](https://github.com/yaroslavche/phptdlib).
|
||||
|
||||
<a name="lua"></a>
|
||||
|
@ -42,6 +42,9 @@ if (GCC OR CLANG)
|
||||
if (CLANG)
|
||||
target_compile_options(tdsqlite PRIVATE -Wno-parentheses-equality -Wno-unused-value)
|
||||
endif()
|
||||
if (GCC AND NOT (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.0))
|
||||
target_compile_options(tdsqlite PRIVATE -Wno-return-local-addr -Wno-stringop-overflow)
|
||||
endif()
|
||||
elseif (MSVC)
|
||||
target_compile_options(tdsqlite PRIVATE /wd4996)
|
||||
endif()
|
||||
|
@ -1857,17 +1857,14 @@ httpUrl url:string = HttpUrl;
|
||||
|
||||
//@class InputInlineQueryResult @description Represents a single result of an inline query; for bots only
|
||||
|
||||
//@description Represents a link to an animated GIF @id Unique identifier of the query result @title Title of the query result @thumbnail_url URL of the static result thumbnail (JPEG or GIF), if it exists
|
||||
//@gif_url The URL of the GIF-file (file size must not exceed 1MB) @gif_duration Duration of the GIF, in seconds @gif_width Width of the GIF @gif_height Height of the GIF
|
||||
//@description Represents a link to an animated GIF or an animated (i.e. without sound) H.264/MPEG-4 AVC video
|
||||
//@id Unique identifier of the query result @title Title of the query result
|
||||
//@thumbnail_url URL of the result thumbnail (JPEG, GIF, or MPEG4), if it exists @thumbnail_mime_type MIME type of the video thumbnail. If non-empty, must be one of "image/jpeg", "image/gif" and "video/mp4"
|
||||
//@video_url The URL of the video file (file size must not exceed 1MB) @video_mime_type MIME type of the video file. Must be one of "image/gif" and "video/mp4"
|
||||
//@video_duration Duration of the video, in seconds @video_width Width of the video @video_height Height of the video
|
||||
//@reply_markup The message reply markup. Must be of type replyMarkupInlineKeyboard or null
|
||||
//@input_message_content The content of the message to be sent. Must be one of the following types: InputMessageText, InputMessageAnimation, InputMessageLocation, InputMessageVenue or InputMessageContact
|
||||
inputInlineQueryResultAnimatedGif id:string title:string thumbnail_url:string gif_url:string gif_duration:int32 gif_width:int32 gif_height:int32 reply_markup:ReplyMarkup input_message_content:InputMessageContent = InputInlineQueryResult;
|
||||
|
||||
//@description Represents a link to an animated (i.e. without sound) H.264/MPEG-4 AVC video @id Unique identifier of the query result @title Title of the result @thumbnail_url URL of the static result thumbnail (JPEG or GIF), if it exists
|
||||
//@mpeg4_url The URL of the MPEG4-file (file size must not exceed 1MB) @mpeg4_duration Duration of the video, in seconds @mpeg4_width Width of the video @mpeg4_height Height of the video
|
||||
//@reply_markup The message reply markup. Must be of type replyMarkupInlineKeyboard or null
|
||||
//@input_message_content The content of the message to be sent. Must be one of the following types: InputMessageText, InputMessageAnimation, InputMessageLocation, InputMessageVenue or InputMessageContact
|
||||
inputInlineQueryResultAnimatedMpeg4 id:string title:string thumbnail_url:string mpeg4_url:string mpeg4_duration:int32 mpeg4_width:int32 mpeg4_height:int32 reply_markup:ReplyMarkup input_message_content:InputMessageContent = InputInlineQueryResult;
|
||||
inputInlineQueryResultAnimation id:string title:string thumbnail_url:string thumbnail_mime_type:string video_url:string video_mime_type:string video_duration:int32 video_width:int32 video_height:int32 reply_markup:ReplyMarkup input_message_content:InputMessageContent = InputInlineQueryResult;
|
||||
|
||||
//@description Represents a link to an article or web page @id Unique identifier of the query result @url URL of the result, if it exists @hide_url True, if the URL must be not shown @title Title of the result
|
||||
//@param_description A short description of the result @thumbnail_url URL of the result thumbnail, if it exists @thumbnail_width Thumbnail width, if known @thumbnail_height Thumbnail height, if known
|
||||
@ -4386,7 +4383,7 @@ answerCustomQuery custom_query_id:int64 data:string = Ok;
|
||||
setAlarm seconds:double = Ok;
|
||||
|
||||
|
||||
//@description Uses current user IP to found their country. Returns two-letter ISO 3166-1 alpha-2 country code. Can be called before authorization
|
||||
//@description Uses current user IP address to found their country. Returns two-letter ISO 3166-1 alpha-2 country code. Can be called before authorization
|
||||
getCountryCode = Text;
|
||||
|
||||
//@description Returns the default text for invitation messages to be used as a placeholder when the current user invites friends to Telegram
|
||||
|
Binary file not shown.
@ -6,7 +6,6 @@
|
||||
//
|
||||
#include "td/mtproto/Handshake.h"
|
||||
|
||||
#include "td/mtproto/crypto.h"
|
||||
#include "td/mtproto/KDF.h"
|
||||
#include "td/mtproto/utils.h"
|
||||
|
||||
|
@ -7,8 +7,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "td/mtproto/AuthKey.h"
|
||||
#include "td/mtproto/crypto.h"
|
||||
#include "td/mtproto/DhHandshake.h"
|
||||
#include "td/mtproto/RSA.h"
|
||||
|
||||
#include "td/utils/buffer.h"
|
||||
#include "td/utils/Slice.h"
|
||||
|
@ -4,7 +4,7 @@
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#include "td/mtproto/crypto.h"
|
||||
#include "td/mtproto/RSA.h"
|
||||
|
||||
#include "td/mtproto/mtproto_api.h"
|
||||
|
||||
@ -25,7 +25,6 @@
|
||||
|
||||
namespace td {
|
||||
|
||||
/*** RSA ***/
|
||||
RSA::RSA(BigNum n, BigNum e) : n_(std::move(n)), e_(std::move(e)) {
|
||||
}
|
||||
|
@ -15,7 +15,6 @@
|
||||
|
||||
namespace td {
|
||||
|
||||
/*** RSA ***/
|
||||
class RSA {
|
||||
public:
|
||||
RSA clone() const;
|
||||
@ -33,7 +32,6 @@ class RSA {
|
||||
BigNum e_;
|
||||
};
|
||||
|
||||
/*** PublicRsaKeyInterface ***/
|
||||
class PublicRsaKeyInterface {
|
||||
public:
|
||||
virtual ~PublicRsaKeyInterface() = default;
|
@ -516,6 +516,9 @@ void AnimationsManager::load_saved_animations(Promise<Unit> &&promise) {
|
||||
}
|
||||
|
||||
void AnimationsManager::on_load_saved_animations_from_database(const string &value) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
if (value.empty()) {
|
||||
LOG(INFO) << "Saved animations aren't found in database";
|
||||
reload_saved_animations(true);
|
||||
|
@ -475,6 +475,10 @@ BackgroundId BackgroundManager::search_background(const string &name, Promise<Un
|
||||
}
|
||||
|
||||
void BackgroundManager::on_load_background_from_database(string name, string value) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto promises_it = being_loaded_from_database_backgrounds_.find(name);
|
||||
CHECK(promises_it != being_loaded_from_database_backgrounds_.end());
|
||||
auto promises = std::move(promises_it->second);
|
||||
|
@ -136,7 +136,7 @@ StringBuilder &operator<<(StringBuilder &string_builder, const BackgroundType &t
|
||||
|
||||
Result<BackgroundType> get_background_type(const td_api::BackgroundType *type) {
|
||||
if (type == nullptr) {
|
||||
return Status::Error(400, "Type must not be empty");
|
||||
return Status::Error(400, "Type must be non-empty");
|
||||
}
|
||||
|
||||
BackgroundType result;
|
||||
@ -149,7 +149,7 @@ Result<BackgroundType> get_background_type(const td_api::BackgroundType *type) {
|
||||
case td_api::backgroundTypePattern::ID: {
|
||||
auto pattern = static_cast<const td_api::backgroundTypePattern *>(type);
|
||||
if (pattern->fill_ == nullptr) {
|
||||
return Status::Error(400, "Fill info must not be empty");
|
||||
return Status::Error(400, "Fill info must be non-empty");
|
||||
}
|
||||
result = BackgroundType(pattern->is_moving_, get_background_fill(pattern->fill_.get()), pattern->intensity_);
|
||||
break;
|
||||
@ -157,7 +157,7 @@ Result<BackgroundType> get_background_type(const td_api::BackgroundType *type) {
|
||||
case td_api::backgroundTypeFill::ID: {
|
||||
auto fill = static_cast<const td_api::backgroundTypeFill *>(type);
|
||||
if (fill->fill_ == nullptr) {
|
||||
return Status::Error(400, "Fill info must not be empty");
|
||||
return Status::Error(400, "Fill info must be non-empty");
|
||||
}
|
||||
result = BackgroundType(get_background_fill(fill->fill_.get()));
|
||||
break;
|
||||
|
@ -27,8 +27,8 @@
|
||||
|
||||
#include "td/mtproto/AuthData.h"
|
||||
#include "td/mtproto/AuthKey.h"
|
||||
#include "td/mtproto/crypto.h"
|
||||
#include "td/mtproto/RawConnection.h"
|
||||
#include "td/mtproto/RSA.h"
|
||||
#include "td/mtproto/TransportType.h"
|
||||
|
||||
#include "td/net/HttpQuery.h"
|
||||
@ -1426,7 +1426,6 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
|
||||
}
|
||||
|
||||
if (!dice_emojis.empty()) {
|
||||
shared_config.set_option_string("dice_emojis", implode(dice_emojis, '\x01'));
|
||||
vector<string> dice_success_values(dice_emojis.size());
|
||||
for (auto &it : dice_emoji_success_value) {
|
||||
if (dice_emoji_index.find(it.first) == dice_emoji_index.end()) {
|
||||
@ -1436,6 +1435,7 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
|
||||
dice_success_values[dice_emoji_index[it.first]] = it.second;
|
||||
}
|
||||
shared_config.set_option_string("dice_success_values", implode(dice_success_values, ','));
|
||||
shared_config.set_option_string("dice_emojis", implode(dice_emojis, '\x01'));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4724,6 +4724,10 @@ void ContactsManager::load_imported_contacts(Promise<Unit> &&promise) {
|
||||
}
|
||||
|
||||
void ContactsManager::on_load_imported_contacts_from_database(string value) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
|
||||
CHECK(!are_imported_contacts_loaded_);
|
||||
if (need_clear_imported_contacts_) {
|
||||
need_clear_imported_contacts_ = false;
|
||||
@ -6679,6 +6683,9 @@ void ContactsManager::on_deleted_contacts(const vector<UserId> &deleted_contact_
|
||||
}
|
||||
|
||||
void ContactsManager::save_next_contacts_sync_date() {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
if (!G()->parameters().use_chat_info_db) {
|
||||
return;
|
||||
}
|
||||
@ -6775,6 +6782,9 @@ void ContactsManager::on_get_contacts_failed(Status error) {
|
||||
}
|
||||
|
||||
void ContactsManager::on_load_contacts_from_database(string value) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
if (value.empty()) {
|
||||
reload_contacts(true);
|
||||
return;
|
||||
@ -7167,6 +7177,10 @@ void ContactsManager::save_user_to_database_impl(User *u, UserId user_id, string
|
||||
}
|
||||
|
||||
void ContactsManager::on_save_user_to_database(UserId user_id, bool success) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
|
||||
User *u = get_user(user_id);
|
||||
CHECK(u != nullptr);
|
||||
LOG_CHECK(u->is_being_saved) << user_id << " " << u->is_saved << " " << u->is_status_saved << " "
|
||||
@ -7219,6 +7233,10 @@ void ContactsManager::load_user_from_database_impl(UserId user_id, Promise<Unit>
|
||||
}
|
||||
|
||||
void ContactsManager::on_load_user_from_database(UserId user_id, string value) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!loaded_from_database_users_.insert(user_id).second) {
|
||||
return;
|
||||
}
|
||||
@ -7433,6 +7451,10 @@ void ContactsManager::save_chat_to_database_impl(Chat *c, ChatId chat_id, string
|
||||
}
|
||||
|
||||
void ContactsManager::on_save_chat_to_database(ChatId chat_id, bool success) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Chat *c = get_chat(chat_id);
|
||||
CHECK(c != nullptr);
|
||||
CHECK(c->is_being_saved);
|
||||
@ -7479,6 +7501,10 @@ void ContactsManager::load_chat_from_database_impl(ChatId chat_id, Promise<Unit>
|
||||
}
|
||||
|
||||
void ContactsManager::on_load_chat_from_database(ChatId chat_id, string value) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!loaded_from_database_chats_.insert(chat_id).second) {
|
||||
return;
|
||||
}
|
||||
@ -7662,6 +7688,10 @@ void ContactsManager::save_channel_to_database_impl(Channel *c, ChannelId channe
|
||||
}
|
||||
|
||||
void ContactsManager::on_save_channel_to_database(ChannelId channel_id, bool success) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Channel *c = get_channel(channel_id);
|
||||
CHECK(c != nullptr);
|
||||
CHECK(c->is_being_saved);
|
||||
@ -7708,6 +7738,10 @@ void ContactsManager::load_channel_from_database_impl(ChannelId channel_id, Prom
|
||||
}
|
||||
|
||||
void ContactsManager::on_load_channel_from_database(ChannelId channel_id, string value) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!loaded_from_database_channels_.insert(channel_id).second) {
|
||||
return;
|
||||
}
|
||||
@ -7894,6 +7928,10 @@ void ContactsManager::save_secret_chat_to_database_impl(SecretChat *c, SecretCha
|
||||
}
|
||||
|
||||
void ContactsManager::on_save_secret_chat_to_database(SecretChatId secret_chat_id, bool success) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SecretChat *c = get_secret_chat(secret_chat_id);
|
||||
CHECK(c != nullptr);
|
||||
CHECK(c->is_being_saved);
|
||||
@ -7941,6 +7979,10 @@ void ContactsManager::load_secret_chat_from_database_impl(SecretChatId secret_ch
|
||||
}
|
||||
|
||||
void ContactsManager::on_load_secret_chat_from_database(SecretChatId secret_chat_id, string value) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!loaded_from_database_secret_chats_.insert(secret_chat_id).second) {
|
||||
return;
|
||||
}
|
||||
@ -8856,7 +8898,7 @@ void ContactsManager::on_get_user_photos(UserId user_id, int32 offset, int32 lim
|
||||
total_count = min_total_count;
|
||||
}
|
||||
LOG_IF(ERROR, limit < photo_count) << "Requested not more than " << limit << " photos, but " << photo_count
|
||||
<< " returned";
|
||||
<< " received";
|
||||
|
||||
User *u = get_user(user_id);
|
||||
if (u == nullptr) {
|
||||
@ -8926,11 +8968,8 @@ void ContactsManager::on_get_user_photos(UserId user_id, int32 offset, int32 lim
|
||||
}
|
||||
|
||||
auto known_photo_count = narrow_cast<int32>(user_photos->photos.size());
|
||||
CHECK(user_photos->count >= known_photo_count);
|
||||
if (user_photos->offset + known_photo_count > user_photos->count) {
|
||||
LOG(ERROR) << "Fix total photo count of " << user_id << " from " << user_photos->count << " to "
|
||||
<< user_photos->offset << " + " << known_photo_count;
|
||||
user_photos->count = user_photos->offset + known_photo_count;
|
||||
user_photos->photos.resize(user_photos->count - user_photos->offset);
|
||||
}
|
||||
}
|
||||
|
||||
@ -12737,7 +12776,7 @@ void ContactsManager::load_dialog_administrators(DialogId dialog_id, Promise<Uni
|
||||
|
||||
void ContactsManager::on_load_dialog_administrators_from_database(DialogId dialog_id, string value,
|
||||
Promise<Unit> &&promise) {
|
||||
if (value.empty()) {
|
||||
if (value.empty() || G()->close_flag()) {
|
||||
promise.set_value(Unit());
|
||||
return;
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
|
||||
FileType file_type = FileType::Document;
|
||||
Slice default_extension;
|
||||
bool supports_streaming = false;
|
||||
bool has_webp_thumbnail = false;
|
||||
PhotoFormat thumbnail_format = PhotoFormat::Jpeg;
|
||||
if (type_attributes == 1 || default_document_type != Document::Type::General) { // not a general document
|
||||
if (animated != nullptr || default_document_type == Document::Type::Animation) {
|
||||
document_type = Document::Type::Animation;
|
||||
@ -166,7 +166,9 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
|
||||
default_extension = Slice("webp");
|
||||
owner_dialog_id = DialogId();
|
||||
file_name.clear();
|
||||
has_webp_thumbnail = td_->stickers_manager_->has_webp_thumbnail(sticker);
|
||||
if (td_->stickers_manager_->has_webp_thumbnail(sticker) && remote_document.secret_file == nullptr) {
|
||||
thumbnail_format = PhotoFormat::Webp;
|
||||
}
|
||||
} else if (video != nullptr || default_document_type == Document::Type::Video ||
|
||||
default_document_type == Document::Type::VideoNote) {
|
||||
bool is_video_note = default_document_type == Document::Type::VideoNote;
|
||||
@ -193,7 +195,6 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
|
||||
<< ", has_stickers = " << has_stickers;
|
||||
}
|
||||
|
||||
bool has_png_thumbnail = false;
|
||||
if (is_background) {
|
||||
if (document_type != Document::Type::General) {
|
||||
LOG(ERROR) << "Receive background of type " << document_type;
|
||||
@ -202,7 +203,7 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
|
||||
file_type = FileType::Background;
|
||||
if (is_pattern) {
|
||||
default_extension = Slice("png");
|
||||
has_png_thumbnail = true;
|
||||
thumbnail_format = PhotoFormat::Png;
|
||||
} else {
|
||||
default_extension = Slice("jpg");
|
||||
}
|
||||
@ -261,9 +262,9 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
|
||||
|
||||
if (document_type != Document::Type::VoiceNote) {
|
||||
for (auto &thumb : document->thumbs_) {
|
||||
auto photo_size = get_photo_size(td_->file_manager_.get(), {FileType::Thumbnail, 0}, id, access_hash,
|
||||
file_reference, DcId::create(dc_id), owner_dialog_id, std::move(thumb),
|
||||
has_webp_thumbnail, has_png_thumbnail);
|
||||
auto photo_size =
|
||||
get_photo_size(td_->file_manager_.get(), {FileType::Thumbnail, 0}, id, access_hash, file_reference,
|
||||
DcId::create(dc_id), owner_dialog_id, std::move(thumb), thumbnail_format);
|
||||
if (photo_size.get_offset() == 0) {
|
||||
thumbnail = std::move(photo_size.get<0>());
|
||||
} else {
|
||||
|
@ -86,6 +86,10 @@ void HashtagHints::hashtag_used_impl(const string &hashtag) {
|
||||
}
|
||||
|
||||
void HashtagHints::from_db(Result<string> data, bool dummy) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
|
||||
sync_with_db_ = true;
|
||||
if (data.is_error() || data.ok().empty()) {
|
||||
return;
|
||||
|
@ -341,7 +341,7 @@ void InlineQueriesManager::answer_inline_query(int64 inline_query_id, bool is_pe
|
||||
bool force_vertical = false;
|
||||
for (auto &input_result : input_results) {
|
||||
if (input_result == nullptr) {
|
||||
return promise.set_error(Status::Error(400, "Inline query result must not be empty"));
|
||||
return promise.set_error(Status::Error(400, "Inline query result must be non-empty"));
|
||||
}
|
||||
|
||||
string id;
|
||||
@ -350,6 +350,7 @@ void InlineQueriesManager::answer_inline_query(int64 inline_query_id, bool is_pe
|
||||
string title;
|
||||
string description;
|
||||
string thumbnail_url;
|
||||
string thumbnail_type = "image/jpeg";
|
||||
string content_url;
|
||||
string content_type;
|
||||
int32 thumbnail_width = 0;
|
||||
@ -361,42 +362,28 @@ void InlineQueriesManager::answer_inline_query(int64 inline_query_id, bool is_pe
|
||||
FileType file_type = FileType::Temp;
|
||||
Result<tl_object_ptr<telegram_api::InputBotInlineMessage>> r_inline_message = Status::Error(500, "Uninited");
|
||||
switch (input_result->get_id()) {
|
||||
case td_api::inputInlineQueryResultAnimatedGif::ID: {
|
||||
auto animated_gif = move_tl_object_as<td_api::inputInlineQueryResultAnimatedGif>(input_result);
|
||||
case td_api::inputInlineQueryResultAnimation::ID: {
|
||||
auto animation = move_tl_object_as<td_api::inputInlineQueryResultAnimation>(input_result);
|
||||
type = "gif";
|
||||
id = std::move(animated_gif->id_);
|
||||
title = std::move(animated_gif->title_);
|
||||
thumbnail_url = std::move(animated_gif->thumbnail_url_);
|
||||
content_url = std::move(animated_gif->gif_url_);
|
||||
content_type = "image/gif";
|
||||
// duration = animated_gif->gif_duration_;
|
||||
width = animated_gif->gif_width_;
|
||||
height = animated_gif->gif_height_;
|
||||
id = std::move(animation->id_);
|
||||
title = std::move(animation->title_);
|
||||
thumbnail_url = std::move(animation->thumbnail_url_);
|
||||
if (!animation->thumbnail_mime_type_.empty()) {
|
||||
thumbnail_type = std::move(animation->thumbnail_mime_type_);
|
||||
}
|
||||
content_url = std::move(animation->video_url_);
|
||||
content_type = std::move(animation->video_mime_type_);
|
||||
if (content_type != "image/gif" && content_type != "video/mp4") {
|
||||
return promise.set_error(Status::Error(400, "Wrong animation MIME type specified"));
|
||||
}
|
||||
duration = animation->video_duration_;
|
||||
width = animation->video_width_;
|
||||
height = animation->video_height_;
|
||||
is_gallery = true;
|
||||
|
||||
file_type = FileType::Animation;
|
||||
r_inline_message =
|
||||
get_inline_message(std::move(animated_gif->input_message_content_), std::move(animated_gif->reply_markup_),
|
||||
td_api::inputMessageAnimation::ID);
|
||||
break;
|
||||
}
|
||||
case td_api::inputInlineQueryResultAnimatedMpeg4::ID: {
|
||||
auto animated_mpeg4 = move_tl_object_as<td_api::inputInlineQueryResultAnimatedMpeg4>(input_result);
|
||||
type = "gif";
|
||||
id = std::move(animated_mpeg4->id_);
|
||||
title = std::move(animated_mpeg4->title_);
|
||||
thumbnail_url = std::move(animated_mpeg4->thumbnail_url_);
|
||||
content_url = std::move(animated_mpeg4->mpeg4_url_);
|
||||
content_type = "video/mp4";
|
||||
duration = animated_mpeg4->mpeg4_duration_;
|
||||
width = animated_mpeg4->mpeg4_width_;
|
||||
height = animated_mpeg4->mpeg4_height_;
|
||||
is_gallery = true;
|
||||
|
||||
file_type = FileType::Animation;
|
||||
r_inline_message =
|
||||
get_inline_message(std::move(animated_mpeg4->input_message_content_),
|
||||
std::move(animated_mpeg4->reply_markup_), td_api::inputMessageAnimation::ID);
|
||||
r_inline_message = get_inline_message(std::move(animation->input_message_content_),
|
||||
std::move(animation->reply_markup_), td_api::inputMessageAnimation::ID);
|
||||
break;
|
||||
}
|
||||
case td_api::inputInlineQueryResultArticle::ID: {
|
||||
@ -701,7 +688,8 @@ void InlineQueriesManager::answer_inline_query(int64 inline_query_id, bool is_pe
|
||||
attributes.push_back(
|
||||
make_tl_object<telegram_api::documentAttributeImageSize>(thumbnail_width, thumbnail_height));
|
||||
}
|
||||
thumbnail = make_tl_object<telegram_api::inputWebDocument>(thumbnail_url, 0, "image/jpeg", std::move(attributes));
|
||||
thumbnail =
|
||||
make_tl_object<telegram_api::inputWebDocument>(thumbnail_url, 0, thumbnail_type, std::move(attributes));
|
||||
}
|
||||
tl_object_ptr<telegram_api::inputWebDocument> content;
|
||||
if (!content_url.empty() || !content_type.empty()) {
|
||||
|
@ -1579,7 +1579,7 @@ Result<LanguagePackManager::LanguageInfo> LanguagePackManager::get_language_info
|
||||
Result<LanguagePackManager::LanguageInfo> LanguagePackManager::get_language_info(
|
||||
td_api::languagePackInfo *language_pack_info) {
|
||||
if (language_pack_info == nullptr) {
|
||||
return Status::Error(400, "Language pack info must not be empty");
|
||||
return Status::Error(400, "Language pack info must be non-empty");
|
||||
}
|
||||
|
||||
if (!clean_input_string(language_pack_info->id_)) {
|
||||
|
@ -47,7 +47,7 @@ static const std::map<Slice, int *> log_tags{
|
||||
|
||||
Status Logging::set_current_stream(td_api::object_ptr<td_api::LogStream> stream) {
|
||||
if (stream == nullptr) {
|
||||
return Status::Error("Log stream must not be empty");
|
||||
return Status::Error("Log stream must be non-empty");
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(logging_mutex);
|
||||
|
@ -663,7 +663,7 @@ class MessageDice : public MessageContent {
|
||||
if (dice_value < 0) {
|
||||
return false;
|
||||
}
|
||||
if (emoji == "DEFAULT_EMOJI" || emoji == "🎯") {
|
||||
if (emoji == DEFAULT_EMOJI || emoji == "🎯") {
|
||||
return dice_value <= 6;
|
||||
}
|
||||
return dice_value <= 1000;
|
||||
@ -1778,7 +1778,7 @@ static Result<InputMessageContent> create_input_message_content(
|
||||
int32 correct_option_id = -1;
|
||||
FormattedText explanation;
|
||||
if (input_poll->type_ == nullptr) {
|
||||
return Status::Error(400, "Poll type must not be empty");
|
||||
return Status::Error(400, "Poll type must be non-empty");
|
||||
}
|
||||
switch (input_poll->type_->get_id()) {
|
||||
case td_api::pollTypeRegular::ID: {
|
||||
|
@ -422,7 +422,7 @@ static vector<Slice> match_bank_card_numbers(Slice str) {
|
||||
const unsigned char *end = str.uend();
|
||||
const unsigned char *ptr = begin;
|
||||
|
||||
// '/(?<=^|[^+_\pL\d-])[\d -]{13,}([^_\pL\d-]|$)/'
|
||||
// '/(?<=^|[^+_\pL\d-.,])[\d -]{13,}([^_\pL\d-]|$)/'
|
||||
|
||||
while (true) {
|
||||
while (ptr != end && !is_digit(*ptr)) {
|
||||
@ -435,7 +435,7 @@ static vector<Slice> match_bank_card_numbers(Slice str) {
|
||||
uint32 prev;
|
||||
next_utf8_unsafe(prev_utf8_unsafe(ptr), &prev, "match_bank_card_numbers");
|
||||
|
||||
if (prev == '+' || prev == '-' || prev == '_' ||
|
||||
if (prev == '.' || prev == ',' || prev == '+' || prev == '-' || prev == '_' ||
|
||||
get_unicode_simple_category(prev) == UnicodeSimpleCategory::Letter) {
|
||||
while (ptr != end && (is_digit(*ptr) || *ptr == ' ' || *ptr == '-')) {
|
||||
ptr++;
|
||||
@ -1965,10 +1965,12 @@ static vector<Slice> find_text_url_entities_v3(Slice text) {
|
||||
}
|
||||
|
||||
// entities must be valid for the text
|
||||
static FormattedText parse_text_url_entities_v3(Slice text, vector<MessageEntity> entities) {
|
||||
static FormattedText parse_text_url_entities_v3(Slice text, const vector<MessageEntity> &entities) {
|
||||
// continuous entities can't intersect TextUrl entities,
|
||||
// so try to find new TextUrl entities only between the predetermined continuous entities
|
||||
|
||||
Slice debug_initial_text = text;
|
||||
|
||||
FormattedText result;
|
||||
int32 result_text_utf16_length = 0;
|
||||
vector<MessageEntity> part_entities;
|
||||
@ -2109,7 +2111,7 @@ static FormattedText parse_text_url_entities_v3(Slice text, vector<MessageEntity
|
||||
splittable_entities.clear();
|
||||
} else {
|
||||
CHECK(pos == splittable_entities.size() - 1);
|
||||
CHECK(!text.empty());
|
||||
LOG_CHECK(!text.empty()) << '"' << debug_initial_text << "\" " << entities;
|
||||
splittable_entities[0] = std::move(splittable_entities.back());
|
||||
splittable_entities.resize(1);
|
||||
}
|
||||
@ -2118,7 +2120,7 @@ static FormattedText parse_text_url_entities_v3(Slice text, vector<MessageEntity
|
||||
part_begin = part_end;
|
||||
};
|
||||
|
||||
for (auto &entity : entities) {
|
||||
for (const auto &entity : entities) {
|
||||
if (is_splittable_entity(entity.type)) {
|
||||
auto index = get_splittable_entity_type_index(entity.type);
|
||||
part_splittable_entities[index].push_back(entity);
|
||||
@ -2207,7 +2209,7 @@ static FormattedText parse_markdown_v3_without_pre(Slice text, vector<MessageEnt
|
||||
|
||||
FormattedText parsed_text_url_text;
|
||||
if (text.find('[') != string::npos) {
|
||||
parsed_text_url_text = parse_text_url_entities_v3(text, std::move(entities));
|
||||
parsed_text_url_text = parse_text_url_entities_v3(text, entities);
|
||||
text = parsed_text_url_text.text;
|
||||
entities = std::move(parsed_text_url_text.entities);
|
||||
}
|
||||
@ -3543,6 +3545,25 @@ static std::pair<size_t, int32> remove_invalid_entities(const string &text, vect
|
||||
break;
|
||||
}
|
||||
|
||||
if (!nested_entities_stack.empty() && nested_entities_stack.back()->offset == utf16_offset &&
|
||||
(text[pos] == '\n' || text[pos] == ' ')) {
|
||||
// entities was fixed, so there can't be more than one splittable entity of each type, one blockquote and
|
||||
// one continuous entity for the given offset
|
||||
for (size_t i = nested_entities_stack.size(); i > 0; i--) {
|
||||
auto *entity = nested_entities_stack[i - 1];
|
||||
if (entity->offset != utf16_offset || entity->type == MessageEntity::Type::TextUrl ||
|
||||
entity->type == MessageEntity::Type::MentionName || is_pre_entity(entity->type)) {
|
||||
break;
|
||||
}
|
||||
entity->offset++;
|
||||
entity->length--;
|
||||
if (entity->length == 0) {
|
||||
CHECK(i == nested_entities_stack.size());
|
||||
nested_entities_stack.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto c = static_cast<unsigned char>(text[pos]);
|
||||
switch (c) {
|
||||
case '\n':
|
||||
|
@ -4946,8 +4946,9 @@ void MessagesManager::save_dialog_to_database(DialogId dialog_id) {
|
||||
};
|
||||
add_group_key(d->message_notification_group);
|
||||
add_group_key(d->mention_notification_group);
|
||||
auto fixed_folder_id = d->folder_id == FolderId::archive() ? FolderId::archive() : FolderId::main();
|
||||
G()->td_db()->get_dialog_db_async()->add_dialog(
|
||||
dialog_id, d->folder_id, d->is_folder_id_inited ? d->order : 0, get_dialog_database_value(d),
|
||||
dialog_id, fixed_folder_id, d->is_folder_id_inited ? d->order : 0, get_dialog_database_value(d),
|
||||
std::move(changed_group_keys), PromiseCreator::lambda([dialog_id, can_reuse_notification_group](Result<> result) {
|
||||
send_closure(G()->messages_manager(), &MessagesManager::on_save_dialog_to_database, dialog_id,
|
||||
can_reuse_notification_group, result.is_ok());
|
||||
@ -4957,7 +4958,7 @@ void MessagesManager::save_dialog_to_database(DialogId dialog_id) {
|
||||
void MessagesManager::on_save_dialog_to_database(DialogId dialog_id, bool can_reuse_notification_group, bool success) {
|
||||
LOG(INFO) << "Successfully saved " << dialog_id << " to database";
|
||||
|
||||
if (success && can_reuse_notification_group) {
|
||||
if (success && can_reuse_notification_group && !G()->close_flag()) {
|
||||
auto d = get_dialog(dialog_id);
|
||||
CHECK(d != nullptr);
|
||||
try_reuse_notification_group(d->message_notification_group);
|
||||
@ -7058,7 +7059,7 @@ void MessagesManager::report_dialog(DialogId dialog_id, const tl_object_ptr<td_a
|
||||
}
|
||||
|
||||
if (reason == nullptr) {
|
||||
return promise.set_error(Status::Error(3, "Reason must not be empty"));
|
||||
return promise.set_error(Status::Error(3, "Reason must be non-empty"));
|
||||
}
|
||||
|
||||
Dialog *user_d = d;
|
||||
@ -7885,7 +7886,7 @@ void MessagesManager::on_get_history(DialogId dialog_id, MessageId from_message_
|
||||
CHECK(offset < 0 || from_the_end);
|
||||
CHECK(!from_message_id.is_scheduled());
|
||||
|
||||
// it is likely that there is no more history messages on the server
|
||||
// it is likely that there are no more history messages on the server
|
||||
bool have_full_history = from_the_end && narrow_cast<int32>(messages.size()) < limit;
|
||||
Dialog *d = get_dialog(dialog_id);
|
||||
|
||||
@ -7940,7 +7941,7 @@ void MessagesManager::on_get_history(DialogId dialog_id, MessageId from_message_
|
||||
MessageId first_received_message_id = get_message_id(messages.back(), false);
|
||||
if (first_received_message_id >= from_message_id && d->first_database_message_id.is_valid() &&
|
||||
first_received_message_id >= d->first_database_message_id) {
|
||||
// it is likely that there is no more history messages on the server
|
||||
// it is likely that there are no more history messages on the server
|
||||
have_full_history = true;
|
||||
}
|
||||
}
|
||||
@ -7958,6 +7959,46 @@ void MessagesManager::on_get_history(DialogId dialog_id, MessageId from_message_
|
||||
prev_have_full_history = d->have_full_history;
|
||||
}
|
||||
|
||||
if (from_the_end && d != nullptr) {
|
||||
auto last_server_message_id = get_message_id(messages[0], false);
|
||||
// delete all server messages with ID > last_server_message_id
|
||||
vector<MessageId> message_ids;
|
||||
find_newer_messages(d->messages.get(), last_server_message_id, message_ids);
|
||||
if (!message_ids.empty()) {
|
||||
bool need_update_dialog_pos = false;
|
||||
vector<int64> deleted_message_ids;
|
||||
for (auto message_id : message_ids) {
|
||||
CHECK(message_id > last_server_message_id);
|
||||
if (message_id.is_server()) {
|
||||
auto message = delete_message(d, message_id, true, &need_update_dialog_pos, "on_get_gistory 1");
|
||||
if (message != nullptr) {
|
||||
deleted_message_ids.push_back(message->message_id.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (need_update_dialog_pos) {
|
||||
send_update_chat_last_message(d, "on_get_gistory 2");
|
||||
}
|
||||
|
||||
if (!deleted_message_ids.empty()) {
|
||||
send_update_delete_messages(dialog_id, std::move(deleted_message_ids), true, false);
|
||||
|
||||
message_ids.clear();
|
||||
find_newer_messages(d->messages.get(), last_server_message_id, message_ids);
|
||||
}
|
||||
|
||||
// connect all messages with ID > last_server_message_id
|
||||
for (size_t i = 0; i + 1 < message_ids.size(); i++) {
|
||||
auto m = get_message(d, message_ids[i]);
|
||||
if (m == nullptr) { continue; }
|
||||
if (!m->have_next) {
|
||||
m->have_next = true;
|
||||
attach_message_to_next(d, message_ids[i], "on_get_history 3");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &message : messages) {
|
||||
if (!have_next && from_the_end && d != nullptr && get_message_id(message, false) < d->last_message_id) {
|
||||
// last message in the dialog should be attached to the next message if there is some
|
||||
@ -8169,18 +8210,20 @@ void MessagesManager::on_get_dialog_messages_search_result(DialogId dialog_id, c
|
||||
|
||||
MessageId first_added_message_id;
|
||||
if (messages.empty()) {
|
||||
// messages may be empty because there is no more messages or they can't be found due to global limit
|
||||
// anyway pretend that there is no more messages
|
||||
// messages may be empty because there are no more messages or they can't be found due to global limit
|
||||
// anyway pretend that there are no more messages
|
||||
first_added_message_id = MessageId::min();
|
||||
}
|
||||
|
||||
auto &result = it->second.second;
|
||||
CHECK(result.empty());
|
||||
int32 added_message_count = 0;
|
||||
for (auto &message : messages) {
|
||||
auto new_full_message_id =
|
||||
on_get_message(std::move(message), false, false, false, false, false, "search call messages");
|
||||
if (new_full_message_id != FullMessageId()) {
|
||||
result.push_back(new_full_message_id);
|
||||
added_message_count++;
|
||||
}
|
||||
|
||||
auto message_id = new_full_message_id.get_message_id();
|
||||
@ -8188,6 +8231,11 @@ void MessagesManager::on_get_dialog_messages_search_result(DialogId dialog_id, c
|
||||
first_added_message_id = message_id;
|
||||
}
|
||||
}
|
||||
if (total_count < added_message_count) {
|
||||
LOG(ERROR) << "Receive total_count = " << total_count << ", but added " << added_message_count
|
||||
<< " messages out of " << messages.size();
|
||||
total_count = added_message_count;
|
||||
}
|
||||
if (G()->parameters().use_message_db) {
|
||||
bool update_state = false;
|
||||
|
||||
@ -8224,7 +8272,7 @@ void MessagesManager::on_get_dialog_messages_search_result(DialogId dialog_id, c
|
||||
CHECK(result.empty());
|
||||
MessageId first_added_message_id;
|
||||
if (messages.empty()) {
|
||||
// messages may be empty because there is no more messages or they can't be found due to global limit
|
||||
// messages may be empty because there are no more messages or they can't be found due to global limit
|
||||
// anyway pretend that there is no more messages
|
||||
first_added_message_id = MessageId::min();
|
||||
}
|
||||
@ -9066,18 +9114,18 @@ void MessagesManager::find_old_messages(const Message *m, MessageId max_message_
|
||||
}
|
||||
}
|
||||
|
||||
void MessagesManager::find_new_messages(const Message *m, MessageId min_message_id, vector<MessageId> &message_ids) {
|
||||
void MessagesManager::find_newer_messages(const Message *m, MessageId min_message_id, vector<MessageId> &message_ids) {
|
||||
if (m == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m->message_id > min_message_id) {
|
||||
find_new_messages(m->left.get(), min_message_id, message_ids);
|
||||
find_newer_messages(m->left.get(), min_message_id, message_ids);
|
||||
|
||||
message_ids.push_back(m->message_id);
|
||||
}
|
||||
|
||||
find_new_messages(m->right.get(), min_message_id, message_ids);
|
||||
find_newer_messages(m->right.get(), min_message_id, message_ids);
|
||||
}
|
||||
|
||||
void MessagesManager::find_unloadable_messages(const Dialog *d, int32 unload_before_date, const Message *m,
|
||||
@ -9829,6 +9877,10 @@ void MessagesManager::repair_secret_chat_total_count(FolderId folder_id) {
|
||||
}
|
||||
|
||||
void MessagesManager::on_get_secret_chat_total_count(FolderId folder_id, int32 total_count) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
|
||||
CHECK(!td_->auth_manager_->is_bot());
|
||||
auto &list = get_dialog_list(folder_id);
|
||||
CHECK(total_count >= 0);
|
||||
@ -10793,6 +10845,10 @@ void MessagesManager::ttl_db_loop(double server_now) {
|
||||
|
||||
void MessagesManager::ttl_db_on_result(Result<std::pair<std::vector<std::pair<DialogId, BufferSlice>>, int32>> r_result,
|
||||
bool dummy) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto result = r_result.move_as_ok();
|
||||
ttl_db_has_query_ = false;
|
||||
ttl_db_expires_from_ = ttl_db_expires_till_;
|
||||
@ -11587,6 +11643,7 @@ FullMessageId MessagesManager::on_get_message(MessageInfo &&message_info, bool f
|
||||
}
|
||||
|
||||
if (message_id.is_scheduled()) {
|
||||
CHECK(message_id.is_scheduled_server());
|
||||
auto dialog_it = update_scheduled_message_ids_.find(dialog_id);
|
||||
CHECK(dialog_it != update_scheduled_message_ids_.end());
|
||||
dialog_it->second.erase(message_id.get_scheduled_server_message_id());
|
||||
@ -11757,7 +11814,7 @@ void MessagesManager::set_dialog_last_new_message_id(Dialog *d, MessageId last_n
|
||||
invalidate_message_indexes(d);
|
||||
|
||||
vector<MessageId> to_delete_message_ids;
|
||||
find_new_messages(d->messages.get(), last_new_message_id, to_delete_message_ids);
|
||||
find_newer_messages(d->messages.get(), last_new_message_id, to_delete_message_ids);
|
||||
td::remove_if(to_delete_message_ids, [](MessageId message_id) { return message_id.is_yet_unsent(); });
|
||||
if (!to_delete_message_ids.empty()) {
|
||||
LOG(WARNING) << "Delete " << format::as_array(to_delete_message_ids) << " because of received last new "
|
||||
@ -13323,6 +13380,9 @@ void MessagesManager::load_dialog_list_from_database(FolderId folder_id, int32 l
|
||||
|
||||
void MessagesManager::on_get_dialogs_from_database(FolderId folder_id, int32 limit, DialogDbGetDialogsResult &&dialogs,
|
||||
Promise<Unit> &&promise) {
|
||||
if (G()->close_flag()) {
|
||||
return promise.set_error(Status::Error(500, "Request aborted"));
|
||||
}
|
||||
CHECK(!td_->auth_manager_->is_bot());
|
||||
auto &list = get_dialog_list(folder_id);
|
||||
LOG(INFO) << "Receive " << dialogs.dialogs.size() << " from expected " << limit << " chats in " << folder_id
|
||||
@ -13975,7 +14035,7 @@ void MessagesManager::get_messages_from_server(vector<FullMessageId> &&message_i
|
||||
auto dialog_id = full_message_id.get_dialog_id();
|
||||
auto message_id = full_message_id.get_message_id();
|
||||
if (!message_id.is_valid() || !message_id.is_server()) {
|
||||
if (message_id.is_valid_scheduled()) {
|
||||
if (message_id.is_valid_scheduled() && message_id.is_scheduled_server()) {
|
||||
scheduled_message_ids[dialog_id].push_back(message_id.get_scheduled_server_message_id().get());
|
||||
}
|
||||
continue;
|
||||
@ -15571,11 +15631,15 @@ td_api::object_ptr<td_api::ChatActionBar> MessagesManager::get_chat_action_bar_o
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
td_api::object_ptr<td_api::chat> MessagesManager::get_chat_object(const Dialog *d) const {
|
||||
td_api::object_ptr<td_api::chat> MessagesManager::get_chat_object(const Dialog *d, int64 real_order) const {
|
||||
CHECK(d != nullptr);
|
||||
|
||||
auto chat_source = is_dialog_sponsored(d) ? sponsored_dialog_source_.get_chat_source_object() : nullptr;
|
||||
|
||||
if (real_order == DEFAULT_ORDER) {
|
||||
real_order = d->order;
|
||||
}
|
||||
|
||||
bool can_delete_for_self = false;
|
||||
bool can_delete_for_all_users = false;
|
||||
if (chat_source != nullptr) {
|
||||
@ -15588,7 +15652,7 @@ td_api::object_ptr<td_api::chat> MessagesManager::get_chat_object(const Dialog *
|
||||
// can't delete
|
||||
break;
|
||||
}
|
||||
} else if (!td_->auth_manager_->is_bot() && d->order != DEFAULT_ORDER) {
|
||||
} else if (!td_->auth_manager_->is_bot() && real_order != DEFAULT_ORDER) {
|
||||
switch (d->dialog_id.get_type()) {
|
||||
case DialogType::User:
|
||||
can_delete_for_self = true;
|
||||
@ -16592,6 +16656,9 @@ vector<FullMessageId> MessagesManager::get_active_live_location_messages(Promise
|
||||
}
|
||||
|
||||
void MessagesManager::on_load_active_live_location_full_message_ids_from_database(string value) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
if (value.empty()) {
|
||||
LOG(INFO) << "Active live location messages aren't found in the database";
|
||||
on_load_active_live_location_messages_finished();
|
||||
@ -16882,6 +16949,9 @@ void MessagesManager::on_search_dialog_messages_db_result(int64 random_id, Dialo
|
||||
SearchMessagesFilter filter_type, int32 offset, int32 limit,
|
||||
Result<std::vector<BufferSlice>> r_messages,
|
||||
Promise<> promise) {
|
||||
if (G()->close_flag()) {
|
||||
return promise.set_error(Status::Error(500, "Request aborted"));
|
||||
}
|
||||
if (r_messages.is_error()) {
|
||||
LOG(ERROR) << r_messages.error();
|
||||
if (first_db_message_id != MessageId::min() && dialog_id.get_type() != DialogType::SecretChat &&
|
||||
@ -16919,8 +16989,9 @@ void MessagesManager::on_search_dialog_messages_db_result(int64 random_id, Dialo
|
||||
int32 result_size = narrow_cast<int32>(res.size());
|
||||
bool from_the_end =
|
||||
from_message_id == MessageId::max() || (offset < 0 && (result_size == 0 || res[0] < from_message_id));
|
||||
if (message_count < result_size || (message_count > result_size && from_the_end &&
|
||||
first_db_message_id == MessageId::min() && result_size < limit + offset)) {
|
||||
if ((message_count != -1 && message_count < result_size) ||
|
||||
(message_count > result_size && from_the_end && first_db_message_id == MessageId::min() &&
|
||||
result_size < limit + offset)) {
|
||||
LOG(INFO) << "Fix found message count in " << dialog_id << " from " << message_count << " to " << result_size;
|
||||
message_count = result_size;
|
||||
if (filter_type == SearchMessagesFilter::UnreadMention) {
|
||||
@ -16997,7 +17068,10 @@ std::pair<int64, vector<FullMessageId>> MessagesManager::offline_search_messages
|
||||
}
|
||||
|
||||
void MessagesManager::on_messages_db_fts_result(Result<MessagesDbFtsResult> result, int64 random_id,
|
||||
Promise<> &&promise) {
|
||||
Promise<Unit> &&promise) {
|
||||
if (G()->close_flag()) {
|
||||
result = Status::Error(500, "Request aborted");
|
||||
}
|
||||
if (result.is_error()) {
|
||||
found_fts_messages_.erase(random_id);
|
||||
return promise.set_error(result.move_as_error());
|
||||
@ -17026,6 +17100,9 @@ void MessagesManager::on_messages_db_calls_result(Result<MessagesDbCallsResult>
|
||||
MessageId first_db_message_id, SearchMessagesFilter filter,
|
||||
Promise<> &&promise) {
|
||||
CHECK(!first_db_message_id.is_scheduled());
|
||||
if (G()->close_flag()) {
|
||||
result = Status::Error(500, "Request aborted");
|
||||
}
|
||||
if (result.is_error()) {
|
||||
found_call_messages_.erase(random_id);
|
||||
return promise.set_error(result.move_as_error());
|
||||
@ -17177,6 +17254,9 @@ MessageId MessagesManager::find_message_by_date(const Message *m, int32 date) {
|
||||
|
||||
void MessagesManager::on_get_dialog_message_by_date_from_database(DialogId dialog_id, int32 date, int64 random_id,
|
||||
Result<BufferSlice> result, Promise<Unit> promise) {
|
||||
if (G()->close_flag()) {
|
||||
return promise.set_error(Status::Error(500, "Request aborted"));
|
||||
}
|
||||
Dialog *d = get_dialog(dialog_id);
|
||||
CHECK(d != nullptr);
|
||||
if (result.is_ok()) {
|
||||
@ -17408,6 +17488,10 @@ void MessagesManager::on_get_history_from_database(DialogId dialog_id, MessageId
|
||||
CHECK(offset < 0 || from_the_end);
|
||||
CHECK(!from_message_id.is_scheduled());
|
||||
|
||||
if (G()->close_flag()) {
|
||||
return promise.set_error(Status::Error(500, "Request aborted"));
|
||||
}
|
||||
|
||||
if (!have_input_peer(dialog_id, AccessRights::Read)) {
|
||||
LOG(WARNING) << "Ignore result of get_history_from_database in " << dialog_id;
|
||||
promise.set_value(Unit());
|
||||
@ -17592,13 +17676,13 @@ void MessagesManager::on_get_history_from_database(DialogId dialog_id, MessageId
|
||||
void MessagesManager::get_history_from_the_end(DialogId dialog_id, bool from_database, bool only_local,
|
||||
Promise<Unit> &&promise) {
|
||||
CHECK(dialog_id.is_valid());
|
||||
if (G()->close_flag()) {
|
||||
return promise.set_error(Status::Error(500, "Request aborted"));
|
||||
}
|
||||
if (!have_input_peer(dialog_id, AccessRights::Read)) {
|
||||
// can't get history in dialogs without read access
|
||||
return promise.set_value(Unit());
|
||||
}
|
||||
if (G()->close_flag()) {
|
||||
return promise.set_error(Status::Error(500, "Request aborted"));
|
||||
}
|
||||
int32 limit = MAX_GET_HISTORY;
|
||||
if (from_database && G()->parameters().use_message_db) {
|
||||
if (!promise) {
|
||||
@ -17799,8 +17883,20 @@ void MessagesManager::load_dialog_scheduled_messages(DialogId dialog_id, bool fr
|
||||
}
|
||||
|
||||
void MessagesManager::on_get_scheduled_messages_from_database(DialogId dialog_id, vector<BufferSlice> &&messages) {
|
||||
if (G()->close_flag()) {
|
||||
auto it = load_scheduled_messages_from_database_queries_.find(dialog_id);
|
||||
if (it == load_scheduled_messages_from_database_queries_.end()) { return; }
|
||||
if (it->second.empty()) { return; }
|
||||
auto promises = std::move(it->second);
|
||||
load_scheduled_messages_from_database_queries_.erase(it);
|
||||
|
||||
for (auto &promise : promises) {
|
||||
promise.set_error(Status::Error(500, "Request aborted"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
auto d = get_dialog(dialog_id);
|
||||
CHECK(d != nullptr);
|
||||
if (d == nullptr) { return; }
|
||||
d->has_loaded_scheduled_messages_from_database = true;
|
||||
|
||||
LOG(INFO) << "Receive " << messages.size() << " scheduled messages from database in " << dialog_id;
|
||||
@ -17970,12 +18066,13 @@ tl_object_ptr<td_api::message> MessagesManager::get_message_object(DialogId dial
|
||||
auto live_location_date = m->is_failed_to_send ? 0 : m->date;
|
||||
auto date = is_scheduled ? 0 : m->date;
|
||||
auto edit_date = m->hide_edit_date ? 0 : m->edit_date;
|
||||
auto views = m->message_id.is_scheduled() || (m->message_id.is_local() && m->forward_info == nullptr) ? 0 : m->views;
|
||||
return make_tl_object<td_api::message>(
|
||||
m->message_id.get(), td_->contacts_manager_->get_user_id_object(m->sender_user_id, "sender_user_id"),
|
||||
dialog_id.get(), std::move(sending_state), std::move(scheduling_state), is_outgoing, can_be_edited,
|
||||
can_be_forwarded, can_delete_for_self, can_delete_for_all_users, m->is_channel_post, contains_unread_mention,
|
||||
date, edit_date, get_message_forward_info_object(m->forward_info), reply_to_message_id, ttl, ttl_expires_in,
|
||||
td_->contacts_manager_->get_user_id_object(m->via_bot_user_id, "via_bot_user_id"), m->author_signature, m->views,
|
||||
td_->contacts_manager_->get_user_id_object(m->via_bot_user_id, "via_bot_user_id"), m->author_signature, views,
|
||||
media_album_id, get_restriction_reason_description(m->restriction_reasons),
|
||||
get_message_content_object(m->content.get(), td_, live_location_date, m->is_content_secret),
|
||||
get_reply_markup_object(m->reply_markup));
|
||||
@ -22212,6 +22309,9 @@ void MessagesManager::on_get_message_notifications_from_database(DialogId dialog
|
||||
NotificationId initial_from_notification_id,
|
||||
int32 limit, Result<vector<BufferSlice>> result,
|
||||
Promise<vector<Notification>> promise) {
|
||||
if (G()->close_flag()) {
|
||||
result = Status::Error(500, "Request aborted");
|
||||
}
|
||||
if (result.is_error()) {
|
||||
return promise.set_error(result.move_as_error());
|
||||
}
|
||||
@ -22398,7 +22498,7 @@ void MessagesManager::remove_message_notifications_by_message_ids(DialogId dialo
|
||||
|
||||
void MessagesManager::do_remove_message_notification(DialogId dialog_id, bool from_mentions,
|
||||
NotificationId notification_id, vector<BufferSlice> result) {
|
||||
if (result.empty()) {
|
||||
if (result.empty() || G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
CHECK(result.size() == 1);
|
||||
@ -22890,10 +22990,10 @@ void MessagesManager::send_update_delete_messages(DialogId dialog_id, vector<int
|
||||
make_tl_object<td_api::updateDeleteMessages>(dialog_id.get(), std::move(message_ids), is_permanent, from_cache));
|
||||
}
|
||||
|
||||
void MessagesManager::send_update_new_chat(Dialog *d) {
|
||||
void MessagesManager::send_update_new_chat(Dialog *d, int64 real_order) {
|
||||
CHECK(d != nullptr);
|
||||
CHECK(d->messages == nullptr);
|
||||
auto chat_object = get_chat_object(d);
|
||||
auto chat_object = get_chat_object(d, real_order);
|
||||
bool has_action_bar = chat_object->action_bar_ != nullptr;
|
||||
d->last_sent_has_scheduled_messages = chat_object->has_scheduled_messages_;
|
||||
send_closure(G()->td(), &Td::send_update, make_tl_object<td_api::updateNewChat>(std::move(chat_object)));
|
||||
@ -23729,6 +23829,9 @@ void MessagesManager::fail_send_message(FullMessageId full_message_id, int error
|
||||
} else {
|
||||
CHECK(message->message_id.is_valid());
|
||||
}
|
||||
if (message->forward_info == nullptr && message->views == 1) {
|
||||
message->views = 0;
|
||||
}
|
||||
message->is_failed_to_send = true;
|
||||
message->send_error_code = error_code;
|
||||
message->send_error_message = error_message;
|
||||
@ -24057,6 +24160,9 @@ void MessagesManager::set_dialog_has_scheduled_server_messages(Dialog *d, bool h
|
||||
|
||||
void MessagesManager::set_dialog_has_scheduled_database_messages(DialogId dialog_id,
|
||||
bool has_scheduled_database_messages) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
return set_dialog_has_scheduled_database_messages_impl(get_dialog(dialog_id), has_scheduled_database_messages);
|
||||
}
|
||||
|
||||
@ -24657,7 +24763,7 @@ bool MessagesManager::is_dialog_action_unneded(DialogId dialog_id) const {
|
||||
void MessagesManager::send_dialog_action(DialogId dialog_id, const tl_object_ptr<td_api::ChatAction> &action,
|
||||
Promise<Unit> &&promise) {
|
||||
if (action == nullptr) {
|
||||
return promise.set_error(Status::Error(5, "Action must not be empty"));
|
||||
return promise.set_error(Status::Error(5, "Action must be non-empty"));
|
||||
}
|
||||
|
||||
if (!have_dialog_force(dialog_id)) {
|
||||
@ -25219,7 +25325,7 @@ void MessagesManager::set_dialog_permissions(DialogId dialog_id,
|
||||
}
|
||||
|
||||
if (permissions == nullptr) {
|
||||
return promise.set_error(Status::Error(3, "New permissions must not be empty"));
|
||||
return promise.set_error(Status::Error(3, "New permissions must be non-empty"));
|
||||
}
|
||||
|
||||
switch (dialog_id.get_type()) {
|
||||
@ -26141,8 +26247,10 @@ MessagesManager::Message *MessagesManager::on_get_message_from_database(DialogId
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// can succeed in private and group chats
|
||||
get_message_from_server({dialog_id, m->message_id}, Auto());
|
||||
if (m->message_id.is_valid() && m->message_id.is_any_server() &&
|
||||
(dialog_id.get_type() == DialogType::User || dialog_id.get_type() == DialogType::Chat)) {
|
||||
get_message_from_server({dialog_id, m->message_id}, Auto());
|
||||
}
|
||||
|
||||
force_create_dialog(dialog_id, source);
|
||||
d = get_dialog_force(dialog_id);
|
||||
@ -26373,7 +26481,7 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (*need_update && message_id <= d->last_new_message_id) {
|
||||
if (*need_update && message_id <= d->last_new_message_id && !td_->auth_manager_->is_bot()) {
|
||||
*need_update = false;
|
||||
}
|
||||
|
||||
@ -26485,7 +26593,7 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq
|
||||
// in get_message_notification_group_force
|
||||
get_dialog_notification_group_id(d->dialog_id, get_notification_group_info(d, message.get()));
|
||||
}
|
||||
if (*need_update || (!d->last_new_message_id.is_valid() && !message_id.is_yet_unsent() && !message->from_database)) {
|
||||
if (*need_update || (!d->last_new_message_id.is_valid() && !message_id.is_yet_unsent())) {
|
||||
auto pinned_message_id = get_message_content_pinned_message_id(message->content.get());
|
||||
if (pinned_message_id.is_valid() && pinned_message_id < message_id &&
|
||||
have_message_force({dialog_id, pinned_message_id}, "preload pinned message")) {
|
||||
@ -26493,7 +26601,7 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq
|
||||
}
|
||||
|
||||
if (d->pinned_message_notification_message_id.is_valid() &&
|
||||
d->pinned_message_notification_message_id < message_id &&
|
||||
d->pinned_message_notification_message_id != message_id &&
|
||||
have_message_force({dialog_id, d->pinned_message_notification_message_id},
|
||||
"preload previously pinned message")) {
|
||||
LOG(INFO) << "Preloaded previously pinned " << d->pinned_message_notification_message_id << " from database";
|
||||
@ -27082,7 +27190,7 @@ void MessagesManager::delete_all_dialog_messages_from_database(Dialog *d, Messag
|
||||
}
|
||||
if (d->pinned_message_notification_message_id.is_valid() &&
|
||||
d->pinned_message_notification_message_id <= max_message_id) {
|
||||
remove_dialog_pinned_message_notification(d, "delete_all_dialog_messages_from_database");
|
||||
remove_dialog_pinned_message_notification(d, source);
|
||||
}
|
||||
remove_message_dialog_notifications(d, max_message_id, false, source);
|
||||
remove_message_dialog_notifications(d, max_message_id, true, source);
|
||||
@ -27995,7 +28103,7 @@ MessagesManager::Dialog *MessagesManager::add_new_dialog(unique_ptr<Dialog> &&d,
|
||||
|
||||
fix_dialog_action_bar(dialog);
|
||||
|
||||
send_update_new_chat(dialog);
|
||||
send_update_new_chat(dialog, order);
|
||||
|
||||
fix_new_dialog(dialog, std::move(last_database_message), last_database_message_id, order, last_clear_history_date,
|
||||
last_clear_history_message_id, is_loaded_from_database);
|
||||
@ -28225,8 +28333,6 @@ void MessagesManager::fix_new_dialog(Dialog *d, unique_ptr<Message> &&last_datab
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
update_dialogs_hints(d);
|
||||
|
||||
if (d->delete_last_message_date != 0) {
|
||||
if (d->last_message_id.is_valid()) {
|
||||
LOG(ERROR) << "Last " << d->deleted_last_message_id << " in " << dialog_id << " was deleted at "
|
||||
@ -28240,19 +28346,6 @@ void MessagesManager::fix_new_dialog(Dialog *d, unique_ptr<Message> &&last_datab
|
||||
}
|
||||
}
|
||||
|
||||
if (need_get_history && !td_->auth_manager_->is_bot() && have_input_peer(dialog_id, AccessRights::Read) &&
|
||||
(d->order != DEFAULT_ORDER || is_dialog_sponsored(d))) {
|
||||
get_history_from_the_end(dialog_id, true, false, Auto());
|
||||
}
|
||||
|
||||
if (d->need_repair_server_unread_count && need_unread_counter(d->order)) {
|
||||
CHECK(dialog_type != DialogType::SecretChat);
|
||||
repair_server_unread_count(dialog_id, d->server_unread_count);
|
||||
}
|
||||
if (d->need_repair_channel_server_unread_count) {
|
||||
repair_channel_server_unread_count(d);
|
||||
}
|
||||
|
||||
if (!G()->parameters().use_message_db) {
|
||||
d->has_loaded_scheduled_messages_from_database = true;
|
||||
}
|
||||
@ -28263,6 +28356,19 @@ void MessagesManager::fix_new_dialog(Dialog *d, unique_ptr<Message> &&last_datab
|
||||
LOG(ERROR) << dialog_id << " has order " << d->order << " instead of saved to database order " << order;
|
||||
}
|
||||
|
||||
// must be after update_dialog_pos, because uses d->order
|
||||
if (need_get_history && !td_->auth_manager_->is_bot() && dialog_id != being_added_dialog_id_ &&
|
||||
have_input_peer(dialog_id, AccessRights::Read) && (d->order != DEFAULT_ORDER || is_dialog_sponsored(d))) {
|
||||
get_history_from_the_end(dialog_id, true, false, Auto());
|
||||
}
|
||||
if (d->need_repair_server_unread_count && need_unread_counter(d->order)) {
|
||||
CHECK(dialog_type != DialogType::SecretChat);
|
||||
repair_server_unread_count(dialog_id, d->server_unread_count);
|
||||
}
|
||||
if (d->need_repair_channel_server_unread_count) {
|
||||
repair_channel_server_unread_count(d);
|
||||
}
|
||||
|
||||
LOG(INFO) << "Loaded " << dialog_id << " with last new " << d->last_new_message_id << ", first database "
|
||||
<< d->first_database_message_id << ", last database " << d->last_database_message_id << ", last "
|
||||
<< d->last_message_id << " with order " << d->order << " and pinned order " << d->pinned_order;
|
||||
@ -28507,7 +28613,7 @@ void MessagesManager::update_dialog_pos(Dialog *d, const char *source, bool need
|
||||
}
|
||||
}
|
||||
if (new_order == DEFAULT_ORDER && !d->is_empty) {
|
||||
LOG(INFO) << "There is no known messages in the chat, just leave it where it is";
|
||||
LOG(INFO) << "There are no known messages in the chat, just leave it where it is";
|
||||
new_order = d->order;
|
||||
}
|
||||
}
|
||||
@ -29931,6 +30037,7 @@ void MessagesManager::on_binlog_events(vector<BinlogEvent> &&events) {
|
||||
}
|
||||
|
||||
for (auto message_id : log_event.message_ids_) {
|
||||
CHECK(message_id.is_scheduled_server());
|
||||
d->deleted_scheduled_server_message_ids.insert(message_id.get_scheduled_server_message_id());
|
||||
}
|
||||
|
||||
|
@ -1680,7 +1680,7 @@ class MessagesManager : public Actor {
|
||||
|
||||
static void find_old_messages(const Message *m, MessageId max_message_id, vector<MessageId> &message_ids);
|
||||
|
||||
static void find_new_messages(const Message *m, MessageId min_message_id, vector<MessageId> &message_ids);
|
||||
static void find_newer_messages(const Message *m, MessageId min_message_id, vector<MessageId> &message_ids);
|
||||
|
||||
void find_unloadable_messages(const Dialog *d, int32 unload_before_date, const Message *m,
|
||||
vector<MessageId> &message_ids, int32 &left_to_unload) const;
|
||||
@ -1910,7 +1910,7 @@ class MessagesManager : public Actor {
|
||||
void send_update_delete_messages(DialogId dialog_id, vector<int64> &&message_ids, bool is_permanent,
|
||||
bool from_cache) const;
|
||||
|
||||
void send_update_new_chat(Dialog *d);
|
||||
void send_update_new_chat(Dialog *d, int64 real_order);
|
||||
|
||||
void send_update_chat_draft_message(const Dialog *d);
|
||||
|
||||
@ -2092,7 +2092,7 @@ class MessagesManager : public Actor {
|
||||
|
||||
td_api::object_ptr<td_api::ChatActionBar> get_chat_action_bar_object(const Dialog *d) const;
|
||||
|
||||
td_api::object_ptr<td_api::chat> get_chat_object(const Dialog *d) const;
|
||||
td_api::object_ptr<td_api::chat> get_chat_object(const Dialog *d, int64 real_order = DEFAULT_ORDER) const;
|
||||
|
||||
bool have_dialog_info(DialogId dialog_id) const;
|
||||
bool have_dialog_info_force(DialogId dialog_id) const;
|
||||
@ -2225,7 +2225,7 @@ class MessagesManager : public Actor {
|
||||
void on_messages_db_fts_result(Result<MessagesDbFtsResult> result, int64 random_id, Promise<> &&promise);
|
||||
|
||||
void on_messages_db_calls_result(Result<MessagesDbCallsResult> result, int64 random_id, MessageId first_db_message_id,
|
||||
SearchMessagesFilter filter, Promise<> &&promise);
|
||||
SearchMessagesFilter filter, Promise<Unit> &&promise);
|
||||
|
||||
void on_load_active_live_location_full_message_ids_from_database(string value);
|
||||
|
||||
|
@ -131,7 +131,7 @@ static int32 get_mute_until(int32 mute_for) {
|
||||
Result<DialogNotificationSettings> get_dialog_notification_settings(
|
||||
td_api::object_ptr<td_api::chatNotificationSettings> &¬ification_settings, bool old_silent_send_message) {
|
||||
if (notification_settings == nullptr) {
|
||||
return Status::Error(400, "New notification settings must not be empty");
|
||||
return Status::Error(400, "New notification settings must be non-empty");
|
||||
}
|
||||
if (!clean_input_string(notification_settings->sound_)) {
|
||||
return Status::Error(400, "Notification settings sound must be encoded in UTF-8");
|
||||
@ -155,7 +155,7 @@ Result<DialogNotificationSettings> get_dialog_notification_settings(
|
||||
Result<ScopeNotificationSettings> get_scope_notification_settings(
|
||||
td_api::object_ptr<td_api::scopeNotificationSettings> &¬ification_settings) {
|
||||
if (notification_settings == nullptr) {
|
||||
return Status::Error(400, "New notification settings must not be empty");
|
||||
return Status::Error(400, "New notification settings must be non-empty");
|
||||
}
|
||||
if (!clean_input_string(notification_settings->sound_)) {
|
||||
return Status::Error(400, "Notification settings sound must be encoded in UTF-8");
|
||||
|
@ -647,7 +647,7 @@ static Status check_postal_code(string &postal_code) {
|
||||
|
||||
Result<Address> get_address(td_api::object_ptr<td_api::address> &&address) {
|
||||
if (address == nullptr) {
|
||||
return Status::Error(400, "Address must not be empty");
|
||||
return Status::Error(400, "Address must be non-empty");
|
||||
}
|
||||
TRY_STATUS(check_country_code(address->country_code_));
|
||||
TRY_STATUS(check_state(address->state_));
|
||||
@ -770,7 +770,7 @@ void answer_shipping_query(int64 shipping_query_id, vector<tl_object_ptr<td_api:
|
||||
vector<tl_object_ptr<telegram_api::shippingOption>> options;
|
||||
for (auto &option : shipping_options) {
|
||||
if (option == nullptr) {
|
||||
return promise.set_error(Status::Error(400, "Shipping option must not be empty"));
|
||||
return promise.set_error(Status::Error(400, "Shipping option must be non-empty"));
|
||||
}
|
||||
if (!clean_input_string(option->id_)) {
|
||||
return promise.set_error(Status::Error(400, "Shipping option id must be encoded in UTF-8"));
|
||||
@ -782,7 +782,7 @@ void answer_shipping_query(int64 shipping_query_id, vector<tl_object_ptr<td_api:
|
||||
vector<tl_object_ptr<telegram_api::labeledPrice>> prices;
|
||||
for (auto &price_part : option->price_parts_) {
|
||||
if (price_part == nullptr) {
|
||||
return promise.set_error(Status::Error(400, "Shipping option price part must not be empty"));
|
||||
return promise.set_error(Status::Error(400, "Shipping option price part must be non-empty"));
|
||||
}
|
||||
if (!clean_input_string(price_part->label_)) {
|
||||
return promise.set_error(Status::Error(400, "Shipping option price part label must be encoded in UTF-8"));
|
||||
|
@ -95,18 +95,32 @@ td_api::object_ptr<td_api::minithumbnail> get_minithumbnail_object(const string
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static StringBuilder &operator<<(StringBuilder &string_builder, PhotoFormat format) {
|
||||
switch (format) {
|
||||
case PhotoFormat::Jpeg:
|
||||
return string_builder << "jpg";
|
||||
case PhotoFormat::Png:
|
||||
return string_builder << "png";
|
||||
case PhotoFormat::Webp:
|
||||
return string_builder << "webp";
|
||||
case PhotoFormat::Tgs:
|
||||
return string_builder << "tgs";
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return string_builder;
|
||||
}
|
||||
}
|
||||
|
||||
static FileId register_photo(FileManager *file_manager, const PhotoSizeSource &source, int64 id, int64 access_hash,
|
||||
std::string file_reference,
|
||||
tl_object_ptr<telegram_api::fileLocationToBeDeprecated> &&location,
|
||||
DialogId owner_dialog_id, int32 file_size, DcId dc_id, bool is_webp = false,
|
||||
bool is_png = false) {
|
||||
DialogId owner_dialog_id, int32 file_size, DcId dc_id, PhotoFormat format) {
|
||||
int32 local_id = location->local_id_;
|
||||
int64 volume_id = location->volume_id_;
|
||||
LOG(DEBUG) << "Receive " << (is_webp ? "webp" : (is_png ? "png" : "jpeg")) << " photo of type "
|
||||
<< source.get_file_type() << " in [" << dc_id << "," << volume_id << "," << local_id << "]. Id: (" << id
|
||||
<< ", " << access_hash << ")";
|
||||
auto suggested_name = PSTRING() << static_cast<uint64>(volume_id) << "_" << static_cast<uint64>(local_id)
|
||||
<< (is_webp ? ".webp" : (is_png ? ".png" : ".jpg"));
|
||||
LOG(DEBUG) << "Receive " << format << " photo of type " << source.get_file_type() << " in [" << dc_id << ","
|
||||
<< volume_id << "," << local_id << "]. Id: (" << id << ", " << access_hash << ")";
|
||||
auto suggested_name = PSTRING() << static_cast<uint64>(volume_id) << "_" << static_cast<uint64>(local_id) << '.'
|
||||
<< format;
|
||||
auto file_location_source = owner_dialog_id.get_type() == DialogType::SecretChat ? FileLocationSource::FromUser
|
||||
: FileLocationSource::FromServer;
|
||||
return file_manager->register_remote(
|
||||
@ -127,10 +141,12 @@ ProfilePhoto get_profile_photo(FileManager *file_manager, UserId user_id, int64
|
||||
|
||||
auto dc_id = DcId::create(profile_photo->dc_id_);
|
||||
result.id = profile_photo->photo_id_;
|
||||
result.small_file_id = register_photo(file_manager, {DialogId(user_id), user_access_hash, false}, result.id, 0,
|
||||
"", std::move(profile_photo->photo_small_), DialogId(), 0, dc_id);
|
||||
result.big_file_id = register_photo(file_manager, {DialogId(user_id), user_access_hash, true}, result.id, 0, "",
|
||||
std::move(profile_photo->photo_big_), DialogId(), 0, dc_id);
|
||||
result.small_file_id =
|
||||
register_photo(file_manager, {DialogId(user_id), user_access_hash, false}, result.id, 0, "",
|
||||
std::move(profile_photo->photo_small_), DialogId(), 0, dc_id, PhotoFormat::Jpeg);
|
||||
result.big_file_id =
|
||||
register_photo(file_manager, {DialogId(user_id), user_access_hash, true}, result.id, 0, "",
|
||||
std::move(profile_photo->photo_big_), DialogId(), 0, dc_id, PhotoFormat::Jpeg);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@ -176,10 +192,11 @@ DialogPhoto get_dialog_photo(FileManager *file_manager, DialogId dialog_id, int6
|
||||
auto chat_photo = move_tl_object_as<telegram_api::chatPhoto>(chat_photo_ptr);
|
||||
|
||||
auto dc_id = DcId::create(chat_photo->dc_id_);
|
||||
result.small_file_id = register_photo(file_manager, {dialog_id, dialog_access_hash, false}, 0, 0, "",
|
||||
std::move(chat_photo->photo_small_), DialogId(), 0, dc_id);
|
||||
result.small_file_id =
|
||||
register_photo(file_manager, {dialog_id, dialog_access_hash, false}, 0, 0, "",
|
||||
std::move(chat_photo->photo_small_), DialogId(), 0, dc_id, PhotoFormat::Jpeg);
|
||||
result.big_file_id = register_photo(file_manager, {dialog_id, dialog_access_hash, true}, 0, 0, "",
|
||||
std::move(chat_photo->photo_big_), DialogId(), 0, dc_id);
|
||||
std::move(chat_photo->photo_big_), DialogId(), 0, dc_id, PhotoFormat::Jpeg);
|
||||
|
||||
break;
|
||||
}
|
||||
@ -269,7 +286,7 @@ PhotoSize get_secret_thumbnail_photo_size(FileManager *file_manager, BufferSlice
|
||||
Variant<PhotoSize, string> get_photo_size(FileManager *file_manager, PhotoSizeSource source, int64 id,
|
||||
int64 access_hash, std::string file_reference, DcId dc_id,
|
||||
DialogId owner_dialog_id, tl_object_ptr<telegram_api::PhotoSize> &&size_ptr,
|
||||
bool is_webp, bool is_png) {
|
||||
PhotoFormat format) {
|
||||
CHECK(size_ptr != nullptr);
|
||||
|
||||
tl_object_ptr<telegram_api::fileLocationToBeDeprecated> location;
|
||||
@ -322,7 +339,7 @@ Variant<PhotoSize, string> get_photo_size(FileManager *file_manager, PhotoSizeSo
|
||||
}
|
||||
|
||||
res.file_id = register_photo(file_manager, source, id, access_hash, file_reference, std::move(location),
|
||||
owner_dialog_id, res.size, dc_id, is_webp, is_png);
|
||||
owner_dialog_id, res.size, dc_id, format);
|
||||
|
||||
if (!content.empty()) {
|
||||
file_manager->set_content(res.file_id, std::move(content));
|
||||
@ -521,7 +538,7 @@ Photo get_photo(FileManager *file_manager, tl_object_ptr<telegram_api::photo> &&
|
||||
for (auto &size_ptr : photo->sizes_) {
|
||||
auto photo_size = get_photo_size(file_manager, {FileType::Photo, 0}, photo->id_, photo->access_hash_,
|
||||
photo->file_reference_.as_slice().str(), DcId::create(photo->dc_id_),
|
||||
owner_dialog_id, std::move(size_ptr), false, false);
|
||||
owner_dialog_id, std::move(size_ptr), PhotoFormat::Jpeg);
|
||||
if (photo_size.get_offset() == 0) {
|
||||
PhotoSize &size = photo_size.get<0>();
|
||||
if (size.type == 0 || size.type == 't' || size.type == 'i') {
|
||||
|
@ -90,12 +90,14 @@ bool operator!=(const DialogPhoto &lhs, const DialogPhoto &rhs);
|
||||
|
||||
StringBuilder &operator<<(StringBuilder &string_builder, const DialogPhoto &dialog_photo);
|
||||
|
||||
enum class PhotoFormat : int32 { Jpeg, Png, Webp, Tgs };
|
||||
|
||||
PhotoSize get_secret_thumbnail_photo_size(FileManager *file_manager, BufferSlice bytes, DialogId owner_dialog_id,
|
||||
int32 width, int32 height);
|
||||
Variant<PhotoSize, string> get_photo_size(FileManager *file_manager, PhotoSizeSource source, int64 id,
|
||||
int64 access_hash, string file_reference, DcId dc_id,
|
||||
DialogId owner_dialog_id, tl_object_ptr<telegram_api::PhotoSize> &&size_ptr,
|
||||
bool is_webp, bool is_png);
|
||||
PhotoFormat format);
|
||||
PhotoSize get_web_document_photo_size(FileManager *file_manager, FileType file_type, DialogId owner_dialog_id,
|
||||
tl_object_ptr<telegram_api::WebDocument> web_document_ptr);
|
||||
td_api::object_ptr<td_api::photoSize> get_photo_size_object(FileManager *file_manager, const PhotoSize *photo_size);
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "td/telegram/PhotoSizeSource.h"
|
||||
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/tl_helpers.h"
|
||||
|
||||
namespace td {
|
||||
@ -69,7 +70,7 @@ void parse(PhotoSizeSource::DialogPhoto &source, ParserT &parser) {
|
||||
switch (source.dialog_id.get_type()) {
|
||||
case DialogType::SecretChat:
|
||||
case DialogType::None:
|
||||
return parser.set_error("Invalid chat identifier");
|
||||
return parser.set_error(PSTRING() << "Invalid chat identifier " << source.dialog_id.get());
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -1297,7 +1297,8 @@ PollId PollManager::on_get_poll(PollId poll_id, tl_object_ptr<telegram_api::poll
|
||||
poll_id = PollId(poll_server->id_);
|
||||
}
|
||||
if (!poll_id.is_valid() || is_local_poll_id(poll_id)) {
|
||||
LOG(ERROR) << "Receive " << poll_id << " from server";
|
||||
LOG(ERROR) << "Receive " << poll_id << " from server: " << oneline(to_string(poll_server)) << " "
|
||||
<< oneline(to_string(poll_results));
|
||||
return PollId();
|
||||
}
|
||||
if (poll_server != nullptr && poll_server->id_ != poll_id.get()) {
|
||||
|
@ -757,7 +757,7 @@ static Status check_gender(string &gender) {
|
||||
|
||||
static Result<string> get_personal_details(td_api::object_ptr<td_api::personalDetails> &&personal_details) {
|
||||
if (personal_details == nullptr) {
|
||||
return Status::Error(400, "Personal details must not be empty");
|
||||
return Status::Error(400, "Personal details must be non-empty");
|
||||
}
|
||||
TRY_STATUS(check_name(personal_details->first_name_));
|
||||
TRY_STATUS(check_name(personal_details->middle_name_));
|
||||
@ -767,7 +767,7 @@ static Result<string> get_personal_details(td_api::object_ptr<td_api::personalDe
|
||||
TRY_STATUS(check_name(personal_details->native_last_name_));
|
||||
TRY_RESULT(birthdate, get_date(std::move(personal_details->birthdate_)));
|
||||
if (birthdate.empty()) {
|
||||
return Status::Error(400, "Birthdate must not be empty");
|
||||
return Status::Error(400, "Birthdate must be non-empty");
|
||||
}
|
||||
TRY_STATUS(check_gender(personal_details->gender_));
|
||||
TRY_STATUS(check_country_code(personal_details->country_code_));
|
||||
@ -808,7 +808,7 @@ static Result<td_api::object_ptr<td_api::personalDetails>> get_personal_details_
|
||||
TRY_RESULT(native_last_name, get_json_object_string_field(object, "last_name_native", true));
|
||||
TRY_RESULT(birthdate, get_json_object_string_field(object, "birth_date", true));
|
||||
if (birthdate.empty()) {
|
||||
return Status::Error(400, "Birthdate must not be empty");
|
||||
return Status::Error(400, "Birthdate must be non-empty");
|
||||
}
|
||||
TRY_RESULT(gender, get_json_object_string_field(object, "gender", true));
|
||||
TRY_RESULT(country_code, get_json_object_string_field(object, "country_code", true));
|
||||
@ -836,7 +836,7 @@ static Status check_document_number(string &number) {
|
||||
return Status::Error(400, "Document number must be encoded in UTF-8");
|
||||
}
|
||||
if (number.empty()) {
|
||||
return Status::Error(400, "Document number must not be empty");
|
||||
return Status::Error(400, "Document number must be non-empty");
|
||||
}
|
||||
if (utf8_length(number) > 24) {
|
||||
return Status::Error(400, "Document number is too long");
|
||||
@ -867,7 +867,7 @@ static Result<SecureValue> get_identity_document(SecureValueType type, FileManag
|
||||
td_api::object_ptr<td_api::inputIdentityDocument> &&identity_document,
|
||||
bool need_reverse_side) {
|
||||
if (identity_document == nullptr) {
|
||||
return Status::Error(400, "Identity document must not be empty");
|
||||
return Status::Error(400, "Identity document must be non-empty");
|
||||
}
|
||||
TRY_STATUS(check_document_number(identity_document->number_));
|
||||
TRY_RESULT(date, get_date(std::move(identity_document->expiry_date_)));
|
||||
@ -950,7 +950,7 @@ static Result<SecureValue> get_personal_document(
|
||||
SecureValueType type, FileManager *file_manager,
|
||||
td_api::object_ptr<td_api::inputPersonalDocument> &&personal_document) {
|
||||
if (personal_document == nullptr) {
|
||||
return Status::Error(400, "Personal document must not be empty");
|
||||
return Status::Error(400, "Personal document must be non-empty");
|
||||
}
|
||||
|
||||
SecureValue res;
|
||||
@ -988,7 +988,7 @@ static Status check_email_address(string &email_address) {
|
||||
Result<SecureValue> get_secure_value(FileManager *file_manager,
|
||||
td_api::object_ptr<td_api::InputPassportElement> &&input_passport_element) {
|
||||
if (input_passport_element == nullptr) {
|
||||
return Status::Error(400, "InputPassportElement must not be empty");
|
||||
return Status::Error(400, "InputPassportElement must be non-empty");
|
||||
}
|
||||
|
||||
SecureValue res;
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include "td/telegram/AccessRights.h"
|
||||
#include "td/telegram/AuthManager.h"
|
||||
#include "td/telegram/ConfigManager.h"
|
||||
#include "td/telegram/ConfigShared.h"
|
||||
#include "td/telegram/ContactsManager.h"
|
||||
#include "td/telegram/DialogId.h"
|
||||
@ -1592,9 +1593,10 @@ std::pair<int64, FileId> StickersManager::on_get_sticker_document(
|
||||
|
||||
PhotoSize thumbnail;
|
||||
for (auto &thumb : document->thumbs_) {
|
||||
auto photo_size = get_photo_size(td_->file_manager_.get(), {FileType::Thumbnail, 0}, document_id,
|
||||
document->access_hash_, document->file_reference_.as_slice().str(), dc_id,
|
||||
DialogId(), std::move(thumb), has_webp_thumbnail(sticker), false);
|
||||
auto photo_size =
|
||||
get_photo_size(td_->file_manager_.get(), {FileType::Thumbnail, 0}, document_id, document->access_hash_,
|
||||
document->file_reference_.as_slice().str(), dc_id, DialogId(), std::move(thumb),
|
||||
has_webp_thumbnail(sticker) ? PhotoFormat::Webp : PhotoFormat::Jpeg);
|
||||
if (photo_size.get_offset() == 0) {
|
||||
thumbnail = std::move(photo_size.get<0>());
|
||||
break;
|
||||
@ -1661,9 +1663,11 @@ StickerSetId StickersManager::get_sticker_set_id(const tl_object_ptr<telegram_ap
|
||||
return search_sticker_set(static_cast<const telegram_api::inputStickerSetShortName *>(set_ptr.get())->short_name_,
|
||||
Auto());
|
||||
case telegram_api::inputStickerSetAnimatedEmoji::ID:
|
||||
case telegram_api::inputStickerSetDice::ID:
|
||||
LOG(ERROR) << "Receive special sticker set " << to_string(set_ptr);
|
||||
return add_special_sticker_set(SpecialStickerSetType(set_ptr).type_).id_;
|
||||
case telegram_api::inputStickerSetDice::ID:
|
||||
LOG(ERROR) << "Receive special sticker set " << to_string(set_ptr);
|
||||
return StickerSetId();
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return StickerSetId();
|
||||
@ -1687,9 +1691,11 @@ StickerSetId StickersManager::add_sticker_set(tl_object_ptr<telegram_api::InputS
|
||||
return search_sticker_set(set->short_name_, Auto());
|
||||
}
|
||||
case telegram_api::inputStickerSetAnimatedEmoji::ID:
|
||||
case telegram_api::inputStickerSetDice::ID:
|
||||
LOG(ERROR) << "Receive special sticker set " << to_string(set_ptr);
|
||||
return add_special_sticker_set(SpecialStickerSetType(set_ptr).type_).id_;
|
||||
case telegram_api::inputStickerSetDice::ID:
|
||||
LOG(ERROR) << "Receive special sticker set " << to_string(set_ptr);
|
||||
return StickerSetId();
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return StickerSetId();
|
||||
@ -1784,9 +1790,9 @@ bool StickersManager::merge_stickers(FileId new_id, FileId old_id, bool can_dele
|
||||
Sticker *new_ = new_it->second.get();
|
||||
CHECK(new_ != nullptr);
|
||||
|
||||
if (old_->alt != new_->alt || old_->set_id != new_->set_id ||
|
||||
(!old_->is_animated && !new_->is_animated && old_->dimensions.width != 0 && old_->dimensions.height != 0 &&
|
||||
old_->dimensions != new_->dimensions)) {
|
||||
if (old_->set_id == new_->set_id && (old_->alt != new_->alt || old_->set_id != new_->set_id ||
|
||||
(!old_->is_animated && !new_->is_animated && old_->dimensions.width != 0 &&
|
||||
old_->dimensions.height != 0 && old_->dimensions != new_->dimensions))) {
|
||||
LOG(ERROR) << "Sticker has changed: alt = (" << old_->alt << ", " << new_->alt << "), set_id = (" << old_->set_id
|
||||
<< ", " << new_->set_id << "), dimensions = (" << old_->dimensions << ", " << new_->dimensions << ")";
|
||||
}
|
||||
@ -1878,11 +1884,14 @@ StickerSetId StickersManager::on_get_input_sticker_set(FileId sticker_file_id,
|
||||
}
|
||||
}));
|
||||
}
|
||||
return set_id;
|
||||
// always return empty StickerSetId, because we can't trust the set_id provided by the peer in the secret chat
|
||||
// the real sticker set id will be set in on_get_sticker if and only if the sticker is really from the set
|
||||
return StickerSetId();
|
||||
}
|
||||
case telegram_api::inputStickerSetAnimatedEmoji::ID:
|
||||
case telegram_api::inputStickerSetDice::ID:
|
||||
return add_special_sticker_set(SpecialStickerSetType(set_ptr).type_).id_;
|
||||
case telegram_api::inputStickerSetDice::ID:
|
||||
return StickerSetId();
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return StickerSetId();
|
||||
@ -1946,7 +1955,9 @@ void StickersManager::create_sticker(FileId file_id, PhotoSize thumbnail, Dimens
|
||||
}
|
||||
}
|
||||
}
|
||||
s->is_animated = is_animated;
|
||||
if (s->set_id.is_valid()) {
|
||||
s->is_animated = is_animated;
|
||||
}
|
||||
on_get_sticker(std::move(s), sticker != nullptr);
|
||||
}
|
||||
|
||||
@ -2108,7 +2119,8 @@ StickerSetId StickersManager::on_get_sticker_set(tl_object_ptr<telegram_api::sti
|
||||
PhotoSize thumbnail;
|
||||
if (set->thumb_ != nullptr) {
|
||||
auto photo_size = get_photo_size(td_->file_manager_.get(), {set_id.get(), s->access_hash}, 0, 0, "",
|
||||
DcId::create(set->thumb_dc_id_), DialogId(), std::move(set->thumb_), true, false);
|
||||
DcId::create(set->thumb_dc_id_), DialogId(), std::move(set->thumb_),
|
||||
is_animated ? PhotoFormat::Tgs : PhotoFormat::Webp);
|
||||
if (photo_size.get_offset() == 0) {
|
||||
thumbnail = std::move(photo_size.get<0>());
|
||||
} else {
|
||||
@ -3082,6 +3094,9 @@ void StickersManager::load_installed_sticker_sets(bool is_masks, Promise<Unit> &
|
||||
}
|
||||
|
||||
void StickersManager::on_load_installed_sticker_sets_from_database(bool is_masks, string value) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
if (value.empty()) {
|
||||
LOG(INFO) << "Installed " << (is_masks ? "mask " : "") << "sticker sets aren't found in database";
|
||||
reload_installed_sticker_sets(is_masks, true);
|
||||
@ -3193,7 +3208,7 @@ string StickersManager::get_sticker_set_database_value(const StickerSet *s, bool
|
||||
void StickersManager::update_sticker_set(StickerSet *sticker_set) {
|
||||
CHECK(sticker_set != nullptr);
|
||||
if (sticker_set->is_changed || sticker_set->need_save_to_database) {
|
||||
if (G()->parameters().use_file_db) {
|
||||
if (G()->parameters().use_file_db && !G()->close_flag()) {
|
||||
LOG(INFO) << "Save " << sticker_set->id << " to database";
|
||||
if (sticker_set->is_inited) {
|
||||
G()->td_db()->get_sqlite_pmc()->set(get_sticker_set_database_key(sticker_set->id),
|
||||
@ -3288,6 +3303,9 @@ void StickersManager::load_sticker_sets_without_stickers(vector<StickerSetId> &&
|
||||
}
|
||||
|
||||
void StickersManager::on_load_sticker_set_from_database(StickerSetId sticker_set_id, bool with_stickers, string value) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
StickerSet *sticker_set = get_sticker_set(sticker_set_id);
|
||||
CHECK(sticker_set != nullptr);
|
||||
if (sticker_set->was_loaded) {
|
||||
@ -3411,12 +3429,22 @@ void StickersManager::on_update_dice_emojis() {
|
||||
return;
|
||||
}
|
||||
|
||||
auto dice_emojis_str = G()->shared_config().get_option_string("dice_emojis", "🎲\x01🎯");
|
||||
auto dice_emojis_str = G()->shared_config().get_option_string("dice_emojis", "🎲\x01🎯\x01🏀");
|
||||
if (dice_emojis_str == dice_emojis_str_) {
|
||||
return;
|
||||
}
|
||||
dice_emojis_str_ = std::move(dice_emojis_str);
|
||||
dice_emojis_ = full_split(dice_emojis_str_, '\x01');
|
||||
auto new_dice_emojis = full_split(dice_emojis_str_, '\x01');
|
||||
for (auto &emoji : new_dice_emojis) {
|
||||
if (!td::contains(dice_emojis_, emoji)) {
|
||||
auto &special_sticker_set = add_special_sticker_set(SpecialStickerSetType::animated_dice(emoji));
|
||||
CHECK(!special_sticker_set.id_.is_valid());
|
||||
|
||||
LOG(INFO) << "Load new dice sticker set for emoji " << emoji;
|
||||
load_special_sticker_set(special_sticker_set);
|
||||
}
|
||||
}
|
||||
dice_emojis_ = std::move(new_dice_emojis);
|
||||
|
||||
send_closure(G()->td(), &Td::send_update, get_update_dice_emojis_object());
|
||||
}
|
||||
@ -3430,7 +3458,7 @@ void StickersManager::on_update_dice_success_values() {
|
||||
return;
|
||||
}
|
||||
|
||||
auto dice_success_values_str = G()->shared_config().get_option_string("dice_success_values", "0,0");
|
||||
auto dice_success_values_str = G()->shared_config().get_option_string("dice_success_values", "0,0,0");
|
||||
if (dice_success_values_str == dice_success_values_str_) {
|
||||
return;
|
||||
}
|
||||
@ -3464,7 +3492,12 @@ void StickersManager::register_dice(const string &emoji, int32 value, FullMessag
|
||||
bool is_inserted = dice_messages_[emoji].insert(full_message_id).second;
|
||||
LOG_CHECK(is_inserted) << source << " " << emoji << " " << value << " " << full_message_id;
|
||||
|
||||
if (!td::contains(dice_emojis_, emoji) && !full_message_id.get_message_id().is_any_server()) {
|
||||
if (!td::contains(dice_emojis_, emoji)) {
|
||||
if (full_message_id.get_message_id().is_any_server() &&
|
||||
full_message_id.get_dialog_id().get_type() != DialogType::SecretChat) {
|
||||
send_closure(G()->config_manager(), &ConfigManager::get_app_config,
|
||||
Promise<td_api::object_ptr<td_api::JsonValue>>());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -3697,6 +3730,10 @@ void StickersManager::on_old_featured_sticker_sets_invalidated() {
|
||||
}
|
||||
|
||||
void StickersManager::invalidate_old_featured_sticker_sets() {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(INFO) << "Invalidate old featured sticker sets";
|
||||
if (G()->parameters().use_file_db) {
|
||||
G()->td_db()->get_binlog_pmc()->erase("invalidate_old_featured_sticker_sets");
|
||||
@ -3801,7 +3838,7 @@ void StickersManager::on_get_featured_sticker_sets(
|
||||
|
||||
if (offset >= 0) {
|
||||
if (generation == old_featured_sticker_set_generation_) {
|
||||
if (G()->parameters().use_file_db) {
|
||||
if (G()->parameters().use_file_db && !G()->close_flag()) {
|
||||
LOG(INFO) << "Save old trending sticker sets to database with offset " << old_featured_sticker_set_ids_.size();
|
||||
CHECK(old_featured_sticker_set_ids_.size() % OLD_FEATURED_STICKER_SET_SLICE_SIZE == 0);
|
||||
StickerSetListLogEvent log_event(featured_sticker_set_ids);
|
||||
@ -3819,7 +3856,7 @@ void StickersManager::on_get_featured_sticker_sets(
|
||||
|
||||
LOG_IF(ERROR, featured_sticker_sets_hash_ != featured_stickers->hash_) << "Trending sticker sets hash mismatch";
|
||||
|
||||
if (!G()->parameters().use_file_db) {
|
||||
if (!G()->parameters().use_file_db || G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -3874,6 +3911,9 @@ void StickersManager::load_featured_sticker_sets(Promise<Unit> &&promise) {
|
||||
}
|
||||
|
||||
void StickersManager::on_load_featured_sticker_sets_from_database(string value) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
if (value.empty()) {
|
||||
LOG(INFO) << "Trending sticker sets aren't found in database";
|
||||
reload_featured_sticker_sets(true);
|
||||
@ -3950,6 +3990,9 @@ void StickersManager::load_old_featured_sticker_sets(Promise<Unit> &&promise) {
|
||||
}
|
||||
|
||||
void StickersManager::on_load_old_featured_sticker_sets_from_database(uint32 generation, string value) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
if (generation != old_featured_sticker_set_generation_) {
|
||||
return;
|
||||
}
|
||||
@ -4155,7 +4198,7 @@ string &StickersManager::get_input_sticker_emojis(td_api::InputSticker *sticker)
|
||||
|
||||
Result<std::tuple<FileId, bool, bool, bool>> StickersManager::prepare_input_sticker(td_api::InputSticker *sticker) {
|
||||
if (sticker == nullptr) {
|
||||
return Status::Error(3, "Input sticker must not be empty");
|
||||
return Status::Error(3, "Input sticker must be non-empty");
|
||||
}
|
||||
|
||||
if (!clean_input_string(get_input_sticker_emojis(sticker))) {
|
||||
@ -4849,7 +4892,7 @@ void StickersManager::send_update_installed_sticker_sets(bool from_database) {
|
||||
installed_sticker_sets_hash_[is_masks] = get_sticker_sets_hash(installed_sticker_set_ids_[is_masks]);
|
||||
send_closure(G()->td(), &Td::send_update, get_update_installed_sticker_sets_object(is_masks));
|
||||
|
||||
if (G()->parameters().use_file_db && !from_database) {
|
||||
if (G()->parameters().use_file_db && !from_database && !G()->close_flag()) {
|
||||
LOG(INFO) << "Save installed " << (is_masks ? "mask " : "") << "sticker sets to database";
|
||||
StickerSetListLogEvent log_event(installed_sticker_set_ids_[is_masks]);
|
||||
G()->td_db()->get_sqlite_pmc()->set(is_masks ? "sss1" : "sss0", log_event_store(log_event).as_slice().str(),
|
||||
@ -4936,6 +4979,9 @@ void StickersManager::load_recent_stickers(bool is_attached, Promise<Unit> &&pro
|
||||
}
|
||||
|
||||
void StickersManager::on_load_recent_stickers_from_database(bool is_attached, string value) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
if (value.empty()) {
|
||||
LOG(INFO) << "Recent " << (is_attached ? "attached " : "") << "stickers aren't found in database";
|
||||
reload_recent_stickers(is_attached, true);
|
||||
@ -5256,7 +5302,7 @@ void StickersManager::send_update_recent_stickers(bool from_database) {
|
||||
}
|
||||
|
||||
void StickersManager::save_recent_stickers_to_database(bool is_attached) {
|
||||
if (G()->parameters().use_file_db) {
|
||||
if (G()->parameters().use_file_db && !G()->close_flag()) {
|
||||
LOG(INFO) << "Save recent " << (is_attached ? "attached " : "") << "stickers to database";
|
||||
StickerListLogEvent log_event(recent_sticker_ids_[is_attached]);
|
||||
G()->td_db()->get_sqlite_pmc()->set(is_attached ? "ssr1" : "ssr0", log_event_store(log_event).as_slice().str(),
|
||||
@ -5356,6 +5402,9 @@ void StickersManager::load_favorite_stickers(Promise<Unit> &&promise) {
|
||||
}
|
||||
|
||||
void StickersManager::on_load_favorite_stickers_from_database(const string &value) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
if (value.empty()) {
|
||||
LOG(INFO) << "Favorite stickers aren't found in database";
|
||||
reload_favorite_stickers(true);
|
||||
@ -5620,7 +5669,7 @@ void StickersManager::send_update_favorite_stickers(bool from_database) {
|
||||
}
|
||||
|
||||
void StickersManager::save_favorite_stickers_to_database() {
|
||||
if (G()->parameters().use_file_db) {
|
||||
if (G()->parameters().use_file_db && !G()->close_flag()) {
|
||||
LOG(INFO) << "Save favorite stickers to database";
|
||||
StickerListLogEvent log_event(favorite_sticker_ids_);
|
||||
G()->td_db()->get_sqlite_pmc()->set("ssfav", log_event_store(log_event).as_slice().str(), Auto());
|
||||
@ -5790,7 +5839,10 @@ void StickersManager::on_get_language_codes(const string &key, Result<vector<str
|
||||
CHECK(it != emoji_language_codes_.end());
|
||||
if (it->second != language_codes) {
|
||||
LOG(INFO) << "Update emoji language codes for " << key << " to " << language_codes;
|
||||
G()->td_db()->get_sqlite_pmc()->set(key, implode(language_codes, '$'), Auto());
|
||||
if (!G()->close_flag()) {
|
||||
CHECK(G()->parameters().use_file_db);
|
||||
G()->td_db()->get_sqlite_pmc()->set(key, implode(language_codes, '$'), Auto());
|
||||
}
|
||||
it->second = std::move(language_codes);
|
||||
}
|
||||
|
||||
@ -5924,7 +5976,8 @@ void StickersManager::on_get_emoji_keywords(
|
||||
is_good = false;
|
||||
}
|
||||
}
|
||||
if (is_good) {
|
||||
if (is_good && !G()->close_flag()) {
|
||||
CHECK(G()->parameters().use_file_db);
|
||||
G()->td_db()->get_sqlite_pmc()->set(get_language_emojis_database_key(language_code, text),
|
||||
implode(keyword->emoticons_, '$'), mpas.get_promise());
|
||||
}
|
||||
@ -5937,10 +5990,13 @@ void StickersManager::on_get_emoji_keywords(
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
G()->td_db()->get_sqlite_pmc()->set(get_emoji_language_code_version_database_key(language_code), to_string(version),
|
||||
mpas.get_promise());
|
||||
G()->td_db()->get_sqlite_pmc()->set(get_emoji_language_code_last_difference_time_database_key(language_code),
|
||||
to_string(G()->unix_time()), mpas.get_promise());
|
||||
if (!G()->close_flag()) {
|
||||
CHECK(G()->parameters().use_file_db);
|
||||
G()->td_db()->get_sqlite_pmc()->set(get_emoji_language_code_version_database_key(language_code), to_string(version),
|
||||
mpas.get_promise());
|
||||
G()->td_db()->get_sqlite_pmc()->set(get_emoji_language_code_last_difference_time_database_key(language_code),
|
||||
to_string(G()->unix_time()), mpas.get_promise());
|
||||
}
|
||||
emoji_language_code_versions_[language_code] = version;
|
||||
emoji_language_code_last_difference_times_[language_code] = static_cast<int32>(Time::now_cached());
|
||||
|
||||
|
@ -99,11 +99,11 @@
|
||||
|
||||
#include "td/db/binlog/BinlogEvent.h"
|
||||
|
||||
#include "td/mtproto/crypto.h"
|
||||
#include "td/mtproto/DhHandshake.h"
|
||||
#include "td/mtproto/Handshake.h"
|
||||
#include "td/mtproto/HandshakeActor.h"
|
||||
#include "td/mtproto/RawConnection.h"
|
||||
#include "td/mtproto/RSA.h"
|
||||
#include "td/mtproto/TransportType.h"
|
||||
|
||||
#include "td/utils/buffer.h"
|
||||
@ -587,10 +587,10 @@ class TestProxyRequest : public RequestOnceActor {
|
||||
}
|
||||
|
||||
auto dc_options = ConnectionCreator::get_default_dc_options(false);
|
||||
IPAddress mtproto_ip;
|
||||
IPAddress mtproto_ip_address;
|
||||
for (auto &dc_option : dc_options.dc_options) {
|
||||
if (dc_option.get_dc_id().get_raw_id() == dc_id_) {
|
||||
mtproto_ip = dc_option.get_ip_address();
|
||||
mtproto_ip_address = dc_option.get_ip_address();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -601,8 +601,8 @@ class TestProxyRequest : public RequestOnceActor {
|
||||
});
|
||||
|
||||
child_ =
|
||||
ConnectionCreator::prepare_connection(r_socket_fd.move_as_ok(), proxy_, mtproto_ip, get_transport(), "Test",
|
||||
"TestPingDC2", nullptr, {}, false, std::move(connection_promise));
|
||||
ConnectionCreator::prepare_connection(r_socket_fd.move_as_ok(), proxy_, mtproto_ip_address, get_transport(),
|
||||
"Test", "TestPingDC2", nullptr, {}, false, std::move(connection_promise));
|
||||
}
|
||||
|
||||
void on_connection_data(Result<ConnectionCreator::ConnectionData> r_data) {
|
||||
@ -5804,7 +5804,7 @@ void Td::on_request(uint64 id, td_api::createCall &request) {
|
||||
});
|
||||
|
||||
if (!request.protocol_) {
|
||||
return query_promise.set_error(Status::Error(5, "Call protocol must not be empty"));
|
||||
return query_promise.set_error(Status::Error(5, "Call protocol must be non-empty"));
|
||||
}
|
||||
|
||||
UserId user_id(request.user_id_);
|
||||
@ -5832,7 +5832,7 @@ void Td::on_request(uint64 id, td_api::acceptCall &request) {
|
||||
CHECK_IS_USER();
|
||||
CREATE_OK_REQUEST_PROMISE();
|
||||
if (!request.protocol_) {
|
||||
return promise.set_error(Status::Error(5, "Call protocol must not be empty"));
|
||||
return promise.set_error(Status::Error(5, "Call protocol must be non-empty"));
|
||||
}
|
||||
send_closure(G()->call_manager(), &CallManager::accept_call, CallId(request.call_id_),
|
||||
CallProtocol::from_td_api(*request.protocol_), std::move(promise));
|
||||
@ -6238,7 +6238,7 @@ void Td::on_request(uint64 id, const td_api::getBlockedUsers &request) {
|
||||
void Td::on_request(uint64 id, td_api::addContact &request) {
|
||||
CHECK_IS_USER();
|
||||
if (request.contact_ == nullptr) {
|
||||
return send_error_raw(id, 5, "Contact must not be empty");
|
||||
return send_error_raw(id, 5, "Contact must be non-empty");
|
||||
}
|
||||
CLEAN_INPUT_STRING(request.contact_->phone_number_);
|
||||
CLEAN_INPUT_STRING(request.contact_->first_name_);
|
||||
@ -6251,7 +6251,7 @@ void Td::on_request(uint64 id, td_api::importContacts &request) {
|
||||
CHECK_IS_USER();
|
||||
for (auto &contact : request.contacts_) {
|
||||
if (contact == nullptr) {
|
||||
return send_error_raw(id, 5, "Contact must not be empty");
|
||||
return send_error_raw(id, 5, "Contact must be non-empty");
|
||||
}
|
||||
CLEAN_INPUT_STRING(contact->phone_number_);
|
||||
CLEAN_INPUT_STRING(contact->first_name_);
|
||||
@ -6285,7 +6285,7 @@ void Td::on_request(uint64 id, td_api::changeImportedContacts &request) {
|
||||
CHECK_IS_USER();
|
||||
for (auto &contact : request.contacts_) {
|
||||
if (contact == nullptr) {
|
||||
return send_error_raw(id, 5, "Contact must not be empty");
|
||||
return send_error_raw(id, 5, "Contact must be non-empty");
|
||||
}
|
||||
CLEAN_INPUT_STRING(contact->phone_number_);
|
||||
CLEAN_INPUT_STRING(contact->first_name_);
|
||||
@ -6604,7 +6604,7 @@ void Td::on_request(uint64 id, const td_api::getChatNotificationSettingsExceptio
|
||||
void Td::on_request(uint64 id, const td_api::getScopeNotificationSettings &request) {
|
||||
CHECK_IS_USER();
|
||||
if (request.scope_ == nullptr) {
|
||||
return send_error_raw(id, 400, "Scope must not be empty");
|
||||
return send_error_raw(id, 400, "Scope must be non-empty");
|
||||
}
|
||||
CREATE_REQUEST(GetScopeNotificationSettingsRequest, get_notification_settings_scope(request.scope_));
|
||||
}
|
||||
@ -6652,7 +6652,7 @@ void Td::on_request(uint64 id, td_api::setChatNotificationSettings &request) {
|
||||
void Td::on_request(uint64 id, td_api::setScopeNotificationSettings &request) {
|
||||
CHECK_IS_USER();
|
||||
if (request.scope_ == nullptr) {
|
||||
return send_error_raw(id, 400, "Scope must not be empty");
|
||||
return send_error_raw(id, 400, "Scope must be non-empty");
|
||||
}
|
||||
answer_ok_query(id, messages_manager_->set_scope_notification_settings(
|
||||
get_notification_settings_scope(request.scope_), std::move(request.notification_settings_)));
|
||||
@ -7207,7 +7207,7 @@ void Td::on_request(uint64 id, td_api::sendPaymentForm &request) {
|
||||
CLEAN_INPUT_STRING(request.order_info_id_);
|
||||
CLEAN_INPUT_STRING(request.shipping_option_id_);
|
||||
if (request.credentials_ == nullptr) {
|
||||
return send_error_raw(id, 400, "Input payments credentials must not be empty");
|
||||
return send_error_raw(id, 400, "Input payments credentials must be non-empty");
|
||||
}
|
||||
CREATE_REQUEST_PROMISE();
|
||||
messages_manager_->send_payment_form({DialogId(request.chat_id_), MessageId(request.message_id_)},
|
||||
@ -7256,7 +7256,7 @@ void Td::on_request(uint64 id, td_api::getPassportElement &request) {
|
||||
CHECK_IS_USER();
|
||||
CLEAN_INPUT_STRING(request.password_);
|
||||
if (request.type_ == nullptr) {
|
||||
return send_error_raw(id, 400, "Type must not be empty");
|
||||
return send_error_raw(id, 400, "Type must be non-empty");
|
||||
}
|
||||
CREATE_REQUEST_PROMISE();
|
||||
send_closure(secure_manager_, &SecureManager::get_secure_value, std::move(request.password_),
|
||||
@ -7286,7 +7286,7 @@ void Td::on_request(uint64 id, td_api::setPassportElement &request) {
|
||||
void Td::on_request(uint64 id, const td_api::deletePassportElement &request) {
|
||||
CHECK_IS_USER();
|
||||
if (request.type_ == nullptr) {
|
||||
return send_error_raw(id, 400, "Type must not be empty");
|
||||
return send_error_raw(id, 400, "Type must be non-empty");
|
||||
}
|
||||
CREATE_OK_REQUEST_PROMISE();
|
||||
send_closure(secure_manager_, &SecureManager::delete_secure_value, get_secure_value_type_td_api(request.type_),
|
||||
@ -7382,7 +7382,7 @@ void Td::on_request(uint64 id, td_api::sendPassportAuthorizationForm &request) {
|
||||
CHECK_IS_USER();
|
||||
for (auto &type : request.types_) {
|
||||
if (type == nullptr) {
|
||||
return send_error_raw(id, 400, "Type must not be empty");
|
||||
return send_error_raw(id, 400, "Type must be non-empty");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -929,6 +929,9 @@ void WebPagesManager::reload_web_page_instant_view(WebPageId web_page_id) {
|
||||
}
|
||||
|
||||
void WebPagesManager::on_load_web_page_instant_view_from_database(WebPageId web_page_id, string value) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
if (!(G()->parameters().use_message_db)) { return; }
|
||||
LOG(INFO) << "Successfully loaded " << web_page_id << " instant view of size " << value.size() << " from database";
|
||||
// G()->td_db()->get_sqlite_pmc()->erase(get_web_page_instant_view_database_key(web_page_id), Auto());
|
||||
@ -1082,6 +1085,9 @@ void WebPagesManager::load_web_page_by_url(const string &url, Promise<Unit> &&pr
|
||||
|
||||
void WebPagesManager::on_load_web_page_id_by_url_from_database(const string &url, string value,
|
||||
Promise<Unit> &&promise) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
LOG(INFO) << "Successfully loaded url \"" << url << "\" of size " << value.size() << " from database";
|
||||
// G()->td_db()->get_sqlite_pmc()->erase(get_web_page_url_database_key(web_page_id), Auto());
|
||||
// return;
|
||||
@ -1449,6 +1455,9 @@ string WebPagesManager::get_web_page_database_key(WebPageId web_page_id) {
|
||||
}
|
||||
|
||||
void WebPagesManager::on_save_web_page_to_database(WebPageId web_page_id, bool success) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
const WebPage *web_page = get_web_page(web_page_id);
|
||||
if (web_page == nullptr) {
|
||||
LOG(ERROR) << "Can't find " << (success ? "saved " : "failed to save ") << web_page_id;
|
||||
@ -1487,6 +1496,9 @@ void WebPagesManager::load_web_page_from_database(WebPageId web_page_id, Promise
|
||||
}
|
||||
|
||||
void WebPagesManager::on_load_web_page_from_database(WebPageId web_page_id, string value) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
if (!loaded_from_database_web_pages_.insert(web_page_id).second) {
|
||||
return;
|
||||
}
|
||||
|
@ -1994,7 +1994,7 @@ class CliClient final : public Actor {
|
||||
|
||||
send_request(td_api::make_object<td_api::getChatMessageCount>(
|
||||
as_chat_id(chat_id), get_search_messages_filter(filter), as_bool(return_local)));
|
||||
} else if (op == "gup" || op == "GetUserPhotos") {
|
||||
} else if (op == "gup" || op == "gupf") {
|
||||
string user_id;
|
||||
string offset;
|
||||
string limit;
|
||||
|
@ -345,7 +345,7 @@ Result<string> check_url(Slice url) {
|
||||
return PSTRING() << (is_tg ? "tg" : "ton") << "://" << http_url.host_ << query;
|
||||
}
|
||||
|
||||
if (http_url.host_.find('.') == string::npos) {
|
||||
if (http_url.host_.find('.') == string::npos && !http_url.is_ipv6_) {
|
||||
return Status::Error("Wrong HTTP URL");
|
||||
}
|
||||
return http_url.get_url();
|
||||
|
@ -379,8 +379,8 @@ void ConnectionCreator::ping_proxy_resolved(int32 proxy_id, IPAddress ip_address
|
||||
CHECK(proxy.use_proxy());
|
||||
auto token = next_token();
|
||||
auto ref =
|
||||
prepare_connection(std::move(socket_fd), proxy, extra.mtproto_ip, extra.transport_type, "Ping", extra.debug_str,
|
||||
nullptr, create_reference(token), false, std::move(connection_promise));
|
||||
prepare_connection(std::move(socket_fd), proxy, extra.mtproto_ip_address, extra.transport_type, "Ping",
|
||||
extra.debug_str, nullptr, create_reference(token), false, std::move(connection_promise));
|
||||
if (!ref.empty()) {
|
||||
children_[token] = {false, std::move(ref)};
|
||||
}
|
||||
@ -661,9 +661,9 @@ Result<mtproto::TransportType> ConnectionCreator::get_transport_type(const Proxy
|
||||
if (!proxy.user().empty() || !proxy.password().empty()) {
|
||||
proxy_authorization = "|basic " + base64_encode(PSLICE() << proxy.user() << ':' << proxy.password());
|
||||
}
|
||||
return mtproto::TransportType{
|
||||
mtproto::TransportType::Http, 0,
|
||||
mtproto::ProxySecret::from_raw(PSTRING() << info.option->get_ip_address().get_ip_str() << proxy_authorization)};
|
||||
return mtproto::TransportType{mtproto::TransportType::Http, 0,
|
||||
mtproto::ProxySecret::from_raw(
|
||||
PSTRING() << info.option->get_ip_address().get_ip_host() << proxy_authorization)};
|
||||
}
|
||||
|
||||
if (info.use_http) {
|
||||
@ -675,7 +675,7 @@ Result<mtproto::TransportType> ConnectionCreator::get_transport_type(const Proxy
|
||||
|
||||
Result<SocketFd> ConnectionCreator::find_connection(const Proxy &proxy, const IPAddress &proxy_ip_address, DcId dc_id,
|
||||
bool allow_media_only, FindConnectionExtra &extra) {
|
||||
extra.debug_str = PSTRING() << "Failed to find valid IP for " << dc_id;
|
||||
extra.debug_str = PSTRING() << "Failed to find valid IP address for " << dc_id;
|
||||
bool prefer_ipv6 =
|
||||
G()->shared_config().get_option_boolean("prefer_ipv6") || (proxy.use_proxy() && proxy_ip_address.is_ipv6());
|
||||
bool only_http = proxy.use_http_caching_proxy();
|
||||
@ -697,9 +697,9 @@ Result<SocketFd> ConnectionCreator::find_connection(const Proxy &proxy, const IP
|
||||
extra.check_mode |= info.should_check;
|
||||
|
||||
if (proxy.use_proxy()) {
|
||||
extra.mtproto_ip = info.option->get_ip_address();
|
||||
extra.mtproto_ip_address = info.option->get_ip_address();
|
||||
extra.debug_str = PSTRING() << (proxy.use_socks5_proxy() ? "Socks5" : (only_http ? "HTTP_ONLY" : "HTTP_TCP")) << ' '
|
||||
<< proxy_ip_address << " --> " << extra.mtproto_ip << extra.debug_str;
|
||||
<< proxy_ip_address << " --> " << extra.mtproto_ip_address << extra.debug_str;
|
||||
VLOG(connections) << "Create: " << extra.debug_str;
|
||||
return SocketFd::open(proxy_ip_address);
|
||||
} else {
|
||||
@ -709,12 +709,10 @@ Result<SocketFd> ConnectionCreator::find_connection(const Proxy &proxy, const IP
|
||||
}
|
||||
}
|
||||
|
||||
ActorOwn<> ConnectionCreator::prepare_connection(SocketFd socket_fd, const Proxy &proxy, const IPAddress &mtproto_ip,
|
||||
mtproto::TransportType transport_type, Slice actor_name_prefix,
|
||||
Slice debug_str,
|
||||
unique_ptr<mtproto::RawConnection::StatsCallback> stats_callback,
|
||||
ActorShared<> parent, bool use_connection_token,
|
||||
Promise<ConnectionData> promise) {
|
||||
ActorOwn<> ConnectionCreator::prepare_connection(
|
||||
SocketFd socket_fd, const Proxy &proxy, const IPAddress &mtproto_ip_address, mtproto::TransportType transport_type,
|
||||
Slice actor_name_prefix, Slice debug_str, unique_ptr<mtproto::RawConnection::StatsCallback> stats_callback,
|
||||
ActorShared<> parent, bool use_connection_token, Promise<ConnectionData> promise) {
|
||||
if (proxy.use_socks5_proxy() || proxy.use_http_tcp_proxy() || transport_type.secret.emulate_tls()) {
|
||||
VLOG(connections) << "Create new transparent proxy connection " << debug_str;
|
||||
class Callback : public TransparentProxy::Callback {
|
||||
@ -765,11 +763,11 @@ ActorOwn<> ConnectionCreator::prepare_connection(SocketFd socket_fd, const Proxy
|
||||
!proxy.use_socks5_proxy());
|
||||
if (proxy.use_socks5_proxy()) {
|
||||
return ActorOwn<>(create_actor<Socks5>(PSLICE() << actor_name_prefix << "Socks5", std::move(socket_fd),
|
||||
mtproto_ip, proxy.user().str(), proxy.password().str(),
|
||||
mtproto_ip_address, proxy.user().str(), proxy.password().str(),
|
||||
std::move(callback), std::move(parent)));
|
||||
} else if (proxy.use_http_tcp_proxy()) {
|
||||
return ActorOwn<>(create_actor<HttpProxy>(PSLICE() << actor_name_prefix << "HttpProxy", std::move(socket_fd),
|
||||
mtproto_ip, proxy.user().str(), proxy.password().str(),
|
||||
mtproto_ip_address, proxy.user().str(), proxy.password().str(),
|
||||
std::move(callback), std::move(parent)));
|
||||
} else if (transport_type.secret.emulate_tls()) {
|
||||
return ActorOwn<>(create_actor<mtproto::TlsInit>(
|
||||
@ -909,7 +907,7 @@ void ConnectionCreator::client_loop(ClientInfo &client) {
|
||||
td::make_unique<detail::StatsCallback>(client.is_media ? media_net_stats_callback_ : common_net_stats_callback_,
|
||||
actor_id(this), client.hash, extra.stat);
|
||||
auto token = next_token();
|
||||
auto ref = prepare_connection(std::move(socket_fd), proxy, extra.mtproto_ip, extra.transport_type, Slice(),
|
||||
auto ref = prepare_connection(std::move(socket_fd), proxy, extra.mtproto_ip_address, extra.transport_type, Slice(),
|
||||
extra.debug_str, std::move(stats_callback), create_reference(token), true,
|
||||
std::move(promise));
|
||||
if (!ref.empty()) {
|
||||
|
@ -91,7 +91,7 @@ class ConnectionCreator : public NetQueryCallback {
|
||||
|
||||
static DcOptions get_default_dc_options(bool is_test);
|
||||
|
||||
static ActorOwn<> prepare_connection(SocketFd socket_fd, const Proxy &proxy, const IPAddress &mtproto_ip,
|
||||
static ActorOwn<> prepare_connection(SocketFd socket_fd, const Proxy &proxy, const IPAddress &mtproto_ip_address,
|
||||
mtproto::TransportType transport_type, Slice actor_name_prefix, Slice debug_str,
|
||||
unique_ptr<mtproto::RawConnection::StatsCallback> stats_callback,
|
||||
ActorShared<> parent, bool use_connection_token,
|
||||
@ -229,7 +229,7 @@ class ConnectionCreator : public NetQueryCallback {
|
||||
DcOptionsSet::Stat *stat{nullptr};
|
||||
mtproto::TransportType transport_type;
|
||||
string debug_str;
|
||||
IPAddress mtproto_ip;
|
||||
IPAddress mtproto_ip_address;
|
||||
bool check_mode{false};
|
||||
};
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
#include "td/telegram/net/DcId.h"
|
||||
|
||||
#include "td/mtproto/crypto.h"
|
||||
#include "td/mtproto/RSA.h"
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/port/RwMutex.h"
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
#include "td/telegram/telegram_api.h"
|
||||
|
||||
#include "td/mtproto/crypto.h"
|
||||
#include "td/mtproto/RSA.h"
|
||||
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/Time.h"
|
||||
|
@ -18,11 +18,11 @@
|
||||
#include "td/telegram/StateManager.h"
|
||||
#include "td/telegram/UniqueId.h"
|
||||
|
||||
#include "td/mtproto/crypto.h"
|
||||
#include "td/mtproto/DhHandshake.h"
|
||||
#include "td/mtproto/Handshake.h"
|
||||
#include "td/mtproto/HandshakeActor.h"
|
||||
#include "td/mtproto/RawConnection.h"
|
||||
#include "td/mtproto/RSA.h"
|
||||
#include "td/mtproto/SessionConnection.h"
|
||||
#include "td/mtproto/TransportType.h"
|
||||
|
||||
|
@ -34,12 +34,7 @@ class GoogleDnsResolver : public Actor {
|
||||
double begin_time_ = 0;
|
||||
|
||||
void start_up() override {
|
||||
auto r_address = IPAddress::get_ipv4_address(host_);
|
||||
if (r_address.is_ok()) {
|
||||
promise_.set_value(r_address.move_as_ok());
|
||||
return stop();
|
||||
}
|
||||
r_address = IPAddress::get_ipv6_address(host_);
|
||||
auto r_address = IPAddress::get_ip_address(host_);
|
||||
if (r_address.is_ok()) {
|
||||
promise_.set_value(r_address.move_as_ok());
|
||||
return stop();
|
||||
|
@ -20,7 +20,7 @@ void HttpProxy::send_connect() {
|
||||
CHECK(state_ == State::SendConnect);
|
||||
state_ = State::WaitConnectResponse;
|
||||
|
||||
string host = PSTRING() << ip_address_.get_ip_str() << ':' << ip_address_.get_port();
|
||||
string host = PSTRING() << ip_address_.get_ip_host() << ':' << ip_address_.get_port();
|
||||
string proxy_authorization;
|
||||
if (!username_.empty() || !password_.empty()) {
|
||||
auto userinfo = PSTRING() << username_ << ':' << password_;
|
||||
@ -47,7 +47,7 @@ Status HttpProxy::wait_connect_response() {
|
||||
size_t len = min(sizeof(buf), it.size());
|
||||
it.advance(len, MutableSlice{buf, sizeof(buf)});
|
||||
VLOG(proxy) << "Failed to connect: " << format::escaped(Slice(buf, len));
|
||||
return Status::Error(PSLICE() << "Failed to connect to " << ip_address_.get_ip_str() << ':'
|
||||
return Status::Error(PSLICE() << "Failed to connect to " << ip_address_.get_ip_host() << ':'
|
||||
<< ip_address_.get_port());
|
||||
}
|
||||
|
||||
|
@ -148,6 +148,7 @@ Result<size_t> HttpReader::read_next(HttpQuery *query) {
|
||||
end_p--;
|
||||
}
|
||||
|
||||
CHECK(p != nullptr);
|
||||
Slice boundary(p, static_cast<size_t>(end_p - p));
|
||||
if (boundary.empty() || boundary.size() > MAX_BOUNDARY_LENGTH) {
|
||||
return Status::Error(400, "Bad Request: boundary too big or empty");
|
||||
|
@ -110,7 +110,7 @@ void Socks5::send_ip_address() {
|
||||
request += static_cast<char>((ipv4 >> 24) & 255);
|
||||
} else {
|
||||
request += '\x04';
|
||||
request += ip_address_.get_ipv6().str();
|
||||
request += ip_address_.get_ipv6();
|
||||
}
|
||||
auto port = ip_address_.get_port();
|
||||
request += static_cast<char>((port >> 8) & 255);
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/port/IPAddress.h"
|
||||
#include "td/utils/port/wstring_convert.h"
|
||||
#include "td/utils/StackAllocator.h"
|
||||
#include "td/utils/Status.h"
|
||||
@ -235,6 +236,9 @@ class SslStreamImpl {
|
||||
options |= SSL_OP_NO_SSLv3;
|
||||
#endif
|
||||
SSL_CTX_set_options(ssl_ctx, options);
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_VERSION);
|
||||
#endif
|
||||
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_ENABLE_PARTIAL_WRITE);
|
||||
|
||||
if (cert_file.empty()) {
|
||||
@ -318,12 +322,18 @@ class SslStreamImpl {
|
||||
SSL_free(ssl_handle);
|
||||
};
|
||||
|
||||
auto r_ip_address = IPAddress::get_ip_address(host);
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||
X509_VERIFY_PARAM *param = SSL_get0_param(ssl_handle);
|
||||
/* Enable automatic hostname checks */
|
||||
// TODO: X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS
|
||||
X509_VERIFY_PARAM_set_hostflags(param, 0);
|
||||
X509_VERIFY_PARAM_set1_host(param, host.c_str(), 0);
|
||||
if (r_ip_address.is_ok()) {
|
||||
LOG(DEBUG) << "Set verification IP address to " << r_ip_address.ok().get_ip_str();
|
||||
X509_VERIFY_PARAM_set1_ip_asc(param, r_ip_address.ok().get_ip_str().c_str());
|
||||
} else {
|
||||
LOG(DEBUG) << "Set verification host to " << host;
|
||||
X509_VERIFY_PARAM_set1_host(param, host.c_str(), 0);
|
||||
}
|
||||
#else
|
||||
#warning DANGEROUS! HTTPS HOST WILL NOT BE CHECKED. INSTALL OPENSSL >= 1.0.2 OR IMPLEMENT HTTPS HOST CHECK MANUALLY
|
||||
#endif
|
||||
@ -333,8 +343,11 @@ class SslStreamImpl {
|
||||
SSL_set_bio(ssl_handle, bio, bio);
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
|
||||
auto host_str = host.str();
|
||||
SSL_set_tlsext_host_name(ssl_handle, MutableCSlice(host_str).begin());
|
||||
if (r_ip_address.is_error()) { // IP address must not be send as SNI
|
||||
LOG(DEBUG) << "Set SNI host name to " << host;
|
||||
auto host_str = host.str();
|
||||
SSL_set_tlsext_host_name(ssl_handle, MutableCSlice(host_str).begin());
|
||||
}
|
||||
#endif
|
||||
SSL_set_connect_state(ssl_handle);
|
||||
|
||||
@ -466,25 +479,26 @@ class SslStreamImpl {
|
||||
LOG(ERROR) << "SSL_get_error returned no error";
|
||||
return 0;
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
LOG(DEBUG) << "SSL_ERROR_ZERO_RETURN";
|
||||
LOG(DEBUG) << "SSL_ZERO_RETURN";
|
||||
return 0;
|
||||
case SSL_ERROR_WANT_READ:
|
||||
LOG(DEBUG) << "SSL_ERROR_WANT_READ";
|
||||
LOG(DEBUG) << "SSL_WANT_READ";
|
||||
return 0;
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
LOG(DEBUG) << "SSL_ERROR_WANT_WRITE";
|
||||
LOG(DEBUG) << "SSL_WANT_WRITE";
|
||||
return 0;
|
||||
case SSL_ERROR_WANT_CONNECT:
|
||||
case SSL_ERROR_WANT_ACCEPT:
|
||||
case SSL_ERROR_WANT_X509_LOOKUP:
|
||||
LOG(DEBUG) << "SSL_ERROR: CONNECT ACCEPT LOOKUP";
|
||||
LOG(DEBUG) << "SSL: CONNECT ACCEPT LOOKUP";
|
||||
return 0;
|
||||
case SSL_ERROR_SYSCALL:
|
||||
LOG(DEBUG) << "SSL_ERROR_SYSCALL";
|
||||
if (ERR_peek_error() == 0) {
|
||||
if (os_error.code() != 0) {
|
||||
LOG(DEBUG) << "SSL_ERROR_SYSCALL";
|
||||
return std::move(os_error);
|
||||
} else {
|
||||
LOG(DEBUG) << "SSL_SYSCALL";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -501,6 +515,7 @@ int strm_read(BIO *b, char *buf, int len) {
|
||||
auto *stream = static_cast<SslStreamImpl *>(BIO_get_data(b));
|
||||
CHECK(stream != nullptr);
|
||||
BIO_clear_retry_flags(b);
|
||||
CHECK(buf != nullptr);
|
||||
int res = narrow_cast<int>(stream->flow_read(MutableSlice(buf, len)));
|
||||
if (res == 0) {
|
||||
BIO_set_retry_read(b);
|
||||
@ -512,6 +527,7 @@ int strm_write(BIO *b, const char *buf, int len) {
|
||||
auto *stream = static_cast<SslStreamImpl *>(BIO_get_data(b));
|
||||
CHECK(stream != nullptr);
|
||||
BIO_clear_retry_flags(b);
|
||||
CHECK(buf != nullptr);
|
||||
return narrow_cast<int>(stream->flow_write(Slice(buf, len)));
|
||||
}
|
||||
} // namespace
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/Parser.h"
|
||||
#include "td/utils/port/IPAddress.h"
|
||||
|
||||
namespace td {
|
||||
|
||||
@ -29,13 +30,7 @@ string HttpUrl::get_url() const {
|
||||
result += userinfo_;
|
||||
result += '@';
|
||||
}
|
||||
if (is_ipv6_) {
|
||||
result += '[';
|
||||
}
|
||||
result += host_;
|
||||
if (is_ipv6_) {
|
||||
result += ']';
|
||||
}
|
||||
if (specified_port_ > 0) {
|
||||
result += ':';
|
||||
result += to_string(specified_port_);
|
||||
@ -88,8 +83,11 @@ Result<HttpUrl> parse_url(Slice url, HttpUrl::Protocol default_protocol) {
|
||||
|
||||
bool is_ipv6 = false;
|
||||
if (!host.empty() && host[0] == '[' && host.back() == ']') {
|
||||
host.remove_prefix(1);
|
||||
host.remove_suffix(1);
|
||||
IPAddress ip_address;
|
||||
if (ip_address.init_ipv6_port(host.str(), 1).is_error()) {
|
||||
return Status::Error("Wrong IPv6 address specified in the URL");
|
||||
}
|
||||
CHECK(ip_address.is_ipv6());
|
||||
is_ipv6 = true;
|
||||
}
|
||||
if (host.empty()) {
|
||||
|
@ -45,7 +45,7 @@ class ParserImpl {
|
||||
return ptr_ == end_;
|
||||
}
|
||||
void clear() {
|
||||
ptr_ = nullptr;
|
||||
ptr_ = SliceT().begin();
|
||||
end_ = ptr_;
|
||||
status_ = Status::OK();
|
||||
}
|
||||
|
@ -267,11 +267,11 @@ uint32 IPAddress::get_ipv4() const {
|
||||
return htonl(ipv4_addr_.sin_addr.s_addr);
|
||||
}
|
||||
|
||||
Slice IPAddress::get_ipv6() const {
|
||||
string IPAddress::get_ipv6() const {
|
||||
static_assert(sizeof(ipv6_addr_.sin6_addr) == 16, "ipv6 size == 16");
|
||||
CHECK(is_valid());
|
||||
CHECK(!is_ipv4());
|
||||
return Slice(ipv6_addr_.sin6_addr.s6_addr, 16);
|
||||
return Slice(ipv6_addr_.sin6_addr.s6_addr, 16).str();
|
||||
}
|
||||
|
||||
IPAddress IPAddress::get_any_addr() const {
|
||||
@ -308,7 +308,12 @@ void IPAddress::init_ipv6_any() {
|
||||
Status IPAddress::init_ipv6_port(CSlice ipv6, int port) {
|
||||
is_valid_ = false;
|
||||
if (port <= 0 || port >= (1 << 16)) {
|
||||
return Status::Error(PSLICE() << "Invalid [port=" << port << "]");
|
||||
return Status::Error(PSLICE() << "Invalid [IPv6 address port=" << port << "]");
|
||||
}
|
||||
string ipv6_plain;
|
||||
if (ipv6.size() > 2 && ipv6[0] == '[' && ipv6.back() == ']') {
|
||||
ipv6_plain.assign(ipv6.begin() + 1, ipv6.size() - 2);
|
||||
ipv6 = ipv6_plain;
|
||||
}
|
||||
std::memset(&ipv6_addr_, 0, sizeof(ipv6_addr_));
|
||||
ipv6_addr_.sin6_family = AF_INET6;
|
||||
@ -330,7 +335,7 @@ Status IPAddress::init_ipv6_as_ipv4_port(CSlice ipv4, int port) {
|
||||
Status IPAddress::init_ipv4_port(CSlice ipv4, int port) {
|
||||
is_valid_ = false;
|
||||
if (port <= 0 || port >= (1 << 16)) {
|
||||
return Status::Error(PSLICE() << "Invalid [port=" << port << "]");
|
||||
return Status::Error(PSLICE() << "Invalid [IPv4 address port=" << port << "]");
|
||||
}
|
||||
std::memset(&ipv4_addr_, 0, sizeof(ipv4_addr_));
|
||||
ipv4_addr_.sin_family = AF_INET;
|
||||
@ -345,6 +350,18 @@ Status IPAddress::init_ipv4_port(CSlice ipv4, int port) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Result<IPAddress> IPAddress::get_ip_address(CSlice host) {
|
||||
auto r_address = get_ipv4_address(host);
|
||||
if (r_address.is_ok()) {
|
||||
return r_address.move_as_ok();
|
||||
}
|
||||
r_address = get_ipv6_address(host);
|
||||
if (r_address.is_ok()) {
|
||||
return r_address.move_as_ok();
|
||||
}
|
||||
return Status::Error("Not a valid IP address");
|
||||
}
|
||||
|
||||
Result<IPAddress> IPAddress::get_ipv4_address(CSlice host) {
|
||||
// sometimes inet_addr allows much more valid IPv4 hosts than inet_pton,
|
||||
// like 0x12.0x34.0x56.0x78, or 0x12345678, or 0x7f.001
|
||||
@ -372,6 +389,10 @@ Result<IPAddress> IPAddress::get_ipv6_address(CSlice host) {
|
||||
}
|
||||
|
||||
Status IPAddress::init_host_port(CSlice host, int port, bool prefer_ipv6) {
|
||||
if (host.size() > 2 && host[0] == '[' && host.back() == ']') {
|
||||
return init_ipv6_port(host, port == 0 ? 1 : port);
|
||||
}
|
||||
|
||||
return init_host_port(host, PSLICE() << port, prefer_ipv6);
|
||||
}
|
||||
|
||||
@ -388,6 +409,11 @@ Status IPAddress::init_host_port(CSlice host, CSlice port, bool prefer_ipv6) {
|
||||
TRY_RESULT(ascii_host, idn_to_ascii(host));
|
||||
host = ascii_host; // assign string to CSlice
|
||||
|
||||
if (host[0] == '[' && host.back() == ']') {
|
||||
auto port_int = to_integer<int>(port);
|
||||
return init_ipv6_port(host, port_int == 0 ? 1 : port_int);
|
||||
}
|
||||
|
||||
// some getaddrinfo implementations use inet_pton instead of inet_aton and support only decimal-dotted IPv4 form,
|
||||
// and so doesn't recognize 0x12.0x34.0x56.0x78, or 0x12345678, or 0x7f.001 as valid IPv4 addresses
|
||||
auto ipv4_numeric_addr = inet_addr(host.c_str());
|
||||
@ -494,19 +520,19 @@ Status IPAddress::init_peer_address(const SocketFd &socket_fd) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
CSlice IPAddress::ipv4_to_str(uint32 ipv4) {
|
||||
string IPAddress::ipv4_to_str(uint32 ipv4) {
|
||||
ipv4 = ntohl(ipv4);
|
||||
return ::td::get_ip_str(AF_INET, &ipv4);
|
||||
return ::td::get_ip_str(AF_INET, &ipv4).str();
|
||||
}
|
||||
|
||||
CSlice IPAddress::ipv6_to_str(Slice ipv6) {
|
||||
string IPAddress::ipv6_to_str(Slice ipv6) {
|
||||
CHECK(ipv6.size() == 16);
|
||||
return ::td::get_ip_str(AF_INET6, ipv6.ubegin());
|
||||
return ::td::get_ip_str(AF_INET6, ipv6.ubegin()).str();
|
||||
}
|
||||
|
||||
Slice IPAddress::get_ip_str() const {
|
||||
CSlice IPAddress::get_ip_str() const {
|
||||
if (!is_valid()) {
|
||||
return Slice("0.0.0.0");
|
||||
return CSlice("0.0.0.0");
|
||||
}
|
||||
|
||||
switch (get_address_family()) {
|
||||
@ -516,7 +542,23 @@ Slice IPAddress::get_ip_str() const {
|
||||
return ::td::get_ip_str(AF_INET, &ipv4_addr_.sin_addr);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return Slice();
|
||||
return CSlice();
|
||||
}
|
||||
}
|
||||
|
||||
string IPAddress::get_ip_host() const {
|
||||
if (!is_valid()) {
|
||||
return "0.0.0.0";
|
||||
}
|
||||
|
||||
switch (get_address_family()) {
|
||||
case AF_INET6:
|
||||
return PSTRING() << '[' << ::td::get_ip_str(AF_INET6, &ipv6_addr_.sin6_addr) << ']';
|
||||
case AF_INET:
|
||||
return ::td::get_ip_str(AF_INET, &ipv4_addr_.sin_addr).str();
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return string();
|
||||
}
|
||||
}
|
||||
|
||||
@ -599,12 +641,7 @@ StringBuilder &operator<<(StringBuilder &builder, const IPAddress &address) {
|
||||
if (!address.is_valid()) {
|
||||
return builder << "[invalid]";
|
||||
}
|
||||
if (address.get_address_family() == AF_INET) {
|
||||
return builder << "[" << address.get_ip_str() << ":" << address.get_port() << "]";
|
||||
} else {
|
||||
CHECK(address.get_address_family() == AF_INET6);
|
||||
return builder << "[[" << address.get_ip_str() << "]:" << address.get_port() << "]";
|
||||
}
|
||||
return builder << "[" << address.get_ip_host() << ":" << address.get_port() << "]";
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
|
@ -39,11 +39,20 @@ class IPAddress {
|
||||
void set_port(int port);
|
||||
|
||||
uint32 get_ipv4() const;
|
||||
Slice get_ipv6() const;
|
||||
Slice get_ip_str() const;
|
||||
string get_ipv6() const;
|
||||
|
||||
// returns result in a static thread-local buffer, which may be overwritten by any subsequent method call
|
||||
CSlice get_ip_str() const;
|
||||
|
||||
// returns IP address as a host, i.e. IPv4 or [IPv6]
|
||||
string get_ip_host() const;
|
||||
|
||||
static string ipv4_to_str(uint32 ipv4);
|
||||
static string ipv6_to_str(Slice ipv6);
|
||||
|
||||
IPAddress get_any_addr() const;
|
||||
|
||||
static Result<IPAddress> get_ip_address(CSlice host); // host must be any IPv4 or IPv6
|
||||
static Result<IPAddress> get_ipv4_address(CSlice host);
|
||||
static Result<IPAddress> get_ipv6_address(CSlice host);
|
||||
|
||||
@ -63,8 +72,6 @@ class IPAddress {
|
||||
const sockaddr *get_sockaddr() const;
|
||||
size_t get_sockaddr_len() const;
|
||||
int get_address_family() const;
|
||||
static CSlice ipv4_to_str(uint32 ipv4);
|
||||
static CSlice ipv6_to_str(Slice ipv6);
|
||||
Status init_sockaddr(sockaddr *addr);
|
||||
Status init_sockaddr(sockaddr *addr, socklen_t len) TD_WARN_UNUSED_RESULT;
|
||||
|
||||
|
@ -10,31 +10,27 @@
|
||||
#include "td/utils/Heap.h"
|
||||
#include "td/utils/Random.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
|
||||
REGISTER_TESTS(heap)
|
||||
|
||||
using namespace td;
|
||||
|
||||
TEST(Heap, sort_random_perm) {
|
||||
int n = 1000000;
|
||||
|
||||
std::vector<int> v(n);
|
||||
td::vector<int> v(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
v[i] = i;
|
||||
}
|
||||
|
||||
// random shuffle
|
||||
for (int i = 1; i < n; i++) {
|
||||
std::swap(v[Random::fast(0, i)], v[i]);
|
||||
std::swap(v[td::Random::fast(0, i)], v[i]);
|
||||
}
|
||||
|
||||
std::vector<HeapNode> nodes(n);
|
||||
KHeap<int> kheap;
|
||||
td::vector<td::HeapNode> nodes(n);
|
||||
td::KHeap<int> kheap;
|
||||
for (int i = 0; i < n; i++) {
|
||||
kheap.insert(v[i], &nodes[i]);
|
||||
}
|
||||
@ -55,7 +51,7 @@ class CheckedHeap {
|
||||
nodes[i].value = i;
|
||||
}
|
||||
}
|
||||
static void xx(int key, const HeapNode *heap_node) {
|
||||
static void xx(int key, const td::HeapNode *heap_node) {
|
||||
const Node *node = static_cast<const Node *>(heap_node);
|
||||
std::fprintf(stderr, "(%d;%d)", node->key, node->value);
|
||||
}
|
||||
@ -70,9 +66,9 @@ class CheckedHeap {
|
||||
}
|
||||
int random_id() const {
|
||||
CHECK(!empty());
|
||||
return ids[Random::fast(0, static_cast<int>(ids.size() - 1))];
|
||||
return ids[td::Random::fast(0, static_cast<int>(ids.size() - 1))];
|
||||
}
|
||||
size_t size() const {
|
||||
std::size_t size() const {
|
||||
return ids.size();
|
||||
}
|
||||
bool empty() const {
|
||||
@ -144,19 +140,19 @@ class CheckedHeap {
|
||||
}
|
||||
|
||||
private:
|
||||
struct Node : public HeapNode {
|
||||
struct Node : public td::HeapNode {
|
||||
Node() = default;
|
||||
Node(int key, int value) : key(key), value(value) {
|
||||
}
|
||||
int key = 0;
|
||||
int value = 0;
|
||||
};
|
||||
vector<int> ids;
|
||||
vector<int> rev_ids;
|
||||
vector<int> free_ids;
|
||||
vector<Node> nodes;
|
||||
td::vector<int> ids;
|
||||
td::vector<int> rev_ids;
|
||||
td::vector<int> free_ids;
|
||||
td::vector<Node> nodes;
|
||||
std::set<std::pair<int, int>> set_heap;
|
||||
KHeap<int> kheap;
|
||||
td::KHeap<int> kheap;
|
||||
};
|
||||
|
||||
TEST(Heap, random_events) {
|
||||
@ -167,11 +163,11 @@ TEST(Heap, random_events) {
|
||||
heap.top_key();
|
||||
}
|
||||
|
||||
int x = Random::fast(0, 4);
|
||||
int x = td::Random::fast(0, 4);
|
||||
if (heap.empty() || (x < 2 && heap.size() < 1000)) {
|
||||
heap.insert(Random::fast(0, 99));
|
||||
heap.insert(td::Random::fast(0, 99));
|
||||
} else if (x < 3) {
|
||||
heap.fix_key(Random::fast(0, 99), heap.random_id());
|
||||
heap.fix_key(td::Random::fast(0, 99), heap.random_id());
|
||||
} else if (x < 4) {
|
||||
heap.erase(heap.random_id());
|
||||
} else if (x < 5) {
|
||||
|
@ -36,7 +36,6 @@
|
||||
#include "data.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <limits>
|
||||
|
||||
REGISTER_TESTS(http)
|
||||
|
@ -201,7 +201,7 @@ TEST(MessageEntities, bank_card_number) {
|
||||
check_bank_card_number(" - - - - 1 - -- 234 - - 56- - 7890150000 - - - -", {"1 - -- 234 - - 56- - 7890150000"});
|
||||
check_bank_card_number("4916-3385-0608-2832; 5280 9342 8317 1080 ;345936346788903",
|
||||
{"4916-3385-0608-2832", "5280 9342 8317 1080", "345936346788903"});
|
||||
check_bank_card_number("4556728228023269,4916141675244747020,49161416752447470,4556728228023269",
|
||||
check_bank_card_number("4556728228023269, 4916141675244747020, 49161416752447470, 4556728228023269",
|
||||
{"4556728228023269", "4916141675244747020", "4556728228023269"});
|
||||
check_bank_card_number("a1234567890128", {});
|
||||
check_bank_card_number("1234567890128a", {});
|
||||
@ -210,7 +210,7 @@ TEST(MessageEntities, bank_card_number) {
|
||||
check_bank_card_number("1234567890128_", {});
|
||||
check_bank_card_number("_1234567890128", {});
|
||||
check_bank_card_number("1234567890128/", {"1234567890128"});
|
||||
check_bank_card_number(",1234567890128", {"1234567890128"});
|
||||
check_bank_card_number("\"1234567890128", {"1234567890128"});
|
||||
check_bank_card_number("+1234567890128", {});
|
||||
}
|
||||
|
||||
@ -625,26 +625,35 @@ TEST(MessageEntities, fix_formatted_text) {
|
||||
str += "a \r\n ";
|
||||
fixed_str += "a \n ";
|
||||
|
||||
for (td::int32 i = 33; i <= 35; i++) {
|
||||
td::vector<td::MessageEntity> entities;
|
||||
entities.emplace_back(td::MessageEntity::Type::Pre, 0, i);
|
||||
|
||||
td::vector<td::MessageEntity> fixed_entities = entities;
|
||||
fixed_entities.back().length = i - 1;
|
||||
check_fix_formatted_text(str, entities, fixed_str, fixed_entities, true, false, false, true);
|
||||
|
||||
td::string expected_str = fixed_str.substr(0, 33);
|
||||
fixed_entities.back().length = i == 33 ? 32 : 33;
|
||||
check_fix_formatted_text(str, entities, expected_str, fixed_entities, false, false, false, false);
|
||||
}
|
||||
|
||||
for (td::int32 i = 33; i <= 35; i++) {
|
||||
td::vector<td::MessageEntity> entities;
|
||||
entities.emplace_back(td::MessageEntity::Type::Bold, 0, i);
|
||||
|
||||
td::vector<td::MessageEntity> fixed_entities;
|
||||
if (i != 33) {
|
||||
fixed_entities = entities;
|
||||
fixed_entities.back().length = i - 1;
|
||||
fixed_entities.emplace_back(td::MessageEntity::Type::Bold, 32, i - 33);
|
||||
}
|
||||
check_fix_formatted_text(str, entities, fixed_str, fixed_entities, true, false, false, true);
|
||||
|
||||
td::string expected_str;
|
||||
if (i != 33) {
|
||||
fixed_entities = entities;
|
||||
fixed_entities.back().length = 33;
|
||||
expected_str = fixed_str.substr(0, 33);
|
||||
} else {
|
||||
fixed_entities.clear();
|
||||
expected_str = "a";
|
||||
fixed_entities.back().offset = 0;
|
||||
fixed_entities.back().length = 1;
|
||||
}
|
||||
expected_str = "a";
|
||||
check_fix_formatted_text(str, entities, expected_str, fixed_entities, false, false, false, false);
|
||||
}
|
||||
|
||||
@ -702,6 +711,12 @@ TEST(MessageEntities, fix_formatted_text) {
|
||||
while (static_cast<size_t>(fixed_offset + fixed_length) > fixed_str.size()) {
|
||||
fixed_length--;
|
||||
}
|
||||
if (type == td::MessageEntity::Type::Bold || type == td::MessageEntity::Type::Url) {
|
||||
while (fixed_length > 0 && (fixed_str[fixed_offset] == ' ' || fixed_str[fixed_offset] == '\n')) {
|
||||
fixed_offset++;
|
||||
fixed_length--;
|
||||
}
|
||||
}
|
||||
|
||||
td::vector<td::MessageEntity> entities;
|
||||
entities.emplace_back(type, offset, length);
|
||||
@ -727,22 +742,29 @@ TEST(MessageEntities, fix_formatted_text) {
|
||||
for (td::int32 offset = -10; offset <= 10; offset++) {
|
||||
td::vector<td::MessageEntity> entities;
|
||||
entities.emplace_back(td::MessageEntity::Type::Bold, offset, length);
|
||||
td::vector<td::MessageEntity> fixed_entities;
|
||||
if (length < 0 || offset < 0 || (length > 0 && static_cast<size_t>(length + offset) > str.size())) {
|
||||
check_fix_formatted_text(str, entities, true, false, false, false);
|
||||
check_fix_formatted_text(str, entities, false, false, false, true);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (length > 0 && (length >= 2 || offset != 3)) {
|
||||
fixed_entities.emplace_back(td::MessageEntity::Type::Bold, offset, length);
|
||||
td::vector<td::MessageEntity> fixed_entities;
|
||||
if (length > 0) {
|
||||
if (offset == 3) {
|
||||
if (length >= 2) {
|
||||
fixed_entities.emplace_back(td::MessageEntity::Type::Bold, offset + 1, length - 1);
|
||||
}
|
||||
} else {
|
||||
fixed_entities.emplace_back(td::MessageEntity::Type::Bold, offset, length);
|
||||
}
|
||||
}
|
||||
|
||||
check_fix_formatted_text(str, entities, str, fixed_entities, true, false, false, false);
|
||||
check_fix_formatted_text(str, entities, str, fixed_entities, false, false, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
str = "aba caba";
|
||||
str = "abadcaba";
|
||||
for (td::int32 length = 1; length <= 7; length++) {
|
||||
for (td::int32 offset = 0; offset <= 8 - length; offset++) {
|
||||
for (td::int32 length2 = 1; length2 <= 7; length2++) {
|
||||
@ -926,7 +948,7 @@ TEST(MessageEntities, fix_formatted_text) {
|
||||
check_fix_formatted_text("example.com a", {{td::MessageEntity::Type::Italic, 0, 13}}, "example.com a",
|
||||
{{td::MessageEntity::Type::Url, 0, 11},
|
||||
{td::MessageEntity::Type::Italic, 0, 11},
|
||||
{td::MessageEntity::Type::Italic, 11, 2}});
|
||||
{td::MessageEntity::Type::Italic, 12, 1}});
|
||||
check_fix_formatted_text("a example.com", {{td::MessageEntity::Type::Italic, 0, 13}}, "a example.com",
|
||||
{{td::MessageEntity::Type::Italic, 0, 2},
|
||||
{td::MessageEntity::Type::Url, 2, 11},
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include "td/actor/PromiseFuture.h"
|
||||
|
||||
#include "td/mtproto/AuthData.h"
|
||||
#include "td/mtproto/crypto.h"
|
||||
#include "td/mtproto/DhHandshake.h"
|
||||
#include "td/mtproto/Handshake.h"
|
||||
#include "td/mtproto/HandshakeActor.h"
|
||||
@ -18,6 +17,7 @@
|
||||
#include "td/mtproto/PingConnection.h"
|
||||
#include "td/mtproto/ProxySecret.h"
|
||||
#include "td/mtproto/RawConnection.h"
|
||||
#include "td/mtproto/RSA.h"
|
||||
#include "td/mtproto/TlsInit.h"
|
||||
#include "td/mtproto/TransportType.h"
|
||||
|
||||
@ -86,10 +86,13 @@ TEST(Mtproto, GetHostByNameActor) {
|
||||
" ",
|
||||
"a",
|
||||
"\x80",
|
||||
"[]",
|
||||
"127.0.0.1.",
|
||||
"0x12.0x34.0x56.0x78",
|
||||
"0x7f.001",
|
||||
"2001:0db8:85a3:0000:0000:8a2e:0370:7334"};
|
||||
"2001:0db8:85a3:0000:0000:8a2e:0370:7334",
|
||||
"[2001:0db8:85a3:0000:0000:8a2e:0370:7334]",
|
||||
"[[2001:0db8:85a3:0000:0000:8a2e:0370:7334]]"};
|
||||
for (auto types : {vector<GetHostByNameActor::ResolverType>{GetHostByNameActor::ResolverType::Native},
|
||||
vector<GetHostByNameActor::ResolverType>{GetHostByNameActor::ResolverType::Google},
|
||||
vector<GetHostByNameActor::ResolverType>{GetHostByNameActor::ResolverType::Google,
|
||||
@ -105,7 +108,7 @@ TEST(Mtproto, GetHostByNameActor) {
|
||||
|
||||
for (auto host : hosts) {
|
||||
for (auto prefer_ipv6 : {false, true}) {
|
||||
bool allow_ok = host.size() > 2;
|
||||
bool allow_ok = host.size() > 2 && host[1] != '[';
|
||||
bool allow_both = host == "127.0.0.1." || host == "localhost" || (host == "москва.рф" && prefer_ipv6);
|
||||
bool allow_error = !allow_ok || allow_both;
|
||||
run(actor_id, host, prefer_ipv6, allow_ok, allow_error);
|
||||
@ -434,11 +437,11 @@ class Socks5TestActor : public Actor {
|
||||
|
||||
IPAddress socks5_ip;
|
||||
socks5_ip.init_ipv4_port("131.191.89.104", 43077).ensure();
|
||||
IPAddress mtproto_ip = get_default_ip_address();
|
||||
IPAddress mtproto_ip_address = get_default_ip_address();
|
||||
|
||||
auto r_socket = SocketFd::open(socks5_ip);
|
||||
create_actor<Socks5>("socks5", r_socket.move_as_ok(), mtproto_ip, "", "", make_unique<Callback>(std::move(promise)),
|
||||
actor_shared())
|
||||
create_actor<Socks5>("socks5", r_socket.move_as_ok(), mtproto_ip_address, "", "",
|
||||
make_unique<Callback>(std::move(promise)), actor_shared())
|
||||
.release();
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "td/tl/tl_object_parse.h"
|
||||
#include "td/tl/tl_object_store.h"
|
||||
|
||||
#include "td/utils/as.h"
|
||||
#include "td/utils/base64.h"
|
||||
#include "td/utils/buffer.h"
|
||||
#include "td/utils/common.h"
|
||||
@ -885,20 +886,17 @@ class Master : public Actor {
|
||||
}
|
||||
void process_net_query(my_api::messages_sendEncryptedService &&message, NetQueryPtr net_query,
|
||||
ActorShared<NetQueryCallback> callback) {
|
||||
process_net_query_send_enrypted(std::move(message.data_), std::move(net_query), std::move(callback));
|
||||
process_net_query_send_encrypted(std::move(message.data_), std::move(net_query), std::move(callback));
|
||||
}
|
||||
void process_net_query(my_api::messages_sendEncrypted &&message, NetQueryPtr net_query,
|
||||
ActorShared<NetQueryCallback> callback) {
|
||||
process_net_query_send_enrypted(std::move(message.data_), std::move(net_query), std::move(callback));
|
||||
process_net_query_send_encrypted(std::move(message.data_), std::move(net_query), std::move(callback));
|
||||
}
|
||||
void process_net_query_send_enrypted(BufferSlice data, NetQueryPtr net_query,
|
||||
ActorShared<NetQueryCallback> callback) {
|
||||
my_api::messages_sentEncryptedMessage sent_message;
|
||||
sent_message.date_ = 0;
|
||||
auto storer = TLObjectStorer<my_api::messages_sentEncryptedMessage>(sent_message);
|
||||
BufferSlice answer(storer.size());
|
||||
auto real_size = storer.store(answer.as_slice().ubegin());
|
||||
CHECK(real_size == answer.size());
|
||||
void process_net_query_send_encrypted(BufferSlice data, NetQueryPtr net_query,
|
||||
ActorShared<NetQueryCallback> callback) {
|
||||
BufferSlice answer(8);
|
||||
answer.as_slice().fill(0);
|
||||
as<int32>(answer.as_slice().begin()) = my_api::messages_sentEncryptedMessage::ID;
|
||||
net_query->set_ok(std::move(answer));
|
||||
send_closure(std::move(callback), &NetQueryCallback::on_result, std::move(net_query));
|
||||
|
||||
|
Reference in New Issue
Block a user