]> jfr.im git - irc/rqf/shadowircd.git/blame - modules/m_xline.c
[svn] - the new plan:
[irc/rqf/shadowircd.git] / modules / m_xline.c
CommitLineData
212380e3 1/* modules/m_xline.c
2 *
3 * Copyright (C) 2002-2003 Lee Hardy <lee@leeh.co.uk>
4 * Copyright (C) 2002-2005 ircd-ratbox development team
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * 1.Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 * 2.Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3.The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 *
30 * $Id: m_xline.c 3059 2006-12-27 00:36:54Z jilles $
31 */
32
33#include "stdinc.h"
34#include "tools.h"
35#include "send.h"
36#include "channel.h"
37#include "client.h"
38#include "common.h"
39#include "config.h"
40#include "class.h"
41#include "ircd.h"
42#include "numeric.h"
43#include "memory.h"
44#include "s_log.h"
45#include "s_serv.h"
46#include "whowas.h"
47#include "irc_string.h"
48#include "sprintf_irc.h"
49#include "hash.h"
50#include "msg.h"
51#include "parse.h"
52#include "modules.h"
53#include "s_conf.h"
54#include "s_newconf.h"
55
56static int mo_xline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]);
57static int ms_xline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]);
58static int me_xline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]);
59static int mo_unxline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]);
60static int ms_unxline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]);
61static int me_unxline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]);
62
63struct Message xline_msgtab = {
64 "XLINE", 0, 0, 0, MFLG_SLOW,
65 {mg_unreg, mg_not_oper, {ms_xline, 5}, {ms_xline, 5}, {me_xline, 5}, {mo_xline, 3}}
66};
67struct Message unxline_msgtab = {
68 "UNXLINE", 0, 0, 0, MFLG_SLOW,
69 {mg_unreg, mg_not_oper, {ms_unxline, 3}, {ms_unxline, 3}, {me_unxline, 2}, {mo_unxline, 2}}
70};
71
72mapi_clist_av1 xline_clist[] = { &xline_msgtab, &unxline_msgtab, NULL };
73DECLARE_MODULE_AV1(xline, NULL, NULL, xline_clist, NULL, NULL, "$Revision: 3059 $");
74
75static int valid_xline(struct Client *, const char *, const char *);
76static void apply_xline(struct Client *client_p, const char *name,
77 const char *reason, int temp_time);
78static void write_xline(struct Client *source_p, struct ConfItem *aconf);
79static void propagate_xline(struct Client *source_p, const char *target,
80 int temp_time, const char *name,
81 const char *type, const char *reason);
82static void cluster_xline(struct Client *source_p, int temp_time,
83 const char *name, const char *reason);
84
85static void handle_remote_xline(struct Client *source_p, int temp_time,
86 const char *name, const char *reason);
87static void handle_remote_unxline(struct Client *source_p, const char *name);
88
89static int remove_temp_xline(struct Client *source_p, const char *name);
90static void remove_xline(struct Client *source_p, const char *gecos);
91
92
93/* m_xline()
94 *
95 * parv[1] - thing to xline
96 * parv[2] - optional type/reason
97 * parv[3] - reason
98 */
99static int
100mo_xline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
101{
102 struct ConfItem *aconf;
103 const char *name;
104 const char *reason;
105 const char *target_server = NULL;
106 int temp_time;
107 int loc = 1;
108
109 if(!IsOperXline(source_p))
110 {
111 sendto_one(source_p, form_str(ERR_NOPRIVS),
112 me.name, source_p->name, "xline");
113 return 0;
114 }
115
116 if((temp_time = valid_temp_time(parv[loc])) >= 0)
117 loc++;
118 /* we just set temp_time to -1! */
119 else
120 temp_time = 0;
121
122 name = parv[loc];
123 loc++;
124
125 /* XLINE <gecos> ON <server> :<reason> */
126 if(parc >= loc+2 && !irccmp(parv[loc], "ON"))
127 {
128 if(!IsOperRemoteBan(source_p))
129 {
130 sendto_one(source_p, form_str(ERR_NOPRIVS),
131 me.name, source_p->name, "remoteban");
132 return 0;
133 }
134
135 target_server = parv[loc+1];
136 loc += 2;
137 }
138
139 if(parc <= loc || EmptyString(parv[loc]))
140 {
141 sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS),
142 me.name, source_p->name, "XLINE");
143 return 0;
144 }
145
146 reason = parv[loc];
147
148 if(target_server != NULL)
149 {
150 propagate_xline(source_p, target_server, temp_time,
151 name, "2", reason);
152
153 if(!match(target_server, me.name))
154 return 0;
155 }
156 else if(dlink_list_length(&cluster_conf_list) > 0)
157 cluster_xline(source_p, temp_time, name, reason);
158
159 if((aconf = find_xline(name, 0)) != NULL)
160 {
161 sendto_one(source_p, ":%s NOTICE %s :[%s] already X-Lined by [%s] - %s",
162 me.name, source_p->name, parv[1], aconf->name, aconf->passwd);
163 return 0;
164 }
165
166 if(!valid_xline(source_p, name, reason))
167 return 0;
168
169 apply_xline(source_p, name, reason, temp_time);
170
171 return 0;
172}
173
174/* ms_xline()
175 *
176 * handles a remote xline
177 */
178static int
179ms_xline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
180{
181 /* parv[0] parv[1] parv[2] parv[3] parv[4]
182 * oper target serv xline type reason
183 */
184 propagate_xline(source_p, parv[1], 0, parv[2], parv[3], parv[4]);
185
186 if(!IsPerson(source_p))
187 return 0;
188
189 /* destined for me? */
190 if(!match(parv[1], me.name))
191 return 0;
192
193 handle_remote_xline(source_p, 0, parv[2], parv[4]);
194 return 0;
195}
196
197static int
198me_xline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
199{
200 /* time name type :reason */
201 if(!IsPerson(source_p))
202 return 0;
203
204 handle_remote_xline(source_p, atoi(parv[1]), parv[2], parv[4]);
205 return 0;
206}
207
208static void
209handle_remote_xline(struct Client *source_p, int temp_time,
210 const char *name, const char *reason)
211{
212 struct ConfItem *aconf;
213
214 if(!find_shared_conf(source_p->username, source_p->host,
215 source_p->user->server,
216 (temp_time > 0) ? SHARED_TXLINE : SHARED_PXLINE))
217 return;
218
219 if(!valid_xline(source_p, name, reason))
220 return;
221
222 /* already xlined */
223 if((aconf = find_xline(name, 0)) != NULL)
224 {
225 sendto_one(source_p, ":%s NOTICE %s :[%s] already X-Lined by [%s] - %s",
226 me.name, source_p->name, name,
227 aconf->name, aconf->passwd);
228 return;
229 }
230
231 apply_xline(source_p, name, reason, temp_time);
232}
233
234/* valid_xline()
235 *
236 * inputs - client xlining, gecos, reason and whether to warn
237 * outputs -
238 * side effects - checks the xline for validity, erroring if needed
239 */
240static int
241valid_xline(struct Client *source_p, const char *gecos,
242 const char *reason)
243{
244 if(EmptyString(reason))
245 {
246 sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS),
247 get_id(&me, source_p),
248 get_id(source_p, source_p), "XLINE");
249 return 0;
250 }
251
252 if(strchr(reason, ':') != NULL)
253 {
254 sendto_one_notice(source_p,
255 ":Invalid character ':' in comment");
256 return 0;
257 }
258
259 if(strchr(reason, '"'))
260 {
261 sendto_one_notice(source_p,
262 ":Invalid character '\"' in comment");
263 return 0;
264 }
265
266 if(!valid_wild_card_simple(gecos))
267 {
268 sendto_one_notice(source_p,
269 ":Please include at least %d non-wildcard "
270 "characters with the xline",
271 ConfigFileEntry.min_nonwildcard_simple);
272 return 0;
273 }
274
275 return 1;
276}
277
278void
279apply_xline(struct Client *source_p, const char *name, const char *reason,
280 int temp_time)
281{
282 struct ConfItem *aconf;
283
284 aconf = make_conf();
285 aconf->status = CONF_XLINE;
286
287 if(strstr(name, "\\s"))
288 {
289 char *tmp = LOCAL_COPY(name);
290 char *orig = tmp;
291 char *new = tmp;
292
293 while(*orig)
294 {
295 if(*orig == '\\' && *(orig + 1) != '\0')
296 {
297 if(*(orig + 1) == 's')
298 {
299 *new++ = ' ';
300 orig += 2;
301 }
302 /* otherwise skip that and the escaped
303 * character after it, so we dont mistake
304 * \\s as \s --fl
305 */
306 else
307 {
308 *new++ = *orig++;
309 *new++ = *orig++;
310 }
311 }
312 else
313 *new++ = *orig++;
314 }
315
316 *new = '\0';
317 DupString(aconf->name, tmp);
318 }
319 else
320 DupString(aconf->name, name);
321
322 DupString(aconf->passwd, reason);
323 collapse(aconf->name);
324
325 if(temp_time > 0)
326 {
327 aconf->hold = CurrentTime + temp_time;
328
329 sendto_realops_snomask(SNO_GENERAL, L_ALL,
330 "%s added temporary %d min. X-Line for [%s] [%s]",
331 get_oper_name(source_p), temp_time / 60,
332 aconf->name, reason);
333 ilog(L_KLINE, "X %s %d %s %s",
334 get_oper_name(source_p), temp_time / 60,
335 name, reason);
336 sendto_one_notice(source_p, ":Added temporary %d min. X-Line [%s]",
337 temp_time / 60, aconf->name);
338 }
339 else
340 {
341 sendto_realops_snomask(SNO_GENERAL, L_ALL, "%s added X-Line for [%s] [%s]",
342 get_oper_name(source_p),
343 aconf->name, aconf->passwd);
344 sendto_one_notice(source_p, ":Added X-Line for [%s] [%s]",
345 aconf->name, aconf->passwd);
346 write_xline(source_p, aconf);
347 ilog(L_KLINE, "X %s 0 %s %s",
348 get_oper_name(source_p), name, reason);
349 }
350
351 dlinkAddAlloc(aconf, &xline_conf_list);
352 check_xlines();
353}
354
355/* write_xline()
356 *
357 * inputs - gecos, reason, xline type
358 * outputs - writes an xline to the config
359 * side effects -
360 */
361static void
362write_xline(struct Client *source_p, struct ConfItem *aconf)
363{
364 char buffer[BUFSIZE * 2];
365 FILE *out;
366 const char *filename;
367
368 filename = ConfigFileEntry.xlinefile;
369
370 if((out = fopen(filename, "a")) == NULL)
371 {
372 sendto_realops_snomask(SNO_GENERAL, L_ALL, "*** Problem opening %s ", filename);
373 sendto_one_notice(source_p, ":*** Problem opening file, xline added temporarily only");
374 return;
375 }
376
377 ircsprintf(buffer, "\"%s\",\"0\",\"%s\",\"%s\",%ld\n",
378 aconf->name, aconf->passwd,
379 get_oper_name(source_p), CurrentTime);
380
381 if(fputs(buffer, out) == -1)
382 {
383 sendto_realops_snomask(SNO_GENERAL, L_ALL, "*** Problem writing to %s", filename);
384 sendto_one_notice(source_p, ":*** Problem writing to file, xline added temporarily only");
385 fclose(out);
386 return;
387 }
388
389 if(fclose(out))
390 {
391 sendto_realops_snomask(SNO_GENERAL, L_ALL, "*** Problem writing to %s", filename);
392 sendto_one_notice(source_p, ":*** Problem writing to file, xline added temporarily only");
393 return;
394 }
395}
396
397static void
398propagate_xline(struct Client *source_p, const char *target,
399 int temp_time, const char *name, const char *type,
400 const char *reason)
401{
402 if(!temp_time)
403 {
404 sendto_match_servs(source_p, target, CAP_CLUSTER, NOCAPS,
405 "XLINE %s %s %s :%s",
406 target, name, type, reason);
407 sendto_match_servs(source_p, target, CAP_ENCAP, CAP_CLUSTER,
408 "ENCAP %s XLINE %d %s 2 :%s",
409 target, temp_time, name, reason);
410 }
411 else
412 sendto_match_servs(source_p, target, CAP_ENCAP, NOCAPS,
413 "ENCAP %s XLINE %d %s %s :%s",
414 target, temp_time, name, type, reason);
415}
416
417static void
418cluster_xline(struct Client *source_p, int temp_time, const char *name,
419 const char *reason)
420{
421 struct remote_conf *shared_p;
422 dlink_node *ptr;
423
424 DLINK_FOREACH(ptr, cluster_conf_list.head)
425 {
426 shared_p = ptr->data;
427
428 /* old protocol cant handle temps, and we dont really want
429 * to convert them to perm.. --fl
430 */
431 if(!temp_time)
432 {
433 if(!(shared_p->flags & SHARED_PXLINE))
434 continue;
435
436 sendto_match_servs(source_p, shared_p->server, CAP_CLUSTER, NOCAPS,
437 "XLINE %s %s 2 :%s",
438 shared_p->server, name, reason);
439 sendto_match_servs(source_p, shared_p->server, CAP_ENCAP, CAP_CLUSTER,
440 "ENCAP %s XLINE 0 %s 2 :%s",
441 shared_p->server, name, reason);
442 }
443 else if(shared_p->flags & SHARED_TXLINE)
444 sendto_match_servs(source_p, shared_p->server, CAP_ENCAP, NOCAPS,
445 "ENCAP %s XLINE %d %s 2 :%s",
446 shared_p->server, temp_time, name, reason);
447 }
448}
449
450/* mo_unxline()
451 *
452 * parv[1] - thing to unxline
453 */
454static int
455mo_unxline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
456{
457 if(!IsOperXline(source_p))
458 {
459 sendto_one(source_p, form_str(ERR_NOPRIVS),
460 me.name, source_p->name, "xline");
461 return 0;
462 }
463
464 if(parc == 4 && !(irccmp(parv[2], "ON")))
465 {
466 if(!IsOperRemoteBan(source_p))
467 {
468 sendto_one(source_p, form_str(ERR_NOPRIVS),
469 me.name, source_p->name, "remoteban");
470 return 0;
471 }
472
473 propagate_generic(source_p, "UNXLINE", parv[3], CAP_CLUSTER,
474 "%s", parv[1]);
475
476 if(match(parv[3], me.name) == 0)
477 return 0;
478 }
479 else if(dlink_list_length(&cluster_conf_list))
480 cluster_generic(source_p, "UNXLINE", SHARED_UNXLINE, CAP_CLUSTER,
481 "%s", parv[1]);
482
483 if(remove_temp_xline(source_p, parv[1]))
484 return 0;
485
486 remove_xline(source_p, parv[1]);
487
488 return 0;
489}
490
491/* ms_unxline()
492 *
493 * handles a remote unxline
494 */
495static int
496ms_unxline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
497{
498 /* parv[0] parv[1] parv[2]
499 * oper target server gecos
500 */
501 propagate_generic(source_p, "UNXLINE", parv[1], CAP_CLUSTER,
502 "%s", parv[2]);
503
504 if(!match(parv[1], me.name))
505 return 0;
506
507 if(!IsPerson(source_p))
508 return 0;
509
510 handle_remote_unxline(source_p, parv[2]);
511 return 0;
512}
513
514static int
515me_unxline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
516{
517 /* name */
518 if(!IsPerson(source_p))
519 return 0;
520
521 handle_remote_unxline(source_p, parv[1]);
522 return 0;
523}
524
525static void
526handle_remote_unxline(struct Client *source_p, const char *name)
527{
528 if(!find_shared_conf(source_p->username, source_p->host,
529 source_p->user->server, SHARED_UNXLINE))
530 return;
531
532 if(remove_temp_xline(source_p, name))
533 return;
534
535 remove_xline(source_p, name);
536
537 return;
538}
539
540static int
541remove_temp_xline(struct Client *source_p, const char *name)
542{
543 struct ConfItem *aconf;
544 dlink_node *ptr;
545
546 DLINK_FOREACH(ptr, xline_conf_list.head)
547 {
548 aconf = ptr->data;
549
550 /* only want to check temp ones! */
551 if(!aconf->hold)
552 continue;
553
554 if(!irccmp(aconf->name, name))
555 {
556 sendto_one_notice(source_p,
557 ":X-Line for [%s] is removed",
558 name);
559 sendto_realops_snomask(SNO_GENERAL, L_ALL,
560 "%s has removed the temporary X-Line for: [%s]",
561 get_oper_name(source_p), name);
562 ilog(L_KLINE, "UX %s %s",
563 get_oper_name(source_p), name);
564
565 free_conf(aconf);
566 dlinkDestroy(ptr, &xline_conf_list);
567 return 1;
568 }
569 }
570
571 return 0;
572}
573
574/* remove_xline()
575 *
576 * inputs - gecos to remove
577 * outputs -
578 * side effects - removes xline from conf, if exists
579 */
580static void
581remove_xline(struct Client *source_p, const char *huntgecos)
582{
583 FILE *in, *out;
584 char buf[BUFSIZE];
585 char buff[BUFSIZE];
586 char temppath[BUFSIZE];
587 const char *filename;
588 const char *gecos;
589 mode_t oldumask;
590 char *p;
591 int error_on_write = 0;
592 int found_xline = 0;
593
594 filename = ConfigFileEntry.xlinefile;
595 ircsnprintf(temppath, sizeof(temppath),
596 "%s.tmp", ConfigFileEntry.xlinefile);
597
598 if((in = fopen(filename, "r")) == NULL)
599 {
600 sendto_one_notice(source_p, ":Cannot open %s", filename);
601 return;
602 }
603
604 oldumask = umask(0);
605
606 if((out = fopen(temppath, "w")) == NULL)
607 {
608 sendto_one_notice(source_p, ":Cannot open %s", temppath);
609 fclose(in);
610 umask(oldumask);
611 return;
612 }
613
614 umask(oldumask);
615
616 while (fgets(buf, sizeof(buf), in))
617 {
618 if(error_on_write)
619 {
620 if(temppath != NULL)
621 (void) unlink(temppath);
622
623 break;
624 }
625
626 strlcpy(buff, buf, sizeof(buff));
627
628 if((p = strchr(buff, '\n')) != NULL)
629 *p = '\0';
630
631 if((*buff == '\0') || (*buff == '#'))
632 {
633 error_on_write = (fputs(buf, out) < 0) ? YES : NO;
634 continue;
635 }
636
637 if((gecos = getfield(buff)) == NULL)
638 {
639 error_on_write = (fputs(buf, out) < 0) ? YES : NO;
640 continue;
641 }
642
643 /* matching.. */
644 if(irccmp(gecos, huntgecos) == 0)
645 found_xline++;
646 else
647 error_on_write = (fputs(buf, out) < 0) ? YES : NO;
648 }
649
650 fclose(in);
651 if (fclose(out))
652 error_on_write = YES;
653
654 if(error_on_write)
655 {
656 sendto_one_notice(source_p,
657 ":Couldn't write temp xline file, aborted");
658 return;
659 }
660 else if(found_xline == 0)
661 {
662 sendto_one_notice(source_p, ":No X-Line for %s", huntgecos);
663
664 if(temppath != NULL)
665 (void) unlink(temppath);
666 return;
667 }
668
669 if (rename(temppath, filename))
670 {
671 sendto_one_notice(source_p, ":Couldn't rename temp file, aborted");
672 return;
673 }
674 rehash_bans(0);
675
676 sendto_one_notice(source_p, ":X-Line for [%s] is removed", huntgecos);
677 sendto_realops_snomask(SNO_GENERAL, L_ALL,
678 "%s has removed the X-Line for: [%s]",
679 get_oper_name(source_p), huntgecos);
680 ilog(L_KLINE, "UX %s %s", get_oper_name(source_p), huntgecos);
681}