1
0
mirror of https://github.com/irssi/irssi.git synced 2024-12-04 14:46:39 -05:00

DCC cleanups - half rewrite. New features: file names with spaces work

properly, you can have multiple dcc chats with same people (or more
useful, same nick in different ircnets), /DCC CHAT|GET|RESUME with no
arguments accepts the last request, notifies if dcc request was sent to
channel, warns about connecting to lowports, /SET dcc_autoget_lowports
specifies if autogetting should work with lowports, complains of
invalid dcc ctcps instead of ignoring. And fixed /SET dcc_autorename
OFF which didn't work before.


git-svn-id: http://svn.irssi.org/repos/irssi/trunk@1135 dbcabf3a-b0e7-0310-adc4-f8d773084564
This commit is contained in:
Timo Sirainen 2001-01-18 02:30:59 +00:00 committed by cras
parent babf7c77ac
commit ce6e5a12f9
15 changed files with 1507 additions and 1072 deletions

View File

@ -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>

View File

@ -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);

View File

@ -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 } },

View File

@ -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,

View File

@ -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

84
src/irc/dcc/dcc-autoget.c Normal file
View File

@ -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);
}

View File

@ -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);
}

11
src/irc/dcc/dcc-chat.h Normal file
View File

@ -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

View File

@ -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);
}

385
src/irc/dcc/dcc-get.c Normal file
View File

@ -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);
}

28
src/irc/dcc/dcc-get.h Normal file
View File

@ -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

177
src/irc/dcc/dcc-resume.c Normal file
View File

@ -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);
}

267
src/irc/dcc/dcc-send.c Normal file
View File

@ -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);
}

View File

@ -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,18 +147,33 @@ 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 *latest;
GSList *tmp;
latest = NULL;
for (tmp = dcc_conns; tmp != NULL; tmp = tmp->next) {
DCC_REC *dcc = tmp->data;
if (dcc->type == type && dcc_is_waiting_user(dcc))
latest = dcc;
}
return latest;
}
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 == type && 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;
}
@ -162,23 +181,6 @@ DCC_REC *dcc_find_item(int type, const char *nick, const char *arg)
return NULL;
}
/* Find DCC record by port # */
DCC_REC *dcc_find_by_port(const char *nick, int port)
{
DCC_REC *dcc;
GSList *tmp;
for (tmp = dcc_conns; tmp != NULL; tmp = tmp->next) {
dcc = tmp->data;
if ((dcc->type == DCC_TYPE_GET || dcc->type == DCC_TYPE_SEND) &&
dcc->port == port && g_strcasecmp(dcc->nick, nick) == 0)
return dcc;
}
return NULL;
}
const char *dcc_type2str(int type)
{
g_return_val_if_fail(type >= 1 && type <= DCC_TYPES, NULL);
@ -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,219 +278,180 @@ 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;
char *args, *str;
g_return_if_fail(data != NULL);
g_return_if_fail(sender != NULL);
str = g_strconcat("ctcp reply dcc ", data, NULL);
args = strchr(str+15, ' ');
if (args != NULL) *args++ = '\0'; else args = "";
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);
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;
DCC_REC *dcc;
char *typestr, *args;
int type;
g_return_if_fail(data != NULL);
g_return_if_fail(sender != NULL);
typestr = g_strdup(data);
args = strchr(typestr, ' ');
if (args != NULL) *args++ = '\0'; else args = "";
if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST, &cmd, &subcmd, &args))
return;
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);
}
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);
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);
}
irc_send_cmd(server, str);
g_free(str);
}
static int dcc_timeout_func(void)
{
GSList *tmp, *next;
time_t now;
signal_emit("dcc closed", 1, dcc);
dcc_destroy(dcc);
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;
}
static void event_no_such_nick(IRC_SERVER_REC *server, char *data)
{
char *params, *nick;
GSList *tmp, *next;
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, NULL, &nick);
/* check if we've send any dcc requests to this nick.. */
for (tmp = dcc_conns; tmp != NULL; tmp = next) {
DCC_REC *dcc = tmp->data;
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);
}
}
g_free(params);
}
/* SYNTAX: DCC CLOSE <type> <nick> [<file>] */
static void cmd_dcc_close(char *data, IRC_SERVER_REC *server)
{
DCC_REC *dcc;
GSList *tmp, *next;
char *type, *nick, *arg;
void *free_arg;
gboolean found;
int itype;
GSList *tmp, *next;
char *typestr, *nick, *arg;
void *free_arg;
int found, type;
g_return_if_fail(data != NULL);
g_return_if_fail(data != NULL);
if (!cmd_get_params(data, &free_arg, 3, &type, &nick, &arg))
return;
if (!cmd_get_params(data, &free_arg, 3, &typestr, &nick, &arg))
return;
if (*nick == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
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;
}
found = FALSE;
for (tmp = dcc_conns; tmp != NULL; tmp = next) {
DCC_REC *dcc = tmp->data;
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;
}
}
if (!found) {
signal_emit("dcc error close not found", 3,
typestr, nick, arg);
}
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);
}
static void cmd_dcc(const char *data, IRC_SERVER_REC *server, void *item)
@ -472,101 +459,51 @@ static void cmd_dcc(const char *data, IRC_SERVER_REC *server, void *item)
command_runsub("dcc", data, server, item);
}
static int dcc_timeout_func(void)
{
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;
}
static void event_no_such_nick(IRC_SERVER_REC *server, char *data)
{
char *params, *nick;
GSList *tmp, *next;
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, NULL, &nick);
/* check if we've send any dcc requests to this nick.. */
for (tmp = dcc_conns; tmp != NULL; tmp = next)
{
DCC_REC *rec = tmp->data;
next = tmp->next;
if (g_strcasecmp(rec->nick, nick) == 0 && rec->starttime == 0)
{
/* timed out. */
signal_emit("dcc closed", 1, rec);
dcc_destroy(rec);
}
}
g_free(params);
}
void irc_dcc_init(void)
{
dcc_conns = NULL;
dcc_timeouttag = g_timeout_add(1000, (GSourceFunc) dcc_timeout_func, NULL);
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_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);
settings_add_bool("dcc", "dcc_fast_send", TRUE);
settings_add_str("dcc", "dcc_upload_path", "~");
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);
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_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();
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 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);
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);
g_source_remove(dcc_timeouttag);
while (dcc_conns != NULL)
dcc_destroy(dcc_conns->data);
while (dcc_conns != NULL)
dcc_destroy(dcc_conns->data);
}

View File

@ -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