3 * Copyright (C) 2002-2003 Lee Hardy <lee@leeh.co.uk>
4 * Copyright (C) 2002-2005 ircd-ratbox development team
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
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.
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.
30 * $Id: m_xline.c 3161 2007-01-25 07:23:01Z nenolod $
51 #include "s_newconf.h"
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
[]);
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}}
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}}
70 mapi_clist_av1 xline_clist
[] = { &xline_msgtab
, &unxline_msgtab
, NULL
};
71 DECLARE_MODULE_AV1(xline
, NULL
, NULL
, xline_clist
, NULL
, NULL
, "$Revision: 3161 $");
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
);
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
);
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
);
94 * parv[1] - thing to xline
95 * parv[2] - optional type/reason
99 mo_xline(struct Client
*client_p
, struct Client
*source_p
, int parc
, const char *parv
[])
101 struct ConfItem
*aconf
;
105 const char *target_server
= NULL
;
109 if(!IsOperXline(source_p
))
111 sendto_one(source_p
, form_str(ERR_NOPRIVS
),
112 me
.name
, source_p
->name
, "xline");
116 if((temp_time
= valid_temp_time(parv
[loc
])) >= 0)
118 /* we just set temp_time to -1! */
125 /* XLINE <gecos> ON <server> :<reason> */
126 if(parc
>= loc
+2 && !irccmp(parv
[loc
], "ON"))
128 if(!IsOperRemoteBan(source_p
))
130 sendto_one(source_p
, form_str(ERR_NOPRIVS
),
131 me
.name
, source_p
->name
, "remoteban");
135 target_server
= parv
[loc
+1];
139 if(parc
<= loc
|| EmptyString(parv
[loc
]))
141 sendto_one(source_p
, form_str(ERR_NEEDMOREPARAMS
),
142 me
.name
, source_p
->name
, "XLINE");
150 escapedname
= escape_perm_xline(name
);
151 if (strcmp(escapedname
, name
))
152 sendto_one_notice(source_p
, ":Changed xline from [%s] to [%s]",
156 escapedname
= rb_strdup(name
);
158 if(target_server
!= NULL
)
160 propagate_xline(source_p
, target_server
, temp_time
,
161 escapedname
, "2", reason
);
163 if(!match(target_server
, me
.name
))
165 rb_free(escapedname
);
169 else if(rb_dlink_list_length(&cluster_conf_list
) > 0)
170 cluster_xline(source_p
, temp_time
, escapedname
, reason
);
172 if((aconf
= find_xline_mask(escapedname
)) != NULL
)
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
);
180 if(!valid_xline(source_p
, escapedname
, reason
, temp_time
))
182 rb_free(escapedname
);
186 apply_xline(source_p
, escapedname
, reason
, temp_time
);
187 rb_free(escapedname
);
194 * handles a remote xline
197 ms_xline(struct Client
*client_p
, struct Client
*source_p
, int parc
, const char *parv
[])
199 /* source_p parv[1] parv[2] parv[3] parv[4]
200 * oper target serv xline type reason
202 propagate_xline(source_p
, parv
[1], 0, parv
[2], parv
[3], parv
[4]);
204 if(!IsPerson(source_p
))
207 /* destined for me? */
208 if(!match(parv
[1], me
.name
))
211 handle_remote_xline(source_p
, 0, parv
[2], parv
[4]);
216 me_xline(struct Client
*client_p
, struct Client
*source_p
, int parc
, const char *parv
[])
218 /* time name type :reason */
219 if(!IsPerson(source_p
))
222 handle_remote_xline(source_p
, atoi(parv
[1]), parv
[2], parv
[4]);
227 handle_remote_xline(struct Client
*source_p
, int temp_time
,
228 const char *name
, const char *reason
)
230 struct ConfItem
*aconf
;
232 if(!find_shared_conf(source_p
->username
, source_p
->host
,
233 source_p
->servptr
->name
,
234 (temp_time
> 0) ? SHARED_TXLINE
: SHARED_PXLINE
))
237 if(!valid_xline(source_p
, name
, reason
, temp_time
))
241 if((aconf
= find_xline_mask(name
)) != NULL
)
243 sendto_one_notice(source_p
, ":[%s] already X-Lined by [%s] - %s", name
, aconf
->name
, aconf
->passwd
);
247 apply_xline(source_p
, name
, reason
, temp_time
);
250 /* escape_perm_xline()
253 * outputs - escaped gecos (allocated with rb_malloc())
254 * side effects - none
257 escape_perm_xline(const char *gecos
)
262 result
= rb_malloc(2 * strlen(gecos
) + 1);
263 for (i
= 0, j
= 0; gecos
[i
] != '\0'; i
++)
265 result
[j
++] = gecos
[i
];
266 if (gecos
[i
] == '"' && gecos
[i
+ 1] == ',')
275 * inputs - client xlining, gecos, reason and temp time
277 * side effects - checks the xline for validity, erroring if needed
280 valid_xline(struct Client
*source_p
, const char *gecos
,
281 const char *reason
, int temp_time
)
283 if(EmptyString(reason
))
285 sendto_one(source_p
, form_str(ERR_NEEDMOREPARAMS
),
286 get_id(&me
, source_p
),
287 get_id(source_p
, source_p
), "XLINE");
291 if(temp_time
== 0 && strstr(gecos
, "\",") != NULL
)
293 sendto_one_notice(source_p
,
294 ":Invalid character sequence '\",' in xline, please replace with '\"\\,'");
298 if(strchr(reason
, ':') != NULL
)
300 sendto_one_notice(source_p
,
301 ":Invalid character ':' in comment");
305 if(strchr(reason
, '"'))
307 sendto_one_notice(source_p
,
308 ":Invalid character '\"' in comment");
312 if(!valid_wild_card_simple(gecos
))
314 sendto_one_notice(source_p
,
315 ":Please include at least %d non-wildcard "
316 "characters with the xline",
317 ConfigFileEntry
.min_nonwildcard_simple
);
325 apply_xline(struct Client
*source_p
, const char *name
, const char *reason
,
328 struct ConfItem
*aconf
;
331 aconf
->status
= CONF_XLINE
;
333 aconf
->name
= rb_strdup(name
);
334 aconf
->passwd
= rb_strdup(reason
);
335 collapse(aconf
->name
);
339 aconf
->hold
= rb_current_time() + temp_time
;
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,
348 sendto_one_notice(source_p
, ":Added temporary %d min. X-Line [%s]",
349 temp_time
/ 60, aconf
->name
);
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
);
363 rb_dlinkAddAlloc(aconf
, &xline_conf_list
);
369 * inputs - gecos, reason, xline type
370 * outputs - writes an xline to the config
374 write_xline(struct Client
*source_p
, struct ConfItem
*aconf
)
376 char buffer
[BUFSIZE
* 2];
378 const char *filename
;
381 if(strstr(aconf
->name
, "\\s"))
383 char *tmp
= LOCAL_COPY(aconf
->name
);
388 if(*orig
== '\\' && *(orig
+ 1) != '\0')
390 if(*(orig
+ 1) == 's')
395 /* otherwise skip that and the escaped
396 * character after it, so we dont mistake
412 mangle_gecos
= aconf
->name
;
414 filename
= ConfigFileEntry
.xlinefile
;
416 if((out
= fopen(filename
, "a")) == NULL
)
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");
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());
427 if(fputs(buffer
, out
) == -1)
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");
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");
444 propagate_xline(struct Client
*source_p
, const char *target
,
445 int temp_time
, const char *name
, const char *type
,
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
);
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
);
464 cluster_xline(struct Client
*source_p
, int temp_time
, const char *name
,
467 struct remote_conf
*shared_p
;
470 RB_DLINK_FOREACH(ptr
, cluster_conf_list
.head
)
472 shared_p
= ptr
->data
;
474 /* old protocol cant handle temps, and we dont really want
475 * to convert them to perm.. --fl
479 if(!(shared_p
->flags
& SHARED_PXLINE
))
482 sendto_match_servs(source_p
, shared_p
->server
, CAP_CLUSTER
, NOCAPS
,
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
);
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
);
498 * parv[1] - thing to unxline
501 mo_unxline(struct Client
*client_p
, struct Client
*source_p
, int parc
, const char *parv
[])
503 if(!IsOperXline(source_p
))
505 sendto_one(source_p
, form_str(ERR_NOPRIVS
),
506 me
.name
, source_p
->name
, "xline");
510 if(parc
== 4 && !(irccmp(parv
[2], "ON")))
512 if(!IsOperRemoteBan(source_p
))
514 sendto_one(source_p
, form_str(ERR_NOPRIVS
),
515 me
.name
, source_p
->name
, "remoteban");
519 propagate_generic(source_p
, "UNXLINE", parv
[3], CAP_CLUSTER
,
522 if(match(parv
[3], me
.name
) == 0)
525 else if(rb_dlink_list_length(&cluster_conf_list
))
526 cluster_generic(source_p
, "UNXLINE", SHARED_UNXLINE
, CAP_CLUSTER
,
529 remove_xline(source_p
, parv
[1]);
536 * handles a remote unxline
539 ms_unxline(struct Client
*client_p
, struct Client
*source_p
, int parc
, const char *parv
[])
541 /* source_p parv[1] parv[2]
542 * oper target server gecos
544 propagate_generic(source_p
, "UNXLINE", parv
[1], CAP_CLUSTER
,
547 if(!match(parv
[1], me
.name
))
550 if(!IsPerson(source_p
))
553 handle_remote_unxline(source_p
, parv
[2]);
558 me_unxline(struct Client
*client_p
, struct Client
*source_p
, int parc
, const char *parv
[])
561 if(!IsPerson(source_p
))
564 handle_remote_unxline(source_p
, parv
[1]);
569 handle_remote_unxline(struct Client
*source_p
, const char *name
)
571 if(!find_shared_conf(source_p
->username
, source_p
->host
,
572 source_p
->servptr
->name
, SHARED_UNXLINE
))
575 remove_xline(source_p
, name
);
581 remove_xline(struct Client
*source_p
, const char *name
)
583 struct ConfItem
*aconf
;
587 encoded
= xline_encode_spaces(name
);
589 RB_DLINK_FOREACH(ptr
, xline_conf_list
.head
)
593 if(!irccmp(aconf
->name
, encoded
))
597 if (!remove_xline_from_file(source_p
, encoded
))
602 sendto_one_notice(source_p
,
603 ":X-Line for [%s] is removed",
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
);
612 remove_reject_mask(aconf
->name
, NULL
);
614 rb_dlinkDestroy(ptr
, &xline_conf_list
);
620 sendto_one_notice(source_p
, ":No X-Line for %s", encoded
);
626 /* remove_xline_from_file()
628 * inputs - gecos to remove
630 * side effects - removes xline from conf, if exists
631 * - does not touch xline_conf_list
634 remove_xline_from_file(struct Client
*source_p
, const char *huntgecos
)
639 char temppath
[BUFSIZE
];
640 const char *filename
;
645 int error_on_write
= 0;
648 filename
= ConfigFileEntry
.xlinefile
;
649 rb_snprintf(temppath
, sizeof(temppath
),
650 "%s.tmp", ConfigFileEntry
.xlinefile
);
652 if((in
= fopen(filename
, "r")) == NULL
)
654 sendto_one_notice(source_p
, ":Cannot open %s", filename
);
660 if((out
= fopen(temppath
, "w")) == NULL
)
662 sendto_one_notice(source_p
, ":Cannot open %s", temppath
);
670 while (fgets(buf
, sizeof(buf
), in
))
675 (void) unlink(temppath
);
680 rb_strlcpy(buff
, buf
, sizeof(buff
));
682 if((p
= strchr(buff
, '\n')) != NULL
)
685 if((*buff
== '\0') || (*buff
== '#'))
687 error_on_write
= (fputs(buf
, out
) < 0) ? YES
: NO
;
691 if((gecos
= getfield(buff
)) == NULL
)
693 error_on_write
= (fputs(buf
, out
) < 0) ? YES
: NO
;
698 encoded
= xline_encode_spaces(gecos
);
699 if(irccmp(encoded
, huntgecos
) == 0)
702 error_on_write
= (fputs(buf
, out
) < 0) ? YES
: NO
;
708 error_on_write
= YES
;
712 sendto_one_notice(source_p
,
713 ":Couldn't write temp xline file, aborted");
716 else if(found_xline
== 0)
718 sendto_one_notice(source_p
, ":Cannot find X-Line for %s in file", huntgecos
);
721 (void) unlink(temppath
);
725 if (rename(temppath
, filename
))
727 sendto_one_notice(source_p
, ":Couldn't rename temp file, aborted");
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
);