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
86 * No need to include handlers.h here the signatures must match
87 * and we don't need to force a rebuild of all the handlers everytime
88 * we add a new one to the list. --Bleep
96 #include "ircd_reply.h"
97 #include "ircd_string.h"
100 #include "numnicks.h"
109 * ms_kill - server message handler template
111 * NOTE: IsServer(cptr) == true;
113 * parv[0] = sender prefix
114 * parv[1] = kill victim
115 * parv[parc-1] = kill path
117 int ms_kill(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
119 struct Client* victim;
128 assert(IsServer(cptr));
131 * XXX - a server sending less than 3 params could really desync
135 return need_more_params(sptr, "KILL");
138 path = parv[parc - 1]; /* Either defined or NULL (parc >= 3) */
140 if (!(victim = findNUser(parv[1]))) {
142 sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :KILL target disconnected "
143 "before I got him :(", sptr);
148 * Notify all *local* opers about the KILL (this includes the one
149 * originating the kill, if from this server--the special numeric
150 * reply message is not generated anymore).
152 * Note: "victim->name" is used instead of "user" because we may
153 * have changed the target because of the nickname change.
155 inpath = cli_name(cptr);
157 sendto_opmask_butone(0, IsServer(sptr) ? SNO_SERVKILL : SNO_OPERKILL,
158 "Received KILL message for %s. From %s Path: %C!%s",
159 get_client_name(victim,SHOW_IP), parv[0], cptr, path);
161 log_write_kill(victim, sptr, cli_name(cptr), path);
163 * And pass on the message to other servers. Note, that if KILL
164 * was changed, the message has to be sent to all links, also
167 sendcmdto_serv_butone(sptr, CMD_KILL, cptr, "%C :%s!%s", victim, cli_name(cptr),
170 * We *can* have crossed a NICK with this numeric... --Run
172 * Note the following situation:
174 * <-- S NICK ... SAA | <-- SAA QUIT <-- S NICK ... SAA <-- SQUIT S
175 * Where the KILL reaches point X before the QUIT does.
176 * This would then *still* cause an orphan because the KILL doesn't reach S
177 * (because of the SQUIT), the QUIT is ignored (because of the KILL)
178 * and the second NICK ... SAA causes an orphan on the server at the
179 * right (which then isn't removed when the SQUIT arrives).
180 * Therefore we still need to detect numeric nick collisions too.
182 * Bounce the kill back to the originator, if the client can't be found
183 * by the next hop (short lag) the bounce won't propagate further.
185 if (MyConnect(victim))
186 sendcmdto_one(&me, CMD_KILL, cptr, "%C :%s!%s (Ghost 5 Numeric Collided)",
187 victim, cli_name(cptr), path);
189 * Set FLAGS_KILLED. This prevents exit_one_client from sending
190 * the unnecessary QUIT for this. (This flag should never be
191 * set in any other place)
193 cli_flags(victim) |= FLAGS_KILLED;
196 * Tell the victim she/he has been zapped, but *only* if
197 * the victim is on current server--no sense in sending the
198 * notification chasing the above kill, it won't get far
199 * anyway (as this user don't exist there any more either)
201 if (MyConnect(victim))
202 sendcmdto_one(sptr, CMD_KILL, victim, "%C :%s!%s", victim, NumServ(cptr),
205 * the first space in path will be at the end of the
207 * bla.bla.bla!host.net.dom!opername (comment)
209 if ((killer = strchr(path, ' '))) {
210 while (killer > path && '!' != *killer)
217 sprintf_irc(buf, "Killed (%s)", killer);
219 return exit_client(cptr, victim, sptr, buf);
223 * mo_kill - oper message handler template
225 * NOTE: IsPrivileged(sptr), IsAnOper(sptr) == true
226 * IsServer(cptr), IsServer(sptr) == false
228 * parv[0] = sender prefix
229 * parv[1] = kill victim
230 * parv[parc-1] = kill path
232 int mo_kill(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
234 struct Client* victim;
244 * oper connection to this server, cptr is always sptr
246 assert(cptr == sptr);
247 assert(IsAnOper(sptr));
249 if (parc < 3 || EmptyString(parv[parc - 1]))
250 return need_more_params(sptr, "KILL");
253 if (!(victim = FindClient(user))) {
255 * If the user has recently changed nick, we automaticly
256 * rewrite the KILL for this new nickname--this keeps
257 * servers in synch when nick change and kill collide
259 if (!(victim = get_history(user, (long)15)))
260 return send_reply(sptr, ERR_NOSUCHNICK, user);
262 sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :Changed KILL %s into %s", sptr,
263 user, cli_name(victim));
265 if (!HasPriv(sptr, MyConnect(victim) ? PRIV_LOCAL_KILL : PRIV_KILL))
266 return send_reply(sptr, ERR_NOPRIVILEGES);
268 if (IsServer(victim) || IsMe(victim)) {
269 return send_reply(sptr, ERR_CANTKILLSERVER);
272 * if the user is +k, prevent a kill from local user
274 if (IsChannelService(victim))
275 return send_reply(sptr, ERR_ISCHANSERVICE, "KILL", cli_name(victim));
278 if (!MyConnect(victim) && !HasPriv(sptr, PRIV_KILL)) {
279 sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :Nick %s isnt on your server",
280 sptr, cli_name(victim));
285 * The kill originates from this server, initialize path.
286 * (In which case the 'path' may contain user suplied
287 * explanation ...or some nasty comment, sigh... >;-)
290 * ...!operhost!oper (comment)
293 comment = parv[parc - 1]; /* Either defined or NULL (parc >= 3) */
295 if (strlen(comment) > TOPICLEN)
296 comment[TOPICLEN] = '\0';
298 inpath = cli_user(sptr)->host;
301 "%s%s (%s)", cli_name(cptr), IsOper(sptr) ? "" : "(L)", comment);
305 * Notify all *local* opers about the KILL (this includes the one
306 * originating the kill, if from this server--the special numeric
307 * reply message is not generated anymore).
309 * Note: "victim->name" is used instead of "user" because we may
310 * have changed the target because of the nickname change.
312 sendto_opmask_butone(0, SNO_OPERKILL,
313 "Received KILL message for %s. From %s Path: %s!%s",
314 get_client_name(victim,SHOW_IP), parv[0], inpath, path);
316 log_write_kill(victim, sptr, inpath, path);
318 * And pass on the message to other servers. Note, that if KILL
319 * was changed, the message has to be sent to all links, also
321 * Suicide kills are NOT passed on --SRB
323 if (!MyConnect(victim)) {
324 sendcmdto_serv_butone(sptr, CMD_KILL, cptr, "%C :%s!%s", victim, inpath,
328 * Set FLAGS_KILLED. This prevents exit_one_client from sending
329 * the unnecessary QUIT for this. (This flag should never be
330 * set in any other place)
332 cli_flags(victim) |= FLAGS_KILLED;
334 sprintf_irc(buf, "Killed by %s (%s)", cli_name(sptr), comment);
338 * Tell the victim she/he has been zapped, but *only* if
339 * the victim is on current server--no sense in sending the
340 * notification chasing the above kill, it won't get far
341 * anyway (as this user don't exist there any more either)
343 sendcmdto_one(sptr, CMD_KILL, victim, "%C :%s!%s", victim, inpath, path);
344 sprintf_irc(buf, "Local kill by %s (%s)", cli_name(sptr), comment);
347 return exit_client(cptr, victim, sptr, buf);
354 * parv[0] = sender prefix
355 * parv[1] = kill victim
356 * parv[parc-1] = kill path
358 int m_kill(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
360 struct Client* acptr;
361 const char* inpath = get_client_name(cptr, HIDE_IP);
369 if (parc < 3 || *parv[1] == '\0')
370 return need_more_params(sptr, parv[0], "KILL");
373 path = parv[parc - 1]; /* Either defined or NULL (parc >= 3) */
376 if (!IsPrivileged(cptr))
378 sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); /* XXX DEAD */
384 sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); /* XXX DEAD */
390 if (EmptyString(path))
391 return need_more_params(sptr, parv[0], "KILL");
393 if (strlen(path) > TOPICLEN)
394 path[TOPICLEN] = '\0';
399 if (!(acptr = FindClient(user)))
402 * If the user has recently changed nick, we automaticly
403 * rewrite the KILL for this new nickname--this keeps
404 * servers in synch when nick change and kill collide
406 if (!(acptr = get_history(user, (long)15)))
408 sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], user); /* XXX DEAD */
411 sendto_one(sptr, ":%s NOTICE %s :Changed KILL %s into %s", /* XXX DEAD */
412 me.name, parv[0], user, acptr->name);
416 else if (!(acptr = findNUser(user)))
419 sendto_one(sptr, /* XXX DEAD */
420 "%s NOTICE %s%s :KILL target disconnected before I got him :(",
421 NumServ(&me), NumNick(sptr));
424 if (!MyConnect(acptr) && IsLocOp(cptr))
426 sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); /* XXX DEAD */
429 if (IsServer(acptr) || IsMe(acptr))
431 sendto_one(sptr, err_str(ERR_CANTKILLSERVER), me.name, parv[0]); /* XXX DEAD */
435 /* if the user is +k, prevent a kill from local user */
436 if (IsChannelService(acptr) && MyUser(sptr))
438 sendto_one(sptr, err_str(ERR_ISCHANSERVICE), me.name, /* XXX DEAD */
439 parv[0], "KILL", acptr->name);
443 #ifdef LOCAL_KILL_ONLY
444 if (MyConnect(sptr) && !MyConnect(acptr))
446 sendto_one(sptr, ":%s NOTICE %s :Nick %s isnt on your server", /* XXX DEAD */
447 me.name, parv[0], acptr->name);
454 * The kill originates from this server, initialize path.
455 * (In which case the 'path' may contain user suplied
456 * explanation ...or some nasty comment, sigh... >;-)
459 * ...!operhost!oper (comment)
461 inpath = cptr->sockhost;
463 if (!EmptyString(path))
466 "%s%s (%s)", cptr->name, IsOper(sptr) ? "" : "(L)", path);
472 else if (EmptyString(path))
473 path = "*no-path*"; /* Bogus server sending??? */
475 * Notify all *local* opers about the KILL (this includes the one
476 * originating the kill, if from this server--the special numeric
477 * reply message is not generated anymore).
479 * Note: "acptr->name" is used instead of "user" because we may
480 * have changed the target because of the nickname change.
482 if (IsLocOp(sptr) && !MyConnect(acptr))
484 sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); /* XXX DEAD */
487 sendto_op_mask(IsServer(sptr) ? SNO_SERVKILL : SNO_OPERKILL, /* XXX DEAD */
488 "Received KILL message for %s. From %s Path: %s!%s",
489 acptr->name, parv[0], inpath, path);
490 #if defined(USE_SYSLOG) && defined(SYSLOG_KILL)
492 { /* get more infos when your local
493 clients are killed -- _dl */
495 ircd_log(L_TRACE, /* XXX DEAD */
496 "A local client %s!%s@%s KILLED from %s [%s] Path: %s!%s)",
497 acptr->name, acptr->user->username, acptr->user->host,
498 parv[0], sptr->name, inpath, path);
500 ircd_log(L_TRACE, /* XXX DEAD */
501 "A local client %s!%s@%s KILLED by %s [%s!%s@%s] (%s!%s)",
502 acptr->name, acptr->user->username, acptr->user->host,
503 parv[0], sptr->name, sptr->user->username, sptr->user->host,
506 else if (IsOper(sptr))
507 ircd_log(L_TRACE, "KILL From %s For %s Path %s!%s", /* XXX DEAD */
508 parv[0], acptr->name, inpath, path);
511 * And pass on the message to other servers. Note, that if KILL
512 * was changed, the message has to be sent to all links, also
514 * Suicide kills are NOT passed on --SRB
516 if (!MyConnect(acptr) || !MyConnect(sptr) || !IsAnOper(sptr))
518 sendto_highprot_butone(cptr, 10, ":%s " TOK_KILL " %s%s :%s!%s", /* XXX DEAD */
519 parv[0], NumNick(acptr), inpath, path);
520 /* We *can* have crossed a NICK with this numeric... --Run */
521 /* Note the following situation:
523 * <-- S NICK ... SAA | <-- SAA QUIT <-- S NICK ... SAA <-- SQUIT S
524 * Where the KILL reaches point X before the QUIT does.
525 * This would then *still* cause an orphan because the KILL doesn't reach S
526 * (because of the SQUIT), the QUIT is ignored (because of the KILL)
527 * and the second NICK ... SAA causes an orphan on the server at the
528 * right (which then isn't removed when the SQUIT arrives).
529 * Therefore we still need to detect numeric nick collisions too.
531 if (MyConnect(acptr) && IsServer(cptr))
532 sendto_one(cptr, "%s " TOK_KILL " %s%s :%s!%s (Ghost5)", /* XXX DEAD */
533 NumServ(&me), NumNick(acptr), inpath, path);
534 acptr->flags |= FLAGS_KILLED;
538 * Tell the victim she/he has been zapped, but *only* if
539 * the victim is on current server--no sense in sending the
540 * notification chasing the above kill, it won't get far
541 * anyway (as this user don't exist there any more either)
543 if (MyConnect(acptr))
544 sendto_prefix_one(acptr, sptr, ":%s KILL %s :%s!%s", /* XXX DEAD */
545 parv[0], acptr->name, inpath, path);
547 * Set FLAGS_KILLED. This prevents exit_one_client from sending
548 * the unnecessary QUIT for this. (This flag should never be
549 * set in any other place)
551 if (MyConnect(acptr) && MyConnect(sptr) && IsAnOper(sptr))
552 sprintf_irc(buf2, "Local kill by %s (%s)", sptr->name,
553 EmptyString(parv[parc - 1]) ? sptr->name : parv[parc - 1]);
556 if ((killer = strchr(path, ' ')))
558 while (*killer && *killer != '!')
567 sprintf_irc(buf2, "Killed (%s)", killer);
569 return exit_client(cptr, acptr, sptr, buf2);