diff options
-rw-r--r-- | docs/help/in/dcc.in | 6 | ||||
-rw-r--r-- | src/fe-common/irc/dcc/fe-dcc.c | 180 | ||||
-rw-r--r-- | src/fe-common/irc/dcc/module-formats.c | 4 | ||||
-rw-r--r-- | src/fe-common/irc/dcc/module-formats.h | 4 | ||||
-rw-r--r-- | src/irc/dcc/Makefile.am | 7 | ||||
-rw-r--r-- | src/irc/dcc/dcc-autoget.c | 84 | ||||
-rw-r--r-- | src/irc/dcc/dcc-chat.c | 186 | ||||
-rw-r--r-- | src/irc/dcc/dcc-chat.h | 11 | ||||
-rw-r--r-- | src/irc/dcc/dcc-files.c | 667 | ||||
-rw-r--r-- | src/irc/dcc/dcc-get.c | 385 | ||||
-rw-r--r-- | src/irc/dcc/dcc-get.h | 28 | ||||
-rw-r--r-- | src/irc/dcc/dcc-resume.c | 177 | ||||
-rw-r--r-- | src/irc/dcc/dcc-send.c | 267 | ||||
-rw-r--r-- | src/irc/dcc/dcc.c | 543 | ||||
-rw-r--r-- | src/irc/dcc/dcc.h | 38 |
15 files changed, 1511 insertions, 1076 deletions
diff --git a/docs/help/in/dcc.in b/docs/help/in/dcc.in index d30a4e68..eb9caa34 100644 --- a/docs/help/in/dcc.in +++ b/docs/help/in/dcc.in @@ -7,12 +7,12 @@ files. /DCC LIST - Shows all the open DCC connections. -/DCC RESUME <nick> [<file>] +/DCC RESUME [<nick> [<file>]] - Resumes a DCC SEND/GET connection. -/DCC CHAT <nick> +/DCC CHAT [<nick>] - Sends a chat connection request to remote client or accepts a chat connection if the remote end has already sent a request. -/DCC GET <nick> [<file>] +/DCC GET [<nick> [<file>]] - Gets the file offered by remote client. The file is downloaded and saved into the current working directory. /DCC SEND <nick> <file> diff --git a/src/fe-common/irc/dcc/fe-dcc.c b/src/fe-common/irc/dcc/fe-dcc.c index ffe7f6ed..acb322c8 100644 --- a/src/fe-common/irc/dcc/fe-dcc.c +++ b/src/fe-common/irc/dcc/fe-dcc.c @@ -29,7 +29,7 @@ #include "channels.h" #include "irc-queries.h" -#include "irc/dcc/dcc.h" +#include "irc/dcc/dcc-chat.h" #include "completion.h" #include "themes.h" @@ -49,20 +49,23 @@ static void dcc_connected(DCC_REC *dcc) switch (dcc->type) { case DCC_TYPE_CHAT: - sender = g_strconcat("=", dcc->nick, NULL); - printformat(dcc->server, NULL, MSGLEVEL_DCC, IRCTXT_DCC_CHAT_CONNECTED, - dcc->nick, dcc->addrstr, dcc->port); + sender = g_strconcat("=", dcc->chat_id, NULL); + printformat(dcc->server, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_CHAT_CONNECTED, + dcc->chat_id, dcc->addrstr, dcc->port); if (autocreate_dccquery && query_find(NULL, sender) == NULL) irc_query_create(dcc->server == NULL ? NULL : dcc->server->tag, sender, TRUE); g_free(sender); break; case DCC_TYPE_SEND: - printformat(dcc->server, NULL, MSGLEVEL_DCC, IRCTXT_DCC_SEND_CONNECTED, + printformat(dcc->server, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_SEND_CONNECTED, dcc->arg, dcc->nick, dcc->addrstr, dcc->port); break; case DCC_TYPE_GET: - printformat(dcc->server, NULL, MSGLEVEL_DCC, IRCTXT_DCC_GET_CONNECTED, + printformat(dcc->server, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_GET_CONNECTED, dcc->arg, dcc->nick, dcc->addrstr, dcc->port); break; } @@ -85,33 +88,40 @@ static void dcc_closed(DCC_REC *dcc) g_return_if_fail(dcc != NULL); secs = dcc->starttime == 0 ? -1 : time(NULL)-dcc->starttime; - kbs = (double) (dcc->transfd-dcc->skipped) / (secs == 0 ? 1 : secs) / 1024.0; + kbs = (double) (dcc->transfd-dcc->skipped) / + (secs == 0 ? 1 : secs) / 1024.0; switch (dcc->type) { case DCC_TYPE_CHAT: - sender = g_strconcat("=", dcc->nick, NULL); + sender = g_strconcat("=", dcc->chat_id, NULL); printformat(dcc->server, NULL, MSGLEVEL_DCC, - IRCTXT_DCC_CHAT_DISCONNECTED, dcc->nick); + IRCTXT_DCC_CHAT_DISCONNECTED, dcc->chat_id); g_free(sender); break; case DCC_TYPE_SEND: if (secs == -1) { /* aborted */ - printformat(dcc->server, NULL, MSGLEVEL_DCC, IRCTXT_DCC_SEND_ABORTED, + printformat(dcc->server, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_SEND_ABORTED, dcc->arg, dcc->nick); } else { - printformat(dcc->server, NULL, MSGLEVEL_DCC, IRCTXT_DCC_SEND_COMPLETE, - dcc->arg, dcc->transfd/1024, dcc->nick, (long) secs, kbs); + printformat(dcc->server, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_SEND_COMPLETE, + dcc->arg, dcc->transfd/1024, dcc->nick, + (long) secs, kbs); } break; case DCC_TYPE_GET: if (secs == -1) { /* aborted */ - printformat(dcc->server, NULL, MSGLEVEL_DCC, IRCTXT_DCC_GET_ABORTED, + printformat(dcc->server, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_GET_ABORTED, dcc->arg, dcc->nick); } else { - printformat(dcc->server, NULL, MSGLEVEL_DCC, IRCTXT_DCC_GET_COMPLETE, - dcc->arg, dcc->transfd/1024, dcc->nick, (long) secs, kbs); + printformat(dcc->server, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_GET_COMPLETE, + dcc->arg, dcc->transfd/1024, dcc->nick, + (long) secs, kbs); } break; } @@ -124,9 +134,9 @@ static void dcc_chat_action(const char *msg, DCC_REC *dcc) g_return_if_fail(dcc != NULL); g_return_if_fail(msg != NULL); - sender = g_strconcat("=", dcc->nick, NULL); + sender = g_strconcat("=", dcc->chat_id, NULL); printformat(NULL, sender, MSGLEVEL_DCCMSGS, - IRCTXT_ACTION_DCC, dcc->nick, msg); + IRCTXT_ACTION_DCC, dcc->chat_id, msg); g_free(sender); } @@ -137,8 +147,9 @@ static void dcc_chat_ctcp(const char *msg, DCC_REC *dcc) g_return_if_fail(dcc != NULL); g_return_if_fail(msg != NULL); - sender = g_strconcat("=", dcc->nick, NULL); - printformat(NULL, sender, MSGLEVEL_DCC, IRCTXT_DCC_CTCP, dcc->nick, msg); + sender = g_strconcat("=", dcc->chat_id, NULL); + printformat(NULL, sender, MSGLEVEL_DCC, + IRCTXT_DCC_CTCP, dcc->chat_id, msg); g_free(sender); } @@ -150,7 +161,7 @@ static void dcc_chat_msg(DCC_REC *dcc, const char *msg) g_return_if_fail(dcc != NULL); g_return_if_fail(msg != NULL); - sender = g_strconcat("=", dcc->nick, NULL); + sender = g_strconcat("=", dcc->chat_id, NULL); query = query_find(NULL, sender); if (settings_get_bool("emphasis")) @@ -160,7 +171,7 @@ static void dcc_chat_msg(DCC_REC *dcc, const char *msg) printformat(NULL, sender, MSGLEVEL_DCCMSGS, query != NULL ? IRCTXT_DCC_MSG_QUERY : - IRCTXT_DCC_MSG, dcc->nick, msg); + IRCTXT_DCC_MSG, dcc->chat_id, msg); g_free_not_null(freemsg); g_free(sender); } @@ -171,14 +182,26 @@ static void dcc_request(DCC_REC *dcc) switch (dcc->type) { case DCC_TYPE_CHAT: - printformat(dcc->server, NULL, MSGLEVEL_DCC, IRCTXT_DCC_CHAT, - dcc->nick, dcc->addrstr, dcc->port); + printformat(dcc->server, NULL, MSGLEVEL_DCC, + ischannel(*dcc->target) ? IRCTXT_DCC_CHAT_CHANNEL : + IRCTXT_DCC_CHAT, dcc->chat_id, dcc->addrstr, + dcc->port, dcc->target); break; case DCC_TYPE_GET: - printformat(dcc->server, NULL, MSGLEVEL_DCC, IRCTXT_DCC_SEND, - dcc->nick, dcc->addrstr, dcc->port, dcc->arg, dcc->size); + printformat(dcc->server, NULL, MSGLEVEL_DCC, + ischannel(*dcc->target) ? IRCTXT_DCC_SEND_CHANNEL : + IRCTXT_DCC_SEND, dcc->nick, dcc->addrstr, + dcc->port, dcc->arg, dcc->size, dcc->target); break; } + + if (dcc->port < 1024) { + char *service = net_getservbyport(dcc->port); + + printformat(dcc->server, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_LOWPORT, dcc->port, + service != NULL ? service : "unknown"); + } } static void dcc_error_connect(DCC_REC *dcc) @@ -201,7 +224,8 @@ static void dcc_error_file_not_found(const char *nick, const char *fname) g_return_if_fail(nick != NULL); g_return_if_fail(fname != NULL); - printformat(NULL, NULL, MSGLEVEL_DCC, IRCTXT_DCC_SEND_FILE_NOT_FOUND, fname); + printformat(NULL, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_SEND_FILE_NOT_FOUND, fname); } static void dcc_error_get_not_found(const char *nick) @@ -216,7 +240,8 @@ static void dcc_error_send_exists(const char *nick, const char *fname) g_return_if_fail(nick != NULL); g_return_if_fail(fname != NULL); - printformat(NULL, NULL, MSGLEVEL_DCC, IRCTXT_DCC_SEND_EXISTS, fname, nick); + printformat(NULL, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_SEND_EXISTS, fname, nick); } static void dcc_error_unknown_type(const char *type) @@ -226,7 +251,8 @@ static void dcc_error_unknown_type(const char *type) printformat(NULL, NULL, MSGLEVEL_DCC, IRCTXT_DCC_UNKNOWN_TYPE, type); } -static void dcc_error_close_not_found(const char *type, const char *nick, const char *fname) +static void dcc_error_close_not_found(const char *type, const char *nick, + const char *fname) { g_return_if_fail(type != NULL); g_return_if_fail(nick != NULL); @@ -235,40 +261,60 @@ static void dcc_error_close_not_found(const char *type, const char *nick, const if (fname == '\0') fname = "(ANY)"; switch (dcc_str2type(type)) { case DCC_TYPE_CHAT: - printformat(NULL, NULL, MSGLEVEL_DCC, IRCTXT_DCC_CHAT_NOT_FOUND, nick); + printformat(NULL, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_CHAT_NOT_FOUND, nick); break; case DCC_TYPE_SEND: - printformat(NULL, NULL, MSGLEVEL_DCC, IRCTXT_DCC_SEND_NOT_FOUND, nick, fname); + printformat(NULL, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_SEND_NOT_FOUND, nick, fname); break; case DCC_TYPE_GET: - printformat(NULL, NULL, MSGLEVEL_DCC, IRCTXT_DCC_GET_NOT_FOUND, nick, fname); + printformat(NULL, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_GET_NOT_FOUND, nick, fname); break; } } -static void dcc_unknown_ctcp(const char *data, const char *sender) +static void dcc_error_ctcp(const char *type, const char *data, + const char *nick, const char *addr, + const char *target) +{ + printformat(NULL, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_INVALID_CTCP, type, nick, addr, target); +} + +static void dcc_unknown_ctcp(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr, + const char *target, DCC_REC *chat) { char *type, *args; void *free_arg; g_return_if_fail(data != NULL); - if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &type, &args)) + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, + &type, &args)) return; - printformat(NULL, NULL, MSGLEVEL_DCC, IRCTXT_DCC_UNKNOWN_CTCP, type, sender, args); + + printformat(NULL, NULL, MSGLEVEL_DCC, IRCTXT_DCC_UNKNOWN_CTCP, + type, nick, args); cmd_params_free(free_arg); } -static void dcc_unknown_reply(const char *data, const char *sender) +static void dcc_unknown_reply(IRC_SERVER_REC *server, const char *data, + const char *nick) { char *type, *args; void *free_arg; g_return_if_fail(data != NULL); - if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &type, &args)) + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, + &type, &args)) return; - printformat(NULL, NULL, MSGLEVEL_DCC, IRCTXT_DCC_UNKNOWN_REPLY, type, sender, args); + + printformat(NULL, NULL, MSGLEVEL_DCC, IRCTXT_DCC_UNKNOWN_REPLY, + type, nick, args); cmd_params_free(free_arg); } @@ -280,7 +326,7 @@ static void sig_dcc_destroyed(DCC_REC *dcc) if (dcc->type != DCC_TYPE_CHAT) return; - nick = g_strconcat("=", dcc->nick, NULL); + nick = g_strconcat("=", dcc->chat_id, NULL); query = query_find(NULL, nick); g_free(nick); @@ -298,7 +344,7 @@ static void sig_query_destroyed(QUERY_REC *query) if (*query->name != '=') return; - dcc = dcc_find_item(DCC_TYPE_CHAT, query->name+1, NULL); + dcc = dcc_chat_find_id(query->name+1); if (dcc != NULL && !dcc->destroyed) { /* DCC query window closed, close the dcc chat too. */ signal_emit("dcc closed", 1, dcc); @@ -319,10 +365,11 @@ static void cmd_msg(const char *data) return; } - if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &target, &text)) + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, + &target, &text)) return; - dcc = dcc_find_item(DCC_TYPE_CHAT, target+1, NULL); + dcc = dcc_chat_find_id(target+1); if (dcc == NULL || dcc->sendbuf == NULL) { printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, IRCTXT_DCC_CHAT_NOT_FOUND, target+1); @@ -361,11 +408,13 @@ static void cmd_action(const char *data, SERVER_REC *server, WI_ITEM_REC *item) return; } - if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &target, &text)) + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, + &target, &text)) return; - if (*target == '\0' || *text == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + if (*target == '\0' || *text == '\0') + cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); - dcc = dcc_find_item(DCC_TYPE_CHAT, target+1, NULL); + dcc = dcc_chat_find_id(target+1); if (dcc == NULL || dcc->sendbuf == NULL) { printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, IRCTXT_DCC_CHAT_NOT_FOUND, target+1); @@ -383,11 +432,14 @@ static void cmd_ctcp(const char *data, SERVER_REC *server) void *free_arg; g_return_if_fail(data != NULL); - if (server == NULL || !server->connected) cmd_return_error(CMDERR_NOT_CONNECTED); + if (server == NULL || !server->connected) + cmd_return_error(CMDERR_NOT_CONNECTED); - if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST, &target, &ctcpcmd, &ctcpdata)) + if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST, + &target, &ctcpcmd, &ctcpdata)) return; - if (*target == '\0' || *ctcpcmd == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + if (*target == '\0' || *ctcpcmd == '\0') + cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); if (*target != '=') { /* handle only DCC CTCPs */ @@ -395,7 +447,7 @@ static void cmd_ctcp(const char *data, SERVER_REC *server) return; } - dcc = dcc_find_item(DCC_TYPE_CHAT, target+1, NULL); + dcc = dcc_chat_find_id(target+1); if (dcc == NULL || dcc->sendbuf == NULL) { printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, IRCTXT_DCC_CHAT_NOT_FOUND, target+1); @@ -422,13 +474,18 @@ static void cmd_dcc_list(const char *data) going = time(NULL) - dcc->starttime; if (going == 0) going = 1; /* no division by zeros :) */ - if (dcc->type == DCC_TYPE_CHAT) - printformat(NULL, NULL, MSGLEVEL_DCC, IRCTXT_DCC_LIST_LINE_CHAT, dcc->nick, dcc_type2str(dcc->type)); - else - printformat(NULL, NULL, MSGLEVEL_DCC, IRCTXT_DCC_LIST_LINE_FILE, - dcc->nick, dcc_type2str(dcc->type), dcc->transfd/1024, dcc->size/1024, - dcc->size == 0 ? 0 : (int)((double)dcc->transfd/(double)dcc->size*100.0), - (double) (dcc->transfd-dcc->skipped)/going/1024, dcc->arg); + if (dcc->type == DCC_TYPE_CHAT) { + printformat(NULL, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_LIST_LINE_CHAT, + dcc->chat_id, dcc_type2str(dcc->type)); + } else { + printformat(NULL, NULL, MSGLEVEL_DCC, + IRCTXT_DCC_LIST_LINE_FILE, + dcc->nick, dcc_type2str(dcc->type), + dcc->transfd/1024, dcc->size/1024, + dcc->size == 0 ? 0 : (int)((double)dcc->transfd/(double)dcc->size*100.0), + (double) (dcc->transfd-dcc->skipped)/going/1024, dcc->arg); + } } printformat(NULL, NULL, MSGLEVEL_DCC, IRCTXT_DCC_LIST_FOOTER); } @@ -442,7 +499,8 @@ static void cmd_dcc(const char *data) } static void sig_dcc_send_complete(GList **list, WINDOW_REC *window, - const char *word, const char *line, int *want_space) + const char *word, const char *line, + int *want_space) { g_return_if_fail(list != NULL); g_return_if_fail(word != NULL); @@ -483,8 +541,9 @@ void fe_irc_dcc_init(void) signal_add("dcc error send exists", (SIGNAL_FUNC) dcc_error_send_exists); signal_add("dcc error unknown type", (SIGNAL_FUNC) dcc_error_unknown_type); signal_add("dcc error close not found", (SIGNAL_FUNC) dcc_error_close_not_found); - signal_add("dcc unknown ctcp", (SIGNAL_FUNC) dcc_unknown_ctcp); - signal_add("dcc unknown reply", (SIGNAL_FUNC) dcc_unknown_reply); + signal_add("dcc error ctcp", (SIGNAL_FUNC) dcc_error_ctcp); + signal_add("default ctcp msg dcc", (SIGNAL_FUNC) dcc_unknown_ctcp); + signal_add("default ctcp reply dcc", (SIGNAL_FUNC) dcc_unknown_reply); signal_add("dcc destroyed", (SIGNAL_FUNC) sig_dcc_destroyed); signal_add("query destroyed", (SIGNAL_FUNC) sig_query_destroyed); signal_add("complete command dcc send", (SIGNAL_FUNC) sig_dcc_send_complete); @@ -518,8 +577,9 @@ void fe_irc_dcc_deinit(void) signal_remove("dcc error send exists", (SIGNAL_FUNC) dcc_error_send_exists); signal_remove("dcc error unknown type", (SIGNAL_FUNC) dcc_error_unknown_type); signal_remove("dcc error close not found", (SIGNAL_FUNC) dcc_error_close_not_found); - signal_remove("dcc unknown ctcp", (SIGNAL_FUNC) dcc_unknown_ctcp); - signal_remove("dcc unknown reply", (SIGNAL_FUNC) dcc_unknown_reply); + signal_remove("dcc error ctcp", (SIGNAL_FUNC) dcc_error_ctcp); + signal_remove("default ctcp msg dcc", (SIGNAL_FUNC) dcc_unknown_ctcp); + signal_remove("default ctcp reply dcc", (SIGNAL_FUNC) dcc_unknown_reply); signal_remove("dcc destroyed", (SIGNAL_FUNC) sig_dcc_destroyed); signal_remove("query destroyed", (SIGNAL_FUNC) sig_query_destroyed); signal_remove("complete command dcc send", (SIGNAL_FUNC) sig_dcc_send_complete); diff --git a/src/fe-common/irc/dcc/module-formats.c b/src/fe-common/irc/dcc/module-formats.c index e1e32930..57ddda63 100644 --- a/src/fe-common/irc/dcc/module-formats.c +++ b/src/fe-common/irc/dcc/module-formats.c @@ -36,10 +36,12 @@ FORMAT_REC fecommon_irc_dcc_formats[] = { { "dcc_msg_query", "{privmsgnick $0}$1", 2, { 0, 0 } }, { "dcc_ctcp", "{dcc >>> DCC CTCP received from {hilight $0}: $1}", 2, { 0, 0 } }, { "dcc_chat", "{dcc DCC CHAT from {nick $0} [$1 port $2]}", 3, { 0, 0, 1 } }, + { "dcc_chat_channel", "{dcc DCC CHAT from {nick $0} [$1 port $2] requested in channel {channel $3}}", 4, { 0, 0, 1, 0 } }, { "dcc_chat_not_found", "{dcc No DCC CHAT connection open to {nick $0}}", 1, { 0 } }, { "dcc_chat_connected", "{dcc DCC CHAT connection with {nick $0} [$1 port $2] established}", 3, { 0, 0, 1 } }, { "dcc_chat_disconnected", "{dcc DCC lost chat to {nick $0}}", 1, { 0 } }, { "dcc_send", "{dcc DCC SEND from {nick $0} [$1 port $2]: $3 [$4 bytes]}", 5, { 0, 0, 1, 0, 2 } }, + { "dcc_send_channel", "{dcc DCC SEND from {nick $0} [$1 port $2]: $3 [$4 bytes] requested in channel {channel $5}}", 6, { 0, 0, 1, 0, 2, 0 } }, { "dcc_send_exists", "{dcc DCC already sending file {dccfile $0} for {nick $1}}", 2, { 0, 0 } }, { "dcc_send_not_found", "{dcc DCC not sending file {dccfile $1} to {nick $0}}", 2, { 0, 0 } }, { "dcc_send_file_not_found", "{dcc DCC file not found: {dccfile $0}}", 1, { 0 } }, @@ -53,10 +55,12 @@ FORMAT_REC fecommon_irc_dcc_formats[] = { { "dcc_unknown_ctcp", "{dcc DCC unknown ctcp {hilight $0} from {nick $1} [$2]}", 3, { 0, 0, 0 } }, { "dcc_unknown_reply", "{dcc DCC unknown reply {hilight $0} from {nick $1} [$2]}", 3, { 0, 0, 0 } }, { "dcc_unknown_type", "{dcc DCC unknown type {hilight $0}}", 1, { 0 } }, + { "dcc_invalid_ctcp", "{dcc DCC received CTCP {hilight $0} with invalid parameters from {nick $1}}", 4, { 0, 0, 0, 0 } }, { "dcc_connect_error", "{dcc DCC can't connect to {hilight $0} port {hilight $1}}", 2, { 0, 1 } }, { "dcc_cant_create", "{dcc DCC can't create file {dccfile $0}}", 1, { 0 } }, { "dcc_rejected", "{dcc DCC $0 was rejected by {nick $1} [{hilight $2}]}", 3, { 0, 0, 0 } }, { "dcc_close", "{dcc DCC $0 close for {nick $1} [{hilight $2}]}", 3, { 0, 0, 0 } }, + { "dcc_lowport", "{dcc Warning: Port sent with DCC request is a lowport ({hilight $0, $1}) - this isn't normal. It is possible the address/port is faked (or maybe someone is just trying to bypass firewall)}", 2, { 1, 0 } }, { "dcc_list_header", "{dcc DCC connections}", 0 }, { "dcc_list_line_chat", "{dcc $0 $1}", 2, { 0, 0 } }, { "dcc_list_line_file", "{dcc $0 $1: $2k of $3k ($4%%) - $5kB/s - $6}", 7, { 0, 0, 2, 2, 1, 3, 0 } }, diff --git a/src/fe-common/irc/dcc/module-formats.h b/src/fe-common/irc/dcc/module-formats.h index f1a89e62..3cbb89da 100644 --- a/src/fe-common/irc/dcc/module-formats.h +++ b/src/fe-common/irc/dcc/module-formats.h @@ -14,10 +14,12 @@ enum { IRCTXT_DCC_MSG_QUERY, IRCTXT_DCC_CTCP, IRCTXT_DCC_CHAT, + IRCTXT_DCC_CHAT_CHANNEL, IRCTXT_DCC_CHAT_NOT_FOUND, IRCTXT_DCC_CHAT_CONNECTED, IRCTXT_DCC_CHAT_DISCONNECTED, IRCTXT_DCC_SEND, + IRCTXT_DCC_SEND_CHANNEL, IRCTXT_DCC_SEND_EXISTS, IRCTXT_DCC_SEND_NOT_FOUND, IRCTXT_DCC_SEND_FILE_NOT_FOUND, @@ -31,10 +33,12 @@ enum { IRCTXT_DCC_UNKNOWN_CTCP, IRCTXT_DCC_UNKNOWN_REPLY, IRCTXT_DCC_UNKNOWN_TYPE, + IRCTXT_DCC_INVALID_CTCP, IRCTXT_DCC_CONNECT_ERROR, IRCTXT_DCC_CANT_CREATE, IRCTXT_DCC_REJECTED, IRCTXT_DCC_CLOSE, + IRCTXT_DCC_LOWPORT, IRCTXT_DCC_LIST_HEADER, IRCTXT_DCC_LIST_LINE_CHAT, IRCTXT_DCC_LIST_LINE_FILE, diff --git a/src/irc/dcc/Makefile.am b/src/irc/dcc/Makefile.am index 91250034..d8274173 100644 --- a/src/irc/dcc/Makefile.am +++ b/src/irc/dcc/Makefile.am @@ -9,8 +9,13 @@ INCLUDES = $(GLIB_CFLAGS) \ libirc_dcc_la_SOURCES = \ dcc.c \ dcc-chat.c \ - dcc-files.c + dcc-get.c \ + dcc-send.c \ + dcc-resume.c \ + dcc-autoget.c noinst_HEADERS = \ dcc.h \ + dcc-chat.h \ + dcc-get.h \ module.h diff --git a/src/irc/dcc/dcc-autoget.c b/src/irc/dcc/dcc-autoget.c new file mode 100644 index 00000000..cb25ecf8 --- /dev/null +++ b/src/irc/dcc/dcc-autoget.c @@ -0,0 +1,84 @@ +/* + dcc-autoget.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" +#include "masks.h" +#include "settings.h" + +#include "dcc-get.h" + +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 for lowports */ + if (dcc->port < 1024 && !settings_get_bool("dcc_autoget_lowports")) + 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, NOTE: it's still possible to send a + bogus file size and then just send what ever sized file.. */ + max_size = settings_get_int("dcc_autoget_max_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); +} + +void dcc_autoget_init(void) +{ + settings_add_bool("dcc", "dcc_autoget", FALSE); + settings_add_bool("dcc", "dcc_autoget_lowports", FALSE); + settings_add_bool("dcc", "dcc_autoresume", FALSE); + settings_add_int("dcc", "dcc_autoget_max_size", 1000); + settings_add_str("dcc", "dcc_autoget_masks", ""); + + signal_add_last("dcc request", (SIGNAL_FUNC) sig_dcc_request); +} + +void dcc_autoget_deinit(void) +{ + signal_remove("dcc request", (SIGNAL_FUNC) sig_dcc_request); +} diff --git a/src/irc/dcc/dcc-chat.c b/src/irc/dcc/dcc-chat.c index 0084cee8..6fa9b19e 100644 --- a/src/irc/dcc/dcc-chat.c +++ b/src/irc/dcc/dcc-chat.c @@ -25,6 +25,7 @@ #include "net-nonblock.h" #include "net-sendbuffer.h" #include "line-split.h" +#include "misc.h" #include "settings.h" #include "masks.h" @@ -34,6 +35,45 @@ #include "dcc.h" +DCC_REC *dcc_chat_find_id(const char *id) +{ + GSList *tmp; + + g_return_val_if_fail(id != NULL, NULL); + + for (tmp = dcc_conns; tmp != NULL; tmp = tmp->next) { + DCC_REC *dcc = tmp->data; + + if (dcc->type == DCC_TYPE_CHAT && dcc->chat_id != NULL && + g_strcasecmp(dcc->chat_id, id) == 0) + return dcc; + } + + return NULL; +} + +static void dcc_chat_set_id(DCC_REC *dcc) +{ + char *id; + int num; + + if (dcc_chat_find_id(dcc->nick) == NULL) { + /* same as nick, good */ + dcc->chat_id = g_strdup(dcc->nick); + return; + } + + /* keep adding numbers after nick until some of them isn't found */ + for (num = 2;; num++) { + id = g_strdup_printf("%s%d", dcc->nick, num); + if (dcc_chat_find_id(id) == NULL) { + dcc->chat_id = id; + break; + } + g_free(id); + } +} + /* Send `data' to dcc chat. */ void dcc_chat_send(DCC_REC *dcc, const char *data) { @@ -54,7 +94,7 @@ DCC_REC *item_get_dcc(WI_ITEM_REC *item) if (query == NULL || *query->name != '=') return NULL; - return dcc_find_item(DCC_TYPE_CHAT, query->name+1, NULL); + return dcc_chat_find_id(query->name+1); } /* Send text to DCC chat */ @@ -74,7 +114,7 @@ static void cmd_msg(const char *data) if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &target, &text)) return; - dcc = dcc_find_item(DCC_TYPE_CHAT, ++target, NULL); + dcc = dcc_chat_find_id(++target); if (dcc != NULL && dcc->sendbuf != NULL) dcc_chat_send(dcc, text); @@ -92,7 +132,7 @@ static void cmd_me(const char *data, IRC_SERVER_REC *server, QUERY_REC *item) dcc = item_get_dcc((WI_ITEM_REC *) item); if (dcc == NULL) return; - str = g_strdup_printf("ACTION %s", data); + str = g_strconcat("ACTION ", data, NULL); dcc_ctcp_message(NULL, dcc->nick, dcc, FALSE, str); g_free(str); @@ -116,9 +156,9 @@ static void cmd_action(const char *data, IRC_SERVER_REC *server) return; if (*target == '\0' || *text == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); - dcc = dcc_find_item(DCC_TYPE_CHAT, target+1, NULL); + dcc = dcc_chat_find_id(target+1); if (dcc != NULL) { - str = g_strdup_printf("ACTION %s", text); + str = g_strconcat("ACTION ", text, NULL); dcc_ctcp_message(NULL, dcc->nick, dcc, FALSE, str); g_free(str); } @@ -146,11 +186,11 @@ static void cmd_ctcp(const char *data, IRC_SERVER_REC *server) return; } - dcc = dcc_find_item(DCC_TYPE_CHAT, target+1, NULL); + dcc = dcc_chat_find_id(target+1); if (dcc != NULL) { g_strup(ctcpcmd); - str = g_strdup_printf("%s %s", ctcpcmd, ctcpdata); + str = g_strconcat(ctcpcmd, " ", ctcpdata, NULL); dcc_ctcp_message(NULL, dcc->nick, dcc, FALSE, str); g_free(str); } @@ -262,53 +302,64 @@ static void dcc_chat_connect(DCC_REC *dcc) } } -/* SYNTAX: DCC CHAT <nick> */ +/* SYNTAX: DCC CHAT [<nick>] */ static void cmd_dcc_chat(const char *data, IRC_SERVER_REC *server) { void *free_arg; DCC_REC *dcc; IPADDR own_ip; GIOChannel *handle; - char *nick, *str, host[MAX_IP_LEN]; + char *nick, host[MAX_IP_LEN]; int port; g_return_if_fail(data != NULL); if (!cmd_get_params(data, &free_arg, 1, &nick)) return; - if (*nick == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); - dcc = dcc_find_item(DCC_TYPE_CHAT, nick, NULL); - if (dcc != NULL) { - /* found from dcc list - so we're the connecting side.. */ + if (*nick == '\0') { + dcc = dcc_find_request_latest(DCC_TYPE_CHAT); + if (dcc != NULL) + dcc_chat_connect(dcc); + cmd_params_free(free_arg); + return; + } + + dcc = dcc_chat_find_id(nick); + if (dcc != NULL && dcc_is_waiting_user(dcc)) { + /* found from dcc chat requests, + we're the connecting side */ dcc_chat_connect(dcc); cmd_params_free(free_arg); return; } - /* send dcc chat request */ + if (dcc != NULL && dcc_is_listening(dcc) && + dcc->server == server) { + /* sending request again even while old request is + still waiting, remove it. */ + dcc_destroy(dcc); + } + + /* start listening */ if (server == NULL || !server->connected) cmd_param_error(CMDERR_NOT_CONNECTED); - if (net_getsockname(net_sendbuffer_handle(server->handle), - &own_ip, NULL) == -1) - cmd_param_error(CMDERR_ERRNO); - - port = settings_get_int("dcc_port"); - handle = net_listen(&own_ip, &port); + handle = dcc_listen(net_sendbuffer_handle(server->handle), + &own_ip, &port); if (handle == NULL) cmd_param_error(CMDERR_ERRNO); - dcc = dcc_create(DCC_TYPE_CHAT, handle, nick, "chat", server, NULL); + dcc = dcc_create(DCC_TYPE_CHAT, nick, "chat", server, NULL); + dcc_chat_set_id(dcc); + dcc->handle = handle; dcc->tagconn = g_input_add(dcc->handle, G_INPUT_READ, (GInputFunction) dcc_chat_listen, dcc); - /* send the request */ + /* send the chat request */ dcc_make_address(&own_ip, host); - str = g_strdup_printf("PRIVMSG %s :\001DCC CHAT CHAT %s %d\001", - nick, host, port); - irc_send_cmd(server, str); - g_free(str); + irc_send_cmdv(server, "PRIVMSG %s :\001DCC CHAT CHAT %s %d\001", + nick, host, port); cmd_params_free(free_arg); } @@ -328,6 +379,63 @@ static void cmd_mircdcc(const char *data, IRC_SERVER_REC *server, g_strncasecmp(data, "OF", 3) != 0; } +#define DCC_AUTOACCEPT_PORT(dcc) \ + ((dcc)->port >= 1024 || settings_get_bool("dcc_autoaccept_lowport")) + +#define DCC_CHAT_AUTOACCEPT(dcc, server, nick, addr) \ + (DCC_AUTOACCEPT_PORT(dcc) && \ + masks_match(SERVER(server), \ + settings_get_str("dcc_autochat_masks"), (nick), (addr))) + + +/* CTCP: DCC CHAT */ +static void ctcp_msg_dcc_chat(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr, + const char *target, DCC_REC *chat) +{ + DCC_REC *dcc; + char **params; + int paramcount; + int autoallow = FALSE; + + /* CHAT <unused> <address> <port> */ + params = g_strsplit(data, " ", -1); + paramcount = strarray_length(params); + + if (paramcount < 3) { + g_strfreev(params); + return; + } + + dcc = dcc_find_request(DCC_TYPE_CHAT, nick, NULL); + if (dcc != NULL) { + if (dcc_is_listening(dcc)) { + /* we requested dcc chat, they requested + dcc chat from us .. allow it. */ + dcc_destroy(dcc); + autoallow = TRUE; + } else { + /* we already have one dcc chat request + from this nick, remove it. */ + dcc_destroy(dcc); + } + } + + dcc = dcc_create(DCC_TYPE_CHAT, nick, params[0], server, chat); + dcc_chat_set_id(dcc); + dcc->target = g_strdup(target); + dcc->port = atoi(params[2]); + dcc_get_address(params[1], &dcc->addr); + net_ip2host(&dcc->addr, dcc->addrstr); + + signal_emit("dcc request", 2, dcc, addr); + + if (autoallow || DCC_CHAT_AUTOACCEPT(dcc, server, nick, addr)) + dcc_chat_connect(dcc); + + g_strfreev(params); +} + /* DCC CHAT: text received */ static void dcc_chat_msg(DCC_REC *dcc, const char *msg) { @@ -364,31 +472,47 @@ static void dcc_chat_msg(DCC_REC *dcc, const char *msg) if (ptr != NULL) *ptr++ = '\0'; else ptr = ""; g_strdown(cmd+9); - if (!signal_emit(cmd, 2, ptr, dcc)) - signal_emit(reply ? "default dcc reply" : "default dcc ctcp", 2, msg, dcc); + if (!signal_emit(cmd, 2, ptr, dcc)) { + signal_emit(reply ? "default dcc reply" : + "default dcc ctcp", 2, msg, dcc); + } g_free(cmd); signal_stop(); } -static void dcc_ctcp_redirect(gchar *msg, DCC_REC *dcc) +static void dcc_ctcp_redirect(const char *msg, DCC_REC *dcc) { g_return_if_fail(msg != NULL); g_return_if_fail(dcc != NULL); - signal_emit("ctcp msg dcc", 6, dcc->server, msg, dcc->nick, "dcc", dcc->mynick, dcc); + signal_emit("ctcp msg dcc", 6, dcc->server, msg, + dcc->nick, "dcc", dcc->mynick, dcc); +} + +static void dcc_ctcp_reply_redirect(const char *msg, DCC_REC *dcc) +{ + g_return_if_fail(msg != NULL); + g_return_if_fail(dcc != NULL); + + signal_emit("ctcp reply dcc", 6, dcc->server, msg, + dcc->nick, "dcc", dcc->mynick, dcc); } void dcc_chat_init(void) { + settings_add_str("dcc", "dcc_autochat_masks", ""); + command_bind("msg", NULL, (SIGNAL_FUNC) cmd_msg); command_bind("me", NULL, (SIGNAL_FUNC) cmd_me); command_bind("action", NULL, (SIGNAL_FUNC) cmd_action); command_bind("ctcp", NULL, (SIGNAL_FUNC) cmd_ctcp); command_bind("dcc chat", NULL, (SIGNAL_FUNC) cmd_dcc_chat); command_bind("mircdcc", NULL, (SIGNAL_FUNC) cmd_mircdcc); + signal_add("ctcp msg dcc chat", (SIGNAL_FUNC) ctcp_msg_dcc_chat); signal_add_first("dcc chat message", (SIGNAL_FUNC) dcc_chat_msg); signal_add("dcc ctcp dcc", (SIGNAL_FUNC) dcc_ctcp_redirect); + signal_add("dcc reply dcc", (SIGNAL_FUNC) dcc_ctcp_reply_redirect); } void dcc_chat_deinit(void) @@ -399,6 +523,8 @@ void dcc_chat_deinit(void) command_unbind("ctcp", (SIGNAL_FUNC) cmd_ctcp); command_unbind("dcc chat", (SIGNAL_FUNC) cmd_dcc_chat); command_unbind("mircdcc", (SIGNAL_FUNC) cmd_mircdcc); + signal_remove("ctcp msg dcc chat", (SIGNAL_FUNC) ctcp_msg_dcc_chat); signal_remove("dcc chat message", (SIGNAL_FUNC) dcc_chat_msg); signal_remove("dcc ctcp dcc", (SIGNAL_FUNC) dcc_ctcp_redirect); + signal_remove("dcc reply dcc", (SIGNAL_FUNC) dcc_ctcp_reply_redirect); } diff --git a/src/irc/dcc/dcc-chat.h b/src/irc/dcc/dcc-chat.h new file mode 100644 index 00000000..e1ea6f1b --- /dev/null +++ b/src/irc/dcc/dcc-chat.h @@ -0,0 +1,11 @@ +#ifndef __DCC_CHAT_H +#define __DCC_CHAT_H + +#include "dcc.h" + +DCC_REC *dcc_chat_find_id(const char *id); + +void dcc_chat_init(void); +void dcc_chat_deinit(void); + +#endif diff --git a/src/irc/dcc/dcc-files.c b/src/irc/dcc/dcc-files.c deleted file mode 100644 index c284087a..00000000 --- a/src/irc/dcc/dcc-files.c +++ /dev/null @@ -1,667 +0,0 @@ -/* - dcc-files.c : irssi - - Copyright (C) 1999-2000 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include "module.h" -#include "signals.h" -#include "commands.h" -#include "network.h" -#include "net-sendbuffer.h" -#include "line-split.h" -#include "misc.h" -#include "settings.h" - -#include "masks.h" -#include "irc.h" -#include "servers-setup.h" - -#include "dcc.h" - -static int dcc_file_create_mode; - -static char *dcc_get_download_path(const char *fname) -{ - char *str, *downpath; - - downpath = convert_home(settings_get_str("dcc_download_path")); - str = g_strconcat(downpath, G_DIR_SEPARATOR_S, g_basename(fname), NULL); - g_free(downpath); - - return str; -} - -static void sig_dccget_send(DCC_REC *dcc); - -void dcc_get_send_received(DCC_REC *dcc) -{ - guint32 recd; - - recd = (guint32) htonl(dcc->transfd); - memcpy(dcc->count_buf, &recd, 4); - - dcc->count_pos = net_transmit(dcc->handle, dcc->count_buf+dcc->count_pos, 4-dcc->count_pos); - if (dcc->count_pos == 4) dcc->count_pos = 0; - - /* count_pos might be -1 here. if this happens, the - count_buf should be re-sent.. also, if it's 1, 2 or 3, the - last 1-3 bytes should be sent later. these happen probably - never, but I just want to do it right.. :) */ - if (dcc->tagwrite == -1) { - dcc->tagwrite = g_input_add(dcc->handle, G_INPUT_WRITE, - (GInputFunction) sig_dccget_send, dcc); - } -} - -/* input function: DCC GET is free to send data */ -static void sig_dccget_send(DCC_REC *dcc) -{ - guint32 recd; - int ret; - - if (dcc->count_pos != 0) { - ret = net_transmit(dcc->handle, dcc->count_buf+dcc->count_pos, 4-dcc->count_pos); - if (dcc->count_pos <= 0) - dcc->count_pos = ret; - else if (ret > 0) - dcc->count_pos += ret; - - if (dcc->count_pos == 4) dcc->count_pos = 0; - - } - - if (dcc->count_pos == 0) { - g_source_remove(dcc->tagwrite); - dcc->tagwrite = -1; - } - - memcpy(&recd, dcc->count_buf, 4); - if (recd != (guint32) htonl(dcc->transfd)) - dcc_get_send_received(dcc); -} - -/* input function: DCC GET received data */ -static void sig_dccget_receive(DCC_REC *dcc) -{ - int ret; - - g_return_if_fail(dcc != NULL); - - for (;;) { - ret = net_receive(dcc->handle, dcc->databuf, dcc->databufsize); - if (ret == 0) break; - - if (ret < 0) { - /* socket closed - transmit complete, - or other side died.. */ - signal_emit("dcc closed", 1, dcc); - dcc_destroy(dcc); - return; - } - - write(dcc->fhandle, dcc->databuf, ret); - dcc->transfd += ret; - } - - /* send number of total bytes received */ - if (dcc->count_pos <= 0) - dcc_get_send_received(dcc); - - signal_emit("dcc transfer update", 1, dcc); -} - -static char *get_rename_file(const char *fname) -{ - GString *newname; - struct stat statbuf; - char *ret; - int num; - - newname = g_string_new(NULL); - num = 1; - do { - g_string_sprintf(newname, "%s.%d", fname, num); - num++; - } while (stat(newname->str, &statbuf) == 0); - - ret = newname->str; - g_string_free(newname, FALSE); - return ret; -} - -/* callback: net_connect() finished for DCC GET */ -static void sig_dccget_connected(DCC_REC *dcc) -{ - struct stat statbuf; - char *fname; - - g_return_if_fail(dcc != NULL); - - if (net_geterror(dcc->handle) != 0) { - /* error connecting */ - signal_emit("dcc error connect", 1, dcc); - dcc_destroy(dcc); - return; - } - - g_source_remove(dcc->tagconn); - - g_free_not_null(dcc->file); - dcc->file = dcc_get_download_path(dcc->arg); - - /* if some plugin wants to change the file name/path here.. */ - signal_emit("dcc get receive", 1, dcc); - - if (stat(dcc->file, &statbuf) == 0 && dcc->get_type == DCC_GET_RENAME) { - /* file exists, rename.. */ - fname = get_rename_file(dcc->file); - g_free(dcc->file); - dcc->file = fname; - } - - if (dcc->get_type != DCC_GET_RESUME) { - dcc->fhandle = open(dcc->file, O_WRONLY | O_TRUNC | O_CREAT, dcc_file_create_mode); - if (dcc->fhandle == -1) { - signal_emit("dcc error file create", 2, dcc, dcc->file); - dcc_destroy(dcc); - return; - } - } - - dcc->databufsize = settings_get_int("dcc_block_size"); - if (dcc->databufsize <= 0) dcc->databufsize = 2048; - dcc->databuf = g_malloc(dcc->databufsize); - - dcc->starttime = time(NULL); - dcc->tagread = g_input_add(dcc->handle, G_INPUT_READ, - (GInputFunction) sig_dccget_receive, dcc); - signal_emit("dcc connected", 1, dcc); -} - -static void dcc_get_connect(DCC_REC *dcc) -{ - dcc->handle = net_connect_ip(&dcc->addr, dcc->port, - source_host_ok ? source_host_ip : NULL); - if (dcc->handle != NULL) { - dcc->tagconn = g_input_add(dcc->handle, - G_INPUT_WRITE | G_INPUT_READ, - (GInputFunction) sig_dccget_connected, dcc); - } else { - /* error connecting */ - signal_emit("dcc error connect", 1, dcc); - dcc_destroy(dcc); - } -} - -#define dcc_is_unget(dcc) \ - ((dcc)->type == DCC_TYPE_GET && (dcc)->handle == NULL) - -/* SYNTAX: DCC GET <nick> [<file>] */ -static void cmd_dcc_get(const char *data) -{ - DCC_REC *dcc; - GSList *tmp, *next; - char *nick, *fname; - void *free_arg; - int found; - - g_return_if_fail(data != NULL); - - if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &nick, &fname)) - return; - if (*nick == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); - - dcc = NULL; found = FALSE; - 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)) { - found = TRUE; - dcc_get_connect(dcc); - } - } - - if (!found) - signal_emit("dcc error get not found", 1, nick); - - cmd_params_free(free_arg); -} - -static void dcc_resume_send(DCC_REC *dcc, int port) -{ - char *str; - - g_return_if_fail(dcc != NULL); - g_return_if_fail(dcc->type == DCC_TYPE_SEND); - - str = g_strdup_printf("DCC ACCEPT %s %d %lu", - dcc->arg, port, dcc->transfd); - dcc_ctcp_message(dcc->server, dcc->nick, dcc->chat, FALSE, str); - g_free(str); -} - -#define is_resume_type(type) \ - (g_strcasecmp(type, "RESUME") == 0 || \ - g_strcasecmp(type, "ACCEPT") == 0) - -#define is_resume_ok(type, dcc) \ - (g_strcasecmp(type, "RESUME") != 0 || \ - ((dcc)->type == DCC_TYPE_SEND && (dcc)->transfd == 0)) - -#define is_accept_ok(type, dcc) \ - (g_strcasecmp(type, "ACCEPT") != 0 || \ - ((dcc)->type == DCC_TYPE_GET && \ - (dcc)->get_type == DCC_GET_RESUME && (dcc)->handle == NULL)) - -static void dcc_ctcp_msg(IRC_SERVER_REC *server, const char *data, - const char *sender, const char *sendaddr, - const char *target, DCC_REC *chat) -{ - char *type, *arg, *portstr, *sizestr; - void *free_arg; - long size; - int port; - DCC_REC *dcc; - - g_return_if_fail(data != NULL); - g_return_if_fail(sender != NULL); - - if (!cmd_get_params(data, &free_arg, 4 | PARAM_FLAG_NOQUOTES, - &type, &arg, &portstr, &sizestr)) - return; - - port = atoi(portstr); - size = atol(sizestr); - - dcc = dcc_find_by_port(sender, port); - if (dcc == NULL || !is_resume_type(type) || - !is_resume_ok(type, dcc) || !is_accept_ok(type, dcc)) { - cmd_params_free(free_arg); - return; - } - - 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 */ - dcc_reject(dcc, server); - } else { - dcc->transfd = dcc->skipped = size; - - if (dcc->type == DCC_TYPE_SEND) - dcc_resume_send(dcc, port); - else - dcc_get_connect(dcc); - } - - cmd_params_free(free_arg); -} - -static void dcc_resume_rec(DCC_REC *dcc) -{ - char *str; - - g_return_if_fail(dcc != NULL); - - 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); - 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); - g_free(str); -} - -/* SYNTAX: DCC RESUME <nick> [<file>] */ -static void cmd_dcc_resume(const char *data) -{ - DCC_REC *dcc; - GSList *tmp, *next; - char *nick, *fname; - void *free_arg; - int found; - - g_return_if_fail(data != NULL); - - if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &nick, &fname)) - return; - if (*nick == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); - - dcc = NULL; found = FALSE; - 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); - found = TRUE; - } - } - - if (!found) - signal_emit("dcc error get not found", 1, nick); - - cmd_params_free(free_arg); -} - -/* input function: DCC SEND - we're ready to send more data */ -static void dcc_send_data(DCC_REC *dcc) -{ - int ret; - - g_return_if_fail(dcc != NULL); - - if (!dcc->fastsend && !dcc->gotalldata) { - /* haven't received everything we've send there yet.. */ - return; - } - - ret = read(dcc->fhandle, dcc->databuf, dcc->databufsize); - if (ret <= 0) { - /* end of file .. or some error .. */ - if (dcc->fastsend) { - /* no need to call this function anymore.. - in fact it just eats all the cpu.. */ - dcc->waitforend = TRUE; - g_source_remove(dcc->tagwrite); - dcc->tagwrite = -1; - } else { - signal_emit("dcc closed", 1, dcc); - dcc_destroy(dcc); - } - return; - } - - ret = net_transmit(dcc->handle, dcc->databuf, ret); - if (ret > 0) dcc->transfd += ret; - dcc->gotalldata = FALSE; - - lseek(dcc->fhandle, dcc->transfd, SEEK_SET); - - signal_emit("dcc transfer update", 1, dcc); -} - -/* input function: DCC SEND - received some data */ -static void dcc_send_read_size(DCC_REC *dcc) -{ - guint32 bytes; - int ret; - - g_return_if_fail(dcc != NULL); - - if (dcc->count_pos == 4) - return; - - /* we need to get 4 bytes.. */ - ret = net_receive(dcc->handle, dcc->count_buf+dcc->count_pos, 4-dcc->count_pos); - if (ret == -1) { - signal_emit("dcc closed", 1, dcc); - dcc_destroy(dcc); - return; - } - - dcc->count_pos += ret; - - if (dcc->count_pos != 4) - return; - - memcpy(&bytes, dcc->count_buf, 4); - bytes = (guint32) ntohl(bytes); - - dcc->gotalldata = (long) bytes == dcc->transfd; - dcc->count_pos = 0; - - if (!dcc->fastsend) { - /* send more data.. */ - dcc_send_data(dcc); - } - - if (dcc->waitforend && dcc->gotalldata) { - /* file is sent */ - signal_emit("dcc closed", 1, dcc); - dcc_destroy(dcc); - } -} - -/* input function: DCC SEND - someone tried to connect to our socket */ -static void dcc_send_init(DCC_REC *dcc) -{ - GIOChannel *handle; - IPADDR addr; - int port; - - g_return_if_fail(dcc != NULL); - - /* accept connection */ - handle = net_accept(dcc->handle, &addr, &port); - if (handle == NULL) - return; - - /* TODO: some kind of paranoia check would be nice. it would check - that the host of the nick who we sent the request matches the - address who connected us. */ - - g_source_remove(dcc->tagconn); - net_disconnect(dcc->handle); - - dcc->starttime = time(NULL); - dcc->fastsend = settings_get_bool("dcc_fast_send"); - dcc->handle = handle; - memcpy(&dcc->addr, &addr, sizeof(IPADDR)); - net_ip2host(&dcc->addr, dcc->addrstr); - dcc->port = port; - - dcc->databufsize = settings_get_int("dcc_block_size"); - if (dcc->databufsize <= 0) dcc->databufsize = 2048; - dcc->databuf = g_malloc(dcc->databufsize); - - dcc->tagread = g_input_add(handle, G_INPUT_READ, - (GInputFunction) dcc_send_read_size, dcc); - dcc->tagwrite = !dcc->fastsend ? -1 : - g_input_add(handle, G_INPUT_WRITE, (GInputFunction) dcc_send_data, dcc); - - signal_emit("dcc connected", 1, dcc); - - if (!dcc->fastsend) { - /* send first block */ - dcc->gotalldata = TRUE; - dcc_send_data(dcc); - } -} - -/* SYNTAX: DCC SEND <nick> <file> */ -static void cmd_dcc_send(const char *data, IRC_SERVER_REC *server, - WI_ITEM_REC *item) -{ - char *target, *fname, *str, *ptr; - void *free_arg; - char host[MAX_IP_LEN]; - int hfile, port; - long fsize; - DCC_REC *dcc, *chat; - IPADDR own_ip; - GIOChannel *handle, *hlisten; - - g_return_if_fail(data != NULL); - - if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &target, &fname)) - return; - if (*target == '\0' || *fname == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); - - /* if we're in dcc chat, send the request via it. */ - chat = item_get_dcc(item); - - if (chat != NULL && (chat->mirc_ctcp || g_strcasecmp(target, chat->nick) != 0)) - chat = NULL; - - if ((server == NULL || !server->connected) && chat == NULL) - cmd_param_error(CMDERR_NOT_CONNECTED); - - if (dcc_find_item(DCC_TYPE_SEND, target, fname)) { - signal_emit("dcc error send exists", 2, target, fname); - cmd_params_free(free_arg); - return; - } - - str = convert_home(fname); - if (!g_path_is_absolute(str)) { - char *path; - - g_free(str); - path = convert_home(settings_get_str("dcc_upload_path")); - str = g_strconcat(path, G_DIR_SEPARATOR_S, fname, NULL); - g_free(path); - } - - hfile = open(str, O_RDONLY); - g_free(str); - - if (hfile == -1) { - signal_emit("dcc error file not found", 2, target, fname); - cmd_params_free(free_arg); - return; - } - fsize = lseek(hfile, 0, SEEK_END); - lseek(hfile, 0, SEEK_SET); - - /* get the IP address we use with IRC server */ - handle = chat != NULL ? chat->handle : - net_sendbuffer_handle(server->handle); - if (net_getsockname(handle, &own_ip, NULL) == -1) { - close(hfile); - cmd_param_error(CMDERR_ERRNO); - } - - /* start listening */ - port = settings_get_int("dcc_port"); - hlisten = net_listen(&own_ip, &port); - if (hlisten == NULL) { - close(hfile); - cmd_param_error(CMDERR_ERRNO); - } - - /* skip path, change all spaces to _ */ - fname = g_strdup(g_basename(fname)); - for (ptr = fname; *ptr != '\0'; ptr++) - if (*ptr == ' ') *ptr = '_'; - - dcc = dcc_create(DCC_TYPE_SEND, hlisten, target, fname, server, chat); - dcc->port = port; - dcc->size = fsize; - dcc->fhandle = hfile; - dcc->tagconn = g_input_add(hlisten, G_INPUT_READ, - (GInputFunction) dcc_send_init, dcc); - - /* send DCC request */ - dcc_make_address(&own_ip, host); - str = g_strdup_printf("DCC SEND %s %s %d %lu", - fname, host, port, fsize); - dcc_ctcp_message(server, target, chat, FALSE, str); - g_free(str); - - g_free(fname); - 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")); -} - -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); - command_bind("dcc resume", NULL, (SIGNAL_FUNC) cmd_dcc_resume); -} - -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); - command_unbind("dcc resume", (SIGNAL_FUNC) cmd_dcc_resume); -} diff --git a/src/irc/dcc/dcc-get.c b/src/irc/dcc/dcc-get.c new file mode 100644 index 00000000..0ebf3e20 --- /dev/null +++ b/src/irc/dcc/dcc-get.c @@ -0,0 +1,385 @@ +/* + dcc-get.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" +#include "commands.h" +#include "network.h" +#include "misc.h" +#include "settings.h" + +#include "servers-setup.h" +#include "dcc-get.h" + +static int dcc_file_create_mode; + +char *dcc_get_download_path(const char *fname) +{ + char *str, *downpath; + + downpath = convert_home(settings_get_str("dcc_download_path")); + str = g_strconcat(downpath, G_DIR_SEPARATOR_S, g_basename(fname), NULL); + g_free(downpath); + + return str; +} + +static char *dcc_get_rename_file(const char *fname) +{ + GString *newname; + struct stat statbuf; + char *ret; + int num; + + newname = g_string_new(NULL); + num = 1; + do { + g_string_sprintf(newname, "%s.%d", fname, num); + num++; + } while (stat(newname->str, &statbuf) == 0); + + ret = newname->str; + g_string_free(newname, FALSE); + return ret; +} + +static void sig_dccget_send(DCC_REC *dcc); + +void dcc_get_send_received(DCC_REC *dcc) +{ + guint32 recd; + + recd = (guint32) htonl(dcc->transfd); + memcpy(dcc->count_buf, &recd, 4); + + dcc->count_pos = + net_transmit(dcc->handle, dcc->count_buf+dcc->count_pos, + 4-dcc->count_pos); + if (dcc->count_pos == 4) dcc->count_pos = 0; + + /* count_pos might be -1 here. if this happens, the + count_buf should be re-sent.. also, if it's 1, 2 or 3, the + last 1-3 bytes should be sent later. these happen probably + never, but I just want to do it right.. :) */ + if (dcc->tagwrite == -1) { + dcc->tagwrite = g_input_add(dcc->handle, G_INPUT_WRITE, + (GInputFunction) sig_dccget_send, + dcc); + } +} + +/* input function: DCC GET is free to send data */ +static void sig_dccget_send(DCC_REC *dcc) +{ + guint32 recd; + int ret; + + if (dcc->count_pos != 0) { + ret = net_transmit(dcc->handle, dcc->count_buf+dcc->count_pos, + 4-dcc->count_pos); + + if (dcc->count_pos <= 0) + dcc->count_pos = ret; + else if (ret > 0) + dcc->count_pos += ret; + + if (dcc->count_pos == 4) dcc->count_pos = 0; + + } + + if (dcc->count_pos == 0) { + g_source_remove(dcc->tagwrite); + dcc->tagwrite = -1; + } + + memcpy(&recd, dcc->count_buf, 4); + if (recd != (guint32) htonl(dcc->transfd)) + dcc_get_send_received(dcc); +} + +/* input function: DCC GET received data */ +static void sig_dccget_receive(DCC_REC *dcc) +{ + int ret; + + g_return_if_fail(dcc != NULL); + + for (;;) { + ret = net_receive(dcc->handle, dcc->databuf, dcc->databufsize); + if (ret == 0) break; + + if (ret < 0) { + /* socket closed - transmit complete, + or other side died.. */ + signal_emit("dcc closed", 1, dcc); + dcc_destroy(dcc); + return; + } + + write(dcc->fhandle, dcc->databuf, ret); + dcc->transfd += ret; + } + + /* send number of total bytes received */ + if (dcc->count_pos <= 0) + dcc_get_send_received(dcc); + + signal_emit("dcc transfer update", 1, dcc); +} + +/* callback: net_connect() finished for DCC GET */ +static void sig_dccget_connected(DCC_REC *dcc) +{ + struct stat statbuf; + char *fname; + + g_return_if_fail(dcc != NULL); + + if (net_geterror(dcc->handle) != 0) { + /* error connecting */ + signal_emit("dcc error connect", 1, dcc); + dcc_destroy(dcc); + return; + } + + g_source_remove(dcc->tagconn); + + g_free_not_null(dcc->file); + dcc->file = dcc_get_download_path(dcc->arg); + + /* if some plugin wants to change the file name/path here.. */ + signal_emit("dcc get receive", 1, dcc); + + if (stat(dcc->file, &statbuf) == 0 && + dcc->get_type == DCC_GET_RENAME) { + /* file exists, rename.. */ + fname = dcc_get_rename_file(dcc->file); + g_free(dcc->file); + dcc->file = fname; + } + + if (dcc->get_type != DCC_GET_RESUME) { + dcc->fhandle = open(dcc->file, O_WRONLY | O_TRUNC | O_CREAT, dcc_file_create_mode); + if (dcc->fhandle == -1) { + signal_emit("dcc error file create", 2, dcc, dcc->file); + dcc_destroy(dcc); + return; + } + } + + dcc->databufsize = settings_get_int("dcc_block_size"); + if (dcc->databufsize <= 0) dcc->databufsize = 2048; + dcc->databuf = g_malloc(dcc->databufsize); + + dcc->starttime = time(NULL); + dcc->tagread = g_input_add(dcc->handle, G_INPUT_READ, + (GInputFunction) sig_dccget_receive, dcc); + signal_emit("dcc connected", 1, dcc); +} + +void dcc_get_connect(DCC_REC *dcc) +{ + if (dcc->get_type == DCC_GET_DEFAULT) { + dcc->get_type = settings_get_bool("dcc_autorename") ? + DCC_GET_RENAME : DCC_GET_OVERWRITE; + } + + + dcc->handle = net_connect_ip(&dcc->addr, dcc->port, + source_host_ok ? source_host_ip : NULL); + if (dcc->handle != NULL) { + dcc->tagconn = g_input_add(dcc->handle, + G_INPUT_WRITE | G_INPUT_READ, + (GInputFunction) sig_dccget_connected, dcc); + } else { + /* error connecting */ + signal_emit("dcc error connect", 1, dcc); + dcc_destroy(dcc); + } +} + +#define get_params_match(params, pos) \ + ((is_numeric(params[pos], '\0') || is_ipv6_address(params[pos])) && \ + is_numeric(params[(pos)+1], '\0') && atol(params[(pos)+1]) < 65536 && \ + is_numeric(params[(pos)+2], '\0')) + +/* Return number of parameters in `params' that belong to file name. + Normally it's paramcount-3, but I don't think anything forbids of + adding some extension where there could be more parameters after + file size. + + MIRC sends filenames with spaces quoted ("file name"), but I'd rather + not trust that entirely either. At least some clients that don't really + understand the problem with spaces in file names sends the file name + without any quotes. */ +int get_file_params_count(char **params, int paramcount) +{ + int pos, best; + + if (*params[0] == '"') { + /* quoted file name? */ + for (pos = 0; pos < paramcount-3; pos++) { + if (params[pos][strlen(params[pos])-1] == '"' && + get_params_match(params, pos+1)) + return pos+1; + } + } + + best = paramcount-3; + for (pos = paramcount-3; pos > 0; pos--) { + if (get_params_match(params, pos)) + best = pos; + } + + return best; +} + +/* CTCP: DCC SEND */ +static void ctcp_msg_dcc_send(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr, + const char *target, DCC_REC *chat) +{ + DCC_REC *dcc; + IPADDR ip; + char **params, *fname; + int paramcount, fileparams; + int port, len, quoted = FALSE; + long size; + + /* SEND <file name> <address> <port> <size> [...] */ + params = g_strsplit(data, " ", -1); + paramcount = strarray_length(params); + + if (paramcount < 4) { + signal_emit("dcc error ctcp", 5, "SEND", data, + nick, addr, target); + g_strfreev(params); + return; + } + + fileparams = get_file_params_count(params, paramcount); + + dcc_get_address(params[fileparams], &ip); + port = atoi(params[fileparams+1]); + size = atol(params[fileparams+2]); + + params[fileparams] = NULL; + fname = g_strjoinv(" ", params); + g_strfreev(params); + + len = strlen(fname); + if (len > 1 && *fname == '"' && fname[len-1] == '"') { + /* "file name" - MIRC sends filenames with spaces like this */ + fname[len-1] = '\0'; + g_memmove(fname, fname+1, len); + quoted = TRUE; + } + + dcc = dcc_find_request(DCC_TYPE_GET, nick, fname); + if (dcc != NULL) { + /* same DCC request offered again, remove the old one */ + dcc_destroy(dcc); + } + + dcc = dcc_create(DCC_TYPE_GET, nick, fname, server, chat); + dcc->target = g_strdup(target); + memcpy(&dcc->addr, &ip, sizeof(ip)); + net_ip2host(&dcc->addr, dcc->addrstr); + dcc->port = port; + dcc->size = size; + dcc->file_quoted = quoted; + + signal_emit("dcc request", 2, dcc, addr); + + g_free(fname); +} + +/* handle receiving DCC - GET/RESUME. */ +void cmd_dcc_receive(const char *data, DCC_GET_FUNC accept) +{ + DCC_REC *dcc; + GSList *tmp, *next; + char *nick, *fname; + void *free_arg; + int found; + + g_return_if_fail(data != NULL); + + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, + &nick, &fname)) + return; + + if (*nick == '\0') { + dcc = dcc_find_request_latest(DCC_TYPE_GET); + if (dcc != NULL) + accept(dcc); + cmd_params_free(free_arg); + return; + } + + found = FALSE; + for (tmp = dcc_conns; tmp != NULL; tmp = next) { + DCC_REC *dcc = tmp->data; + + next = tmp->next; + if (dcc_is_waiting_get(dcc) && + g_strcasecmp(dcc->nick, nick) == 0 && + (*fname == '\0' || strcmp(dcc->arg, fname) == 0)) { + found = TRUE; + accept(dcc); + } + } + + if (!found) + signal_emit("dcc error get not found", 1, nick); + + cmd_params_free(free_arg); +} + +/* SYNTAX: DCC GET [<nick> [<file>]] */ +static void cmd_dcc_get(const char *data) +{ + cmd_dcc_receive(data, dcc_get_connect); +} + +static void read_settings(void) +{ + dcc_file_create_mode = + octal2dec(settings_get_int("dcc_file_create_mode")); +} + +void dcc_get_init(void) +{ + settings_add_bool("dcc", "dcc_autorename", FALSE); + settings_add_str("dcc", "dcc_download_path", "~"); + settings_add_int("dcc", "dcc_file_create_mode", 644); + + read_settings(); + signal_add("ctcp msg dcc send", (SIGNAL_FUNC) ctcp_msg_dcc_send); + signal_add("setup changed", (SIGNAL_FUNC) read_settings); + command_bind("dcc get", NULL, (SIGNAL_FUNC) cmd_dcc_get); +} + +void dcc_get_deinit(void) +{ + signal_remove("ctcp msg dcc send", (SIGNAL_FUNC) ctcp_msg_dcc_send); + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); + command_unbind("dcc get", (SIGNAL_FUNC) cmd_dcc_get); +} diff --git a/src/irc/dcc/dcc-get.h b/src/irc/dcc/dcc-get.h new file mode 100644 index 00000000..f103b2ae --- /dev/null +++ b/src/irc/dcc/dcc-get.h @@ -0,0 +1,28 @@ +#ifndef __DCC_GET_H +#define __DCC_GET_H + +#include "dcc.h" + +enum { + DCC_GET_DEFAULT, + + DCC_GET_RENAME, + DCC_GET_OVERWRITE, + DCC_GET_RESUME +}; + +typedef void (*DCC_GET_FUNC) (DCC_REC *); + +/* handle receiving DCC - GET/RESUME. */ +void cmd_dcc_receive(const char *data, DCC_GET_FUNC accept); + +void dcc_get_connect(DCC_REC *dcc); +char *dcc_get_download_path(const char *fname); + +#define dcc_is_waiting_get(dcc) \ + ((dcc)->type == DCC_TYPE_GET && dcc_is_waiting_user(dcc)) + +void dcc_get_init(void); +void dcc_get_deinit(void); + +#endif diff --git a/src/irc/dcc/dcc-resume.c b/src/irc/dcc/dcc-resume.c new file mode 100644 index 00000000..3fa884bb --- /dev/null +++ b/src/irc/dcc/dcc-resume.c @@ -0,0 +1,177 @@ +/* + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" +#include "commands.h" +#include "network.h" +#include "misc.h" + +#include "dcc-get.h" + +static DCC_REC *dcc_resume_find(int type, const char *nick, int port) +{ + GSList *tmp; + + for (tmp = dcc_conns; tmp != NULL; tmp = tmp->next) { + DCC_REC *dcc = tmp->data; + + if (dcc->type == type && !dcc_is_connected(dcc) && + dcc->port == port && g_strcasecmp(dcc->nick, nick) == 0) + return dcc; + } + + return NULL; +} + +static int dcc_ctcp_resume_parse(int type, const char *data, const char *nick, + DCC_REC **dcc, long *size) +{ + char **params; + int paramcount; + int port; + + /* RESUME|ACCEPT <file name> <port> <size> */ + params = g_strsplit(data, " ", -1); + paramcount = strarray_length(params); + + if (paramcount >= 3) { + port = atoi(params[paramcount-2]); + *size = atol(params[paramcount-1]); + + type = type == DCC_TYPE_RESUME ? DCC_TYPE_SEND : DCC_TYPE_GET; + *dcc = dcc_resume_find(type, nick, port); + } + g_strfreev(params); + return paramcount >= 3; +} + +static int dcc_resume_file_check(DCC_REC *dcc, IRC_SERVER_REC *server, + long size) +{ + if (lseek(dcc->fhandle, 0, SEEK_END) == dcc->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 */ + dcc_reject(dcc, server); + } else { + dcc->transfd = dcc->skipped = size; + return TRUE; + } + + return FALSE; +} + +/* CTCP: DCC RESUME */ +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) +{ + DCC_REC *dcc; + char *str; + long size; + + if (!dcc_ctcp_resume_parse(DCC_TYPE_RESUME, data, nick, &dcc, &size)) + signal_emit("dcc error ctcp", 5, "RESUME", data, + nick, addr, target); + + if (dcc != NULL && dcc_resume_file_check(dcc, server, size)) { + str = g_strdup_printf(dcc->file_quoted ? + "DCC ACCEPT \"%s\" %d %lu" : + "DCC ACCEPT %s %d %lu", + dcc->arg, dcc->port, dcc->transfd); + dcc_ctcp_message(dcc->server, dcc->nick, + dcc->chat, FALSE, str); + g_free(str); + } +} + +/* CTCP: DCC ACCEPT */ +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) +{ + DCC_REC *dcc; + long size; + + if (!dcc_ctcp_resume_parse(DCC_TYPE_ACCEPT, data, nick, &dcc, &size) || + (dcc != NULL && dcc->get_type != DCC_GET_RESUME)) + signal_emit("dcc error ctcp", 5, "ACCEPT", data, + nick, addr, target); + + if (dcc != NULL && dcc_resume_file_check(dcc, server, size)) + dcc_get_connect(dcc); +} + +static void dcc_send_resume(DCC_REC *dcc) +{ + 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 not found", 2, dcc, dcc->file); + 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->file_quoted ? + "DCC RESUME \"%s\" %d %lu" : + "DCC RESUME %s %d %lu", + dcc->arg, dcc->port, dcc->transfd); + 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); +} + +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); +} diff --git a/src/irc/dcc/dcc-send.c b/src/irc/dcc/dcc-send.c new file mode 100644 index 00000000..2ad66b25 --- /dev/null +++ b/src/irc/dcc/dcc-send.c @@ -0,0 +1,267 @@ +/* + dcc-send.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" +#include "commands.h" +#include "network.h" +#include "net-sendbuffer.h" +#include "misc.h" +#include "settings.h" + +#include "dcc.h" + +/* input function: DCC SEND - we're ready to send more data */ +static void dcc_send_data(DCC_REC *dcc) +{ + int ret; + + g_return_if_fail(dcc != NULL); + + if (!dcc->fastsend && !dcc->gotalldata) { + /* haven't received everything we've send there yet.. */ + return; + } + + ret = read(dcc->fhandle, dcc->databuf, dcc->databufsize); + if (ret <= 0) { + /* end of file .. or some error .. */ + if (dcc->fastsend) { + /* no need to call this function anymore.. + in fact it just eats all the cpu.. */ + dcc->waitforend = TRUE; + g_source_remove(dcc->tagwrite); + dcc->tagwrite = -1; + } else { + signal_emit("dcc closed", 1, dcc); + dcc_destroy(dcc); + } + return; + } + + ret = net_transmit(dcc->handle, dcc->databuf, ret); + if (ret > 0) dcc->transfd += ret; + dcc->gotalldata = FALSE; + + lseek(dcc->fhandle, dcc->transfd, SEEK_SET); + + signal_emit("dcc transfer update", 1, dcc); +} + +/* input function: DCC SEND - received some data */ +static void dcc_send_read_size(DCC_REC *dcc) +{ + guint32 bytes; + int ret; + + g_return_if_fail(dcc != NULL); + + if (dcc->count_pos == 4) + return; + + /* we need to get 4 bytes.. */ + ret = net_receive(dcc->handle, dcc->count_buf+dcc->count_pos, + 4-dcc->count_pos); + if (ret == -1) { + signal_emit("dcc closed", 1, dcc); + dcc_destroy(dcc); + return; + } + + dcc->count_pos += ret; + + if (dcc->count_pos != 4) + return; + + memcpy(&bytes, dcc->count_buf, 4); + bytes = (guint32) ntohl(bytes); + + dcc->gotalldata = (long) bytes == dcc->transfd; + dcc->count_pos = 0; + + if (!dcc->fastsend) { + /* send more data.. */ + dcc_send_data(dcc); + } + + if (dcc->waitforend && dcc->gotalldata) { + /* file is sent */ + signal_emit("dcc closed", 1, dcc); + dcc_destroy(dcc); + } +} + +/* input function: DCC SEND - someone tried to connect to our socket */ +static void dcc_send_connected(DCC_REC *dcc) +{ + GIOChannel *handle; + IPADDR addr; + int port; + + g_return_if_fail(dcc != NULL); + + /* accept connection */ + handle = net_accept(dcc->handle, &addr, &port); + if (handle == NULL) + return; + + /* TODO: some kind of paranoia check would be nice. it would check + that the host of the nick who we sent the request matches the + address who connected us. */ + + g_source_remove(dcc->tagconn); + net_disconnect(dcc->handle); + + dcc->starttime = time(NULL); + dcc->fastsend = settings_get_bool("dcc_fast_send"); + dcc->handle = handle; + memcpy(&dcc->addr, &addr, sizeof(IPADDR)); + net_ip2host(&dcc->addr, dcc->addrstr); + dcc->port = port; + + dcc->databufsize = settings_get_int("dcc_block_size"); + if (dcc->databufsize <= 0) dcc->databufsize = 2048; + dcc->databuf = g_malloc(dcc->databufsize); + + dcc->tagread = g_input_add(handle, G_INPUT_READ, + (GInputFunction) dcc_send_read_size, dcc); + dcc->tagwrite = !dcc->fastsend ? -1 : + g_input_add(handle, G_INPUT_WRITE, + (GInputFunction) dcc_send_data, dcc); + + signal_emit("dcc connected", 1, dcc); + + if (!dcc->fastsend) { + /* send first block */ + dcc->gotalldata = TRUE; + dcc_send_data(dcc); + } +} + +static char *dcc_send_get_file(const char *fname) +{ + char *str, *path; + + str = convert_home(fname); + if (!g_path_is_absolute(str)) { + /* full path not given to file, use dcc_upload_path */ + g_free(str); + + path = convert_home(settings_get_str("dcc_upload_path")); + str = g_strconcat(path, G_DIR_SEPARATOR_S, fname, NULL); + g_free(path); + } + + return str; +} + +/* SYNTAX: DCC SEND <nick> <file> */ +static void cmd_dcc_send(const char *data, IRC_SERVER_REC *server, + WI_ITEM_REC *item) +{ + char *target, *fname, *str; + void *free_arg; + char host[MAX_IP_LEN]; + int hfile, port; + long fsize; + DCC_REC *dcc, *chat; + IPADDR own_ip; + GIOChannel *handle; + + g_return_if_fail(data != NULL); + + if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, + &target, &fname)) + return; + if (*target == '\0' || *fname == '\0') + cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + /* if we're in dcc chat, send the request via it. */ + chat = item_get_dcc(item); + + if (chat != NULL && (chat->mirc_ctcp || + g_strcasecmp(target, chat->nick) != 0)) + chat = NULL; + + if ((server == NULL || !server->connected) && chat == NULL) + cmd_param_error(CMDERR_NOT_CONNECTED); + + if (dcc_find_request(DCC_TYPE_SEND, target, fname)) { + signal_emit("dcc error send exists", 2, target, fname); + cmd_params_free(free_arg); + return; + } + + str = dcc_send_get_file(fname); + hfile = open(str, O_RDONLY); + g_free(str); + + if (hfile == -1) { + signal_emit("dcc error file not found", 2, target, fname); + cmd_params_free(free_arg); + return; + } + fsize = lseek(hfile, 0, SEEK_END); + lseek(hfile, 0, SEEK_SET); + + /* start listening */ + handle = dcc_listen(chat != NULL ? chat->handle : + net_sendbuffer_handle(server->handle), + &own_ip, &port); + if (handle == NULL) { + close(hfile); + cmd_param_error(CMDERR_ERRNO); + } + + fname = g_basename(fname); + + dcc = dcc_create(DCC_TYPE_SEND, target, fname, server, chat); + dcc->handle = handle; + dcc->port = port; + dcc->size = fsize; + dcc->fhandle = hfile; + dcc->file_quoted = strchr(fname, ' ') != NULL; + dcc->tagconn = g_input_add(handle, G_INPUT_READ, + (GInputFunction) dcc_send_connected, dcc); + + /* send DCC request */ + dcc_make_address(&own_ip, host); + str = g_strdup_printf(dcc->file_quoted ? + "DCC SEND \"%s\" %s %d %lu" : + "DCC SEND %s %s %d %lu", + fname, host, port, fsize); + dcc_ctcp_message(server, target, chat, FALSE, str); + g_free(str); + + cmd_params_free(free_arg); +} + +void dcc_send_init(void) +{ + settings_add_bool("dcc", "dcc_fast_send", TRUE); + settings_add_str("dcc", "dcc_upload_path", "~"); + + command_bind("dcc send", NULL, (SIGNAL_FUNC) cmd_dcc_send); +} + +void dcc_send_deinit(void) +{ + command_unbind("dcc send", (SIGNAL_FUNC) cmd_dcc_send); +} diff --git a/src/irc/dcc/dcc.c b/src/irc/dcc/dcc.c index 1d0fc2dc..8e5cee14 100644 --- a/src/irc/dcc/dcc.c +++ b/src/irc/dcc/dcc.c @@ -1,7 +1,7 @@ /* dcc.c : irssi - Copyright (C) 1999-2000 Timo Sirainen + 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 @@ -26,16 +26,19 @@ #include "line-split.h" #include "settings.h" -#include "masks.h" #include "irc.h" -#include "dcc.h" +#include "dcc-chat.h" +#include "dcc-get.h" -void dcc_chat_init(void); -void dcc_chat_deinit(void); +void dcc_send_init(void); +void dcc_send_deinit(void); -void dcc_files_init(void); -void dcc_files_deinit(void); +void dcc_resume_init(void); +void dcc_resume_deinit(void); + +void dcc_autoget_init(void); +void dcc_autoget_deinit(void); #define DCC_TYPES 5 @@ -52,7 +55,7 @@ GSList *dcc_conns; static int dcc_timeouttag; /* Create new DCC record */ -DCC_REC *dcc_create(int type, GIOChannel *handle, const char *nick, const char *arg, +DCC_REC *dcc_create(int type, const char *nick, const char *arg, IRC_SERVER_REC *server, DCC_REC *chat) { DCC_REC *dcc; @@ -67,7 +70,6 @@ DCC_REC *dcc_create(int type, GIOChannel *handle, const char *nick, const char * dcc->type = type; dcc->arg = g_strdup(arg); dcc->nick = g_strdup(nick); - dcc->handle = handle; dcc->fhandle = -1; dcc->tagconn = dcc->tagread = dcc->tagwrite = -1; dcc->server = server; @@ -123,6 +125,8 @@ void dcc_destroy(DCC_REC *dcc) g_free_not_null(dcc->file); g_free_not_null(dcc->ircnet); + g_free_not_null(dcc->chat_id); + g_free_not_null(dcc->target); g_free(dcc->mynick); g_free(dcc->nick); g_free(dcc->arg); @@ -143,36 +147,34 @@ void dcc_make_address(IPADDR *ip, char *host) } } -/* Find DCC record, arg can be NULL */ -DCC_REC *dcc_find_item(int type, const char *nick, const char *arg) +DCC_REC *dcc_find_request_latest(int type) { - DCC_REC *dcc; - GSList *tmp; - - g_return_val_if_fail(nick != NULL, NULL); + DCC_REC *latest; + GSList *tmp; + latest = NULL; for (tmp = dcc_conns; tmp != NULL; tmp = tmp->next) { - dcc = tmp->data; + DCC_REC *dcc = tmp->data; - if (dcc->type == type && g_strcasecmp(dcc->nick, nick) == 0 && - (arg == NULL || strcmp(dcc->arg, arg) == 0)) - return dcc; + if (dcc->type == type && dcc_is_waiting_user(dcc)) + latest = dcc; } - return NULL; + return latest; } -/* Find DCC record by port # */ -DCC_REC *dcc_find_by_port(const char *nick, int port) +DCC_REC *dcc_find_request(int type, const char *nick, const char *arg) { - DCC_REC *dcc; GSList *tmp; + g_return_val_if_fail(nick != NULL, NULL); + for (tmp = dcc_conns; tmp != NULL; tmp = tmp->next) { - dcc = tmp->data; + DCC_REC *dcc = tmp->data; - if ((dcc->type == DCC_TYPE_GET || dcc->type == DCC_TYPE_SEND) && - dcc->port == port && g_strcasecmp(dcc->nick, nick) == 0) + if (dcc->type == type && !dcc_is_connected(dcc) && + g_strcasecmp(dcc->nick, nick) == 0 && + (arg == NULL || strcmp(dcc->arg, arg) == 0)) return dcc; } @@ -198,6 +200,31 @@ int dcc_str2type(const char *type) return 0; } +GIOChannel *dcc_listen(GIOChannel *interface, IPADDR *ip, int *port) +{ + if (net_getsockname(interface, ip, NULL) == -1) + return NULL; + + *port = settings_get_int("dcc_port"); + return net_listen(ip, port); +} + +void dcc_get_address(const char *str, IPADDR *ip) +{ + unsigned long addr; + + if (strchr(str, ':') == NULL) { + /* normal IPv4 address in 32bit number form */ + addr = strtoul(str, NULL, 10); + ip->family = AF_INET; + addr = (unsigned long) ntohl(addr); + memcpy(&ip->addr, &addr, 4); + } else { + /* IPv6 - in standard form */ + net_host2ip(str, ip); + } +} + void dcc_ctcp_message(IRC_SERVER_REC *server, const char *target, DCC_REC *chat, int notice, const char *msg) { @@ -206,15 +233,14 @@ void dcc_ctcp_message(IRC_SERVER_REC *server, const char *target, if (chat != NULL && chat->sendbuf != NULL) { /* send it via open DCC chat */ str = g_strdup_printf("%s\001%s\001", chat->mirc_ctcp ? "" : - notice ? "CTCP_REPLY " : "CTCP_MESSAGE ", msg); + notice ? "CTCP_REPLY " : + "CTCP_MESSAGE ", msg); dcc_chat_send(chat, str); + g_free(str); } else { - str = g_strdup_printf("%s %s :\001%s\001", - notice ? "NOTICE" : "PRIVMSG", target, msg); - irc_send_cmd(server, str); + irc_send_cmdv(server, "%s %s :\001%s\001", + notice ? "NOTICE" : "PRIVMSG", target, msg); } - - g_free(str); } /* Server connected, check if there's any open dcc sessions for this ircnet.. */ @@ -252,321 +278,232 @@ static void dcc_server_disconnected(IRC_SERVER_REC *server) if (dcc->server != server) continue; - if (dcc->ircnet == NULL) - dcc->server = NULL; - else { - dcc->server = (IRC_SERVER_REC *) server_find_chatnet(dcc->ircnet); - if (dcc->server != NULL) { - g_free(dcc->mynick); - dcc->mynick = g_strdup(dcc->server->nick); - } + dcc->server = dcc->ircnet == NULL ? NULL : + IRC_SERVER(server_find_chatnet(dcc->ircnet)); + if (dcc->server != NULL) { + g_free(dcc->mynick); + dcc->mynick = g_strdup(dcc->server->nick); } } } -static void dcc_get_address(const char *str, IPADDR *ip) +/* Handle incoming DCC CTCP messages - either from IRC server or DCC chat */ +static void ctcp_msg_dcc(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr, + const char *target, DCC_REC *chat) { - unsigned long addr; + char *args, *str; - if (strchr(str, ':') == NULL) { - /* normal IPv4 address in 32bit number form */ - addr = strtoul(str, NULL, 10); - ip->family = AF_INET; - addr = (unsigned long) ntohl(addr); - memcpy(&ip->addr, &addr, 4); - } else { - /* IPv6 - in standard form */ - net_host2ip(str, ip); + str = g_strconcat("ctcp msg dcc ", data, NULL); + args = strchr(str+13, ' '); + if (args != NULL) *args++ = '\0'; else args = ""; + + g_strdown(str+13); + if (!signal_emit(str, 6, server, args, nick, addr, target, chat)) { + signal_emit("default ctcp msg dcc", 6, + server, data, nick, addr, target, chat); } + g_free(str); } -/* Handle incoming DCC CTCP messages */ -static void dcc_ctcp_msg(IRC_SERVER_REC *server, char *data, - char *sender, char *sendaddr, - char *target, DCC_REC *chat) +/* Handle incoming DCC CTCP replies - either from IRC server or DCC chat */ +static void ctcp_reply_dcc(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr, + const char *target) { - char *type, *arg, *addrstr, *portstr, *sizestr, *str; - void *free_arg; - const char *masks; - DCC_REC *dcc, *olddcc; - long size; - int dcctype, port; - - g_return_if_fail(data != NULL); - g_return_if_fail(sender != NULL); - - if (!cmd_get_params(data, &free_arg, 5 | PARAM_FLAG_NOQUOTES, - &type, &arg, &addrstr, &portstr, &sizestr)) - return; - - if (sscanf(portstr, "%d", &port) != 1) port = 0; - if (sscanf(sizestr, "%ld", &size) != 1) size = 0; - - dcctype = SWAP_SENDGET(dcc_str2type(type)); - olddcc = dcc_find_item(dcctype, sender, - dcctype == DCC_TYPE_CHAT ? NULL : arg); - if (olddcc != NULL) { - /* same DCC request offered again */ - if (olddcc->type == DCC_TYPE_CHAT && - olddcc->handle != NULL && olddcc->starttime == 0) { - /* we requested dcc chat, they requested - dcc chat from us .. allow it. */ - dcc_destroy(olddcc); - } else { - /* if the connection isn't open, update the port, - otherwise just ignore */ - if (olddcc->handle == NULL) - olddcc->port = port; - cmd_params_free(free_arg); - return; - } - } - - dcc = dcc_create(dcctype, NULL, sender, arg, server, chat); - dcc_get_address(addrstr, &dcc->addr); - net_ip2host(&dcc->addr, dcc->addrstr); - dcc->port = port; - dcc->size = size; - - switch (dcc->type) - { - case DCC_TYPE_GET: - /* send request */ - signal_emit("dcc request", 2, dcc, sendaddr); - break; - - case DCC_TYPE_CHAT: - /* send request */ - signal_emit("dcc request", 2, dcc, sendaddr); - - masks = settings_get_str("dcc_autochat_masks"); - if (olddcc != NULL || - (*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); - } - break; - - case DCC_TYPE_RESUME: - case DCC_TYPE_ACCEPT: - /* handle this in dcc-files.c */ - dcc_destroy(dcc); - break; - - default: - /* unknown DCC command */ - signal_emit("dcc unknown ctcp", 3, data, sender, sendaddr); - dcc_destroy(dcc); - break; - } - - cmd_params_free(free_arg); + char *args, *str; + + str = g_strconcat("ctcp reply dcc ", data, NULL); + args = strchr(str+15, ' '); + if (args != NULL) *args++ = '\0'; else args = ""; + + g_strdown(str+15); + if (!signal_emit(str, 5, server, args, nick, addr, target)) { + signal_emit("default ctcp reply dcc", 5, + server, data, nick, addr, target); + } + g_free(str); } -/* Handle incoming DCC CTCP replies */ -static void dcc_ctcp_reply(IRC_SERVER_REC *server, char *data, - char *sender, char *sendaddr) +/* CTCP REPLY: REJECT */ +static void ctcp_reply_dcc_reject(IRC_SERVER_REC *server, const char *data, + const char *nick, const char *addr, + DCC_REC *chat) { - char *cmd, *subcmd, *args; - void *free_arg; - int type; - DCC_REC *dcc; - - g_return_if_fail(data != NULL); - g_return_if_fail(sender != NULL); - - if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST, &cmd, &subcmd, &args)) - return; - - if (g_strcasecmp(cmd, "REJECT") == 0) - { - type = dcc_str2type(subcmd); - dcc = dcc_find_item(type, sender, type == DCC_TYPE_CHAT ? NULL : args); - if (dcc != NULL) - { - signal_emit("dcc closed", 1, dcc); - dcc_destroy(dcc); - } - } - else - { - /* unknown dcc ctcp reply */ - signal_emit("dcc unknown reply", 3, data, sender, sendaddr); - } - - cmd_params_free(free_arg); + DCC_REC *dcc; + char *typestr, *args; + int type; + + typestr = g_strdup(data); + args = strchr(typestr, ' '); + if (args != NULL) *args++ = '\0'; else args = ""; + + type = dcc_str2type(typestr); + dcc = dcc_find_request(type, nick, + type == DCC_TYPE_CHAT ? NULL : args); + if (dcc != NULL) { + signal_emit("dcc closed", 1, dcc); + dcc_destroy(dcc); + } + + g_free(typestr); } -/* reject DCC request */ +/* Reject a DCC request */ void dcc_reject(DCC_REC *dcc, IRC_SERVER_REC *server) { - char *str; + g_return_if_fail(dcc != NULL); - g_return_if_fail(dcc != NULL); + if (dcc->server != NULL) server = dcc->server; + if (server != NULL && (dcc->type != DCC_TYPE_CHAT || + dcc->starttime == 0)) { + signal_emit("dcc rejected", 1, dcc); + irc_send_cmdv(server, "NOTICE %s :\001DCC REJECT %s %s\001", + dcc->nick, dcc_type2str(SWAP_SENDGET(dcc->type)), + dcc->arg); + } - if (dcc->server != NULL) server = dcc->server; - if (server != NULL && (dcc->type != DCC_TYPE_CHAT || dcc->starttime == 0)) - { - signal_emit("dcc rejected", 1, dcc); - str = g_strdup_printf("NOTICE %s :\001DCC REJECT %s %s\001", - dcc->nick, dcc_type2str(SWAP_SENDGET(dcc->type)), dcc->arg); + signal_emit("dcc closed", 1, dcc); + dcc_destroy(dcc); +} + +static int dcc_timeout_func(void) +{ + GSList *tmp, *next; + time_t now; - irc_send_cmd(server, str); - g_free(str); - } + now = time(NULL)-settings_get_int("dcc_timeout"); + for (tmp = dcc_conns; tmp != NULL; tmp = next) { + DCC_REC *rec = tmp->data; - signal_emit("dcc closed", 1, dcc); - dcc_destroy(dcc); + next = tmp->next; + if (rec->tagread == -1 && now > rec->created) { + /* timed out. */ + dcc_reject(rec, NULL); + } + } + return 1; } -/* SYNTAX: DCC CLOSE <type> <nick> [<file>] */ -static void cmd_dcc_close(char *data, IRC_SERVER_REC *server) +static void event_no_such_nick(IRC_SERVER_REC *server, char *data) { - DCC_REC *dcc; - GSList *tmp, *next; - char *type, *nick, *arg; - void *free_arg; - gboolean found; - int itype; + char *params, *nick; + GSList *tmp, *next; - g_return_if_fail(data != NULL); + g_return_if_fail(data != NULL); - if (!cmd_get_params(data, &free_arg, 3, &type, &nick, &arg)) - return; + params = event_get_params(data, 2, NULL, &nick); - if (*nick == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + /* check if we've send any dcc requests to this nick.. */ + for (tmp = dcc_conns; tmp != NULL; tmp = next) { + DCC_REC *dcc = tmp->data; - g_strup(type); - itype = dcc_str2type(type); - if (itype == 0) - { - signal_emit("dcc error unknown type", 1, type); - cmd_params_free(free_arg); - return; - } - - dcc = NULL; found = FALSE; - for (tmp = dcc_conns; tmp != NULL; tmp = next) - { - dcc = tmp->data; - next = tmp->next; - - if (dcc->type == itype && g_strcasecmp(nick, dcc->nick) == 0) - { - dcc_reject(dcc, server); - found = TRUE; - } - } - - if (!found) - signal_emit("dcc error close not found", 3, type, nick, arg); - - cmd_params_free(free_arg); -} + next = tmp->next; + if (!dcc_is_connected(dcc) && dcc->server == server && + g_strcasecmp(dcc->nick, nick) == 0) { + signal_emit("dcc closed", 1, dcc); + dcc_destroy(dcc); + } + } -static void cmd_dcc(const char *data, IRC_SERVER_REC *server, void *item) -{ - command_runsub("dcc", data, server, item); + g_free(params); } -static int dcc_timeout_func(void) +/* SYNTAX: DCC CLOSE <type> <nick> [<file>] */ +static void cmd_dcc_close(char *data, IRC_SERVER_REC *server) { - GSList *tmp, *next; - time_t now; - - now = time(NULL)-settings_get_int("dcc_timeout"); - for (tmp = dcc_conns; tmp != NULL; tmp = next) - { - DCC_REC *rec = tmp->data; - - next = tmp->next; - if (rec->tagread == -1 && now > rec->created) - { - /* timed out. */ - dcc_reject(rec, NULL); - } - } - return 1; -} + GSList *tmp, *next; + char *typestr, *nick, *arg; + void *free_arg; + int found, type; -static void event_no_such_nick(IRC_SERVER_REC *server, char *data) -{ - char *params, *nick; - GSList *tmp, *next; + g_return_if_fail(data != NULL); + + if (!cmd_get_params(data, &free_arg, 3, &typestr, &nick, &arg)) + return; + + if (*nick == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + g_strup(typestr); + type = dcc_str2type(typestr); + if (type == 0) { + signal_emit("dcc error unknown type", 1, typestr); + cmd_params_free(free_arg); + return; + } - g_return_if_fail(data != NULL); + found = FALSE; + for (tmp = dcc_conns; tmp != NULL; tmp = next) { + DCC_REC *dcc = tmp->data; - params = event_get_params(data, 2, NULL, &nick); + next = tmp->next; + if (dcc->type == type && + g_strcasecmp(dcc->chat_id != NULL ? + dcc->chat_id : dcc->nick, nick) == 0 && + (*arg == '\0' || strcmp(dcc->arg, arg) == 0)) { + dcc_reject(dcc, server); + found = TRUE; + } + } - /* check if we've send any dcc requests to this nick.. */ - for (tmp = dcc_conns; tmp != NULL; tmp = next) - { - DCC_REC *rec = tmp->data; + if (!found) { + signal_emit("dcc error close not found", 3, + typestr, nick, arg); + } - next = tmp->next; - if (g_strcasecmp(rec->nick, nick) == 0 && rec->starttime == 0) - { - /* timed out. */ - signal_emit("dcc closed", 1, rec); - dcc_destroy(rec); - } - } + cmd_params_free(free_arg); +} - g_free(params); +static void cmd_dcc(const char *data, IRC_SERVER_REC *server, void *item) +{ + command_runsub("dcc", data, server, item); } void irc_dcc_init(void) { - dcc_conns = NULL; - dcc_timeouttag = g_timeout_add(1000, (GSourceFunc) dcc_timeout_func, NULL); - - 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); - settings_add_str("dcc", "dcc_autoget_masks", ""); - settings_add_str("dcc", "dcc_autochat_masks", ""); - - settings_add_bool("dcc", "dcc_fast_send", TRUE); - settings_add_str("dcc", "dcc_upload_path", "~"); - - settings_add_bool("dcc", "dcc_mirc_ctcp", FALSE); - settings_add_int("dcc", "dcc_block_size", 2048); - settings_add_int("dcc", "dcc_port", 0); - settings_add_int("dcc", "dcc_timeout", 300); - - signal_add("server connected", (SIGNAL_FUNC) dcc_server_connected); - signal_add("server disconnected", (SIGNAL_FUNC) dcc_server_disconnected); - signal_add("ctcp reply dcc", (SIGNAL_FUNC) dcc_ctcp_reply); - signal_add("ctcp msg dcc", (SIGNAL_FUNC) dcc_ctcp_msg); - command_bind("dcc", NULL, (SIGNAL_FUNC) cmd_dcc); - command_bind("dcc close", NULL, (SIGNAL_FUNC) cmd_dcc_close); - signal_add("event 401", (SIGNAL_FUNC) event_no_such_nick); - - dcc_chat_init(); - dcc_files_init(); + dcc_conns = NULL; + dcc_timeouttag = g_timeout_add(1000, (GSourceFunc) dcc_timeout_func, NULL); + + settings_add_bool("dcc", "dcc_mirc_ctcp", FALSE); + settings_add_int("dcc", "dcc_port", 0); + settings_add_int("dcc", "dcc_timeout", 300); + settings_add_int("dcc", "dcc_block_size", 2048); + + signal_add("server connected", (SIGNAL_FUNC) dcc_server_connected); + signal_add("server disconnected", (SIGNAL_FUNC) dcc_server_disconnected); + signal_add("ctcp msg dcc", (SIGNAL_FUNC) ctcp_msg_dcc); + signal_add("ctcp reply dcc", (SIGNAL_FUNC) ctcp_reply_dcc); + signal_add("ctcp reply dcc reject", (SIGNAL_FUNC) ctcp_reply_dcc_reject); + command_bind("dcc", NULL, (SIGNAL_FUNC) cmd_dcc); + command_bind("dcc close", NULL, (SIGNAL_FUNC) cmd_dcc_close); + signal_add("event 401", (SIGNAL_FUNC) event_no_such_nick); + + dcc_chat_init(); + dcc_get_init(); + dcc_send_init(); + dcc_resume_init(); + dcc_autoget_init(); } void irc_dcc_deinit(void) { - dcc_chat_deinit(); - dcc_files_deinit(); - - signal_remove("server connected", (SIGNAL_FUNC) dcc_server_connected); - signal_remove("server disconnected", (SIGNAL_FUNC) dcc_server_disconnected); - signal_remove("ctcp reply dcc", (SIGNAL_FUNC) dcc_ctcp_reply); - signal_remove("ctcp msg dcc", (SIGNAL_FUNC) dcc_ctcp_msg); - command_unbind("dcc", (SIGNAL_FUNC) cmd_dcc); - command_unbind("dcc close", (SIGNAL_FUNC) cmd_dcc_close); - signal_remove("event 401", (SIGNAL_FUNC) event_no_such_nick); - - g_source_remove(dcc_timeouttag); - - while (dcc_conns != NULL) - dcc_destroy(dcc_conns->data); + dcc_chat_deinit(); + dcc_get_deinit(); + dcc_send_deinit(); + dcc_resume_deinit(); + dcc_autoget_deinit(); + + signal_remove("server connected", (SIGNAL_FUNC) dcc_server_connected); + signal_remove("server disconnected", (SIGNAL_FUNC) dcc_server_disconnected); + signal_remove("ctcp msg dcc", (SIGNAL_FUNC) ctcp_msg_dcc); + signal_remove("ctcp reply dcc", (SIGNAL_FUNC) ctcp_reply_dcc); + signal_remove("ctcp reply dcc reject", (SIGNAL_FUNC) ctcp_reply_dcc_reject); + command_unbind("dcc", (SIGNAL_FUNC) cmd_dcc); + command_unbind("dcc close", (SIGNAL_FUNC) cmd_dcc_close); + signal_remove("event 401", (SIGNAL_FUNC) event_no_such_nick); + + g_source_remove(dcc_timeouttag); + + while (dcc_conns != NULL) + dcc_destroy(dcc_conns->data); } diff --git a/src/irc/dcc/dcc.h b/src/irc/dcc/dcc.h index 88e4effb..eabbef68 100644 --- a/src/irc/dcc/dcc.h +++ b/src/irc/dcc/dcc.h @@ -2,6 +2,7 @@ #define __DCC_H #include "network.h" +#include "irc-servers.h" enum { DCC_TYPE_CHAT = 1, @@ -11,12 +12,6 @@ enum { DCC_TYPE_ACCEPT }; -enum { - DCC_GET_RENAME = 0, /* this also acts as default */ - DCC_GET_OVERWRITE, - DCC_GET_RESUME -}; - #define SWAP_SENDGET(a) ((a) == DCC_TYPE_SEND ? DCC_TYPE_GET : \ (a) == DCC_TYPE_GET ? DCC_TYPE_SEND : (a)) @@ -25,7 +20,9 @@ typedef struct DCC_REC { time_t created; IRC_SERVER_REC *server; + char *chat_id; /* unique identifier for dcc chat. usually same as nick. */ char *nick; + char *target; /* who the request was sent to - your nick, channel or NULL if you sent the request */ struct DCC_REC *chat; /* if the request came through DCC chat */ @@ -53,6 +50,7 @@ typedef struct DCC_REC { unsigned int waitforend:1; /* DCC fast send: file is sent, just wait for the replies from the other side */ unsigned int gotalldata:1; /* DCC fast send: got all acks from the other end (needed to make sure the end of transfer works right) */ + unsigned int file_quoted:1; /* file name was received quoted ("file name") */ unsigned int mirc_ctcp:1; /* DCC chat: Send CTCPs without the CTCP_MESSAGE prefix */ unsigned int connection_lost:1; /* DCC chat: other side closed connection */ unsigned int destroyed:1; /* We're about to destroy this DCC recond */ @@ -72,18 +70,22 @@ extern GSList *dcc_conns; void dcc_init(void); void dcc_deinit(void); -/* Find DCC record, arg can be NULL */ -DCC_REC *dcc_find_item(int type, const char *nick, const char *arg); -DCC_REC *dcc_find_by_port(const char *nick, int port); +/* Find waiting DCC requests (non-connected) */ +DCC_REC *dcc_find_request_latest(int type); +DCC_REC *dcc_find_request(int type, const char *nick, const char *arg); const char *dcc_type2str(int type); int dcc_str2type(const char *type); void dcc_make_address(IPADDR *ip, char *host); -DCC_REC *dcc_create(int type, GIOChannel *handle, const char *nick, - const char *arg, IRC_SERVER_REC *server, DCC_REC *chat); +DCC_REC *dcc_create(int type, const char *nick, const char *arg, + IRC_SERVER_REC *server, DCC_REC *chat); void dcc_destroy(DCC_REC *dcc); +GIOChannel *dcc_listen(GIOChannel *interface, IPADDR *ip, int *port); + +void dcc_get_address(const char *str, IPADDR *ip); + /* Send a CTCP message/notify to target. Send the CTCP via DCC chat if `chat' is specified. */ void dcc_ctcp_message(IRC_SERVER_REC *server, const char *target, @@ -94,7 +96,19 @@ 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(WI_ITEM_REC *item); -/* reject DCC request */ +/* Reject a DCC request */ void dcc_reject(DCC_REC *dcc, IRC_SERVER_REC *server); +/* fully connected? */ +#define dcc_is_connected(dcc) \ + ((dcc)->starttime != 0) + +/* not connected, we're waiting for other side to connect */ +#define dcc_is_listening(dcc) \ + ((dcc)->handle != NULL && (dcc)->starttime == 0) + +/* not connected, waiting for user to accept it */ +#define dcc_is_waiting_user(dcc) \ + ((dcc)->handle == NULL) + #endif |