2 * IRC - Internet Relay Chat, ircd/m_kill.c
3 * Copyright (C) 1990 Jarkko Oikarinen and
4 * University of Oulu, Computing Center
6 * See file AUTHORS in IRC package for additional names of
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 1, or (at your option)
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 * m_functions execute protocol messages on this server:
29 * cptr is always NON-NULL, pointing to a *LOCAL* client
30 * structure (with an open socket connected!). This
31 * identifies the physical socket where the message
32 * originated (or which caused the m_function to be
33 * executed--some m_functions may call others...).
35 * sptr is the source of the message, defined by the
36 * prefix part of the message if present. If not
37 * or prefix not found, then sptr==cptr.
39 * (!IsServer(cptr)) => (cptr == sptr), because
40 * prefixes are taken *only* from servers...
43 * (sptr == cptr) => the message didn't
46 * (sptr != cptr && IsServer(sptr) means
47 * the prefix specified servername. (?)
49 * (sptr != cptr && !IsServer(sptr) means
50 * that message originated from a remote
55 * (!IsServer(sptr)) means that, sptr can safely
56 * taken as defining the target structure of the
57 * message in this server.
59 * *Always* true (if 'parse' and others are working correct):
61 * 1) sptr->from == cptr (note: cptr->from == cptr)
63 * 2) MyConnect(sptr) <=> sptr == cptr (e.g. sptr
64 * *cannot* be a local connection, unless it's
65 * actually cptr!). [MyConnect(x) should probably
66 * be defined as (x == x->from) --msa ]
68 * parc number of variable parameter strings (if zero,
69 * parv is allowed to be NULL)
71 * parv a NULL terminated list of parameter pointers,
73 * parv[0], sender (prefix string), if not present
74 * this points to an empty string.
75 * parv[1]...parv[parc-1]
76 * pointers to additional parameters
77 * parv[parc] == NULL, *always*
79 * note: it is guaranteed that parv[0]..parv[parc-1] are all
84 * No need to include handlers.h here the signatures must match
85 * and we don't need to force a rebuild of all the handlers everytime
86 * we add a new one to the list. --Bleep
94 #include "ircd_reply.h"
95 #include "ircd_string.h"
107 * ms_kill - server message handler template
109 * NOTE: IsServer(cptr) == true;
111 * parv[0] = sender prefix
112 * parv[1] = kill victim
113 * parv[parc-1] = kill path
115 int ms_kill(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
117 struct Client* victim;
126 assert(IsServer(cptr));
129 * XXX - a server sending less than 3 params could really desync
133 return need_more_params(sptr, "KILL");
136 path = parv[parc - 1]; /* Either defined or NULL (parc >= 3) */
138 if (!(victim = findNUser(parv[1]))) {
140 sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :KILL target disconnected "
141 "before I got him :(", sptr);
146 * Notify all *local* opers about the KILL (this includes the one
147 * originating the kill, if from this server--the special numeric
148 * reply message is not generated anymore).
150 * Note: "victim->name" is used instead of "user" because we may
151 * have changed the target because of the nickname change.
153 inpath = cli_name(cptr);
155 sendto_opmask_butone(0, IsServer(sptr) ? SNO_SERVKILL : SNO_OPERKILL,
156 "Received KILL message for %s. From %s Path: %C!%s",
157 get_client_name(victim,SHOW_IP), parv[0], cptr, path);
159 log_write_kill(victim, sptr, cli_name(cptr), path);
161 * And pass on the message to other servers. Note, that if KILL
162 * was changed, the message has to be sent to all links, also
165 sendcmdto_serv_butone(sptr, CMD_KILL, cptr, "%C :%s!%s", victim, cli_name(cptr),
168 * We *can* have crossed a NICK with this numeric... --Run
170 * Note the following situation:
172 * <-- S NICK ... SAA | <-- SAA QUIT <-- S NICK ... SAA <-- SQUIT S
173 * Where the KILL reaches point X before the QUIT does.
174 * This would then *still* cause an orphan because the KILL doesn't reach S
175 * (because of the SQUIT), the QUIT is ignored (because of the KILL)
176 * and the second NICK ... SAA causes an orphan on the server at the
177 * right (which then isn't removed when the SQUIT arrives).
178 * Therefore we still need to detect numeric nick collisions too.
180 * Bounce the kill back to the originator, if the client can't be found
181 * by the next hop (short lag) the bounce won't propagate further.
183 if (MyConnect(victim))
184 sendcmdto_one(&me, CMD_KILL, cptr, "%C :%s!%s (Ghost 5 Numeric Collided)",
185 victim, cli_name(cptr), path);
187 * Set FLAGS_KILLED. This prevents exit_one_client from sending
188 * the unnecessary QUIT for this. (This flag should never be
189 * set in any other place)
191 cli_flags(victim) |= FLAGS_KILLED;
194 * Tell the victim she/he has been zapped, but *only* if
195 * the victim is on current server--no sense in sending the
196 * notification chasing the above kill, it won't get far
197 * anyway (as this user don't exist there any more either)
199 if (MyConnect(victim))
200 sendcmdto_one(sptr, CMD_KILL, victim, "%C :%s!%s", victim, NumServ(cptr),
203 * the first space in path will be at the end of the
205 * bla.bla.bla!host.net.dom!opername (comment)
207 if ((killer = strchr(path, ' '))) {
208 while (killer > path && '!' != *killer)
215 sprintf_irc(buf, "Killed (%s)", killer);
217 return exit_client(cptr, victim, sptr, buf);
221 * mo_kill - oper message handler template
223 * NOTE: IsPrivileged(sptr), IsAnOper(sptr) == true
224 * IsServer(cptr), IsServer(sptr) == false
226 * parv[0] = sender prefix
227 * parv[1] = kill victim
228 * parv[parc-1] = kill path
230 int mo_kill(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
232 struct Client* victim;
242 * oper connection to this server, cptr is always sptr
244 assert(cptr == sptr);
245 assert(IsAnOper(sptr));
247 if (parc < 3 || EmptyString(parv[parc - 1]))
248 return need_more_params(sptr, "KILL");
251 if (!(victim = FindClient(user))) {
253 * If the user has recently changed nick, we automaticly
254 * rewrite the KILL for this new nickname--this keeps
255 * servers in synch when nick change and kill collide
257 if (!(victim = get_history(user, (long)15)))
258 return send_reply(sptr, ERR_NOSUCHNICK, user);
260 sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :Changed KILL %s into %s", sptr,
261 user, cli_name(victim));
263 if (!HasPriv(sptr, MyConnect(victim) ? PRIV_LOCAL_KILL : PRIV_KILL))
264 return send_reply(sptr, ERR_NOPRIVILEGES);
266 if (IsServer(victim) || IsMe(victim)) {
267 return send_reply(sptr, ERR_CANTKILLSERVER);
270 * if the user is +k, prevent a kill from local user
272 if (IsChannelService(victim))
273 return send_reply(sptr, ERR_ISCHANSERVICE, "KILL", cli_name(victim));
276 if (!MyConnect(victim) && !HasPriv(sptr, PRIV_KILL)) {
277 sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :Nick %s isnt on your server",
278 sptr, cli_name(victim));
283 * The kill originates from this server, initialize path.
284 * (In which case the 'path' may contain user suplied
285 * explanation ...or some nasty comment, sigh... >;-)
288 * ...!operhost!oper (comment)
291 comment = parv[parc - 1]; /* Either defined or NULL (parc >= 3) */
293 if (strlen(comment) > TOPICLEN)
294 comment[TOPICLEN] = '\0';
296 inpath = cli_user(sptr)->host;
299 "%s%s (%s)", cli_name(cptr), IsOper(sptr) ? "" : "(L)", comment);
303 * Notify all *local* opers about the KILL (this includes the one
304 * originating the kill, if from this server--the special numeric
305 * reply message is not generated anymore).
307 * Note: "victim->name" is used instead of "user" because we may
308 * have changed the target because of the nickname change.
310 sendto_opmask_butone(0, SNO_OPERKILL,
311 "Received KILL message for %s. From %s Path: %s!%s",
312 get_client_name(victim,SHOW_IP), parv[0], inpath, path);
314 log_write_kill(victim, sptr, inpath, path);
316 * And pass on the message to other servers. Note, that if KILL
317 * was changed, the message has to be sent to all links, also
319 * Suicide kills are NOT passed on --SRB
321 if (!MyConnect(victim)) {
322 sendcmdto_serv_butone(sptr, CMD_KILL, cptr, "%C :%s!%s", victim, inpath,
326 * Set FLAGS_KILLED. This prevents exit_one_client from sending
327 * the unnecessary QUIT for this. (This flag should never be
328 * set in any other place)
330 cli_flags(victim) |= FLAGS_KILLED;
332 sprintf_irc(buf, "Killed by %s (%s)", cli_name(sptr), comment);
336 * Tell the victim she/he has been zapped, but *only* if
337 * the victim is on current server--no sense in sending the
338 * notification chasing the above kill, it won't get far
339 * anyway (as this user don't exist there any more either)
341 sendcmdto_one(sptr, CMD_KILL, victim, "%C :%s!%s", victim, inpath, path);
342 sprintf_irc(buf, "Local kill by %s (%s)", cli_name(sptr), comment);
345 return exit_client(cptr, victim, sptr, buf);
352 * parv[0] = sender prefix
353 * parv[1] = kill victim
354 * parv[parc-1] = kill path
356 int m_kill(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
358 struct Client* acptr;
359 const char* inpath = get_client_name(cptr, HIDE_IP);
367 if (parc < 3 || *parv[1] == '\0')
368 return need_more_params(sptr, parv[0], "KILL");
371 path = parv[parc - 1]; /* Either defined or NULL (parc >= 3) */
374 if (!IsPrivileged(cptr))
376 sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); /* XXX DEAD */
382 sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); /* XXX DEAD */
388 if (EmptyString(path))
389 return need_more_params(sptr, parv[0], "KILL");
391 if (strlen(path) > TOPICLEN)
392 path[TOPICLEN] = '\0';
397 if (!(acptr = FindClient(user)))
400 * If the user has recently changed nick, we automaticly
401 * rewrite the KILL for this new nickname--this keeps
402 * servers in synch when nick change and kill collide
404 if (!(acptr = get_history(user, (long)15)))
406 sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], user); /* XXX DEAD */
409 sendto_one(sptr, ":%s NOTICE %s :Changed KILL %s into %s", /* XXX DEAD */
410 me.name, parv[0], user, acptr->name);
414 else if (!(acptr = findNUser(user)))
417 sendto_one(sptr, /* XXX DEAD */
418 "%s NOTICE %s%s :KILL target disconnected before I got him :(",
419 NumServ(&me), NumNick(sptr));
422 if (!MyConnect(acptr) && IsLocOp(cptr))
424 sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); /* XXX DEAD */
427 if (IsServer(acptr) || IsMe(acptr))
429 sendto_one(sptr, err_str(ERR_CANTKILLSERVER), me.name, parv[0]); /* XXX DEAD */
433 /* if the user is +k, prevent a kill from local user */
434 if (IsChannelService(acptr) && MyUser(sptr))
436 sendto_one(sptr, err_str(ERR_ISCHANSERVICE), me.name, /* XXX DEAD */
437 parv[0], "KILL", acptr->name);
441 #ifdef LOCAL_KILL_ONLY
442 if (MyConnect(sptr) && !MyConnect(acptr))
444 sendto_one(sptr, ":%s NOTICE %s :Nick %s isnt on your server", /* XXX DEAD */
445 me.name, parv[0], acptr->name);
452 * The kill originates from this server, initialize path.
453 * (In which case the 'path' may contain user suplied
454 * explanation ...or some nasty comment, sigh... >;-)
457 * ...!operhost!oper (comment)
459 inpath = cptr->sockhost;
461 if (!EmptyString(path))
464 "%s%s (%s)", cptr->name, IsOper(sptr) ? "" : "(L)", path);
470 else if (EmptyString(path))
471 path = "*no-path*"; /* Bogus server sending??? */
473 * Notify all *local* opers about the KILL (this includes the one
474 * originating the kill, if from this server--the special numeric
475 * reply message is not generated anymore).
477 * Note: "acptr->name" is used instead of "user" because we may
478 * have changed the target because of the nickname change.
480 if (IsLocOp(sptr) && !MyConnect(acptr))
482 sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); /* XXX DEAD */
485 sendto_op_mask(IsServer(sptr) ? SNO_SERVKILL : SNO_OPERKILL, /* XXX DEAD */
486 "Received KILL message for %s. From %s Path: %s!%s",
487 acptr->name, parv[0], inpath, path);
488 #if defined(USE_SYSLOG) && defined(SYSLOG_KILL)
490 { /* get more infos when your local
491 clients are killed -- _dl */
493 ircd_log(L_TRACE, /* XXX DEAD */
494 "A local client %s!%s@%s KILLED from %s [%s] Path: %s!%s)",
495 acptr->name, acptr->user->username, acptr->user->host,
496 parv[0], sptr->name, inpath, path);
498 ircd_log(L_TRACE, /* XXX DEAD */
499 "A local client %s!%s@%s KILLED by %s [%s!%s@%s] (%s!%s)",
500 acptr->name, acptr->user->username, acptr->user->host,
501 parv[0], sptr->name, sptr->user->username, sptr->user->host,
504 else if (IsOper(sptr))
505 ircd_log(L_TRACE, "KILL From %s For %s Path %s!%s", /* XXX DEAD */
506 parv[0], acptr->name, inpath, path);
509 * And pass on the message to other servers. Note, that if KILL
510 * was changed, the message has to be sent to all links, also
512 * Suicide kills are NOT passed on --SRB
514 if (!MyConnect(acptr) || !MyConnect(sptr) || !IsAnOper(sptr))
516 sendto_highprot_butone(cptr, 10, ":%s " TOK_KILL " %s%s :%s!%s", /* XXX DEAD */
517 parv[0], NumNick(acptr), inpath, path);
518 /* We *can* have crossed a NICK with this numeric... --Run */
519 /* Note the following situation:
521 * <-- S NICK ... SAA | <-- SAA QUIT <-- S NICK ... SAA <-- SQUIT S
522 * Where the KILL reaches point X before the QUIT does.
523 * This would then *still* cause an orphan because the KILL doesn't reach S
524 * (because of the SQUIT), the QUIT is ignored (because of the KILL)
525 * and the second NICK ... SAA causes an orphan on the server at the
526 * right (which then isn't removed when the SQUIT arrives).
527 * Therefore we still need to detect numeric nick collisions too.
529 if (MyConnect(acptr) && IsServer(cptr))
530 sendto_one(cptr, "%s " TOK_KILL " %s%s :%s!%s (Ghost5)", /* XXX DEAD */
531 NumServ(&me), NumNick(acptr), inpath, path);
532 acptr->flags |= FLAGS_KILLED;
536 * Tell the victim she/he has been zapped, but *only* if
537 * the victim is on current server--no sense in sending the
538 * notification chasing the above kill, it won't get far
539 * anyway (as this user don't exist there any more either)
541 if (MyConnect(acptr))
542 sendto_prefix_one(acptr, sptr, ":%s KILL %s :%s!%s", /* XXX DEAD */
543 parv[0], acptr->name, inpath, path);
545 * Set FLAGS_KILLED. This prevents exit_one_client from sending
546 * the unnecessary QUIT for this. (This flag should never be
547 * set in any other place)
549 if (MyConnect(acptr) && MyConnect(sptr) && IsAnOper(sptr))
550 sprintf_irc(buf2, "Local kill by %s (%s)", sptr->name,
551 EmptyString(parv[parc - 1]) ? sptr->name : parv[parc - 1]);
554 if ((killer = strchr(path, ' ')))
556 while (*killer && *killer != '!')
565 sprintf_irc(buf2, "Killed (%s)", killer);
567 return exit_client(cptr, acptr, sptr, buf2);