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:
mjentsch 2015-03-12 01:38:17 +01:00
parent 95258c940b
commit 1e442ac195
8 changed files with 470 additions and 386 deletions

View file

@ -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:

View file

@ -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 */,

View file

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

View file

@ -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
View 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
View 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

View file

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

View file

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