/* * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd). * m_nick.c: Sets a users nick. * * Copyright (C) 2002 by the past and present ircd coders, and others. * * 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 * * $Id: $ */ #include "stdinc.h" #include "client.h" #include "hash.h" #include "fdlist.h" #include "irc_string.h" #include "ircd.h" #include "conf.h" #include "log.h" #include "numeric.h" #include "channel_mode.h" #include "s_user.h" #include "hash.h" #include "whowas.h" #include "s_serv.h" #include "send.h" #include "list.h" #include "channel.h" #include "resv.h" #include "parse.h" #include "modules.h" #include "packet.h" #include "watch.h" static char buf[IRCD_BUFSIZE]; static int m_forcenick(struct Client *, struct Client *, int, char**); static int mo_forcenick(struct Client *, struct Client *, int, char**); static int clean_nick_name(char *); static void relay_kill(struct Client *one, struct Client *source_p, struct Client *target_p, const char *inpath, const char *reason); /* * m_forcenick * parv[1] = old nick * parv[2] = new nick */ static int m_forcenick(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { sendto_one(client_p, ":%s %d %s FORCENICK :Unknown command", me.name, ERR_UNKNOWNCOMMAND, client_p->name); return 0; } static void change_local_nick(struct Client *source_p, const char *nick) { assert(source_p->name[0] && !EmptyString(nick)); assert(MyConnect(source_p)); /* * Client just changing his/her nick. If he/she is * on a channel, send note of change to all clients * on that channel. Propagate notice to other servers. */ if ((source_p->localClient->last_nick_change + ConfigFileEntry.max_nick_time) < CurrentTime) source_p->localClient->number_of_nick_changes = 0; source_p->localClient->last_nick_change = CurrentTime; source_p->localClient->number_of_nick_changes++; if ((ConfigFileEntry.anti_nick_flood && (source_p->localClient->number_of_nick_changes <= ConfigFileEntry.max_nick_changes)) || !ConfigFileEntry.anti_nick_flood || (HasUMode(source_p, UMODE_OPER) && ConfigFileEntry.no_oper_flood)) { int samenick = !irccmp(source_p->name, nick); if (!samenick) { source_p->tsinfo = CurrentTime; clear_ban_cache_client(source_p); watch_check_hash(source_p, RPL_LOGOFF); if (HasUMode(source_p, UMODE_REGISTERED)) { unsigned int oldmodes = source_p->umodes; char modebuf[IRCD_BUFSIZE] = { '\0' }; DelUMode(source_p, UMODE_REGISTERED); send_umode(source_p, source_p, oldmodes, 0xffffffff, modebuf); } } /* XXX - the format of this notice should eventually be changed * to either %s[%s@%s], or even better would be get_client_name() -bill */ sendto_realops_flags(UMODE_NCHANGE, L_ALL, SEND_NOTICE, "Nick change: From %s to %s [%s@%s]", source_p->name, nick, source_p->username, source_p->host); sendto_common_channels_local(source_p, 1, 0, ":%s!%s@%s NICK :%s", source_p->name, source_p->username, source_p->host, nick); whowas_add_history(source_p, 1); sendto_server(source_p, CAP_TS6, NOCAPS, ":%s NICK %s :%lu", ID(source_p), nick, (unsigned long)source_p->tsinfo); sendto_server(source_p, NOCAPS, CAP_TS6, ":%s NICK %s :%lu", source_p->name, nick, (unsigned long)source_p->tsinfo); hash_del_client(source_p); strcpy(source_p->name, nick); hash_add_client(source_p); if (!samenick) watch_check_hash(source_p, RPL_LOGON); /* fd_desc is long enough */ fd_note(&source_p->localClient->fd, "Nick: %s", nick); } else sendto_one(source_p, form_str(ERR_NICKTOOFAST), me.name, source_p->name, source_p->name, nick, ConfigFileEntry.max_nick_time); } /* * mo_forcenick * parv[0] = sender prefix * parv[1] = old nick * parv[2] = new nick */ static int mo_forcenick(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { char vnick[NICKLEN]; char tnick[NICKLEN]; struct Client *victim_p; struct Client *target_p; if (!HasUMode(client_p, UMODE_ADMIN) || (parc != 3)) { sendto_one(client_p, ":%s %d %s FORCENICK :Unknown command", me.name, ERR_UNKNOWNCOMMAND, client_p->name); return 0; } /* XXX BadPtr is needed */ if(parc < 2 || !parv[1] || parv[1] == '\0') { sendto_one(source_p, form_str(ERR_NONICKNAMEGIVEN), me.name, parv[0]); return 0; } /* terminate nick to NICKLEN */ strlcpy(vnick, parv[1], NICKLEN); strlcpy(tnick, parv[2], NICKLEN); if (!(victim_p = hash_find_client(vnick))) { /* not a local client */ sendto_one(source_p, form_str(ERR_NOSUCHNICK), me.name, parv[0], vnick); return 0; } if (!MyConnect(victim_p)) { sendto_one(source_p, ":%s NOTICE %s :Nick %s isn't on your server", me.name, parv[0], victim_p->name); return 0; } /* check the nickname is ok */ if(!clean_nick_name(tnick)) { sendto_one(source_p, ":%s NOTICE %s :Nick %s is invalid", me.name, parv[0], tnick); return 0; } if ((target_p = hash_find_client(tnick)) != NULL) { /* If(target_p == source_p) the client is changing nicks between * equivalent nicknames ie: [nick] -> {nick} */ if(target_p == victim_p) { /* check the nick isnt exactly the same */ if(strcmp(target_p->name, tnick)) { change_local_nick(victim_p, tnick); return 0; } else { /* client is doing :old NICK old * ignore it.. */ return 0; } } /* if the client that has the nick isnt registered yet (nick but no * user) then drop the unregged client */ if(IsUnknown(target_p)) { /* the old code had an if(MyConnect(target_p)) here.. but I cant see * how that can happen, m_nick() is local only --fl_ */ exit_client(target_p, &me, "Overridden"); change_local_nick(victim_p, tnick); return 0; } else { if (!MyConnect(target_p)) { snprintf(buf, sizeof(buf), "renaming %s", victim_p->name); relay_kill(client_p, source_p, target_p, client_p->name, buf); target_p->flags |= FLAGS_KILLED; } snprintf(buf, sizeof(buf), "Killed by %s (renaming %s)", source_p->name, victim_p->name); exit_client(target_p, source_p, buf); sendto_realops_flags(UMODE_ALL, L_ALL, SEND_NOTICE, "Force nick %s -> %s by %s!%s@%s", vnick, tnick, source_p->name, source_p->username, source_p->host); ilog(LOG_TYPE_IRCD, "FORCENICK From %s!%s@%s (%s -> %s)", source_p->name, source_p->username, source_p->host, vnick, tnick); change_local_nick(victim_p, tnick); return 0; } } else { sendto_realops_flags(UMODE_ALL, L_ALL, SEND_NOTICE, "Force nick %s -> %s by %s!%s@%s", vnick, tnick, source_p->name, source_p->username, source_p->host); ilog(LOG_TYPE_IRCD, "FORCENICK From %s!%s@%s (%s -> %s)", source_p->name, source_p->username, source_p->host, vnick, tnick); change_local_nick(victim_p, tnick); } return 0; } /* clean_nick_name() * * input - nickname * output - none * side effects - walks through the nickname, returning 0 if erroneous */ static int clean_nick_name(char *nick) { assert(nick); if(nick == NULL) return (0); /* nicks cant start with a digit or - or be 0 length */ /* This closer duplicates behaviour of hybrid-6 */ if (*nick == '-' || IsDigit(*nick) || *nick == '\0') return (0); for(; *nick; nick++) { if(!IsNickChar(*nick)) return (0); } return (1); } static void relay_kill(struct Client *one, struct Client *source_p, struct Client *target_p, const char *inpath, const char *reason) { dlink_node *ptr; struct Client *client_p; char* user; for( ptr = serv_list.head; ptr; ptr = ptr->next ) { client_p = (struct Client *) ptr->data; if( !client_p || client_p == one ) continue; /* check the server supports UID */ user = ID_or_name(target_p, client_p); if(MyClient(source_p)) { sendto_one(client_p, ":%s KILL %s :%s!%s!%s!%s (%s)", source_p->name, user, me.name, source_p->host, source_p->username, source_p->name, reason); } else { sendto_one(client_p, ":%s KILL %s :%s %s", source_p->name, user, inpath, reason); } } } struct Message forcenick_msgtab = { "FORCENICK", 0, 0, 0, MAXPARA, MFLG_SLOW|MFLG_HIDDEN, 0, {m_ignore, m_forcenick, m_forcenick, m_forcenick, mo_forcenick, m_ignore} }; static void module_init(void) { mod_add_cmd(&forcenick_msgtab); } static void module_exit(void) { mod_del_cmd(&forcenick_msgtab); } struct module module_entry = { .node = { NULL, NULL, NULL }, .name = NULL, .version = "$Revision: 0.0 $", .handle = NULL, .modinit = module_init, .modexit = module_exit, .flags = 0 };