Forward port missing fixes from Kev, beware and someone else.
[ircu2.10.12-pk.git] / ircd / gline.c
1 /*
2  * IRC - Internet Relay Chat, ircd/gline.c
3  * Copyright (C) 1990 Jarkko Oikarinen and
4  *                    University of Oulu, Finland
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 1, or (at your option)
9  * any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  *
20  * $Id$
21  */
22 #include "config.h"
23
24 #include "gline.h"
25 #include "client.h"
26 #include "ircd.h"
27 #include "ircd_alloc.h"
28 #include "ircd_features.h"
29 #include "ircd_log.h"
30 #include "ircd_reply.h"
31 #include "ircd_snprintf.h"
32 #include "ircd_string.h"
33 #include "match.h"
34 #include "numeric.h"
35 #include "s_bsd.h"
36 #include "s_debug.h"
37 #include "s_misc.h"
38 #include "s_stats.h"
39 #include "send.h"
40 #include "struct.h"
41 #include "support.h"
42 #include "msg.h"
43 #include "numnicks.h"
44 #include "numeric.h"
45 #include "sys.h"    /* FALSE bleah */
46 #include "whocmds.h"
47
48 #include <assert.h>
49 #include <string.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <arpa/inet.h> /* for inet_ntoa */
53
54 #define CHECK_APPROVED     0    /* Mask is acceptable */
55 #define CHECK_OVERRIDABLE  1    /* Mask is acceptable, but not by default */
56 #define CHECK_REJECTED     2    /* Mask is totally unacceptable */
57
58 #define MASK_WILD_0     0x01    /* Wildcards in the last position */
59 #define MASK_WILD_1     0x02    /* Wildcards in the next-to-last position */
60
61 #define MASK_WILD_MASK  0x03    /* Mask out the positional wildcards */
62
63 #define MASK_WILDS      0x10    /* Mask contains wildcards */
64 #define MASK_IP         0x20    /* Mask is an IP address */
65 #define MASK_HALT       0x40    /* Finished processing mask */
66
67 struct Gline* GlobalGlineList  = 0;
68 struct Gline* BadChanGlineList = 0;
69
70 static void
71 canon_userhost(char *userhost, char **user_p, char **host_p, char *def_user)
72 {
73   char *tmp;
74
75   if (*userhost == '$') {
76     *user_p = userhost;
77     *host_p = NULL;
78     return;
79   }
80
81   if (!(tmp = strchr(userhost, '@'))) {
82     *user_p = def_user;
83     *host_p = userhost;
84   } else {
85     *user_p = userhost;
86     *(tmp++) = '\0';
87     *host_p = tmp;
88   }
89 }
90
91 static struct Gline *
92 make_gline(char *user, char *host, char *reason, time_t expire, time_t lastmod,
93            unsigned int flags)
94 {
95   struct Gline *gline, *sgline, *after = 0;
96
97   if (!(flags & GLINE_BADCHAN)) { /* search for overlapping glines first */
98
99     for (gline = GlobalGlineList; gline; gline = sgline) {
100       sgline = gline->gl_next;
101
102       if (gline->gl_expire <= CurrentTime)
103         gline_free(gline);
104       else if (((gline->gl_flags & GLINE_LOCAL) != (flags & GLINE_LOCAL)) ||
105                (gline->gl_host && !host) || (!gline->gl_host && host))
106         continue;
107       else if (!mmatch(gline->gl_user, user) /* gline contains new mask */
108                && (gline->gl_host == NULL || !mmatch(gline->gl_host, host))) {
109         if (expire <= gline->gl_expire) /* will expire before wider gline */
110           return 0;
111         else
112           after = gline; /* stick new gline after this one */
113       } else if (!mmatch(user, gline->gl_user) /* new mask contains gline */
114                  && (gline->gl_host==NULL || !mmatch(host, gline->gl_host)) 
115                  && gline->gl_expire <= expire) /* old expires before new */
116         gline_free(gline); /* save some memory */
117     }
118   }
119
120   gline = (struct Gline *)MyMalloc(sizeof(struct Gline)); /* alloc memory */
121   assert(0 != gline);
122
123   DupString(gline->gl_reason, reason); /* initialize gline... */
124   gline->gl_expire = expire;
125   gline->gl_lastmod = lastmod;
126   gline->gl_flags = flags & GLINE_MASK;
127
128   if (flags & GLINE_BADCHAN) { /* set a BADCHAN gline */
129     DupString(gline->gl_user, user); /* first, remember channel */
130     gline->gl_host = 0;
131
132     gline->gl_next = BadChanGlineList; /* then link it into list */
133     gline->gl_prev_p = &BadChanGlineList;
134     if (BadChanGlineList)
135       BadChanGlineList->gl_prev_p = &gline->gl_next;
136     BadChanGlineList = gline;
137   } else {
138     DupString(gline->gl_user, user); /* remember them... */
139     if (*user != '$')
140       DupString(gline->gl_host, host);
141     else
142       gline->gl_host = NULL;
143
144     if (*user != '$' && check_if_ipmask(host)) { /* mark if it's an IP mask */
145       int c_class;
146       char ipname[16];
147       int ad[4] = { 0 };
148       int bits2 = 0;
149       char *ch;
150       int seenwild;
151       int badmask=0;
152       
153       /* Sanity check for dodgy IP masks 
154        * Any mask featuring a digit after a wildcard will 
155        * not behave as expected. */
156       for (seenwild=0,ch=host;*ch;ch++) {
157         if (*ch=='*' || *ch=='?') 
158           seenwild=1;
159         if (IsDigit(*ch) && seenwild) {
160           badmask=1;
161           break;
162         }
163       }
164       
165       if (badmask) {
166         /* It's bad - let's make it match 0.0.0.0/32 */
167         gline->bits=32;
168         gline->ipnum.s_addr=0;
169       } else {
170         c_class = sscanf(host,"%d.%d.%d.%d/%d",
171                          &ad[0],&ad[1],&ad[2],&ad[3], &bits2);
172         if (c_class!=5)
173           gline->bits=c_class*8;
174         else
175           gline->bits=bits2;
176         ircd_snprintf(0, ipname, sizeof(ipname), "%d.%d.%d.%d", ad[0], ad[1],
177                       ad[2], ad[3]);
178         gline->ipnum.s_addr = inet_addr(ipname);
179       }
180       Debug((DEBUG_DEBUG,"IP gline: %08x/%i",gline->ipnum.s_addr,gline->bits));
181       gline->gl_flags |= GLINE_IPMASK;
182     }
183
184     if (after) {
185       gline->gl_next = after->gl_next;
186       gline->gl_prev_p = &after->gl_next;
187       if (after->gl_next)
188         after->gl_next->gl_prev_p = &gline->gl_next;
189       after->gl_next = gline;
190     } else {
191       gline->gl_next = GlobalGlineList; /* then link it into list */
192       gline->gl_prev_p = &GlobalGlineList;
193       if (GlobalGlineList)
194         GlobalGlineList->gl_prev_p = &gline->gl_next;
195       GlobalGlineList = gline;
196     }
197   }
198
199   return gline;
200 }
201
202 static int
203 do_gline(struct Client *cptr, struct Client *sptr, struct Gline *gline)
204 {
205   struct Client *acptr;
206   int fd, retval = 0, tval;
207
208   if (!GlineIsActive(gline)) /* no action taken on inactive glines */
209     return 0;
210
211   for (fd = HighestFd; fd >= 0; --fd) {
212     /*
213      * get the users!
214      */
215     if ((acptr = LocalClientArray[fd])) {
216       if (!cli_user(acptr))
217         continue;
218         
219       if (GlineIsRealName(gline)) { /* Realname Gline */
220         Debug((DEBUG_DEBUG,"Realname Gline: %s %s",(cli_info(acptr)),
221                                         gline->gl_user+2));
222         if (match(gline->gl_user+2, cli_info(acptr)) != 0)
223             continue;
224         Debug((DEBUG_DEBUG,"Matched!"));
225       } else { /* Host/IP gline */
226         if (cli_user(acptr)->username && 
227             match(gline->gl_user, (cli_user(acptr))->username) != 0)
228             continue;
229           
230         if (GlineIsIpMask(gline)) {
231           Debug((DEBUG_DEBUG,"IP gline: %08x %08x/%i",(cli_ip(cptr)).s_addr,gline->ipnum.s_addr,gline->bits));
232           if (((cli_ip(acptr)).s_addr & NETMASK(gline->bits)) != gline->ipnum.s_addr)
233             continue;
234         }
235         else {
236           if (match(gline->gl_host, cli_sockhost(acptr)) != 0)
237             continue;
238         }
239       }
240
241       /* ok, here's one that got G-lined */
242       send_reply(acptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP, ":%s",
243            gline->gl_reason);
244
245       /* let the ops know about it */
246       sendto_opmask_butone(0, SNO_GLINE, "G-line active for %s",
247                      get_client_name(acptr, TRUE));
248
249       /* and get rid of him */
250       if ((tval = exit_client_msg(cptr, acptr, &me, "G-lined (%s)",
251           gline->gl_reason)))
252         retval = tval; /* retain killed status */
253     }
254   }
255   return retval;
256 }
257
258 /*
259  * This routine implements the mask checking applied to local
260  * G-lines.  Basically, host masks must have a minimum of two non-wild
261  * domain fields, and IP masks must have a minimum of 16 bits.  If the
262  * mask has even one wild-card, OVERRIDABLE is returned, assuming the
263  * other check doesn't fail.
264  */
265 static int
266 gline_checkmask(char *mask)
267 {
268   unsigned int flags = MASK_IP;
269   unsigned int dots = 0;
270   unsigned int ipmask = 0;
271
272   for (; *mask; mask++) { /* go through given mask */
273     if (*mask == '.') { /* it's a separator; advance positional wilds */
274       flags = (flags & ~MASK_WILD_MASK) | ((flags << 1) & MASK_WILD_MASK);
275       dots++;
276
277       if ((flags & (MASK_IP | MASK_WILDS)) == MASK_IP)
278         ipmask += 8; /* It's an IP with no wilds, count bits */
279     } else if (*mask == '*' || *mask == '?')
280       flags |= MASK_WILD_0 | MASK_WILDS; /* found a wildcard */
281     else if (*mask == '/') { /* n.n.n.n/n notation; parse bit specifier */
282       ++mask;
283       ipmask = strtoul(mask, &mask, 10);
284
285       if (*mask || dots != 3 || ipmask > 32 || /* sanity-check to date */
286           (flags & (MASK_WILDS | MASK_IP)) != MASK_IP)
287         return CHECK_REJECTED; /* how strange... */
288
289       if (ipmask < 32) /* it's a masked address; mark wilds */
290         flags |= MASK_WILDS;
291
292       flags |= MASK_HALT; /* Halt the ipmask calculation */
293
294       break; /* get out of the loop */
295     } else if (!IsDigit(*mask)) {
296       flags &= ~MASK_IP; /* not an IP anymore! */
297       ipmask = 0;
298     }
299   }
300
301   /* Sanity-check quads */
302   if (dots > 3 || (!(flags & MASK_WILDS) && dots < 3)) {
303     flags &= ~MASK_IP;
304     ipmask = 0;
305   }
306
307   /* update bit count if necessary */
308   if ((flags & (MASK_IP | MASK_WILDS | MASK_HALT)) == MASK_IP)
309     ipmask += 8;
310
311   /* Check to see that it's not too wide of a mask */
312   if (flags & MASK_WILDS &&
313       ((!(flags & MASK_IP) && (dots < 2 || flags & MASK_WILD_MASK)) ||
314        (flags & MASK_IP && ipmask < 16)))
315     return CHECK_REJECTED; /* to wide, reject */
316
317   /* Ok, it's approved; require override if it has wildcards, though */
318   return flags & MASK_WILDS ? CHECK_OVERRIDABLE : CHECK_APPROVED;
319 }
320
321 int
322 gline_propagate(struct Client *cptr, struct Client *sptr, struct Gline *gline)
323 {
324   if (GlineIsLocal(gline) || (IsUser(sptr) && !gline->gl_lastmod))
325     return 0;
326
327   if (gline->gl_lastmod)
328     sendcmdto_serv_butone(sptr, CMD_GLINE, cptr, "* %c%s%s%s %Tu %Tu :%s",
329                           GlineIsRemActive(gline) ? '+' : '-', gline->gl_user,
330                           gline->gl_host ? "@" : "",
331                           gline->gl_host ? gline->gl_host : "",
332                           gline->gl_expire - CurrentTime, gline->gl_lastmod,
333                           gline->gl_reason);
334   else
335     sendcmdto_serv_butone(sptr, CMD_GLINE, cptr,
336                           (GlineIsRemActive(gline) ?
337                            "* +%s%s%s %Tu :%s" : "* -%s%s%s"),
338                           gline->gl_user, 
339                           gline->gl_host ? "@" : "",
340                           gline->gl_host ? gline->gl_host : "",
341                           gline->gl_expire - CurrentTime, gline->gl_reason);
342
343   return 0;
344 }
345
346 int 
347 gline_add(struct Client *cptr, struct Client *sptr, char *userhost,
348           char *reason, time_t expire, time_t lastmod, unsigned int flags)
349 {
350   struct Gline *agline;
351   char uhmask[USERLEN + HOSTLEN + 2];
352   char *user, *host;
353   int tmp;
354
355   assert(0 != userhost);
356   assert(0 != reason);
357
358   /* NO_OLD_GLINE allows *@#channel to work correctly */
359   if (*userhost == '#' || *userhost == '&'
360 # ifndef NO_OLD_GLINE
361       || ((userhost[2] == '#' || userhost[2] == '&') && (userhost[1] == '@'))
362 # endif /* OLD_GLINE */
363       ) {
364     if ((flags & GLINE_LOCAL) && !HasPriv(sptr, PRIV_LOCAL_BADCHAN))
365       return send_reply(sptr, ERR_NOPRIVILEGES);
366
367     flags |= GLINE_BADCHAN;
368 # ifndef NO_OLD_GLINE
369     if ((userhost[2] == '#' || userhost[2] == '&') && (userhost[1] == '@'))
370       user = userhost + 2;
371     else
372 # endif /* OLD_GLINE */
373       user = userhost;
374     host = 0;
375   } else if (*userhost == '$' 
376 # ifndef NO_OLD_GLINE
377   || userhost[2] == '$'
378 # endif /* OLD_GLINE */
379   ) {
380     switch (*userhost == '$' ? userhost[1] : userhost[3]) {
381       case 'R': flags |= GLINE_REALNAME; break;
382       default:
383         /* uh, what to do here? */
384         /* The answer, my dear Watson, is we throw a protocol_violation()
385            -- hikari */
386         return protocol_violation(sptr,"%s has been smoking the sweet leaf and sent me a whacky gline",cli_name(sptr));
387         break;
388     }
389      user = (*userhost =='$' ? userhost : userhost+2);
390      host = 0;
391   } else {
392     canon_userhost(userhost, &user, &host, "*");
393     if (sizeof(uhmask) <
394         ircd_snprintf(0, uhmask, sizeof(uhmask), "%s@%s", user, host))
395       return send_reply(sptr, ERR_LONGMASK);
396     else if (MyUser(sptr) || (IsUser(sptr) && flags & GLINE_LOCAL)) {
397       switch (gline_checkmask(host)) {
398       case CHECK_OVERRIDABLE: /* oper overrided restriction */
399         if (flags & GLINE_OPERFORCE)
400           break;
401         /*FALLTHROUGH*/
402       case CHECK_REJECTED:
403         return send_reply(sptr, ERR_MASKTOOWIDE, uhmask);
404         break;
405       }
406
407       if ((tmp = count_users(uhmask)) >=
408           feature_int(FEAT_GLINEMAXUSERCOUNT) && !(flags & GLINE_OPERFORCE))
409         return send_reply(sptr, ERR_TOOMANYUSERS, tmp);
410     }
411   }
412
413   /*
414    * You cannot set a negative (or zero) expire time, nor can you set an
415    * expiration time for greater than GLINE_MAX_EXPIRE.
416    */
417   if (!(flags & GLINE_FORCE) && (expire <= 0 || expire > GLINE_MAX_EXPIRE)) {
418     if (!IsServer(sptr) && MyConnect(sptr))
419       send_reply(sptr, ERR_BADEXPIRE, expire);
420     return 0;
421   }
422
423   expire += CurrentTime; /* convert from lifetime to timestamp */
424
425   /* Inform ops... */
426   sendto_opmask_butone(0, ircd_strncmp(reason, "AUTO", 4) ? SNO_GLINE :
427                        SNO_AUTO, "%s adding %s %s for %s%s%s, expiring at "
428                        "%Tu: %s",
429                        (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
430                          cli_name(sptr) :
431                          cli_name((cli_user(sptr))->server),
432                        (flags & GLINE_LOCAL) ? "local" : "global",
433                        (flags & GLINE_BADCHAN) ? "BADCHAN" : "GLINE", user,
434                        (flags & (GLINE_BADCHAN|GLINE_REALNAME)) ? "" : "@",
435                        (flags & (GLINE_BADCHAN|GLINE_REALNAME)) ? "" : host,
436                        expire + TSoffset, reason);
437
438   /* and log it */
439   log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE,
440             "%#C adding %s %s for %s, expiring at %Tu: %s", sptr,
441             flags & GLINE_LOCAL ? "local" : "global",
442             flags & GLINE_BADCHAN ? "BADCHAN" : "GLINE", userhost,
443             expire + TSoffset, reason);
444
445   /* make the gline */
446   agline = make_gline(user, host, reason, expire, lastmod, flags);
447
448   if (!agline) /* if it overlapped, silently return */
449     return 0;
450
451   gline_propagate(cptr, sptr, agline);
452
453   if (GlineIsBadChan(agline))
454     return 0;
455
456   return do_gline(cptr, sptr, agline); /* knock off users if necessary */
457 }
458
459 int
460 gline_activate(struct Client *cptr, struct Client *sptr, struct Gline *gline,
461                time_t lastmod, unsigned int flags)
462 {
463   unsigned int saveflags = 0;
464
465   assert(0 != gline);
466
467   saveflags = gline->gl_flags;
468
469   if (flags & GLINE_LOCAL)
470     gline->gl_flags &= ~GLINE_LDEACT;
471   else {
472     gline->gl_flags |= GLINE_ACTIVE;
473
474     if (gline->gl_lastmod) {
475       if (gline->gl_lastmod >= lastmod) /* force lastmod to increase */
476         gline->gl_lastmod++;
477       else
478         gline->gl_lastmod = lastmod;
479     }
480   }
481
482   if ((saveflags & GLINE_ACTMASK) == GLINE_ACTIVE)
483     return 0; /* was active to begin with */
484
485   /* Inform ops and log it */
486   sendto_opmask_butone(0, SNO_GLINE, "%s activating global %s for %s%s%s, "
487                        "expiring at %Tu: %s",
488                        (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
489                          cli_name(sptr) :
490                          cli_name((cli_user(sptr))->server),
491                        GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
492                        gline->gl_user, gline->gl_host ? "@" : "",
493                        gline->gl_host ? gline->gl_host : "",
494                        gline->gl_expire + TSoffset, gline->gl_reason);
495   
496   log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE,
497             "%#C activating global %s for %s%s%s, expiring at %Tu: %s", sptr,
498             GlineIsBadChan(gline) ? "BADCHAN" : "GLINE", gline->gl_user,
499             gline->gl_host ? "@" : "",
500             gline->gl_host ? gline->gl_host : "",
501             gline->gl_expire + TSoffset, gline->gl_reason);
502
503   if (!(flags & GLINE_LOCAL)) /* don't propagate local changes */
504     gline_propagate(cptr, sptr, gline);
505
506   return GlineIsBadChan(gline) ? 0 : do_gline(cptr, sptr, gline);
507 }
508
509 int
510 gline_deactivate(struct Client *cptr, struct Client *sptr, struct Gline *gline,
511                  time_t lastmod, unsigned int flags)
512 {
513   unsigned int saveflags = 0;
514   char *msg;
515
516   assert(0 != gline);
517
518   saveflags = gline->gl_flags;
519
520   if (GlineIsLocal(gline))
521     msg = "removing local";
522   else if (!gline->gl_lastmod && !(flags & GLINE_LOCAL)) {
523     msg = "removing global";
524     gline->gl_flags &= ~GLINE_ACTIVE; /* propagate a -<mask> */
525   } else {
526     msg = "deactivating global";
527
528     if (flags & GLINE_LOCAL)
529       gline->gl_flags |= GLINE_LDEACT;
530     else {
531       gline->gl_flags &= ~GLINE_ACTIVE;
532
533       if (gline->gl_lastmod) {
534         if (gline->gl_lastmod >= lastmod)
535           gline->gl_lastmod++;
536         else
537           gline->gl_lastmod = lastmod;
538       }
539     }
540
541     if ((saveflags & GLINE_ACTMASK) != GLINE_ACTIVE)
542       return 0; /* was inactive to begin with */
543   }
544
545   /* Inform ops and log it */
546   sendto_opmask_butone(0, SNO_GLINE, "%s %s %s for %s%s%s, expiring at %Tu: "
547                        "%s",
548                        (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
549                          cli_name(sptr) :
550                          cli_name((cli_user(sptr))->server),
551                        msg, GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
552                        gline->gl_user, gline->gl_host ? "@" : "",
553                        gline->gl_host ? gline->gl_host : "",
554                        gline->gl_expire + TSoffset, gline->gl_reason);
555
556   log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE,
557             "%#C %s %s for %s%s%s, expiring at %Tu: %s", sptr, msg,
558             GlineIsBadChan(gline) ? "BADCHAN" : "GLINE", gline->gl_user,
559             gline->gl_host ? "@" : "",
560             gline->gl_host ? gline->gl_host : "",
561             gline->gl_expire + TSoffset, gline->gl_reason);
562
563   if (!(flags & GLINE_LOCAL)) /* don't propagate local changes */
564     gline_propagate(cptr, sptr, gline);
565
566   /* if it's a local gline or a Uworld gline (and not locally deactivated).. */
567   if (GlineIsLocal(gline) || (!gline->gl_lastmod && !(flags & GLINE_LOCAL)))
568     gline_free(gline); /* get rid of it */
569
570   return 0;
571 }
572
573 struct Gline *
574 gline_find(char *userhost, unsigned int flags)
575 {
576   struct Gline *gline;
577   struct Gline *sgline;
578   char *user, *host, *t_uh;
579
580   if (flags & (GLINE_BADCHAN | GLINE_ANY)) {
581     for (gline = BadChanGlineList; gline; gline = sgline) {
582       sgline = gline->gl_next;
583
584       if (gline->gl_expire <= CurrentTime)
585         gline_free(gline);
586       else if ((flags & GLINE_GLOBAL && gline->gl_flags & GLINE_LOCAL) ||
587                (flags & GLINE_LASTMOD && !gline->gl_lastmod))
588         continue;
589       else if ((flags & GLINE_EXACT ? ircd_strcmp(gline->gl_user, userhost) :
590                 match(gline->gl_user, userhost)) == 0)
591         return gline;
592     }
593   }
594
595   if ((flags & (GLINE_BADCHAN | GLINE_ANY)) == GLINE_BADCHAN ||
596       *userhost == '#' || *userhost == '&'
597 #ifndef NO_OLD_GLINE
598       || userhost[2] == '#' || userhost[2] == '&'
599 #endif /* NO_OLD_GLINE */
600       )
601     return 0;
602
603   DupString(t_uh, userhost);
604   canon_userhost(t_uh, &user, &host, 0);
605
606   if (BadPtr(user))
607     return 0;
608
609   for (gline = GlobalGlineList; gline; gline = sgline) {
610     sgline = gline->gl_next;
611
612     if (gline->gl_expire <= CurrentTime)
613       gline_free(gline);
614     else if ((flags & GLINE_GLOBAL && gline->gl_flags & GLINE_LOCAL) ||
615              (flags & GLINE_LASTMOD && !gline->gl_lastmod))
616       continue;
617     else if (flags & GLINE_EXACT) {
618       if (((gline->gl_host && host && ircd_strcmp(gline->gl_host, host) == 0)
619            || (!gline->gl_host && !host)) &&
620           ((!user && ircd_strcmp(gline->gl_user, "*") == 0) ||
621            ircd_strcmp(gline->gl_user, user) == 0))
622         break;
623     } else {
624       if (((gline->gl_host && host && ircd_strcmp(gline->gl_host, host) == 0)
625            || (!gline->gl_host && !host)) &&
626           ((!user && ircd_strcmp(gline->gl_user, "*") == 0) ||
627            match(gline->gl_user, user) == 0))
628       break;
629     }
630   }
631
632   MyFree(t_uh);
633
634   return gline;
635 }
636
637 struct Gline *
638 gline_lookup(struct Client *cptr, unsigned int flags)
639 {
640   struct Gline *gline;
641   struct Gline *sgline;
642
643   for (gline = GlobalGlineList; gline; gline = sgline) {
644     sgline = gline->gl_next;
645
646     if (gline->gl_expire <= CurrentTime) {
647       gline_free(gline);
648       continue;
649     }
650     
651     if ((flags & GLINE_GLOBAL && gline->gl_flags & GLINE_LOCAL) ||
652         (flags & GLINE_LASTMOD && !gline->gl_lastmod))
653       continue;
654
655     if (GlineIsRealName(gline)) {
656       Debug((DEBUG_DEBUG,"realname gline: '%s' '%s'",gline->gl_user,cli_info(cptr)));
657       if (match(gline->gl_user+2, cli_info(cptr)) != 0)
658         continue;
659       if (!GlineIsActive(gline))
660         continue;
661       return gline;
662     }
663     else {
664       if (match(gline->gl_user, (cli_user(cptr))->username) != 0)
665         continue;
666          
667       if (GlineIsIpMask(gline)) {
668         Debug((DEBUG_DEBUG,"IP gline: %08x %08x/%i",(cli_ip(cptr)).s_addr,gline->ipnum.s_addr,gline->bits));
669         if (((cli_ip(cptr)).s_addr & NETMASK(gline->bits)) != gline->ipnum.s_addr)
670           continue;
671       }
672       else {
673         if (match(gline->gl_host, (cli_user(cptr))->realhost) != 0) 
674           continue;
675       }
676     }
677     if (GlineIsActive(gline))
678       return gline;
679   }
680   /*
681    * No Glines matched
682    */
683   return 0;
684 }
685
686 void
687 gline_free(struct Gline *gline)
688 {
689   assert(0 != gline);
690
691   *gline->gl_prev_p = gline->gl_next; /* squeeze this gline out */
692   if (gline->gl_next)
693     gline->gl_next->gl_prev_p = gline->gl_prev_p;
694
695   MyFree(gline->gl_user); /* free up the memory */
696   if (gline->gl_host)
697     MyFree(gline->gl_host);
698   MyFree(gline->gl_reason);
699   MyFree(gline);
700 }
701
702 void
703 gline_burst(struct Client *cptr)
704 {
705   struct Gline *gline;
706   struct Gline *sgline;
707
708   for (gline = GlobalGlineList; gline; gline = sgline) { /* all glines */
709     sgline = gline->gl_next;
710
711     if (gline->gl_expire <= CurrentTime) /* expire any that need expiring */
712       gline_free(gline);
713     else if (!GlineIsLocal(gline) && gline->gl_lastmod)
714       sendcmdto_one(&me, CMD_GLINE, cptr, "* %c%s%s%s %Tu %Tu :%s",
715                     GlineIsRemActive(gline) ? '+' : '-', gline->gl_user,
716                     gline->gl_host ? "@" : "",
717                     gline->gl_host ? gline->gl_host : "",
718                     gline->gl_expire - CurrentTime, gline->gl_lastmod,
719                     gline->gl_reason);
720   }
721
722   for (gline = BadChanGlineList; gline; gline = sgline) { /* all glines */
723     sgline = gline->gl_next;
724
725     if (gline->gl_expire <= CurrentTime) /* expire any that need expiring */
726       gline_free(gline);
727     else if (!GlineIsLocal(gline) && gline->gl_lastmod)
728       sendcmdto_one(&me, CMD_GLINE, cptr, "* %c%s %Tu %Tu :%s",
729                     GlineIsRemActive(gline) ? '+' : '-', gline->gl_user,
730                     gline->gl_expire - CurrentTime, gline->gl_lastmod,
731                     gline->gl_reason);
732   }
733 }
734
735 int
736 gline_resend(struct Client *cptr, struct Gline *gline)
737 {
738   if (GlineIsLocal(gline) || !gline->gl_lastmod)
739     return 0;
740
741   sendcmdto_one(&me, CMD_GLINE, cptr, "* %c%s%s%s %Tu %Tu :%s",
742                 GlineIsRemActive(gline) ? '+' : '-', gline->gl_user,
743                 gline->gl_host ? "@" : "",
744                 gline->gl_host ? gline->gl_host : "",
745                 gline->gl_expire - CurrentTime, gline->gl_lastmod,
746                 gline->gl_reason);
747
748   return 0;
749 }
750
751 int
752 gline_list(struct Client *sptr, char *userhost)
753 {
754   struct Gline *gline;
755   struct Gline *sgline;
756
757   if (userhost) {
758     if (!(gline = gline_find(userhost, GLINE_ANY))) /* no such gline */
759       return send_reply(sptr, ERR_NOSUCHGLINE, userhost);
760
761     /* send gline information along */
762     send_reply(sptr, RPL_GLIST, gline->gl_user,
763                gline->gl_host ? "@" : "",
764                gline->gl_host ? gline->gl_host : "",
765                gline->gl_expire + TSoffset,
766                GlineIsLocal(gline) ? cli_name(&me) : "*",
767                GlineIsActive(gline) ? '+' : '-', gline->gl_reason);
768   } else {
769     for (gline = GlobalGlineList; gline; gline = sgline) {
770       sgline = gline->gl_next;
771
772       if (gline->gl_expire <= CurrentTime)
773         gline_free(gline);
774       else
775         send_reply(sptr, RPL_GLIST, gline->gl_user,
776                    gline->gl_host ? "@" : "",
777                    gline->gl_host ? gline->gl_host : "",
778                    gline->gl_expire + TSoffset,
779                    GlineIsLocal(gline) ? cli_name(&me) : "*",
780                    GlineIsActive(gline) ? '+' : '-', gline->gl_reason);
781     }
782
783     for (gline = BadChanGlineList; gline; gline = sgline) {
784       sgline = gline->gl_next;
785
786       if (gline->gl_expire <= CurrentTime)
787         gline_free(gline);
788       else
789         send_reply(sptr, RPL_GLIST, gline->gl_user, "", "",
790                    gline->gl_expire + TSoffset,
791                    GlineIsLocal(gline) ? cli_name(&me) : "*",
792                    GlineIsActive(gline) ? '+' : '-', gline->gl_reason);
793     }
794   }
795
796   /* end of gline information */
797   return send_reply(sptr, RPL_ENDOFGLIST);
798 }
799
800 void
801 gline_stats(struct Client *sptr, struct StatDesc *sd, int stat,
802             char *param)
803 {
804   struct Gline *gline;
805   struct Gline *sgline;
806
807   for (gline = GlobalGlineList; gline; gline = sgline) {
808     sgline = gline->gl_next;
809
810     if (gline->gl_expire <= CurrentTime)
811       gline_free(gline);
812     else
813       send_reply(sptr, RPL_STATSGLINE, 'G', gline->gl_user,
814                  gline->gl_host ? "@" : "",
815                  gline->gl_host ? gline->gl_host : "",
816                  gline->gl_expire + TSoffset, gline->gl_reason);
817   }
818 }
819
820 int
821 gline_memory_count(size_t *gl_size)
822 {
823   struct Gline *gline;
824   unsigned int gl = 0;
825   
826   for (gline = GlobalGlineList; gline; gline = gline->gl_next)
827   {
828     gl++;
829     *gl_size += sizeof(struct Gline);
830     *gl_size += gline->gl_user ? (strlen(gline->gl_user) + 1) : 0;
831     *gl_size += gline->gl_host ? (strlen(gline->gl_host) + 1) : 0;
832     *gl_size += gline->gl_reason ? (strlen(gline->gl_reason) + 1) : 0;
833   }
834   return gl;
835 }
836