From 1ef5c89a9142768f96173cc84f5a48c577166923 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 19 Jun 2018 13:23:52 +0300 Subject: [PATCH] Allow tg URLs in text links and url buttons. GitOrigin-RevId: 9708c3016375564e737bf5a8f79494807d61c10f --- td/generate/scheme/td_api.tl | 4 ++-- td/telegram/MessageEntity.cpp | 41 ++++++++++++++++------------------- td/telegram/ReplyMarkup.cpp | 5 ++--- td/telegram/misc.cpp | 29 +++++++++++++++++++++++++ td/telegram/misc.h | 5 +++++ tdutils/td/utils/HttpUrl.cpp | 4 ++-- tdutils/td/utils/HttpUrl.h | 2 +- 7 files changed, 60 insertions(+), 30 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 45b9a458f..787b52e95 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -582,7 +582,7 @@ keyboardButton text:string type:KeyboardButtonType = KeyboardButton; //@class InlineKeyboardButtonType @description Describes the type of an inline keyboard button -//@description A button that opens a specified URL @url URL to open +//@description A button that opens a specified URL @url HTTP or tg:// URL to open inlineKeyboardButtonTypeUrl url:string = InlineKeyboardButtonType; //@description A button that sends a special callback query to a bot @data Data to be sent to the bot via a callback query @@ -1179,7 +1179,7 @@ textEntityTypePre = TextEntityType; //@description Text that must be formatted as if inside pre, and code HTML tags @language Programming language of the code; as defined by the sender textEntityTypePreCode language:string = TextEntityType; -//@description A text description shown instead of a raw URL @url URL to be opened when the link is clicked +//@description A text description shown instead of a raw URL @url HTTP or tg:// URL to be opened when the link is clicked textEntityTypeTextUrl url:string = TextEntityType; //@description A text shows instead of a raw mention of the user (e.g., when the user has no username) @user_id Identifier of the mentioned user diff --git a/td/telegram/MessageEntity.cpp b/td/telegram/MessageEntity.cpp index 53f2baeb6..dae0fddd7 100644 --- a/td/telegram/MessageEntity.cpp +++ b/td/telegram/MessageEntity.cpp @@ -9,7 +9,6 @@ #include "td/telegram/ContactsManager.h" #include "td/telegram/misc.h" -#include "td/utils/HttpUrl.h" #include "td/utils/logging.h" #include "td/utils/misc.h" #include "td/utils/unicode.h" @@ -1329,10 +1328,10 @@ Result> parse_markdown(string &text) { if (user_id.is_valid()) { entities.emplace_back(utf16_offset, utf16_entity_length, user_id); } else { - auto r_http_url = parse_url(url); - if (r_http_url.is_ok() && url.find('.') != string::npos) { + auto r_url = check_url(url); + if (r_url.is_ok()) { entities.emplace_back(MessageEntity::Type::TextUrl, utf16_offset, utf16_entity_length, - r_http_url.ok().get_url()); + r_url.move_as_ok()); } } break; @@ -1585,10 +1584,9 @@ Result> parse_html(string &text) { if (user_id.is_valid()) { entities.emplace_back(utf16_offset, utf16_entity_length, user_id); } else { - auto r_http_url = parse_url(url); - if (r_http_url.is_ok() && url.find('.') != string::npos) { - entities.emplace_back(MessageEntity::Type::TextUrl, utf16_offset, utf16_entity_length, - r_http_url.ok().get_url()); + auto r_url = check_url(url); + if (r_url.is_ok()) { + entities.emplace_back(MessageEntity::Type::TextUrl, utf16_offset, utf16_entity_length, r_url.move_as_ok()); } } } else if (tag_name == "pre") { @@ -1746,12 +1744,11 @@ Result> get_message_entities(const ContactsManager *contac if (!clean_input_string(entity_text_url->url_)) { return Status::Error(400, "MessageEntityTextUrl.url must be encoded in UTF-8"); } - auto r_http_url = parse_url(entity_text_url->url_); - if (r_http_url.is_error()) { - return Status::Error(400, PSTRING() << "Wrong message entity: " << r_http_url.error().message()); + auto r_url = check_url(entity_text_url->url_); + if (r_url.is_error()) { + return Status::Error(400, PSTRING() << "Wrong message entity: " << r_url.error().message()); } - entities.emplace_back(MessageEntity::Type::TextUrl, entity->offset_, entity->length_, - r_http_url.ok().get_url()); + entities.emplace_back(MessageEntity::Type::TextUrl, entity->offset_, entity->length_, r_url.move_as_ok()); break; } case td_api::textEntityTypeMentionName::ID: { @@ -1843,14 +1840,14 @@ vector get_message_entities(const ContactsManager *contacts_manag case telegram_api::messageEntityTextUrl::ID: { // TODO const telegram_api::messageEntityTextUrl * auto entity_text_url = static_cast(entity.get()); - auto r_http_url = parse_url(entity_text_url->url_); - if (r_http_url.is_error()) { - LOG(ERROR) << "Wrong URL entity: \"" << entity_text_url->url_ << "\": " << r_http_url.error().message() - << " from " << source; + auto r_url = check_url(entity_text_url->url_); + if (r_url.is_error()) { + LOG(ERROR) << "Wrong URL entity: \"" << entity_text_url->url_ << "\": " << r_url.error().message() << " from " + << source; continue; } entities.emplace_back(MessageEntity::Type::TextUrl, entity_text_url->offset_, entity_text_url->length_, - r_http_url.ok().get_url()); + r_url.move_as_ok()); break; } case telegram_api::messageEntityMentionName::ID: { @@ -1947,13 +1944,13 @@ vector get_message_entities(vectorurl_ << '"'; continue; } - auto r_http_url = parse_url(entity_text_url->url_); - if (r_http_url.is_error()) { - LOG(WARNING) << "Wrong URL entity: \"" << entity_text_url->url_ << "\": " << r_http_url.error().message(); + auto r_url = check_url(entity_text_url->url_); + if (r_url.is_error()) { + LOG(WARNING) << "Wrong URL entity: \"" << entity_text_url->url_ << "\": " << r_url.error().message(); continue; } entities.emplace_back(MessageEntity::Type::TextUrl, entity_text_url->offset_, entity_text_url->length_, - r_http_url.ok().get_url()); + r_url.move_as_ok()); break; } case secret_api::messageEntityMentionName::ID: diff --git a/td/telegram/ReplyMarkup.cpp b/td/telegram/ReplyMarkup.cpp index 110331f2b..c498b2adc 100644 --- a/td/telegram/ReplyMarkup.cpp +++ b/td/telegram/ReplyMarkup.cpp @@ -13,7 +13,6 @@ #include "td/utils/buffer.h" #include "td/utils/format.h" -#include "td/utils/HttpUrl.h" #include "td/utils/logging.h" namespace td { @@ -363,8 +362,8 @@ static Result get_inline_keyboard_button(tl_object_ptr(button->type_.get())->url_)); - current_button.data = http_url.get_url(); + TRY_RESULT(url, check_url(static_cast(button->type_.get())->url_)); + current_button.data = std::move(url); if (!clean_input_string(current_button.data)) { return Status::Error(400, "Inline keyboard button url must be encoded in UTF-8"); } diff --git a/td/telegram/misc.cpp b/td/telegram/misc.cpp index fc72546c7..59cd77ca0 100644 --- a/td/telegram/misc.cpp +++ b/td/telegram/misc.cpp @@ -7,6 +7,7 @@ #include "td/telegram/misc.h" #include "td/utils/common.h" +#include "td/utils/HttpUrl.h" #include "td/utils/logging.h" #include "td/utils/misc.h" #include "td/utils/Slice.h" @@ -274,4 +275,32 @@ string get_emoji_fingerprint(uint64 num) { return emojis[(num & 0x7FFFFFFFFFFFFFFF) % emojis.size()].str(); } +Result check_url(MutableSlice url) { + bool is_tg = false; + if (begins_with(url, "tg://")) { + url.remove_prefix(5); + is_tg = true; + } else if (begins_with(url, "tg:")) { + url.remove_prefix(3); + is_tg = true; + } else { + is_tg = false; + } + TRY_RESULT(http_url, parse_url(url)); + if (is_tg) { + if (begins_with(url, "http://") || http_url.protocol_ == HttpUrl::Protocol::HTTPS || !http_url.userinfo_.empty() || http_url.specified_port_ != 0 || http_url.is_ipv6_) { + return Status::Error("Wrong tg URL"); + } + + auto result = http_url.get_url(); + CHECK(begins_with(result, "http://")); + return PSTRING() << "tg" << Slice(result).substr(4); + } + + if (url.find('.') == string::npos) { + return Status::Error("Wrong HTTP URL"); + } + return http_url.get_url(); +} + } // namespace td diff --git a/td/telegram/misc.h b/td/telegram/misc.h index 8efbb23c2..22ce23c09 100644 --- a/td/telegram/misc.h +++ b/td/telegram/misc.h @@ -7,6 +7,8 @@ #pragma once #include "td/utils/common.h" +#include "td/utils/Slice.h" +#include "td/utils/Status.h" namespace td { @@ -31,4 +33,7 @@ int32 get_vector_hash(const vector &numbers) TD_WARN_UNUSED_RESULT; // returns emoji corresponding to the specified number string get_emoji_fingerprint(uint64 num); +// checks whether url is a valid tg or HTTP(S) URL and returns its in a canonical form +Result check_url(MutableSlice url); + } // namespace td diff --git a/tdutils/td/utils/HttpUrl.cpp b/tdutils/td/utils/HttpUrl.cpp index 55b66f7b3..d30913db1 100644 --- a/tdutils/td/utils/HttpUrl.cpp +++ b/tdutils/td/utils/HttpUrl.cpp @@ -29,11 +29,11 @@ string HttpUrl::get_url() const { result += userinfo_; result += '@'; } - if (is_ipv6) { + if (is_ipv6_) { result += '['; } result += host_; - if (is_ipv6) { + if (is_ipv6_) { result += ']'; } if (specified_port_ > 0) { diff --git a/tdutils/td/utils/HttpUrl.h b/tdutils/td/utils/HttpUrl.h index f7d1e4aab..9328b5831 100644 --- a/tdutils/td/utils/HttpUrl.h +++ b/tdutils/td/utils/HttpUrl.h @@ -18,7 +18,7 @@ class HttpUrl { enum class Protocol { HTTP, HTTPS } protocol_; string userinfo_; string host_; - bool is_ipv6; + bool is_ipv6_; int specified_port_; int port_; string query_;