mirror of
https://github.com/irssi/irssi.git
synced 2026-08-11 12:53:42 +02:00
During shutdown, query_destroy() fires before dcc_close(), leaving the DCC record pointing at a freed window. dcc_closed() then calls printformat() with NULL as the window item, which causes a NULL dereference in store_lineinfo_tmp() at textbuffer-formats.c:148. Fix by looking up the query window and passing its name string to printformat(), falling back to NULL which safely prints to the server window. Also removes dead code where sender was allocated but never used in the printformat call. Reproducer: /dcc chat <nick>, then /quit Tested against both a local server and irc.libera.chat.
390 lines
11 KiB
C
390 lines
11 KiB
C
/*
|
|
fe-dcc-chat.c : irssi
|
|
|
|
Copyright (C) 1999-2002 Timo Sirainen
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along
|
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#include "module.h"
|
|
#include <irssi/src/core/signals.h>
|
|
#include <irssi/src/core/commands.h>
|
|
#include <irssi/src/core/levels.h>
|
|
#include <irssi/src/core/settings.h>
|
|
#include <irssi/src/core/misc.h>
|
|
|
|
#include <irssi/src/irc/core/irc.h>
|
|
#include <irssi/src/irc/core/irc-servers.h>
|
|
#include <irssi/src/irc/core/irc-queries.h>
|
|
#include <irssi/src/irc/dcc/dcc-chat.h>
|
|
|
|
#include <irssi/src/fe-common/irc/dcc/module-formats.h>
|
|
#include <irssi/src/fe-common/core/printtext.h>
|
|
#include <irssi/src/fe-common/core/fe-messages.h>
|
|
|
|
#include <irssi/src/fe-common/core/chat-completion.h>
|
|
|
|
void fe_dcc_chat_messages_init(void);
|
|
void fe_dcc_chat_messages_deinit(void);
|
|
|
|
static void dcc_request(CHAT_DCC_REC *dcc)
|
|
{
|
|
if (!IS_DCC_CHAT(dcc)) return;
|
|
|
|
printformat(dcc->server, NULL, MSGLEVEL_DCC,
|
|
server_ischannel(SERVER(dcc->server), dcc->target) ? IRCTXT_DCC_CHAT_CHANNEL :
|
|
IRCTXT_DCC_CHAT, dcc->id, dcc->addrstr,
|
|
dcc->port, dcc->target);
|
|
}
|
|
|
|
static void dcc_connected(CHAT_DCC_REC *dcc)
|
|
{
|
|
char *sender;
|
|
|
|
if (!IS_DCC_CHAT(dcc)) return;
|
|
|
|
sender = g_strconcat("=", dcc->id, NULL);
|
|
printformat(dcc->server, NULL, MSGLEVEL_DCC,
|
|
IRCTXT_DCC_CHAT_CONNECTED,
|
|
dcc->id, dcc->addrstr, dcc->port);
|
|
|
|
if (query_find(NULL, sender) == NULL) {
|
|
int level = settings_get_level("autocreate_query_level");
|
|
int autocreate_dccquery = (level & MSGLEVEL_DCCMSGS) != 0;
|
|
|
|
if (!autocreate_dccquery)
|
|
completion_last_message_add(sender);
|
|
else
|
|
irc_query_create(dcc->servertag, sender, TRUE);
|
|
}
|
|
g_free(sender);
|
|
}
|
|
|
|
static void dcc_closed(CHAT_DCC_REC *dcc)
|
|
{
|
|
QUERY_REC *query;
|
|
char *sender;
|
|
|
|
if (!IS_DCC_CHAT(dcc)) return;
|
|
|
|
|
|
sender = g_strconcat("=", dcc->id, NULL);
|
|
query = query_find((SERVER_REC *)dcc->server, sender);
|
|
g_free(sender);
|
|
|
|
|
|
printformat(dcc->server, query ? query->name : NULL, MSGLEVEL_DCC,
|
|
IRCTXT_DCC_CHAT_DISCONNECTED, dcc->id);
|
|
}
|
|
|
|
static void dcc_chat_msg(CHAT_DCC_REC *dcc, const char *msg)
|
|
{
|
|
QUERY_REC *query;
|
|
char *sender, *freemsg;
|
|
|
|
g_return_if_fail(IS_DCC_CHAT(dcc));
|
|
g_return_if_fail(msg != NULL);
|
|
|
|
sender = g_strconcat("=", dcc->id, NULL);
|
|
query = query_find(NULL, sender);
|
|
|
|
if (settings_get_bool("emphasis"))
|
|
msg = freemsg = expand_emphasis((WI_ITEM_REC *) query, msg);
|
|
else
|
|
freemsg = NULL;
|
|
|
|
if (query == NULL)
|
|
completion_last_message_add(sender);
|
|
signal_emit("message dcc", 2, dcc, msg);
|
|
|
|
g_free_not_null(freemsg);
|
|
g_free(sender);
|
|
}
|
|
|
|
static void dcc_chat_action(CHAT_DCC_REC *dcc, const char *msg)
|
|
{
|
|
char *sender;
|
|
|
|
g_return_if_fail(IS_DCC_CHAT(dcc));
|
|
g_return_if_fail(msg != NULL);
|
|
|
|
sender = g_strconcat("=", dcc->id, NULL);
|
|
if (query_find(NULL, sender) == NULL)
|
|
completion_last_message_add(sender);
|
|
|
|
signal_emit("message dcc action", 2, dcc, msg);
|
|
g_free(sender);
|
|
}
|
|
|
|
static void dcc_chat_ctcp(CHAT_DCC_REC *dcc, const char *cmd, const char *data)
|
|
{
|
|
g_return_if_fail(IS_DCC_CHAT(dcc));
|
|
|
|
signal_emit("message dcc ctcp", 3, dcc, cmd, data);
|
|
}
|
|
|
|
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, CHAT_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))
|
|
return;
|
|
|
|
printformat(NULL, NULL, MSGLEVEL_DCC, IRCTXT_DCC_UNKNOWN_CTCP,
|
|
type, nick, args);
|
|
cmd_params_free(free_arg);
|
|
}
|
|
|
|
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))
|
|
return;
|
|
|
|
printformat(NULL, NULL, MSGLEVEL_DCC, IRCTXT_DCC_UNKNOWN_REPLY,
|
|
type, nick, args);
|
|
cmd_params_free(free_arg);
|
|
}
|
|
|
|
static void sig_dcc_destroyed(CHAT_DCC_REC *dcc)
|
|
{
|
|
QUERY_REC *query;
|
|
char *nick;
|
|
|
|
if (!IS_DCC_CHAT(dcc)) return;
|
|
|
|
nick = g_strconcat("=", dcc->id, NULL);
|
|
query = query_find(NULL, nick);
|
|
if (query != NULL) {
|
|
/* DCC chat closed, close the query with it. */
|
|
if (dcc->connection_lost) query->unwanted = TRUE;
|
|
query_destroy(query);
|
|
} else {
|
|
/* remove nick from msg completion
|
|
since it won't work anymore */
|
|
completion_last_message_remove(nick);
|
|
}
|
|
|
|
g_free(nick);
|
|
}
|
|
|
|
static void sig_query_destroyed(QUERY_REC *query)
|
|
{
|
|
CHAT_DCC_REC *dcc;
|
|
|
|
if (*query->name != '=')
|
|
return;
|
|
|
|
dcc = dcc_chat_find_id(query->name+1);
|
|
if (dcc != NULL && !dcc->destroyed) {
|
|
/* DCC query window closed, close the dcc chat too. */
|
|
dcc_close(DCC(dcc));
|
|
}
|
|
}
|
|
|
|
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);
|
|
if (g_ascii_strcasecmp(type, "CHAT") != 0) return;
|
|
|
|
printformat(NULL, NULL, MSGLEVEL_DCC,
|
|
IRCTXT_DCC_CHAT_NOT_FOUND, nick);
|
|
}
|
|
|
|
static void sig_dcc_list_print(CHAT_DCC_REC *dcc)
|
|
{
|
|
if (!IS_DCC_CHAT(dcc)) return;
|
|
|
|
printformat(NULL, NULL, MSGLEVEL_DCC, IRCTXT_DCC_LIST_LINE_CHAT,
|
|
dcc->id, "CHAT");
|
|
}
|
|
|
|
static void cmd_msg(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
|
|
{
|
|
CHAT_DCC_REC *dcc;
|
|
GHashTable *optlist;
|
|
char *text, *target;
|
|
void *free_arg;
|
|
|
|
g_return_if_fail(data != NULL);
|
|
|
|
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_UNKNOWN_OPTIONS |
|
|
PARAM_FLAG_OPTIONS | PARAM_FLAG_GETREST, "msg",
|
|
&optlist, &target, &text))
|
|
return;
|
|
|
|
/* handle only DCC messages */
|
|
if (g_strcmp0(target, "*") == 0)
|
|
dcc = item_get_dcc(item);
|
|
else if (*target == '=')
|
|
dcc = dcc_chat_find_id(target+1);
|
|
else
|
|
dcc = NULL;
|
|
|
|
if (dcc == NULL && *target == '=') {
|
|
printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
|
|
IRCTXT_DCC_CHAT_NOT_FOUND, target+1);
|
|
} else if (dcc != NULL) {
|
|
if (query_find(NULL, target) == NULL)
|
|
completion_last_message_add(target);
|
|
|
|
signal_emit("message dcc own", 2, dcc, text);
|
|
}
|
|
|
|
cmd_params_free(free_arg);
|
|
}
|
|
|
|
static void cmd_me(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
|
|
{
|
|
CHAT_DCC_REC *dcc;
|
|
|
|
dcc = item_get_dcc(item);
|
|
if (dcc != NULL)
|
|
signal_emit("message dcc own_action", 2, dcc, data);
|
|
}
|
|
|
|
static void cmd_action(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
|
|
{
|
|
CHAT_DCC_REC *dcc;
|
|
char *target, *text;
|
|
void *free_arg;
|
|
|
|
g_return_if_fail(data != NULL);
|
|
|
|
if (*data != '=') {
|
|
/* handle only DCC actions */
|
|
return;
|
|
}
|
|
|
|
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);
|
|
|
|
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);
|
|
} else {
|
|
if (query_find(NULL, target) == NULL)
|
|
completion_last_message_add(target);
|
|
|
|
signal_emit("message dcc own_action", 2, dcc, text);
|
|
}
|
|
cmd_params_free(free_arg);
|
|
}
|
|
|
|
static void cmd_ctcp(const char *data, SERVER_REC *server)
|
|
{
|
|
CHAT_DCC_REC *dcc;
|
|
char *target, *ctcpcmd, *ctcpdata;
|
|
void *free_arg;
|
|
|
|
g_return_if_fail(data != NULL);
|
|
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))
|
|
return;
|
|
if (*target == '\0' || *ctcpcmd == '\0')
|
|
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
|
|
|
|
if (*target != '=') {
|
|
/* handle only DCC CTCPs */
|
|
cmd_params_free(free_arg);
|
|
return;
|
|
}
|
|
|
|
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);
|
|
} else {
|
|
ascii_strup(ctcpcmd);
|
|
signal_emit("message dcc own_ctcp", 3, dcc, ctcpcmd, ctcpdata);
|
|
}
|
|
|
|
cmd_params_free(free_arg);
|
|
}
|
|
|
|
void fe_dcc_chat_init(void)
|
|
{
|
|
fe_dcc_chat_messages_init();
|
|
|
|
signal_add("dcc request", (SIGNAL_FUNC) dcc_request);
|
|
signal_add("dcc connected", (SIGNAL_FUNC) dcc_connected);
|
|
signal_add("dcc closed", (SIGNAL_FUNC) dcc_closed);
|
|
signal_add("dcc chat message", (SIGNAL_FUNC) dcc_chat_msg);
|
|
signal_add("dcc ctcp action", (SIGNAL_FUNC) dcc_chat_action);
|
|
signal_add("default dcc ctcp", (SIGNAL_FUNC) dcc_chat_ctcp);
|
|
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("dcc list print", (SIGNAL_FUNC) sig_dcc_list_print);
|
|
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);
|
|
signal_add("dcc error close not found", (SIGNAL_FUNC) dcc_error_close_not_found);
|
|
}
|
|
|
|
void fe_dcc_chat_deinit(void)
|
|
{
|
|
fe_dcc_chat_messages_deinit();
|
|
|
|
signal_remove("dcc request", (SIGNAL_FUNC) dcc_request);
|
|
signal_remove("dcc connected", (SIGNAL_FUNC) dcc_connected);
|
|
signal_remove("dcc closed", (SIGNAL_FUNC) dcc_closed);
|
|
signal_remove("dcc chat message", (SIGNAL_FUNC) dcc_chat_msg);
|
|
signal_remove("dcc ctcp action", (SIGNAL_FUNC) dcc_chat_action);
|
|
signal_remove("default dcc ctcp", (SIGNAL_FUNC) dcc_chat_ctcp);
|
|
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("dcc list print", (SIGNAL_FUNC) sig_dcc_list_print);
|
|
command_unbind("msg", (SIGNAL_FUNC) cmd_msg);
|
|
command_unbind("me", (SIGNAL_FUNC) cmd_me);
|
|
command_unbind("action", (SIGNAL_FUNC) cmd_action);
|
|
command_unbind("ctcp", (SIGNAL_FUNC) cmd_ctcp);
|
|
signal_remove("dcc error close not found", (SIGNAL_FUNC) dcc_error_close_not_found);
|
|
}
|