1 /*
2  * bans.c
3  *
4  * This source code implements methods and algorithms used to deal with channel bans.
5  *
6  * written by Joshua J. Drake
7  * copyright (c) 1998
8  *
9  * -= SuckSoft =-
10  */
11 
12 #include "irc.h"
13 
14 #include "bans.h"
15 #include "channels.h"
16 #include "dma.h"
17 #include "server.h"
18 #include "output.h"
19 #include "ircaux.h"
20 #include "list.h"
21 #include "how_many.h"
22 #include "ninja.h"
23 
24 /*
25  * adds a ban to the cached ban list for a channel
26  * accessed via Channel.bans
27  */
28 void
add_ban(Channel * chan,u_char * ban,u_char * owner,u_char * bantime)29 add_ban(Channel *chan, u_char *ban, u_char *owner, u_char *bantime)
30 {
31    Ban *new;
32 
33    /* if we have no channel or ban, report so and return */
34    if (!chan || !ban)
35      {
36 	put_error("add_ban() called with no channel or ban.");
37 	return;
38      }
39 
40    /* try to remove the ban specified, if it was found, free the memory used. */
41    if ((new = (Ban *) remove_from_list((List **)&(chan->bans), ban)) != NULL)
42      {
43 	dma_Free(&(new->ban));
44 	dma_Free(&(new->owner));
45 	dma_Free(&new);
46      }
47 
48    /* create a new ban entry */
49    new = (Ban *) dma_Malloc(sizeof(Ban));
50    /* pointless.. handled by dma_Malloc()
51    new->ban = NULL;
52    new->owner = NULL;
53    new->time = 0;
54     */
55    if (bantime)
56      new->time = my_atol(bantime);
57    if (owner)
58      dma_strcpy(&(new->owner), owner);
59    dma_strcpy(&(new->ban), ban);
60 
61    /* add this new entry to the list. */
62    add_to_list((List **)&(chan->bans), (List *)new);
63 }
64 
65 /*
66  * this is done after a special whois is done..
67  * it bans a user
68  */
69 void
ban_queue(WhoisStuff * stuff,u_char * nick,u_char * args)70 ban_queue(WhoisStuff *stuff, u_char *nick, u_char *args)
71 {
72    u_char *foo;
73 
74    /* check out the whois reply, make sure it returned something base a ban on. */
75    if (!stuff || !stuff->nick || !nick || strcmp(stuff->user, "<UNKNOWN>") == 0 ||
76        my_stricmp(stuff->nick, nick) || !args)
77      {
78         put_info("%s is not on irc.", nick);
79         return;
80      }
81 
82    /* skip over any bullshit characters before the username. */
83    foo = stuff->user;
84    if (foo && *foo && *foo == '~')
85      foo++;
86    if (foo && *foo)
87      foo++;
88 
89    /* ban the mask */
90    send_to_server("MODE %s +b *!*%s@%s", args, foo ? foo : empty_string, cluster(stuff->host));
91 }
92 
93 /*
94  * this is done after a special whois is done..
95  * it unbans all bans on a channel matching the nick!user@host
96  */
97 void
unban_queue(WhoisStuff * stuff,u_char * nick,u_char * args)98 unban_queue(WhoisStuff *stuff, u_char *nick, u_char *args)
99 {
100    /* check out the whois reply... */
101    if (!stuff || !stuff->nick || !nick || strcmp(stuff->user, "<UNKNOWN>") == 0 ||
102        my_stricmp(stuff->nick, nick) || !args)
103      {
104         put_info("%s is not on irc.", nick);
105         return;
106      }
107 
108     /* remove them bans. */
109    remove_matching_bans(stuff->nick, stuff->user, stuff->host, args, 0);
110 }
111 
112 /*
113  * remove all bans matching nick!user@host on channel
114  * if quiet, don't talk about the fact that we're doing it..
115  */
116 void
remove_matching_bans(u_char * nick,u_char * user,u_char * host,u_char * channel,int quiet)117 remove_matching_bans(u_char *nick, u_char *user, u_char *host, u_char *channel, int quiet)
118 {
119    Channel *ctmp;
120    Ban *btmp;
121    int found = 0, count = 0, mtch = 0, rmtch = 0;
122    /* int nuhl, nml; */
123    u_char nuh[512], tmpbuf[1024];
124 
125    /* if called there are invalid arguments, get out */
126    if (!nick || !channel)
127       return;
128 
129    /* try to find the specified channel, if it's not in the list or the user doesn't have
130     * ops on it, get out.
131     */
132    if ((ctmp = lookup_channel(channel, from_server, 0)) == NULL || !(ctmp->status & CHAN_CHOP))
133      {
134         put_info("You don't have access to remove bans from %s", channel);
135         return;
136      }
137 
138    /* if a mask was specified, remove bans matching it. */
139    if (my_index(nick, '@') && my_index(nick, '!'))
140      my_strncpy(nuh, nick, sizeof(nuh)-1);
141    else
142      {
143 	/* if the user and host aren't there, get out */
144 	if (!user || !host)
145 	  {
146 	     if (!quiet)
147 	       put_error("invalid mask: %s", nick);
148 	     return;
149 	  }
150 	/* otherwise, we will remove bans matching nick!user@host */
151 	snprintf(nuh, sizeof(nuh)-1, "%s!%s@%s", nick, user, host);
152      }
153    nuh[sizeof(nuh)-1] = '\0';
154 
155    tmpbuf[0] = '\0';
156    /* nuhl = my_strlen(nuh); */
157    /* run through the bans and remove all bans matching the nuh */
158    for (btmp = ctmp->bans; btmp; btmp = btmp->next)
159      {
160 	/* if the nuh matches the ban or the ban matches the nuh.. */
161 
162 	/* nml = my_strlen(btmp->ban); */
163 	mtch = match(btmp->ban, nuh);
164 	rmtch = match(nuh, btmp->ban);
165 	/* put_info("mtch: %d, rmtch: %d", mtch, rmtch); */
166         if (mtch || rmtch)
167           {
168 	     /* add this ban to the list to be removed. */
169 	     strmcat(tmpbuf, btmp->ban, sizeof(tmpbuf)-1);
170 	     strmcat(tmpbuf, " ", sizeof(tmpbuf)-1);
171              count++;
172           }
173 	/* only four bans can be removed at a time, so if we have four, we remove them and
174 	 * reset the list.
175 	 */
176         if (count == 4)
177           {
178              send_to_server("MODE %s -bbbb %s", ctmp->channel, tmpbuf);
179              tmpbuf[0] = '\0';
180              count = 0;
181 	     found += 4;
182           }
183      }
184 
185    /* if there are any left over, we remove them too. */
186    if (count)
187      {
188 	send_to_server("MODE %s -%s %s", ctmp->channel, strfill('b', count), tmpbuf);
189 	found += count;
190      }
191 
192    /* if we aren't supposed to be quiet, tell how many bans were removed. */
193    if (!quiet)
194      {
195 	if (!found)
196 	  put_info("No bans matching %s were found on %s", nuh, ctmp->channel);
197      }
198 }
199 
200 /*
201  * make a nice mask for the hostname passed
202  * ex: elite.net = elite.net
203  *     1.1.1.1   = 1.1.1.*
204  *     boom.zoom.com = *.zoom.com
205  *     c123459-a.oak1.il.home.com = *.oak*.il.home.com
206  * etc.
207  *
208  * if '@' is in "hn" then it will be replaced by '*@' and
209  * anything before it will be removed...
210  */
211 u_char *
cluster(u_char * hn)212 cluster(u_char *hn)
213 {
214    u_char *ptr = NULL, *ptr2;
215    static u_char cltmp[512];
216    int dots_in_host, ip = 0, clidx = 0;
217 
218    /* default to returning '*' */
219    memset(cltmp, 0, sizeof(cltmp));
220 
221    /* if there is a hostname, try to mask it */
222    if (hn)
223      {
224 	/* got a user name? */
225 	ptr2 = strrchr(hn, '@');
226 	if (ptr2)
227 	  {
228 	     cltmp[clidx++] = '*';
229 	     *ptr2++ = '\0';
230 	     if (*hn)
231 	       {
232 		  hn++;
233 		  my_strmcat(cltmp, hn, sizeof(cltmp)-1);
234 		  clidx += my_strlen(hn);
235 	       }
236 	     cltmp[clidx++] = '@';
237 	     hn = ptr2;
238 	  }
239 
240 	/* no more stuff? */
241 	if (!hn)
242 	  my_strmcat(cltmp, "ninja!irc@rulez", sizeof(cltmp)-1);
243 	else
244 	  {
245 	     dots_in_host = how_many(hn, '.');
246 	     /* its a hostname! */
247 	     if (dots_in_host <= 1)
248 	       my_strmcat(cltmp, hn, sizeof(cltmp)-1);
249 	     else
250 	       {
251 		  dma_strcpy(&ptr, hn);
252 		  switch (dots_in_host)
253 		    {
254 		     case 3:
255 		       /* if we have an ip address... */
256 		       if (isdigit(ptr[my_strlen(ptr) - 1]))
257 			 {
258 			    /* truncate after the last . to ban class c */
259 			    ptr2 = my_rindex(ptr, '.');
260 			    *(ptr2+1) = '\0';
261 			    my_strmcat(cltmp, ptr, sizeof(cltmp)-1);
262 			    my_strmcat(cltmp, "*", sizeof(cltmp)-1);
263 			    ip = 1;
264 			    break;
265 			 }
266 		     default:
267 		       /* hostname with 3 or more parts */
268 		       ptr2 = my_index(ptr, '.');
269 		       my_strmcat(cltmp, "*", sizeof(cltmp)-1);
270 		       my_strmcat(cltmp, ptr2, sizeof(cltmp)-1);
271 		       break;
272 		    }
273 		  dma_Free(&ptr);
274 
275 		  /* now replace digit strings in non-ips with '*' */
276 		  if (!ip)
277 		    {
278 		       int last_star = 0;
279 		       u_char *lptr;
280 
281 		       for (ptr2 = cltmp; ptr2 && *ptr2; ptr2++)
282 			 {
283 			    /* when we find a digit... */
284 			    if (isdigit(*ptr2))
285 			      {
286 				 /* ..skip to the end of the digits */
287 				 for (lptr = ptr2; lptr && *lptr && isdigit(*lptr); lptr++);
288 
289 				 /* did we run out of input? */
290 				 if (!lptr || !*lptr)
291 				   break;
292 
293 				 /* if there's no '*' on either side,
294 				  * put a '*' in the place of the first digit
295 				  */
296 				 if (!last_star && *lptr != '*')
297 				   *ptr2++ = '*';
298 
299 				 /* we have a '*' in place, move the rest back */
300 				 memcpy(ptr2, lptr, MIN(my_strlen(lptr)+1, sizeof(cltmp)));
301 			      }
302 			    else if (*ptr2 == '*')
303 			      last_star = 1;
304 			    else
305 			      last_star = 0;
306 			 }
307 		    }
308 	       }
309 	  }
310      }
311    return cltmp;
312 }
313