diff --git a/telegram-base.c b/telegram-base.c index cffd29b..1b575f3 100644 --- a/telegram-base.c +++ b/telegram-base.c @@ -31,13 +31,15 @@ #include #include +#include -#include -#include -#include +#include "telegram-purple.h" +#include "msglog.h" +#include "tgp-2prpl.h" #include "tgp-structs.h" #include "lodepng/lodepng.h" + #define DC_SERIALIZED_MAGIC 0x868aa81d #define STATE_FILE_MAGIC 0x28949a93 #define SECRET_CHAT_FILE_MAGIC 0x37a1988a @@ -234,6 +236,136 @@ void read_auth_file (struct tgl_state *TLS) { close (auth_file_fd); } + +void write_secret_chat (tgl_peer_t *_P, void *extra) { + struct tgl_secret_chat *P = (void *)_P; + if (tgl_get_peer_type (P->id) != TGL_PEER_ENCR_CHAT) { return; } + if (P->state != sc_ok) { return; } + int *a = extra; + int fd = a[0]; + a[1] ++; + + int id = tgl_get_peer_id (P->id); + assert (write (fd, &id, 4) == 4); + //assert (write (fd, &P->flags, 4) == 4); + int l = strlen (P->print_name); + assert (write (fd, &l, 4) == 4); + assert (write (fd, P->print_name, l) == l); + assert (write (fd, &P->user_id, 4) == 4); + assert (write (fd, &P->admin_id, 4) == 4); + assert (write (fd, &P->date, 4) == 4); + assert (write (fd, &P->ttl, 4) == 4); + assert (write (fd, &P->layer, 4) == 4); + assert (write (fd, &P->access_hash, 8) == 8); + assert (write (fd, &P->state, 4) == 4); + assert (write (fd, &P->key_fingerprint, 8) == 8); + assert (write (fd, &P->key, 256) == 256); + assert (write (fd, &P->first_key_sha, 20) == 20); + assert (write (fd, &P->in_seq_no, 4) == 4); + assert (write (fd, &P->last_in_seq_no, 4) == 4); + assert (write (fd, &P->out_seq_no, 4) == 4); +} + +void write_secret_chat_file (struct tgl_state *TLS) { + char *name = 0; + if (asprintf (&name, "%s/%s", TLS->base_path, "secret") < 0) { + return; + } + int secret_chat_fd = open (name, O_CREAT | O_RDWR, 0600); + free (name); + assert (secret_chat_fd >= 0); + int x = SECRET_CHAT_FILE_MAGIC; + assert (write (secret_chat_fd, &x, 4) == 4); + x = 2; + assert (write (secret_chat_fd, &x, 4) == 4); // version + assert (write (secret_chat_fd, &x, 4) == 4); // num + + int y[2]; + y[0] = secret_chat_fd; + y[1] = 0; + + tgl_peer_iterator_ex (TLS, write_secret_chat, y); + + lseek (secret_chat_fd, 8, SEEK_SET); + assert (write (secret_chat_fd, &y[1], 4) == 4); + close (secret_chat_fd); +} + +void read_secret_chat (struct tgl_state *TLS, int fd, int v) { + int id, l, user_id, admin_id, date, ttl, layer, state; + long long access_hash, key_fingerprint; + static char s[1000]; + static unsigned char key[256]; + static unsigned char sha[20]; + assert (read (fd, &id, 4) == 4); + //assert (read (fd, &flags, 4) == 4); + assert (read (fd, &l, 4) == 4); + assert (l > 0 && l < 1000); + assert (read (fd, s, l) == l); + assert (read (fd, &user_id, 4) == 4); + assert (read (fd, &admin_id, 4) == 4); + assert (read (fd, &date, 4) == 4); + assert (read (fd, &ttl, 4) == 4); + assert (read (fd, &layer, 4) == 4); + assert (read (fd, &access_hash, 8) == 8); + assert (read (fd, &state, 4) == 4); + assert (read (fd, &key_fingerprint, 8) == 8); + assert (read (fd, &key, 256) == 256); + if (v >= 2) { + assert (read (fd, sha, 20) == 20); + } + int in_seq_no = 0, out_seq_no = 0, last_in_seq_no = 0; + if (v >= 1) { + assert (read (fd, &in_seq_no, 4) == 4); + assert (read (fd, &last_in_seq_no, 4) == 4); + assert (read (fd, &out_seq_no, 4) == 4); + } + + bl_do_encr_chat_create (TLS, id, user_id, admin_id, s, l); + struct tgl_secret_chat *P = (void *)tgl_peer_get (TLS, TGL_MK_ENCR_CHAT (id)); + assert (P && (P->flags & FLAG_CREATED)); + bl_do_encr_chat_set_date (TLS, P, date); + bl_do_encr_chat_set_ttl (TLS, P, ttl); + bl_do_encr_chat_set_layer (TLS ,P, layer); + bl_do_encr_chat_set_state (TLS, P, state); + bl_do_encr_chat_set_key (TLS, P, key, key_fingerprint); + if (v >= 2) { + bl_do_encr_chat_set_sha (TLS, P, sha); + } else { + SHA1 ((void *)key, 256, sha); + bl_do_encr_chat_set_sha (TLS, P, sha); + } + if (v >= 1) { + bl_do_encr_chat_set_seq (TLS, P, in_seq_no, last_in_seq_no, out_seq_no); + } + bl_do_encr_chat_set_access_hash (TLS, P, access_hash); +} + +void read_secret_chat_file (struct tgl_state *TLS) { + char *name = 0; + if (asprintf (&name, "%s/%s", TLS->base_path, "secret") < 0) { + return; + } + + int secret_chat_fd = open (name, O_RDWR, 0600); + free (name); + + if (secret_chat_fd < 0) { return; } + + int x; + if (read (secret_chat_fd, &x, 4) < 4) { close (secret_chat_fd); return; } + if (x != SECRET_CHAT_FILE_MAGIC) { close (secret_chat_fd); return; } + int v = 0; + assert (read (secret_chat_fd, &v, 4) == 4); + assert (v == 0 || v == 1 || v == 2); // version + assert (read (secret_chat_fd, &x, 4) == 4); + assert (x >= 0); + while (x -- > 0) { + read_secret_chat (TLS, secret_chat_fd, v); + } + close (secret_chat_fd); +} + void telegram_export_authorization (struct tgl_state *TLS); void export_auth_callback (struct tgl_state *TLS, void *extra, int success) { if (!error_if_val_false(TLS, success, "Authentication Export failed.")) { @@ -397,6 +529,7 @@ static int check_all_authorized (gpointer arg) { void telegram_login (struct tgl_state *TLS) { read_auth_file (TLS); read_state_file (TLS); + read_secret_chat_file (TLS); if (all_authorized (TLS)) { telegram_send_sms (TLS); return; diff --git a/telegram-base.h b/telegram-base.h index 72ed30f..80c87a1 100644 --- a/telegram-base.h +++ b/telegram-base.h @@ -26,6 +26,8 @@ void read_state_file (struct tgl_state *TLS); void read_auth_file (struct tgl_state *TLS); void write_auth_file (struct tgl_state *TLS); void write_state_file (struct tgl_state *TLS); +void read_secret_chat_file (struct tgl_state *TLS); +void write_secret_chat_file (struct tgl_state *TLS); void telegram_login (struct tgl_state *TLS); PurpleConversation *chat_show (PurpleConnection *gc, int id); diff --git a/telegram-purple.c b/telegram-purple.c index b38b0d4..d9f0685 100644 --- a/telegram-purple.c +++ b/telegram-purple.c @@ -153,7 +153,6 @@ static char *format_service_msg (struct tgl_state *TLS, struct tgl_message *M) case tgl_message_action_notify_layer: txt_action = g_strdup_printf ("updated layer to %d", M->action.layer); break; - /* case tgl_message_action_request_key: txt_action = g_strdup_printf ("Request rekey #%016llx\n", M->action.exchange_id); break; @@ -166,7 +165,6 @@ static char *format_service_msg (struct tgl_state *TLS, struct tgl_message *M) case tgl_message_action_abort_key: txt_action = g_strdup_printf ("Abort rekey #%016llx\n", M->action.exchange_id); break; - */ default: txt_action = NULL; break; @@ -180,6 +178,13 @@ static char *format_service_msg (struct tgl_state *TLS, struct tgl_message *M) return txt; } +static void tgl_do_send_unescape_message (struct tgl_state *TLS, const char *message, tgl_peer_id_t to) +{ + gchar *raw = purple_unescape_html(message); + tgl_do_send_message (TLS, to, raw, (int)strlen (raw), 0, 0); + g_free(raw); +} + static int our_msg (struct tgl_state *TLS, struct tgl_message *M) { //return tgl_get_peer_id(M->from_id) == TLS->our_id; //return M->out; @@ -201,6 +206,19 @@ static gboolean queries_timerfunc (gpointer data) { return 1; } +static void created_secret_chat (struct tgl_state *TLS, void *data, int success, struct tgl_secret_chat *chat) { + debug ("created_secret_chat: success=%d\n", success); +} + +static void start_secret_chat (PurpleBlistNode *node, gpointer data) { + PurpleBuddy *buddy = data; + connection_data *conn = purple_connection_get_protocol_data ( + purple_account_get_connection (purple_buddy_get_account(buddy))); + + tgl_do_create_secret_chat(conn->TLS, TGL_MK_USER(atoi (purple_buddy_get_name (buddy))), + created_secret_chat, buddy); +} + static void on_update_user_name (struct tgl_state *TLS, tgl_peer_t *user) __attribute__ ((unused)); static void on_update_user_name (struct tgl_state *TLS, tgl_peer_t *user) { p2tgl_got_alias(TLS, user->id, p2tgl_strdup_alias(user)); @@ -264,13 +282,6 @@ void on_message_load_photo (struct tgl_state *TLS, void *extra, int success, cha } break; - case TGL_PEER_ENCR_CHAT: - debug ("PEER_ENCR_CHAT\n"); - if (our_msg(TLS, M)) { - //encr_chat_add_message(...) - } - break; - case TGL_PEER_GEO_CHAT: break; } @@ -330,6 +341,17 @@ static void update_message_received (struct tgl_state *TLS, struct tgl_message * } break; + case TGL_PEER_ENCR_CHAT: + if (!our_msg(TLS, M)) { + p2tgl_got_im (TLS, M->to_id, text, PURPLE_MESSAGE_RECV, M->date); + + pending_reads_add (conn->pending_reads, M->to_id); + if (p2tgl_status_is_present (purple_account_get_active_status(conn->pa))) { + pending_reads_send_all (conn->pending_reads, conn->TLS); + } + } + break; + case TGL_PEER_USER: debug ("PEER_USER\n"); @@ -342,20 +364,13 @@ static void update_message_received (struct tgl_state *TLS, struct tgl_message * p2tgl_got_im (TLS, M->from_id, text, PURPLE_MESSAGE_RECV, M->date); pending_reads_add (conn->pending_reads, M->from_id); - if (p2tgl_status_is_present(purple_account_get_active_status(conn->pa))) { + if (p2tgl_status_is_present (purple_account_get_active_status(conn->pa))) { pending_reads_send_all (conn->pending_reads, conn->TLS); } } } break; - case TGL_PEER_ENCR_CHAT: - debug ("PEER_ENCR_CHAT\n"); - if (! our_msg(TLS, M)) { - //encr_chat_add_message(...) - } - break; - case TGL_PEER_GEO_CHAT: break; } @@ -389,15 +404,51 @@ static void update_user_handler (struct tgl_state *TLS, struct tgl_user *user, u } } -static void update_secret_chat_handler (struct tgl_state *TLS, struct tgl_secret_chat *C, unsigned flags) { - //tgl_do_get_chat_info (TLS, chat->id, 0, on_chat_get_info, 0); - PurpleBuddy *buddy = p2tgl_buddy_find (TLS, C->id); +static void write_secret_chat_cb (struct tgl_state *TLS, void *extra, int success, struct tgl_secret_chat *E) { + debug ("update_secret_chat_handle success=%d", success); + write_secret_chat_file (TLS); +} + +static void update_secret_chat_handler (struct tgl_state *TLS, struct tgl_secret_chat *U, unsigned flags) { + if (U->state == sc_none) { return; } + debug ("secret-chat-state: %d", U->state); + + PurpleBuddy *buddy = p2tgl_buddy_find (TLS, U->id); + if (!buddy) { + buddy = p2tgl_buddy_new (TLS, (tgl_peer_t *)U); + purple_blist_add_buddy (buddy, NULL, tggroup, NULL); + purple_blist_alias_buddy (buddy, U->print_name); + } + + if (U->first_key_sha[0]) { + // display secret key + PurpleNotifyUserInfo *info = purple_notify_user_info_new(); + int sha1key_store_id = generate_ident_icon (TLS, U->first_key_sha); + if (sha1key_store_id != -1) { + char *ident_icon = format_img_full (sha1key_store_id); + purple_notify_user_info_add_pair (info, "Secret Key", ident_icon); + g_free(ident_icon); + } + connection_data *conn = TLS->ev_base; + char *id = g_strdup_printf ("%d", tgl_get_peer_id (U->id)); + purple_notify_userinfo (conn->gc, id, info, NULL, NULL); + g_free (id); + } + + p2tgl_prpl_got_set_status_mobile (TLS, U->id); + + if ((flags & TGL_UPDATE_WORKING) || (flags & TGL_UPDATE_DELETED)) { + write_secret_chat_file (TLS); + } + + if ((flags & TGL_UPDATE_REQUESTED)) { + // TODO: autoaccept setting, otherwise prompt for ok + tgl_do_accept_encr_chat_request (TLS, U, write_secret_chat_cb, 0); + } if (flags & TGL_UPDATE_CREATED) { - if (!buddy) { - buddy = p2tgl_buddy_new (TLS, (tgl_peer_t *)C); - purple_blist_add_buddy (buddy, NULL, tggroup, NULL); - } + purple_buddy_set_protocol_data (buddy, (gpointer)U); + p2tgl_buddy_update (TLS, (tgl_peer_t *)U, flags); } if (flags & (TGL_UPDATE_NAME | TGL_UPDATE_REAL_NAME | TGL_UPDATE_USERNAME) && buddy) { } @@ -486,16 +537,6 @@ static void on_userpic_loaded (struct tgl_state *TLS, void *extra, int success, PurpleNotifyUserInfo *info = create_user_notify_info(U); char *profile_image = profile_image = format_img_full(imgStoreId); purple_notify_user_info_add_pair (info, "Profile image", profile_image); - //TODO: get the sha1key from the secret chat - unsigned char sha1_key[20] = {129, 236, 235, 161, 62, 139, 244, 162, 120, 99, 99, 26, 171, 224, 25, 125}; - int sha1key_store_id = generate_ident_icon(TLS, sha1_key); - if(sha1key_store_id != -1) - { - char *ident_icon = g_strdup_printf("
", sha1key_store_id); - purple_notify_user_info_add_pair(info, "Identification icon", ident_icon); - g_free(ident_icon); - } - purple_notify_userinfo (conn->gc, who, info, NULL, NULL); g_free (profile_image); } @@ -505,7 +546,10 @@ static void on_userpic_loaded (struct tgl_state *TLS, void *extra, int success, void on_user_get_info (struct tgl_state *TLS, void *show_info, int success, struct tgl_user *U) { - assert (success); + if (! success) { + warning ("on_user_get_info not successfull, aborting...\n"); + return; + } if (U->photo.sizes_num == 0) { if (show_info) { @@ -558,7 +602,7 @@ void on_ready (struct tgl_state *TLS) { purple_connection_set_state(conn->gc, PURPLE_CONNECTED); purple_connection_set_display_name(conn->gc, purple_account_get_username(conn->pa)); purple_blist_add_account(conn->pa); - tggroup = purple_find_group("Telegram"); + tggroup = purple_find_group ("Telegram"); if (tggroup == NULL) { debug ("PurpleGroup = NULL, creating"); tggroup = purple_group_new ("Telegram"); @@ -621,6 +665,20 @@ static GList *tgprpl_status_types (PurpleAccount * acct) { return g_list_reverse (types); } +static GList* tgprpl_blist_node_menu (PurpleBlistNode *node) { + purple_debug_info (PLUGIN_ID, "tgprpl_blist_node_menu()\n"); + + GList* menu = NULL; + if (PURPLE_BLIST_NODE_IS_BUDDY(node)) { + // Add encrypted chat option to the right click menu of all buddies + PurpleBuddy* buddy = (PurpleBuddy*)node; + PurpleMenuAction* menu_action = purple_menu_action_new("Start Secret Chat", + PURPLE_CALLBACK(start_secret_chat), buddy, NULL); + menu = g_list_append(menu, (gpointer)menu_action); + } + return menu; +} + static GList *tgprpl_chat_join_info (PurpleConnection * gc) { debug ("tgprpl_chat_join_info()\n"); struct proto_chat_entry *pce; @@ -702,15 +760,24 @@ static int tgprpl_send_im (PurpleConnection * gc, const char *who, const char *m return -1; } - tgl_peer_t *peer = tgl_peer_get(conn->TLS, TGL_MK_USER(atoi (who))); - if (!peer) { - warning ("Protocol data tgl_peer_t for %s not found, cannot send IM\n", who); - return -1; + /* + Make sure that we only send messages to an existing peer by + searching it in the peer tree. This allows us to give immediate feedback + by returning an error-code in case the peer doesn't exist + */ + tgl_peer_t *peer = tgl_peer_get (conn->TLS, TGL_MK_USER(atoi (who))); + if (peer) { + tgl_do_send_unescape_message (conn->TLS, message, peer->id); + return 1; } - gchar *raw = purple_unescape_html(message); - tgl_do_send_message (conn->TLS, peer->id, raw, (int)strlen (raw), 0, 0); - g_free(raw); - return 1; + peer = tgl_peer_get (conn->TLS, TGL_MK_ENCR_CHAT(atoi(who))); + if (peer) { + tgl_do_send_unescape_message (conn->TLS, message, peer->id); + return 1; + } + + // peer not found + return -1; } static unsigned int tgprpl_send_typing (PurpleConnection * gc, const char *who, PurpleTypingState typing) { @@ -829,10 +896,7 @@ static void tgprpl_chat_invite (PurpleConnection * gc, int id, const char *messa static int tgprpl_send_chat (PurpleConnection * gc, int id, const char *message, PurpleMessageFlags flags) { debug ("tgprpl_send_chat()\n"); connection_data *conn = purple_connection_get_protocol_data (gc); - - gchar *raw = purple_unescape_html(message); - tgl_do_send_message (conn->TLS, TGL_MK_CHAT(id), raw, (int)strlen (raw), 0, 0); - g_free (raw); + tgl_do_send_unescape_message (conn->TLS, message, TGL_MK_CHAT(id)); /* Pidgin won't display the written message if we don't call this, Adium will display it twice if we call it, so we don't do it for the adium Plugin. @@ -881,25 +945,6 @@ static gboolean tgprpl_offline_message (const PurpleBuddy * buddy) { return 0; } -void start_encrypted_chat ( PurpleBlistNode *node, gpointer data ) -{ - debug("start_encrypted_chat()\n"); -} - -//Add encrypted chat option to the rightclick menue of all buddies -static GList* tgprpl_blist_node_menu(PurpleBlistNode *node) -{ - purple_debug_info (PLUGIN_ID, "tgprpl_blist_node_menu()\n"); - GList* menu = NULL; - if (PURPLE_BLIST_NODE_IS_BUDDY(node)) - { - PurpleBuddy* buddy = (PurpleBuddy*)node; - PurpleMenuAction* menu_action = purple_menu_action_new("Start encrypted chat...", PURPLE_CALLBACK(start_encrypted_chat), buddy, NULL); - menu = g_list_append(menu, (gpointer)menu_action); - } - return menu; -} - static PurplePluginProtocolInfo prpl_info = { OPT_PROTO_NO_PASSWORD, NULL, // user_splits, initialized in tgprpl_init() diff --git a/tgp-2prpl.c b/tgp-2prpl.c index e6e4191..2afc86e 100644 --- a/tgp-2prpl.c +++ b/tgp-2prpl.c @@ -68,26 +68,12 @@ PurpleConnection *tg_get_conn (struct tgl_state *TLS) { return (PurpleConnection *) ((connection_data *)TLS->ev_base)->gc; } -static char *peer_strdup_id(tgl_peer_id_t user) { +static char *p2tgl_peer_strdup_id (tgl_peer_id_t user) { return g_strdup_printf("%d", tgl_get_peer_id(user)); } -char *p2tgl_strdup_alias(tgl_peer_t *user) { - char *alias = malloc(64); - - // snprintf returs number of bytes it wanted to write - // negative return points on write error - // should never happen - int r = user_get_alias(user, alias, 64); - if (r >= 64) { - warning ("user name too long"); - alias[63] = 0; - } - assert (r >= 0); - - gchar *g_alias = g_strdup(alias); - free (alias); - return g_alias; +gchar *p2tgl_strdup_alias (tgl_peer_t *user) { + return g_strdup (user->print_name); } int p2tgl_status_is_present (PurpleStatus *status) @@ -205,6 +191,14 @@ PurpleBuddy *p2tgl_buddy_update (struct tgl_state *TLS, tgl_peer_t *user, unsign return b; } +void p2tgl_prpl_got_set_status_mobile (struct tgl_state *TLS, tgl_peer_id_t user) { + char *name = peer_strdup_id (user); + + purple_prpl_got_user_status (tg_get_acc(TLS), name, "mobile", NULL); + + g_free (name); +} + void p2tgl_prpl_got_user_status (struct tgl_state *TLS, tgl_peer_id_t user, struct tgl_user_status *status) { if (status->online == 1) { diff --git a/tgp-2prpl.h b/tgp-2prpl.h index 77f04a5..0c4fad5 100644 --- a/tgp-2prpl.h +++ b/tgp-2prpl.h @@ -53,7 +53,7 @@ PurpleBuddy *p2tgl_buddy_new (struct tgl_state *TLS, tgl_peer_t *user); PurpleBuddy *p2tgl_buddy_update (struct tgl_state *TLS, tgl_peer_t *user, unsigned flags); void p2tgl_buddy_add_data (struct tgl_state *TLS, tgl_peer_id_t user, void *data); void p2tgl_prpl_got_user_status (struct tgl_state *TLS, tgl_peer_id_t user, struct tgl_user_status *status); - +void p2tgl_prpl_got_set_status_mobile (struct tgl_state *TLS, tgl_peer_id_t user); void p2tgl_connection_set_display_name(struct tgl_state *TLS, tgl_peer_t *user); void p2tgl_conv_del_user (PurpleConversation *conv, tgl_peer_id_t user);