Mercurial > pidgin.yaz
diff libpurple/protocols/silc/ft.c @ 15374:5fe8042783c1
Rename gtk/ and libgaim/ to pidgin/ and libpurple/
author | Sean Egan <seanegan@gmail.com> |
---|---|
date | Sat, 20 Jan 2007 02:32:10 +0000 |
parents | |
children | 32c366eeeb99 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/silc/ft.c Sat Jan 20 02:32:10 2007 +0000 @@ -0,0 +1,412 @@ +/* + + silcgaim_ft.c + + Author: Pekka Riikonen <priikone@silcnet.org> + + Copyright (C) 2004 Pekka Riikonen + + 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; version 2 of the License. + + 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. + +*/ + +#include "silcincludes.h" +#include "silcclient.h" +#include "silcgaim.h" + +/****************************** File Transfer ********************************/ + +/* This implements the secure file transfer protocol (SFTP) using the SILC + SFTP library implementation. The API we use from the SILC Toolkit is the + SILC Client file transfer API, as it provides a simple file transfer we + need in this case. We could use the SILC SFTP API directly, but it would + be an overkill since we'd effectively re-implement the file transfer what + the SILC Client's file transfer API already provides. + + From Gaim we do NOT use the FT API to do the transfer as it is very limiting. + In fact it does not suite to file transfers like SFTP at all. For example, + it assumes that read operations are synchronous what they are not in SFTP. + It also assumes that the file transfer socket is to be handled by the Gaim + eventloop, and this naturally is something we don't want to do in case of + SILC Toolkit. The FT API suites well to purely stream based file transfers + like HTTP GET and similar. + + For this reason, we directly access the Gaim GKT FT API and hack the FT + API to merely provide the user interface experience and all the magic + is done in the SILC Toolkit. Ie. we update the statistics information in + the FT API for user interface, and that's it. A bit dirty but until the + FT API gets better this is the way to go. Good thing that FT API allowed + us to do this. */ + +typedef struct { + SilcGaim sg; + SilcClientEntry client_entry; + SilcUInt32 session_id; + char *hostname; + SilcUInt16 port; + GaimXfer *xfer; + + SilcClientFileName completion; + void *completion_context; +} *SilcGaimXfer; + +static void +silcgaim_ftp_monitor(SilcClient client, + SilcClientConnection conn, + SilcClientMonitorStatus status, + SilcClientFileError error, + SilcUInt64 offset, + SilcUInt64 filesize, + SilcClientEntry client_entry, + SilcUInt32 session_id, + const char *filepath, + void *context) +{ + SilcGaimXfer xfer = context; + GaimConnection *gc = xfer->sg->gc; + char tmp[256]; + + if (status == SILC_CLIENT_FILE_MONITOR_CLOSED) { + gaim_xfer_unref(xfer->xfer); + silc_free(xfer); + return; + } + + if (status == SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT) + return; + + if (status == SILC_CLIENT_FILE_MONITOR_ERROR) { + if (error == SILC_CLIENT_FILE_NO_SUCH_FILE) { + g_snprintf(tmp, sizeof(tmp), "No such file %s", + filepath ? filepath : "[N/A]"); + gaim_notify_error(gc, _("Secure File Transfer"), + _("Error during file transfer"), tmp); + } else if (error == SILC_CLIENT_FILE_PERMISSION_DENIED) { + gaim_notify_error(gc, _("Secure File Transfer"), + _("Error during file transfer"), + _("Permission denied")); + } else if (error == SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED) { + gaim_notify_error(gc, _("Secure File Transfer"), + _("Error during file transfer"), + _("Key agreement failed")); + } else if (error == SILC_CLIENT_FILE_UNKNOWN_SESSION) { + gaim_notify_error(gc, _("Secure File Transfer"), + _("Error during file transfer"), + _("File transfer session does not exist")); + } else { + gaim_notify_error(gc, _("Secure File Transfer"), + _("Error during file transfer"), NULL); + } + silc_client_file_close(client, conn, session_id); + gaim_xfer_unref(xfer->xfer); + silc_free(xfer); + return; + } + + /* Update file transfer UI */ + if (!offset && filesize) + gaim_xfer_set_size(xfer->xfer, filesize); + if (offset && filesize) { + xfer->xfer->bytes_sent = offset; + xfer->xfer->bytes_remaining = filesize - offset; + } + gaim_xfer_update_progress(xfer->xfer); + + if (status == SILC_CLIENT_FILE_MONITOR_SEND || + status == SILC_CLIENT_FILE_MONITOR_RECEIVE) { + if (offset == filesize) { + /* Download finished */ + gaim_xfer_set_completed(xfer->xfer, TRUE); + silc_client_file_close(client, conn, session_id); + } + } +} + +static void +silcgaim_ftp_cancel(GaimXfer *x) +{ + SilcGaimXfer xfer = x->data; + xfer->xfer->status = GAIM_XFER_STATUS_CANCEL_LOCAL; + gaim_xfer_update_progress(xfer->xfer); + silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id); +} + +static void +silcgaim_ftp_ask_name_cancel(GaimXfer *x) +{ + SilcGaimXfer xfer = x->data; + + /* Cancel the transmission */ + xfer->completion(NULL, xfer->completion_context); + silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id); +} + +static void +silcgaim_ftp_ask_name_ok(GaimXfer *x) +{ + SilcGaimXfer xfer = x->data; + const char *name; + + name = gaim_xfer_get_local_filename(x); + g_unlink(name); + xfer->completion(name, xfer->completion_context); +} + +static void +silcgaim_ftp_ask_name(SilcClient client, + SilcClientConnection conn, + SilcUInt32 session_id, + const char *remote_filename, + SilcClientFileName completion, + void *completion_context, + void *context) +{ + SilcGaimXfer xfer = context; + + xfer->completion = completion; + xfer->completion_context = completion_context; + + gaim_xfer_set_init_fnc(xfer->xfer, silcgaim_ftp_ask_name_ok); + gaim_xfer_set_request_denied_fnc(xfer->xfer, silcgaim_ftp_ask_name_cancel); + + /* Request to save the file */ + gaim_xfer_set_filename(xfer->xfer, remote_filename); + gaim_xfer_request(xfer->xfer); +} + +static void +silcgaim_ftp_request_result(GaimXfer *x) +{ + SilcGaimXfer xfer = x->data; + SilcClientFileError status; + GaimConnection *gc = xfer->sg->gc; + + if (gaim_xfer_get_status(x) != GAIM_XFER_STATUS_ACCEPTED) + return; + + /* Start the file transfer */ + status = silc_client_file_receive(xfer->sg->client, xfer->sg->conn, + silcgaim_ftp_monitor, xfer, + NULL, xfer->session_id, + silcgaim_ftp_ask_name, xfer); + switch (status) { + case SILC_CLIENT_FILE_OK: + return; + break; + + case SILC_CLIENT_FILE_UNKNOWN_SESSION: + gaim_notify_error(gc, _("Secure File Transfer"), + _("No file transfer session active"), NULL); + break; + + case SILC_CLIENT_FILE_ALREADY_STARTED: + gaim_notify_error(gc, _("Secure File Transfer"), + _("File transfer already started"), NULL); + break; + + case SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED: + gaim_notify_error(gc, _("Secure File Transfer"), + _("Could not perform key agreement for file transfer"), + NULL); + break; + + default: + gaim_notify_error(gc, _("Secure File Transfer"), + _("Could not start the file transfer"), NULL); + break; + } + + /* Error */ + gaim_xfer_unref(xfer->xfer); + g_free(xfer->hostname); + silc_free(xfer); +} + +static void +silcgaim_ftp_request_denied(GaimXfer *x) +{ + +} + +void silcgaim_ftp_request(SilcClient client, SilcClientConnection conn, + SilcClientEntry client_entry, SilcUInt32 session_id, + const char *hostname, SilcUInt16 port) +{ + GaimConnection *gc = client->application; + SilcGaim sg = gc->proto_data; + SilcGaimXfer xfer; + + xfer = silc_calloc(1, sizeof(*xfer)); + if (!xfer) { + silc_client_file_close(sg->client, sg->conn, session_id); + return; + } + + xfer->sg = sg; + xfer->client_entry = client_entry; + xfer->session_id = session_id; + xfer->hostname = g_strdup(hostname); + xfer->port = port; + xfer->xfer = gaim_xfer_new(xfer->sg->account, GAIM_XFER_RECEIVE, + xfer->client_entry->nickname); + if (!xfer->xfer) { + silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id); + g_free(xfer->hostname); + silc_free(xfer); + return; + } + gaim_xfer_set_init_fnc(xfer->xfer, silcgaim_ftp_request_result); + gaim_xfer_set_request_denied_fnc(xfer->xfer, silcgaim_ftp_request_denied); + gaim_xfer_set_cancel_recv_fnc(xfer->xfer, silcgaim_ftp_cancel); + xfer->xfer->remote_ip = g_strdup(hostname); + xfer->xfer->remote_port = port; + xfer->xfer->data = xfer; + + /* File transfer request */ + gaim_xfer_request(xfer->xfer); +} + +static void +silcgaim_ftp_send_cancel(GaimXfer *x) +{ + SilcGaimXfer xfer = x->data; + silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id); + gaim_xfer_unref(xfer->xfer); + g_free(xfer->hostname); + silc_free(xfer); +} + +static void +silcgaim_ftp_send(GaimXfer *x) +{ + SilcGaimXfer xfer = x->data; + const char *name; + char *local_ip = NULL, *remote_ip = NULL; + gboolean local = TRUE; + + name = gaim_xfer_get_local_filename(x); + + /* Do the same magic what we do with key agreement (see silcgaim_buddy.c) + to see if we are behind NAT. */ + if (silc_net_check_local_by_sock(xfer->sg->conn->sock->sock, + NULL, &local_ip)) { + /* Check if the IP is private */ + if (silcgaim_ip_is_private(local_ip)) { + local = FALSE; + /* Local IP is private, resolve the remote server IP to see whether + we are talking to Internet or just on LAN. */ + if (silc_net_check_host_by_sock(xfer->sg->conn->sock->sock, NULL, + &remote_ip)) + if (silcgaim_ip_is_private(remote_ip)) + /* We assume we are in LAN. Let's provide the connection point. */ + local = TRUE; + } + } + + if (local && !local_ip) + local_ip = silc_net_localip(); + + /* Send the file */ + silc_client_file_send(xfer->sg->client, xfer->sg->conn, + silcgaim_ftp_monitor, xfer, + local_ip, 0, !local, xfer->client_entry, + name, &xfer->session_id); + + silc_free(local_ip); + silc_free(remote_ip); +} + +static void +silcgaim_ftp_send_file_resolved(SilcClient client, + SilcClientConnection conn, + SilcClientEntry *clients, + SilcUInt32 clients_count, + void *context) +{ + GaimConnection *gc = client->application; + char tmp[256]; + + if (!clients) { + g_snprintf(tmp, sizeof(tmp), + _("User %s is not present in the network"), + (const char *)context); + gaim_notify_error(gc, _("Secure File Transfer"), + _("Cannot send file"), tmp); + silc_free(context); + return; + } + + silcgaim_ftp_send_file(client->application, (const char *)context, NULL); + silc_free(context); +} + +GaimXfer *silcgaim_ftp_new_xfer(GaimConnection *gc, const char *name) +{ + SilcGaim sg = gc->proto_data; + SilcClient client = sg->client; + SilcClientConnection conn = sg->conn; + SilcClientEntry *clients; + SilcUInt32 clients_count; + SilcGaimXfer xfer; + char *nickname; + + g_return_val_if_fail(name != NULL, NULL); + + if (!silc_parse_userfqdn(name, &nickname, NULL)) + return NULL; + + /* Find client entry */ + clients = silc_client_get_clients_local(client, conn, nickname, name, + &clients_count); + if (!clients) { + silc_client_get_clients(client, conn, nickname, NULL, + silcgaim_ftp_send_file_resolved, + strdup(name)); + silc_free(nickname); + return NULL; + } + + xfer = silc_calloc(1, sizeof(*xfer)); + + g_return_val_if_fail(xfer != NULL, NULL); + + xfer->sg = sg; + xfer->client_entry = clients[0]; + xfer->xfer = gaim_xfer_new(xfer->sg->account, GAIM_XFER_SEND, + xfer->client_entry->nickname); + if (!xfer->xfer) { + silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id); + g_free(xfer->hostname); + silc_free(xfer); + return NULL; + } + gaim_xfer_set_init_fnc(xfer->xfer, silcgaim_ftp_send); + gaim_xfer_set_request_denied_fnc(xfer->xfer, silcgaim_ftp_request_denied); + gaim_xfer_set_cancel_send_fnc(xfer->xfer, silcgaim_ftp_send_cancel); + xfer->xfer->data = xfer; + + silc_free(clients); + silc_free(nickname); + + return xfer->xfer; +} + +void silcgaim_ftp_send_file(GaimConnection *gc, const char *name, const char *file) +{ + GaimXfer *xfer = silcgaim_ftp_new_xfer(gc, name); + + g_return_if_fail(xfer != NULL); + + /* Choose file to send */ + if (file) + gaim_xfer_request_accepted(xfer, file); + else + gaim_xfer_request(xfer); +}