telegram-purple/tgp-ft.c

371 lines
12 KiB
C
Raw Permalink Normal View History

2015-02-23 19:42:48 +01:00
/*
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
2015-03-12 01:47:57 +01:00
Copyright Matthias Jentsch 2014-2015
2015-02-23 19:42:48 +01:00
*/
2015-11-25 20:31:44 +01:00
#include <glib/gstdio.h>
#include "telegram-purple.h"
2015-02-23 19:42:48 +01:00
static void tgprpl_xfer_free_data (struct tgp_xfer_send_data *data);
static char *tgp_strdup_determine_filename (const char *mime, const char *caption,
int flags, long long hash) {
if (caption) {
return g_strdup (caption);
}
const char *type = NULL;
if (mime) {
2015-07-30 21:02:22 +02:00
if (flags & TGLDF_VIDEO) {
// video message
type = "mp4";
} else if (flags & TGLDF_AUDIO) {
// audio message
type = "ogg";
} else {
// document message
type = tgp_mime_to_filetype (mime);
}
}
2015-07-30 21:02:22 +02:00
if (! str_not_empty(type)) {
if (flags & TGLDF_IMAGE) {
type = "png";
} else if (flags & TGLDF_AUDIO) {
type = "ogg";
} else if (flags & TGLDF_VIDEO) {
type = "mp4";
} else if (flags & TGLDF_STICKER) {
type = "webp";
} else {
type = "bin";
}
}
2015-12-10 11:56:48 +01:00
return g_strdup_printf ("%" G_GINT64_MODIFIER "d.%s", (gint64) ABS(hash), type);
}
2015-05-23 00:18:26 +02:00
static void tgprpl_xfer_recv_on_finished (struct tgl_state *TLS, void *_data, int success, const char *filename) {
2015-02-23 19:42:48 +01:00
debug ("tgprpl_xfer_recv_on_finished()");
struct tgp_xfer_send_data *data = _data;
2016-01-03 00:29:53 +01:00
char *selected = g_strdup (purple_xfer_get_local_filename (data->xfer));
2015-02-23 19:42:48 +01:00
if (success) {
debug ("purple_xfer_set_completed");
// always completed the file transfer to avoid a warning dialogue when closing (Adium)
purple_xfer_set_bytes_sent (data->xfer, purple_xfer_get_size (data->xfer));
purple_xfer_set_completed (data->xfer, TRUE);
if (! purple_xfer_is_canceled (data->xfer)) {
purple_xfer_end (data->xfer);
2015-02-23 19:42:48 +01:00
}
} else {
2015-08-01 23:34:00 +02:00
tgp_notify_on_error_gw (TLS, NULL, success);
if (! purple_xfer_is_canceled (data->xfer)) {
purple_xfer_cancel_remote (data->xfer);
}
failure ("recv xfer failed");
2015-02-23 19:42:48 +01:00
}
data->loading = FALSE;
data->xfer->data = NULL;
2016-01-03 00:29:53 +01:00
purple_xfer_unref (data->xfer);
tgprpl_xfer_free_data (data);
debug ("moving transferred file from tgl directory %s to selected target %s", selected, filename);
g_unlink (selected);
g_rename (filename, selected);
g_free (selected);
2015-02-23 19:42:48 +01:00
}
2016-01-03 00:29:53 +01:00
static void tgprpl_xfer_send_on_finished (struct tgl_state *TLS, void *_data, int success, struct tgl_message *M) {
2015-02-23 19:42:48 +01:00
debug ("tgprpl_xfer_on_finished()");
struct tgp_xfer_send_data *data = _data;
2016-01-03 00:29:53 +01:00
2015-02-23 19:42:48 +01:00
if (success) {
if (! purple_xfer_is_canceled (data->xfer)) {
debug ("purple_xfer_set_completed");
purple_xfer_set_bytes_sent (data->xfer, purple_xfer_get_size (data->xfer));
2015-02-23 19:42:48 +01:00
purple_xfer_set_completed (data->xfer, TRUE);
2016-01-03 00:29:53 +01:00
purple_xfer_end (data->xfer);
2015-02-23 19:42:48 +01:00
}
2015-08-25 21:54:15 +02:00
write_secret_chat_file (TLS);
2015-02-23 19:42:48 +01:00
} else {
2015-08-01 23:34:00 +02:00
tgp_notify_on_error_gw (TLS, NULL, success);
if (! purple_xfer_is_canceled (data->xfer)) {
purple_xfer_cancel_remote (data->xfer);
}
failure ("send xfer failed");
2015-02-23 19:42:48 +01:00
}
2016-01-03 00:29:53 +01:00
data->loading = FALSE;
data->xfer->data = NULL;
2016-01-03 00:29:53 +01:00
purple_xfer_unref (data->xfer);
tgprpl_xfer_free_data (data);
2015-02-23 19:42:48 +01:00
}
static void tgprpl_xfer_canceled (PurpleXfer *X) {
2016-01-03 00:29:53 +01:00
debug ("tgprpl_xfer_canceled()");
2015-02-23 19:42:48 +01:00
struct tgp_xfer_send_data *data = X->data;
// the xfer data must not be freed when the transfer is still running, since there is no way to cancel
// the running transfer and the callback still needs the xfer data. In that case transfer data will
// be freed once the transfer finished or the account goes offline and all loading transfers are aborted.
if (! data->loading) {
data->xfer->data = NULL;
tgprpl_xfer_free_data (data);
}
2015-02-23 19:42:48 +01:00
}
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);
2015-02-23 19:42:48 +01:00
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);
debug ("PURPLE_XFER_SEND progress %d / %d", conn->TLS->cur_uploaded_bytes, conn->TLS->cur_uploading_bytes);
2015-02-23 19:42:48 +01:00
if (conn->TLS->cur_uploaded_bytes == conn->TLS->cur_uploading_bytes) {
data->timer = 0;
return FALSE;
}
break;
case PURPLE_XFER_RECEIVE:
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);
2015-03-12 01:47:57 +01:00
debug ("PURPLE_XFER_RECEIVE progress %d / %d", conn->TLS->cur_downloaded_bytes, conn->TLS->cur_downloading_bytes);
2015-02-23 19:42:48 +01:00
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(): receiving xfer accepted.");
2015-02-23 19:42:48 +01:00
struct tgp_xfer_send_data *data = X->data;
struct tgl_state *TLS = data->conn->TLS;
struct tgl_message *M = data->msg;
struct tgl_document *D = M->media.document;
tgl_peer_t *P = NULL;
2015-02-23 19:42:48 +01:00
purple_xfer_start (X, -1, NULL, 0);
const char *who = purple_xfer_get_remote_user (X);
P = tgp_blist_lookup_peer_get (TLS, who);
g_return_if_fail(P);
switch (M->media.type) {
case tgl_message_media_document:
tgl_do_load_document (TLS, D, tgprpl_xfer_recv_on_finished, data);
break;
case tgl_message_media_document_encr:
tgl_do_load_encr_document (TLS, M->media.encr_document, tgprpl_xfer_recv_on_finished, data);
break;
case tgl_message_media_audio:
tgl_do_load_audio (TLS, D, tgprpl_xfer_recv_on_finished, data);
break;
case tgl_message_media_video:
tgl_do_load_video (TLS, D, tgprpl_xfer_recv_on_finished, data);
break;
default:
failure ("Unknown message media type: %d, XFER not possible.", M->media.type);
return;
2015-02-23 19:42:48 +01:00
}
// Prevent the xfer data from getting freed after cancelling to allow the file transfer to complete
// without crashing. This is necessary cause loading the file in libtgl cannot be aborted once started.
purple_xfer_ref (X);
2015-02-23 19:42:48 +01:00
data->timer = purple_timeout_add (100, tgprpl_xfer_upload_progress, X);
data->loading = TRUE;
2015-02-23 19:42:48 +01:00
}
static void tgprpl_xfer_send_init (PurpleXfer *X) {
debug ("tgprpl_xfer_send_init(): sending xfer accepted.");
2015-11-21 22:42:07 +01:00
struct tgp_xfer_send_data *data;
const char *file, *localfile, *who;
tgl_peer_t *P;
data = X->data;
2015-02-23 19:42:48 +01:00
purple_xfer_start (X, -1, NULL, 0);
2015-11-21 22:42:07 +01:00
file = purple_xfer_get_filename (X);
localfile = purple_xfer_get_local_filename (X);
who = purple_xfer_get_remote_user (X);
2015-02-23 19:42:48 +01:00
debug ("xfer_on_init (file=%s, local=%s, who=%s)", file, localfile, who);
2015-11-21 22:42:07 +01:00
P = tgp_blist_lookup_peer_get (data->conn->TLS, who);
2015-11-21 22:42:07 +01:00
g_return_if_fail (P);
if (tgl_get_peer_type (P->id) == TGL_PEER_ENCR_CHAT) {
2015-11-21 22:42:07 +01:00
purple_xfer_error (PURPLE_XFER_SEND, data->conn->pa, who,
_("Sorry, sending documents to encrypted chats not yet supported."));
2016-01-03 00:29:53 +01:00
purple_xfer_cancel_local (X);
return;
}
tgl_do_send_document (data->conn->TLS, P->id, (char*) localfile, NULL, 0,
TGL_SEND_MSG_FLAG_DOCUMENT_AUTO, tgprpl_xfer_send_on_finished, data);
// see comment in tgprpl_xfer_recv_init()
purple_xfer_ref (X);
2015-02-23 19:42:48 +01:00
data->timer = purple_timeout_add (100, tgprpl_xfer_upload_progress, X);
data->loading = TRUE;
2015-02-23 19:42:48 +01:00
}
static void tgprpl_xfer_init_data (PurpleXfer *X, connection_data *conn, struct tgl_message *msg) {
2015-02-23 19:42:48 +01:00
if (!X->data) {
struct tgp_xfer_send_data *data = g_malloc0 (sizeof (struct tgp_xfer_send_data));
data->xfer = X;
data->conn = conn;
data->msg = msg;
2015-02-23 19:42:48 +01:00
X->data = data;
}
}
static void tgprpl_xfer_free_data (struct tgp_xfer_send_data *data) {
2015-11-21 22:43:16 +01:00
if (data->timer) {
2016-01-03 00:29:53 +01:00
purple_input_remove (data->timer);
2015-11-21 22:43:16 +01:00
}
data->timer = 0;
g_free (data);
}
void tgprpl_xfer_free_all (connection_data *conn) {
GList *xfers = purple_xfers_get_all ();
while (xfers) {
PurpleXfer *xfer = xfers->data;
2016-01-03 00:29:53 +01:00
if (purple_xfer_get_account (xfer) == conn->pa) {
debug ("xfer: %s", xfer->filename);
// cancel all non-completed file tranfsers to avoid them from being called
// in future sessions, as they still contain references to already freed data.
if (! purple_xfer_is_canceled (xfer) && ! purple_xfer_is_completed (xfer)) {
purple_xfer_cancel_local (xfer);
}
// if a file transfer is still running while going offline, it will be canceled when
// cleaning up libtgl memory. Since canceled file transfers are being kept (see
// tgprpl_xfer_canceled() and tgprpl_xfer_recv_init()) those need to be freed now.
struct tgp_xfer_send_data *data = xfer->data;
if (data) {
if (data->loading) {
tgprpl_xfer_free_data (data);
xfer->data = NULL;
purple_xfer_unref (xfer);
} else {
g_warn_if_reached();
}
2016-01-03 00:29:53 +01:00
}
}
2016-01-03 00:29:53 +01:00
xfers = g_list_next(xfers);
2015-02-23 19:42:48 +01:00
}
}
2015-11-21 22:43:16 +01:00
PurpleXfer *tgprpl_new_xfer (PurpleConnection *gc, const char *who) {
2015-02-23 19:42:48 +01:00
debug ("tgprpl_new_xfer()");
2016-01-03 00:29:53 +01:00
2015-11-21 22:43:16 +01:00
PurpleXfer *X = purple_xfer_new (purple_connection_get_account (gc), PURPLE_XFER_SEND, who);
if (X) {
purple_xfer_set_init_fnc (X, tgprpl_xfer_send_init);
purple_xfer_set_cancel_send_fnc (X, tgprpl_xfer_canceled);
tgprpl_xfer_init_data (X, purple_connection_get_protocol_data (gc), NULL);
}
2016-01-03 00:29:53 +01:00
2015-11-21 22:43:16 +01:00
return X;
2015-02-23 19:42:48 +01:00
}
2015-11-21 22:43:16 +01:00
static PurpleXfer *tgprpl_new_xfer_recv (PurpleConnection *gc, const char *who) {
PurpleXfer *X = purple_xfer_new (purple_connection_get_account (gc), PURPLE_XFER_RECEIVE, who);
2015-02-23 19:42:48 +01:00
purple_xfer_set_init_fnc (X, tgprpl_xfer_recv_init);
purple_xfer_set_cancel_recv_fnc (X, tgprpl_xfer_canceled);
2016-01-03 00:29:53 +01:00
return X;
}
void tgprpl_recv_file (PurpleConnection *gc, const char *who, struct tgl_message *M) {
debug ("tgprpl_recv_file()");
g_return_if_fail (who);
PurpleXfer *X = tgprpl_new_xfer_recv (gc, who);
const char *mime_type, *caption;
long long access_hash;
int flags, size;
if (M->media.type == tgl_message_media_document_encr) {
mime_type = M->media.encr_document->mime_type;
caption = M->media.encr_document->caption;
access_hash = M->media.encr_document->access_hash;
flags = M->media.encr_document->flags;
size = M->media.encr_document->size;
} else {
mime_type = M->media.document->mime_type;
caption = M->media.document->caption;
access_hash = M->media.document->access_hash;
flags = M->media.document->flags;
size = M->media.document->size;
}
char *filename = tgp_strdup_determine_filename (mime_type, caption, flags, access_hash);
purple_xfer_set_filename (X, filename);
g_free (filename);
purple_xfer_set_size (X, size);
tgprpl_xfer_init_data (X, purple_connection_get_protocol_data (gc), M);
2015-02-23 19:42:48 +01:00
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);
if (file) {
purple_xfer_request_accepted (X, file);
debug ("starting xfer...");
} else {
purple_xfer_request (X);
}
}