diff --git a/Makefile.in b/Makefile.in index c37c707..8b7f283 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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: diff --git a/telegram-adium/telegram-adium.xcodeproj/project.pbxproj b/telegram-adium/telegram-adium.xcodeproj/project.pbxproj index 50302b5..543223c 100644 --- a/telegram-adium/telegram-adium.xcodeproj/project.pbxproj +++ b/telegram-adium/telegram-adium.xcodeproj/project.pbxproj @@ -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 = ""; }; C438CE3B1A12C0C900E1DA0F /* tgp-timers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "tgp-timers.h"; path = "../tgp-timers.h"; sourceTree = ""; }; C438CE3C1A12C15100E1DA0F /* tg-server.pub */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "tg-server.pub"; path = "../tg-server.pub"; sourceTree = ""; }; + C448ADA61AB0789A001B7ECD /* tgp-msg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "tgp-msg.c"; path = "../tgp-msg.c"; sourceTree = ""; }; + C448ADA81AB078BB001B7ECD /* tgp-msg.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "tgp-msg.h"; path = "../tgp-msg.h"; sourceTree = ""; }; 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 = ""; }; C4877C1719BB37EA006FA91F /* TelegramService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TelegramService.m; sourceTree = ""; }; @@ -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 */, diff --git a/telegram-purple.c b/telegram-purple.c index 1543c79..b860bfb 100755 --- a/telegram-purple.c +++ b/telegram-purple.c @@ -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, " 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; } diff --git a/tgp-chat.c b/tgp-chat.c index c0dd2f9..46ceeb1 100644 --- a/tgp-chat.c +++ b/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) { diff --git a/tgp-msg.c b/tgp-msg.c new file mode 100644 index 0000000..35a0e0d --- /dev/null +++ b/tgp-msg.c @@ -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 +#include +#include +#include + +#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 ("%s %s
%s", M->media.first_name, M->media.last_name, M->media.phone); + break; + case tgl_message_media_geo: + return g_strdup_printf("" + "http://openstreetmap.org/?lat=%f&lon=%f&zoom=20", + M->media.geo.latitude, M->media.geo.longitude, + M->media.geo.latitude, M->media.geo.longitude); + return g_strdup_printf ("%s %s
%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, " 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); +} + + diff --git a/tgp-msg.h b/tgp-msg.h new file mode 100644 index 0000000..c084923 --- /dev/null +++ b/tgp-msg.h @@ -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 diff --git a/tgp-structs.c b/tgp-structs.c index 2724fb5..8b46402 100644 --- a/tgp-structs.c +++ b/tgp-structs.c @@ -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); diff --git a/tgp-structs.h b/tgp-structs.h index 602d0c4..2942064 100755 --- a/tgp-structs.h +++ b/tgp-structs.h @@ -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