]> jfr.im git - irc/quakenet/newserv.git/blob - bans/bans.c
A4STATS: remove E style escapes and switch to createtable for indices
[irc/quakenet/newserv.git] / bans / bans.c
1 /* channelbans.c */
2
3 #include <assert.h>
4 #include <string.h>
5 #include <stdio.h>
6 #include "bans.h"
7
8 #include "../nick/nick.h"
9 #include "../lib/irc_string.h"
10 #include "../irc/irc_config.h"
11 #include "../core/nsmalloc.h"
12 #include "../lib/flags.h"
13 #include "../lib/version.h"
14
15 MODULE_VERSION("")
16
17 void _fini() {
18 nsfreeall(POOL_BANS);
19 }
20
21 chanban *getchanban() {
22 chanban *cbp;
23
24 cbp = nsmalloc(POOL_BANS, sizeof(chanban));
25 if (!cbp)
26 return NULL;
27
28 cbp->nick=NULL;
29 cbp->user=NULL;
30 cbp->host=NULL;
31
32 return cbp;
33 }
34
35 void freechanban(chanban *cbp) {
36 freesstring(cbp->nick);
37 freesstring(cbp->user);
38 freesstring(cbp->host);
39
40 nsfree(POOL_BANS, cbp);
41 }
42
43 /*
44 * makeban:
45 * Converts the specified ban into a ban structure
46 */
47
48 chanban *makeban(const char *mask) {
49 int len;
50 int foundat=-1,foundbang=-1;
51 int foundwild=0;
52 int i;
53 int checklen;
54 chanban *cbp;
55 char tmpbuf[512];
56
57 cbp=getchanban();
58 len=strlen(mask);
59
60 cbp->flags=0;
61
62 /* Let's catch a silly case first */
63 if (!strcmp(mask,"*")) {
64 cbp->flags=CHANBAN_HOSTANY | CHANBAN_USERANY | CHANBAN_NICKANY;
65 cbp->nick=NULL;
66 cbp->user=NULL;
67 cbp->host=NULL;
68 cbp->timeset=time(NULL);
69 return cbp;
70 }
71
72 for (i=(len-1);i>=0;i--) {
73 if (mask[i]=='@') {
74 /* Got @ sign: everything after here is host */
75 if ((len-i)-1 > HOSTLEN) {
76 /* This is too long, we need to truncate it */
77 strncpy(tmpbuf,&mask[len-HOSTLEN],HOSTLEN);
78 tmpbuf[HOSTLEN]='\0';
79 tmpbuf[0]='*';
80 cbp->host=getsstring(tmpbuf,HOSTLEN);
81 cbp->flags |= CHANBAN_HOSTMASK;
82 } else if (i==(len-1)) {
83 /* Ban ending with @, just mark it invalid */
84 /* Note that "moo@*" overrides "moo@", so mark it as having a host too */
85 cbp->flags |= (CHANBAN_INVALID | CHANBAN_HOSTNULL);
86 cbp->host=NULL;
87 } else if (i==(len-2) && mask[i+1]=='*') {
88 /* Special case: "@*" */
89 cbp->flags |= CHANBAN_HOSTANY;
90 cbp->host=NULL;
91 } else {
92 /* We have some string with between 1 and HOSTLEN characters.. */
93 cbp->host=getsstring(&mask[i+1],HOSTLEN);
94
95 /* If it matches an IP address, flag it as such */
96 if (ipmask_parse(cbp->host->content, &(cbp->ipaddr), &(cbp->prefixlen))) {
97 cbp->flags |= CHANBAN_IP;
98 }
99
100 if (foundwild) {
101 /* We check all characters after the last wildcard (if any).. if they match
102 * the corresponding bits of the hidden host string we mark it accordingly */
103 if (!(checklen=len-foundwild-1)) { /* Ban ends in *, mark it anyway.. */
104 cbp->flags |= CHANBAN_HIDDENHOST;
105 } else {
106 if (checklen>=strlen(HIS_HIDDENHOST)) {
107 if (!ircd_strcmp(cbp->host->content+(cbp->host->length-strlen(HIS_HIDDENHOST)), HIS_HIDDENHOST))
108 cbp->flags |= CHANBAN_HIDDENHOST;
109 } else {
110 if (!ircd_strcmp(cbp->host->content+(cbp->host->length-checklen),
111 (HIS_HIDDENHOST)+strlen(HIS_HIDDENHOST)-checklen))
112 cbp->flags |= CHANBAN_HIDDENHOST;
113 }
114 }
115 cbp->flags |= CHANBAN_HOSTMASK;
116 } else {
117 /* Exact host: see if it ends with the "hidden host" string */
118 cbp->flags |= CHANBAN_HOSTEXACT;
119 if ((cbp->host->length > (strlen(HIS_HIDDENHOST)+1)) &&
120 !ircd_strcmp(cbp->host->content+(cbp->host->length-strlen(HIS_HIDDENHOST)), HIS_HIDDENHOST)) {
121 cbp->flags |= CHANBAN_HIDDENHOST;
122 }
123 }
124 }
125 foundat=i;
126 break;
127 } else if (mask[i]=='?' || mask[i]=='*') {
128 if (!foundwild) /* Mark last wildcard in string */
129 foundwild=i;
130 }
131 }
132
133 if (foundat<0) {
134 /* If there wasn't an @, this ban matches any host */
135 cbp->host=NULL;
136 cbp->flags |= CHANBAN_HOSTANY;
137 }
138
139 foundwild=0;
140
141 for (i=0;i<foundat;i++) {
142 if (mask[i]=='!') {
143 if (i==0) {
144 /* Invalid mask: nick is empty */
145 cbp->flags |= CHANBAN_NICKNULL;
146 cbp->nick=NULL;
147 } else if (i==1 && mask[0]=='*') {
148 /* matches any nick */
149 cbp->flags |= CHANBAN_NICKANY;
150 cbp->nick=NULL;
151 } else {
152 if (i>NICKLEN) {
153 /* too long: just take the first NICKLEN chars */
154 cbp->nick=getsstring(mask,NICKLEN);
155 } else {
156 cbp->nick=getsstring(mask,i);
157 }
158 if (foundwild)
159 cbp->flags |= CHANBAN_NICKMASK;
160 else
161 cbp->flags |= CHANBAN_NICKEXACT;
162 }
163 foundbang=i;
164 break;
165 } else if (mask[i]=='?' || mask[i]=='*') {
166 if (i<NICKLEN) {
167 foundwild=1;
168 }
169 }
170 }
171
172 if (foundbang<0) {
173 /* We didn't find a !, what we do now depends on what happened
174 * with the @ */
175 if (foundat<0) {
176 /* A ban with no ! or @ is treated as a nick ban only */
177 /* Note that we've special-cased "*" at the top, so we can only
178 * hit the MASK or EXACT case here. */
179 if (len>NICKLEN)
180 cbp->nick=getsstring(mask,NICKLEN);
181 else
182 cbp->nick=getsstring(mask,len);
183
184 if (foundwild)
185 cbp->flags |= CHANBAN_NICKMASK;
186 else
187 cbp->flags |= CHANBAN_NICKEXACT;
188
189 cbp->flags |= (CHANBAN_USERANY | CHANBAN_HOSTANY);
190 cbp->host=NULL;
191 cbp->user=NULL;
192 } else {
193 /* A ban with @ only is treated as user@host */
194 cbp->nick=NULL;
195 cbp->flags |= CHANBAN_NICKANY;
196 }
197 }
198
199 if (foundat>=0) {
200 /* We found an @, so everything between foundbang+1 and foundat-1 is supposed to be ident */
201 /* This is true even if there was no !.. */
202 if (foundat==(foundbang+1)) {
203 /* empty ident matches nothing */
204 cbp->flags |= (CHANBAN_INVALID | CHANBAN_USERNULL);
205 cbp->user=NULL;
206 } else if (foundat - foundbang - 1 > USERLEN) {
207 /* It's too long.. */
208 strncpy(tmpbuf,&mask[foundat-USERLEN],USERLEN);
209 tmpbuf[USERLEN]='\0';
210 tmpbuf[0]='*';
211 cbp->user=getsstring(tmpbuf,USERLEN);
212 cbp->flags |= CHANBAN_USERMASK;
213 } else if ((foundat - foundbang - 1 == 1) && mask[foundbang+1]=='*') {
214 cbp->user=NULL;
215 cbp->flags |= CHANBAN_USERANY;
216 } else {
217 cbp->user=getsstring(&mask[foundbang+1],(foundat-foundbang-1));
218 if (strchr(cbp->user->content,'*') || strchr(cbp->user->content,'?'))
219 cbp->flags |= CHANBAN_USERMASK;
220 else
221 cbp->flags |= CHANBAN_USEREXACT;
222 }
223 /* Username part can't contain an @ */
224 if (cbp->user && strchr(cbp->user->content,'@')) {
225 cbp->flags |= CHANBAN_INVALID;
226 }
227 }
228
229 assert(cbp->flags & (CHANBAN_USEREXACT | CHANBAN_USERMASK | CHANBAN_USERANY | CHANBAN_USERNULL));
230 assert(cbp->flags & (CHANBAN_NICKEXACT | CHANBAN_NICKMASK | CHANBAN_NICKANY | CHANBAN_NICKNULL));
231 assert(cbp->flags & (CHANBAN_HOSTEXACT | CHANBAN_HOSTMASK | CHANBAN_HOSTANY | CHANBAN_HOSTNULL));
232
233 cbp->timeset=time(NULL);
234
235 cbp->next=NULL;
236
237 return cbp;
238 }
239
240 /* banoverlap:
241 * Returns nonzero iff bana is a SUPERSET of banb
242 */
243
244 int banoverlap(const chanban *bana, const chanban *banb) {
245 /* This function works by looking for cases where bana DOESN'T overlap banb */
246
247 /* NICK section */
248 /* If bana has CHANBAN_NICKANY then it clearly overlaps
249 * in the nick section so there are no checks */
250
251 if ((bana->flags & CHANBAN_NICKNULL) && !(banb->flags & CHANBAN_NICKNULL)) {
252 return 0;
253 }
254
255 if (bana->flags & CHANBAN_NICKMASK) {
256 if (banb->flags & (CHANBAN_NICKANY | CHANBAN_NICKNULL)) {
257 return 0;
258 }
259 if ((banb->flags & CHANBAN_NICKMASK) && !match2patterns(bana->nick->content, banb->nick->content)) {
260 return 0;
261 }
262 if ((banb->flags & CHANBAN_NICKEXACT) && !match2strings(bana->nick->content, banb->nick->content)) {
263 return 0;
264 }
265 }
266
267 if (bana->flags & CHANBAN_NICKEXACT) {
268 if (banb->flags & (CHANBAN_NICKANY | CHANBAN_NICKMASK | CHANBAN_NICKNULL)) {
269 return 0;
270 }
271 if ((!bana->nick && banb->nick) || (bana->nick && !banb->nick)) {
272 return 0;
273 }
274 if (bana->nick && ircd_strcmp(bana->nick->content,banb->nick->content)) {
275 return 0;
276 }
277 }
278
279 /* USER section */
280 /* If bana has CHANBAN_USERANY then it clearly overlaps
281 * in the user section so there are no checks */
282
283 if ((bana->flags & CHANBAN_USERNULL) && !(banb->flags & CHANBAN_USERNULL)) {
284 return 0;
285 }
286
287 if (bana->flags & CHANBAN_USERMASK) {
288 if (banb->flags & (CHANBAN_USERANY | CHANBAN_USERNULL)) {
289 return 0;
290 }
291 if ((banb->flags & CHANBAN_USERMASK) && !match2patterns(bana->user->content, banb->user->content)) {
292 return 0;
293 }
294 if ((banb->flags & CHANBAN_USEREXACT) && !match2strings(bana->user->content, banb->user->content)) {
295 return 0;
296 }
297 }
298
299 if (bana->flags & CHANBAN_USEREXACT) {
300 if (banb->flags & (CHANBAN_USERANY | CHANBAN_USERMASK | CHANBAN_USERNULL)) {
301 return 0;
302 }
303 if ((!bana->user && banb->user) || (bana->user && !banb->user)) {
304 return 0;
305 }
306 if (bana->user && ircd_strcmp(bana->user->content,banb->user->content)) {
307 return 0;
308 }
309 }
310
311 /* HOST section */
312 /* If bana has CHANBAN_HOSTANY then it clearly overlaps
313 * in the host section so there are no checks */
314
315 if ((bana->flags & CHANBAN_HOSTNULL) && !(banb->flags & CHANBAN_HOSTNULL)) {
316 return 0;
317 }
318
319 if (bana->flags & CHANBAN_HOSTMASK) {
320 if (banb->flags & (CHANBAN_HOSTANY | CHANBAN_HOSTNULL)) {
321 return 0;
322 }
323 if ((banb->flags & CHANBAN_HOSTMASK) && !match2patterns(bana->host->content, banb->host->content)) {
324 return 0;
325 }
326 if ((banb->flags & CHANBAN_HOSTEXACT) && !match2strings(bana->host->content, banb->host->content)) {
327 return 0;
328 }
329 }
330
331 if (bana->flags & CHANBAN_HOSTEXACT) {
332 if (banb->flags & (CHANBAN_HOSTANY | CHANBAN_HOSTMASK | CHANBAN_HOSTNULL)) {
333 return 0;
334 }
335 if ((!bana->host && banb->host) || (bana->host && !banb->host)) {
336 return 0;
337 }
338 if (bana->host && ircd_strcmp(bana->host->content,banb->host->content)) {
339 return 0;
340 }
341 }
342
343 return 1;
344 }
345
346 /*
347 * banequal:
348 * Returns nonzero iff the bans are EXACTLY the same
349 */
350
351 int banequal(chanban *bana, chanban *banb) {
352 if (bana->flags != banb->flags)
353 return 0;
354
355 if ((!bana->nick && banb->nick) || (bana->nick && !banb->nick))
356 return 0;
357
358 if (bana->nick && ircd_strcmp(bana->nick->content,banb->nick->content))
359 return 0;
360
361 if ((!bana->user && banb->user) || (bana->user && !banb->user))
362 return 0;
363
364 if (bana->user && ircd_strcmp(bana->user->content,banb->user->content))
365 return 0;
366
367 if ((!bana->host && banb->host) || (bana->host && !banb->host))
368 return 0;
369
370 if (bana->host && ircd_strcmp(bana->host->content,banb->host->content))
371 return 0;
372
373 return 1;
374 }
375
376 /*
377 * bantostring:
378 * Convert the specified ban to a string
379 */
380
381 char *bantostring(chanban *bp) {
382 static char outstring[NICKLEN+USERLEN+HOSTLEN+5];
383 int strpos=0;
384
385 if (bp->flags & CHANBAN_NICKANY) {
386 strpos += sprintf(outstring+strpos,"*");
387 } else if (bp->nick) {
388 strpos += sprintf(outstring+strpos,"%s",bp->nick->content);
389 }
390
391 strpos += sprintf(outstring+strpos,"!");
392
393 if (bp->flags & CHANBAN_USERANY) {
394 strpos += sprintf(outstring+strpos,"*");
395 } else if (bp->user) {
396 strpos += sprintf(outstring+strpos,"%s",bp->user->content);
397 }
398
399 strpos += sprintf(outstring+strpos,"@");
400
401 if (bp->flags & CHANBAN_HOSTANY) {
402 sprintf(outstring+strpos,"*");
403 } else if (bp->host) {
404 sprintf(outstring+strpos,"%s",bp->host->content);
405 }
406
407 return outstring;
408 }
409
410 /*
411 * bantostringdebug:
412 * Convert the specified ban to a string (debugging version)
413 */
414
415 char *bantostringdebug(chanban *bp) {
416 static char outstring[NICKLEN+USERLEN+HOSTLEN+5];
417 int strpos=0;
418
419 strpos += sprintf(outstring+strpos, "flags=%04x ",bp->flags);
420
421 if (bp->nick) {
422 strpos += sprintf(outstring+strpos, "nick=%s ",bp->nick->content);
423 } else {
424 strpos += sprintf(outstring+strpos, "nonick ");
425 }
426
427 if (bp->user) {
428 strpos += sprintf(outstring+strpos, "user=%s ",bp->user->content);
429 } else {
430 strpos += sprintf(outstring+strpos, "nouser ");
431 }
432
433 if (bp->host) {
434 sprintf(outstring+strpos, "host=%s ",bp->host->content);
435 } else {
436 sprintf(outstring+strpos, "nohost ");
437 }
438
439 return outstring;
440 }
441