]> jfr.im git - irc/rqf/shadowircd.git/blob - modules/m_xline.c
Automated merge with ssh://hg.atheme.org//hg/charybdis
[irc/rqf/shadowircd.git] / modules / m_xline.c
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 3161 2007-01-25 07:23:01Z nenolod $
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
56 static int mo_xline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]);
57 static int ms_xline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]);
58 static int me_xline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]);
59 static int mo_unxline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]);
60 static int ms_unxline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]);
61 static int me_unxline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]);
62
63 struct 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 };
67 struct 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
72 mapi_clist_av1 xline_clist[] = { &xline_msgtab, &unxline_msgtab, NULL };
73 DECLARE_MODULE_AV1(xline, NULL, NULL, xline_clist, NULL, NULL, "$Revision: 3161 $");
74
75 static int valid_xline(struct Client *, const char *, const char *);
76 static void apply_xline(struct Client *client_p, const char *name,
77 const char *reason, int temp_time);
78 static void write_xline(struct Client *source_p, struct ConfItem *aconf);
79 static void propagate_xline(struct Client *source_p, const char *target,
80 int temp_time, const char *name,
81 const char *type, const char *reason);
82 static void cluster_xline(struct Client *source_p, int temp_time,
83 const char *name, const char *reason);
84
85 static void handle_remote_xline(struct Client *source_p, int temp_time,
86 const char *name, const char *reason);
87 static void handle_remote_unxline(struct Client *source_p, const char *name);
88
89 static void remove_xline(struct Client *source_p, const char *name);
90 static int remove_xline_from_file(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 */
99 static int
100 mo_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_mask(name)) != NULL)
160 {
161 sendto_one(source_p, ":%s NOTICE %s :[%s] already X-Lined by [%s] - %s",
162 me.name, source_p->name, name, 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 */
178 static int
179 ms_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
197 static int
198 me_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
208 static void
209 handle_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->servptr->name,
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_mask(name)) != NULL)
224 {
225 sendto_one_notice(source_p, ":[%s] already X-Lined by [%s] - %s", name, aconf->name, aconf->passwd);
226 return;
227 }
228
229 apply_xline(source_p, name, reason, temp_time);
230 }
231
232 /* valid_xline()
233 *
234 * inputs - client xlining, gecos, reason and whether to warn
235 * outputs -
236 * side effects - checks the xline for validity, erroring if needed
237 */
238 static int
239 valid_xline(struct Client *source_p, const char *gecos,
240 const char *reason)
241 {
242 if(EmptyString(reason))
243 {
244 sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS),
245 get_id(&me, source_p),
246 get_id(source_p, source_p), "XLINE");
247 return 0;
248 }
249
250 if(strchr(reason, ':') != NULL)
251 {
252 sendto_one_notice(source_p,
253 ":Invalid character ':' in comment");
254 return 0;
255 }
256
257 if(strchr(reason, '"'))
258 {
259 sendto_one_notice(source_p,
260 ":Invalid character '\"' in comment");
261 return 0;
262 }
263
264 if(!valid_wild_card_simple(gecos))
265 {
266 sendto_one_notice(source_p,
267 ":Please include at least %d non-wildcard "
268 "characters with the xline",
269 ConfigFileEntry.min_nonwildcard_simple);
270 return 0;
271 }
272
273 return 1;
274 }
275
276 void
277 apply_xline(struct Client *source_p, const char *name, const char *reason,
278 int temp_time)
279 {
280 struct ConfItem *aconf;
281
282 aconf = make_conf();
283 aconf->status = CONF_XLINE;
284
285 if(strstr(name, "\\s"))
286 {
287 char *tmp = LOCAL_COPY(name);
288 char *orig = tmp;
289 char *new = tmp;
290
291 while(*orig)
292 {
293 if(*orig == '\\' && *(orig + 1) != '\0')
294 {
295 if(*(orig + 1) == 's')
296 {
297 *new++ = ' ';
298 orig += 2;
299 }
300 /* otherwise skip that and the escaped
301 * character after it, so we dont mistake
302 * \\s as \s --fl
303 */
304 else
305 {
306 *new++ = *orig++;
307 *new++ = *orig++;
308 }
309 }
310 else
311 *new++ = *orig++;
312 }
313
314 *new = '\0';
315 DupString(aconf->name, tmp);
316 }
317 else
318 DupString(aconf->name, name);
319
320 DupString(aconf->passwd, reason);
321 collapse(aconf->name);
322
323 if(temp_time > 0)
324 {
325 aconf->hold = CurrentTime + temp_time;
326
327 sendto_realops_snomask(SNO_GENERAL, L_ALL,
328 "%s added temporary %d min. X-Line for [%s] [%s]",
329 get_oper_name(source_p), temp_time / 60,
330 aconf->name, reason);
331 ilog(L_KLINE, "X %s %d %s %s",
332 get_oper_name(source_p), temp_time / 60,
333 name, reason);
334 sendto_one_notice(source_p, ":Added temporary %d min. X-Line [%s]",
335 temp_time / 60, aconf->name);
336 }
337 else
338 {
339 sendto_realops_snomask(SNO_GENERAL, L_ALL, "%s added X-Line for [%s] [%s]",
340 get_oper_name(source_p),
341 aconf->name, aconf->passwd);
342 sendto_one_notice(source_p, ":Added X-Line for [%s] [%s]",
343 aconf->name, aconf->passwd);
344 write_xline(source_p, aconf);
345 ilog(L_KLINE, "X %s 0 %s %s",
346 get_oper_name(source_p), name, reason);
347 }
348
349 dlinkAddAlloc(aconf, &xline_conf_list);
350 check_xlines();
351 }
352
353 /* write_xline()
354 *
355 * inputs - gecos, reason, xline type
356 * outputs - writes an xline to the config
357 * side effects -
358 */
359 static void
360 write_xline(struct Client *source_p, struct ConfItem *aconf)
361 {
362 char buffer[BUFSIZE * 2];
363 FILE *out;
364 const char *filename;
365
366 filename = ConfigFileEntry.xlinefile;
367
368 if((out = fopen(filename, "a")) == NULL)
369 {
370 sendto_realops_snomask(SNO_GENERAL, L_ALL, "*** Problem opening %s ", filename);
371 sendto_one_notice(source_p, ":*** Problem opening file, xline added temporarily only");
372 return;
373 }
374
375 ircsprintf(buffer, "\"%s\",\"0\",\"%s\",\"%s\",%ld\n",
376 aconf->name, aconf->passwd,
377 get_oper_name(source_p), CurrentTime);
378
379 if(fputs(buffer, out) == -1)
380 {
381 sendto_realops_snomask(SNO_GENERAL, L_ALL, "*** Problem writing to %s", filename);
382 sendto_one_notice(source_p, ":*** Problem writing to file, xline added temporarily only");
383 fclose(out);
384 return;
385 }
386
387 if(fclose(out))
388 {
389 sendto_realops_snomask(SNO_GENERAL, L_ALL, "*** Problem writing to %s", filename);
390 sendto_one_notice(source_p, ":*** Problem writing to file, xline added temporarily only");
391 return;
392 }
393 }
394
395 static void
396 propagate_xline(struct Client *source_p, const char *target,
397 int temp_time, const char *name, const char *type,
398 const char *reason)
399 {
400 if(!temp_time)
401 {
402 sendto_match_servs(source_p, target, CAP_CLUSTER, NOCAPS,
403 "XLINE %s %s %s :%s",
404 target, name, type, reason);
405 sendto_match_servs(source_p, target, CAP_ENCAP, CAP_CLUSTER,
406 "ENCAP %s XLINE %d %s 2 :%s",
407 target, temp_time, name, reason);
408 }
409 else
410 sendto_match_servs(source_p, target, CAP_ENCAP, NOCAPS,
411 "ENCAP %s XLINE %d %s %s :%s",
412 target, temp_time, name, type, reason);
413 }
414
415 static void
416 cluster_xline(struct Client *source_p, int temp_time, const char *name,
417 const char *reason)
418 {
419 struct remote_conf *shared_p;
420 dlink_node *ptr;
421
422 DLINK_FOREACH(ptr, cluster_conf_list.head)
423 {
424 shared_p = ptr->data;
425
426 /* old protocol cant handle temps, and we dont really want
427 * to convert them to perm.. --fl
428 */
429 if(!temp_time)
430 {
431 if(!(shared_p->flags & SHARED_PXLINE))
432 continue;
433
434 sendto_match_servs(source_p, shared_p->server, CAP_CLUSTER, NOCAPS,
435 "XLINE %s %s 2 :%s",
436 shared_p->server, name, reason);
437 sendto_match_servs(source_p, shared_p->server, CAP_ENCAP, CAP_CLUSTER,
438 "ENCAP %s XLINE 0 %s 2 :%s",
439 shared_p->server, name, reason);
440 }
441 else if(shared_p->flags & SHARED_TXLINE)
442 sendto_match_servs(source_p, shared_p->server, CAP_ENCAP, NOCAPS,
443 "ENCAP %s XLINE %d %s 2 :%s",
444 shared_p->server, temp_time, name, reason);
445 }
446 }
447
448 /* mo_unxline()
449 *
450 * parv[1] - thing to unxline
451 */
452 static int
453 mo_unxline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
454 {
455 if(!IsOperXline(source_p))
456 {
457 sendto_one(source_p, form_str(ERR_NOPRIVS),
458 me.name, source_p->name, "xline");
459 return 0;
460 }
461
462 if(parc == 4 && !(irccmp(parv[2], "ON")))
463 {
464 if(!IsOperRemoteBan(source_p))
465 {
466 sendto_one(source_p, form_str(ERR_NOPRIVS),
467 me.name, source_p->name, "remoteban");
468 return 0;
469 }
470
471 propagate_generic(source_p, "UNXLINE", parv[3], CAP_CLUSTER,
472 "%s", parv[1]);
473
474 if(match(parv[3], me.name) == 0)
475 return 0;
476 }
477 else if(dlink_list_length(&cluster_conf_list))
478 cluster_generic(source_p, "UNXLINE", SHARED_UNXLINE, CAP_CLUSTER,
479 "%s", parv[1]);
480
481 remove_xline(source_p, parv[1]);
482
483 return 0;
484 }
485
486 /* ms_unxline()
487 *
488 * handles a remote unxline
489 */
490 static int
491 ms_unxline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
492 {
493 /* parv[0] parv[1] parv[2]
494 * oper target server gecos
495 */
496 propagate_generic(source_p, "UNXLINE", parv[1], CAP_CLUSTER,
497 "%s", parv[2]);
498
499 if(!match(parv[1], me.name))
500 return 0;
501
502 if(!IsPerson(source_p))
503 return 0;
504
505 handle_remote_unxline(source_p, parv[2]);
506 return 0;
507 }
508
509 static int
510 me_unxline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
511 {
512 /* name */
513 if(!IsPerson(source_p))
514 return 0;
515
516 handle_remote_unxline(source_p, parv[1]);
517 return 0;
518 }
519
520 static void
521 handle_remote_unxline(struct Client *source_p, const char *name)
522 {
523 if(!find_shared_conf(source_p->username, source_p->host,
524 source_p->servptr->name, SHARED_UNXLINE))
525 return;
526
527 remove_xline(source_p, name);
528
529 return;
530 }
531
532 static void
533 remove_xline(struct Client *source_p, const char *name)
534 {
535 struct ConfItem *aconf;
536 dlink_node *ptr;
537
538 DLINK_FOREACH(ptr, xline_conf_list.head)
539 {
540 aconf = ptr->data;
541
542 if(!irccmp(aconf->name, name))
543 {
544 if (!aconf->hold)
545 {
546 if (!remove_xline_from_file(source_p, name))
547 return;
548 }
549 else
550 {
551 sendto_one_notice(source_p,
552 ":X-Line for [%s] is removed",
553 name);
554 sendto_realops_snomask(SNO_GENERAL, L_ALL,
555 "%s has removed the temporary X-Line for: [%s]",
556 get_oper_name(source_p), name);
557 ilog(L_KLINE, "UX %s %s",
558 get_oper_name(source_p), name);
559 }
560
561 free_conf(aconf);
562 dlinkDestroy(ptr, &xline_conf_list);
563 return;
564 }
565 }
566
567 sendto_one_notice(source_p, ":No X-Line for %s", name);
568
569 return;
570 }
571
572 /* remove_xline_from_file()
573 *
574 * inputs - gecos to remove
575 * outputs -
576 * side effects - removes xline from conf, if exists
577 * - does not touch xline_conf_list
578 */
579 static int
580 remove_xline_from_file(struct Client *source_p, const char *huntgecos)
581 {
582 FILE *in, *out;
583 char buf[BUFSIZE];
584 char buff[BUFSIZE];
585 char temppath[BUFSIZE];
586 const char *filename;
587 const char *gecos;
588 mode_t oldumask;
589 char *p;
590 int error_on_write = 0;
591 int found_xline = 0;
592
593 filename = ConfigFileEntry.xlinefile;
594 ircsnprintf(temppath, sizeof(temppath),
595 "%s.tmp", ConfigFileEntry.xlinefile);
596
597 if((in = fopen(filename, "r")) == NULL)
598 {
599 sendto_one_notice(source_p, ":Cannot open %s", filename);
600 return 0;
601 }
602
603 oldumask = umask(0);
604
605 if((out = fopen(temppath, "w")) == NULL)
606 {
607 sendto_one_notice(source_p, ":Cannot open %s", temppath);
608 fclose(in);
609 umask(oldumask);
610 return 0;
611 }
612
613 umask(oldumask);
614
615 while (fgets(buf, sizeof(buf), in))
616 {
617 if(error_on_write)
618 {
619 if(temppath != NULL)
620 (void) unlink(temppath);
621
622 break;
623 }
624
625 strlcpy(buff, buf, sizeof(buff));
626
627 if((p = strchr(buff, '\n')) != NULL)
628 *p = '\0';
629
630 if((*buff == '\0') || (*buff == '#'))
631 {
632 error_on_write = (fputs(buf, out) < 0) ? YES : NO;
633 continue;
634 }
635
636 if((gecos = getfield(buff)) == NULL)
637 {
638 error_on_write = (fputs(buf, out) < 0) ? YES : NO;
639 continue;
640 }
641
642 /* matching.. */
643 if(irccmp(gecos, huntgecos) == 0)
644 found_xline++;
645 else
646 error_on_write = (fputs(buf, out) < 0) ? YES : NO;
647 }
648
649 fclose(in);
650 if (fclose(out))
651 error_on_write = YES;
652
653 if(error_on_write)
654 {
655 sendto_one_notice(source_p,
656 ":Couldn't write temp xline file, aborted");
657 return 0;
658 }
659 else if(found_xline == 0)
660 {
661 sendto_one_notice(source_p, ":Cannot find X-Line for %s in file", huntgecos);
662
663 if(temppath != NULL)
664 (void) unlink(temppath);
665 return 0;
666 }
667
668 if (rename(temppath, filename))
669 {
670 sendto_one_notice(source_p, ":Couldn't rename temp file, aborted");
671 return 0;
672 }
673
674 sendto_one_notice(source_p, ":X-Line for [%s] is removed", huntgecos);
675 sendto_realops_snomask(SNO_GENERAL, L_ALL,
676 "%s has removed the X-Line for: [%s]",
677 get_oper_name(source_p), huntgecos);
678 ilog(L_KLINE, "UX %s %s", get_oper_name(source_p), huntgecos);
679
680 return 1;
681 }