diff --git a/modules/smtp/smtp_client.cpp b/modules/smtp/smtp_client.cpp index 693493225..574dcaa5c 100644 --- a/modules/smtp/smtp_client.cpp +++ b/modules/smtp/smtp_client.cpp @@ -30,14 +30,699 @@ #include "smtp_client.h" +#include "core/bind/core_bind.h" +#include "core/config/engine.h" +#include "core/io/stream_peer_ssl.h" +#include "core/io/stream_peer_tcp.h" +#include "core/log/logger.h" +#include "core/os/os.h" + +#include "email.h" + +String SMTPClient::get_client_id() const { + return client_id; +} +void SMTPClient::set_client_id(const String &p_value) { + client_id = p_value; +} + +String SMTPClient::get_host() const { + return host; +} +void SMTPClient::set_host(const String &p_value) { + host = p_value; +} + +int SMTPClient::get_port() const { + return port; +} +void SMTPClient::set_port(const int p_value) { + port = p_value; +} + +SMTPClient::TLSMethod SMTPClient::get_tls_method() const { + return tls_method; +} +void SMTPClient::set_tls_method(const TLSMethod p_value) { + tls_method = p_value; +} + +String SMTPClient::get_server_auth_username() const { + return server_auth_username; +} +void SMTPClient::set_server_auth_username(const String &p_value) { + server_auth_username = p_value; +} + +String SMTPClient::get_server_auth_password() const { + return server_auth_password; +} +void SMTPClient::set_server_auth_password(const String &p_value) { + server_auth_password = p_value; +} + +SMTPClient::ServerAuthMethod SMTPClient::get_server_auth_method() const { + return server_auth_method; +} +void SMTPClient::set_server_auth_method(const ServerAuthMethod p_value) { + server_auth_method = p_value; +} + +String SMTPClient::get_email_default_sender_email() const { + return email_default_sender_email; +} +void SMTPClient::set_email_default_sender_email(const String &p_value) { + email_default_sender_email = p_value; +} + +String SMTPClient::get_email_default_sender_name() const { + return email_default_sender_name; +} +void SMTPClient::set_email_default_sender_name(const String &p_value) { + email_default_sender_name = p_value; +} + +bool SMTPClient::get_use_threads() const { + return _use_threads; +} +void SMTPClient::set_use_threads(const bool p_value) { + if (_use_threads == p_value) { + return; + } + + if (Engine::get_singleton()->is_editor_hint()) { + _use_threads = p_value; + return; + } + + if (is_inside_tree()) { + if (should_use_threading()) { + //currently using threads, will no longer be the case + + if (_worker_thread) { + _worker_thread_running = false; + //slow quit = let the current mail finish processing + _worker_thread_fast_quit = false; + + _worker_semaphore.post(); + _worker_thread->wait_to_finish(); + memdelete(_worker_thread); + _worker_thread = NULL; + } + + } else { + //currently not using threads, we want to + + if (should_use_threading()) { + _worker_thread_running = true; + _worker_thread = memnew(Thread); + _worker_thread->start(_worker_thread_func, this); + } + } + } + + _use_threads = p_value; +} + +int SMTPClient::get_thread_sleep_usec() const { + return thread_sleep_usec; +} +void SMTPClient::set_thread_sleep_usec(const int p_value) { + thread_sleep_usec = p_value; +} + +void SMTPClient::send_email(const Ref &p_email) { + ERR_FAIL_COND(!Engine::get_singleton()->is_editor_hint()); + ERR_FAIL_COND(!is_inside_tree()); + ERR_FAIL_COND(!p_email.is_valid()); + + if (should_use_threading()) { + _mail_queue_mutex.lock(); + _mail_queue.push_back(p_email); + _mail_queue_mutex.unlock(); + + _worker_semaphore.post(); + } else { + _send_email(p_email); + } +} + +bool SMTPClient::should_use_threading() const { +#if !defined(NO_THREADS) + return _use_threads; +#else + return false; +#endif +} + +void SMTPClient::_send_email(const Ref &p_email) { + _current_session_email = p_email; + + if (!_current_session_email.is_valid()) { + return; + } + + Error err = _tcp_client->connect_to_host(host, port); + if (err != OK) { + PLOG_ERR("Could not connect! " + itos(err)); + + Dictionary error_body; + + error_body["message"] = "Error connecting to host."; + error_body["code"] = err; + + emit_signal("error", error_body); + + Dictionary result; + result["success"] = false; + result["error"] = error_body; + + emit_signal("result", result); + + if (!should_use_threading()) { + set_process(false); + } + } + + if (tls_method == TLS_METHOD_SMTPS) { + err = _tls_client->connect_to_stream(_tcp_client, false, host); + + if (err != OK) { + _current_session_status = SESSION_STATUS_SERVER_ERROR; + + Dictionary error_body; + + error_body["message"] = "Error connecting to TLS Stream."; + error_body["code"] = err; + + emit_signal("error", error_body); + + Dictionary result; + result["success"] = false; + result["error"] = error_body; + + emit_signal("result", result); + + if (!should_use_threading()) { + set_process(false); + } + + return; + } + + _current_tls_started = true; + _current_tls_established = true; + } + + _current_session_status = SESSION_STATUS_HELO; + + if (!should_use_threading()) { + set_process(true); + } +} + +Error SMTPClient::poll_client() { + if (_current_tls_started or _current_tls_established) { + _tls_client->poll(); + return OK; + } else { + return OK; + } +} + +bool SMTPClient::client_get_status() { + if (_current_tls_started) { + return _tls_client->get_status() == StreamPeerSSL::STATUS_CONNECTED; + } + + return _tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED; +} + +int SMTPClient::client_get_available_bytes() { + if (_current_tls_started) { + return _tls_client->get_available_bytes(); + } + + return _tcp_client->get_available_bytes(); +} + +String SMTPClient::client_get_string(int bytes) { + if (_current_tls_started) { + return _tls_client->get_string(bytes); + } + + return _tcp_client->get_string(bytes); +} + +bool SMTPClient::start_auth() { + if (server_auth_method == SERVER_AUTH_PLAIN) { + _current_session_status = SESSION_STATUS_AUTHENTICATED; + return true; + } + + if (!write_command("AUTH LOGIN")) { + return false; + } + + _current_session_status = SESSION_STATUS_AUTH_LOGIN; + return true; +} +bool SMTPClient::start_hello() { + //_current_session_status = SESSION_STATUS_HELO + if (!write_command("HELO " + client_id)) { + return false; + } + + _current_session_status = SESSION_STATUS_HELO_ACK; + return true; +} + +Error SMTPClient::client_put_data(const CharString &data) { + if (_current_tls_established) { + return _tls_client->put_data((const uint8_t *)data.ptr(), data.size()); + } + + return _tcp_client->put_data((const uint8_t *)data.ptr(), data.size()); +} + +bool SMTPClient::write_command(const String &command) { + //print("COMMAND: " + command) + Error err = client_put_data((command + "\n").utf8()); + + if (err != OK) { + _current_session_status = SESSION_STATUS_COMMAND_NOT_SENT; + + Dictionary error_body; + error_body["message"] = "Session error on command: " + command; + error_body["code"] = err; + + emit_signal("error", error_body); + + Dictionary result; + result["success"] = false; + result["error"] = error_body; + + emit_signal("result", result); + } + + return (err == OK); +} + +Error SMTPClient::write_data(const String &data) { + return client_put_data((data + "\r\n.\r\n").utf8()); +} + +void SMTPClient::close_connection() { + _current_session_status = SESSION_STATUS_NONE; + _tls_client->disconnect_from_stream(); + _tcp_client->disconnect_from_host(); + _current_session_email.unref(); + _current_to_index = 0; + _current_tls_started = false; + _current_tls_established = false; + + if (!should_use_threading()) { + set_process(false); + } +} + +String SMTPClient::encode_username() { + return _Marshalls::get_singleton()->utf8_to_base64(server_auth_username); +} +String SMTPClient::encode_password() { + return _Marshalls::get_singleton()->utf8_to_base64(server_auth_password); +} + +void SMTPClient::_process_email() { + if (_current_session_status == SESSION_STATUS_SERVER_ERROR) { + close_connection(); + } + + if (poll_client() == OK) { + bool connected = client_get_status(); + + if (connected) { + int bytes = client_get_available_bytes(); + + if (bytes > 0) { + String msg = client_get_string(bytes); + //print("RECEIVED: " + msg) + String code = msg.left(3); + + if (code == "220") { + if (_current_session_status == SESSION_STATUS_HELO) { + start_hello(); + } else if (_current_session_status == SESSION_STATUS_STARTTLS) { + Error err = _tls_client->connect_to_stream(_tcp_client, false, host); + + if (err != OK) { + _current_session_status = SESSION_STATUS_SERVER_ERROR; + + Dictionary error_body; + error_body["message"] = "Error connecting to TLS Stream."; + error_body["code"] = err; + + emit_signal("error", error_body); + + Dictionary result; + result["success"] = false; + result["error"] = error_body; + + emit_signal("result", result); + + return; + } + + _current_tls_started = true; + _current_tls_established = true; + + // We need to do HELO + EHLO again + _current_session_status = SESSION_STATUS_HELO; + start_hello(); + } + + } else if (code == "250") { + if (_current_session_status == SESSION_STATUS_HELO_ACK) { + if (!write_command("EHLO " + client_id)) { + return; + } + + _current_session_status = SESSION_STATUS_EHLO_ACK; + + } else if (_current_session_status == SESSION_STATUS_EHLO_ACK) { + if (tls_method == TLS_METHOD_STARTTLS) { + if (_current_tls_started) { + // second round of HELO + EHLO done + if (!start_auth()) { + return; + } + + } else { + if (!write_command("STARTTLS")) { + return; + } + + _current_session_status = SESSION_STATUS_STARTTLS; + } + } else { + if (!start_auth()) { + return; + } + } + + } else if (_current_session_status == SESSION_STATUS_MAIL_FROM) { + if (_current_to_index < _current_session_email->get_recipient_count()) { + if (!write_command("RCPT TO: <" + _current_session_email->get_recipient_address(_current_to_index) + ">")) { + return; + } + + _current_to_index += 1; + } + + if (_current_cc_index < _current_session_email->get_cc_count()) { + if (!write_command("RCPT TO: <" + _current_session_email->get_cc_address(_current_cc_index) + ">")) { + return; + } + + _current_cc_index += 1; + } + + _current_session_status = SESSION_STATUS_RCPT_TO; + + } else if (_current_session_status == SESSION_STATUS_RCPT_TO) { + if (_current_to_index < _current_session_email->get_recipient_count()) { + _current_session_status = SESSION_STATUS_MAIL_FROM; + return; + } + + if (_current_cc_index < _current_session_email->get_cc_count()) { + _current_session_status = SESSION_STATUS_MAIL_FROM; + return; + } + + if (!write_command("DATA")) { + return; + } + + _current_session_status = SESSION_STATUS_DATA; + + } else if (_current_session_status == SESSION_STATUS_DATA_ACK) { + if (!write_command("QUIT")) { + return; + } + + _current_session_status = SESSION_STATUS_QUIT; + } + + } else if (code == "221") { + if (_current_session_status == SESSION_STATUS_QUIT) { + close_connection(); + emit_signal("email_sent"); + + Dictionary result; + result["success"] = true; + + emit_signal("result", result); + } + } else if (code == "235") { + // Authentication Succeeded + + if (_current_session_status == SESSION_STATUS_PASSWORD) { + _current_session_status = SESSION_STATUS_AUTHENTICATED; + } + + } else if (code == "334") { + if (_current_session_status == SESSION_STATUS_AUTH_LOGIN) { + if (msg.begins_with("334 VXNlcm5hbWU6")) { + if (!write_command(encode_username())) { + return; + } + + _current_session_status = SESSION_STATUS_USERNAME; + } + } else if (_current_session_status == SESSION_STATUS_USERNAME) { + if (msg.begins_with("334 UGFzc3dvcmQ6")) { + if (!write_command(encode_password())) { + return; + } + + _current_session_status = SESSION_STATUS_PASSWORD; + } + } + } else if (code == "354") { + if (_current_session_status == SESSION_STATUS_DATA) { + if (!(write_data(_current_session_email->get_email_data_string(email_default_sender_name, email_default_sender_email)) == OK)) { + _current_session_status = SESSION_STATUS_SERVER_ERROR; + return; + } + + _current_session_status = SESSION_STATUS_DATA_ACK; + } + } else { + PLOG_ERR(msg); + } + } + } + + if (_current_session_email.is_valid() && (_current_session_status == SESSION_STATUS_AUTHENTICATED)) { + _current_session_status = SESSION_STATUS_MAIL_FROM; + + String sender_address = _current_session_email->get_sender_address(); + + String fn; + + if (sender_address.size() > 0) { + fn = "<" + sender_address + ">"; + } else { + fn = "<" + email_default_sender_email + ">"; + } + + if (!write_command("MAIL FROM: " + fn)) { + return; + } + } else { + return; + } + } else { + PLOG_ERR("Couldn't poll!") + } +} + +void SMTPClient::_worker_thread_func(void *user_data) { + SMTPClient *self = (SMTPClient *)user_data; + + while (self->_worker_thread_running) { + Ref _mail; + + self->_mail_queue_mutex.lock(); + + int size = self->_mail_queue.size(); + if (size > 0) { + _mail = self->_mail_queue[0]; + self->_mail_queue.remove(0); + } + + self->_mail_queue_mutex.unlock(); + + if (_mail.is_valid()) { + self->_send_email(_mail); + } + + while (self->_current_session_email.is_valid()) { + OS::get_singleton()->delay_usec(self->thread_sleep_usec); + + // Early return if we want to quit + if (!self->_worker_thread_running && self->_worker_thread_fast_quit) { + self->close_connection(); + return; + } + + self->_process_email(); + } + + if (!self->_worker_thread_running) { + return; + } + + if (self->_mail_queue.size() == 0) { + self->_worker_semaphore.wait(); + } + } +} SMTPClient::SMTPClient() { + client_id = "smtp.pandemoniumengine.org"; + port = 465; + + tls_method = TLS_METHOD_SMTPS; + + server_auth_method = SERVER_AUTH_LOGIN; + + _use_threads = true; + _worker_thread = NULL; + // 10 msec + thread_sleep_usec = 10000; + + // Networking + _tcp_client.instance(); + _tls_client = Ref(StreamPeerSSL::create()); + + //SessionStatus + _current_session_status = SESSION_STATUS_NONE; + _current_to_index = 0; + _current_cc_index = 0; + + _current_tls_started = false; + _current_tls_established = false; + + // Threading + _worker_thread_running = false; + _worker_thread_fast_quit = false; + + set_process(false); } SMTPClient::~SMTPClient() { } -void SMTPClient::_bind_methods() { - //ClassDB::bind_method(D_METHOD("get_ignored_urls"), &SMTPClient::get_ignored_urls); - //ClassDB::bind_method(D_METHOD("set_ignored_urls", "val"), &SMTPClient::set_ignored_urls); - //ADD_PROPERTY(PropertyInfo(Variant::POOL_STRING_ARRAY, "ignored_urls"), "set_ignored_urls", "get_ignored_urls"); +void SMTPClient::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_PROCESS: { + if (Engine::get_singleton()->is_editor_hint()) { + set_process(false); + return; + } + + if (should_use_threading()) { + set_process(false); + return; + } + + _process_email(); + } break; + case NOTIFICATION_ENTER_TREE: { + if (Engine::get_singleton()->is_editor_hint()) { + set_process(false); + return; + } + + set_process(false); + + _worker_thread_fast_quit = false; + + if (should_use_threading()) { + _worker_thread_running = true; + _worker_thread = memnew(Thread); + _worker_thread->start(_worker_thread_func, this); + } + + } break; + case NOTIFICATION_EXIT_TREE: { + if (Engine::get_singleton()->is_editor_hint()) { + set_process(false); + return; + } + + if (_worker_thread) { + _worker_thread_running = false; + _worker_thread_fast_quit = true; + _worker_semaphore.post(); + _worker_thread->wait_to_finish(); + memdelete(_worker_thread); + _worker_thread = NULL; + } + + } break; + } +} + +void SMTPClient::_bind_methods() { + ADD_SIGNAL(MethodInfo("error", PropertyInfo(Variant::DICTIONARY, "error"))); + ADD_SIGNAL(MethodInfo("email_sent")); + ADD_SIGNAL(MethodInfo("result", PropertyInfo(Variant::DICTIONARY, "content"))); + + ClassDB::bind_method(D_METHOD("get_client_id"), &SMTPClient::get_client_id); + ClassDB::bind_method(D_METHOD("set_client_id", "val"), &SMTPClient::set_client_id); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "client_id"), "set_client_id", "get_client_id"); + + ClassDB::bind_method(D_METHOD("get_host"), &SMTPClient::get_host); + ClassDB::bind_method(D_METHOD("set_host", "val"), &SMTPClient::set_host); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "host"), "set_host", "get_host"); + + ClassDB::bind_method(D_METHOD("get_port"), &SMTPClient::get_port); + ClassDB::bind_method(D_METHOD("set_port", "val"), &SMTPClient::set_port); + ADD_PROPERTY(PropertyInfo(Variant::INT, "port"), "set_port", "get_port"); + + ClassDB::bind_method(D_METHOD("get_tls_method"), &SMTPClient::get_tls_method); + ClassDB::bind_method(D_METHOD("set_tls_method", "val"), &SMTPClient::set_tls_method); + ADD_PROPERTY(PropertyInfo(Variant::INT, "tls_method", PROPERTY_HINT_ENUM, "NONE,STARTTLS,SMTPS"), "set_tls_method", "get_tls_method"); + + ClassDB::bind_method(D_METHOD("get_server_auth_username"), &SMTPClient::get_server_auth_username); + ClassDB::bind_method(D_METHOD("set_server_auth_username", "val"), &SMTPClient::set_server_auth_username); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "server_auth_username"), "set_server_auth_username", "get_server_auth_username"); + + ClassDB::bind_method(D_METHOD("get_server_auth_password"), &SMTPClient::get_server_auth_password); + ClassDB::bind_method(D_METHOD("set_server_auth_password", "val"), &SMTPClient::set_server_auth_password); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "server_auth_password"), "set_server_auth_password", "get_server_auth_password"); + + ClassDB::bind_method(D_METHOD("get_server_auth_method"), &SMTPClient::get_server_auth_method); + ClassDB::bind_method(D_METHOD("set_server_auth_method", "val"), &SMTPClient::set_server_auth_method); + ADD_PROPERTY(PropertyInfo(Variant::INT, "server_auth_method", PROPERTY_HINT_ENUM, "Plain,Login"), "set_server_auth_method", "get_server_auth_method"); + + ClassDB::bind_method(D_METHOD("get_email_default_sender_email"), &SMTPClient::get_email_default_sender_email); + ClassDB::bind_method(D_METHOD("set_email_default_sender_email", "val"), &SMTPClient::set_email_default_sender_email); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "email_default_sender_email"), "set_email_default_sender_email", "get_email_default_sender_email"); + + ClassDB::bind_method(D_METHOD("get_email_default_sender_name"), &SMTPClient::get_email_default_sender_name); + ClassDB::bind_method(D_METHOD("set_email_default_sender_name", "val"), &SMTPClient::set_email_default_sender_name); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "email_default_sender_name"), "set_email_default_sender_name", "get_email_default_sender_name"); + + ClassDB::bind_method(D_METHOD("get_use_threads"), &SMTPClient::get_use_threads); + ClassDB::bind_method(D_METHOD("set_use_threads", "val"), &SMTPClient::set_use_threads); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_threads"), "set_use_threads", "get_use_threads"); + + ClassDB::bind_method(D_METHOD("get_thread_sleep_usec"), &SMTPClient::get_thread_sleep_usec); + ClassDB::bind_method(D_METHOD("set_thread_sleep_usec", "val"), &SMTPClient::set_thread_sleep_usec); + ADD_PROPERTY(PropertyInfo(Variant::INT, "thread_sleep_usec"), "set_thread_sleep_usec", "get_thread_sleep_usec"); + + ClassDB::bind_method(D_METHOD("send_email", "email"), &SMTPClient::send_email); } diff --git a/modules/smtp/smtp_client.h b/modules/smtp/smtp_client.h index a2913b95a..1d1e08551 100644 --- a/modules/smtp/smtp_client.h +++ b/modules/smtp/smtp_client.h @@ -31,17 +31,165 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#include "core/object/reference.h" +#include "core/os/mutex.h" +#include "core/os/semaphore.h" +#include "core/os/thread.h" + #include "scene/main/node.h" +class EMail; +class StreamPeerTCP; +class StreamPeerSSL; + class SMTPClient : public Node { GDCLASS(SMTPClient, Node); public: + enum TLSMethod { + TLS_METHOD_NONE = 0, + TLS_METHOD_STARTTLS, + TLS_METHOD_SMTPS, + }; + + enum ServerAuthMethod { + SERVER_AUTH_PLAIN, + SERVER_AUTH_LOGIN + }; + + String get_client_id() const; + void set_client_id(const String &p_value); + + String get_host() const; + void set_host(const String &p_value); + + int get_port() const; + void set_port(const int p_value); + + TLSMethod get_tls_method() const; + void set_tls_method(const TLSMethod p_value); + + String get_server_auth_username() const; + void set_server_auth_username(const String &p_value); + + String get_server_auth_password() const; + void set_server_auth_password(const String &p_value); + + ServerAuthMethod get_server_auth_method() const; + void set_server_auth_method(const ServerAuthMethod p_value); + + String get_email_default_sender_email() const; + void set_email_default_sender_email(const String &p_value); + + String get_email_default_sender_name() const; + void set_email_default_sender_name(const String &p_value); + + bool get_use_threads() const; + void set_use_threads(const bool p_value); + + int get_thread_sleep_usec() const; + void set_thread_sleep_usec(const int p_value); + + void send_email(const Ref &p_email); + SMTPClient(); ~SMTPClient(); protected: + enum SessionStatus { + SESSION_STATUS_NONE = 0, + SESSION_STATUS_SERVER_ERROR, + SESSION_STATUS_COMMAND_NOT_SENT, + SESSION_STATUS_COMMAND_REFUSED, + SESSION_STATUS_HELO, + SESSION_STATUS_HELO_ACK, + SESSION_STATUS_EHLO, + SESSION_STATUS_EHLO_ACK, + SESSION_STATUS_MAIL_FROM, + SESSION_STATUS_RCPT_TO, + SESSION_STATUS_DATA, + SESSION_STATUS_DATA_ACK, + SESSION_STATUS_QUIT, + SESSION_STATUS_STARTTLS, + SESSION_STATUS_STARTTLS_ACK, + SESSION_STATUS_AUTH_LOGIN, + SESSION_STATUS_USERNAME, + SESSION_STATUS_PASSWORD, + SESSION_STATUS_AUTHENTICATED + }; + + bool should_use_threading() const; + + void _send_email(const Ref &p_email); + + Error poll_client(); + + bool client_get_status(); + int client_get_available_bytes(); + String client_get_string(int bytes); + + bool start_auth(); + bool start_hello(); + + Error client_put_data(const CharString &data); + + bool write_command(const String &command); + Error write_data(const String &data); + + void close_connection(); + + String encode_username(); + String encode_password(); + + void _process_email(); + + static void _worker_thread_func(void *user_data); + + void _notification(int p_what); static void _bind_methods(); + + String client_id; + + String host; + int port; + + TLSMethod tls_method; + + String server_auth_username; + String server_auth_password; + + ServerAuthMethod server_auth_method; + + String email_default_sender_email; + String email_default_sender_name; + + bool _use_threads; + int thread_sleep_usec; + + // Networking + Ref _tls_client; + Ref _tcp_client; + + // SessionStatus + SessionStatus _current_session_status; + Ref _current_session_email; + int _current_to_index; + int _current_cc_index; + + bool _current_tls_started; + bool _current_tls_established; + + // Threading + bool _worker_thread_running; + bool _worker_thread_fast_quit; + Thread *_worker_thread; + Semaphore _worker_semaphore; + Mutex _mail_queue_mutex; + + Vector> _mail_queue; }; +VARIANT_ENUM_CAST(SMTPClient::TLSMethod); +VARIANT_ENUM_CAST(SMTPClient::ServerAuthMethod); + #endif