/* This file is part of telegram-purple This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA Copyright Matthias Jentsch 2014-2015 */ #include "telegram-purple.h" static struct request_values_data *request_values_data_init (struct tgl_state *TLS, void *callback, void *arg, int num_values) { struct request_values_data *data = talloc0 (sizeof (struct request_values_data)); data->TLS = TLS; data->callback = callback; data->arg = arg; data->num_values = num_values; return data; } static void request_code_entered (struct request_values_data *data, const gchar *code) { char *stripped = g_strstrip (purple_markup_strip_html (code)); debug ("sending code: '%s'\n", stripped); data->callback (data->TLS, (const char **)&stripped, data->arg); g_free (stripped); } static void request_canceled_disconnect (struct request_values_data *data) { purple_connection_error_reason (tls_get_conn (data->TLS), PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, "registration canceled"); free (data); } static void request_canceled (struct request_values_data *data) { free (data); } static void request_code (struct tgl_state *TLS, void (*callback) (struct tgl_state *TLS, const char *string[], void *arg), void *arg) { debug ("client is not registered, registering..."); char *explanation = _("Telegram wants to verify your " "identity. Please enter the login code that you have received via SMS."); if (purple_account_get_bool (tls_get_pa (TLS), "compat-verification", 0) || !purple_request_input (tls_get_conn (TLS), _("Login code"), _("Enter login code"), explanation, NULL, 0, 0, _("the code"), _("OK"), G_CALLBACK(request_code_entered), _("Cancel"), G_CALLBACK(request_canceled_disconnect), tls_get_pa (TLS), NULL, NULL, request_values_data_init (TLS, callback, arg, 0))) { // the purple request API is not supported, create a new conversation (the Telegram system account "Telegram") to // prompt the user for the code. tls_get_data (TLS)->request_code_data = request_values_data_init (TLS, callback, arg, 0); purple_connection_set_state (tls_get_conn (TLS), PURPLE_CONNECTED); PurpleConversation *conv = purple_conversation_new (PURPLE_CONV_TYPE_IM, tls_get_pa (TLS), "Telegram"); purple_conversation_write (conv, "Telegram", explanation, PURPLE_MESSAGE_RECV | PURPLE_MESSAGE_SYSTEM, 0); } } static void request_name (struct tgl_state *TLS, void (*callback) (struct tgl_state *TLS, const char *string[], void *arg), void *arg); static void request_name_code_entered (struct request_values_data *data, PurpleRequestFields* fields) { char *names[] = { g_strdup ("y"), // input line is a y/n choice g_strstrip (g_strdup (purple_request_fields_get_string (fields, "first_name"))), g_strstrip (g_strdup (purple_request_fields_get_string (fields, "last_name"))) }; if (str_not_empty (names[1]) && str_not_empty (names[2])) { data->callback (data->TLS, (const char **) names, data->arg); } else { request_name (data->TLS, data->callback, data->arg); } int j; for (j = 0; j < 3; ++j) { g_free (names[j]); } free (data); } static void request_name (struct tgl_state *TLS, void (*callback) (struct tgl_state *TLS, const char *string[], void *arg), void *arg) { debug("Phone is not registered, registering..."); PurpleRequestFields *fields = purple_request_fields_new (); PurpleRequestField *field = 0; PurpleRequestFieldGroup *group = purple_request_field_group_new (_("Registration")); field = purple_request_field_string_new ("first_name", _("First name"), "", 0); purple_request_field_group_add_field (group, field); field = purple_request_field_string_new ("last_name", _("Last name"), "", 0); purple_request_field_group_add_field (group, field); purple_request_fields_add_group (fields, group); if (!purple_request_fields (tls_get_conn (TLS), _("Register"), _("Please register your phone number."), NULL, fields, _("OK"), G_CALLBACK(request_name_code_entered), _("Cancel"), G_CALLBACK(request_canceled_disconnect), tls_get_pa (TLS), NULL, NULL, request_values_data_init(TLS, callback, arg, 0))) { // purple_request API not available const char *error = _("Phone number is not registered. Please register your phone on a different client."); purple_connection_error_reason (tls_get_conn (TLS), PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, error); purple_notify_error (_telegram_protocol, _("Not registered"), _("Not registered"), error); } } static void request_password_entered (struct request_values_data *data, char *password) { data->callback (data->TLS, (const char **) &password, data->arg); free (data); } void request_password (struct tgl_state *TLS, void (*callback) (struct tgl_state *TLS, const char *string[], void *arg), void *arg) { if (! purple_request_input (tls_get_conn (TLS), _("Password needed"), _("Password needed"), _("Enter password for two factor authentication"), NULL, FALSE, TRUE, NULL, _("Ok"), G_CALLBACK(request_password_entered), _("Cancel"), G_CALLBACK(request_canceled_disconnect), tls_get_pa (TLS), NULL, NULL, request_values_data_init (TLS, callback, arg, 0))) { const char *error = _("No password set for two factor authentication. Please enter it in the extended settings."); purple_connection_error_reason (tls_get_conn (TLS), PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, error); purple_notify_error (_telegram_protocol, _("Password invalid"), _("Password invalid"), error); } } static void accept_secret_chat_cb (struct accept_secret_chat_data *data) { tgl_do_accept_encr_chat_request (data->TLS, data->U, write_secret_chat_gw, 0); free (data); } static void decline_secret_chat_cb (struct accept_secret_chat_data *data) { bl_do_peer_delete (data->TLS, data->U->id); purple_blist_remove_buddy (tgp_blist_buddy_find (data->TLS, data->U->id)); free (data); } void request_accept_secret_chat (struct tgl_state *TLS, struct tgl_secret_chat *U) { tgl_peer_t *P = tgl_peer_get (TLS, TGL_MK_USER(U->user_id)); g_return_if_fail (P); struct accept_secret_chat_data *data = talloc0 (sizeof (struct accept_secret_chat_data)); data->TLS = TLS; data->U = U; gchar *message = g_strdup_printf (_("Accept secret chat '%s' on this device?"), U->print_name); purple_request_accept_cancel (tls_get_conn (TLS), _("Secret chat"), message, _("Secret chats can only have one " "end point. If you accept a secret chat on this device, its messages will not be available anywhere " "else. If you decline, you can still accept the chat on other devices."), 0, tls_get_pa (TLS), P->print_name, NULL, data, G_CALLBACK(accept_secret_chat_cb), G_CALLBACK(decline_secret_chat_cb)); g_free (message); } static void create_group_chat_cb (struct request_values_data *data, PurpleRequestFields* fields) { // FIXME: Oh god. const char *users[3] = { purple_request_fields_get_string (fields, "user1"), purple_request_fields_get_string (fields, "user2"), purple_request_fields_get_string (fields, "user3") }; tgp_create_group_chat_by_usernames (data->TLS, data->arg, users, 3, FALSE); g_free (data->arg); free (data); } static void cancel_group_chat_cb (struct request_values_data *data) { g_free (data->arg); free (data); } void request_create_chat (struct tgl_state *TLS, const char *subject) { // Telegram doesn't allow to create chats with only one user, so we need to force // the user to specify at least one other one. PurpleRequestFields* fields = purple_request_fields_new (); PurpleRequestFieldGroup* group = purple_request_field_group_new ( _("Invite at least one additional user by specifying\n their full name (autocompletion available).\nYou can add more users once" " the chat was created.")); PurpleRequestField *field = purple_request_field_string_new ("user1", _("Username"), NULL, FALSE); purple_request_field_set_type_hint (field, "screenname"); purple_request_field_group_add_field (group, field); field = purple_request_field_string_new ("user2", _("Username"), NULL, FALSE); purple_request_field_set_type_hint (field, "screenname"); purple_request_field_group_add_field (group, field); field = purple_request_field_string_new ("user3", _("Username"), NULL, FALSE); purple_request_field_set_type_hint (field, "screenname"); purple_request_field_group_add_field (group, field); purple_request_fields_add_group (fields, group); purple_request_fields (tls_get_conn (TLS), _("Create group chat"), _("Invite users"), NULL, fields, _("OK"), G_CALLBACK(create_group_chat_cb), _("Cancel"), G_CALLBACK(cancel_group_chat_cb), tls_get_pa (TLS), NULL, NULL, request_values_data_init (TLS, NULL, (void *) g_strdup (subject), 0)); } static void request_cur_and_new_password_ok (struct request_values_data *data, PurpleRequestFields* fields) { const char *names[3] = { purple_request_fields_get_string (fields, "current"), purple_request_fields_get_string (fields, "new1"), purple_request_fields_get_string (fields, "new2") }; data->callback(data->TLS, names, data->arg); free (data); } void request_cur_and_new_password (struct tgl_state *TLS, void (*callback) (struct tgl_state *TLS, const char *string[], void *arg), void *arg) { PurpleRequestFields* fields = purple_request_fields_new (); PurpleRequestFieldGroup* group = purple_request_field_group_new (_("Change password")); PurpleRequestField *field = purple_request_field_string_new ("current", _("Current"), NULL, FALSE); purple_request_field_string_set_masked (field, TRUE); purple_request_field_group_add_field (group, field); field = purple_request_field_string_new ("new1", _("Password"), NULL, FALSE); purple_request_field_string_set_masked (field, TRUE); purple_request_field_group_add_field (group, field); field = purple_request_field_string_new ("new2", _("Confirm"), NULL, FALSE); purple_request_field_string_set_masked (field, TRUE); purple_request_field_group_add_field (group, field); purple_request_fields_add_group (fields, group); purple_request_fields (tls_get_conn (TLS), _("Change password"), _("Change password"), NULL, fields, _("OK"), G_CALLBACK(request_cur_and_new_password_ok), _("Cancel"), G_CALLBACK(request_canceled), tls_get_pa (TLS), NULL, NULL, request_values_data_init (TLS, callback, arg, 0)); } static void request_new_password_ok (struct request_values_data *data, PurpleRequestFields* fields) { const char *names[2] = { purple_request_fields_get_string (fields, "new1"), purple_request_fields_get_string (fields, "new2") }; data->callback(data->TLS, names, data->arg); free (data); } void request_new_password (struct tgl_state *TLS, void (*callback) (struct tgl_state *TLS, const char *string[], void *arg), void *arg) { PurpleRequestFields* fields = purple_request_fields_new (); PurpleRequestFieldGroup* group = purple_request_field_group_new (_("New password")); PurpleRequestField * field = purple_request_field_string_new ("new1", _("Password"), NULL, FALSE); purple_request_field_string_set_masked (field, TRUE); purple_request_field_group_add_field (group, field); field = purple_request_field_string_new ("new2", _("Confirm"), NULL, FALSE); purple_request_field_string_set_masked (field, TRUE); purple_request_field_group_add_field (group, field); purple_request_fields_add_group (fields, group); purple_request_fields (tls_get_conn (TLS), _("New password"), _("New password"), NULL, fields, _("OK"), G_CALLBACK(request_new_password_ok), _("Cancel"), G_CALLBACK(request_canceled), tls_get_pa (TLS), NULL, NULL, request_values_data_init (TLS, callback, arg, 0)); } void request_value (struct tgl_state *TLS, enum tgl_value_type type, const char *prompt, int num_values, void (*callback) (struct tgl_state *TLS, const char *string[], void *arg), void *arg) { debug ("tgl requests user input, tgl_value_type: %d, prompt: %s, count: %d", type, prompt, num_values); switch (type) { case tgl_cur_password: { const char *P = purple_account_get_string (tls_get_pa(TLS), TGP_KEY_PASSWORD_TWO_FACTOR, NULL); if (str_not_empty (P)) { if (tls_get_data (TLS)->password_retries ++ < 1) { callback (TLS, &P, arg); return; } } request_password (TLS, callback, arg); break; } case tgl_cur_and_new_password: request_cur_and_new_password (TLS, callback, arg); break; case tgl_new_password: request_new_password (TLS, callback, arg); break; case tgl_register_info: request_name (TLS, callback, arg); break; case tgl_code: request_code (TLS, callback, arg); break; case tgl_phone_number: { // if we arrive here for the second time the specified phone number is not valid. We do not // ask for the phone number directly, cause in that case the account would still be created // named with the invalid phone number, even though the login will work tgp_error_if_false (TLS, tls_get_data (TLS)->login_retries++ < 1, _("Invalid phone number"), _("Please enter only numbers in the international phone number format, " "a leading + following by the country prefix and the phone number.\n" "Do not use any other special chars.")); const char *username = purple_account_get_username (tls_get_pa (TLS)); callback (TLS, &username, arg); break; } case tgl_bot_hash: assert (FALSE && "we are not a bot"); break; } } // delete contact static void request_delete_contact_ok (struct request_values_data *data, PurpleRequestFields* fields) { tgl_peer_t *P = data->arg; g_return_if_fail(P); switch (tgl_get_peer_type (P->id)) { case TGL_PEER_CHAT: g_warn_if_fail (P->chat.flags & TGLCF_LEFT); leave_and_delete_chat (data->TLS, P); break; case TGL_PEER_ENCR_CHAT: tgl_do_discard_secret_chat (data->TLS, &P->encr_chat, NULL, NULL); break; case TGL_PEER_USER: g_warn_if_fail(P->user.flags & TGLUF_CONTACT); tgl_do_del_contact (data->TLS, P->id, tgp_notify_on_error_gw, NULL); break; case TGL_PEER_CHANNEL: g_warn_if_fail(P->channel.flags & TGLCHF_CREATOR); if (! (P->channel.flags & TGLCHF_LEFT)) { tgl_do_leave_channel (data->TLS, P->id, tgp_notify_on_error_gw, NULL); } break; default: g_warn_if_reached(); break; } free (data); } static void request_delete_contact_cancel (struct request_values_data *data, PurpleRequestFields* fields) { free (data); } void tgprpl_request_delete_contact (PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) { const char *title1 = NULL, *title2 = NULL, *msg = NULL; g_return_if_fail(buddy); struct tgl_state *TLS = gc_get_tls (gc); tgl_peer_t *P = tgp_blist_buddy_get_peer (buddy); g_return_if_fail(P); switch (tgl_get_peer_type (P->id)) { case TGL_PEER_CHAT: if (! (P->chat.flags & TGLCF_LEFT)) { title1 = _("Leave Chat"); title2 = title1; msg = _("Do you want to leave this chat permantently?"); } break; case TGL_PEER_ENCR_CHAT: title1 = _("Abort Secret Chat"); title2 = title1; msg = _("Do you want to terminate this secret chat permantently?"); break; case TGL_PEER_USER: if (P->user.flags & TGLUF_CONTACT) { title1 = _("Delete Contact"); title2 = title1; msg = _("Do you want to remove this user from your global contact list?"); } break; case TGL_PEER_CHANNEL: if (P->channel.flags & TGLCHF_CREATOR) { /* FIXME: Support destorying channels title1 = _("Destroy Channel"); title2 = title1; msg = _("You are admin of this channel, do you want to delete it permanently?"); */ } else { if (! (P->channel.flags & TGLCHF_LEFT)) { title1 = _("Leave Channel"); title2 = title1; msg = _("Do you want to leave this channel?"); } } break; default: g_warn_if_reached(); break; } if (msg) { purple_request_ok_cancel(tls_get_conn (TLS), title1, title2, msg, 0, tls_get_pa (TLS), tgp_blist_lookup_purple_name (TLS, P->id), NULL, (void *) request_values_data_init (TLS, 0, P, 0), request_delete_contact_ok, request_delete_contact_cancel); } } // add new contact void request_add_contact (struct tgl_state *TLS) { }