Support file transfers

This commit is contained in:
mjentsch 2015-02-23 19:42:48 +01:00
parent 8fec18e8c4
commit c9aab9c8eb
10 changed files with 324 additions and 23 deletions

View file

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

View file

@ -116,6 +116,8 @@ Acknowledgements
This software is based on the library [Libtgl](https://github.com/vysheng/tgl), which was written by Vitaly Valtman <mail@vysheng.ru> 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)

View file

@ -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, "<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);
}
}
}
}
// 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

View file

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

View file

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

View file

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

204
tgp-ft.c Normal file
View file

@ -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 <purple.h>
#include <glib.h>
#include <glib/gstdio.h>
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);
}

29
tgp-ft.h Normal file
View file

@ -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 <stdio.h>
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

View file

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

View file

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