From be4b473be51c11d071803870aee90e470028b8ae Mon Sep 17 00:00:00 2001
From: Timo Sirainen <cras@irssi.org>
Date: Tue, 2 Jan 2001 16:14:19 +0000
Subject: [PATCH] /SET dcc_autoresume - like /SET dcc_autoget, but resume the
 files if they're already found. Also fixed file transfer being stuck when
 resuming file that was already fully sent (happened with both send and get).
 /dcc close was also broken.

git-svn-id: http://svn.irssi.org/repos/irssi/trunk@1051 dbcabf3a-b0e7-0310-adc4-f8d773084564
---
 src/irc/dcc/dcc-files.c | 67 ++++++++++++++++++++++++++++++++++++-----
 src/irc/dcc/dcc.c       | 38 ++++++++---------------
 src/irc/dcc/dcc.h       |  3 ++
 3 files changed, 74 insertions(+), 34 deletions(-)

diff --git a/src/irc/dcc/dcc-files.c b/src/irc/dcc/dcc-files.c
index 04e393d7..37bec340 100644
--- a/src/irc/dcc/dcc-files.c
+++ b/src/irc/dcc/dcc-files.c
@@ -297,10 +297,14 @@ static void dcc_ctcp_msg(IRC_SERVER_REC *server, const char *data,
 		return;
 	}
 
-	if (lseek(dcc->fhandle, size, SEEK_SET) != size) {
+	if (lseek(dcc->fhandle, 0, SEEK_END) == size) {
+		/* whole file sent */
+		dcc->starttime = time(NULL);
+		dcc_reject(dcc, server);
+	}
+	else if (lseek(dcc->fhandle, size, SEEK_SET) != size) {
 		/* error, or trying to seek after end of file */
-		signal_emit("dcc closed", 1, dcc);
-		dcc_destroy(dcc);
+		dcc_reject(dcc, server);
 	} else {
 		dcc->transfd = dcc->skipped = size;
 
@@ -319,20 +323,26 @@ static void dcc_resume_rec(DCC_REC *dcc)
 
 	g_return_if_fail(dcc != NULL);
 
-	dcc->get_type = DCC_GET_RESUME;
 	dcc->file = dcc_get_download_path(dcc->arg);
-
 	dcc->fhandle = open(dcc->file, O_WRONLY, dcc_file_create_mode);
 	if (dcc->fhandle == -1) {
 		signal_emit("dcc error file not found", 2, dcc, dcc->file);
-		dcc_destroy(dcc);
 		return;
 	}
 
+	dcc->get_type = DCC_GET_RESUME;
+
 	dcc->transfd = lseek(dcc->fhandle, 0, SEEK_END);
 	if (dcc->transfd < 0) dcc->transfd = 0;
 	dcc->skipped = dcc->transfd;
 
+	if (dcc->skipped == dcc->size) {
+		/* already received whole file */
+		dcc->starttime = time(NULL);
+		dcc_reject(dcc, NULL);
+                return;
+	}
+
 	str = g_strdup_printf("DCC RESUME %s %d %lu",
 			      dcc->arg, dcc->port, dcc->transfd);
 	dcc_ctcp_message(dcc->server, dcc->nick, dcc->chat, FALSE, str);
@@ -343,7 +353,7 @@ static void dcc_resume_rec(DCC_REC *dcc)
 static void cmd_dcc_resume(const char *data)
 {
 	DCC_REC *dcc;
-	GSList *tmp;
+	GSList *tmp, *next;
 	char *nick, *fname;
 	void *free_arg;
 	int found;
@@ -355,9 +365,10 @@ static void cmd_dcc_resume(const char *data)
 	if (*nick == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
 
 	dcc = NULL; found = FALSE;
-	for (tmp = dcc_conns; tmp != NULL; tmp = tmp->next) {
+	for (tmp = dcc_conns; tmp != NULL; tmp = next) {
 		dcc = tmp->data;
 
+                next = tmp->next;
 		if (dcc_is_unget(dcc) && g_strcasecmp(dcc->nick, nick) == 0 &&
 		    (*fname == '\0' || strcmp(dcc->arg, fname) == 0)) {
 			dcc_resume_rec(dcc);
@@ -589,6 +600,44 @@ static void cmd_dcc_send(const char *data, IRC_SERVER_REC *server, void *item)
 	cmd_params_free(free_arg);
 }
 
+static void sig_dcc_request(DCC_REC *dcc, const char *nickaddr)
+{
+        struct stat statbuf;
+	const char *masks;
+        char *str, *file;
+        int max_size;
+
+	g_return_if_fail(dcc != NULL);
+	if (dcc->type != DCC_TYPE_GET) return;
+
+	/* check if we want to autoget file offer */
+	if (!settings_get_bool("dcc_autoget") &&
+	    !settings_get_bool("dcc_autoresume"))
+		return;
+
+	/* check that autoget masks match */
+	masks = settings_get_str("dcc_autoget_masks");
+	if (*masks != '\0' &&
+	    !masks_match(SERVER(dcc->server), masks, dcc->nick, nickaddr))
+		return;
+
+	/* check file size limit, FIXME: it's still possible to send a
+	   bogus file size and then just send what ever sized file.. */
+        max_size = settings_get_int("dcc_max_autoget_size");
+	if (max_size > 0 && max_size*1024 < dcc->size)
+                return;
+
+	/* ok. but do we want/need to resume? */
+	file = dcc_get_download_path(dcc->arg);
+	str = g_strdup_printf(settings_get_bool("dcc_autoresume") &&
+			      stat(file, &statbuf) == 0 ?
+			      "RESUME %s %s" : "GET %s %s",
+			      dcc->nick, dcc->arg);
+	signal_emit("command dcc", 2, str, dcc->server);
+        g_free(file);
+	g_free(str);
+}
+
 static void read_settings(void)
 {
 	dcc_file_create_mode = octal2dec(settings_get_int("dcc_file_create_mode"));
@@ -598,6 +647,7 @@ void dcc_files_init(void)
 {
 	signal_add("ctcp msg dcc", (SIGNAL_FUNC) dcc_ctcp_msg);
 	signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+	signal_add_last("dcc request", (SIGNAL_FUNC) sig_dcc_request);
 	signal_add("irssi init finished", (SIGNAL_FUNC) read_settings);
 	command_bind("dcc send", NULL, (SIGNAL_FUNC) cmd_dcc_send);
 	command_bind("dcc get", NULL, (SIGNAL_FUNC) cmd_dcc_get);
@@ -608,6 +658,7 @@ void dcc_files_deinit(void)
 {
 	signal_remove("ctcp msg dcc", (SIGNAL_FUNC) dcc_ctcp_msg);
 	signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+	signal_remove("dcc request", (SIGNAL_FUNC) sig_dcc_request);
 	signal_remove("irssi init finished", (SIGNAL_FUNC) read_settings);
 	command_unbind("dcc send", (SIGNAL_FUNC) cmd_dcc_send);
 	command_unbind("dcc get", (SIGNAL_FUNC) cmd_dcc_get);
diff --git a/src/irc/dcc/dcc.c b/src/irc/dcc/dcc.c
index de8601d5..1d0fc2dc 100644
--- a/src/irc/dcc/dcc.c
+++ b/src/irc/dcc/dcc.c
@@ -287,7 +287,7 @@ static void dcc_ctcp_msg(IRC_SERVER_REC *server, char *data,
 {
     char *type, *arg, *addrstr, *portstr, *sizestr, *str;
     void *free_arg;
-    const char *cstr;
+    const char *masks;
     DCC_REC *dcc, *olddcc;
     long size;
     int dcctype, port;
@@ -331,39 +331,23 @@ static void dcc_ctcp_msg(IRC_SERVER_REC *server, char *data,
     switch (dcc->type)
     {
 	case DCC_TYPE_GET:
-	    cstr = settings_get_str("dcc_autoget_masks");
-	    /* check that autoget masks match */
-	    if (settings_get_bool("dcc_autoget") && (*cstr == '\0' || masks_match(SERVER(server), cstr, sender, sendaddr)) &&
-                /* check file size limit, FIXME: it's possible to send a bogus file size and then just send what ever sized file.. */
-		(settings_get_int("dcc_max_autoget_size") <= 0 || (settings_get_int("dcc_max_autoget_size") > 0 && size <= settings_get_int("dcc_max_autoget_size")*1024)))
-            {
-                /* automatically get */
-                str = g_strdup_printf("GET %s %s", dcc->nick, dcc->arg);
-                signal_emit("command dcc", 2, str, server);
-                g_free(str);
-            }
-            else
-            {
-                /* send request */
-                signal_emit("dcc request", 1, dcc);
-            }
+	    /* send request */
+	    signal_emit("dcc request", 2, dcc, sendaddr);
             break;
 
 	case DCC_TYPE_CHAT:
-	    cstr = settings_get_str("dcc_autochat_masks");
+	    /* send request */
+	    signal_emit("dcc request", 2, dcc, sendaddr);
+
+	    masks = settings_get_str("dcc_autochat_masks");
 	    if (olddcc != NULL ||
-		(*cstr != '\0' && masks_match(SERVER(server), cstr, sender, sendaddr)))
+		(*masks != '\0' && masks_match(SERVER(server), masks, sender, sendaddr)))
 	    {
                 /* automatically accept chat */
                 str = g_strdup_printf("CHAT %s", dcc->nick);
                 signal_emit("command dcc", 2, str, server);
                 g_free(str);
 	    }
-	    else
-	    {
-		/* send request */
-		signal_emit("dcc request", 1, dcc);
-	    }
 	    break;
 
 	case DCC_TYPE_RESUME:
@@ -416,7 +400,8 @@ static void dcc_ctcp_reply(IRC_SERVER_REC *server, char *data,
     cmd_params_free(free_arg);
 }
 
-static void dcc_reject(DCC_REC *dcc, IRC_SERVER_REC *server)
+/* reject DCC request */
+void dcc_reject(DCC_REC *dcc, IRC_SERVER_REC *server)
 {
     char *str;
 
@@ -438,7 +423,7 @@ static void dcc_reject(DCC_REC *dcc, IRC_SERVER_REC *server)
 }
 
 /* SYNTAX: DCC CLOSE <type> <nick> [<file>] */
-static void cmd_dcc_close(IRC_SERVER_REC *server, char *data)
+static void cmd_dcc_close(char *data, IRC_SERVER_REC *server)
 {
     DCC_REC *dcc;
     GSList *tmp, *next;
@@ -540,6 +525,7 @@ void irc_dcc_init(void)
 
     settings_add_bool("dcc", "dcc_autorename", FALSE);
     settings_add_bool("dcc", "dcc_autoget", FALSE);
+    settings_add_bool("dcc", "dcc_autoresume", FALSE);
     settings_add_int("dcc", "dcc_max_autoget_size", 1000);
     settings_add_str("dcc", "dcc_download_path", "~");
     settings_add_int("dcc", "dcc_file_create_mode", 644);
diff --git a/src/irc/dcc/dcc.h b/src/irc/dcc/dcc.h
index 8aef0c82..686f65a5 100644
--- a/src/irc/dcc/dcc.h
+++ b/src/irc/dcc/dcc.h
@@ -94,4 +94,7 @@ void dcc_chat_send(DCC_REC *dcc, const char *data);
 /* If `item' is a query of a =nick, return DCC chat record of nick */
 DCC_REC *item_get_dcc(void *item);
 
+/* reject DCC request */
+void dcc_reject(DCC_REC *dcc, IRC_SERVER_REC *server);
+
 #endif