diff --git a/CMakeLists.txt b/CMakeLists.txt index d573309c1..8fdb81377 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -891,6 +891,9 @@ install(FILES "${TL_TD_AUTO_INCLUDE_DIR}/td/telegram/td_api.h" "${TL_TD_AUTO_INC if (TD_ENABLE_JNI) install(FILES td/tl/tl_jni_object.h DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/td/tl") endif() +if (MSVC AND VCPKG_TOOLCHAIN) + install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/$/" DESTINATION "${CMAKE_INSTALL_BINDIR}" FILES_MATCHING PATTERN "*.dll") +endif() include(CMakePackageConfigHelpers) write_basic_package_version_file("TdConfigVersion.cmake" diff --git a/benchmark/check_proxy.cpp b/benchmark/check_proxy.cpp index a493b1c46..e1ff5523e 100644 --- a/benchmark/check_proxy.cpp +++ b/benchmark/check_proxy.cpp @@ -160,20 +160,22 @@ int main(int argc, char **argv) { SET_VERBOSITY_LEVEL(new_verbosity_level); - td::Client client; + td::ClientManager client_manager; + auto client_id = client_manager.create_client(); for (size_t i = 0; i < requests.size(); i++) { auto &request = requests[i].second; request->dc_id_ = dc_id; request->timeout_ = timeout; - client.send({i + 1, std::move(request)}); + client_manager.send(client_id, i + 1, std::move(request)); } size_t successful_requests = 0; size_t failed_requests = 0; while (successful_requests + failed_requests != requests.size()) { - auto response = client.receive(100.0); - if (1 <= response.id && response.id <= requests.size()) { - auto &proxy = requests[static_cast(response.id - 1)].first; + auto response = client_manager.receive(100.0); + CHECK(client_id == response.client_id); + if (1 <= response.request_id && response.request_id <= requests.size()) { + auto &proxy = requests[static_cast(response.request_id - 1)].first; if (response.object->get_id() == td::td_api::error::ID) { LOG(ERROR) << proxy << ": " << to_string(response.object); failed_requests++; diff --git a/build.html b/build.html index 2a2bab1c8..ae6629c72 100644 --- a/build.html +++ b/build.html @@ -604,7 +604,7 @@ function onOptionsChanged() { case 'Alpine': commands.push(sudo + 'apk update'); commands.push(sudo + 'apk upgrade'); - var packages = 'alpine-sdk linux-headers git zlib-dev openssl-dev gperf php php-ctype cmake'; + var packages = 'alpine-sdk linux-headers git zlib-dev openssl-dev gperf php cmake'; if (target === 'JNI') { packages += ' openjdk8'; } @@ -676,7 +676,7 @@ function onOptionsChanged() { } } else if (os_freebsd) { commands.push(sudo + 'pkg upgrade'); - var packages = 'git gperf php72 php72-ctype cmake'; + var packages = 'git gperf php72 cmake'; if (target === 'JNI') { packages += ' openjdk'; } diff --git a/example/cpp/README.md b/example/cpp/README.md index addc1d4f7..1658ade6f 100644 --- a/example/cpp/README.md +++ b/example/cpp/README.md @@ -21,4 +21,4 @@ cmake --build . Documentation for all available classes and methods can be found at https://core.telegram.org/tdlib/docs. -To run `tdjson_example` you may need to manually copy a `tdjson` shared library from `td/bin` to a directory containing built binaries. +To run the examples you may need to manually copy needed shared libraries from `td/bin` to a directory containing built binaries. diff --git a/example/cpp/td_example.cpp b/example/cpp/td_example.cpp index 4771e5417..4bb493857 100644 --- a/example/cpp/td_example.cpp +++ b/example/cpp/td_example.cpp @@ -53,8 +53,10 @@ namespace td_api = td::td_api; class TdExample { public: TdExample() { - td::Client::execute({0, td_api::make_object(1)}); - client_ = std::make_unique(); + td::ClientManager::execute(td_api::make_object(1)); + client_manager_ = std::make_unique(); + client_id_ = client_manager_->create_client(); + send_query(td_api::make_object("version"), {}); } void loop() { @@ -62,7 +64,7 @@ class TdExample { if (need_restart_) { restart(); } else if (!are_authorized_) { - process_response(client_->receive(10)); + process_response(client_manager_->receive(10)); } else { std::cout << "Enter action [q] quit [u] check for updates and request results [c] show chats [m ] " "send message [me] show self [l] logout: " @@ -80,7 +82,7 @@ class TdExample { if (action == "u") { std::cout << "Checking for updates..." << std::endl; while (true) { - auto response = client_->receive(0); + auto response = client_manager_->receive(0); if (response.object) { process_response(std::move(response)); } else { @@ -131,7 +133,8 @@ class TdExample { private: using Object = td_api::object_ptr; - std::unique_ptr client_; + std::unique_ptr client_manager_; + std::int32_t client_id_{0}; td_api::object_ptr authorization_state_; bool are_authorized_{false}; @@ -146,7 +149,7 @@ class TdExample { std::map chat_title_; void restart() { - client_.reset(); + client_manager_.reset(); *this = TdExample(); } @@ -155,18 +158,18 @@ class TdExample { if (handler) { handlers_.emplace(query_id, std::move(handler)); } - client_->send({query_id, std::move(f)}); + client_manager_->send(client_id_, query_id, std::move(f)); } - void process_response(td::Client::Response response) { + void process_response(td::ClientManager::Response response) { if (!response.object) { return; } - //std::cout << response.id << " " << to_string(response.object) << std::endl; - if (response.id == 0) { + //std::cout << response.request_id << " " << to_string(response.object) << std::endl; + if (response.request_id == 0) { return process_update(std::move(response.object)); } - auto it = handlers_.find(response.id); + auto it = handlers_.find(response.request_id); if (it != handlers_.end()) { it->second(std::move(response.object)); } diff --git a/example/cpp/tdjson_example.cpp b/example/cpp/tdjson_example.cpp index 5d69c4e21..62978f178 100644 --- a/example/cpp/tdjson_example.cpp +++ b/example/cpp/tdjson_example.cpp @@ -18,6 +18,9 @@ int main() { int client_id = td_create_client(); // somehow share the client_id with other threads, which will be able to send requests via td_send + // start the client by sending request to it + td_send(client_id, "{\"@type\":\"getOption\", \"name\":\"version\"}"); + const bool test_incorrect_queries = false; if (test_incorrect_queries) { td_execute("{\"@type\":\"setLogVerbosityLevel\", \"new_verbosity_level\":1}"); diff --git a/example/java/CMakeLists.txt b/example/java/CMakeLists.txt index 46373743f..057c8eca9 100644 --- a/example/java/CMakeLists.txt +++ b/example/java/CMakeLists.txt @@ -105,3 +105,6 @@ install(TARGETS tdjni LIBRARY DESTINATION bin RUNTIME DESTINATION bin ) +if (MSVC AND VCPKG_TOOLCHAIN) + install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/$/" DESTINATION bin FILES_MATCHING PATTERN "*.dll" PATTERN "*.pdb") +endif() diff --git a/example/java/org/drinkless/tdlib/Client.java b/example/java/org/drinkless/tdlib/Client.java index e8bed6787..b923df391 100644 --- a/example/java/org/drinkless/tdlib/Client.java +++ b/example/java/org/drinkless/tdlib/Client.java @@ -188,6 +188,7 @@ public final class Client { if (defaultExceptionHandler != null) { defaultExceptionHandlers.put(nativeClientId, defaultExceptionHandler); } + send(new TdApi.GetAuthorizationState(), null, null); } @Override diff --git a/sqlite/CMakeLists.txt b/sqlite/CMakeLists.txt index 131facb48..6e1c78a4f 100644 --- a/sqlite/CMakeLists.txt +++ b/sqlite/CMakeLists.txt @@ -47,7 +47,7 @@ endif() if (GCC OR CLANG) target_compile_options(tdsqlite PRIVATE -Wno-deprecated-declarations -Wno-unused-variable -Wno-unused-const-variable -Wno-unused-function) if (CLANG) - target_compile_options(tdsqlite PRIVATE -Wno-parentheses-equality -Wno-unused-value) + target_compile_options(tdsqlite PRIVATE -Wno-parentheses-equality -Wno-unused-value -Wno-unused-command-line-argument -Qunused-arguments) 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) diff --git a/td/generate/TlDocumentationGenerator.php b/td/generate/TlDocumentationGenerator.php index 9346fae14..0882b3326 100644 --- a/td/generate/TlDocumentationGenerator.php +++ b/td/generate/TlDocumentationGenerator.php @@ -53,7 +53,7 @@ abstract class TlDocumentationGenerator } } if ($bracket_count === 0) { - if (ctype_upper($str[$pos + 1])) { + if (ord('A') <= ord($str[$pos + 1]) && ord($str[$pos + 1]) <= ord('Z')) { return substr($str, 0, -1).'.)'; } } else { diff --git a/td/generate/scheme/mtproto_api.tlo b/td/generate/scheme/mtproto_api.tlo deleted file mode 100644 index 2e00490e5..000000000 Binary files a/td/generate/scheme/mtproto_api.tlo and /dev/null differ diff --git a/td/generate/scheme/secret_api.tlo b/td/generate/scheme/secret_api.tlo deleted file mode 100644 index d265dbfc9..000000000 Binary files a/td/generate/scheme/secret_api.tlo and /dev/null differ diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 43863b4fd..3165ba3da 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -1053,14 +1053,14 @@ richTextPhoneNumber text:RichText phone_number:string = RichText; //@height Height of a bounding box in which the image should be shown; 0 if unknown richTextIcon document:document width:int32 height:int32 = RichText; -//@description A rich text reference of a text on the same web page @text The text @reference_text The text to show on click @url An HTTP URL, opening the reference -richTextReference text:RichText reference_text:RichText url:string = RichText; +//@description A reference to a richTexts object on the same web page @text The text @anchor_name The name of a richTextAnchor object, which is the first element of the target richTexts object @url An HTTP URL, opening the reference +richTextReference text:RichText anchor_name:string url:string = RichText; //@description An anchor @name Anchor name richTextAnchor name:string = RichText; -//@description A link to an anchor on the same web page @text The link text @name The anchor name. If the name is empty, the link should bring back to top @url An HTTP URL, opening the anchor -richTextAnchorLink text:RichText name:string url:string = RichText; +//@description A link to an anchor on the same web page @text The link text @anchor_name The anchor name. If the name is empty, the link should bring back to top @url An HTTP URL, opening the anchor +richTextAnchorLink text:RichText anchor_name:string url:string = RichText; //@description A concatenation of rich texts @texts Texts richTexts texts:vector = RichText; @@ -3794,7 +3794,7 @@ getMessageLinkInfo url:string = MessageLinkInfo; //@reply_markup Markup for replying to the message; for bots only @input_message_content The content of the message to be sent sendMessage chat_id:int53 message_thread_id:int53 reply_to_message_id:int53 options:messageSendOptions reply_markup:ReplyMarkup input_message_content:InputMessageContent = Message; -//@description Sends messages grouped together into an album. Currently only audio, document, photo and video messages can be grouped into an album. Documents and audio files can be only groupув in an album with messages of the same type. Returns sent messages +//@description Sends messages grouped together into an album. Currently only audio, document, photo and video messages can be grouped into an album. Documents and audio files can be only grouped in an album with messages of the same type. Returns sent messages //@chat_id Target chat //@message_thread_id If not 0, a message thread identifier in which the messages will be sent //@reply_to_message_id Identifier of a message to reply to or 0 diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo deleted file mode 100644 index 7a41f13ba..000000000 Binary files a/td/generate/scheme/td_api.tlo and /dev/null differ diff --git a/td/generate/scheme/telegram_api.tlo b/td/generate/scheme/telegram_api.tlo deleted file mode 100644 index bb3f233d1..000000000 Binary files a/td/generate/scheme/telegram_api.tlo and /dev/null differ diff --git a/td/telegram/Client.cpp b/td/telegram/Client.cpp index 7c36dd13a..f8540726e 100644 --- a/td/telegram/Client.cpp +++ b/td/telegram/Client.cpp @@ -79,21 +79,25 @@ class TdReceiver { class ClientManager::Impl final { public: ClientId create_client() { - if (tds_.empty()) { - CHECK(concurrent_scheduler_ == nullptr); - CHECK(options_.net_query_stats == nullptr); - options_.net_query_stats = std::make_shared(); - concurrent_scheduler_ = make_unique(); - concurrent_scheduler_->init(0); - concurrent_scheduler_->start(); - } + CHECK(client_id_ != std::numeric_limits::max()); auto client_id = ++client_id_; - tds_[client_id] = - concurrent_scheduler_->create_actor_unsafe(0, "Td", receiver_.create_callback(client_id), options_); + pending_clients_.insert(client_id); return client_id; } void send(ClientId client_id, RequestId request_id, td_api::object_ptr &&request) { + if (pending_clients_.erase(client_id) != 0) { + if (tds_.empty()) { + CHECK(concurrent_scheduler_ == nullptr); + CHECK(options_.net_query_stats == nullptr); + options_.net_query_stats = std::make_shared(); + concurrent_scheduler_ = make_unique(); + concurrent_scheduler_->init(0); + concurrent_scheduler_->start(); + } + tds_[client_id] = + concurrent_scheduler_->create_actor_unsafe(0, "Td", receiver_.create_callback(client_id), options_); + } requests_.push_back({client_id, request_id, std::move(request)}); } @@ -196,6 +200,7 @@ class ClientManager::Impl final { unique_ptr concurrent_scheduler_; ClientId client_id_{0}; Td::Options options_; + std::unordered_set pending_clients_; std::unordered_map> tds_; }; @@ -356,14 +361,19 @@ class MultiImpl { MultiImpl(MultiImpl &&) = delete; MultiImpl &operator=(MultiImpl &&) = delete; - int32 create(TdReceiver &receiver) { - auto id = create_id(); - create(id, receiver.create_callback(id)); - return id; + static int32 create_id() { + auto result = current_id_.fetch_add(1); + CHECK(result <= static_cast(std::numeric_limits::max())); + return static_cast(result); + } + + void create(int32 td_id, unique_ptr callback) { + auto guard = concurrent_scheduler_->get_send_guard(); + send_closure(multi_td_, &MultiTd::create, td_id, std::move(callback)); } static bool is_valid_client_id(int32 client_id) { - return client_id > 0 && client_id < current_id_.load(); + return client_id > 0 && static_cast(client_id) < current_id_.load(); } void send(ClientManager::ClientId client_id, ClientManager::RequestId request_id, @@ -394,19 +404,10 @@ class MultiImpl { thread scheduler_thread_; ActorOwn multi_td_; - static std::atomic current_id_; - - static int32 create_id() { - return current_id_.fetch_add(1); - } - - void create(int32 td_id, unique_ptr callback) { - auto guard = concurrent_scheduler_->get_send_guard(); - send_closure(multi_td_, &MultiTd::create, td_id, std::move(callback)); - } + static std::atomic current_id_; }; -std::atomic MultiImpl::current_id_{1}; +std::atomic MultiImpl::current_id_{1}; class MultiImplPool { public: @@ -456,11 +457,10 @@ class MultiImplPool { class ClientManager::Impl final { public: ClientId create_client() { - auto impl = pool_.get(); - auto client_id = impl->create(receiver_); + auto client_id = MultiImpl::create_id(); { auto lock = impls_mutex_.lock_write().move_as_ok(); - impls_[client_id].impl = std::move(impl); + impls_[client_id]; // create empty MultiImplInfo } return client_id; } @@ -472,7 +472,22 @@ class ClientManager::Impl final { td_api::make_object(400, "Invalid TDLib instance specified")); return; } + auto it = impls_.find(client_id); + if (it != impls_.end() && it->second.impl == nullptr) { + lock.reset(); + + auto write_lock = impls_mutex_.lock_write().move_as_ok(); + it = impls_.find(client_id); + if (it != impls_.end() && it->second.impl == nullptr) { + it->second.impl = pool_.get(); + it->second.impl->create(client_id, receiver_.create_callback(client_id)); + } + write_lock.reset(); + + lock = impls_mutex_.lock_read().move_as_ok(); + it = impls_.find(client_id); + } if (it == impls_.end() || it->second.is_closed) { receiver_.add_response(client_id, request_id, td_api::make_object(500, "Request aborted")); return; @@ -515,7 +530,11 @@ class ClientManager::Impl final { CHECK(it != impls_.end()); if (!it->second.is_closed) { it->second.is_closed = true; - it->second.impl->close(client_id); + if (it->second.impl == nullptr) { + receiver_.add_response(client_id, 0, nullptr); + } else { + it->second.impl->close(client_id); + } } } @@ -552,7 +571,8 @@ class Client::Impl final { Impl() { static MultiImplPool pool; multi_impl_ = pool.get(); - td_id_ = multi_impl_->create(receiver_); + td_id_ = MultiImpl::create_id(); + multi_impl_->create(td_id_, receiver_.create_callback(td_id_)); } void send(Request request) { diff --git a/td/telegram/Client.h b/td/telegram/Client.h index 7cb386def..ee1e2d755 100644 --- a/td/telegram/Client.h +++ b/td/telegram/Client.h @@ -184,6 +184,7 @@ class ClientManager final { /** * Creates a new TDLib client and returns its opaque identifier. + * The client will not send updates until the first request is sent to it. */ ClientId create_client(); diff --git a/td/telegram/ClientJson.cpp b/td/telegram/ClientJson.cpp index 1e4f37c57..960b55207 100644 --- a/td/telegram/ClientJson.cpp +++ b/td/telegram/ClientJson.cpp @@ -125,11 +125,11 @@ static std::mutex extra_mutex; static std::unordered_map extra; static std::atomic extra_id{1}; -int td_json_create_client() { +int json_create_client() { return static_cast(get_manager()->create_client()); } -void td_json_send(int client_id, Slice request) { +void json_send(int client_id, Slice request) { auto parsed_request = to_request(request); auto request_id = extra_id.fetch_add(1, std::memory_order_relaxed); if (!parsed_request.second.empty()) { @@ -139,7 +139,7 @@ void td_json_send(int client_id, Slice request) { get_manager()->send(client_id, request_id, std::move(parsed_request.first)); } -const char *td_json_receive(double timeout) { +const char *json_receive(double timeout) { auto response = get_manager()->receive(timeout); if (!response.object) { return nullptr; @@ -157,7 +157,7 @@ const char *td_json_receive(double timeout) { return store_string(from_response(*response.object, extra_str, response.client_id)); } -const char *td_json_execute(Slice request) { +const char *json_execute(Slice request) { auto parsed_request = to_request(request); return store_string( from_response(*ClientManager::execute(std::move(parsed_request.first)), parsed_request.second, 0)); diff --git a/td/telegram/ClientJson.h b/td/telegram/ClientJson.h index 9421b1d33..d7bc85125 100644 --- a/td/telegram/ClientJson.h +++ b/td/telegram/ClientJson.h @@ -33,12 +33,12 @@ class ClientJson final { std::atomic extra_id_{1}; }; -int td_json_create_client(); +int json_create_client(); -void td_json_send(int client_id, Slice request); +void json_send(int client_id, Slice request); -const char *td_json_receive(double timeout); +const char *json_receive(double timeout); -const char *td_json_execute(Slice request); +const char *json_execute(Slice request); } // namespace td diff --git a/td/telegram/WebPageBlock.cpp b/td/telegram/WebPageBlock.cpp index 3be67d5e3..8f770178e 100644 --- a/td/telegram/WebPageBlock.cpp +++ b/td/telegram/WebPageBlock.cpp @@ -128,11 +128,8 @@ class RichText { return make_tl_object(texts[0].get_rich_text_object(context), anchor.str(), content); } else { - context->is_first_pass_ = true; - auto reference_text = it->second->get_rich_text_object(context); - context->is_first_pass_ = false; - return make_tl_object(texts[0].get_rich_text_object(context), - std::move(reference_text), content); + return make_tl_object(texts[0].get_rich_text_object(context), anchor.str(), + content); } } } diff --git a/td/telegram/td_json_client.cpp b/td/telegram/td_json_client.cpp index 3ddb5945c..c346a9048 100644 --- a/td/telegram/td_json_client.cpp +++ b/td/telegram/td_json_client.cpp @@ -31,17 +31,17 @@ const char *td_json_client_execute(void *client, const char *request) { } int td_create_client() { - return td::td_json_create_client(); + return td::json_create_client(); } void td_send(int client_id, const char *request) { - td::td_json_send(client_id, td::Slice(request == nullptr ? "" : request)); + td::json_send(client_id, td::Slice(request == nullptr ? "" : request)); } const char *td_receive(double timeout) { - return td::td_json_receive(timeout); + return td::json_receive(timeout); } const char *td_execute(const char *request) { - return td::td_json_execute(td::Slice(request == nullptr ? "" : request)); + return td::json_execute(td::Slice(request == nullptr ? "" : request)); } diff --git a/td/telegram/td_json_client.h b/td/telegram/td_json_client.h index 918e40522..f98705210 100644 --- a/td/telegram/td_json_client.h +++ b/td/telegram/td_json_client.h @@ -126,8 +126,8 @@ TDJSON_EXPORT void td_json_client_destroy(void *client); */ /** - * Creates a new instance of TDLib. - * \return Opaque indentifier of the created TDLib client. + * Creates a new instance of TDLib. The TDLib instance will not send updates until the first request is sent to it. + * \return Opaque indentifier of the created TDLib instance. */ TDJSON_EXPORT int td_create_client(); diff --git a/tdutils/td/utils/port/uname.cpp b/tdutils/td/utils/port/uname.cpp index ee0fde119..c3694e74d 100644 --- a/tdutils/td/utils/port/uname.cpp +++ b/tdutils/td/utils/port/uname.cpp @@ -20,6 +20,10 @@ #if TD_ANDROID #include +#elif TD_EMSCRIPTEN +#include + +#include #else #if TD_DARWIN #include @@ -90,6 +94,98 @@ Slice get_operating_system_version() { if (length > 0) { return "Android " + string(version, length); } +#elif TD_EMSCRIPTEN + // clang-format off + char *os_name_js = (char*)EM_ASM_INT(({ + function detectOsName() { + if (typeof process === 'object' && typeof process.platform === 'string') { // Node.js + switch (process.platform) { + case 'aix': + return 'IBM AIX'; + case 'android': + return 'Android'; + case 'darwin': + return 'macOS'; + case 'freebsd': + return 'FreeBSD'; + case 'linux': + return 'Linux'; + case 'openbsd': + return 'OpenBSD'; + case 'sunos': + return 'SunOS'; + case 'win32': + return 'Windows'; + case 'darwin': + return 'macOS'; + default: + return 'Node.js'; + } + } + + var userAgent = 'Unknown'; + if (typeof window === 'object') { // Web + userAgent = window.navigator.userAgent; + } else if (typeof importScripts === 'function') { // Web Worker + userAgent = navigator.userAgent; + } + + var match = /(Mac OS|Mac OS X|MacPPC|MacIntel|Mac_PowerPC|Macintosh) ([._0-9]+)/.exec(userAgent); + if (match !== null) { + return 'macOS ' + match[2].replace('_', '.'); + } + + match = /Android [._0-9]+/.exec(userAgent); + if (match !== null) { + return match[0].replace('_', '.'); + } + + if (/(iPhone|iPad|iPod)/.test(userAgent)) { + match = /OS ([._0-9]+)/.exec(userAgent); + if (match !== null) { + return 'iOS ' + match[1].replace('_', '.'); + } + return 'iOS'; + } + + var clientStrings = [ + {s:'Windows 10', r:/(Windows 10.0|Windows NT 10.0)/}, + {s:'Windows 8.1', r:/(Windows 8.1|Windows NT 6.3)/}, + {s:'Windows 8', r:/(Windows 8|Windows NT 6.2)/}, + {s:'Windows 7', r:/(Windows 7|Windows NT 6.1)/}, + {s:'Windows Vista', r:/Windows NT 6.0/}, + {s:'Windows Server 2003', r:/Windows NT 5.2/}, + {s:'Windows XP', r:/(Windows XP|Windows NT 5.1)/}, + {s:'Windows', r:/Windows/}, + {s:'Android', r:/Android/}, + {s:'FreeBSD', r:/FreeBSD/}, + {s:'OpenBSD', r:/OpenBSD/}, + {s:'Chrome OS', r:/CrOS/}, + {s:'Linux', r:/(Linux|X11)/}, + {s:'macOS', r:/(Mac OS|MacPPC|MacIntel|Mac_PowerPC|Macintosh)/}, + {s:'QNX', r:/QNX/}, + {s:'BeOS', r:/BeOS/} + ]; + for (var id in clientStrings) { + var cs = clientStrings[id]; + if (cs.r.test(userAgent)) { + return cs.s; + } + } + return 'Emscripten'; + } + + var os_name = detectOsName(); + var length = lengthBytesUTF8(os_name) + 1; + var result = _malloc(length); + stringToUTF8(os_name, result, length); + return result; + })); + // clang-format on + string os_name(os_name_js); + std::free(os_name_js); + + return os_name; #else #if TD_LINUX auto os_name = read_os_name("/etc/os-release", "PRETTY_NAME=\"", "\"\n"); @@ -124,8 +220,6 @@ Slice get_operating_system_version() { return "NetBSD"; #elif TD_CYGWIN return "Cygwin"; -#elif TD_EMSCRIPTEN - return "Emscripten"; #else return "Unix"; #endif diff --git a/test/tdclient.cpp b/test/tdclient.cpp index 5217b33d8..800108b82 100644 --- a/test/tdclient.cpp +++ b/test/tdclient.cpp @@ -941,9 +941,11 @@ TEST(Client, Manager) { client.send(-1, 3, td::make_tl_object(3)); for (int i = 0; i < threads_n; i++) { threads.emplace_back([&] { - for (int i = 0; i < clients_n; i++) { + for (int i = 0; i <= clients_n; i++) { auto id = client.create_client(); - client.send(id, 3, td::make_tl_object(3)); + if (i != 0) { + client.send(id, 3, td::make_tl_object(3)); + } } }); }