void on_chat_get_info (struct tgl_state *TLS, void *extra, int success, struct tgl_chat *C) { assert (success); debug ("on_chat_joined(%d)\n", tgl_get_peer_id (C->id)); telegram_conn *conn = TLS->ev_base; PurpleConversation *conv; if (!(conv = purple_find_chat(conn->gc, tgl_get_peer_id(C->id)))) { // chat conversation is not existing, create it conv = serv_got_joined_chat(conn->gc, tgl_get_peer_id(C->id), C->title); } purple_conv_chat_clear_users(purple_conversation_get_chat_data(conv)); chat_add_all_users(conv, C); struct message_text *mt = 0; while ((mt = g_queue_pop_head (conn->new_messages))) { if (!chat_add_message(TLS, mt->M, mt->text)) { warning ("WARNING, chat %d still not existing... \n", tgl_get_peer_id (C->id)); break; } if (mt->text) { g_free (mt->text); } free (mt); } gchar *name = g_strdup_printf ("%d", tgl_get_peer_id(C->id)); g_hash_table_remove (conn->joining_chats, name); g_free (name); }
void tgprpl_chat_join (PurpleConnection *gc, GHashTable *data) { debug ("tgprpl_chat_join()"); g_return_if_fail(data); // auto joins will cause chat joins at a time when the dialogue list is not ready, remember // those chats and join them once the dialogue list is fetched if (! gc_get_data (gc)->dialogues_ready) { g_return_if_fail (data); const char *id = g_hash_table_lookup (data, "id"); if (id) { debug ("attempting to join chat %s while not ready, queue up for later", id); gc_get_data (gc)->pending_joins = g_list_append (gc_get_data (gc)->pending_joins, g_strdup (id)); } return; } // join existing chat by id when the user clicks on a chat in the buddy list void *value = g_hash_table_lookup (data, "id"); if (value && atoi (value)) { tgl_peer_id_t cid = TGL_MK_CHAT(atoi (value)); tgl_peer_t *P = tgl_peer_get (gc_get_tls (gc), cid); if (P) { debug ("joining chat by id %d ...", tgl_get_peer_id (cid)); tgl_do_get_chat_info (gc_get_tls (gc), cid, FALSE, tgp_chat_on_loaded_chat_full_joining, NULL); } else { warning ("Cannot join chat %d, peer not found...", tgl_get_peer_id (cid)); purple_serv_got_join_chat_failed (gc, data); } return; } // join chat by invite link provided in the chat join window const char *link = g_hash_table_lookup (data, "link"); if (str_not_empty (link)) { tgl_do_import_chat_link (gc_get_tls (gc), link, (int)strlen (link), tgp_notify_on_error_gw, NULL); return; } // if a chat with this name doesn't exist yet, prompt to create one const char *subject = g_hash_table_lookup (data, "subject"); if (str_not_empty (subject)) { tgl_peer_t *P = tgl_peer_get_by_name (gc_get_tls (gc), subject); // handle joining chats by print_names as used by the Adium plugin if (P && tgl_get_peer_type (P->id) == TGL_PEER_CHAT) { debug ("joining chat by subject %s ...", subject); tgl_do_get_chat_info (gc_get_tls (gc), P->id, FALSE, tgp_chat_on_loaded_chat_full_joining, NULL); return; } // user creates a new chat by providing its subject the chat join window request_create_chat (gc_get_tls (gc), subject); } }
PurpleConversation *tgp_chat_show (struct tgl_state *TLS, struct tgl_chat *C) { connection_data *conn = TLS->ev_base; PurpleConversation *convo = purple_find_chat (conn->gc, tgl_get_peer_id (C->id)); PurpleConvChat *chat = purple_conversation_get_chat_data (convo); if (! convo || (chat && purple_conv_chat_has_left (chat))) { convo = serv_got_joined_chat (conn->gc, tgl_get_peer_id (C->id), C->print_title); tgp_chat_users_update (conn->TLS, C); } return convo; }
void tgp_msg_recv (struct tgl_state *TLS, struct tgl_message *M) { connection_data *conn = TLS->ev_base; struct tgp_msg_loading *C = tgp_msg_loading_init (TRUE, M); if (M->date != 0 && M->date < tgp_msg_oldest_relevant_ts (TLS)) { debug ("Message from %d on %d too old, ignored.", tgl_get_peer_id (M->from_id), M->date); return; } if (M->media.type == tgl_message_media_photo) { C->done = FALSE; tgl_do_load_photo (TLS, M->media.photo, tgp_msg_on_loaded_document, C); } if (M->media.type == tgl_message_media_document_encr && M->media.encr_document->flags & TGLDF_IMAGE && !(M->media.encr_document->flags & TGLDF_STICKER)) { C->done = FALSE; tgl_do_load_encr_document (TLS, M->media.encr_document, tgp_msg_on_loaded_document, C); } #ifdef HAVE_LIBWEBP if (M->media.type == tgl_message_media_document && M->media.document->flags & TGLDF_STICKER) { C->done = FALSE; tgl_do_load_document (TLS, M->media.document, tgp_msg_on_loaded_document, C); } #endif if (M->media.type == tgl_message_media_geo) { // TODO: load geo thumbnail } g_queue_push_tail (conn->new_messages, C); tgp_msg_process_in_ready (TLS); }
void write_secret_chat (tgl_peer_t *Peer, void *extra) { struct tgl_secret_chat *P = (void *)Peer; 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 p2tgl_chat_update (PurpleChat *chat, tgl_peer_id_t id, int admin_id, const char *subject) { GHashTable *ht = purple_chat_get_components (chat); g_hash_table_replace (ht, g_strdup ("id"), g_strdup_printf ("%d", tgl_get_peer_id (id))); g_hash_table_replace (ht, g_strdup ("owner"), g_strdup_printf ("%d", admin_id)); g_hash_table_replace (ht, g_strdup ("subject"), g_strdup (subject)); }
void tgp_chat_users_update (struct tgl_state *TLS, struct tgl_chat *C) { PurpleConversation *pc = purple_find_chat (tg_get_conn (TLS), tgl_get_peer_id (C->id)); if (pc) { purple_conv_chat_clear_users (purple_conversation_get_chat_data (pc)); tgp_chat_add_all_users (TLS, pc, C); } }
static void on_update_chat_participants (struct tgl_state *TLS, struct tgl_chat *chat) { PurpleConversation *pc = purple_find_chat(tg_get_conn(TLS), tgl_get_peer_id(chat->id)); if (pc) { purple_conv_chat_clear_users (purple_conversation_get_chat_data(pc)); chat_add_all_users (pc, chat); } }
static void update_user_handler (struct tgl_state *TLS, struct tgl_user *user, unsigned flags) { // if (!(flags & TGL_UPDATE_CREATED)) { return; } if (TLS->our_id == tgl_get_peer_id (user->id)) { if (flags & TGL_UPDATE_NAME) { p2tgl_connection_set_display_name (TLS, (tgl_peer_t *)user); } } else { PurpleBuddy *buddy = p2tgl_buddy_find (TLS, user->id); if (!buddy) { buddy = p2tgl_buddy_new (TLS, (tgl_peer_t *)user); purple_blist_add_buddy (buddy, NULL, tggroup, NULL); } if (flags & TGL_UPDATE_CREATED) { purple_buddy_set_protocol_data (buddy, (gpointer)user); p2tgl_prpl_got_user_status (TLS, user->id, &user->status); p2tgl_buddy_update (TLS, (tgl_peer_t *)user, flags); } if (flags & (TGL_UPDATE_NAME | TGL_UPDATE_REAL_NAME | TGL_UPDATE_USERNAME) && buddy) { p2tgl_blist_alias_buddy (buddy, user); } if (flags & TGL_UPDATE_PHOTO) { tgl_do_get_user_info (TLS, user->id, 0, on_user_get_info, 0); } if (flags & TGL_UPDATE_DELETED && buddy) { purple_blist_remove_buddy (buddy); } } }
static void on_userpic_loaded (struct tgl_state *TLS, void *extra, int success, char *filename) { if (!success) { struct download_desc *dld = extra; struct tgl_user *U = dld->data; warning ("Can not load userpic for user %s %s\n", U->first_name, U->last_name); } telegram_conn *conn = TLS->ev_base; gchar *data = NULL; size_t len; GError *err = NULL; g_file_get_contents (filename, &data, &len, &err); int imgStoreId = purple_imgstore_add_with_id (g_memdup(data, (guint)len), len, NULL); struct download_desc *dld = extra; struct tgl_user *U = dld->data; if (imgStoreId <= 0) { warning ("Can not load userpic for user %s %s\n", U->first_name, U->last_name); } char *who = g_strdup_printf ("%d", tgl_get_peer_id (U->id)); if (dld->type == 1) { 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); purple_notify_userinfo (conn->gc, who, info, NULL, NULL); g_free (profile_image); } purple_buddy_icons_set_for_user(conn->pa, who, g_memdup(data, (guint)len), len, NULL); g_free(who); }
void p2tgl_got_typing (struct tgl_state *TLS, tgl_peer_id_t user, int timeout) { char *who = g_strdup_printf("%d", tgl_get_peer_id(user)); serv_got_typing(tg_get_conn(TLS), who, timeout, PURPLE_TYPING); g_free(who); }
void p2tgl_got_chat_in (struct tgl_state *TLS, tgl_peer_id_t chat, tgl_peer_id_t who, const char *message, int flags, time_t when) { char *name = peer_strdup_id(who); serv_got_chat_in(tg_get_conn(TLS), tgl_get_peer_id (chat), name, flags, message, when); g_free (name); }
void tgp_blist_buddy_set_id (PurpleBuddy *buddy, tgl_peer_id_t id) { int uid = tgl_get_peer_id (id), type = tgl_get_peer_type (id); assert (type == TGL_PEER_ENCR_CHAT || type == TGL_PEER_USER); purple_blist_node_set_int (&buddy->node, TGP_BUDDY_KEY_PEER_ID, uid); purple_blist_node_set_int (&buddy->node, TGP_BUDDY_KEY_PEER_TYPE, type); }
json_t *json_pack_peer (tgl_peer_id_t id, tgl_peer_t *P) { json_t *res = json_object (); assert (json_object_set (res, "id", json_integer (tgl_get_peer_id (id))) >= 0); json_pack_peer_type (res, id); assert (res); if (!P || !(P->flags & TGLPF_CREATED)) { static char s[100]; switch (tgl_get_peer_type (id)) { case TGL_PEER_USER: sprintf (s, "user#%d", tgl_get_peer_id (id)); break; case TGL_PEER_CHAT: sprintf (s, "chat#%d", tgl_get_peer_id (id)); break; case TGL_PEER_ENCR_CHAT: sprintf (s, "encr_chat#%d", tgl_get_peer_id (id)); break; default: assert (0); } assert (json_object_set (res, "print_name", json_string (s)) >= 0); return res; } assert (json_object_set (res, "print_name", json_string (P->print_name)) >= 0); assert (json_object_set (res, "flags", json_integer (P->flags)) >= 0); switch (tgl_get_peer_type (id)) { case TGL_PEER_USER: json_pack_user (res, P); break; case TGL_PEER_CHAT: json_pack_chat (res, P); break; case TGL_PEER_ENCR_CHAT: json_pack_encr_chat (res, P); break; default: assert (0); } return res; }
PurpleConversation *p2tgl_got_joined_chat (struct tgl_state *TLS, struct tgl_chat *chat) { telegram_conn *conn = TLS->ev_base; gchar *alias = p2tgl_strdup_alias((tgl_peer_t *)chat); PurpleConversation *conv = serv_got_joined_chat(conn->gc, tgl_get_peer_id(chat->id), alias); g_free(alias); return conv; }
void tgl_do_messages_mark_read_encr (struct tgl_state *TLS, tgl_peer_id_t id, long long access_hash, int last_time, void (*callback)(struct tgl_state *TLS, void *callback_extra, int), void *callback_extra) { clear_packet (); out_int (CODE_messages_read_encrypted_history); out_int (CODE_input_encrypted_chat); out_int (tgl_get_peer_id (id)); out_long (access_hash); out_int (last_time); tglq_send_query (TLS, TLS->DC_working, packet_ptr - packet_buffer, packet_buffer, &mark_read_encr_methods, tgl_peer_get (TLS, id), callback, callback_extra); }
void pending_reads_add (struct tgl_state *TLS, struct tgl_message *M) { tgl_peer_id_t *copy = g_new (tgl_peer_id_t, 1); if (tgl_get_peer_type (M->to_id) == TGL_PEER_USER) { *copy = M->from_id; } else { *copy = M->to_id; } g_hash_table_replace (tls_get_data (TLS)->pending_reads, GINT_TO_POINTER (tgl_get_peer_id (*copy)), copy); }
GHashTable *tgp_chat_info_new (struct tgl_state *TLS, struct tgl_chat *chat) { gchar *title = g_strdup (chat->print_title); GHashTable *ht = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); g_hash_table_insert (ht, "subject", title); g_hash_table_insert (ht, "id", g_strdup_printf ("%d", tgl_get_peer_id (chat->id))); return ht; }
void tgprpl_chat_join (PurpleConnection * gc, GHashTable *data) { debug ("tgprpl_chat_join()"); connection_data *conn = purple_connection_get_protocol_data (gc); // join existing chat by id when the user clicks on a chat in the buddy list void *value = g_hash_table_lookup (data, "id"); if (value && atoi (value)) { tgl_peer_id_t cid = TGL_MK_CHAT(atoi (value)); tgl_peer_t *P = tgl_peer_get (conn->TLS, cid); if (P) { debug ("joining chat by id %d ...", tgl_get_peer_id (cid)); tgl_do_get_chat_info (conn->TLS, cid, FALSE, tgp_chat_on_loaded_chat_full_joining, NULL); } else { warning ("Cannot join chat %d, peer not found...", tgl_get_peer_id (cid)); purple_serv_got_join_chat_failed (gc, data); } return; } // join chat by invite link provided in the chat join window const char *link = g_hash_table_lookup (data, "link"); if (str_not_empty (link)) { tgl_do_import_chat_link (conn->TLS, link, (int)strlen (link), tgp_notify_on_error_gw, NULL); return; } // if a chat with this name doesn't exist yet, prompt to create one const char *subject = g_hash_table_lookup (data, "subject"); if (str_not_empty (subject)) { tgl_peer_t *P = tgl_peer_get_by_name (conn->TLS, subject); // handle joining chats by print_names as used by the Adium plugin if (P && tgl_get_peer_type (P->id) == TGL_PEER_CHAT) { debug ("joining chat by subject %s ...", subject); tgl_do_get_chat_info (conn->TLS, P->id, FALSE, tgp_chat_on_loaded_chat_full_joining, NULL); return; } // user creates a new chat by providing its subject the chat join window request_create_chat (conn->TLS, subject); } }
PurpleConversation *tgp_chat_show (struct tgl_state *TLS, struct tgl_chat *C) { PurpleConvChat *chat = NULL; // check if chat is already shown PurpleConversation *conv = purple_find_chat (tls_get_conn (TLS), tgl_get_peer_id (C->id)); if (conv) { chat = purple_conversation_get_chat_data (conv); if (chat && ! purple_conv_chat_has_left (chat)) { return conv; } } // join the chat now conv = serv_got_joined_chat (tls_get_conn (TLS), tgl_get_peer_id (C->id), C->print_title); g_return_val_if_fail(conv, NULL); purple_conv_chat_clear_users (purple_conversation_get_chat_data (conv)); tgp_chat_add_all_users (TLS, conv, C); return conv; }
PurpleBuddy *p2tgl_buddy_update (struct tgl_state *TLS, tgl_peer_t *user, unsigned flags) { PurpleBuddy *b = p2tgl_buddy_find (TLS, user->id); if (!b) { b = p2tgl_buddy_new (TLS, user); } if (flags & TGL_UPDATE_NAME) { debug ("Update username for id%d (name %s %s)\n", tgl_get_peer_id (user->id), user->user.first_name, user->user.last_name); char *alias = p2tgl_strdup_alias (user); purple_blist_alias_buddy(b, alias); g_free (alias); } return b; }
void tgl_do_send_encr_action (struct tgl_state *TLS, struct tgl_secret_chat *E, struct tl_ds_decrypted_message_action *A) { long long t; tglt_secure_random (&t, 8); int peer_id = tgl_get_peer_id (E->id); int peer_type = TGL_PEER_ENCR_CHAT; int date = time (0); bl_do_create_message_encr_new (TLS, t, &TLS->our_id, &peer_type, &peer_id, &date, NULL, 0, NULL, A, NULL, TGLMF_PENDING | TGLMF_OUT | TGLMF_UNREAD | TGLMF_CREATE | TGLMF_CREATED | TGLMF_ENCRYPTED); struct tgl_message *M = tgl_message_get (TLS, t); assert (M); tgl_do_send_msg (TLS, M, 0, 0); }
static void tgp_chat_roomlist_it (tgl_peer_t *P, void *extra) { connection_data *conn = extra; if (tgl_get_peer_type (P->id) == TGL_PEER_CHAT && P->chat.users_num) { char *id = g_strdup_printf ("%d", tgl_get_peer_id (P->id)); PurpleRoomlistRoom *room = purple_roomlist_room_new (PURPLE_ROOMLIST_ROOMTYPE_ROOM, P->chat.print_title, NULL); purple_roomlist_room_add_field (conn->roomlist, room, id); purple_roomlist_room_add_field (conn->roomlist, room, GINT_TO_POINTER(P->chat.users_num)); purple_roomlist_room_add (conn->roomlist, room); g_free (id); } }
void tgl_do_send_encr_msg (struct tgl_state *TLS, struct tgl_message *M, void (*callback)(struct tgl_state *TLS, void *callback_extra, int success, struct tgl_message *M), void *callback_extra) { if (M->flags & TGLMF_SERVICE) { tgl_do_send_encr_msg_action (TLS, M, callback, callback_extra); return; } tgl_peer_t *P = tgl_peer_get (TLS, M->to_id); if (!P || P->encr_chat.state != sc_ok) { vlogprintf (E_WARNING, "Unknown encrypted chat\n"); if (callback) { callback (TLS, callback_extra, 0, M); } return; } assert (M->flags & TGLMF_ENCRYPTED); clear_packet (); out_int (CODE_messages_send_encrypted); out_int (CODE_input_encrypted_chat); out_int (tgl_get_peer_id (M->to_id)); out_long (P->encr_chat.access_hash); out_long (M->id); encr_start (); out_int (CODE_decrypted_message_layer); out_random (15 + 4 * (lrand48 () % 3)); out_int (TGL_ENCRYPTED_LAYER); out_int (2 * P->encr_chat.in_seq_no + (P->encr_chat.admin_id != TLS->our_id)); out_int (2 * P->encr_chat.out_seq_no + (P->encr_chat.admin_id == TLS->our_id) - 2); out_int (CODE_decrypted_message); out_long (M->id); out_int (P->encr_chat.ttl); out_cstring ((void *)M->message, M->message_len); switch (M->media.type) { case tgl_message_media_none: out_int (CODE_decrypted_message_media_empty); break; case tgl_message_media_geo: out_int (CODE_decrypted_message_media_geo_point); out_double (M->media.geo.latitude); out_double (M->media.geo.longitude); break; default: assert (0); } encr_finish (&P->encr_chat); tglq_send_query (TLS, TLS->DC_working, packet_ptr - packet_buffer, packet_buffer, &msg_send_encr_methods, M, callback, callback_extra); }
PurpleChat *tgp_blist_chat_find (struct tgl_state *TLS, tgl_peer_id_t user) { PurpleBlistNode *node = purple_blist_get_root (); while (node) { if (PURPLE_BLIST_NODE_IS_CHAT(node)) { PurpleChat *chat = PURPLE_CHAT(node); if (purple_chat_get_account (chat) == tg_get_acc (TLS)) { const char *id = g_hash_table_lookup (purple_chat_get_components (chat), "id"); if (id && *id && atoi (id) == tgl_get_peer_id (user)) { return chat; } } } node = purple_blist_node_next (node, FALSE); } return NULL; }
PurpleBuddy *tgp_blist_buddy_find (struct tgl_state *TLS, tgl_peer_id_t user) { PurpleBlistNode *node = purple_blist_get_root (); while (node) { if (PURPLE_BLIST_NODE_IS_BUDDY(node)) { PurpleBuddy *buddy = PURPLE_BUDDY(node); if (purple_buddy_get_account (buddy) == tg_get_acc (TLS)) { if (purple_blist_node_get_int (node, TGP_BUDDY_KEY_PEER_ID) == tgl_get_peer_id (user)) { assert (tgl_get_peer_type (user) == purple_blist_node_get_int (node, TGP_BUDDY_KEY_PEER_TYPE)); return buddy; } } } node = purple_blist_node_next (node, FALSE); } return NULL; }
static int user_get_alias (tgl_peer_t *user, char *buffer, int maxlen) { char* last_name = (user->user.last_name && strlen(user->user.last_name)) ? user->user.last_name : ""; char* first_name = (user->user.first_name && strlen(user->user.first_name)) ? user->user.first_name : ""; last_name = strdup (last_name); first_name = strdup (first_name); sanitize_alias (last_name); sanitize_alias (first_name); int res; if (strlen (first_name) && strlen(last_name)) { res = snprintf (buffer, maxlen, "%s %s", first_name, last_name); } else if (strlen (first_name)) { res = snprintf (buffer, maxlen, "%s", first_name); } else if (strlen (last_name)) { res = snprintf (buffer, maxlen, "%s", last_name); } else { res = snprintf (buffer, maxlen, "%d", tgl_get_peer_id (user->id)); } free (last_name); free (first_name); return res; }
void tgp_msg_recv (struct tgl_state *TLS, struct tgl_message *M) { connection_data *conn = TLS->ev_base; struct tgp_msg_loading *C = tgp_msg_loading_init (TRUE, M); if (M->date != 0 && M->date < tgp_msg_oldest_relevant_ts (TLS)) { debug ("Message from %d on %d too old, ignored.", tgl_get_peer_id (M->from_id), M->date); return; } if (M->media.type == tgl_message_media_photo) { C->done = FALSE; tgl_do_load_photo (TLS, &M->media.photo, tgp_msg_on_loaded_photo, C); } if (M->media.type == tgl_message_media_geo) { // TODO: load geo thumbnail } g_queue_push_tail (conn->new_messages, C); tgp_msg_process_ready (TLS); }
void tgl_do_send_location_encr (struct tgl_state *TLS, tgl_peer_id_t id, double latitude, double longitude, unsigned long long flags, void (*callback)(struct tgl_state *TLS, void *callback_extra, int success, struct tgl_message *M), void *callback_extra) { struct tl_ds_decrypted_message_media TDSM; TDSM.magic = CODE_decrypted_message_media_geo_point; TDSM.latitude = talloc (sizeof (double)); *TDSM.latitude = latitude; TDSM.longitude = talloc (sizeof (double)); *TDSM.longitude = longitude; int peer_type = tgl_get_peer_type (id); int peer_id = tgl_get_peer_id (id); int date = time (0); long long t; tglt_secure_random (&t, 8); bl_do_create_message_encr_new (TLS, t, &TLS->our_id, &peer_type, &peer_id, &date, NULL, 0, &TDSM, NULL, NULL, TGLMF_UNREAD | TGLMF_OUT | TGLMF_PENDING | TGLMF_CREATE | TGLMF_CREATED | TGLMF_ENCRYPTED); tfree (TDSM.latitude, sizeof (double)); tfree (TDSM.longitude, sizeof (double)); struct tgl_message *M = tgl_message_get (TLS, t); tgl_do_send_encr_msg (TLS, M, callback, callback_extra); }
void tgp_blist_peer_add_purple_name (struct tgl_state *TLS, tgl_peer_id_t id, const char *purple_name) { g_hash_table_replace (tg_get_data (TLS)->id_to_purple_name, GINT_TO_POINTER(tgl_get_peer_id (id)), g_strdup (purple_name)); }