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]))) {
145 "%s NOTICE %s%s :KILL target disconnected before I got him :(",
146 NumServ(&me), NumNick(sptr));
151 * XXX - strictly speaking, the next 2 checks shouldn't be needed
152 * this function only handles kills from servers, and the check
153 * is done before the message is propagated --Bleep
155 if (IsServer(victim) || IsMe(victim)) {
156 return send_error_to_client(sptr, ERR_CANTKILLSERVER);
159 if (IsLocOp(sptr) && !MyConnect(victim)) {
160 return send_error_to_client(sptr, ERR_NOPRIVILEGES);
164 * XXX - this is guaranteed by the parser not to happen
166 if (EmptyString(path))
167 path = "*no-path*"; /* Bogus server sending??? */
170 * Notify all *local* opers about the KILL (this includes the one
171 * originating the kill, if from this server--the special numeric
172 * reply message is not generated anymore).
174 * Note: "victim->name" is used instead of "user" because we may
175 * have changed the target because of the nickname change.
179 sendto_op_mask(IsServer(sptr) ? SNO_SERVKILL : SNO_OPERKILL,
180 "Received KILL message for %s. From %s Path: %s!%s",
181 victim->name, parv[0], cptr->name, path);
183 #if defined(SYSLOG_KILL)
184 ircd_log_kill(victim, sptr, cptr->name, path);
187 * And pass on the message to other servers. Note, that if KILL
188 * was changed, the message has to be sent to all links, also
191 #if defined(EVERYONE_SENDS_NUMERICS)
193 * just pass parv[0] here, it's the numeric nick of the sender
195 sendto_highprot_butone(cptr, 10, "%s " TOK_KILL " %s%s :%s!%s",
196 parv[0], NumNick(victim), cptr->name, path);
199 * translate to numerics
202 sendto_highprot_butone(cptr, 10, "%s " TOK_KILL " %s%s :%s!%s",
203 NumServ(sptr), NumNick(victim), cptr->name, path);
205 sendto_highprot_butone(cptr, 10, "%s%s " TOK_KILL " %s%s :%s!%s",
206 NumNick(sptr), NumNick(victim), cptr->name, path);
209 * We *can* have crossed a NICK with this numeric... --Run
211 * Note the following situation:
213 * <-- S NICK ... SAA | <-- SAA QUIT <-- S NICK ... SAA <-- SQUIT S
214 * Where the KILL reaches point X before the QUIT does.
215 * This would then *still* cause an orphan because the KILL doesn't reach S
216 * (because of the SQUIT), the QUIT is ignored (because of the KILL)
217 * and the second NICK ... SAA causes an orphan on the server at the
218 * right (which then isn't removed when the SQUIT arrives).
219 * Therefore we still need to detect numeric nick collisions too.
221 * Bounce the kill back to the originator, if the client can't be found
222 * by the next hop (short lag) the bounce won't propagate further.
224 if (MyConnect(victim))
225 sendto_one(cptr, "%s " TOK_KILL " %s%s :%s!%s (Ghost 5 Numeric Collided)",
226 NumServ(&me), NumNick(victim), cptr->name, path);
228 * Set FLAGS_KILLED. This prevents exit_one_client from sending
229 * the unnecessary QUIT for this. (This flag should never be
230 * set in any other place)
232 victim->flags |= FLAGS_KILLED;
235 * Tell the victim she/he has been zapped, but *only* if
236 * the victim is on current server--no sense in sending the
237 * notification chasing the above kill, it won't get far
238 * anyway (as this user don't exist there any more either)
240 if (MyConnect(victim))
241 sendto_prefix_one(victim, sptr, ":%s KILL %s :%s!%s",
242 sptr->name, victim->name, cptr->name, path);
244 * the first space in path will be at the end of the
246 * bla.bla.bla!host.net.dom!opername (comment)
248 if ((killer = strchr(path, ' '))) {
249 while (killer > path && '!' != *killer)
256 sprintf_irc(buf, "Killed (%s)", killer);
258 return exit_client(cptr, victim, sptr, buf);
262 * mo_kill - oper message handler template
264 * NOTE: IsPrivileged(sptr), IsAnOper(sptr) == true
265 * IsServer(cptr), IsServer(sptr) == false
267 * parv[0] = sender prefix
268 * parv[1] = kill victim
269 * parv[parc-1] = kill path
271 int mo_kill(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
273 struct Client* victim;
283 * oper connection to this server, cptr is always sptr
285 assert(cptr == sptr);
286 assert(IsAnOper(sptr));
288 #if defined(OPER_KILL)
290 if (parc < 3 || EmptyString(parv[parc - 1]))
291 return need_more_params(sptr, "KILL");
294 if (!(victim = FindClient(user))) {
296 * If the user has recently changed nick, we automaticly
297 * rewrite the KILL for this new nickname--this keeps
298 * servers in synch when nick change and kill collide
300 if (!(victim = get_history(user, (long)15)))
301 return send_error_to_client(sptr, ERR_NOSUCHNICK, user);
303 sendto_one(sptr, ":%s NOTICE %s :Changed KILL %s into %s",
304 me.name, parv[0], user, victim->name);
306 if (!MyConnect(victim) && IsLocOp(cptr))
307 return send_error_to_client(sptr, ERR_NOPRIVILEGES);
309 if (IsServer(victim) || IsMe(victim)) {
310 return send_error_to_client(sptr, ERR_CANTKILLSERVER);
313 * if the user is +k, prevent a kill from local user
315 if (IsChannelService(victim))
316 return send_error_to_client(sptr, ERR_ISCHANSERVICE, "KILL", victim->name);
319 #ifdef LOCAL_KILL_ONLY
320 if (!MyConnect(victim)) {
321 sendto_one(sptr, ":%s NOTICE %s :Nick %s isnt on your server",
322 me.name, parv[0], victim->name);
327 * The kill originates from this server, initialize path.
328 * (In which case the 'path' may contain user suplied
329 * explanation ...or some nasty comment, sigh... >;-)
332 * ...!operhost!oper (comment)
335 comment = parv[parc - 1]; /* Either defined or NULL (parc >= 3) */
337 if (strlen(comment) > TOPICLEN)
338 comment[TOPICLEN] = '\0';
340 inpath = sptr->user->host;
343 "%s%s (%s)", cptr->name, IsOper(sptr) ? "" : "(L)", comment);
347 * Notify all *local* opers about the KILL (this includes the one
348 * originating the kill, if from this server--the special numeric
349 * reply message is not generated anymore).
351 * Note: "victim->name" is used instead of "user" because we may
352 * have changed the target because of the nickname change.
354 sendto_op_mask(SNO_OPERKILL,
355 "Received KILL message for %s. From %s Path: %s!%s",
356 victim->name, parv[0], inpath, path);
358 #if defined(SYSLOG_KILL)
359 ircd_log_kill(victim, sptr, inpath, path);
362 * And pass on the message to other servers. Note, that if KILL
363 * was changed, the message has to be sent to all links, also
365 * Suicide kills are NOT passed on --SRB
367 if (!MyConnect(victim)) {
368 sendto_highprot_butone(cptr, 10, "%s%s " TOK_KILL " %s%s :%s!%s",
369 NumNick(sptr), NumNick(victim), inpath, path);
372 * Set FLAGS_KILLED. This prevents exit_one_client from sending
373 * the unnecessary QUIT for this. (This flag should never be
374 * set in any other place)
376 victim->flags |= FLAGS_KILLED;
378 sprintf_irc(buf, "Killed by %s (%s)", sptr->name, comment);
382 * Tell the victim she/he has been zapped, but *only* if
383 * the victim is on current server--no sense in sending the
384 * notification chasing the above kill, it won't get far
385 * anyway (as this user don't exist there any more either)
387 sendto_prefix_one(victim, sptr, ":%s KILL %s :%s!%s",
388 parv[0], victim->name, inpath, path);
389 sprintf_irc(buf, "Local kill by %s (%s)", sptr->name, comment);
392 return exit_client(cptr, victim, sptr, buf);
394 #else /* !defined(OPER_KILL) */
396 return send_error_to_client(sptr, ERR_NOPRIVILEGES);
398 #endif /* !defined(OPER_KILL) */
405 * parv[0] = sender prefix
406 * parv[1] = kill victim
407 * parv[parc-1] = kill path
409 int m_kill(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
411 struct Client* acptr;
412 const char* inpath = get_client_name(cptr, HIDE_IP);
420 if (parc < 3 || *parv[1] == '\0')
421 return need_more_params(sptr, parv[0], "KILL");
424 path = parv[parc - 1]; /* Either defined or NULL (parc >= 3) */
427 if (!IsPrivileged(cptr))
429 sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
435 sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
441 if (EmptyString(path))
442 return need_more_params(sptr, parv[0], "KILL");
444 if (strlen(path) > (size_t)TOPICLEN)
445 path[TOPICLEN] = '\0';
450 if (!(acptr = FindClient(user)))
453 * If the user has recently changed nick, we automaticly
454 * rewrite the KILL for this new nickname--this keeps
455 * servers in synch when nick change and kill collide
457 if (!(acptr = get_history(user, (long)15)))
459 sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], user);
462 sendto_one(sptr, ":%s NOTICE %s :Changed KILL %s into %s",
463 me.name, parv[0], user, acptr->name);
467 else if (!(acptr = findNUser(user)))
471 "%s NOTICE %s%s :KILL target disconnected before I got him :(",
472 NumServ(&me), NumNick(sptr));
475 if (!MyConnect(acptr) && IsLocOp(cptr))
477 sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
480 if (IsServer(acptr) || IsMe(acptr))
482 sendto_one(sptr, err_str(ERR_CANTKILLSERVER), me.name, parv[0]);
486 /* if the user is +k, prevent a kill from local user */
487 if (IsChannelService(acptr) && MyUser(sptr))
489 sendto_one(sptr, err_str(ERR_ISCHANSERVICE), me.name,
490 parv[0], "KILL", acptr->name);
494 #ifdef LOCAL_KILL_ONLY
495 if (MyConnect(sptr) && !MyConnect(acptr))
497 sendto_one(sptr, ":%s NOTICE %s :Nick %s isnt on your server",
498 me.name, parv[0], acptr->name);
505 * The kill originates from this server, initialize path.
506 * (In which case the 'path' may contain user suplied
507 * explanation ...or some nasty comment, sigh... >;-)
510 * ...!operhost!oper (comment)
512 inpath = cptr->sockhost;
514 if (!EmptyString(path))
517 "%s%s (%s)", cptr->name, IsOper(sptr) ? "" : "(L)", path);
523 else if (EmptyString(path))
524 path = "*no-path*"; /* Bogus server sending??? */
526 * Notify all *local* opers about the KILL (this includes the one
527 * originating the kill, if from this server--the special numeric
528 * reply message is not generated anymore).
530 * Note: "acptr->name" is used instead of "user" because we may
531 * have changed the target because of the nickname change.
533 if (IsLocOp(sptr) && !MyConnect(acptr))
535 sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
538 sendto_op_mask(IsServer(sptr) ? SNO_SERVKILL : SNO_OPERKILL,
539 "Received KILL message for %s. From %s Path: %s!%s",
540 acptr->name, parv[0], inpath, path);
541 #if defined(USE_SYSLOG) && defined(SYSLOG_KILL)
543 { /* get more infos when your local
544 clients are killed -- _dl */
547 "A local client %s!%s@%s KILLED from %s [%s] Path: %s!%s)",
548 acptr->name, acptr->user->username, acptr->user->host,
549 parv[0], sptr->name, inpath, path);
552 "A local client %s!%s@%s KILLED by %s [%s!%s@%s] (%s!%s)",
553 acptr->name, acptr->user->username, acptr->user->host,
554 parv[0], sptr->name, sptr->user->username, sptr->user->host,
557 else if (IsOper(sptr))
558 ircd_log(L_TRACE, "KILL From %s For %s Path %s!%s",
559 parv[0], acptr->name, inpath, path);
562 * And pass on the message to other servers. Note, that if KILL
563 * was changed, the message has to be sent to all links, also
565 * Suicide kills are NOT passed on --SRB
567 if (!MyConnect(acptr) || !MyConnect(sptr) || !IsAnOper(sptr))
569 sendto_highprot_butone(cptr, 10, ":%s " TOK_KILL " %s%s :%s!%s",
570 parv[0], NumNick(acptr), inpath, path);
571 /* We *can* have crossed a NICK with this numeric... --Run */
572 /* Note the following situation:
574 * <-- S NICK ... SAA | <-- SAA QUIT <-- S NICK ... SAA <-- SQUIT S
575 * Where the KILL reaches point X before the QUIT does.
576 * This would then *still* cause an orphan because the KILL doesn't reach S
577 * (because of the SQUIT), the QUIT is ignored (because of the KILL)
578 * and the second NICK ... SAA causes an orphan on the server at the
579 * right (which then isn't removed when the SQUIT arrives).
580 * Therefore we still need to detect numeric nick collisions too.
582 if (MyConnect(acptr) && IsServer(cptr))
583 sendto_one(cptr, "%s " TOK_KILL " %s%s :%s!%s (Ghost5)",
584 NumServ(&me), NumNick(acptr), inpath, path);
585 acptr->flags |= FLAGS_KILLED;
589 * Tell the victim she/he has been zapped, but *only* if
590 * the victim is on current server--no sense in sending the
591 * notification chasing the above kill, it won't get far
592 * anyway (as this user don't exist there any more either)
594 if (MyConnect(acptr))
595 sendto_prefix_one(acptr, sptr, ":%s KILL %s :%s!%s",
596 parv[0], acptr->name, inpath, path);
598 * Set FLAGS_KILLED. This prevents exit_one_client from sending
599 * the unnecessary QUIT for this. (This flag should never be
600 * set in any other place)
602 if (MyConnect(acptr) && MyConnect(sptr) && IsAnOper(sptr))
603 sprintf_irc(buf2, "Local kill by %s (%s)", sptr->name,
604 EmptyString(parv[parc - 1]) ? sptr->name : parv[parc - 1]);
607 if ((killer = strchr(path, ' ')))
609 while (*killer && *killer != '!')
618 sprintf_irc(buf2, "Killed (%s)", killer);
620 return exit_client(cptr, acptr, sptr, buf2);