Support secret chats

Load and store secret chats in a file, handle secret chat updates, visualize fingerprint in settings
This commit is contained in:
mjentsch 2015-01-04 12:36:13 +01:00
parent 0c718206d7
commit 1dda956166
5 changed files with 262 additions and 88 deletions

View file

@ -31,13 +31,15 @@
#include <glib.h>
#include <request.h>
#include <openssl/sha.h>
#include <telegram-purple.h>
#include <msglog.h>
#include <tgp-2prpl.h>
#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;

View file

@ -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);

View file

@ -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("<br><img id=\"%u\">", 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()

View file

@ -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) {

View file

@ -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);