]> 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 "send.h"
35 #include "channel.h"
36 #include "client.h"
37 #include "common.h"
38 #include "config.h"
39 #include "class.h"
40 #include "ircd.h"
41 #include "numeric.h"
42 #include "logger.h"
43 #include "s_serv.h"
44 #include "whowas.h"
45 #include "match.h"
46 #include "hash.h"
47 #include "msg.h"
48 #include "parse.h"
49 #include "modules.h"
50 #include "s_conf.h"
51 #include "s_newconf.h"
52 #include "reject.h"
53
54 static int mo_xline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]);
55 static int ms_xline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]);
56 static int me_xline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]);
57 static int mo_unxline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]);
58 static int ms_unxline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]);
59 static int me_unxline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]);
60
61 struct Message xline_msgtab = {
62 "XLINE", 0, 0, 0, MFLG_SLOW,
63 {mg_unreg, mg_not_oper, {ms_xline, 5}, {ms_xline, 5}, {me_xline, 5}, {mo_xline, 3}}
64 };
65 struct Message unxline_msgtab = {
66 "UNXLINE", 0, 0, 0, MFLG_SLOW,
67 {mg_unreg, mg_not_oper, {ms_unxline, 3}, {ms_unxline, 3}, {me_unxline, 2}, {mo_unxline, 2}}
68 };
69
70 mapi_clist_av1 xline_clist[] = { &xline_msgtab, &unxline_msgtab, NULL };
71 DECLARE_MODULE_AV1(xline, NULL, NULL, xline_clist, NULL, NULL, "$Revision: 3161 $");
72
73 static char *escape_perm_xline(const char *);
74 static int valid_xline(struct Client *, const char *, const char *, int);
75 static void apply_xline(struct Client *client_p, const char *name,
76 const char *reason, int temp_time);
77 static void write_xline(struct Client *source_p, struct ConfItem *aconf);
78 static void propagate_xline(struct Client *source_p, const char *target,
79 int temp_time, const char *name,
80 const char *type, const char *reason);
81 static void cluster_xline(struct Client *source_p, int temp_time,
82 const char *name, const char *reason);
83
84 static void handle_remote_xline(struct Client *source_p, int temp_time,
85 const char *name, const char *reason);
86 static void handle_remote_unxline(struct Client *source_p, const char *name);
87
88 static void remove_xline(struct Client *source_p, const char *name);
89 static int remove_xline_from_file(struct Client *source_p, const char *gecos);
90
91
92 /* m_xline()
93 *
94 * parv[1] - thing to xline
95 * parv[2] - optional type/reason
96 * parv[3] - reason
97 */
98 static int
99 mo_xline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
100 {
101 struct ConfItem *aconf;
102 const char *name;
103 char *escapedname;
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 (temp_time == 0)
149 {
150 escapedname = escape_perm_xline(name);
151 if (strcmp(escapedname, name))
152 sendto_one_notice(source_p, ":Changed xline from [%s] to [%s]",
153 name, escapedname);
154 }
155 else
156 escapedname = rb_strdup(name);
157
158 if(target_server != NULL)
159 {
160 propagate_xline(source_p, target_server, temp_time,
161 escapedname, "2", reason);
162
163 if(!match(target_server, me.name))
164 {
165 rb_free(escapedname);
166 return 0;
167 }
168 }
169 else if(rb_dlink_list_length(&cluster_conf_list) > 0)
170 cluster_xline(source_p, temp_time, escapedname, reason);
171
172 if((aconf = find_xline_mask(escapedname)) != NULL)
173 {
174 sendto_one(source_p, ":%s NOTICE %s :[%s] already X-Lined by [%s] - %s",
175 me.name, source_p->name, escapedname, aconf->name, aconf->passwd);
176 rb_free(escapedname);
177 return 0;
178 }
179
180 if(!valid_xline(source_p, escapedname, reason, temp_time))
181 {
182 rb_free(escapedname);
183 return 0;
184 }
185
186 apply_xline(source_p, escapedname, reason, temp_time);
187 rb_free(escapedname);
188
189 return 0;
190 }
191
192 /* ms_xline()
193 *
194 * handles a remote xline
195 */
196 static int
197 ms_xline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
198 {
199 /* source_p parv[1] parv[2] parv[3] parv[4]
200 * oper target serv xline type reason
201 */
202 propagate_xline(source_p, parv[1], 0, parv[2], parv[3], parv[4]);
203
204 if(!IsPerson(source_p))
205 return 0;
206
207 /* destined for me? */
208 if(!match(parv[1], me.name))
209 return 0;
210
211 handle_remote_xline(source_p, 0, parv[2], parv[4]);
212 return 0;
213 }
214
215 static int
216 me_xline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
217 {
218 /* time name type :reason */
219 if(!IsPerson(source_p))
220 return 0;
221
222 handle_remote_xline(source_p, atoi(parv[1]), parv[2], parv[4]);
223 return 0;
224 }
225
226 static void
227 handle_remote_xline(struct Client *source_p, int temp_time,
228 const char *name, const char *reason)
229 {
230 struct ConfItem *aconf;
231
232 if(!find_shared_conf(source_p->username, source_p->host,
233 source_p->servptr->name,
234 (temp_time > 0) ? SHARED_TXLINE : SHARED_PXLINE))
235 return;
236
237 if(!valid_xline(source_p, name, reason, temp_time))
238 return;
239
240 /* already xlined */
241 if((aconf = find_xline_mask(name)) != NULL)
242 {
243 sendto_one_notice(source_p, ":[%s] already X-Lined by [%s] - %s", name, aconf->name, aconf->passwd);
244 return;
245 }
246
247 apply_xline(source_p, name, reason, temp_time);
248 }
249
250 /* escape_perm_xline()
251 *
252 * inputs - gecos
253 * outputs - escaped gecos (allocated with rb_malloc())
254 * side effects - none
255 */
256 static char *
257 escape_perm_xline(const char *gecos)
258 {
259 char *result;
260 int i, j;
261
262 result = rb_malloc(2 * strlen(gecos) + 1);
263 for (i = 0, j = 0; gecos[i] != '\0'; i++)
264 {
265 result[j++] = gecos[i];
266 if (gecos[i] == '"' && gecos[i + 1] == ',')
267 result[j++] = '\\';
268 }
269 result[j] = '\0';
270 return result;
271 }
272
273 /* valid_xline()
274 *
275 * inputs - client xlining, gecos, reason and temp time
276 * outputs -
277 * side effects - checks the xline for validity, erroring if needed
278 */
279 static int
280 valid_xline(struct Client *source_p, const char *gecos,
281 const char *reason, int temp_time)
282 {
283 if(EmptyString(reason))
284 {
285 sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS),
286 get_id(&me, source_p),
287 get_id(source_p, source_p), "XLINE");
288 return 0;
289 }
290
291 if(temp_time == 0 && strstr(gecos, "\",") != NULL)
292 {
293 sendto_one_notice(source_p,
294 ":Invalid character sequence '\",' in xline, please replace with '\"\\,'");
295 return 0;
296 }
297
298 if(strchr(reason, ':') != NULL)
299 {
300 sendto_one_notice(source_p,
301 ":Invalid character ':' in comment");
302 return 0;
303 }
304
305 if(strchr(reason, '"'))
306 {
307 sendto_one_notice(source_p,
308 ":Invalid character '\"' in comment");
309 return 0;
310 }
311
312 if(!valid_wild_card_simple(gecos))
313 {
314 sendto_one_notice(source_p,
315 ":Please include at least %d non-wildcard "
316 "characters with the xline",
317 ConfigFileEntry.min_nonwildcard_simple);
318 return 0;
319 }
320
321 return 1;
322 }
323
324 void
325 apply_xline(struct Client *source_p, const char *name, const char *reason,
326 int temp_time)
327 {
328 struct ConfItem *aconf;
329
330 aconf = make_conf();
331 aconf->status = CONF_XLINE;
332
333 aconf->name = rb_strdup(name);
334 aconf->passwd = rb_strdup(reason);
335 collapse(aconf->name);
336
337 if(temp_time > 0)
338 {
339 aconf->hold = rb_current_time() + temp_time;
340
341 sendto_realops_snomask(SNO_GENERAL, L_ALL,
342 "%s added temporary %d min. X-Line for [%s] [%s]",
343 get_oper_name(source_p), temp_time / 60,
344 aconf->name, reason);
345 ilog(L_KLINE, "X %s %d %s %s",
346 get_oper_name(source_p), temp_time / 60,
347 name, reason);
348 sendto_one_notice(source_p, ":Added temporary %d min. X-Line [%s]",
349 temp_time / 60, aconf->name);
350 }
351 else
352 {
353 sendto_realops_snomask(SNO_GENERAL, L_ALL, "%s added X-Line for [%s] [%s]",
354 get_oper_name(source_p),
355 aconf->name, aconf->passwd);
356 sendto_one_notice(source_p, ":Added X-Line for [%s] [%s]",
357 aconf->name, aconf->passwd);
358 write_xline(source_p, aconf);
359 ilog(L_KLINE, "X %s 0 %s %s",
360 get_oper_name(source_p), name, reason);
361 }
362
363 rb_dlinkAddAlloc(aconf, &xline_conf_list);
364 check_xlines();
365 }
366
367 /* write_xline()
368 *
369 * inputs - gecos, reason, xline type
370 * outputs - writes an xline to the config
371 * side effects -
372 */
373 static void
374 write_xline(struct Client *source_p, struct ConfItem *aconf)
375 {
376 char buffer[BUFSIZE * 2];
377 FILE *out;
378 const char *filename;
379 char *mangle_gecos;
380
381 if(strstr(aconf->name, "\\s"))
382 {
383 char *tmp = LOCAL_COPY(aconf->name);
384 char *orig = tmp;
385 char *new = tmp;
386 while(*orig)
387 {
388 if(*orig == '\\' && *(orig + 1) != '\0')
389 {
390 if(*(orig + 1) == 's')
391 {
392 *new++ = ' ';
393 orig += 2;
394 }
395 /* otherwise skip that and the escaped
396 * character after it, so we dont mistake
397 * \\s as \s --fl
398 */
399 else
400 {
401 *new++ = *orig++;
402 *new++ = *orig++;
403 }
404 }
405 else
406 *new++ = *orig++;
407 }
408
409 *new = '\0';
410 mangle_gecos = tmp;
411 } else
412 mangle_gecos = aconf->name;
413
414 filename = ConfigFileEntry.xlinefile;
415
416 if((out = fopen(filename, "a")) == NULL)
417 {
418 sendto_realops_snomask(SNO_GENERAL, L_ALL, "*** Problem opening %s ", filename);
419 sendto_one_notice(source_p, ":*** Problem opening file, xline added temporarily only");
420 return;
421 }
422
423 rb_sprintf(buffer, "\"%s\",\"0\",\"%s\",\"%s\",%ld\n",
424 mangle_gecos, aconf->passwd,
425 get_oper_name(source_p), (long) rb_current_time());
426
427 if(fputs(buffer, out) == -1)
428 {
429 sendto_realops_snomask(SNO_GENERAL, L_ALL, "*** Problem writing to %s", filename);
430 sendto_one_notice(source_p, ":*** Problem writing to file, xline added temporarily only");
431 fclose(out);
432 return;
433 }
434
435 if(fclose(out))
436 {
437 sendto_realops_snomask(SNO_GENERAL, L_ALL, "*** Problem writing to %s", filename);
438 sendto_one_notice(source_p, ":*** Problem writing to file, xline added temporarily only");
439 return;
440 }
441 }
442
443 static void
444 propagate_xline(struct Client *source_p, const char *target,
445 int temp_time, const char *name, const char *type,
446 const char *reason)
447 {
448 if(!temp_time)
449 {
450 sendto_match_servs(source_p, target, CAP_CLUSTER, NOCAPS,
451 "XLINE %s %s %s :%s",
452 target, name, type, reason);
453 sendto_match_servs(source_p, target, CAP_ENCAP, CAP_CLUSTER,
454 "ENCAP %s XLINE %d %s 2 :%s",
455 target, temp_time, name, reason);
456 }
457 else
458 sendto_match_servs(source_p, target, CAP_ENCAP, NOCAPS,
459 "ENCAP %s XLINE %d %s %s :%s",
460 target, temp_time, name, type, reason);
461 }
462
463 static void
464 cluster_xline(struct Client *source_p, int temp_time, const char *name,
465 const char *reason)
466 {
467 struct remote_conf *shared_p;
468 rb_dlink_node *ptr;
469
470 RB_DLINK_FOREACH(ptr, cluster_conf_list.head)
471 {
472 shared_p = ptr->data;
473
474 /* old protocol cant handle temps, and we dont really want
475 * to convert them to perm.. --fl
476 */
477 if(!temp_time)
478 {
479 if(!(shared_p->flags & SHARED_PXLINE))
480 continue;
481
482 sendto_match_servs(source_p, shared_p->server, CAP_CLUSTER, NOCAPS,
483 "XLINE %s %s 2 :%s",
484 shared_p->server, name, reason);
485 sendto_match_servs(source_p, shared_p->server, CAP_ENCAP, CAP_CLUSTER,
486 "ENCAP %s XLINE 0 %s 2 :%s",
487 shared_p->server, name, reason);
488 }
489 else if(shared_p->flags & SHARED_TXLINE)
490 sendto_match_servs(source_p, shared_p->server, CAP_ENCAP, NOCAPS,
491 "ENCAP %s XLINE %d %s 2 :%s",
492 shared_p->server, temp_time, name, reason);
493 }
494 }
495
496 /* mo_unxline()
497 *
498 * parv[1] - thing to unxline
499 */
500 static int
501 mo_unxline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
502 {
503 if(!IsOperXline(source_p))
504 {
505 sendto_one(source_p, form_str(ERR_NOPRIVS),
506 me.name, source_p->name, "xline");
507 return 0;
508 }
509
510 if(parc == 4 && !(irccmp(parv[2], "ON")))
511 {
512 if(!IsOperRemoteBan(source_p))
513 {
514 sendto_one(source_p, form_str(ERR_NOPRIVS),
515 me.name, source_p->name, "remoteban");
516 return 0;
517 }
518
519 propagate_generic(source_p, "UNXLINE", parv[3], CAP_CLUSTER,
520 "%s", parv[1]);
521
522 if(match(parv[3], me.name) == 0)
523 return 0;
524 }
525 else if(rb_dlink_list_length(&cluster_conf_list))
526 cluster_generic(source_p, "UNXLINE", SHARED_UNXLINE, CAP_CLUSTER,
527 "%s", parv[1]);
528
529 remove_xline(source_p, parv[1]);
530
531 return 0;
532 }
533
534 /* ms_unxline()
535 *
536 * handles a remote unxline
537 */
538 static int
539 ms_unxline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
540 {
541 /* source_p parv[1] parv[2]
542 * oper target server gecos
543 */
544 propagate_generic(source_p, "UNXLINE", parv[1], CAP_CLUSTER,
545 "%s", parv[2]);
546
547 if(!match(parv[1], me.name))
548 return 0;
549
550 if(!IsPerson(source_p))
551 return 0;
552
553 handle_remote_unxline(source_p, parv[2]);
554 return 0;
555 }
556
557 static int
558 me_unxline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
559 {
560 /* name */
561 if(!IsPerson(source_p))
562 return 0;
563
564 handle_remote_unxline(source_p, parv[1]);
565 return 0;
566 }
567
568 static void
569 handle_remote_unxline(struct Client *source_p, const char *name)
570 {
571 if(!find_shared_conf(source_p->username, source_p->host,
572 source_p->servptr->name, SHARED_UNXLINE))
573 return;
574
575 remove_xline(source_p, name);
576
577 return;
578 }
579
580 static void
581 remove_xline(struct Client *source_p, const char *name)
582 {
583 struct ConfItem *aconf;
584 rb_dlink_node *ptr;
585 char *encoded;
586
587 encoded = xline_encode_spaces(name);
588
589 RB_DLINK_FOREACH(ptr, xline_conf_list.head)
590 {
591 aconf = ptr->data;
592
593 if(!irccmp(aconf->name, encoded))
594 {
595 if (!aconf->hold)
596 {
597 if (!remove_xline_from_file(source_p, encoded))
598 return;
599 }
600 else
601 {
602 sendto_one_notice(source_p,
603 ":X-Line for [%s] is removed",
604 encoded);
605 sendto_realops_snomask(SNO_GENERAL, L_ALL,
606 "%s has removed the temporary X-Line for: [%s]",
607 get_oper_name(source_p), encoded);
608 ilog(L_KLINE, "UX %s %s",
609 get_oper_name(source_p), encoded);
610 }
611
612 remove_reject_mask(aconf->name, NULL);
613 free_conf(aconf);
614 rb_dlinkDestroy(ptr, &xline_conf_list);
615 rb_free(encoded);
616 return;
617 }
618 }
619
620 sendto_one_notice(source_p, ":No X-Line for %s", encoded);
621 rb_free(encoded);
622
623 return;
624 }
625
626 /* remove_xline_from_file()
627 *
628 * inputs - gecos to remove
629 * outputs -
630 * side effects - removes xline from conf, if exists
631 * - does not touch xline_conf_list
632 */
633 static int
634 remove_xline_from_file(struct Client *source_p, const char *huntgecos)
635 {
636 FILE *in, *out;
637 char buf[BUFSIZE];
638 char buff[BUFSIZE];
639 char temppath[BUFSIZE];
640 const char *filename;
641 const char *gecos;
642 mode_t oldumask;
643 char *p;
644 char *encoded;
645 int error_on_write = 0;
646 int found_xline = 0;
647
648 filename = ConfigFileEntry.xlinefile;
649 rb_snprintf(temppath, sizeof(temppath),
650 "%s.tmp", ConfigFileEntry.xlinefile);
651
652 if((in = fopen(filename, "r")) == NULL)
653 {
654 sendto_one_notice(source_p, ":Cannot open %s", filename);
655 return 0;
656 }
657
658 oldumask = umask(0);
659
660 if((out = fopen(temppath, "w")) == NULL)
661 {
662 sendto_one_notice(source_p, ":Cannot open %s", temppath);
663 fclose(in);
664 umask(oldumask);
665 return 0;
666 }
667
668 umask(oldumask);
669
670 while (fgets(buf, sizeof(buf), in))
671 {
672 if(error_on_write)
673 {
674 if(temppath != NULL)
675 (void) unlink(temppath);
676
677 break;
678 }
679
680 rb_strlcpy(buff, buf, sizeof(buff));
681
682 if((p = strchr(buff, '\n')) != NULL)
683 *p = '\0';
684
685 if((*buff == '\0') || (*buff == '#'))
686 {
687 error_on_write = (fputs(buf, out) < 0) ? YES : NO;
688 continue;
689 }
690
691 if((gecos = getfield(buff)) == NULL)
692 {
693 error_on_write = (fputs(buf, out) < 0) ? YES : NO;
694 continue;
695 }
696
697 /* matching.. */
698 encoded = xline_encode_spaces(gecos);
699 if(irccmp(encoded, huntgecos) == 0)
700 found_xline++;
701 else
702 error_on_write = (fputs(buf, out) < 0) ? YES : NO;
703 rb_free(encoded);
704 }
705
706 fclose(in);
707 if (fclose(out))
708 error_on_write = YES;
709
710 if(error_on_write)
711 {
712 sendto_one_notice(source_p,
713 ":Couldn't write temp xline file, aborted");
714 return 0;
715 }
716 else if(found_xline == 0)
717 {
718 sendto_one_notice(source_p, ":Cannot find X-Line for %s in file", huntgecos);
719
720 if(temppath != NULL)
721 (void) unlink(temppath);
722 return 0;
723 }
724
725 if (rename(temppath, filename))
726 {
727 sendto_one_notice(source_p, ":Couldn't rename temp file, aborted");
728 return 0;
729 }
730
731 sendto_one_notice(source_p, ":X-Line for [%s] is removed", huntgecos);
732 sendto_realops_snomask(SNO_GENERAL, L_ALL,
733 "%s has removed the X-Line for: [%s]",
734 get_oper_name(source_p), huntgecos);
735 ilog(L_KLINE, "UX %s %s", get_oper_name(source_p), huntgecos);
736
737 return 1;
738 }