1
0
mirror of https://github.com/irssi/irssi.git synced 2024-06-23 06:35:36 +00:00
irssi/src/irc/core/irc-nicklist.c
Timo Sirainen 484f62f757 added server_change_nick() function for servers.[ch]. Lots of fixes for
support to multiple identical nicks. Should work now properly :)


git-svn-id: http://svn.irssi.org/repos/irssi/trunk@1351 dbcabf3a-b0e7-0310-adc4-f8d773084564
2001-03-04 10:30:41 +00:00

390 lines
11 KiB
C

/*
irc-nicklist.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 "misc.h"
#include "irc.h"
#include "irc-channels.h"
#include "irc-masks.h"
#include "irc-nicklist.h"
#include "modes.h"
#include "servers.h"
/* Add new nick to list */
NICK_REC *irc_nicklist_insert(IRC_CHANNEL_REC *channel, const char *nick,
int op, int voice, int send_massjoin)
{
NICK_REC *rec;
g_return_val_if_fail(IS_IRC_CHANNEL(channel), NULL);
g_return_val_if_fail(nick != NULL, NULL);
rec = g_new0(NICK_REC, 1);
rec->nick = g_strdup(nick);
if (op) rec->op = TRUE;
if (voice) rec->voice = TRUE;
rec->send_massjoin = send_massjoin;
nicklist_insert(CHANNEL(channel), rec);
return rec;
}
#define isnickchar(a) \
(isalnum((int) (a)) || (a) == '`' || (a) == '-' || (a) == '_' || \
(a) == '[' || (a) == ']' || (a) == '{' || (a) == '}' || \
(a) == '|' || (a) == '\\' || (a) == '^')
/* Remove all "extra" characters from `nick'. Like _nick_ -> nick */
char *irc_nick_strip(const char *nick)
{
char *stripped, *spos;
g_return_val_if_fail(nick != NULL, NULL);
spos = stripped = g_strdup(nick);
while (isnickchar(*nick)) {
if (isalnum((int) *nick))
*spos++ = *nick;
nick++;
}
if ((unsigned char) *nick >= 128)
*spos++ = *nick; /* just add it so that nicks won't match.. */
*spos = '\0';
return stripped;
}
static void event_names_list(IRC_SERVER_REC *server, const char *data)
{
IRC_CHANNEL_REC *chanrec;
char *params, *type, *channel, *names, *ptr;
g_return_if_fail(data != NULL);
params = event_get_params(data, 4, NULL, &type, &channel, &names);
chanrec = irc_channel_find(server, channel);
if (chanrec == NULL || chanrec->names_got) {
/* unknown channel / names list already read */
g_free(params);
return;
}
/* type = '=' = public, '*' = private, '@' = secret.
This is actually pretty useless to check here, but at least we
get to know if the channel is +p or +s a few seconds before
we receive the MODE reply... */
if (*type == '*')
parse_channel_modes(IRC_CHANNEL(chanrec), NULL, "+p");
else if (*type == '@')
parse_channel_modes(IRC_CHANNEL(chanrec), NULL, "+s");
while (*names != '\0') {
while (*names == ' ') names++;
ptr = names;
while (*names != '\0' && *names != ' ') names++;
if (*names != '\0') *names++ = '\0';
irc_nicklist_insert(chanrec, ptr+isnickflag(*ptr),
*ptr == '@', *ptr == '+', FALSE);
}
g_free(params);
}
static void event_end_of_names(SERVER_REC *server, const char *data)
{
char *params, *channel;
CHANNEL_REC *chanrec;
g_return_if_fail(server != NULL);
params = event_get_params(data, 2, NULL, &channel);
chanrec = channel_find(server, channel);
if (chanrec != NULL && !chanrec->names_got) {
nicklist_set_own(chanrec,
nicklist_find(chanrec, server->nick));
chanrec->chanop = chanrec->ownnick->op;
chanrec->names_got = TRUE;
signal_emit("channel joined", 1, chanrec);
}
g_free(params);
}
static void event_who(SERVER_REC *server, const char *data)
{
char *params, *nick, *channel, *user, *host, *stat, *realname, *hops;
CHANNEL_REC *chanrec;
NICK_REC *nickrec;
g_return_if_fail(data != NULL);
params = event_get_params(data, 8, NULL, &channel, &user, &host,
NULL, &nick, &stat, &realname);
/* get hop count */
hops = realname;
while (*realname != '\0' && *realname != ' ') realname++;
*realname++ = '\0';
while (*realname == ' ') realname++;
/* update host, realname, hopcount */
chanrec = channel_find(server, channel);
nickrec = chanrec == NULL ? NULL :
nicklist_find(chanrec, nick);
if (nickrec != NULL) {
if (nickrec->host == NULL) {
char *str = g_strdup_printf("%s@%s", user, host);
nicklist_set_host(chanrec, nickrec, str);
g_free(str);
}
if (nickrec->realname == NULL)
nickrec->realname = g_strdup(realname);
sscanf(hops, "%d", &nickrec->hops);
}
nicklist_update_flags(server, nick,
strchr(stat, 'G') != NULL, /* gone */
strchr(stat, '*') != NULL); /* ircop */
g_free(params);
}
static void event_whois(IRC_SERVER_REC *server, const char *data)
{
char *params, *nick, *realname;
GSList *nicks, *tmp;
NICK_REC *rec;
g_return_if_fail(data != NULL);
server->whois_coming = TRUE;
/* first remove the gone-flag, if user is gone
it will be set later.. */
params = event_get_params(data, 6, NULL, &nick, NULL,
NULL, NULL, &realname);
nicks = nicklist_get_same(SERVER(server), nick);
for (tmp = nicks; tmp != NULL; tmp = tmp->next->next) {
rec = tmp->next->data;
if (rec->realname == NULL)
rec->realname = g_strdup(realname);
}
g_slist_free(nicks);
/* reset gone and ircop status, we'll handle them in the following
WHOIS replies */
nicklist_update_flags(SERVER(server), nick, FALSE, FALSE);
g_free(params);
}
static void event_whois_away(SERVER_REC *server, const char *data)
{
char *params, *nick, *awaymsg;
g_return_if_fail(data != NULL);
/* set user's gone flag.. */
params = event_get_params(data, 3, NULL, &nick, &awaymsg);
nicklist_update_flags(server, nick, TRUE, -1);
g_free(params);
}
static void event_whois_ircop(SERVER_REC *server, const char *data)
{
char *params, *nick, *awaymsg;
g_return_if_fail(data != NULL);
/* set user's gone flag.. */
params = event_get_params(data, 3, NULL, &nick, &awaymsg);
nicklist_update_flags(server, nick, -1, TRUE);
g_free(params);
}
static void event_end_of_whois(IRC_SERVER_REC *server, const char *data)
{
server->whois_coming = FALSE;
}
static void event_nick_in_use(IRC_SERVER_REC *server, const char *data)
{
char *str;
int n;
g_return_if_fail(data != NULL);
if (server->connected) {
/* Already connected, no need to handle this anymore. */
return;
}
/* nick already in use - need to change it .. */
if (strcmp(server->nick, server->connrec->nick) == 0 &&
server->connrec->alternate_nick != NULL) {
/* first try, so try the alternative nick.. */
g_free(server->nick);
server->nick = g_strdup(server->connrec->alternate_nick);
}
else if (strlen(server->nick) < 9) {
/* keep adding '_' to end of nick.. */
str = g_strdup_printf("%s_", server->nick);
g_free(server->nick);
server->nick = str;
} else {
/* nick full, keep adding number at the end */
for (n = 8; n > 0; n--) {
if (server->nick[n] < '0' || server->nick[n] > '9') {
server->nick[n] = '1';
break;
}
if (server->nick[n] < '9') {
server->nick[n]++;
break;
}
server->nick[n] = '0';
}
}
irc_send_cmdv(server, "NICK %s", server->nick);
}
static void event_target_unavailable(IRC_SERVER_REC *server, const char *data)
{
char *params, *channel;
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, NULL, &channel);
if (!ischannel(*channel)) {
/* nick is unavailable. */
event_nick_in_use(server, data);
}
g_free(params);
}
static void event_nick(SERVER_REC *server, const char *data,
const char *orignick)
{
char *params, *nick;
g_return_if_fail(data != NULL);
params = event_get_params(data, 1, &nick);
if (g_strcasecmp(orignick, server->nick) == 0) {
/* You changed your nick */
server_change_nick(server, nick);
}
nicklist_rename(server, orignick, nick);
g_free(params);
}
static void event_userhost(SERVER_REC *server, const char *data)
{
char *params, *hosts, **phosts, **pos, *ptr;
g_return_if_fail(data != NULL);
/* set user's gone flag.. */
params = event_get_params(data, 2, NULL, &hosts);
phosts = g_strsplit(hosts, " ", -1);
for (pos = phosts; *pos != NULL; pos++) {
ptr = strchr(*pos, '=');
if (ptr == NULL) continue;
*ptr++ = '\0';
nicklist_update_flags(server, *pos, *ptr == '-', -1);
}
g_strfreev(phosts);
g_free(params);
}
static void sig_usermode(SERVER_REC *server)
{
g_return_if_fail(IS_SERVER(server));
nicklist_update_flags(server, server->nick, server->usermode_away, -1);
}
static const char *get_nick_flags(void)
{
return "@+%";
}
static void sig_connected(IRC_SERVER_REC *server)
{
if (IS_IRC_SERVER(server)) {
server->get_nick_flags =
(const char *(*)(void)) get_nick_flags;
}
}
void irc_nicklist_init(void)
{
signal_add_first("event nick", (SIGNAL_FUNC) event_nick);
signal_add_first("event 352", (SIGNAL_FUNC) event_who);
signal_add("silent event who", (SIGNAL_FUNC) event_who);
signal_add("silent event whois", (SIGNAL_FUNC) event_whois);
signal_add_first("event 311", (SIGNAL_FUNC) event_whois);
signal_add_first("event 301", (SIGNAL_FUNC) event_whois_away);
signal_add_first("event 313", (SIGNAL_FUNC) event_whois_ircop);
signal_add("event 318", (SIGNAL_FUNC) event_end_of_whois);
signal_add("event 353", (SIGNAL_FUNC) event_names_list);
signal_add("event 366", (SIGNAL_FUNC) event_end_of_names);
signal_add("event 433", (SIGNAL_FUNC) event_nick_in_use);
signal_add("event 437", (SIGNAL_FUNC) event_target_unavailable);
signal_add("event 302", (SIGNAL_FUNC) event_userhost);
signal_add("userhost event", (SIGNAL_FUNC) event_userhost);
signal_add("user mode changed", (SIGNAL_FUNC) sig_usermode);
signal_add("server connected", (SIGNAL_FUNC) sig_connected);
}
void irc_nicklist_deinit(void)
{
signal_remove("event nick", (SIGNAL_FUNC) event_nick);
signal_remove("event 352", (SIGNAL_FUNC) event_who);
signal_remove("silent event who", (SIGNAL_FUNC) event_who);
signal_remove("silent event whois", (SIGNAL_FUNC) event_whois);
signal_remove("event 311", (SIGNAL_FUNC) event_whois);
signal_remove("event 301", (SIGNAL_FUNC) event_whois_away);
signal_remove("event 313", (SIGNAL_FUNC) event_whois_ircop);
signal_remove("event 318", (SIGNAL_FUNC) event_end_of_whois);
signal_remove("event 353", (SIGNAL_FUNC) event_names_list);
signal_remove("event 366", (SIGNAL_FUNC) event_end_of_names);
signal_remove("event 433", (SIGNAL_FUNC) event_nick_in_use);
signal_remove("event 437", (SIGNAL_FUNC) event_target_unavailable);
signal_remove("event 302", (SIGNAL_FUNC) event_userhost);
signal_remove("userhost event", (SIGNAL_FUNC) event_userhost);
signal_remove("user mode changed", (SIGNAL_FUNC) sig_usermode);
signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
}