1 /*
2 * Unreal Internet Relay Chat Daemon, src/modules/m_kill.c
3 * (C) 2000-2001 Carsten V. Munk and the UnrealIRCd Team
4 * Moved to modules by Fish (Justin Hammond)
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 1, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "config.h"
22 #include "struct.h"
23 #include "common.h"
24 #include "sys.h"
25 #include "numeric.h"
26 #include "msg.h"
27 #include "channel.h"
28 #include <time.h>
29 #include <sys/stat.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #ifdef _WIN32
34 #include <io.h>
35 #endif
36 #include <fcntl.h>
37 #include "h.h"
38 #include "proto.h"
39 #ifdef STRIPBADWORDS
40 #include "badwords.h"
41 #endif
42 #ifdef _WIN32
43 #include "version.h"
44 #endif
45
46 DLLFUNC int m_kill(aClient *cptr, aClient *sptr, int parc, char *parv[]);
47 static char buf[BUFSIZE], buf2[BUFSIZE];
48
49 /* Place includes here */
50 #define MSG_KILL "KILL" /* KILL */
51 #define TOK_KILL "." /* 46 */
52
53 ModuleHeader MOD_HEADER(m_kill)
54 = {
55 "kill", /* Name of module */
56 "$Id$", /* Version */
57 "command /kill", /* Short description of module */
58 "3.2-b8-1",
59 NULL
60 };
61
62 /* This is called on module init, before Server Ready */
MOD_INIT(m_kill)63 DLLFUNC int MOD_INIT(m_kill)(ModuleInfo *modinfo)
64 {
65 /*
66 * We call our add_Command crap here
67 */
68 add_Command(MSG_KILL, TOK_KILL, m_kill, 2);
69 MARK_AS_OFFICIAL_MODULE(modinfo);
70 return MOD_SUCCESS;
71 }
72
73 /* Is first run when server is 100% ready */
MOD_LOAD(m_kill)74 DLLFUNC int MOD_LOAD(m_kill)(int module_load)
75 {
76 return MOD_SUCCESS;
77 }
78
79
80 /* Called when module is unloaded */
MOD_UNLOAD(m_kill)81 DLLFUNC int MOD_UNLOAD(m_kill)(int module_unload)
82 {
83 if (del_Command(MSG_KILL, TOK_KILL, m_kill) < 0)
84 {
85 sendto_realops("Failed to delete commands when unloading %s",
86 MOD_HEADER(m_kill).name);
87 }
88 return MOD_SUCCESS;
89
90 }
91
92
93 /*
94 ** m_kill
95 ** parv[0] = sender prefix
96 ** parv[1] = kill victim(s) - comma separated list
97 ** parv[2] = kill path
98 */
m_kill(aClient * cptr,aClient * sptr,int parc,char * parv[])99 DLLFUNC int m_kill(aClient *cptr, aClient *sptr, int parc, char *parv[])
100 {
101 aClient *acptr;
102 anUser *auser;
103 char inpath[HOSTLEN * 2 + USERLEN + 5];
104 char *oinpath = get_client_name(cptr, FALSE);
105 char *user, *path, *killer, *nick, *p, *s;
106 int chasing = 0, kcount = 0;
107
108
109
110 if (parc < 2 || *parv[1] == '\0')
111 {
112 sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
113 me.name, parv[0], "KILL");
114 return 0;
115 }
116
117 user = parv[1];
118 path = parv[2]; /* Either defined or NULL (parc >= 2!!) */
119
120 strlcpy(inpath, oinpath, sizeof inpath);
121
122 #ifndef ROXnet
123 if (IsServer(cptr) && (s = (char *)index(inpath, '.')) != NULL)
124 *s = '\0'; /* Truncate at first "." */
125 #endif
126
127 if (!IsPrivileged(cptr))
128 {
129 sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
130 return 0;
131 }
132 if (IsAnOper(cptr))
133 {
134 if (BadPtr(path))
135 {
136 sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
137 me.name, parv[0], "KILL");
138 return 0;
139 }
140 if (strlen(path) > (size_t)TOPICLEN)
141 path[TOPICLEN] = '\0';
142 }
143
144 if (MyClient(sptr))
145 user = (char *)canonize(user);
146
147 for (p = NULL, nick = strtoken(&p, user, ","); nick;
148 nick = strtoken(&p, NULL, ","))
149 {
150
151 chasing = 0;
152
153 if (!(acptr = find_client(nick, NULL)))
154 {
155 /*
156 ** If the user has recently changed nick, we automaticly
157 ** rewrite the KILL for this new nickname--this keeps
158 ** servers in synch when nick change and kill collide
159 */
160 if (!(acptr =
161 get_history(nick, (long)KILLCHASETIMELIMIT)))
162 {
163 sendto_one(sptr, err_str(ERR_NOSUCHNICK),
164 me.name, parv[0], nick);
165 continue;
166 }
167 sendto_one(sptr,
168 ":%s %s %s :*** KILL changed from %s to %s",
169 me.name, IsWebTV(sptr) ? "PRIVMSG" : "NOTICE", parv[0], nick, acptr->name);
170 chasing = 1;
171 }
172 if ((!MyConnect(acptr) && MyClient(cptr) && !OPCanGKill(cptr))
173 || (MyConnect(acptr) && MyClient(cptr)
174 && !OPCanLKill(cptr)))
175 {
176 sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name,
177 parv[0]);
178 continue;
179 }
180 if (IsServer(acptr) || IsMe(acptr))
181 {
182 sendto_one(sptr, err_str(ERR_CANTKILLSERVER),
183 me.name, parv[0]);
184 continue;
185 }
186 if (!IsPerson(acptr))
187 {
188 /* Nick exists but user is not registered yet: IOTW "doesn't exist". -- Syzop */
189 sendto_one(sptr, err_str(ERR_NOSUCHNICK),
190 me.name, parv[0], nick);
191 continue;
192 }
193
194 if (IsServices(acptr) && !(IsNetAdmin(sptr) || IsULine(sptr)))
195 {
196 sendto_one(sptr, err_str(ERR_KILLDENY), me.name,
197 parv[0], parv[1]);
198 return 0;
199 }
200 /* From here on, the kill is probably going to be successful. */
201
202 kcount++;
203
204 if (!IsServer(sptr) && (kcount > MAXKILLS))
205 {
206 sendto_one(sptr,
207 ":%s %s %s :*** Too many targets, kill list was truncated. Maximum is %d.",
208 me.name, IsWebTV(sptr) ? "PRIVMSG" : "NOTICE", parv[0], MAXKILLS);
209 break;
210 }
211 if (!IsServer(cptr))
212 {
213 /*
214 ** The kill originates from this server, initialize path.
215 ** (In which case the 'path' may contain user suplied
216 ** explanation ...or some nasty comment, sigh... >;-)
217 **
218 ** ...!operhost!oper
219 ** ...!operhost!oper (comment)
220 */
221 strlcpy(inpath, GetHost(cptr), sizeof inpath);
222 if (kcount < 2) { /* Only check the path the first time
223 around, or it gets appended to itself. */
224 if (!BadPtr(path))
225 {
226 (void)ircsprintf(buf, "%s%s (%s)",
227 cptr->name,
228 IsOper(sptr) ? "" : "(L)", path);
229 path = buf;
230 }
231 else
232 path = cptr->name;
233 }
234 }
235 else if (BadPtr(path))
236 path = "*no-path*"; /* Bogus server sending??? */
237 /*
238 ** Notify all *local* opers about the KILL (this includes the one
239 ** originating the kill, if from this server--the special numeric
240 ** reply message is not generated anymore).
241 **
242 ** Note: "acptr->name" is used instead of "user" because we may
243 ** have changed the target because of the nickname change.
244 */
245
246 auser = acptr->user;
247
248 sendto_snomask_normal(SNO_KILLS,
249 "*** Notice -- Received KILL message for %s!%s@%s from %s Path: %s!%s",
250 acptr->name, auser->username,
251 IsHidden(acptr) ? auser->virthost : auser->realhost,
252 parv[0], inpath, path);
253 #if defined(USE_SYSLOG) && defined(SYSLOG_KILL)
254 if (IsOper(sptr))
255 syslog(LOG_DEBUG, "KILL From %s For %s Path %s!%s",
256 parv[0], acptr->name, inpath, path);
257 #endif
258 /*
259 * By otherguy
260 */
261 ircd_log
262 (LOG_KILL, "KILL (%s) by %s(%s!%s)",
263 make_nick_user_host
264 (acptr->name, acptr->user->username, GetHost(acptr)),
265 parv[0],
266 inpath,
267 path);
268 /*
269 ** And pass on the message to other servers. Note, that if KILL
270 ** was changed, the message has to be sent to all links, also
271 ** back.
272 ** Suicide kills are NOT passed on --SRB
273 */
274 if (!MyConnect(acptr) || !MyConnect(sptr) || !IsAnOper(sptr))
275 {
276 sendto_serv_butone(cptr, ":%s KILL %s :%s!%s",
277 parv[0], acptr->name, inpath, path);
278 if (chasing && IsServer(cptr))
279 sendto_one(cptr, ":%s KILL %s :%s!%s",
280 me.name, acptr->name, inpath, path);
281 acptr->flags |= FLAGS_KILLED;
282 }
283
284 /*
285 ** Tell the victim she/he has been zapped, but *only* if
286 ** the victim is on current server--no sense in sending the
287 ** notification chasing the above kill, it won't get far
288 ** anyway (as this user don't exist there any more either)
289 */
290 if (MyConnect(acptr))
291 sendto_prefix_one(acptr, sptr, ":%s KILL %s :%s!%s",
292 parv[0], acptr->name, inpath, path);
293 /*
294 ** Set FLAGS_KILLED. This prevents exit_one_client from sending
295 ** the unnecessary QUIT for this. (This flag should never be
296 ** set in any other place)
297 */
298 if (MyConnect(acptr) && MyConnect(sptr) && IsAnOper(sptr))
299
300 (void)ircsprintf(buf2, "[%s] Local kill by %s (%s)",
301 me.name, sptr->name,
302 BadPtr(parv[2]) ? sptr->name : parv[2]);
303 else
304 {
305 if ((killer = index(path, ' ')))
306 {
307 while ((killer >= path) && *killer && *killer != '!')
308 killer--;
309 if (!*killer)
310 killer = path;
311 else
312 killer++;
313 }
314 else
315 killer = path;
316 (void)ircsprintf(buf2, "Killed (%s)", killer);
317 }
318
319 if (MyClient(sptr))
320 RunHook3(HOOKTYPE_LOCAL_KILL, sptr, acptr, parv[2]);
321 if (exit_client(cptr, acptr, sptr, buf2) == FLUSH_BUFFER)
322 return FLUSH_BUFFER;
323 }
324 return 0;
325 }
326