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 * XXX - strictly speaking, the next 2 checks shouldn't be needed
147 * this function only handles kills from servers, and the check
148 * is done before the message is propagated --Bleep
150 if (IsServer(victim) || IsMe(victim)) {
151 return send_error_to_client(sptr, ERR_CANTKILLSERVER); /* XXX DEAD */
154 if (IsLocOp(sptr) && !MyConnect(victim)) {
155 return send_error_to_client(sptr, ERR_NOPRIVILEGES); /* XXX DEAD */
159 * XXX - this is guaranteed by the parser not to happen
161 if (EmptyString(path))
162 path = "*no-path*"; /* Bogus server sending??? */
165 * Notify all *local* opers about the KILL (this includes the one
166 * originating the kill, if from this server--the special numeric
167 * reply message is not generated anymore).
169 * Note: "victim->name" is used instead of "user" because we may
170 * have changed the target because of the nickname change.
174 sendto_opmask_butone(0, IsServer(sptr) ? SNO_SERVKILL : SNO_OPERKILL,
175 "Received KILL message for %s. From %s Path: %C!%s",
176 get_client_name(victim,SHOW_IP), parv[0], cptr, path);
178 log_write_kill(victim, sptr, cptr->name, path);
180 * And pass on the message to other servers. Note, that if KILL
181 * was changed, the message has to be sent to all links, also
184 sendcmdto_serv_butone(sptr, CMD_KILL, cptr, "%C :%s!%s", victim, cptr->name,
187 * We *can* have crossed a NICK with this numeric... --Run
189 * Note the following situation:
191 * <-- S NICK ... SAA | <-- SAA QUIT <-- S NICK ... SAA <-- SQUIT S
192 * Where the KILL reaches point X before the QUIT does.
193 * This would then *still* cause an orphan because the KILL doesn't reach S
194 * (because of the SQUIT), the QUIT is ignored (because of the KILL)
195 * and the second NICK ... SAA causes an orphan on the server at the
196 * right (which then isn't removed when the SQUIT arrives).
197 * Therefore we still need to detect numeric nick collisions too.
199 * Bounce the kill back to the originator, if the client can't be found
200 * by the next hop (short lag) the bounce won't propagate further.
202 if (MyConnect(victim))
203 sendcmdto_one(&me, CMD_KILL, cptr, "%C :%s!%s (Ghost 5 Numeric Collided)",
204 victim, cptr->name, path);
206 * Set FLAGS_KILLED. This prevents exit_one_client from sending
207 * the unnecessary QUIT for this. (This flag should never be
208 * set in any other place)
210 victim->flags |= FLAGS_KILLED;
213 * Tell the victim she/he has been zapped, but *only* if
214 * the victim is on current server--no sense in sending the
215 * notification chasing the above kill, it won't get far
216 * anyway (as this user don't exist there any more either)
218 if (MyConnect(victim))
219 sendcmdto_one(sptr, CMD_KILL, victim, "%C :%s!%s", victim, NumServ(cptr),
222 * the first space in path will be at the end of the
224 * bla.bla.bla!host.net.dom!opername (comment)
226 if ((killer = strchr(path, ' '))) {
227 while (killer > path && '!' != *killer)
234 sprintf_irc(buf, "Killed (%s)", killer);
236 return exit_client(cptr, victim, sptr, buf);
240 * mo_kill - oper message handler template
242 * NOTE: IsPrivileged(sptr), IsAnOper(sptr) == true
243 * IsServer(cptr), IsServer(sptr) == false
245 * parv[0] = sender prefix
246 * parv[1] = kill victim
247 * parv[parc-1] = kill path
249 int mo_kill(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
251 struct Client* victim;
261 * oper connection to this server, cptr is always sptr
263 assert(cptr == sptr);
264 assert(IsAnOper(sptr));
266 #if defined(OPER_KILL)
268 if (parc < 3 || EmptyString(parv[parc - 1]))
269 return need_more_params(sptr, "KILL");
272 if (!(victim = FindClient(user))) {
274 * If the user has recently changed nick, we automaticly
275 * rewrite the KILL for this new nickname--this keeps
276 * servers in synch when nick change and kill collide
278 if (!(victim = get_history(user, (long)15)))
279 return send_reply(sptr, ERR_NOSUCHNICK, user);
281 sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :Changed KILL %s into %s", sptr,
284 if (!MyConnect(victim) && IsLocOp(cptr))
285 return send_reply(sptr, ERR_NOPRIVILEGES);
287 if (IsServer(victim) || IsMe(victim)) {
288 return send_reply(sptr, ERR_CANTKILLSERVER);
291 * if the user is +k, prevent a kill from local user
293 if (IsChannelService(victim))
294 return send_reply(sptr, ERR_ISCHANSERVICE, "KILL", victim->name);
297 #ifdef LOCAL_KILL_ONLY
298 if (!MyConnect(victim)) {
299 sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :Nick %s isnt on your server", sptr,
305 * The kill originates from this server, initialize path.
306 * (In which case the 'path' may contain user suplied
307 * explanation ...or some nasty comment, sigh... >;-)
310 * ...!operhost!oper (comment)
313 comment = parv[parc - 1]; /* Either defined or NULL (parc >= 3) */
315 if (strlen(comment) > TOPICLEN)
316 comment[TOPICLEN] = '\0';
318 inpath = sptr->user->host;
321 "%s%s (%s)", cptr->name, IsOper(sptr) ? "" : "(L)", comment);
325 * Notify all *local* opers about the KILL (this includes the one
326 * originating the kill, if from this server--the special numeric
327 * reply message is not generated anymore).
329 * Note: "victim->name" is used instead of "user" because we may
330 * have changed the target because of the nickname change.
332 sendto_opmask_butone(0, SNO_OPERKILL,
333 "Received KILL message for %s. From %s Path: %s!%s",
334 get_client_name(victim,SHOW_IP), parv[0], inpath, path);
336 log_write_kill(victim, sptr, inpath, path);
338 * And pass on the message to other servers. Note, that if KILL
339 * was changed, the message has to be sent to all links, also
341 * Suicide kills are NOT passed on --SRB
343 if (!MyConnect(victim)) {
344 sendcmdto_serv_butone(sptr, CMD_KILL, cptr, "%C :%s!%s", victim, inpath,
348 * Set FLAGS_KILLED. This prevents exit_one_client from sending
349 * the unnecessary QUIT for this. (This flag should never be
350 * set in any other place)
352 victim->flags |= FLAGS_KILLED;
354 sprintf_irc(buf, "Killed by %s (%s)", sptr->name, comment);
358 * Tell the victim she/he has been zapped, but *only* if
359 * the victim is on current server--no sense in sending the
360 * notification chasing the above kill, it won't get far
361 * anyway (as this user don't exist there any more either)
363 sendcmdto_one(sptr, CMD_KILL, victim, "%C :%s!%s", victim, inpath, path);
364 sprintf_irc(buf, "Local kill by %s (%s)", sptr->name, comment);
367 return exit_client(cptr, victim, sptr, buf);
369 #else /* !defined(OPER_KILL) */
371 return send_reply(sptr, ERR_NOPRIVILEGES);
373 #endif /* !defined(OPER_KILL) */
380 * parv[0] = sender prefix
381 * parv[1] = kill victim
382 * parv[parc-1] = kill path
384 int m_kill(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
386 struct Client* acptr;
387 const char* inpath = get_client_name(cptr, HIDE_IP);
395 if (parc < 3 || *parv[1] == '\0')
396 return need_more_params(sptr, parv[0], "KILL");
399 path = parv[parc - 1]; /* Either defined or NULL (parc >= 3) */
402 if (!IsPrivileged(cptr))
404 sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); /* XXX DEAD */
410 sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); /* XXX DEAD */
416 if (EmptyString(path))
417 return need_more_params(sptr, parv[0], "KILL");
419 if (strlen(path) > TOPICLEN)
420 path[TOPICLEN] = '\0';
425 if (!(acptr = FindClient(user)))
428 * If the user has recently changed nick, we automaticly
429 * rewrite the KILL for this new nickname--this keeps
430 * servers in synch when nick change and kill collide
432 if (!(acptr = get_history(user, (long)15)))
434 sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], user); /* XXX DEAD */
437 sendto_one(sptr, ":%s NOTICE %s :Changed KILL %s into %s", /* XXX DEAD */
438 me.name, parv[0], user, acptr->name);
442 else if (!(acptr = findNUser(user)))
445 sendto_one(sptr, /* XXX DEAD */
446 "%s NOTICE %s%s :KILL target disconnected before I got him :(",
447 NumServ(&me), NumNick(sptr));
450 if (!MyConnect(acptr) && IsLocOp(cptr))
452 sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); /* XXX DEAD */
455 if (IsServer(acptr) || IsMe(acptr))
457 sendto_one(sptr, err_str(ERR_CANTKILLSERVER), me.name, parv[0]); /* XXX DEAD */
461 /* if the user is +k, prevent a kill from local user */
462 if (IsChannelService(acptr) && MyUser(sptr))
464 sendto_one(sptr, err_str(ERR_ISCHANSERVICE), me.name, /* XXX DEAD */
465 parv[0], "KILL", acptr->name);
469 #ifdef LOCAL_KILL_ONLY
470 if (MyConnect(sptr) && !MyConnect(acptr))
472 sendto_one(sptr, ":%s NOTICE %s :Nick %s isnt on your server", /* XXX DEAD */
473 me.name, parv[0], acptr->name);
480 * The kill originates from this server, initialize path.
481 * (In which case the 'path' may contain user suplied
482 * explanation ...or some nasty comment, sigh... >;-)
485 * ...!operhost!oper (comment)
487 inpath = cptr->sockhost;
489 if (!EmptyString(path))
492 "%s%s (%s)", cptr->name, IsOper(sptr) ? "" : "(L)", path);
498 else if (EmptyString(path))
499 path = "*no-path*"; /* Bogus server sending??? */
501 * Notify all *local* opers about the KILL (this includes the one
502 * originating the kill, if from this server--the special numeric
503 * reply message is not generated anymore).
505 * Note: "acptr->name" is used instead of "user" because we may
506 * have changed the target because of the nickname change.
508 if (IsLocOp(sptr) && !MyConnect(acptr))
510 sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); /* XXX DEAD */
513 sendto_op_mask(IsServer(sptr) ? SNO_SERVKILL : SNO_OPERKILL, /* XXX DEAD */
514 "Received KILL message for %s. From %s Path: %s!%s",
515 acptr->name, parv[0], inpath, path);
516 #if defined(USE_SYSLOG) && defined(SYSLOG_KILL)
518 { /* get more infos when your local
519 clients are killed -- _dl */
521 ircd_log(L_TRACE, /* XXX DEAD */
522 "A local client %s!%s@%s KILLED from %s [%s] Path: %s!%s)",
523 acptr->name, acptr->user->username, acptr->user->host,
524 parv[0], sptr->name, inpath, path);
526 ircd_log(L_TRACE, /* XXX DEAD */
527 "A local client %s!%s@%s KILLED by %s [%s!%s@%s] (%s!%s)",
528 acptr->name, acptr->user->username, acptr->user->host,
529 parv[0], sptr->name, sptr->user->username, sptr->user->host,
532 else if (IsOper(sptr))
533 ircd_log(L_TRACE, "KILL From %s For %s Path %s!%s", /* XXX DEAD */
534 parv[0], acptr->name, inpath, path);
537 * And pass on the message to other servers. Note, that if KILL
538 * was changed, the message has to be sent to all links, also
540 * Suicide kills are NOT passed on --SRB
542 if (!MyConnect(acptr) || !MyConnect(sptr) || !IsAnOper(sptr))
544 sendto_highprot_butone(cptr, 10, ":%s " TOK_KILL " %s%s :%s!%s", /* XXX DEAD */
545 parv[0], NumNick(acptr), inpath, path);
546 /* We *can* have crossed a NICK with this numeric... --Run */
547 /* Note the following situation:
549 * <-- S NICK ... SAA | <-- SAA QUIT <-- S NICK ... SAA <-- SQUIT S
550 * Where the KILL reaches point X before the QUIT does.
551 * This would then *still* cause an orphan because the KILL doesn't reach S
552 * (because of the SQUIT), the QUIT is ignored (because of the KILL)
553 * and the second NICK ... SAA causes an orphan on the server at the
554 * right (which then isn't removed when the SQUIT arrives).
555 * Therefore we still need to detect numeric nick collisions too.
557 if (MyConnect(acptr) && IsServer(cptr))
558 sendto_one(cptr, "%s " TOK_KILL " %s%s :%s!%s (Ghost5)", /* XXX DEAD */
559 NumServ(&me), NumNick(acptr), inpath, path);
560 acptr->flags |= FLAGS_KILLED;
564 * Tell the victim she/he has been zapped, but *only* if
565 * the victim is on current server--no sense in sending the
566 * notification chasing the above kill, it won't get far
567 * anyway (as this user don't exist there any more either)
569 if (MyConnect(acptr))
570 sendto_prefix_one(acptr, sptr, ":%s KILL %s :%s!%s", /* XXX DEAD */
571 parv[0], acptr->name, inpath, path);
573 * Set FLAGS_KILLED. This prevents exit_one_client from sending
574 * the unnecessary QUIT for this. (This flag should never be
575 * set in any other place)
577 if (MyConnect(acptr) && MyConnect(sptr) && IsAnOper(sptr))
578 sprintf_irc(buf2, "Local kill by %s (%s)", sptr->name,
579 EmptyString(parv[parc - 1]) ? sptr->name : parv[parc - 1]);
582 if ((killer = strchr(path, ' ')))
584 while (*killer && *killer != '!')
593 sprintf_irc(buf2, "Killed (%s)", killer);
595 return exit_client(cptr, acptr, sptr, buf2);