diff --git a/Makefile.in b/Makefile.in index cf205e5..8fd3d40 100644 --- a/Makefile.in +++ b/Makefile.in @@ -15,7 +15,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}/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}/lodepng/lodepng.o ALL_OBJS=${PLUGIN_OBJECTS} .SUFFIXES: diff --git a/README.md b/README.md index f6e0d07..df510c0 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,8 @@ Acknowledgements This software is based on the library [Libtgl](https://github.com/vysheng/tgl), which was written by Vitaly Valtman and others, see (http://github.com/vysheng/tgl) +For PNG rendering, it includes the [lodepng library](http://lodev.org/lodepng/). + This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. (http://www.openssl.org/) This product includes cryptographic software written by Eric Young (eay@cryptsoft.com) diff --git a/telegram-purple.c b/telegram-purple.c index a523eac..b660996 100755 --- a/telegram-purple.c +++ b/telegram-purple.c @@ -68,6 +68,7 @@ #include "msglog.h" #include "tgp-utils.h" #include "tgp-chat.h" +#include "tgp-ft.h" #define _(m) m @@ -255,10 +256,56 @@ static char *format_message (struct tgl_message *M) { } } +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 void tgl_do_send_unescape_message (struct tgl_state *TLS, const char *message, tgl_peer_id_t to) { - gchar *raw = purple_unescape_html(message); + + debug (message); + + // search for outgoing embedded image tags and send them + gchar *img = 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); + } + } + } + } + + // remove all remaining html elements that would mess up the message + gchar *stripped = purple_markup_strip_html (message); + + // unescape all escaped html markup, so that we don't see any escape symbols in the message + gchar *raw = purple_unescape_html (stripped); tgl_do_send_message (TLS, to, raw, (int)strlen (raw), 0, 0); - g_free(raw); + g_free (raw); + + g_free (stripped); } static void start_secret_chat (PurpleBlistNode *node, gpointer data) { @@ -354,6 +401,15 @@ static void update_message_received (struct tgl_state *TLS, struct tgl_message * 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)) { @@ -666,7 +722,7 @@ static GList *tgprpl_status_types (PurpleAccount * acct) { } static GList* tgprpl_blist_node_menu (PurpleBlistNode *node) { - purple_debug_info (PLUGIN_ID, "tgprpl_blist_node_menu()"); + debug ("tgprpl_blist_node_menu()"); GList* menu = NULL; if (PURPLE_BLIST_NODE_IS_BUDDY(node)) { @@ -880,15 +936,6 @@ static char *tgprpl_get_chat_name (GHashTable * data) { return g_strdup(g_hash_table_lookup(data, "subject")); } -static PurpleXfer *tgprpl_new_xfer (PurpleConnection * gc, const char *who) { - debug ("tgprpl_new_xfer()"); - return (PurpleXfer *)NULL; -} - -static void tgprpl_send_file (PurpleConnection * gc, const char *who, const char *file) { - debug ("tgprpl_send_file()"); -} - static GHashTable *tgprpl_chat_info_deflt (PurpleConnection * gc, const char *chat_name) { debug ("tgprpl_chat_info_defaults()"); return NULL; @@ -947,16 +994,11 @@ static void tgprpl_set_buddy_icon (PurpleConnection * gc, PurpleStoredImage * im static gboolean tgprpl_can_receive_file (PurpleConnection * gc, const char *who) { debug ("tgprpl_can_receive_file()"); - return 0; -} - -static gboolean tgprpl_offline_message (const PurpleBuddy * buddy) { - debug ("tgprpl_offline_message()"); - return 0; + return TRUE; } static PurplePluginProtocolInfo prpl_info = { - OPT_PROTO_NO_PASSWORD, + OPT_PROTO_NO_PASSWORD | OPT_PROTO_IM_IMAGE, NULL, // user_splits, initialized in tgprpl_init() NULL, // protocol_options, initialized in tgprpl_init() { @@ -1022,7 +1064,7 @@ static PurplePluginProtocolInfo prpl_info = { tgprpl_can_receive_file, tgprpl_send_file, tgprpl_new_xfer, - tgprpl_offline_message, + NULL, // offline_message NULL, // whiteboard_prpl_ops NULL, // send_raw NULL, // roomlist_room_serialize diff --git a/telegram-purple.h b/telegram-purple.h index 91ccaac..0c3842c 100644 --- a/telegram-purple.h +++ b/telegram-purple.h @@ -42,5 +42,6 @@ extern const char *pk_path; extern const char *config_dir; extern PurplePlugin *_telegram_protocol; char *format_user_status (struct tgl_user_status *status); +char *p2tgl_peer_strdup_id (tgl_peer_id_t user); #endif diff --git a/tgp-2prpl.c b/tgp-2prpl.c index fa35431..3ba19a7 100644 --- a/tgp-2prpl.c +++ b/tgp-2prpl.c @@ -37,7 +37,7 @@ PurpleConnection *tg_get_conn (struct tgl_state *TLS) { return (PurpleConnection *) ((connection_data *)TLS->ev_base)->gc; } -static char *p2tgl_peer_strdup_id (tgl_peer_id_t user) { +char *p2tgl_peer_strdup_id (tgl_peer_id_t user) { return g_strdup_printf("%d", tgl_get_peer_id(user)); } @@ -51,6 +51,11 @@ int p2tgl_status_is_present (PurpleStatus *status) return !(strcmp (name, "unavailable") == 0 || strcmp (name, "away") == 0); } +/* + Disclaimer: I stole this function from davidgfnet's whatsapp plugin, all + credit for it goes to him + @see: https://github.com/davidgfnet/whatsapp-purple + */ static PurpleChat *blist_find_chat_by_hasht_cond (PurpleConnection *gc, int (*fn)(GHashTable *hasht, void *data), void *data) { PurpleAccount *account = purple_connection_get_account(gc); @@ -261,7 +266,8 @@ PurpleChat *p2tgl_chat_new (struct tgl_state *TLS, struct tgl_chat *chat) { g_hash_table_insert(ht, g_strdup("id"), name); g_hash_table_insert(ht, g_strdup("owner"), admin); - return purple_chat_new(tg_get_acc(TLS), chat->title, ht); + PurpleChat *C = purple_chat_new(tg_get_acc(TLS), chat->title, ht); + return C; } PurpleChat *p2tgl_chat_find (struct tgl_state *TLS, tgl_peer_id_t id) { diff --git a/tgp-2prpl.h b/tgp-2prpl.h index 2a787c3..d503d70 100644 --- a/tgp-2prpl.h +++ b/tgp-2prpl.h @@ -34,6 +34,7 @@ PurpleAccount *tg_get_acc (struct tgl_state *TLS); PurpleConnection *tg_get_conn (struct tgl_state *TLS); tgl_peer_t *p2tgl_get_peer (tgl_peer_id_t peer); tgl_peer_t *p2tgl_get_peer_by_id (int id); +char *p2tgl_peer_strdup_id (tgl_peer_id_t user); char *p2tgl_strdup_alias(tgl_peer_t *user); int p2tgl_status_is_present (PurpleStatus *status); diff --git a/tgp-ft.c b/tgp-ft.c new file mode 100644 index 0000000..3d0e9da --- /dev/null +++ b/tgp-ft.c @@ -0,0 +1,204 @@ +/* + 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 "tgp-utils.h" +#include "tgp-ft.h" +#include "tgp-structs.h" +#include "msglog.h" + +#include +#include +#include + +static void tgprpl_xfer_recv_on_finished (struct tgl_state *TLS, void *_data, int success, char *filename) { + debug ("tgprpl_xfer_recv_on_finished()"); + struct tgp_xfer_send_data *data = _data; + + if (success) { + if (!data->done) { + purple_xfer_set_completed (data->xfer, TRUE); + } + + g_unlink (purple_xfer_get_local_filename (data->xfer)); + g_rename (filename, purple_xfer_get_local_filename(data->xfer)); + + } else { + failure ("ERROR xfer failed"); + } + + if (data->timer) { purple_timeout_remove(data->timer); } + data->timer = 0; +} + +static void tgprpl_xfer_on_finished (struct tgl_state *TLS, void *_data, int success, struct tgl_message *M) { + debug ("tgprpl_xfer_on_finished()"); + struct tgp_xfer_send_data *data = _data; + + if (success) { + if (!data->done) { + purple_xfer_set_completed (data->xfer, TRUE); + } + } else { + failure ("ERROR xfer failed"); + } + + if (data->timer) { purple_timeout_remove(data->timer); } + data->timer = 0; +} + +static void tgprpl_xfer_canceled (PurpleXfer *X) { + struct tgp_xfer_send_data *data = X->data; + data->done = TRUE; + + if (data->timer) { purple_timeout_remove (data->timer); } + data->timer = 0; +} + +static gboolean tgprpl_xfer_upload_progress (gpointer _data) { + PurpleXfer *X = _data; + struct tgp_xfer_send_data *data = X->data; + connection_data *conn = data->conn; + + PurpleXferType type = purple_xfer_get_type(X); + switch (type) { + case PURPLE_XFER_SEND: + purple_xfer_set_size (X, conn->TLS->cur_uploading_bytes); + purple_xfer_set_bytes_sent (X, conn->TLS->cur_uploaded_bytes); + purple_xfer_update_progress (X); + + if (conn->TLS->cur_uploaded_bytes == conn->TLS->cur_uploading_bytes) { + data->timer = 0; + return FALSE; + } + break; + + case PURPLE_XFER_RECEIVE: + debug ("PURPLE_XFER_RECEIVER progress ..."); + purple_xfer_set_size (X, conn->TLS->cur_downloading_bytes); + purple_xfer_set_bytes_sent (X, conn->TLS->cur_downloaded_bytes); + purple_xfer_update_progress (X); + + debug ("PURPLE_XFER_RECEIVER progress %d", conn->TLS->cur_downloaded_bytes); + if (conn->TLS->cur_downloading_bytes == conn->TLS->cur_downloaded_bytes) { + data->timer = 0; + return FALSE; + } + break; + + default: + case PURPLE_XFER_UNKNOWN: + failure ("ERROR: tgprpl_xfer_upload_progress xfer type PURPLE_XFER_UNKNOWN."); + return FALSE; + break; + } + return TRUE; +} + +static void tgprpl_xfer_recv_init (PurpleXfer *X) { + debug ("tgprpl_xfer_recv_init"); + struct tgp_xfer_send_data *data = X->data; + + purple_xfer_start (X, -1, NULL, 0); + + const char *who = purple_xfer_get_remote_user (X); + debug ("who: %s", who); + tgl_peer_t *P = find_peer_by_name (data->conn->TLS, who); + + if (P) { + tgl_do_load_document (data->conn->TLS, data->document, tgprpl_xfer_recv_on_finished, data); + } else { + warning ("User not found, not downloading..."); + } + + data->timer = purple_timeout_add (100, tgprpl_xfer_upload_progress, X); +} + +static void tgprpl_xfer_send_init (PurpleXfer *X) { + struct tgp_xfer_send_data *data = X->data; + + purple_xfer_start (X, -1, NULL, 0); + + const char *file = purple_xfer_get_filename (X); + const char *localfile = purple_xfer_get_local_filename (X); + const char *who = purple_xfer_get_remote_user (X); + debug ("xfer_on_init (file=%s, local=%s, who=%s)", file, localfile, who); + + tgl_peer_t *P = find_peer_by_name (data->conn->TLS, who); + if (P) { + tgl_do_send_document (data->conn->TLS, -2, P->id, (char*)localfile, tgprpl_xfer_on_finished, data); + } + + data->timer = purple_timeout_add (100, tgprpl_xfer_upload_progress, X); +} + +static void tgprpl_xfer_init_data (PurpleXfer *X, connection_data *conn, struct tgl_document *D) { + if (!X->data) { + // TODO: free this somewhere + struct tgp_xfer_send_data *data = g_malloc0 (sizeof (struct tgp_xfer_send_data)); + data->xfer = X; + data->conn = conn; + data->document = D; + X->data = data; + conn->transfers = g_list_append (conn->transfers, data); + } +} + +PurpleXfer *tgprpl_new_xfer (PurpleConnection * gc, const char *who) { + debug ("tgprpl_new_xfer()"); + + connection_data *conn = purple_connection_get_protocol_data (gc); + + PurpleXfer *X = purple_xfer_new (conn->pa, PURPLE_XFER_SEND, who); + + return (PurpleXfer *)X; +} + +void tgprpl_recv_file (PurpleConnection * gc, const char *who, struct tgl_document *D) { + debug ("tgprpl_recv_file()"); + connection_data *conn = purple_connection_get_protocol_data (gc); + + PurpleXfer *X = purple_xfer_new (conn->pa, PURPLE_XFER_RECEIVE, who); + + purple_xfer_set_filename (X, D->caption); + purple_xfer_set_init_fnc (X, tgprpl_xfer_recv_init); + purple_xfer_set_cancel_recv_fnc (X, tgprpl_xfer_canceled); + + tgprpl_xfer_init_data (X, purple_connection_get_protocol_data (gc), D); + + purple_xfer_request (X); +} + +void tgprpl_send_file (PurpleConnection * gc, const char *who, const char *file) { + debug ("tgprpl_send_file()"); + + PurpleXfer *X = tgprpl_new_xfer (gc, who); + purple_xfer_set_init_fnc (X, tgprpl_xfer_send_init); + purple_xfer_set_cancel_send_fnc (X, tgprpl_xfer_canceled); + + if (file) { + purple_xfer_request_accepted (X, file); + debug ("starting xfer..."); + } else { + purple_xfer_request (X); + } + + tgprpl_xfer_init_data (X, purple_connection_get_protocol_data (gc), NULL); +} + diff --git a/tgp-ft.h b/tgp-ft.h new file mode 100644 index 0000000..f20b186 --- /dev/null +++ b/tgp-ft.h @@ -0,0 +1,29 @@ +/* + 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_ft__ +#define __telegram_adium__tgp_ft__ + +#include + +PurpleXfer *tgprpl_new_xfer (PurpleConnection * gc, const char *who); +void tgprpl_send_file (PurpleConnection * gc, const char *who, const char *file); +void tgprpl_recv_file (PurpleConnection * gc, const char *who, struct tgl_document *D); +#endif diff --git a/tgp-structs.c b/tgp-structs.c index 5648cbe..3ec98f3 100644 --- a/tgp-structs.c +++ b/tgp-structs.c @@ -93,6 +93,12 @@ connection_data *connection_data_init (struct tgl_state *TLS, PurpleConnection * return conn; } +static void tgp_xfer_send_data_free (gpointer _data) { + struct tgp_xfer_send_data *data = _data; + if (data->timer) { purple_timeout_remove(data->timer); } + g_free (data); +} + void *connection_data_free (connection_data *conn) { if (conn->write_timer) { purple_timeout_remove (conn->write_timer); } if (conn->login_timer) { purple_timeout_remove (conn->login_timer); } @@ -100,6 +106,7 @@ void *connection_data_free (connection_data *conn) { tgp_g_queue_free_full (conn->pending_reads, pending_reads_free_cb); tgp_g_queue_free_full (conn->new_messages, message_text_free); g_list_free_full (conn->used_images, used_image_free); + g_list_free_full (conn->transfers, tgp_xfer_send_data_free); tgl_free_all (conn->TLS); free (conn->TLS); diff --git a/tgp-structs.h b/tgp-structs.h index fac458d..e8f114e 100755 --- a/tgp-structs.h +++ b/tgp-structs.h @@ -37,6 +37,7 @@ typedef struct { GList *used_images; guint write_timer; guint login_timer; + GList *transfers; int in_fallback_chat; } connection_data; @@ -45,6 +46,14 @@ typedef struct { tgl_peer_id_t peer; } get_user_info_data; +struct tgp_xfer_send_data { + int timer; + int done; + PurpleXfer *xfer; + connection_data *conn; + struct tgl_document *document; +}; + struct download_desc { get_user_info_data *get_user_info_data; void *data;