Ensure that the message sequence doesn't get mixed up
Process incoming messages in a queue. Move all message-related functionality into one file.
This commit is contained in:
parent
95258c940b
commit
1e442ac195
8 changed files with 470 additions and 386 deletions
|
@ -17,7 +17,7 @@ OBJ=objs
|
|||
LIB=libs
|
||||
DIR_LIST=${DEP} ${AUTO} ${EXE} ${OBJ} ${LIB} ${DEP}/auto ${OBJ}/auto ${DEP}/lodepng ${OBJ}/lodepng
|
||||
|
||||
PLUGIN_OBJECTS=${OBJ}/tgp-net.o ${OBJ}/tgp-timers.o ${OBJ}/msglog.o ${OBJ}/telegram-base.o ${OBJ}/telegram-purple.o ${OBJ}/tgp-2prpl.o ${OBJ}/tgp-structs.o ${OBJ}/tgp-utils.o ${OBJ}/tgp-chat.o ${OBJ}/tgp-ft.o ${OBJ}/lodepng/lodepng.o
|
||||
PLUGIN_OBJECTS=${OBJ}/tgp-net.o ${OBJ}/tgp-timers.o ${OBJ}/msglog.o ${OBJ}/telegram-base.o ${OBJ}/telegram-purple.o ${OBJ}/tgp-2prpl.o ${OBJ}/tgp-structs.o ${OBJ}/tgp-utils.o ${OBJ}/tgp-chat.o ${OBJ}/tgp-ft.o ${OBJ}/tgp-msg.o ${OBJ}/lodepng/lodepng.o
|
||||
ALL_OBJS=${PLUGIN_OBJECTS}
|
||||
|
||||
.SUFFIXES:
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
C438CE351A12C07800E1DA0F /* tgp-net.c in Sources */ = {isa = PBXBuildFile; fileRef = C438CE301A12C07800E1DA0F /* tgp-net.c */; };
|
||||
C438CE361A12C07800E1DA0F /* tgp-timers.c in Sources */ = {isa = PBXBuildFile; fileRef = C438CE311A12C07800E1DA0F /* tgp-timers.c */; };
|
||||
C438CE3D1A12C15100E1DA0F /* tg-server.pub in Resources */ = {isa = PBXBuildFile; fileRef = C438CE3C1A12C15100E1DA0F /* tg-server.pub */; };
|
||||
C448ADA71AB0789A001B7ECD /* tgp-msg.c in Sources */ = {isa = PBXBuildFile; fileRef = C448ADA61AB0789A001B7ECD /* tgp-msg.c */; };
|
||||
C466937819E703370036A108 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C466937719E703370036A108 /* AppKit.framework */; };
|
||||
C4877C1819BB37EA006FA91F /* TelegramService.m in Sources */ = {isa = PBXBuildFile; fileRef = C4877C1719BB37EA006FA91F /* TelegramService.m */; };
|
||||
C4877C1E19BB676B006FA91F /* AdiumTelegramAccount.m in Sources */ = {isa = PBXBuildFile; fileRef = C4877C1D19BB676B006FA91F /* AdiumTelegramAccount.m */; };
|
||||
|
@ -70,6 +71,8 @@
|
|||
C438CE3A1A12C0C900E1DA0F /* tgp-net.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "tgp-net.h"; path = "../tgp-net.h"; sourceTree = "<group>"; };
|
||||
C438CE3B1A12C0C900E1DA0F /* tgp-timers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "tgp-timers.h"; path = "../tgp-timers.h"; sourceTree = "<group>"; };
|
||||
C438CE3C1A12C15100E1DA0F /* tg-server.pub */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "tg-server.pub"; path = "../tg-server.pub"; sourceTree = "<group>"; };
|
||||
C448ADA61AB0789A001B7ECD /* tgp-msg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "tgp-msg.c"; path = "../tgp-msg.c"; sourceTree = "<group>"; };
|
||||
C448ADA81AB078BB001B7ECD /* tgp-msg.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "tgp-msg.h"; path = "../tgp-msg.h"; sourceTree = "<group>"; };
|
||||
C466937719E703370036A108 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk/System/Library/Frameworks/AppKit.framework; sourceTree = DEVELOPER_DIR; };
|
||||
C4877C1619BB37EA006FA91F /* TelegramService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TelegramService.h; sourceTree = "<group>"; };
|
||||
C4877C1719BB37EA006FA91F /* TelegramService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TelegramService.m; sourceTree = "<group>"; };
|
||||
|
@ -200,6 +203,8 @@
|
|||
C438CE2D1A12C07800E1DA0F /* msglog.c */,
|
||||
C438CE2E1A12C07800E1DA0F /* telegram-base.c */,
|
||||
C438CE2F1A12C07800E1DA0F /* telegram-purple.c */,
|
||||
C448ADA61AB0789A001B7ECD /* tgp-msg.c */,
|
||||
C448ADA81AB078BB001B7ECD /* tgp-msg.h */,
|
||||
C438CE301A12C07800E1DA0F /* tgp-net.c */,
|
||||
C438CE311A12C07800E1DA0F /* tgp-timers.c */,
|
||||
C41D583F1A16D86A00B22448 /* tgp-2prpl.h */,
|
||||
|
@ -294,6 +299,7 @@
|
|||
C438CE331A12C07800E1DA0F /* telegram-base.c in Sources */,
|
||||
C438CE341A12C07800E1DA0F /* telegram-purple.c in Sources */,
|
||||
C4877C1819BB37EA006FA91F /* TelegramService.m in Sources */,
|
||||
C448ADA71AB0789A001B7ECD /* tgp-msg.c in Sources */,
|
||||
C4E528111A8A907200C4B915 /* tgp-ft.c in Sources */,
|
||||
C438CE361A12C07800E1DA0F /* tgp-timers.c in Sources */,
|
||||
C410949B19BB337A0083BF3F /* TelegramPlugin.m in Sources */,
|
||||
|
|
|
@ -69,6 +69,7 @@
|
|||
#include "tgp-utils.h"
|
||||
#include "tgp-chat.h"
|
||||
#include "tgp-ft.h"
|
||||
#include "tgp-msg.h"
|
||||
|
||||
#define _(m) m
|
||||
|
||||
|
@ -83,92 +84,6 @@ static char *format_status (struct tgl_user_status *status) {
|
|||
return status->online ? "Online" : "Mobile";
|
||||
}
|
||||
|
||||
static char *format_service_msg (struct tgl_state *TLS, struct tgl_message *M) {
|
||||
assert (M && M->service);
|
||||
|
||||
char *txt_user = NULL;
|
||||
char *txt_action = NULL;
|
||||
char *txt = NULL;
|
||||
|
||||
tgl_peer_t *peer = tgl_peer_get (TLS, M->from_id);
|
||||
if (! peer) {
|
||||
return NULL;
|
||||
}
|
||||
txt_user = p2tgl_strdup_alias (peer);
|
||||
|
||||
switch (M->action.type) {
|
||||
case tgl_message_action_chat_create:
|
||||
txt_action = g_strdup_printf ("created chat %s", M->action.title);
|
||||
break;
|
||||
case tgl_message_action_chat_edit_title:
|
||||
txt_action = g_strdup_printf ("changed title to %s", M->action.new_title);
|
||||
break;
|
||||
case tgl_message_action_chat_edit_photo:
|
||||
txt_action = g_strdup ("changed photo");
|
||||
break;
|
||||
case tgl_message_action_chat_delete_photo:
|
||||
txt_action = g_strdup ("deleted photo");
|
||||
break;
|
||||
case tgl_message_action_chat_add_user:
|
||||
{
|
||||
tgl_peer_t *peer = tgl_peer_get (TLS, TGL_MK_USER (M->action.user));
|
||||
if (peer) {
|
||||
char *alias = p2tgl_strdup_alias (peer);
|
||||
txt_action = g_strdup_printf ("added user %s", alias);
|
||||
g_free (alias);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case tgl_message_action_chat_delete_user:
|
||||
{
|
||||
tgl_peer_t *peer = tgl_peer_get (TLS, TGL_MK_USER (M->action.user));
|
||||
if (peer) {
|
||||
char *alias = p2tgl_strdup_alias (peer);
|
||||
txt_action = g_strdup_printf ("deleted user %s", alias);
|
||||
g_free (alias);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case tgl_message_action_set_message_ttl:
|
||||
txt_action = g_strdup_printf ("set ttl to %d seconds", M->action.ttl);
|
||||
break;
|
||||
case tgl_message_action_read_messages:
|
||||
txt_action = g_strdup_printf ("%d messages marked read", M->action.read_cnt);
|
||||
break;
|
||||
case tgl_message_action_delete_messages:
|
||||
txt_action = g_strdup_printf ("%d messages deleted", M->action.delete_cnt);
|
||||
break;
|
||||
case tgl_message_action_screenshot_messages:
|
||||
txt_action = g_strdup_printf ("%d messages screenshoted", M->action.screenshot_cnt);
|
||||
break;
|
||||
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", M->action.exchange_id);
|
||||
break;
|
||||
case tgl_message_action_accept_key:
|
||||
txt_action = g_strdup_printf ("Accept rekey #%016llx", M->action.exchange_id);
|
||||
break;
|
||||
case tgl_message_action_commit_key:
|
||||
txt_action = g_strdup_printf ("Commit rekey #%016llx", M->action.exchange_id);
|
||||
break;
|
||||
case tgl_message_action_abort_key:
|
||||
txt_action = g_strdup_printf ("Abort rekey #%016llx", M->action.exchange_id);
|
||||
break;
|
||||
default:
|
||||
txt_action = NULL;
|
||||
break;
|
||||
}
|
||||
if (txt_action) {
|
||||
debug ("SERVICE MESSAGE: %s", txt_action);
|
||||
txt = g_strdup_printf ("%s %s.", txt_user, txt_action);
|
||||
g_free (txt_action);
|
||||
}
|
||||
g_free (txt_user);
|
||||
return txt;
|
||||
}
|
||||
|
||||
char *format_user_status (struct tgl_user_status *status) {
|
||||
char *when;
|
||||
switch (status->online) {
|
||||
|
@ -211,7 +126,7 @@ static char *format_print_name (struct tgl_state *TLS, tgl_peer_id_t id, const c
|
|||
s++;
|
||||
}
|
||||
s = buf;
|
||||
int fl = strlen (s);
|
||||
int fl = (int)strlen (s);
|
||||
int cc = 0;
|
||||
while (1) {
|
||||
tgl_peer_t *P = tgl_peer_get_by_name (TLS, s);
|
||||
|
@ -225,137 +140,6 @@ static char *format_print_name (struct tgl_state *TLS, tgl_peer_id_t id, const c
|
|||
return tgl_strdup (s);
|
||||
}
|
||||
|
||||
static char *format_document_desc (char *type, char *caption, gint64 size) {
|
||||
char *s = tgp_g_format_size (size);
|
||||
char *msg = g_strdup_printf ("[%s] %s %s", type, caption, s);
|
||||
g_free (s);
|
||||
return msg;
|
||||
}
|
||||
|
||||
static char *format_message (struct tgl_message *M) {
|
||||
|
||||
switch (M->media.type) {
|
||||
case tgl_message_media_document:
|
||||
return format_document_desc("DOCUMENT", M->media.document.caption, M->media.document.size);
|
||||
break;
|
||||
case tgl_message_media_document_encr:
|
||||
return format_document_desc("DOCUMENT", M->media.encr_document.caption, M->media.encr_document.size);
|
||||
break;
|
||||
case tgl_message_media_photo_encr:
|
||||
return format_document_desc("PHOTO", "", M->media.encr_photo.size);
|
||||
break;
|
||||
case tgl_message_media_contact:
|
||||
return g_strdup ("[CONTACT]");
|
||||
break;
|
||||
default:
|
||||
if (M->message && *M->message != 0) {
|
||||
return purple_markup_escape_text (M->message, strlen (M->message));
|
||||
}
|
||||
return g_strdup("");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void picture_message_done (struct tgl_state *TLS, void *callback_extra, int success, struct tgl_message *M) {
|
||||
if (success) {
|
||||
debug ("send photo sucess!");
|
||||
} else {
|
||||
debug ("send photo fail!");
|
||||
}
|
||||
}
|
||||
|
||||
static int tgp_do_send_split(struct tgl_state *TLS, const char *message, tgl_peer_id_t to)
|
||||
{
|
||||
connection_data *data = TLS->ev_base;
|
||||
int max = purple_account_get_int(data->pa, "max-msg-split-count",
|
||||
TGP_DEFAULT_HISTORY_RETRIEVAL_THRESHOLD);
|
||||
if (max < 1) {
|
||||
max = 1;
|
||||
}
|
||||
|
||||
int size = (int)g_utf8_strlen(message, -1);
|
||||
if (size > TGP_MAX_MSG_SIZE * max) {
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
int start = 0;
|
||||
while (size > start) {
|
||||
int e = start + (int)TGP_MAX_MSG_SIZE;
|
||||
|
||||
gchar *chunk = g_utf8_substring (message, start, e);
|
||||
tgl_do_send_message (TLS, to, chunk, (int)strlen (chunk), NULL, NULL);
|
||||
g_free (chunk);
|
||||
|
||||
start = e;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int tgl_do_send_unescape_message (struct tgl_state *TLS, const char *message, tgl_peer_id_t to) {
|
||||
|
||||
// search for outgoing embedded image tags and send them
|
||||
gchar *img = NULL;
|
||||
gchar *stripped = NULL;
|
||||
if ((img = g_strrstr (message, "<IMG")) || (img = g_strrstr (message, "<img"))) {
|
||||
debug ("img found: %s", img);
|
||||
|
||||
gchar *id;
|
||||
if ((id = g_strrstr (img, "ID=\"")) || (id = g_strrstr (img, "id=\""))) {
|
||||
id += 4;
|
||||
debug ("id found: %s", id);
|
||||
int imgid = atoi (id);
|
||||
|
||||
if (imgid > 0) {
|
||||
PurpleStoredImage *psi = purple_imgstore_find_by_id (imgid);
|
||||
|
||||
gchar *tmp = g_build_filename(g_get_tmp_dir(), purple_imgstore_get_filename (psi), NULL) ;
|
||||
|
||||
GError *err = NULL;
|
||||
gconstpointer data = purple_imgstore_get_data (psi);
|
||||
g_file_set_contents (tmp, data, purple_imgstore_get_size (psi), &err);
|
||||
if (! err) {
|
||||
tgl_do_send_document (TLS, -2, to, tmp, picture_message_done, NULL);
|
||||
} else {
|
||||
failure ("Cannot store image, temp directory not available: %s\n", err->message);
|
||||
g_error_free (err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// send remaining text as additional plaintext message
|
||||
stripped = purple_markup_strip_html (message);
|
||||
int ret = tgp_do_send_split (TLS, stripped, to);
|
||||
g_free (stripped);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef __ADIUM_
|
||||
/*
|
||||
Adium won't escape any HTML markup and just pass it through,
|
||||
while Pidgin will replace special chars with the escape chars.
|
||||
Also, Pidgin will add additional markup for RTL languages and such,
|
||||
which Adium doesn't do either.
|
||||
|
||||
TODO: This is ridiculous, there has to a better way to handle this across platforms.
|
||||
*/
|
||||
|
||||
/* first, we remove any HTML markup added by Pidgin, since Telegram won't handle it properly.
|
||||
User-entered HTML is still escaped and therefore won't be harmed. */
|
||||
stripped = purple_markup_strip_html (message);
|
||||
|
||||
/* now unescape the markup, so that html special chars will still show
|
||||
up properly in Telegram */
|
||||
gchar *unescaped = purple_unescape_text (stripped);
|
||||
int ret = tgp_do_send_split (TLS, stripped, to);
|
||||
|
||||
g_free (unescaped);
|
||||
g_free (stripped);
|
||||
return ret;
|
||||
#endif
|
||||
|
||||
return tgp_do_send_split (TLS, message, to);
|
||||
}
|
||||
|
||||
static void start_secret_chat (PurpleBlistNode *node, gpointer data) {
|
||||
PurpleBuddy *buddy = data;
|
||||
|
||||
|
@ -380,127 +164,9 @@ struct tgl_update_callback tgp_callback = {
|
|||
.create_print_name = format_print_name
|
||||
};
|
||||
|
||||
void on_message_load_photo (struct tgl_state *TLS, void *extra, int success, char *filename) {
|
||||
|
||||
connection_data *conn = TLS->ev_base;
|
||||
|
||||
int imgStoreId = p2tgl_imgstore_add_with_id (filename);
|
||||
if (imgStoreId > 0) {
|
||||
used_images_add (conn, imgStoreId);
|
||||
char *image = format_img_full (imgStoreId);
|
||||
|
||||
struct tgl_message *M = extra;
|
||||
switch (tgl_get_peer_type (M->to_id)) {
|
||||
case TGL_PEER_CHAT:
|
||||
if (!our_msg (TLS, M)) {
|
||||
chat_add_message (TLS, M, image);
|
||||
}
|
||||
break;
|
||||
|
||||
case TGL_PEER_USER:
|
||||
if (out_msg (TLS, M)) {
|
||||
p2tgl_got_im_combo (TLS, M->to_id, image, PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_IMAGES, M->date);
|
||||
} else {
|
||||
p2tgl_got_im (TLS, M->from_id, image, PURPLE_MESSAGE_RECV | PURPLE_MESSAGE_IMAGES, M->date);
|
||||
}
|
||||
break;
|
||||
}
|
||||
g_free (image);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void update_message_received (struct tgl_state *TLS, struct tgl_message *M) {
|
||||
connection_data *conn = TLS->ev_base;
|
||||
|
||||
/* don't write to this file on every single message, which
|
||||
can be pretty overkill on calls to tgl_do_get_difference that result in many
|
||||
updates */
|
||||
write_state_file_schedule (conn->TLS);
|
||||
|
||||
if (M->service) {
|
||||
char *text = format_service_msg (TLS, M);
|
||||
if (text) {
|
||||
switch (tgl_get_peer_type (M->to_id)) {
|
||||
case TGL_PEER_CHAT:
|
||||
chat_add_message (TLS, M, text);
|
||||
break;
|
||||
|
||||
case TGL_PEER_USER:
|
||||
p2tgl_got_im (TLS, M->from_id, text, PURPLE_MESSAGE_SYSTEM, M->date);
|
||||
break;
|
||||
}
|
||||
g_free (text);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
// ignore all empty or deleted messages
|
||||
(M->flags & (FLAG_MESSAGE_EMPTY | FLAG_DELETED)) ||
|
||||
|
||||
// ignore all messages that are not created for the first time
|
||||
!(M->flags & FLAG_CREATED) ||
|
||||
|
||||
// drop all messages that don't contain any data
|
||||
!M->message ||
|
||||
|
||||
// drop messages that were created by *this* client and were already
|
||||
// added to the conversation
|
||||
our_msg (TLS, M) ||
|
||||
|
||||
// drop all messages with an invalid peer id.
|
||||
// TODO: Verify if we actually need this
|
||||
!tgl_get_peer_type (M->to_id)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (M->media.type == tgl_message_media_photo) {
|
||||
tgl_do_load_photo (TLS, &M->media.photo, on_message_load_photo, M);
|
||||
return;
|
||||
}
|
||||
|
||||
if (M->media.type == tgl_message_media_document) {
|
||||
char *who = g_strdup_printf("%d", tgl_get_peer_id(M->from_id));
|
||||
|
||||
tgprpl_recv_file (conn->gc, who, &M->media.document);
|
||||
|
||||
g_free (who);
|
||||
return;
|
||||
}
|
||||
|
||||
char *text = format_message (M);
|
||||
switch (tgl_get_peer_type (M->to_id)) {
|
||||
case TGL_PEER_CHAT:
|
||||
chat_add_message (TLS, M, text);
|
||||
break;
|
||||
|
||||
case TGL_PEER_ENCR_CHAT:
|
||||
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:
|
||||
|
||||
if (out_msg (TLS, M)) {
|
||||
p2tgl_got_im_combo (TLS, M->to_id, text, PURPLE_MESSAGE_SEND, M->date);
|
||||
} else {
|
||||
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))) {
|
||||
pending_reads_send_all (conn->pending_reads, conn->TLS);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
g_free (text);
|
||||
write_state_file_schedule (TLS);
|
||||
tgp_msg_recv (TLS, M);
|
||||
}
|
||||
|
||||
static void update_user_handler (struct tgl_state *TLS, struct tgl_user *user, unsigned flags) {
|
||||
|
@ -637,7 +303,8 @@ static void on_contact_added (struct tgl_state *TLS,void *callback_extra, int su
|
|||
|
||||
purple_blist_remove_buddy (buddy);
|
||||
if (!success || !size) {
|
||||
purple_notify_error (_telegram_protocol, "Adding Buddy Failed", "Buddy Not Found", "No contact with this phone number was found.");
|
||||
purple_notify_error (_telegram_protocol, "Adding Buddy Failed", "Buddy Not Found",
|
||||
"No contact with this phone number was found.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -850,7 +517,6 @@ static void tgprpl_login (PurpleAccount * acct) {
|
|||
static void tgprpl_close (PurpleConnection * gc) {
|
||||
debug ("tgprpl_close()");
|
||||
connection_data *conn = purple_connection_get_protocol_data (gc);
|
||||
|
||||
connection_data_free (conn);
|
||||
}
|
||||
|
||||
|
@ -878,8 +544,7 @@ static int tgprpl_send_im (PurpleConnection * gc, const char *who, const char *m
|
|||
warning ("secret chat not ready for sending messages or deleted");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return tgl_do_send_unescape_message (conn->TLS, message, peer->id);;
|
||||
return tgp_msg_send (conn->TLS, message, peer->id);
|
||||
}
|
||||
|
||||
warning ("peer not found");
|
||||
|
@ -1017,9 +682,8 @@ 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()");
|
||||
connection_data *conn = purple_connection_get_protocol_data (gc);
|
||||
int ret = tgl_do_send_unescape_message (conn->TLS, message, TGL_MK_CHAT(id));
|
||||
|
||||
p2tgl_got_chat_in(conn->TLS, TGL_MK_CHAT(id), TGL_MK_USER(conn->TLS->our_id), message,
|
||||
int ret = tgp_msg_send (conn->TLS, message, TGL_MK_CHAT(id));
|
||||
p2tgl_got_chat_in (conn->TLS, TGL_MK_CHAT(id), TGL_MK_USER(conn->TLS->our_id), message,
|
||||
PURPLE_MESSAGE_RECV, time(NULL));
|
||||
return ret;
|
||||
}
|
||||
|
|
17
tgp-chat.c
17
tgp-chat.c
|
@ -51,23 +51,8 @@ PurpleConversation *chat_show (PurpleConnection *gc, int id) {
|
|||
convo = p2tgl_got_joined_chat (conn->TLS, &P->chat);
|
||||
chat_users_update (conn->TLS, &P->chat);
|
||||
}
|
||||
|
||||
return convo;
|
||||
}
|
||||
|
||||
int chat_add_message (struct tgl_state *TLS, struct tgl_message *M, char *text) {
|
||||
connection_data *conn = TLS->ev_base;
|
||||
|
||||
if (chat_show (conn->gc, tgl_get_peer_id (M->to_id))) {
|
||||
p2tgl_got_chat_in(TLS, M->to_id, M->from_id, text ? text : M->message, M->service ? PURPLE_MESSAGE_SYSTEM : 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);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
return convo;
|
||||
}
|
||||
|
||||
int chat_is_member (int who, struct tgl_chat *chat) {
|
||||
|
|
393
tgp-msg.c
Normal file
393
tgp-msg.c
Normal file
|
@ -0,0 +1,393 @@
|
|||
/*
|
||||
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
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <tgl.h>
|
||||
#include <glib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "telegram-purple.h"
|
||||
#include "tgp-structs.h"
|
||||
#include "tgp-msg.h"
|
||||
#include "tgp-ft.h"
|
||||
#include "tgp-2prpl.h"
|
||||
#include "tgp-chat.h"
|
||||
#include "tgp-utils.h"
|
||||
#include "tgp-chat.h"
|
||||
#include "msglog.h"
|
||||
|
||||
static void tgp_msg_err_out (struct tgl_state *TLS, const char *error, tgl_peer_id_t to);
|
||||
|
||||
static char *format_service_msg (struct tgl_state *TLS, struct tgl_message *M) {
|
||||
assert (M && M->service);
|
||||
|
||||
char *txt_user = NULL;
|
||||
char *txt_action = NULL;
|
||||
char *txt = NULL;
|
||||
|
||||
tgl_peer_t *peer = tgl_peer_get (TLS, M->from_id);
|
||||
if (! peer) {
|
||||
return NULL;
|
||||
}
|
||||
txt_user = p2tgl_strdup_alias (peer);
|
||||
|
||||
switch (M->action.type) {
|
||||
case tgl_message_action_chat_create:
|
||||
txt_action = g_strdup_printf ("created chat %s", M->action.title);
|
||||
break;
|
||||
case tgl_message_action_chat_edit_title:
|
||||
txt_action = g_strdup_printf ("changed title to %s", M->action.new_title);
|
||||
break;
|
||||
case tgl_message_action_chat_edit_photo:
|
||||
txt_action = g_strdup ("changed photo");
|
||||
break;
|
||||
case tgl_message_action_chat_delete_photo:
|
||||
txt_action = g_strdup ("deleted photo");
|
||||
break;
|
||||
case tgl_message_action_chat_add_user:
|
||||
{
|
||||
tgl_peer_t *peer = tgl_peer_get (TLS, TGL_MK_USER (M->action.user));
|
||||
if (peer) {
|
||||
char *alias = p2tgl_strdup_alias (peer);
|
||||
txt_action = g_strdup_printf ("added user %s", alias);
|
||||
g_free (alias);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case tgl_message_action_chat_delete_user:
|
||||
{
|
||||
tgl_peer_t *peer = tgl_peer_get (TLS, TGL_MK_USER (M->action.user));
|
||||
if (peer) {
|
||||
char *alias = p2tgl_strdup_alias (peer);
|
||||
txt_action = g_strdup_printf ("deleted user %s", alias);
|
||||
g_free (alias);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case tgl_message_action_set_message_ttl:
|
||||
txt_action = g_strdup_printf ("set ttl to %d seconds", M->action.ttl);
|
||||
break;
|
||||
case tgl_message_action_read_messages:
|
||||
txt_action = g_strdup_printf ("%d messages marked read", M->action.read_cnt);
|
||||
break;
|
||||
case tgl_message_action_delete_messages:
|
||||
txt_action = g_strdup_printf ("%d messages deleted", M->action.delete_cnt);
|
||||
break;
|
||||
case tgl_message_action_screenshot_messages:
|
||||
txt_action = g_strdup_printf ("%d messages screenshoted", M->action.screenshot_cnt);
|
||||
break;
|
||||
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", M->action.exchange_id);
|
||||
break;
|
||||
case tgl_message_action_accept_key:
|
||||
txt_action = g_strdup_printf ("Accept rekey #%016llx", M->action.exchange_id);
|
||||
break;
|
||||
case tgl_message_action_commit_key:
|
||||
txt_action = g_strdup_printf ("Commit rekey #%016llx", M->action.exchange_id);
|
||||
break;
|
||||
case tgl_message_action_abort_key:
|
||||
txt_action = g_strdup_printf ("Abort rekey #%016llx", M->action.exchange_id);
|
||||
break;
|
||||
default:
|
||||
txt_action = NULL;
|
||||
break;
|
||||
}
|
||||
if (txt_action) {
|
||||
debug ("SERVICE MESSAGE: %s", txt_action);
|
||||
txt = g_strdup_printf ("%s %s.", txt_user, txt_action);
|
||||
g_free (txt_action);
|
||||
}
|
||||
g_free (txt_user);
|
||||
return txt;
|
||||
}
|
||||
|
||||
static char *format_document_desc (char *type, char *caption, gint64 size) {
|
||||
char *s = tgp_g_format_size (size);
|
||||
char *msg = g_strdup_printf ("[%s] %s %s", type, caption, s);
|
||||
g_free (s);
|
||||
return msg;
|
||||
}
|
||||
|
||||
static char *format_message (struct tgl_message *M) {
|
||||
|
||||
switch (M->media.type) {
|
||||
case tgl_message_media_document_encr:
|
||||
return format_document_desc ("DOCUMENT", M->media.encr_document.caption, M->media.encr_document.size);
|
||||
break;
|
||||
case tgl_message_media_photo_encr:
|
||||
return format_document_desc ("PHOTO", "", M->media.encr_photo.size);
|
||||
break;
|
||||
case tgl_message_media_contact:
|
||||
return g_strdup_printf ("<b>%s %s</b><br>%s", M->media.first_name, M->media.last_name, M->media.phone);
|
||||
break;
|
||||
case tgl_message_media_geo:
|
||||
return g_strdup_printf("<a href=\"http://openstreetmap.org/?lat=%f&lon=%f&zoom=20\">"
|
||||
"http://openstreetmap.org/?lat=%f&lon=%f&zoom=20</a>",
|
||||
M->media.geo.latitude, M->media.geo.longitude,
|
||||
M->media.geo.latitude, M->media.geo.longitude);
|
||||
return g_strdup_printf ("<b>%s %s</b><br>%s", M->media.first_name, M->media.last_name, M->media.phone);
|
||||
break;
|
||||
default:
|
||||
if (*M->message != 0) {
|
||||
return purple_markup_escape_text (M->message, strlen (M->message));
|
||||
}
|
||||
return g_strdup("");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void tgp_msg_send_done (struct tgl_state *TLS, void *callback_extra, int success, struct tgl_message *M) {
|
||||
if (! success) {
|
||||
const char *err = "Sending message failed. Maybe you don't have the permission "
|
||||
"to send to this peer, or the peer does no longer exist.";
|
||||
warning (err);
|
||||
tgp_msg_err_out (TLS, err, M->to_id);
|
||||
}
|
||||
}
|
||||
|
||||
static int tgp_msg_send_split (struct tgl_state *TLS, const char *message, tgl_peer_id_t to) {
|
||||
connection_data *data = TLS->ev_base;
|
||||
int max = purple_account_get_int (data->pa, "max-msg-split-count",
|
||||
TGP_DEFAULT_HISTORY_RETRIEVAL_THRESHOLD);
|
||||
if (max < 1) {
|
||||
max = 1;
|
||||
}
|
||||
|
||||
int size = (int)g_utf8_strlen(message, -1);
|
||||
if (size > TGP_MAX_MSG_SIZE * max) {
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
int start = 0;
|
||||
while (size > start) {
|
||||
int e = start + (int)TGP_MAX_MSG_SIZE;
|
||||
gchar *chunk = g_utf8_substring (message, start, e);
|
||||
tgl_do_send_message (TLS, to, chunk, (int)strlen (chunk), tgp_msg_send_done, NULL);
|
||||
g_free (chunk);
|
||||
start = e;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void tgp_msg_err_out (struct tgl_state *TLS, const char *error, tgl_peer_id_t to) {
|
||||
int flags = PURPLE_MESSAGE_ERROR | PURPLE_MESSAGE_SYSTEM;
|
||||
time_t now;
|
||||
time (&now);
|
||||
switch (tgl_get_peer_type (to)) {
|
||||
case TGL_PEER_CHAT:
|
||||
p2tgl_got_chat_in (TLS, to, to, error, flags, now);
|
||||
break;
|
||||
case TGL_PEER_USER:
|
||||
case TGL_PEER_ENCR_CHAT:
|
||||
p2tgl_got_im (TLS, to, error, flags, now);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int tgp_msg_send (struct tgl_state *TLS, const char *message, tgl_peer_id_t to) {
|
||||
|
||||
// search for outgoing embedded image tags and send them
|
||||
gchar *img = NULL;
|
||||
gchar *stripped = NULL;
|
||||
if ((img = g_strrstr (message, "<IMG")) || (img = g_strrstr (message, "<img"))) {
|
||||
debug ("img found: %s", img);
|
||||
|
||||
gchar *id;
|
||||
if ((id = g_strrstr (img, "ID=\"")) || (id = g_strrstr (img, "id=\""))) {
|
||||
id += 4;
|
||||
debug ("id found: %s", id);
|
||||
int imgid = atoi (id);
|
||||
if (imgid > 0) {
|
||||
PurpleStoredImage *psi = purple_imgstore_find_by_id (imgid);
|
||||
gchar *tmp = g_build_filename(g_get_tmp_dir(), purple_imgstore_get_filename (psi), NULL) ;
|
||||
|
||||
GError *err = NULL;
|
||||
gconstpointer data = purple_imgstore_get_data (psi);
|
||||
g_file_set_contents (tmp, data, purple_imgstore_get_size (psi), &err);
|
||||
if (! err) {
|
||||
tgl_do_send_document (TLS, -2, to, tmp, tgp_msg_send_done, NULL);
|
||||
} else {
|
||||
failure ("Cannot store image, temp directory not available: %s\n", err->message);
|
||||
g_error_free (err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// send remaining text as additional plaintext message
|
||||
stripped = purple_markup_strip_html (message);
|
||||
int ret = tgp_msg_send_split (TLS, stripped, to);
|
||||
g_free (stripped);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef __ADIUM_
|
||||
/*
|
||||
Adium won't escape any HTML markup and just pass any user-input through,
|
||||
while Pidgin will replace special chars with the escape chars and also add
|
||||
additional markup for RTL languages and such.
|
||||
|
||||
First, we remove any HTML markup added by Pidgin, since Telegram won't handle it properly.
|
||||
User-entered HTML is still escaped and therefore won't be harmed.
|
||||
*/
|
||||
stripped = purple_markup_strip_html (message);
|
||||
|
||||
/*
|
||||
now unescape the markup, so that html special chars will still show
|
||||
up properly in Telegram
|
||||
*/
|
||||
gchar *unescaped = purple_unescape_text (stripped);
|
||||
int ret = tgp_msg_send_split (TLS, stripped, to);
|
||||
|
||||
g_free (unescaped);
|
||||
g_free (stripped);
|
||||
return ret;
|
||||
#endif
|
||||
|
||||
return tgp_msg_send_split (TLS, message, to);
|
||||
}
|
||||
|
||||
static void tgp_msg_display (struct tgl_state *TLS, struct tgp_msg_loading *C) {
|
||||
connection_data *conn = TLS->ev_base;
|
||||
struct tgl_message *M = C->msg;
|
||||
char *text;
|
||||
int flags = 0;
|
||||
|
||||
// Filter message updates and deletes, are not created and
|
||||
// all messages in general that were already displayed, or shouldn't be displayed
|
||||
if ((M->flags & (FLAG_MESSAGE_EMPTY | FLAG_DELETED)) ||
|
||||
!(M->flags & FLAG_CREATED) ||
|
||||
!M->message ||
|
||||
our_msg (TLS, M) ||
|
||||
!tgl_get_peer_type (M->to_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (M->service) {
|
||||
text = format_service_msg (TLS, M);
|
||||
flags |= PURPLE_MESSAGE_SYSTEM;
|
||||
}
|
||||
else if (M->media.type == tgl_message_media_document) {
|
||||
char *who = p2tgl_peer_strdup_id (M->from_id);
|
||||
tgprpl_recv_file (conn->gc, who, &M->media.document);
|
||||
g_free (who);
|
||||
return;
|
||||
}
|
||||
else if (M->media.type == tgl_message_media_photo) {
|
||||
char *filename = C->data;
|
||||
|
||||
int imgStoreId = p2tgl_imgstore_add_with_id (filename);
|
||||
if (imgStoreId <= 0) {
|
||||
failure ("Cannot display picture message, adding to imgstore failed.");
|
||||
return;
|
||||
}
|
||||
used_images_add (conn, imgStoreId);
|
||||
text = format_img_full (imgStoreId);
|
||||
flags |= PURPLE_MESSAGE_IMAGES;
|
||||
}
|
||||
else {
|
||||
text = format_message (M);
|
||||
flags |= PURPLE_MESSAGE_RECV;
|
||||
}
|
||||
|
||||
|
||||
switch (tgl_get_peer_type (M->to_id)) {
|
||||
case TGL_PEER_CHAT: {
|
||||
if (chat_show (conn->gc, tgl_get_peer_id (M->to_id))) {
|
||||
p2tgl_got_chat_in (TLS, M->to_id, M->from_id, text, flags, M->date);
|
||||
}
|
||||
pending_reads_add (conn->pending_reads, M->to_id);
|
||||
break;
|
||||
}
|
||||
case TGL_PEER_ENCR_CHAT: {
|
||||
p2tgl_got_im (TLS, M->to_id, text, flags, M->date);
|
||||
pending_reads_add (conn->pending_reads, M->to_id);
|
||||
break;
|
||||
}
|
||||
case TGL_PEER_USER: {
|
||||
if (out_msg (TLS, M)) {
|
||||
flags |= PURPLE_MESSAGE_SEND;
|
||||
flags &= ~PURPLE_MESSAGE_RECV;
|
||||
p2tgl_got_im_combo (TLS, M->to_id, text, flags, M->date);
|
||||
} else {
|
||||
p2tgl_got_im (TLS, M->from_id, text, flags, M->date);
|
||||
pending_reads_add (conn->pending_reads, M->from_id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (p2tgl_status_is_present (purple_account_get_active_status (conn->pa))) {
|
||||
pending_reads_send_all (conn->pending_reads, conn->TLS);
|
||||
}
|
||||
|
||||
|
||||
g_free (text);
|
||||
}
|
||||
|
||||
static void tgp_msg_process_ready (struct tgl_state *TLS)
|
||||
{
|
||||
connection_data *conn = TLS->ev_base;
|
||||
struct tgp_msg_loading *C;
|
||||
|
||||
while ((C = g_queue_peek_head (conn->new_messages))) {
|
||||
if (! C->done) {
|
||||
break;
|
||||
}
|
||||
g_queue_pop_head (conn->new_messages);
|
||||
tgp_msg_display (TLS, C);
|
||||
tgp_msg_loading_free (C);
|
||||
}
|
||||
}
|
||||
|
||||
static void tgp_msg_on_loaded_photo (struct tgl_state *TLS, void *extra, int success, char *filename) {
|
||||
struct tgp_msg_loading *C = extra;
|
||||
C->data = filename;
|
||||
C->done = TRUE;
|
||||
tgp_msg_process_ready (TLS);
|
||||
}
|
||||
|
||||
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->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) {
|
||||
C->done = FALSE;
|
||||
}
|
||||
|
||||
if (M->media.type == tgl_message_media_photo_encr) {
|
||||
// TODO: handle encrypted document.
|
||||
}
|
||||
|
||||
g_queue_push_tail (conn->new_messages, C);
|
||||
tgp_msg_process_ready (TLS);
|
||||
}
|
||||
|
||||
|
41
tgp-msg.h
Normal file
41
tgp-msg.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
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
|
||||
*/
|
||||
|
||||
#ifndef __telegram_adium__tgp_msg__
|
||||
#define __telegram_adium__tgp_msg__
|
||||
|
||||
/**
|
||||
* Process a message and display it
|
||||
*
|
||||
* Loads embedded ressources like pictures or document thumbnails and ensures that
|
||||
* that all messages are still displayed in the original incoming order.
|
||||
*/
|
||||
void tgp_msg_recv (struct tgl_state *TLS, struct tgl_message *M);
|
||||
|
||||
/**
|
||||
* Process a message and send it the peer
|
||||
*
|
||||
* Removes all HTML escape chars and HTML markup, finds embedded images and sends
|
||||
* them as pictures and splits up messages that are too big for single Telegram
|
||||
* messages.
|
||||
*/
|
||||
int tgp_msg_send (struct tgl_state *TLS, const char *msg, tgl_peer_id_t to);
|
||||
|
||||
#endif
|
|
@ -59,22 +59,6 @@ void pending_reads_add (GQueue *queue, tgl_peer_id_t id) {
|
|||
}
|
||||
}
|
||||
|
||||
struct message_text *message_text_init (struct tgl_message *M, gchar *text) {
|
||||
struct message_text *mt = malloc (sizeof (struct message_text));
|
||||
mt->M = M;
|
||||
mt->text = text ? g_strdup (text) : text;
|
||||
return mt;
|
||||
}
|
||||
|
||||
void message_text_free (gpointer data)
|
||||
{
|
||||
struct message_text *mt = (struct message_text*)data;
|
||||
if (mt->text) {
|
||||
g_free (mt->text);
|
||||
}
|
||||
free (mt);
|
||||
}
|
||||
|
||||
static void used_image_free (gpointer data) {
|
||||
int id = GPOINTER_TO_INT(data);
|
||||
purple_imgstore_unref_by_id (id);
|
||||
|
@ -84,6 +68,18 @@ void used_images_add (connection_data *data, gint imgid) {
|
|||
data->used_images = g_list_append (data->used_images, GINT_TO_POINTER(imgid));
|
||||
}
|
||||
|
||||
void tgp_msg_loading_free (gpointer data) {
|
||||
struct tgp_msg_loading *C = data;
|
||||
free (C);
|
||||
}
|
||||
|
||||
struct tgp_msg_loading *tgp_msg_loading_init (int done, struct tgl_message *M) {
|
||||
struct tgp_msg_loading *C = malloc (sizeof (struct tgp_msg_loading));
|
||||
C->done = done;
|
||||
C->msg = M;
|
||||
return C;
|
||||
}
|
||||
|
||||
connection_data *connection_data_init (struct tgl_state *TLS, PurpleConnection *gc, PurpleAccount *pa) {
|
||||
connection_data *conn = g_new0 (connection_data, 1);
|
||||
conn->TLS = TLS;
|
||||
|
@ -99,7 +95,7 @@ void *connection_data_free (connection_data *conn) {
|
|||
if (conn->login_timer) { purple_timeout_remove (conn->login_timer); }
|
||||
|
||||
tgp_g_queue_free_full (conn->pending_reads, pending_reads_free_cb);
|
||||
tgp_g_queue_free_full (conn->new_messages, message_text_free);
|
||||
tgp_g_queue_free_full (conn->new_messages, tgp_msg_loading_free);
|
||||
tgp_g_list_free_full (conn->used_images, used_image_free);
|
||||
tgprpl_xfer_free_all (conn);
|
||||
tgl_free_all (conn->TLS);
|
||||
|
|
|
@ -58,22 +58,21 @@ struct download_desc {
|
|||
void *data;
|
||||
};
|
||||
|
||||
struct message_text {
|
||||
struct tgl_message *M;
|
||||
char *text;
|
||||
struct tgp_msg_loading {
|
||||
int done;
|
||||
struct tgl_message *msg;
|
||||
void *data;
|
||||
};
|
||||
|
||||
void pending_reads_send_all (GQueue *queue, struct tgl_state *TLS);
|
||||
void pending_reads_add (GQueue *queue, tgl_peer_id_t id);
|
||||
|
||||
struct message_text *message_text_init (struct tgl_message *M, gchar *text);
|
||||
void message_text_free (gpointer data);
|
||||
|
||||
void used_images_add (connection_data *data, gint imgid);
|
||||
|
||||
void *connection_data_free (connection_data *conn);
|
||||
connection_data *connection_data_init (struct tgl_state *TLS, PurpleConnection *gc, PurpleAccount *pa);
|
||||
|
||||
get_user_info_data* get_user_info_data_new (int show_info, tgl_peer_id_t peer);
|
||||
struct tgp_msg_loading *tgp_msg_loading_init (int done, struct tgl_message *M);
|
||||
void tgp_msg_loading_free (gpointer data);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Add table
Reference in a new issue