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"
106 #if defined(DEBUGMODE)
111 * ms_kill - server message handler template
113 * NOTE: IsServer(cptr) == true;
115 * parv[0] = sender prefix
116 * parv[1] = kill victim
117 * parv[parc-1] = kill path
119 int ms_kill(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
121 struct Client* victim;
130 assert(IsServer(cptr));
133 * XXX - a server sending less than 3 params could really desync
137 return need_more_params(sptr, "KILL");
140 path = parv[parc - 1]; /* Either defined or NULL (parc >= 3) */
142 if (!(victim = findNUser(parv[1]))) {
144 sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :KILL target disconnected "
145 "before I got him :(", sptr);
150 * XXX - strictly speaking, the next 2 checks shouldn't be needed
151 * this function only handles kills from servers, and the check
152 * is done before the message is propagated --Bleep
154 if (IsServer(victim) || IsMe(victim)) {
155 return send_error_to_client(sptr, ERR_CANTKILLSERVER); /* XXX DEAD */
158 if (IsLocOp(sptr) && !MyConnect(victim)) {
159 return send_error_to_client(sptr, ERR_NOPRIVILEGES); /* XXX DEAD */
163 * XXX - this is guaranteed by the parser not to happen
165 if (EmptyString(path))
166 path = "*no-path*"; /* Bogus server sending??? */
169 * Notify all *local* opers about the KILL (this includes the one
170 * originating the kill, if from this server--the special numeric
171 * reply message is not generated anymore).
173 * Note: "victim->name" is used instead of "user" because we may
174 * have changed the target because of the nickname change.
178 sendto_opmask_butone(0, IsServer(sptr) ? SNO_SERVKILL : SNO_OPERKILL,
179 "Received KILL message for %C. From %s Path: %C!%s",
180 victim, parv[0], cptr, path);
182 #if defined(SYSLOG_KILL)
183 ircd_log_kill(victim, sptr, cptr->name, path);
186 * And pass on the message to other servers. Note, that if KILL
187 * was changed, the message has to be sent to all links, also
190 sendcmdto_serv_butone(sptr, CMD_KILL, cptr, "%C :%s!%s", victim, cptr->name,
193 * We *can* have crossed a NICK with this numeric... --Run
195 * Note the following situation:
197 * <-- S NICK ... SAA | <-- SAA QUIT <-- S NICK ... SAA <-- SQUIT S
198 * Where the KILL reaches point X before the QUIT does.
199 * This would then *still* cause an orphan because the KILL doesn't reach S
200 * (because of the SQUIT), the QUIT is ignored (because of the KILL)
201 * and the second NICK ... SAA causes an orphan on the server at the
202 * right (which then isn't removed when the SQUIT arrives).
203 * Therefore we still need to detect numeric nick collisions too.
205 * Bounce the kill back to the originator, if the client can't be found
206 * by the next hop (short lag) the bounce won't propagate further.
208 if (MyConnect(victim))
209 sendcmdto_one(&me, CMD_KILL, cptr, "%C :%s!%s (Ghost 5 Numeric Collided)",
210 victim, cptr->name, path);
212 * Set FLAGS_KILLED. This prevents exit_one_client from sending
213 * the unnecessary QUIT for this. (This flag should never be
214 * set in any other place)
216 victim->flags |= FLAGS_KILLED;
219 * Tell the victim she/he has been zapped, but *only* if
220 * the victim is on current server--no sense in sending the
221 * notification chasing the above kill, it won't get far
222 * anyway (as this user don't exist there any more either)
224 if (MyConnect(victim))
225 sendcmdto_one(sptr, CMD_KILL, victim, "%C :%s!%s", victim, cptr->name,
228 * the first space in path will be at the end of the
230 * bla.bla.bla!host.net.dom!opername (comment)
232 if ((killer = strchr(path, ' '))) {
233 while (killer > path && '!' != *killer)
240 sprintf_irc(buf, "Killed (%s)", killer);
242 return exit_client(cptr, victim, sptr, buf);
246 * mo_kill - oper message handler template
248 * NOTE: IsPrivileged(sptr), IsAnOper(sptr) == true
249 * IsServer(cptr), IsServer(sptr) == false
251 * parv[0] = sender prefix
252 * parv[1] = kill victim
253 * parv[parc-1] = kill path
255 int mo_kill(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
257 struct Client* victim;
267 * oper connection to this server, cptr is always sptr
269 assert(cptr == sptr);
270 assert(IsAnOper(sptr));
272 #if defined(OPER_KILL)
274 if (parc < 3 || EmptyString(parv[parc - 1]))
275 return need_more_params(sptr, "KILL");
278 if (!(victim = FindClient(user))) {
280 * If the user has recently changed nick, we automaticly
281 * rewrite the KILL for this new nickname--this keeps
282 * servers in synch when nick change and kill collide
284 if (!(victim = get_history(user, (long)15)))
285 return send_reply(sptr, ERR_NOSUCHNICK, user);
287 sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :Changed KILL %s into %s", sptr,
290 if (!MyConnect(victim) && IsLocOp(cptr))
291 return send_reply(sptr, ERR_NOPRIVILEGES);
293 if (IsServer(victim) || IsMe(victim)) {
294 return send_reply(sptr, ERR_CANTKILLSERVER);
297 * if the user is +k, prevent a kill from local user
299 if (IsChannelService(victim))
300 return send_reply(sptr, ERR_ISCHANSERVICE, "KILL", victim->name);
303 #ifdef LOCAL_KILL_ONLY
304 if (!MyConnect(victim)) {
305 send_reply(&me, CMD_NOTICE, sptr, "%C :Nick %s isnt on your server", sptr,
311 * The kill originates from this server, initialize path.
312 * (In which case the 'path' may contain user suplied
313 * explanation ...or some nasty comment, sigh... >;-)
316 * ...!operhost!oper (comment)
319 comment = parv[parc - 1]; /* Either defined or NULL (parc >= 3) */
321 if (strlen(comment) > TOPICLEN)
322 comment[TOPICLEN] = '\0';
324 inpath = sptr->user->host;
327 "%s%s (%s)", cptr->name, IsOper(sptr) ? "" : "(L)", comment);
331 * Notify all *local* opers about the KILL (this includes the one
332 * originating the kill, if from this server--the special numeric
333 * reply message is not generated anymore).
335 * Note: "victim->name" is used instead of "user" because we may
336 * have changed the target because of the nickname change.
338 sendto_opmask_butone(0, SNO_OPERKILL,
339 "Received KILL message for %s. From %s Path: %s!%s",
340 victim->name, parv[0], inpath, path);
342 #if defined(SYSLOG_KILL)
343 ircd_log_kill(victim, sptr, inpath, path);
346 * And pass on the message to other servers. Note, that if KILL
347 * was changed, the message has to be sent to all links, also
349 * Suicide kills are NOT passed on --SRB
351 if (!MyConnect(victim)) {
352 sendcmdto_serv_butone(sptr, CMD_KILL, cptr, "%C :%s!%s", victim, inpath,
356 * Set FLAGS_KILLED. This prevents exit_one_client from sending
357 * the unnecessary QUIT for this. (This flag should never be
358 * set in any other place)
360 victim->flags |= FLAGS_KILLED;
362 sprintf_irc(buf, "Killed by %s (%s)", sptr->name, comment);
366 * Tell the victim she/he has been zapped, but *only* if
367 * the victim is on current server--no sense in sending the
368 * notification chasing the above kill, it won't get far
369 * anyway (as this user don't exist there any more either)
371 sendcmdto_one(sptr, CMD_KILL, victim, "%C :%s!%s", victim, inpath, path);
372 sprintf_irc(buf, "Local kill by %s (%s)", sptr->name, comment);
375 return exit_client(cptr, victim, sptr, buf);
377 #else /* !defined(OPER_KILL) */
379 return send_reply(sptr, ERR_NOPRIVILEGES);
381 #endif /* !defined(OPER_KILL) */
388 * parv[0] = sender prefix
389 * parv[1] = kill victim
390 * parv[parc-1] = kill path
392 int m_kill(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
394 struct Client* acptr;
395 const char* inpath = get_client_name(cptr, HIDE_IP);
403 if (parc < 3 || *parv[1] == '\0')
404 return need_more_params(sptr, parv[0], "KILL");
407 path = parv[parc - 1]; /* Either defined or NULL (parc >= 3) */
410 if (!IsPrivileged(cptr))
412 sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); /* XXX DEAD */
418 sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); /* XXX DEAD */
424 if (EmptyString(path))
425 return need_more_params(sptr, parv[0], "KILL");
427 if (strlen(path) > TOPICLEN)
428 path[TOPICLEN] = '\0';
433 if (!(acptr = FindClient(user)))
436 * If the user has recently changed nick, we automaticly
437 * rewrite the KILL for this new nickname--this keeps
438 * servers in synch when nick change and kill collide
440 if (!(acptr = get_history(user, (long)15)))
442 sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], user); /* XXX DEAD */
445 sendto_one(sptr, ":%s NOTICE %s :Changed KILL %s into %s", /* XXX DEAD */
446 me.name, parv[0], user, acptr->name);
450 else if (!(acptr = findNUser(user)))
453 sendto_one(sptr, /* XXX DEAD */
454 "%s NOTICE %s%s :KILL target disconnected before I got him :(",
455 NumServ(&me), NumNick(sptr));
458 if (!MyConnect(acptr) && IsLocOp(cptr))
460 sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); /* XXX DEAD */
463 if (IsServer(acptr) || IsMe(acptr))
465 sendto_one(sptr, err_str(ERR_CANTKILLSERVER), me.name, parv[0]); /* XXX DEAD */
469 /* if the user is +k, prevent a kill from local user */
470 if (IsChannelService(acptr) && MyUser(sptr))
472 sendto_one(sptr, err_str(ERR_ISCHANSERVICE), me.name, /* XXX DEAD */
473 parv[0], "KILL", acptr->name);
477 #ifdef LOCAL_KILL_ONLY
478 if (MyConnect(sptr) && !MyConnect(acptr))
480 sendto_one(sptr, ":%s NOTICE %s :Nick %s isnt on your server", /* XXX DEAD */
481 me.name, parv[0], acptr->name);
488 * The kill originates from this server, initialize path.
489 * (In which case the 'path' may contain user suplied
490 * explanation ...or some nasty comment, sigh... >;-)
493 * ...!operhost!oper (comment)
495 inpath = cptr->sockhost;
497 if (!EmptyString(path))
500 "%s%s (%s)", cptr->name, IsOper(sptr) ? "" : "(L)", path);
506 else if (EmptyString(path))
507 path = "*no-path*"; /* Bogus server sending??? */
509 * Notify all *local* opers about the KILL (this includes the one
510 * originating the kill, if from this server--the special numeric
511 * reply message is not generated anymore).
513 * Note: "acptr->name" is used instead of "user" because we may
514 * have changed the target because of the nickname change.
516 if (IsLocOp(sptr) && !MyConnect(acptr))
518 sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); /* XXX DEAD */
521 sendto_op_mask(IsServer(sptr) ? SNO_SERVKILL : SNO_OPERKILL, /* XXX DEAD */
522 "Received KILL message for %s. From %s Path: %s!%s",
523 acptr->name, parv[0], inpath, path);
524 #if defined(USE_SYSLOG) && defined(SYSLOG_KILL)
526 { /* get more infos when your local
527 clients are killed -- _dl */
530 "A local client %s!%s@%s KILLED from %s [%s] Path: %s!%s)",
531 acptr->name, acptr->user->username, acptr->user->host,
532 parv[0], sptr->name, inpath, path);
535 "A local client %s!%s@%s KILLED by %s [%s!%s@%s] (%s!%s)",
536 acptr->name, acptr->user->username, acptr->user->host,
537 parv[0], sptr->name, sptr->user->username, sptr->user->host,
540 else if (IsOper(sptr))
541 ircd_log(L_TRACE, "KILL From %s For %s Path %s!%s",
542 parv[0], acptr->name, inpath, path);
545 * And pass on the message to other servers. Note, that if KILL
546 * was changed, the message has to be sent to all links, also
548 * Suicide kills are NOT passed on --SRB
550 if (!MyConnect(acptr) || !MyConnect(sptr) || !IsAnOper(sptr))
552 sendto_highprot_butone(cptr, 10, ":%s " TOK_KILL " %s%s :%s!%s", /* XXX DEAD */
553 parv[0], NumNick(acptr), inpath, path);
554 /* We *can* have crossed a NICK with this numeric... --Run */
555 /* Note the following situation:
557 * <-- S NICK ... SAA | <-- SAA QUIT <-- S NICK ... SAA <-- SQUIT S
558 * Where the KILL reaches point X before the QUIT does.
559 * This would then *still* cause an orphan because the KILL doesn't reach S
560 * (because of the SQUIT), the QUIT is ignored (because of the KILL)
561 * and the second NICK ... SAA causes an orphan on the server at the
562 * right (which then isn't removed when the SQUIT arrives).
563 * Therefore we still need to detect numeric nick collisions too.
565 if (MyConnect(acptr) && IsServer(cptr))
566 sendto_one(cptr, "%s " TOK_KILL " %s%s :%s!%s (Ghost5)", /* XXX DEAD */
567 NumServ(&me), NumNick(acptr), inpath, path);
568 acptr->flags |= FLAGS_KILLED;
572 * Tell the victim she/he has been zapped, but *only* if
573 * the victim is on current server--no sense in sending the
574 * notification chasing the above kill, it won't get far
575 * anyway (as this user don't exist there any more either)
577 if (MyConnect(acptr))
578 sendto_prefix_one(acptr, sptr, ":%s KILL %s :%s!%s", /* XXX DEAD */
579 parv[0], acptr->name, inpath, path);
581 * Set FLAGS_KILLED. This prevents exit_one_client from sending
582 * the unnecessary QUIT for this. (This flag should never be
583 * set in any other place)
585 if (MyConnect(acptr) && MyConnect(sptr) && IsAnOper(sptr))
586 sprintf_irc(buf2, "Local kill by %s (%s)", sptr->name,
587 EmptyString(parv[parc - 1]) ? sptr->name : parv[parc - 1]);
590 if ((killer = strchr(path, ' ')))
592 while (*killer && *killer != '!')
601 sprintf_irc(buf2, "Killed (%s)", killer);
603 return exit_client(cptr, acptr, sptr, buf2);