summaryrefslogtreecommitdiffstats
path: root/src/irc/dcc/dcc-resume.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 20:18:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 20:18:39 +0000
commitfff5217f02d91268ce90c8c05665602c059faaef (patch)
tree2ba24d32dc96eafe7ed0a85269548e76796d849d /src/irc/dcc/dcc-resume.c
parentInitial commit. (diff)
downloadirssi-upstream.tar.xz
irssi-upstream.zip
Adding upstream version 1.4.5.upstream/1.4.5upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/irc/dcc/dcc-resume.c')
-rw-r--r--src/irc/dcc/dcc-resume.c248
1 files changed, 248 insertions, 0 deletions
diff --git a/src/irc/dcc/dcc-resume.c b/src/irc/dcc/dcc-resume.c
new file mode 100644
index 0000000..dd62ff9
--- /dev/null
+++ b/src/irc/dcc/dcc-resume.c
@@ -0,0 +1,248 @@
+/*
+ dcc-resume.c : irssi
+
+ Copyright (C) 1999-2001 Timo Sirainen
+
+ 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 02110-1301 USA.
+*/
+
+#include "module.h"
+#include <irssi/src/core/signals.h>
+#include <irssi/src/core/commands.h>
+#include <irssi/src/core/network.h>
+#include <irssi/src/core/misc.h>
+
+#include <irssi/src/irc/dcc/dcc-file.h>
+#include <irssi/src/irc/dcc/dcc-get.h>
+#include <irssi/src/irc/dcc/dcc-send.h>
+#include <irssi/src/irc/dcc/dcc-chat.h>
+
+static FILE_DCC_REC *dcc_resume_find(int type, const char *nick, int port)
+{
+ GSList *tmp;
+
+ for (tmp = dcc_conns; tmp != NULL; tmp = tmp->next) {
+ FILE_DCC_REC *dcc = tmp->data;
+
+ if (dcc->type == type && !dcc_is_connected(dcc) &&
+ dcc->port == port &&
+ g_ascii_strcasecmp(dcc->nick, nick) == 0)
+ return dcc;
+ }
+
+ return NULL;
+}
+
+#define get_params_match_resume(params, pos) \
+ (is_numeric(params[pos], '\0') && atol(params[pos]) < 65536 && \
+ is_numeric(params[(pos)+1], '\0'))
+
+/* Based on get_file_params_count() found in dcc-get.c. The main difference
+ is represented by the number of params expected after the filename (2 at
+ least). I've added this new routine to avoid possible troubles connected
+ to relaxing the old checks done on DCC GET params to suite the ACCEPT/RESUME
+ needs.
+ */
+int get_file_params_count_resume(char **params, int paramcount)
+{
+ int pos, best;
+
+ if (*params[0] == '"') {
+ /* quoted file name? */
+ for (pos = 0; pos < paramcount-2; pos++) {
+ if (strlen(params[pos]) == 0)
+ continue;
+ if (params[pos][strlen(params[pos])-1] == '"' &&
+ get_params_match_resume(params, pos+1))
+ return pos+1;
+ }
+ }
+
+ best = paramcount-2;
+ for (pos = paramcount-2; pos > 0; pos--) {
+ if (get_params_match_resume(params, pos))
+ best = pos;
+ }
+
+ return best;
+}
+
+
+static int dcc_ctcp_resume_parse(int type, const char *data, const char *nick,
+ FILE_DCC_REC **dcc, uoff_t *size, int *pasv_id)
+{
+ char **params;
+ int paramcount, fileparams;
+ int port;
+
+ /* RESUME|ACCEPT <file name> <port> <size> */
+ /* RESUME|ACCEPT <file name> 0 <size> <id> (passive protocol) */
+ params = g_strsplit(data, " ", -1);
+ paramcount = g_strv_length(params);
+
+ if (paramcount < 3) {
+ g_strfreev(params);
+ return 0;
+ }
+
+ fileparams = get_file_params_count_resume(params, paramcount);
+
+ if (paramcount >= fileparams + 2) {
+ port = atoi(params[fileparams]);
+ *size = str_to_uofft(params[fileparams+1]);
+ *pasv_id = ((port == 0) && (paramcount == fileparams + 3)) ? atoi(params[fileparams+2]) : -1;
+ *dcc = dcc_resume_find(type, nick, port);
+ g_strfreev(params);
+
+ /* If the ID is different then the DCC cannot be resumed */
+ return ((*dcc != NULL) && ((*dcc)->pasv_id == *pasv_id));
+ }
+ g_strfreev(params);
+ return FALSE;
+}
+
+static int dcc_resume_file_check(FILE_DCC_REC *dcc, IRC_SERVER_REC *server,
+ uoff_t size)
+{
+ if (size >= dcc->size) {
+ /* whole file sent */
+ dcc->starttime = time(NULL);
+ dcc_reject(DCC(dcc), server);
+ } else if (lseek(dcc->fhandle, (off_t)size, SEEK_SET) != (off_t)size) {
+ /* error */
+ dcc_reject(DCC(dcc), server);
+ } else {
+ dcc->transfd = dcc->skipped = size;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* CTCP: DCC RESUME - requesting to resume DCC SEND */
+static void ctcp_msg_dcc_resume(IRC_SERVER_REC *server, const char *data,
+ const char *nick, const char *addr,
+ const char *target, DCC_REC *chat)
+{
+ FILE_DCC_REC *dcc;
+ char *str;
+ uoff_t size;
+ int pasv_id = -1;
+
+ if (!dcc_ctcp_resume_parse(DCC_SEND_TYPE, data, nick, &dcc, &size, &pasv_id)) {
+ signal_emit("dcc error ctcp", 5, "RESUME", data,
+ nick, addr, target);
+ } else if (dcc != NULL && dcc_resume_file_check(dcc, server, size)) {
+ if (!dcc_is_passive(dcc)) {
+ str = g_strdup_printf(DCC_SEND(dcc)->file_quoted ?
+ "DCC ACCEPT \"%s\" %d %"PRIuUOFF_T :
+ "DCC ACCEPT %s %d %"PRIuUOFF_T,
+ dcc->arg, dcc->port, dcc->transfd);
+ } else {
+ str = g_strdup_printf(DCC_SEND(dcc)->file_quoted ?
+ "DCC ACCEPT \"%s\" 0 %"PRIuUOFF_T" %d" :
+ "DCC ACCEPT %s 0 %"PRIuUOFF_T" %d",
+ dcc->arg, dcc->transfd, dcc->pasv_id);
+ }
+ dcc_ctcp_message(dcc->server, dcc->nick,
+ dcc->chat, FALSE, str);
+ g_free(str);
+ }
+}
+
+/* CTCP: DCC ACCEPT - accept resuming DCC GET */
+static void ctcp_msg_dcc_accept(IRC_SERVER_REC *server, const char *data,
+ const char *nick, const char *addr,
+ const char *target, DCC_REC *chat)
+{
+ FILE_DCC_REC *dcc;
+ uoff_t size;
+ int pasv_id;
+
+ if (!dcc_ctcp_resume_parse(DCC_GET_TYPE, data, nick, &dcc, &size, &pasv_id) ||
+ (dcc != NULL && DCC_GET(dcc)->get_type != DCC_GET_RESUME)) {
+ signal_emit("dcc error ctcp", 5, "ACCEPT", data,
+ nick, addr, target);
+ } else if (dcc != NULL && dcc_resume_file_check(dcc, server, size)) {
+ if (!dcc_is_passive(dcc))
+ dcc_get_connect(DCC_GET(dcc));
+ else
+ dcc_get_passive(DCC_GET(dcc));
+ }
+}
+
+/* Resume a DCC GET */
+static void dcc_send_resume(GET_DCC_REC *dcc)
+{
+ off_t pos;
+ char *str;
+
+ g_return_if_fail(dcc != NULL);
+
+ dcc->file = dcc_get_download_path(dcc->arg);
+ dcc->fhandle = open(dcc->file, O_WRONLY);
+ if (dcc->fhandle == -1) {
+ signal_emit("dcc error file open", 3, dcc->nick, dcc->file,
+ GINT_TO_POINTER(errno));
+ return;
+ }
+
+ dcc->get_type = DCC_GET_RESUME;
+
+ pos = lseek(dcc->fhandle, 0, SEEK_END);
+ dcc->transfd = pos < 0 ? 0 : (uoff_t)pos;
+ dcc->skipped = dcc->transfd;
+
+ if (dcc->skipped == dcc->size) {
+ /* already received whole file */
+ dcc->starttime = time(NULL);
+ dcc_reject(DCC(dcc), NULL);
+ } else {
+ if (!dcc_is_passive(dcc)) {
+ str = g_strdup_printf(dcc->file_quoted ?
+ "DCC RESUME \"%s\" %d %"PRIuUOFF_T :
+ "DCC RESUME %s %d %"PRIuUOFF_T,
+ dcc->arg, dcc->port, dcc->transfd);
+ } else {
+ str = g_strdup_printf(dcc->file_quoted ?
+ "DCC RESUME \"%s\" 0 %"PRIuUOFF_T" %d" :
+ "DCC RESUME %s 0 %"PRIuUOFF_T" %d",
+ dcc->arg, dcc->transfd, dcc->pasv_id);
+ }
+ dcc_ctcp_message(dcc->server, dcc->nick,
+ dcc->chat, FALSE, str);
+ g_free(str);
+ }
+}
+
+/* SYNTAX: DCC RESUME [<nick> [<file>]] */
+static void cmd_dcc_resume(const char *data)
+{
+ cmd_dcc_receive(data, dcc_send_resume, dcc_send_resume);
+}
+
+void dcc_resume_init(void)
+{
+ signal_add("ctcp msg dcc resume", (SIGNAL_FUNC) ctcp_msg_dcc_resume);
+ signal_add("ctcp msg dcc accept", (SIGNAL_FUNC) ctcp_msg_dcc_accept);
+ command_bind("dcc resume", NULL, (SIGNAL_FUNC) cmd_dcc_resume);
+}
+
+void dcc_resume_deinit(void)
+{
+ signal_remove("ctcp msg dcc resume", (SIGNAL_FUNC) ctcp_msg_dcc_resume);
+ signal_remove("ctcp msg dcc accept", (SIGNAL_FUNC) ctcp_msg_dcc_accept);
+ command_unbind("dcc resume", (SIGNAL_FUNC) cmd_dcc_resume);
+}