]>
jfr.im git - irc/quakenet/newserv.git/blob - channel/channelbans.c
8 #include "../nick/nick.h"
9 #include "../lib/irc_string.h"
10 #include "../irc/irc_config.h"
11 #include "../irc/irc.h"
15 * Converts the specified ban into a ban structure
18 chanban
*makeban(const char *mask
) {
20 int foundat
=-1,foundbang
=-1;
34 /* Let's catch a silly case first */
35 if (!strcmp(mask
,"*")) {
36 cbp
->flags
=CHANBAN_HOSTANY
| CHANBAN_USERANY
| CHANBAN_NICKANY
;
40 cbp
->timeset
=time(NULL
);
44 for (i
=(len
-1);i
>=0;i
--) {
46 /* Got @ sign: everything after here is host */
47 if ((len
-i
)-1 > HOSTLEN
) {
48 /* This is too long, we need to truncate it */
49 cbp
->host
=getsstring(&mask
[len
-HOSTLEN
],HOSTLEN
);
50 cbp
->host
->content
[0]='*';
51 cbp
->flags
|= CHANBAN_HOSTMASK
;
52 } else if (i
==(len
-1)) {
53 /* Ban ending with @, just mark it invalid */
54 /* Note that "moo@*" overrides "moo@", so mark it as having a host too */
55 cbp
->flags
|= (CHANBAN_INVALID
| CHANBAN_HOSTNULL
);
57 } else if (i
==(len
-2) && mask
[i
+1]=='*') {
58 /* Special case: "@*" */
59 cbp
->flags
|= CHANBAN_HOSTANY
;
61 } else if (foundslash
) {
62 /* If we found a slash (/), this can only be a CIDR ban */
63 /* However, it might be broken, so we need to retain the exact string
64 * to track it accurately */
65 cbp
->host
=getsstring(&mask
[i
+1],HOSTLEN
);
66 if ((notip
|| dotcount
!=3) && !foundwild
) {
67 cbp
->flags
|= (CHANBAN_INVALID
| CHANBAN_HOSTEXACT
);
70 cbp
->flags
|= (CHANBAN_INVALID
| CHANBAN_HOSTMASK
);
72 cbp
->flags
|= (CHANBAN_HOSTEXACT
| CHANBAN_CIDR
);
74 /* We have some string with between 1 and HOSTLEN characters.. */
75 cbp
->host
=getsstring(&mask
[i
+1],HOSTLEN
);
77 /* We check all characters after the last wildcard (if any).. if they match
78 * the corresponding bits of the hidden host string we mark it accordingly */
79 if (!(checklen
=len
-foundwild
-1)) { /* Ban ends in *, mark it anyway.. */
80 cbp
->flags
|= CHANBAN_HIDDENHOST
;
82 if (checklen
>=strlen(HIS_HIDDENHOST
)) {
83 if (!ircd_strcmp(cbp
->host
->content
+(cbp
->host
->length
-strlen(HIS_HIDDENHOST
)), HIS_HIDDENHOST
))
84 cbp
->flags
|= CHANBAN_HIDDENHOST
;
86 if (!ircd_strcmp(cbp
->host
->content
+(cbp
->host
->length
-checklen
),
87 (HIS_HIDDENHOST
)+strlen(HIS_HIDDENHOST
)-checklen
))
88 cbp
->flags
|= CHANBAN_HIDDENHOST
;
91 cbp
->flags
|= CHANBAN_HOSTMASK
;
92 if (!notip
&& dotcount
<=3)
93 cbp
->flags
|= CHANBAN_IP
;
95 /* Exact host: see if it ends with the "hidden host" string */
96 cbp
->flags
|= CHANBAN_HOSTEXACT
;
97 if ((cbp
->host
->length
> (strlen(HIS_HIDDENHOST
)+1)) &&
98 !ircd_strcmp(cbp
->host
->content
+(cbp
->host
->length
-strlen(HIS_HIDDENHOST
)), HIS_HIDDENHOST
))
99 cbp
->flags
|= CHANBAN_HIDDENHOST
;
100 else if (!notip
&& dotcount
==3)
101 cbp
->flags
|= CHANBAN_IP
;
106 } else if (mask
[i
]=='/') {
108 } else if (mask
[i
]=='.') {
110 } else if (mask
[i
]=='?' || mask
[i
]=='*') {
111 if (!foundwild
) /* Mark last wildcard in string */
113 } else if (mask
[i
]<'0' || mask
[i
]>'9') {
119 /* If there wasn't an @, this ban matches any host */
121 cbp
->flags
|= CHANBAN_HOSTANY
;
126 for (i
=0;i
<foundat
;i
++) {
129 /* Invalid mask: nick is empty */
130 cbp
->flags
|= CHANBAN_NICKNULL
;
132 } else if (i
==1 && mask
[0]=='*') {
133 /* matches any nick */
134 cbp
->flags
|= CHANBAN_NICKANY
;
138 /* too long: just take the first NICKLEN chars */
139 cbp
->nick
=getsstring(mask
,NICKLEN
);
141 cbp
->nick
=getsstring(mask
,i
);
144 cbp
->flags
|= CHANBAN_NICKMASK
;
146 cbp
->flags
|= CHANBAN_NICKEXACT
;
150 } else if (mask
[i
]=='?' || mask
[i
]=='*') {
158 /* We didn't find a !, what we do now depends on what happened
161 /* A ban with no ! or @ is treated as a nick ban only */
162 /* Note that we've special-cased "*" at the top, so we can only
163 * hit the MASK or EXACT case here. */
165 cbp
->nick
=getsstring(mask
,NICKLEN
);
167 cbp
->nick
=getsstring(mask
,len
);
170 cbp
->flags
|= CHANBAN_NICKMASK
;
172 cbp
->flags
|= CHANBAN_NICKEXACT
;
174 cbp
->flags
|= (CHANBAN_USERANY
| CHANBAN_HOSTANY
);
178 /* A ban with @ only is treated as user@host */
180 cbp
->flags
|= CHANBAN_NICKANY
;
185 /* We found an @, so everything between foundbang+1 and foundat-1 is supposed to be ident */
186 /* This is true even if there was no !.. */
187 if (foundat
==(foundbang
+1)) {
188 /* empty ident matches nothing */
189 cbp
->flags
|= (CHANBAN_INVALID
| CHANBAN_USERNULL
);
191 } else if (foundat
- foundbang
- 1 > USERLEN
) {
192 /* It's too long.. */
193 cbp
->user
=getsstring(&mask
[foundat
-USERLEN
],USERLEN
);
194 cbp
->user
->content
[0]='*';
195 cbp
->flags
|= CHANBAN_USERMASK
;
196 } else if ((foundat
- foundbang
- 1 == 1) && mask
[foundbang
+1]=='*') {
198 cbp
->flags
|= CHANBAN_USERANY
;
200 cbp
->user
=getsstring(&mask
[foundbang
+1],(foundat
-foundbang
-1));
201 if (strchr(cbp
->user
->content
,'*') || strchr(cbp
->user
->content
,'?'))
202 cbp
->flags
|= CHANBAN_USERMASK
;
204 cbp
->flags
|= CHANBAN_USEREXACT
;
208 assert(cbp
->flags
& (CHANBAN_USEREXACT
| CHANBAN_USERMASK
| CHANBAN_USERANY
| CHANBAN_USERNULL
));
209 assert(cbp
->flags
& (CHANBAN_NICKEXACT
| CHANBAN_NICKMASK
| CHANBAN_NICKANY
| CHANBAN_NICKNULL
));
210 assert(cbp
->flags
& (CHANBAN_HOSTEXACT
| CHANBAN_HOSTMASK
| CHANBAN_HOSTANY
| CHANBAN_HOSTNULL
));
212 cbp
->timeset
=time(NULL
);
218 * Returns nonzero iff bana is a SUPERSET of banb
221 int banoverlap(const chanban
*bana
, const chanban
*banb
) {
222 /* This function works by looking for cases where bana DOESN'T overlap banb */
225 /* If bana has CHANBAN_NICKANY then it clearly overlaps
226 * in the nick section so there are no checks */
228 if ((bana
->flags
& CHANBAN_NICKNULL
) && !(banb
->flags
& CHANBAN_NICKNULL
)) {
232 if (bana
->flags
& CHANBAN_NICKMASK
) {
233 if (banb
->flags
& (CHANBAN_NICKANY
| CHANBAN_NICKNULL
)) {
236 if ((banb
->flags
& CHANBAN_NICKMASK
) && !match2patterns(bana
->nick
->content
, banb
->nick
->content
)) {
239 if ((banb
->flags
& CHANBAN_NICKEXACT
) && !match2strings(bana
->nick
->content
, banb
->nick
->content
)) {
244 if (bana
->flags
& CHANBAN_NICKEXACT
) {
245 if (banb
->flags
& (CHANBAN_NICKANY
| CHANBAN_NICKMASK
| CHANBAN_NICKNULL
)) {
248 if ((!bana
->nick
&& banb
->nick
) || (bana
->nick
&& !banb
->nick
)) {
251 if (bana
->nick
&& ircd_strcmp(bana
->nick
->content
,banb
->nick
->content
)) {
257 /* If bana has CHANBAN_USERANY then it clearly overlaps
258 * in the user section so there are no checks */
260 if ((bana
->flags
& CHANBAN_USERNULL
) && !(banb
->flags
& CHANBAN_USERNULL
)) {
264 if (bana
->flags
& CHANBAN_USERMASK
) {
265 if (banb
->flags
& (CHANBAN_USERANY
| CHANBAN_USERNULL
)) {
268 if ((banb
->flags
& CHANBAN_USERMASK
) && !match2patterns(bana
->user
->content
, banb
->user
->content
)) {
271 if ((banb
->flags
& CHANBAN_USEREXACT
) && !match2strings(bana
->user
->content
, banb
->user
->content
)) {
276 if (bana
->flags
& CHANBAN_USEREXACT
) {
277 if (banb
->flags
& (CHANBAN_USERANY
| CHANBAN_USERMASK
| CHANBAN_USERNULL
)) {
280 if ((!bana
->user
&& banb
->user
) || (bana
->user
&& !banb
->user
)) {
283 if (bana
->user
&& ircd_strcmp(bana
->user
->content
,banb
->user
->content
)) {
289 /* If bana has CHANBAN_HOSTANY then it clearly overlaps
290 * in the host section so there are no checks */
292 if ((bana
->flags
& CHANBAN_HOSTNULL
) && !(banb
->flags
& CHANBAN_HOSTNULL
)) {
296 if (bana
->flags
& CHANBAN_HOSTMASK
) {
297 if (banb
->flags
& (CHANBAN_HOSTANY
| CHANBAN_HOSTNULL
)) {
300 if ((banb
->flags
& CHANBAN_HOSTMASK
) && !match2patterns(bana
->host
->content
, banb
->host
->content
)) {
303 if ((banb
->flags
& CHANBAN_HOSTEXACT
) && !match2strings(bana
->host
->content
, banb
->host
->content
)) {
308 if (bana
->flags
& CHANBAN_HOSTEXACT
) {
309 if (banb
->flags
& (CHANBAN_HOSTANY
| CHANBAN_HOSTMASK
| CHANBAN_HOSTNULL
)) {
312 if ((!bana
->host
&& banb
->host
) || (bana
->host
&& !banb
->host
)) {
315 if (bana
->host
&& ircd_strcmp(bana
->host
->content
,banb
->host
->content
)) {
325 * Returns nonzero iff the bans are EXACTLY the same
328 int banequal(chanban
*bana
, chanban
*banb
) {
329 if (bana
->flags
!= banb
->flags
)
332 if ((!bana
->nick
&& banb
->nick
) || (bana
->nick
&& !banb
->nick
))
335 if (bana
->nick
&& ircd_strcmp(bana
->nick
->content
,banb
->nick
->content
))
338 if ((!bana
->user
&& banb
->user
) || (bana
->user
&& !banb
->user
))
341 if (bana
->user
&& ircd_strcmp(bana
->user
->content
,banb
->user
->content
))
344 if ((!bana
->host
&& banb
->host
) || (bana
->host
&& !banb
->host
))
347 if (bana
->host
&& ircd_strcmp(bana
->host
->content
,banb
->host
->content
))
355 * Convert the specified ban to a string
358 char *bantostring(chanban
*bp
) {
359 static char outstring
[NICKLEN
+USERLEN
+HOSTLEN
+5];
362 if (bp
->flags
& CHANBAN_NICKANY
) {
363 strpos
+= sprintf(outstring
+strpos
,"*");
364 } else if (bp
->nick
) {
365 strpos
+= sprintf(outstring
+strpos
,"%s",bp
->nick
->content
);
368 strpos
+= sprintf(outstring
+strpos
,"!");
370 if (bp
->flags
& CHANBAN_USERANY
) {
371 strpos
+= sprintf(outstring
+strpos
,"*");
372 } else if (bp
->user
) {
373 strpos
+= sprintf(outstring
+strpos
,"%s",bp
->user
->content
);
376 strpos
+= sprintf(outstring
+strpos
,"@");
378 if (bp
->flags
& CHANBAN_HOSTANY
) {
379 strpos
+= sprintf(outstring
+strpos
,"*");
380 } else if (bp
->host
) {
381 strpos
+= sprintf(outstring
+strpos
,"%s",bp
->host
->content
);
389 * Convert the specified ban to a string (debugging version)
392 char *bantostringdebug(chanban
*bp
) {
393 static char outstring
[NICKLEN
+USERLEN
+HOSTLEN
+5];
396 strpos
+= sprintf(outstring
+strpos
, "flags=%04x ",bp
->flags
);
399 strpos
+= sprintf(outstring
+strpos
, "nick=%s ",bp
->nick
->content
);
401 strpos
+= sprintf(outstring
+strpos
, "nonick ");
405 strpos
+= sprintf(outstring
+strpos
, "user=%s ",bp
->user
->content
);
407 strpos
+= sprintf(outstring
+strpos
, "nouser ");
411 strpos
+= sprintf(outstring
+strpos
, "host=%s ",bp
->host
->content
);
413 strpos
+= sprintf(outstring
+strpos
, "nohost ");
422 * Returns true iff the supplied nick* matches the supplied ban*
425 int nickmatchban(nick
*np
, chanban
*bp
) {
426 const char *ipstring
;
427 char fakehost
[HOSTLEN
+1];
429 /* nick/ident section: return 0 (no match) if they don't match */
431 if (bp
->flags
& CHANBAN_INVALID
)
434 if (bp
->flags
& CHANBAN_USEREXACT
&&
435 ircd_strcmp(np
->ident
,bp
->user
->content
) &&
436 (!IsSetHost(np
) || !np
->shident
||
437 ircd_strcmp(np
->shident
->content
,bp
->user
->content
)))
440 if (bp
->flags
& CHANBAN_NICKEXACT
&& ircd_strcmp(np
->nick
,bp
->nick
->content
))
443 if (bp
->flags
& CHANBAN_USERMASK
&&
444 !match2strings(bp
->user
->content
,np
->ident
) &&
445 (!IsSetHost(np
) || !np
->shident
||
446 !match2strings(bp
->user
->content
, np
->shident
->content
)))
449 if (bp
->flags
& CHANBAN_NICKMASK
&& !match2strings(bp
->nick
->content
,np
->nick
))
452 /* host section. Return 1 (match) if they do match
453 * Note that if user or ident was specified, they've already been checked
456 if (bp
->flags
& CHANBAN_HOSTANY
)
459 if (bp
->flags
& CHANBAN_IP
) {
460 /* IP bans are checked against IP address only */
461 ipstring
=IPtostr(np
->ipaddress
);
463 if (bp
->flags
& CHANBAN_HOSTEXACT
&& !ircd_strcmp(ipstring
,bp
->host
->content
))
466 if (bp
->flags
& CHANBAN_HOSTMASK
&& match2strings(bp
->host
->content
,ipstring
))
469 /* Hostname bans need to be checked against +x host, +h host (if set)
470 * and actual host. Note that the +x host is only generated (and checked) if it's
471 * possible for the ban to match a hidden host.. */
473 if ((bp
->flags
& CHANBAN_HIDDENHOST
) && IsAccount(np
)) {
474 sprintf(fakehost
,"%s.%s",np
->authname
, HIS_HIDDENHOST
);
476 if ((bp
->flags
& CHANBAN_HOSTEXACT
) &&
477 !ircd_strcmp(fakehost
, bp
->host
->content
))
480 if ((bp
->flags
& CHANBAN_HOSTMASK
) &&
481 match2strings(bp
->host
->content
, fakehost
))
486 if ((bp
->flags
& CHANBAN_HOSTEXACT
) &&
487 !ircd_strcmp(np
->sethost
->content
, bp
->host
->content
))
490 if ((bp
->flags
& CHANBAN_HOSTMASK
) &&
491 match2strings(bp
->host
->content
, np
->sethost
->content
))
495 if (bp
->flags
& CHANBAN_HOSTEXACT
&& !ircd_strcmp(np
->host
->name
->content
,bp
->host
->content
))
498 if (bp
->flags
& CHANBAN_HOSTMASK
&& match2strings(bp
->host
->content
,np
->host
->name
->content
))
507 * Returns true iff the supplied nick* is banned on the supplied chan*
510 int nickbanned(nick
*np
, channel
*cp
) {
513 for (cbp
=cp
->bans
;cbp
;cbp
=cbp
->next
) {
514 if (nickmatchban(np
,cbp
))
523 * Set the specified ban; if it completely encloses any existing bans
527 void setban(channel
*cp
, const char *ban
) {
528 chanban
**cbh
,*cbp
,*cbp2
;
532 /* Remove enclosed bans first */
533 for (cbh
=&(cp
->bans
);*cbh
;) {
534 if (banoverlap(cbp
,*cbh
)) {
538 /* Break out of the loop if we just deleted the last ban */
543 cbh
=(chanban
**)&((*cbh
)->next
);
547 /* Now set the new ban */
548 cbp
->next
=(struct chanban
*)cp
->bans
;
554 * Remove the specified ban iff an exact match is found
555 * Returns 1 if the ban was cleared, 0 if the ban didn't exist.
556 * If "optional" is 0 and the ban didn't exist, flags an error
559 int clearban(channel
*cp
, const char *ban
, int optional
) {
560 chanban
**cbh
,*cbp
,*cbp2
;
569 for (cbh
=&(cp
->bans
);*cbh
;cbh
=(chanban
**)&((*cbh
)->next
)) {
570 if (banequal(cbp
,*cbh
)) {
579 if (!found
&& !optional
) {
580 Error("channel",ERR_DEBUG
,"Couldn't remove ban %s from %s. Dumping banlist:",ban
,cp
->index
->name
->content
);
581 for (cbp2
=cp
->bans
;cbp2
;cbp2
=cbp2
->next
) {
582 Error("channel",ERR_DEBUG
,"%s %d %s",cp
->index
->name
->content
, i
++, bantostringdebug(cbp2
));
593 * Just free all the bans on the channel
596 void clearallbans(channel
*cp
) {
599 for (cbp
=cp
->bans
;cbp
;cbp
=ncbp
) {
600 ncbp
=(chanban
*)cbp
->next
;