e8c7b29a74729760304bdb86e8a59cadc7f2c70a
[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 2, 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 /** @file
21  * @brief Implementation of Gline manipulation functions.
22  * @version $Id: gline.c 1904 2009-02-09 00:03:34Z entrope $
23  */
24 #include "config.h"
25
26 #include "gline.h"
27 #include "channel.h"
28 #include "client.h"
29 #include "hash.h"
30 #include "ircd.h"
31 #include "ircd_alloc.h"
32 #include "ircd_features.h"
33 #include "ircd_log.h"
34 #include "ircd_reply.h"
35 #include "ircd_snprintf.h"
36 #include "ircd_string.h"
37 #include "match.h"
38 #include "numeric.h"
39 #include "s_bsd.h"
40 #include "s_debug.h"
41 #include "s_misc.h"
42 #include "s_stats.h"
43 #include "send.h"
44 #include "struct.h"
45 #include "sys.h"
46 #include "msg.h"
47 #include "numnicks.h"
48 #include "numeric.h"
49
50 /* #include <assert.h> -- Now using assert in ircd_log.h */
51 #include <string.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54
55 #define CHECK_APPROVED     0    /**< Mask is acceptable */
56 #define CHECK_OVERRIDABLE  1    /**< Mask is acceptable, but not by default */
57 #define CHECK_REJECTED     2    /**< Mask is totally unacceptable */
58
59 #define MASK_WILD_0     0x01    /**< Wildcards in the last position */
60 #define MASK_WILD_1     0x02    /**< Wildcards in the next-to-last position */
61
62 #define MASK_WILD_MASK  0x03    /**< Mask out the positional wildcards */
63
64 #define MASK_WILDS      0x10    /**< Mask contains wildcards */
65 #define MASK_IP         0x20    /**< Mask is an IP address */
66 #define MASK_HALT       0x40    /**< Finished processing mask */
67
68 /** List of user G-lines. */
69 struct Gline* GlobalGlineList  = 0;
70 /** List of BadChan G-lines. */
71 struct Gline* BadChanGlineList = 0;
72
73 /** Iterate through \a list of G-lines.  Use this like a for loop,
74  * i.e., follow it with braces and use whatever you passed as \a gl
75  * as a single G-line to be acted upon.
76  *
77  * @param[in] list List of G-lines to iterate over.
78  * @param[in] gl Name of a struct Gline pointer variable that will be made to point to the G-lines in sequence.
79  * @param[in] next Name of a scratch struct Gline pointer variable.
80  */
81 /* There is some subtlety here with the boolean operators:
82  * (x || 1) is used to continue in a logical-and series even when !x.
83  * (x && 0) is used to continue in a logical-or series even when x.
84  */
85 #ifdef OLD_OGN_IRCU_COMPAT
86     #define GL_EXPIRED(gl) ((gl)->gl_lifetime <= CurrentTime)
87 #else
88     #define GL_EXPIRED(gl) ((gl)->gl_lifetime <= CurrentTime || (gl)->gl_expire <= CurrentTime)
89 #endif
90 #define gliter(list, gl, next)                          \
91   /* Iterate through the G-lines in the list */         \
92   for ((gl) = (list); (gl); (gl) = (next))              \
93     /* Figure out the next pointer in list... */        \
94     if ((((next) = (gl)->gl_next) || 1) &&              \
95         /* Then see if it's expired */                  \
96         GL_EXPIRED(gl)) \
97       /* Record has expired, so free the G-line */      \
98       gline_free((gl));                                 \
99     /* See if we need to expire the G-line */           \
100     else if ((((gl)->gl_expire > CurrentTime) ||        \
101               (((gl)->gl_flags &= ~GLINE_ACTIVE) && 0) ||       \
102               ((gl)->gl_state = GLOCAL_GLOBAL)) && 0)   \
103       ; /* empty statement */                           \
104     else
105
106 /** Find canonical user and host for a string.
107  * If \a userhost starts with '$', assign \a userhost to *user_p and NULL to *host_p.
108  * Otherwise, if \a userhost contains '@', assign the earlier part of it to *user_p and the rest to *host_p.
109  * Otherwise, assign \a def_user to *user_p and \a userhost to *host_p.
110  *
111  * @param[in] userhost Input string from user.
112  * @param[out] user_p Gets pointer to user (or channel/realname) part of hostmask.
113  * @param[out] host_p Gets point to host part of hostmask (may be assigned NULL).
114  * @param[in] def_user Default value for user part.
115  */
116 static void
117 canon_userhost(char *userhost, char **user_p, char **host_p, char *def_user)
118 {
119   char *tmp;
120
121   if (*userhost == '$') {
122     *user_p = userhost;
123     *host_p = NULL;
124     return;
125   }
126
127   if (!(tmp = strchr(userhost, '@'))) {
128     *user_p = def_user;
129     *host_p = userhost;
130   } else {
131     *user_p = userhost;
132     *(tmp++) = '\0';
133     *host_p = tmp;
134   }
135 }
136
137 /** Create a Gline structure.
138  * @param[in] user User part of mask.
139  * @param[in] host Host part of mask (NULL if not applicable).
140  * @param[in] reason Reason for G-line.
141  * @param[in] expire Expiration timestamp.
142  * @param[in] lastmod Last modification timestamp.
143  * @param[in] flags Bitwise combination of GLINE_* bits.
144  * @return Newly allocated G-line.
145  */
146 static struct Gline *
147 make_gline(char *user, char *host, char *reason, time_t expire, time_t lastmod,
148            time_t lifetime, unsigned int flags)
149 {
150   struct Gline *gline;
151
152   assert(0 != expire);
153
154   gline = (struct Gline *)MyMalloc(sizeof(struct Gline)); /* alloc memory */
155   assert(0 != gline);
156
157   DupString(gline->gl_reason, reason); /* initialize gline... */
158   gline->gl_expire = expire;
159   gline->gl_lifetime = lifetime;
160   gline->gl_lastmod = lastmod;
161   gline->gl_flags = flags & GLINE_MASK;
162   gline->gl_state = GLOCAL_GLOBAL; /* not locally modified */
163
164   if (flags & GLINE_BADCHAN) { /* set a BADCHAN gline */
165     DupString(gline->gl_user, user); /* first, remember channel */
166     gline->gl_host = NULL;
167
168     gline->gl_next = BadChanGlineList; /* then link it into list */
169     gline->gl_prev_p = &BadChanGlineList;
170     if (BadChanGlineList)
171       BadChanGlineList->gl_prev_p = &gline->gl_next;
172     BadChanGlineList = gline;
173   } else {
174     DupString(gline->gl_user, user); /* remember them... */
175     if (*user != '$')
176       DupString(gline->gl_host, host);
177     else
178       gline->gl_host = NULL;
179
180     if (*user != '$' && ipmask_parse(host, &gline->gl_addr, &gline->gl_bits))
181       gline->gl_flags |= GLINE_IPMASK;
182
183     gline->gl_next = GlobalGlineList; /* then link it into list */
184     gline->gl_prev_p = &GlobalGlineList;
185     if (GlobalGlineList)
186       GlobalGlineList->gl_prev_p = &gline->gl_next;
187     GlobalGlineList = gline;
188   }
189
190   return gline;
191 }
192
193 /** Check local clients against a new G-line.
194  * If the G-line is inactive, return immediately.
195  * Otherwise, if any users match it, disconnect them.
196  * @param[in] cptr Peer connect that sent the G-line.
197  * @param[in] sptr Client that originated the G-line.
198  * @param[in] gline New G-line to check.
199  * @return Zero, unless \a sptr G-lined himself, in which case CPTR_KILLED.
200  */
201 static int
202 do_gline(struct Client *cptr, struct Client *sptr, struct Gline *gline)
203 {
204   struct Client *acptr;
205   int fd, retval = 0, tval;
206   struct Membership *member, *nmember;
207   struct Channel *chptr, *nchptr;
208
209   if (feature_bool(FEAT_DISABLE_GLINES))
210     return 0; /* G-lines are disabled */
211
212   if (!GlineIsActive(gline)) /* no action taken on inactive glines */
213     return 0;
214
215     /* Kick all users out of BADCHANs. */
216     if(GlineIsBadChan(gline)) {
217         for(chptr = GlobalChannelList; chptr; chptr = nchptr) {
218             nchptr = chptr->next; /* save nchptr, we may delete him */
219             if(0 == match(GlineUser(gline), chptr->chname)) {
220                 for(member = chptr->members; member; member = nmember) {
221                     nmember = member->next_member; /* save nmember, we may delete him */
222                     /* Do not kick opers. */
223                     if(!MyUser(member->user) || IsZombie(member) || IsAnOper(member->user)) continue;
224                     sendcmdto_serv_butone(&me, CMD_KICK, NULL, "%H %C :Badchanneled: %s", chptr, member->user, GlineReason(gline));
225                     sendcmdto_channel_butserv_butone(&me, CMD_KICK, chptr, NULL, 0, "%H %C :Badchanneled: %s", chptr, member->user, GlineReason(gline));
226                     make_zombie(member, member->user, &me, &me, chptr);
227                 }
228                 if(!string_has_wildcards(GlineUser(gline))) break;
229             }
230         }
231         return 0;
232     }
233
234   for (fd = HighestFd; fd >= 0; --fd) {
235     /*
236      * get the users!
237      */
238     if ((acptr = LocalClientArray[fd])) {
239       if (!cli_user(acptr))
240         continue;
241
242       if (GlineIsRealName(gline)) { /* Realname Gline */
243         Debug((DEBUG_DEBUG,"Realname Gline: %s %s",(cli_info(acptr)),
244                                         gline->gl_user+2));
245         if (match(gline->gl_user+2, cli_info(acptr)) != 0)
246             continue;
247         Debug((DEBUG_DEBUG,"Matched!"));
248       } else { /* Host/IP gline */
249         if (cli_user(acptr)->username &&
250             match(gline->gl_user, (cli_user(acptr))->username) != 0)
251           continue;
252
253         if (GlineIsIpMask(gline)) {
254           if (!ipmask_check(&cli_ip(acptr), &gline->gl_addr, gline->gl_bits))
255             continue;
256         }
257         else {
258           if (match(gline->gl_host, cli_sockhost(acptr)) != 0)
259             continue;
260         }
261       }
262
263       /* ok, here's one that got G-lined */
264       send_reply(acptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP, ":%s",
265            gline->gl_reason);
266
267       /* let the ops know about it */
268       sendto_opmask_butone(0, SNO_GLINE, "G-line active for %s",
269                            get_client_name(acptr, SHOW_IP));
270
271       /* and get rid of him */
272       if ((tval = exit_client_msg(cptr, acptr, &me, "G-lined (%s)",
273           gline->gl_reason)))
274         retval = tval; /* retain killed status */
275     }
276   }
277   return retval;
278 }
279
280 /**
281  * Implements the mask checking applied to local G-lines.
282  * Basically, host masks must have a minimum of two non-wild domain
283  * fields, and IP masks must have a minimum of 16 bits.  If the mask
284  * has even one wild-card, OVERRIDABLE is returned, assuming the other
285  * check doesn't fail.
286  * @param[in] mask G-line mask to check.
287  * @return One of CHECK_REJECTED, CHECK_OVERRIDABLE, or CHECK_APPROVED.
288  */
289 static int
290 gline_checkmask(char *mask)
291 {
292   unsigned int flags = MASK_IP;
293   unsigned int dots = 0;
294   unsigned int ipmask = 0;
295
296   for (; *mask; mask++) { /* go through given mask */
297     if (*mask == '.') { /* it's a separator; advance positional wilds */
298       flags = (flags & ~MASK_WILD_MASK) | ((flags << 1) & MASK_WILD_MASK);
299       dots++;
300
301       if ((flags & (MASK_IP | MASK_WILDS)) == MASK_IP)
302         ipmask += 8; /* It's an IP with no wilds, count bits */
303     } else if (*mask == '*' || *mask == '?')
304       flags |= MASK_WILD_0 | MASK_WILDS; /* found a wildcard */
305     else if (*mask == '/') { /* n.n.n.n/n notation; parse bit specifier */
306       ++mask;
307       ipmask = strtoul(mask, &mask, 10);
308
309       /* sanity-check to date */
310       if (*mask || (flags & (MASK_WILDS | MASK_IP)) != MASK_IP)
311         return CHECK_REJECTED;
312       if (!dots) {
313         if (ipmask > 128)
314           return CHECK_REJECTED;
315         if (ipmask < 128)
316           flags |= MASK_WILDS;
317       } else {
318         if (dots != 3 || ipmask > 32)
319           return CHECK_REJECTED;
320         if (ipmask < 32)
321           flags |= MASK_WILDS;
322       }
323
324       flags |= MASK_HALT; /* Halt the ipmask calculation */
325       break; /* get out of the loop */
326     } else if (!IsIP6Char(*mask)) {
327       flags &= ~MASK_IP; /* not an IP anymore! */
328       ipmask = 0;
329     }
330   }
331
332   /* Sanity-check quads */
333   if (dots > 3 || (!(flags & MASK_WILDS) && dots < 3)) {
334     flags &= ~MASK_IP;
335     ipmask = 0;
336   }
337
338   /* update bit count if necessary */
339   if ((flags & (MASK_IP | MASK_WILDS | MASK_HALT)) == MASK_IP)
340     ipmask += 8;
341
342   /* Check to see that it's not too wide of a mask */
343   if (flags & MASK_WILDS &&
344       ((!(flags & MASK_IP) && (dots < 2 || flags & MASK_WILD_MASK)) ||
345        (flags & MASK_IP && ipmask < 16)))
346     return CHECK_REJECTED; /* to wide, reject */
347
348   /* Ok, it's approved; require override if it has wildcards, though */
349   return flags & MASK_WILDS ? CHECK_OVERRIDABLE : CHECK_APPROVED;
350 }
351
352 /** Forward a G-line to other servers.
353  * @param[in] cptr Client that sent us the G-line.
354  * @param[in] sptr Client that originated the G-line.
355  * @param[in] gline G-line to forward.
356  * @return Zero.
357  */
358 static int
359 gline_propagate(struct Client *cptr, struct Client *sptr, struct Gline *gline)
360 {
361   if (GlineIsLocal(gline))
362     return 0;
363
364   assert(gline->gl_lastmod);
365
366 #ifdef OLD_OGN_IRCU_COMPAT
367   sendcmdto_serv_butone(sptr, CMD_GLINE, cptr, "* %c%s%s%s %Tu %Tu :%s",
368                         GlineIsRemActive(gline) ? '+' : '-', gline->gl_user,
369                         gline->gl_host ? "@" : "",
370                         gline->gl_host ? gline->gl_host : "",
371                         gline->gl_expire - CurrentTime, gline->gl_lastmod,
372                         /*gline->gl_lifetime,*/ gline->gl_reason);
373 #else
374   sendcmdto_serv_butone(sptr, CMD_GLINE, cptr, "* %c%s%s%s %Tu %Tu %Tu :%s",
375                         GlineIsRemActive(gline) ? '+' : '-', gline->gl_user,
376                         gline->gl_host ? "@" : "",
377                         gline->gl_host ? gline->gl_host : "",
378                         gline->gl_expire - CurrentTime, gline->gl_lastmod,
379                         gline->gl_lifetime, gline->gl_reason);
380 #endif
381
382   return 0;
383 }
384
385 /** Count number of users who match \a mask.
386  * @param[in] mask user\@host or user\@ip mask to check.
387  * @param[in] flags Bitmask possibly containing the value GLINE_LOCAL, to limit searches to this server.
388  * @return Count of matching users.
389  */
390 static int
391 count_users(char *mask, int flags)
392 {
393   struct irc_in_addr ipmask;
394   struct Client *acptr;
395   int count = 0;
396   int ipmask_valid;
397   char namebuf[USERLEN + HOSTLEN + 2];
398   char ipbuf[USERLEN + SOCKIPLEN + 2];
399   unsigned char ipmask_len;
400
401   ipmask_valid = ipmask_parse(mask, &ipmask, &ipmask_len);
402   for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) {
403     if (!IsUser(acptr))
404       continue;
405     if ((flags & GLINE_LOCAL) && !MyConnect(acptr))
406       continue;
407
408     ircd_snprintf(0, namebuf, sizeof(namebuf), "%s@%s",
409                   cli_user(acptr)->username, cli_user(acptr)->host);
410     ircd_snprintf(0, ipbuf, sizeof(ipbuf), "%s@%s", cli_user(acptr)->username,
411                   ircd_ntoa(&cli_ip(acptr)));
412
413     if (!match(mask, namebuf)
414         || !match(mask, ipbuf)
415         || (ipmask_valid && ipmask_check(&cli_ip(acptr), &ipmask, ipmask_len)))
416       count++;
417   }
418
419   return count;
420 }
421
422 /** Count number of users with a realname matching \a mask.
423  * @param[in] mask Wildcard mask to match against realnames.
424  * @return Count of matching users.
425  */
426 static int
427 count_realnames(const char *mask)
428 {
429   struct Client *acptr;
430   int minlen;
431   int count;
432   char cmask[BUFSIZE];
433
434   count = 0;
435   matchcomp(cmask, &minlen, NULL, mask);
436   for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) {
437     if (!IsUser(acptr))
438       continue;
439     if (strlen(cli_info(acptr)) < minlen)
440       continue;
441     if (!matchexec(cli_info(acptr), cmask, minlen))
442       count++;
443   }
444   return count;
445 }
446
447 /** Create a new G-line and add it to global lists.
448  * \a userhost may be in one of four forms:
449  * \li A channel name, to add a BadChan.
450  * \li A string starting with $R and followed by a mask to match against their realname.
451  * \li A user\@IP mask (user\@ part optional) to create an IP-based ban.
452  * \li A user\@host mask (user\@ part optional) to create a hostname ban.
453  *
454  * @param[in] cptr Client that sent us the G-line.
455  * @param[in] sptr Client that originated the G-line.
456  * @param[in] userhost Text mask for the G-line.
457  * @param[in] reason Reason for G-line.
458  * @param[in] expire Expiration time of G-line.
459  * @param[in] lastmod Last modification time of G-line.
460  * @param[in] lifetime Lifetime of G-line.
461  * @param[in] flags Bitwise combination of GLINE_* flags.
462  * @return Zero or CPTR_KILLED, depending on whether \a sptr is suicidal.
463  */
464 int
465 gline_add(struct Client *cptr, struct Client *sptr, char *userhost,
466           char *reason, time_t expire, time_t lastmod, time_t lifetime,
467           unsigned int flags)
468 {
469   struct Gline *agline;
470   char uhmask[USERLEN + HOSTLEN + 2];
471   char *user, *host;
472   int tmp;
473
474   assert(0 != userhost);
475   assert(0 != reason);
476   assert(((flags & (GLINE_GLOBAL | GLINE_LOCAL)) == GLINE_GLOBAL) ||
477          ((flags & (GLINE_GLOBAL | GLINE_LOCAL)) == GLINE_LOCAL));
478
479   Debug((DEBUG_DEBUG, "gline_add(\"%s\", \"%s\", \"%s\", \"%s\", %Tu, %Tu "
480          "%Tu, 0x%04x)", cli_name(cptr), cli_name(sptr), userhost, reason,
481          expire, lastmod, lifetime, flags));
482
483   if (*userhost == '#' || *userhost == '&') {
484     if ((flags & GLINE_LOCAL) && !HasPriv(sptr, PRIV_LOCAL_BADCHAN))
485       return send_reply(sptr, ERR_NOPRIVILEGES);
486
487     flags |= GLINE_BADCHAN;
488     user = userhost;
489     host = NULL;
490   } else if (*userhost == '$') {
491     switch (userhost[1]) {
492       case 'R': flags |= GLINE_REALNAME; break;
493       default:
494         /* uh, what to do here? */
495         /* The answer, my dear Watson, is we throw a protocol_violation()
496            -- hikari */
497         if (IsServer(cptr))
498           return protocol_violation(sptr,"%s has been smoking the sweet leaf and sent me a whacky gline",cli_name(sptr));
499         sendto_opmask_butone(NULL, SNO_GLINE, "%s has been smoking the sweet leaf and sent me a whacky gline", cli_name(sptr));
500         return 0;
501     }
502     user = userhost;
503     host = NULL;
504     if (MyUser(sptr) || (IsUser(sptr) && flags & GLINE_LOCAL)) {
505       tmp = count_realnames(userhost + 2);
506       if ((tmp >= feature_int(FEAT_GLINEMAXUSERCOUNT))
507           && !(flags & GLINE_OPERFORCE))
508         return send_reply(sptr, ERR_TOOMANYUSERS, tmp);
509     }
510   } else {
511     canon_userhost(userhost, &user, &host, "*");
512     if (sizeof(uhmask) <
513         ircd_snprintf(0, uhmask, sizeof(uhmask), "%s@%s", user, host))
514       return send_reply(sptr, ERR_LONGMASK);
515     else if (MyUser(sptr) || (IsUser(sptr) && flags & GLINE_LOCAL)) {
516       switch (gline_checkmask(host)) {
517       case CHECK_OVERRIDABLE: /* oper overrided restriction */
518         if (flags & GLINE_OPERFORCE)
519           break;
520         /*FALLTHROUGH*/
521       case CHECK_REJECTED:
522         return send_reply(sptr, ERR_MASKTOOWIDE, uhmask);
523         break;
524       }
525
526       if ((tmp = count_users(uhmask, flags)) >=
527           feature_int(FEAT_GLINEMAXUSERCOUNT) && !(flags & GLINE_OPERFORCE))
528         return send_reply(sptr, ERR_TOOMANYUSERS, tmp);
529     }
530   }
531
532   /*
533    * You cannot set a negative (or zero) expire time, nor can you set an
534    * expiration time for greater than GLINE_MAX_EXPIRE, it will be
535    * set to GLINE_MAX_EXPIRE
536    */
537   if(expire <= CurrentTime) {
538     if(!IsServer(sptr) && MyConnect(sptr))
539       send_reply(sptr, ERR_BADEXPIRE, expire);
540     return 0;
541   }
542   else if(expire > CurrentTime + GLINE_MAX_EXPIRE) {
543     if(!IsServer(sptr) && MyConnect(sptr))
544       send_reply(sptr, ERR_CHANGEDEXPIRE, expire, CurrentTime + GLINE_MAX_EXPIRE);
545     expire = CurrentTime + GLINE_MAX_EXPIRE;
546   }
547
548   if (!lifetime) /* no lifetime set, use expiration time */
549     lifetime = expire;
550
551   /* lifetime is already an absolute timestamp */
552
553   /* Inform ops... */
554   sendto_opmask_butone(0, ircd_strncmp(reason, "AUTO", 4) ? SNO_GLINE :
555                        SNO_AUTO, "%s adding %s%s %s for %s%s%s, expiring at "
556                        "%Tu: %s",
557                        (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
558                          cli_name(sptr) :
559                          cli_name((cli_user(sptr))->server),
560                        (flags & GLINE_ACTIVE) ? "" : "deactivated ",
561                        (flags & GLINE_LOCAL) ? "local" : "global",
562                        (flags & GLINE_BADCHAN) ? "BADCHAN" : "GLINE", user,
563                        (flags & (GLINE_BADCHAN|GLINE_REALNAME)) ? "" : "@",
564                        (flags & (GLINE_BADCHAN|GLINE_REALNAME)) ? "" : host,
565                        expire + TSoffset, reason);
566
567   /* and log it */
568   log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE,
569             "%#C adding %s %s for %s%s%s, expiring at %Tu: %s", sptr,
570             flags & GLINE_LOCAL ? "local" : "global",
571             flags & GLINE_BADCHAN ? "BADCHAN" : "GLINE", user,
572             flags & (GLINE_BADCHAN|GLINE_REALNAME) ? "" : "@",
573             flags & (GLINE_BADCHAN|GLINE_REALNAME) ? "" : host,
574             expire + TSoffset, reason);
575
576   /* make the gline */
577   agline = make_gline(user, host, reason, expire, lastmod, lifetime, flags);
578
579   /* since we've disabled overlapped G-line checking, agline should
580    * never be NULL...
581    */
582   assert(agline);
583
584   gline_propagate(cptr, sptr, agline);
585
586   return do_gline(cptr, sptr, agline); /* knock off users if necessary */
587 }
588
589 /** Activate a currently inactive G-line.
590  * @param[in] cptr Peer that told us to activate the G-line.
591  * @param[in] sptr Client that originally thought it was a good idea.
592  * @param[in] gline G-line to activate.
593  * @param[in] lastmod New value for last modification timestamp.
594  * @param[in] flags 0 if the activation should be propagated, GLINE_LOCAL if not.
595  * @return Zero, unless \a sptr had a death wish (in which case CPTR_KILLED).
596  */
597 int
598 gline_activate(struct Client *cptr, struct Client *sptr, struct Gline *gline,
599                time_t lastmod, unsigned int flags)
600 {
601   unsigned int saveflags = 0;
602
603   assert(0 != gline);
604
605   saveflags = gline->gl_flags;
606
607   if (flags & GLINE_LOCAL)
608     gline->gl_flags &= ~GLINE_LDEACT;
609   else {
610     gline->gl_flags |= GLINE_ACTIVE;
611
612     if (gline->gl_lastmod) {
613       if (gline->gl_lastmod >= lastmod) /* force lastmod to increase */
614         gline->gl_lastmod++;
615       else
616         gline->gl_lastmod = lastmod;
617     }
618   }
619
620   if ((saveflags & GLINE_ACTMASK) == GLINE_ACTIVE)
621     return 0; /* was active to begin with */
622
623   /* Inform ops and log it */
624   sendto_opmask_butone(0, SNO_GLINE, "%s activating global %s for %s%s%s, "
625                        "expiring at %Tu: %s",
626                        (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
627                          cli_name(sptr) :
628                          cli_name((cli_user(sptr))->server),
629                        GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
630                        gline->gl_user, gline->gl_host ? "@" : "",
631                        gline->gl_host ? gline->gl_host : "",
632                        gline->gl_expire + TSoffset, gline->gl_reason);
633   
634   log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE,
635             "%#C activating global %s for %s%s%s, expiring at %Tu: %s", sptr,
636             GlineIsBadChan(gline) ? "BADCHAN" : "GLINE", gline->gl_user,
637             gline->gl_host ? "@" : "",
638             gline->gl_host ? gline->gl_host : "",
639             gline->gl_expire + TSoffset, gline->gl_reason);
640
641   if (!(flags & GLINE_LOCAL)) /* don't propagate local changes */
642     gline_propagate(cptr, sptr, gline);
643
644   return do_gline(cptr, sptr, gline);
645 }
646
647 /** Deactivate a G-line.
648  * @param[in] cptr Peer that gave us the message.
649  * @param[in] sptr Client that initiated the deactivation.
650  * @param[in] gline G-line to deactivate.
651  * @param[in] lastmod New value for G-line last modification timestamp.
652  * @param[in] flags GLINE_LOCAL to only deactivate locally, 0 to propagate.
653  * @return Zero.
654  */
655 int
656 gline_deactivate(struct Client *cptr, struct Client *sptr, struct Gline *gline,
657                  time_t lastmod, unsigned int flags)
658 {
659   unsigned int saveflags = 0;
660   char *msg;
661
662   assert(0 != gline);
663
664   saveflags = gline->gl_flags;
665
666   if (GlineIsLocal(gline))
667     msg = "removing local";
668   else if (!gline->gl_lastmod && !(flags & GLINE_LOCAL)) {
669     msg = "removing global";
670     gline->gl_flags &= ~GLINE_ACTIVE; /* propagate a -<mask> */
671   } else {
672     msg = "deactivating global";
673
674     if (flags & GLINE_LOCAL)
675       gline->gl_flags |= GLINE_LDEACT;
676     else {
677       gline->gl_flags &= ~GLINE_ACTIVE;
678
679       if (gline->gl_lastmod) {
680         if (gline->gl_lastmod >= lastmod)
681           gline->gl_lastmod++;
682         else
683           gline->gl_lastmod = lastmod;
684       }
685     }
686
687     if ((saveflags & GLINE_ACTMASK) != GLINE_ACTIVE)
688       return 0; /* was inactive to begin with */
689   }
690
691   /* Inform ops and log it */
692   sendto_opmask_butone(0, SNO_GLINE, "%s %s %s for %s%s%s, expiring at %Tu: "
693                        "%s",
694                        (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
695                          cli_name(sptr) :
696                          cli_name((cli_user(sptr))->server),
697                        msg, GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
698                        gline->gl_user, gline->gl_host ? "@" : "",
699                        gline->gl_host ? gline->gl_host : "",
700                        gline->gl_expire + TSoffset, gline->gl_reason);
701
702   log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE,
703             "%#C %s %s for %s%s%s, expiring at %Tu: %s", sptr, msg,
704             GlineIsBadChan(gline) ? "BADCHAN" : "GLINE", gline->gl_user,
705             gline->gl_host ? "@" : "",
706             gline->gl_host ? gline->gl_host : "",
707             gline->gl_expire + TSoffset, gline->gl_reason);
708
709   if (!(flags & GLINE_LOCAL)) /* don't propagate local changes */
710     gline_propagate(cptr, sptr, gline);
711
712   /* if it's a local gline or a Uworld gline (and not locally deactivated).. */
713   if (GlineIsLocal(gline) || (!gline->gl_lastmod && !(flags & GLINE_LOCAL)))
714     gline_free(gline); /* get rid of it */
715
716   return 0;
717 }
718
719 /** Modify a global G-line.
720  * @param[in] cptr Client that sent us the G-line modification.
721  * @param[in] sptr Client that originated the G-line modification.
722  * @param[in] gline G-line being modified.
723  * @param[in] action Resultant status of the G-line.
724  * @param[in] reason Reason for G-line.
725  * @param[in] expire Expiration time of G-line.
726  * @param[in] lastmod Last modification time of G-line.
727  * @param[in] lifetime Lifetime of G-line.
728  * @param[in] flags Bitwise combination of GLINE_* flags.
729  * @return Zero or CPTR_KILLED, depending on whether \a sptr is suicidal.
730  */
731 int
732 gline_modify(struct Client *cptr, struct Client *sptr, struct Gline *gline,
733              enum GlineAction action, char *reason, time_t expire,
734              time_t lastmod, time_t lifetime, unsigned int flags)
735 {
736   char buf[BUFSIZE], *op = "";
737   int pos = 0, disable_gl = 0;
738
739   assert(gline);
740   assert(!GlineIsLocal(gline));
741
742   Debug((DEBUG_DEBUG,  "gline_modify(\"%s\", \"%s\", \"%s%s%s\", %s, \"%s\", "
743          "%Tu, %Tu, %Tu, 0x%04x)", cli_name(cptr), cli_name(sptr),
744          gline->gl_user, gline->gl_host ? "@" : "",
745          gline->gl_host ? gline->gl_host : "",
746          action == GLINE_ACTIVATE ? "GLINE_ACTIVATE" :
747          (action == GLINE_DEACTIVATE ? "GLINE_DEACTIVATE" :
748           (action == GLINE_LOCAL_ACTIVATE ? "GLINE_LOCAL_ACTIVATE" :
749            (action == GLINE_LOCAL_DEACTIVATE ? "GLINE_LOCAL_DEACTIVATE" :
750             (action == GLINE_MODIFY ? "GLINE_MODIFY" : "<UNKNOWN>")))),
751          reason, expire, lastmod, lifetime, flags));
752
753   /* First, let's check lastmod... */
754   if (action != GLINE_LOCAL_ACTIVATE && action != GLINE_LOCAL_DEACTIVATE) {
755     if (GlineLastMod(gline) > lastmod) { /* we have a more recent version */
756       if (IsBurstOrBurstAck(cptr))
757         return 0; /* middle of a burst, it'll resync on its own */
758       return gline_resend(cptr, gline); /* resync the server */
759     } else if (GlineLastMod(gline) == lastmod)
760       return 0; /* we have that version of the G-line... */
761   }
762
763   /* All right, we know that there's a change of some sort.  What is it? */
764   /* first, check out the expiration time... */
765   if (flags & GLINE_EXPIRE) {
766     if (!(flags & GLINE_FORCE)) {
767       /* Allow to set times smaller than CurrentTime to allow the
768        * explicit deletion/deactivation of a gline.
769        */
770       if(expire > CurrentTime + GLINE_MAX_EXPIRE) {
771         if(!IsServer(sptr) && MyConnect(sptr))
772           send_reply(sptr, ERR_CHANGEDEXPIRE, expire, CurrentTime + GLINE_MAX_EXPIRE);
773         expire = CurrentTime + GLINE_MAX_EXPIRE;
774       }
775     }
776     if(expire <= CurrentTime) disable_gl = 1;
777 #ifdef OLD_OGN_IRCU_COMPAT
778     /* Compatibility issue:
779      * The ircu prior to ircu-2.10.12.10 has no lifetime, therefore, we force
780      * the lifetime to be the same as the expiration time here. This allows to
781      * remove glines by setting the expiration-time to a smaller value than CurrentTime.
782      * This has to be removed when the full ircu-2.10.12.10 gline features
783      * want to be used on the whole network.
784      */
785     lifetime = expire;
786     flags |= GLINE_LIFETIME;
787 #endif
788   } else {
789     flags &= ~GLINE_EXPIRE;
790     expire = 0;
791   }
792
793   /* Now check to see if there's any change... */
794   if ((flags & GLINE_EXPIRE) && expire == gline->gl_expire) {
795     flags &= ~GLINE_EXPIRE; /* no change to expiration time... */
796     expire = 0;
797   }
798
799   /* Next, check out lifetime--this one's a bit trickier... */
800   if (!(flags & GLINE_LIFETIME))
801     lifetime = gline->gl_lifetime; /* use G-line lifetime */
802
803   lifetime = IRCD_MAX(lifetime, expire); /* set lifetime to the max */
804
805 #ifdef OLD_OGN_IRCU_COMPAT
806   flags |= GLINE_LIFETIME;
807 #else
808   /* OK, let's see which is greater... */
809   if (lifetime > gline->gl_lifetime)
810     flags |= GLINE_LIFETIME; /* have to update lifetime */
811   else {
812     flags &= ~GLINE_LIFETIME; /* no change to lifetime */
813     lifetime = 0;
814   }
815 #endif
816
817   /* Finally, let's see if the reason needs to be updated */
818   if ((flags & GLINE_REASON) && reason &&
819       !ircd_strcmp(gline->gl_reason, reason))
820     flags &= ~GLINE_REASON; /* no changes to the reason */
821
822   /* OK, now let's take a look at the action... */
823   if ((action == GLINE_ACTIVATE && (gline->gl_flags & GLINE_ACTIVE)) ||
824       (action == GLINE_DEACTIVATE && !(gline->gl_flags & GLINE_ACTIVE)) ||
825       (action == GLINE_LOCAL_ACTIVATE &&
826        (gline->gl_state == GLOCAL_ACTIVATED)) ||
827       (action == GLINE_LOCAL_DEACTIVATE &&
828        (gline->gl_state == GLOCAL_DEACTIVATED)) ||
829       /* can't activate an expired G-line */
830       IRCD_MAX(gline->gl_expire, expire) <= CurrentTime)
831     action = GLINE_MODIFY; /* no activity state modifications */
832
833   Debug((DEBUG_DEBUG,  "About to perform changes; flags 0x%04x, action %s",
834          flags, action == GLINE_ACTIVATE ? "GLINE_ACTIVATE" :
835          (action == GLINE_DEACTIVATE ? "GLINE_DEACTIVATE" :
836           (action == GLINE_LOCAL_ACTIVATE ? "GLINE_LOCAL_ACTIVATE" :
837            (action == GLINE_LOCAL_DEACTIVATE ? "GLINE_LOCAL_DEACTIVATE" :
838             (action == GLINE_MODIFY ? "GLINE_MODIFY" : "<UNKNOWN>"))))));
839
840   /* If there are no changes to perform, do no changes */
841   if (!(flags & GLINE_UPDATE) && action == GLINE_MODIFY)
842     return 0;
843
844   /* Now we know what needs to be changed, so let's process the changes... */
845
846   /* Start by updating lastmod, if indicated... */
847   if (action != GLINE_LOCAL_ACTIVATE && action != GLINE_LOCAL_DEACTIVATE)
848     gline->gl_lastmod = lastmod;
849
850   /* Then move on to activity status changes... */
851   switch (action) {
852   case GLINE_ACTIVATE: /* Globally activating G-line */
853     gline->gl_flags |= GLINE_ACTIVE; /* make it active... */
854     gline->gl_state = GLOCAL_GLOBAL; /* reset local activity state */
855     pos += ircd_snprintf(0, buf, sizeof(buf), " globally activating G-line");
856     op = "+"; /* operation for G-line propagation */
857     break;
858
859   case GLINE_DEACTIVATE: /* Globally deactivating G-line */
860     gline->gl_flags &= ~GLINE_ACTIVE; /* make it inactive... */
861     gline->gl_state = GLOCAL_GLOBAL; /* reset local activity state */
862     pos += ircd_snprintf(0, buf, sizeof(buf), " globally deactivating G-line");
863     op = "-"; /* operation for G-line propagation */
864     break;
865
866   case GLINE_LOCAL_ACTIVATE: /* Locally activating G-line */
867     gline->gl_state = GLOCAL_ACTIVATED; /* make it locally active */
868     pos += ircd_snprintf(0, buf, sizeof(buf), " locally activating G-line");
869     break;
870
871   case GLINE_LOCAL_DEACTIVATE: /* Locally deactivating G-line */
872     gline->gl_state = GLOCAL_DEACTIVATED; /* make it locally inactive */
873     pos += ircd_snprintf(0, buf, sizeof(buf), " locally deactivating G-line");
874     break;
875
876   case GLINE_MODIFY: /* no change to activity status */
877     break;
878   }
879
880   /* Handle expiration changes... */
881   if (flags & GLINE_EXPIRE) {
882     gline->gl_expire = expire; /* save new expiration time */
883     if (pos < BUFSIZE)
884       pos += ircd_snprintf(0, buf + pos, sizeof(buf) - pos,
885                            "%s%s changing expiration time to %Tu",
886                            pos ? ";" : "",
887                            pos && !(flags & (GLINE_LIFETIME | GLINE_REASON)) ?
888                            " and" : "", expire);
889   }
890
891   /* Next, handle lifetime changes... */
892   if (flags & GLINE_LIFETIME) {
893     gline->gl_lifetime = lifetime; /* save new lifetime */
894     if (pos < BUFSIZE)
895       pos += ircd_snprintf(0, buf + pos, sizeof(buf) - pos,
896                            "%s%s extending record lifetime to %Tu",
897                            pos ? ";" : "", pos && !(flags & GLINE_REASON) ?
898                            " and" : "", lifetime);
899   }
900
901   /* Now, handle reason changes... */
902   if (flags & GLINE_REASON) {
903     MyFree(gline->gl_reason); /* release old reason */
904     DupString(gline->gl_reason, reason); /* store new reason */
905     if (pos < BUFSIZE)
906       pos += ircd_snprintf(0, buf + pos, sizeof(buf) - pos,
907                            "%s%s changing reason to \"%s\"",
908                            pos ? ";" : "", pos ? " and" : "", reason);
909   }
910
911   /* All right, inform ops... */
912   if(disable_gl) {
913     /* We send a special message if the expiration time has been set below CurrentTime.
914      * However, we additionally save the exact message in the log, so we have both information
915      * in there. But the ops don't need to be bothered twice.
916      */
917     sendto_opmask_butone(0, SNO_GLINE, "%s deleting global %s for %s%s%s",
918                        (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
919                        cli_name(sptr) : cli_name((cli_user(sptr))->server),
920                        GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
921                        gline->gl_user, gline->gl_host ? "@" : "",
922                        gline->gl_host ? gline->gl_host : "");
923     log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE,
924             "%#C deleting global %s for %s%s%s", sptr,
925             GlineIsBadChan(gline) ? "BADCHAN" : "GLINE", gline->gl_user,
926             gline->gl_host ? "@" : "", gline->gl_host ? gline->gl_host : "");
927   }
928   else {
929     sendto_opmask_butone(0, SNO_GLINE, "%s modifying global %s for %s%s%s:%s",
930                        (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
931                        cli_name(sptr) : cli_name((cli_user(sptr))->server),
932                        GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
933                        gline->gl_user, gline->gl_host ? "@" : "",
934                        gline->gl_host ? gline->gl_host : "", buf);
935   }
936
937   /* and log the change */
938   log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE,
939             "%#C modifying global %s for %s%s%s:%s", sptr,
940             GlineIsBadChan(gline) ? "BADCHAN" : "GLINE", gline->gl_user,
941             gline->gl_host ? "@" : "", gline->gl_host ? gline->gl_host : "",
942             buf);
943
944   /* We'll be simple for this release, but we can update this to change
945    * the propagation syntax on future updates
946    */
947 #ifdef OLD_OGN_IRCU_COMPAT
948   if (action != GLINE_LOCAL_ACTIVATE && action != GLINE_LOCAL_DEACTIVATE)
949     sendcmdto_serv_butone(sptr, CMD_GLINE, cptr,
950                           "* %s%s%s%s%s %Tu %Tu :%s",
951                           flags & GLINE_OPERFORCE ? "!" : "", op,
952                           gline->gl_user, gline->gl_host ? "@" : "",
953                           gline->gl_host ? gline->gl_host : "",
954                           gline->gl_expire, gline->gl_lastmod,
955                           /*gline->gl_lifetime,*/ gline->gl_reason);
956 #else
957   if (action != GLINE_LOCAL_ACTIVATE && action != GLINE_LOCAL_DEACTIVATE)
958     sendcmdto_serv_butone(sptr, CMD_GLINE, cptr,
959                           "* %s%s%s%s%s %Tu %Tu %Tu :%s",
960                           flags & GLINE_OPERFORCE ? "!" : "", op,
961                           gline->gl_user, gline->gl_host ? "@" : "",
962                           gline->gl_host ? gline->gl_host : "",
963                           gline->gl_expire, gline->gl_lastmod,
964                           gline->gl_lifetime, gline->gl_reason);
965 #endif
966
967   /* OK, let's do the G-line... */
968   if(disable_gl) return 0;
969   else return do_gline(cptr, sptr, gline);
970 }
971
972 /** Destroy a local G-line.
973  * @param[in] cptr Peer that gave us the message.
974  * @param[in] sptr Client that initiated the destruction.
975  * @param[in] gline G-line to destroy.
976  * @return Zero.
977  */
978 int
979 gline_destroy(struct Client *cptr, struct Client *sptr, struct Gline *gline)
980 {
981   assert(gline);
982   assert(GlineIsLocal(gline));
983
984   /* Inform ops and log it */
985   sendto_opmask_butone(0, SNO_GLINE, "%s removing local %s for %s%s%s",
986                        (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
987                        cli_name(sptr) : cli_name((cli_user(sptr))->server),
988                        GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
989                        gline->gl_user, gline->gl_host ? "@" : "",
990                        gline->gl_host ? gline->gl_host : "");
991   log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE,
992             "%#C removing local %s for %s%s%s", sptr,
993             GlineIsBadChan(gline) ? "BADCHAN" : "GLINE", gline->gl_user,
994             gline->gl_host ? "@" : "", gline->gl_host ? gline->gl_host : "");
995
996   gline_free(gline); /* get rid of the G-line */
997
998   return 0; /* convenience return */
999 }
1000
1001 /** Find a G-line for a particular mask, guided by certain flags.
1002  * Certain bits in \a flags are interpreted specially:
1003  * <dl>
1004  * <dt>GLINE_ANY</dt><dd>Search both BadChans and user G-lines.</dd>
1005  * <dt>GLINE_BADCHAN</dt><dd>Search BadChans.</dd>
1006  * <dt>GLINE_GLOBAL</dt><dd>Only match global G-lines.</dd>
1007  * <dt>GLINE_LOCAL</dt><dd>Only match local G-lines.</dd>
1008  * <dt>GLINE_LASTMOD</dt><dd>Only match G-lines with a last modification time.</dd>
1009  * <dt>GLINE_EXACT</dt><dd>Require an exact match of G-line mask.</dd>
1010  * <dt>anything else</dt><dd>Search user G-lines.</dd>
1011  * </dl>
1012  * @param[in] userhost Mask to search for.
1013  * @param[in] flags Bitwise combination of GLINE_* flags.
1014  * @return First matching G-line, or NULL if none are found.
1015  */
1016 struct Gline *
1017 gline_find(char *userhost, unsigned int flags)
1018 {
1019   struct Gline *gline = 0;
1020   struct Gline *sgline;
1021   char *user, *host, *t_uh;
1022
1023   if (flags & (GLINE_BADCHAN | GLINE_ANY)) {
1024     gliter(BadChanGlineList, gline, sgline) {
1025         if ((flags & (GlineIsLocal(gline) ? GLINE_GLOBAL : GLINE_LOCAL)) ||
1026           (flags & GLINE_LASTMOD && !gline->gl_lastmod))
1027         continue;
1028       else if ((flags & GLINE_EXACT ? ircd_strcmp(gline->gl_user, userhost) :
1029                 match(gline->gl_user, userhost)) == 0)
1030         return gline;
1031     }
1032   }
1033
1034   if ((flags & (GLINE_BADCHAN | GLINE_ANY)) == GLINE_BADCHAN ||
1035       *userhost == '#' || *userhost == '&')
1036     return 0;
1037
1038   DupString(t_uh, userhost);
1039   canon_userhost(t_uh, &user, &host, "*");
1040
1041   gliter(GlobalGlineList, gline, sgline) {
1042     if ((flags & (GlineIsLocal(gline) ? GLINE_GLOBAL : GLINE_LOCAL)) ||
1043         (flags & GLINE_LASTMOD && !gline->gl_lastmod))
1044       continue;
1045     else if (flags & GLINE_EXACT) {
1046       if (((gline->gl_host && host && ircd_strcmp(gline->gl_host, host) == 0)
1047            || (!gline->gl_host && !host)) &&
1048           (ircd_strcmp(gline->gl_user, user) == 0))
1049         break;
1050     } else {
1051       if (((gline->gl_host && host && match(gline->gl_host, host) == 0)
1052            || (!gline->gl_host && !host)) &&
1053           (match(gline->gl_user, user) == 0))
1054         break;
1055     }
1056   }
1057
1058   MyFree(t_uh);
1059
1060   return gline;
1061 }
1062
1063 /** Find a matching G-line for a user.
1064  * @param[in] cptr Client to compare against.
1065  * @param[in] flags Bitwise combination of GLINE_GLOBAL and/or
1066  * GLINE_LASTMOD to limit matches.
1067  * @return Matching G-line, or NULL if none are found.
1068  */
1069 struct Gline *
1070 gline_lookup(struct Client *cptr, unsigned int flags)
1071 {
1072   struct Gline *gline;
1073   struct Gline *sgline;
1074
1075   gliter(GlobalGlineList, gline, sgline) {
1076     if ((flags & GLINE_GLOBAL && gline->gl_flags & GLINE_LOCAL) ||
1077         (flags & GLINE_LASTMOD && !gline->gl_lastmod))
1078       continue;
1079
1080     if (GlineIsRealName(gline)) {
1081       Debug((DEBUG_DEBUG,"realname gline: '%s' '%s'",gline->gl_user,cli_info(cptr)));
1082       if (match(gline->gl_user+2, cli_info(cptr)) != 0)
1083         continue;
1084     }
1085     else {
1086       if (match(gline->gl_user, (cli_user(cptr))->username) != 0)
1087         continue;
1088
1089       if (GlineIsIpMask(gline)) {
1090         if (!ipmask_check(&cli_ip(cptr), &gline->gl_addr, gline->gl_bits))
1091           continue;
1092       }
1093       else {
1094         if (match(gline->gl_host, (cli_user(cptr))->realhost) != 0)
1095           continue;
1096       }
1097     }
1098     if (GlineIsActive(gline))
1099       return gline;
1100   }
1101   /*
1102    * No Glines matched
1103    */
1104   return 0;
1105 }
1106
1107 /** Delink and free a G-line.
1108  * @param[in] gline G-line to free.
1109  */
1110 void
1111 gline_free(struct Gline *gline)
1112 {
1113   assert(0 != gline);
1114
1115   *gline->gl_prev_p = gline->gl_next; /* squeeze this gline out */
1116   if (gline->gl_next)
1117     gline->gl_next->gl_prev_p = gline->gl_prev_p;
1118
1119   MyFree(gline->gl_user); /* free up the memory */
1120   if (gline->gl_host)
1121     MyFree(gline->gl_host);
1122   MyFree(gline->gl_reason);
1123   MyFree(gline);
1124 }
1125
1126 /** Burst all known global G-lines to another server.
1127  * @param[in] cptr Destination of burst.
1128  */
1129 void
1130 gline_burst(struct Client *cptr)
1131 {
1132   struct Gline *gline;
1133   struct Gline *sgline;
1134
1135 #ifdef OLD_OGN_IRCU_COMPAT
1136   gliter(GlobalGlineList, gline, sgline) {
1137     if (!GlineIsLocal(gline) && gline->gl_lastmod)
1138       sendcmdto_one(&me, CMD_GLINE, cptr, "* %c%s%s%s %Tu %Tu :%s",
1139                     GlineIsRemActive(gline) ? '+' : '-', gline->gl_user,
1140                     gline->gl_host ? "@" : "",
1141                     gline->gl_host ? gline->gl_host : "",
1142                     gline->gl_expire - CurrentTime, gline->gl_lastmod,
1143                     /*gline->gl_lifetime,*/ gline->gl_reason);
1144   }
1145   gliter(BadChanGlineList, gline, sgline) {
1146     if (!GlineIsLocal(gline) && gline->gl_lastmod)
1147       sendcmdto_one(&me, CMD_GLINE, cptr, "* %c%s %Tu %Tu :%s",
1148                     GlineIsRemActive(gline) ? '+' : '-', gline->gl_user,
1149                     gline->gl_expire - CurrentTime, gline->gl_lastmod,
1150                     /*gline->gl_lifetime,*/ gline->gl_reason);
1151   }
1152 #else
1153   gliter(GlobalGlineList, gline, sgline) {
1154     if (!GlineIsLocal(gline) && gline->gl_lastmod)
1155       sendcmdto_one(&me, CMD_GLINE, cptr, "* %c%s%s%s %Tu %Tu %Tu :%s",
1156                     GlineIsRemActive(gline) ? '+' : '-', gline->gl_user,
1157                     gline->gl_host ? "@" : "",
1158                     gline->gl_host ? gline->gl_host : "",
1159                     gline->gl_expire - CurrentTime, gline->gl_lastmod,
1160                     gline->gl_lifetime, gline->gl_reason);
1161   }
1162   gliter(BadChanGlineList, gline, sgline) {
1163     if (!GlineIsLocal(gline) && gline->gl_lastmod)
1164       sendcmdto_one(&me, CMD_GLINE, cptr, "* %c%s %Tu %Tu %Tu :%s",
1165                     GlineIsRemActive(gline) ? '+' : '-', gline->gl_user,
1166                     gline->gl_expire - CurrentTime, gline->gl_lastmod,
1167                     gline->gl_lifetime, gline->gl_reason);
1168   }
1169 #endif
1170 }
1171
1172 /** Send a G-line to another server.
1173  * @param[in] cptr Who to inform of the G-line.
1174  * @param[in] gline G-line to send.
1175  * @return Zero.
1176  */
1177 int
1178 gline_resend(struct Client *cptr, struct Gline *gline)
1179 {
1180   if (GlineIsLocal(gline) || !gline->gl_lastmod)
1181     return 0;
1182
1183 #ifdef OLD_OGN_IRCU_COMPAT
1184   sendcmdto_one(&me, CMD_GLINE, cptr, "* %c%s%s%s %Tu %Tu :%s",
1185                 GlineIsRemActive(gline) ? '+' : '-', gline->gl_user,
1186                 gline->gl_host ? "@" : "",
1187                 gline->gl_host ? gline->gl_host : "",
1188                 gline->gl_expire - CurrentTime, gline->gl_lastmod,
1189                 /*gline->gl_lifetime,*/ gline->gl_reason);
1190 #else
1191   sendcmdto_one(&me, CMD_GLINE, cptr, "* %c%s%s%s %Tu %Tu %Tu :%s",
1192                 GlineIsRemActive(gline) ? '+' : '-', gline->gl_user,
1193                 gline->gl_host ? "@" : "",
1194                 gline->gl_host ? gline->gl_host : "",
1195                 gline->gl_expire - CurrentTime, gline->gl_lastmod,
1196                 gline->gl_lifetime, gline->gl_reason);
1197 #endif
1198
1199   return 0;
1200 }
1201
1202 /** Display one or all G-lines to a user.
1203  * If \a userhost is not NULL, only send the first matching G-line.
1204  * Otherwise send the whole list.
1205  * @param[in] sptr User asking for G-line list.
1206  * @param[in] userhost G-line mask to search for (or NULL).
1207  * @return Zero.
1208  */
1209 int
1210 gline_list(struct Client *sptr, char *userhost)
1211 {
1212   struct Gline *gline;
1213   struct Gline *sgline;
1214
1215   if (userhost) {
1216     if (!(gline = gline_find(userhost, GLINE_ANY))) /* no such gline */
1217       return send_reply(sptr, ERR_NOSUCHGLINE, userhost);
1218
1219     /* send gline information along */
1220     send_reply(sptr, RPL_GLIST, gline->gl_user,
1221                gline->gl_host ? "@" : "",
1222                gline->gl_host ? gline->gl_host : "",
1223                gline->gl_expire + TSoffset, gline->gl_lastmod,
1224                gline->gl_lifetime + TSoffset,
1225                GlineIsLocal(gline) ? cli_name(&me) : "*",
1226                gline->gl_state == GLOCAL_ACTIVATED ? ">" :
1227                (gline->gl_state == GLOCAL_DEACTIVATED ? "<" : ""),
1228                GlineIsRemActive(gline) ? '+' : '-', gline->gl_reason);
1229   } else {
1230     gliter(GlobalGlineList, gline, sgline) {
1231       send_reply(sptr, RPL_GLIST, gline->gl_user,
1232                  gline->gl_host ? "@" : "",
1233                  gline->gl_host ? gline->gl_host : "",
1234                  gline->gl_expire + TSoffset, gline->gl_lastmod,
1235                  gline->gl_lifetime + TSoffset,
1236                  GlineIsLocal(gline) ? cli_name(&me) : "*",
1237                  gline->gl_state == GLOCAL_ACTIVATED ? ">" :
1238                  (gline->gl_state == GLOCAL_DEACTIVATED ? "<" : ""),
1239                  GlineIsRemActive(gline) ? '+' : '-', gline->gl_reason);
1240     }
1241
1242     gliter(BadChanGlineList, gline, sgline) {
1243       send_reply(sptr, RPL_GLIST, gline->gl_user, "", "",
1244                  gline->gl_expire + TSoffset, gline->gl_lastmod,
1245                  gline->gl_lifetime + TSoffset,
1246                  GlineIsLocal(gline) ? cli_name(&me) : "*",
1247                  gline->gl_state == GLOCAL_ACTIVATED ? ">" :
1248                  (gline->gl_state == GLOCAL_DEACTIVATED ? "<" : ""),
1249                  GlineIsRemActive(gline) ? '+' : '-', gline->gl_reason);
1250     }
1251   }
1252
1253   /* end of gline information */
1254   return send_reply(sptr, RPL_ENDOFGLIST);
1255 }
1256
1257 /** Statistics callback to list G-lines.
1258  * @param[in] sptr Client requesting statistics.
1259  * @param[in] sd Stats descriptor for request (ignored).
1260  * @param[in] param Extra parameter from user (ignored).
1261  */
1262 void
1263 gline_stats(struct Client *sptr, const struct StatDesc *sd,
1264             char *param)
1265 {
1266   struct Gline *gline;
1267   struct Gline *sgline;
1268
1269   gliter(GlobalGlineList, gline, sgline) {
1270     send_reply(sptr, RPL_STATSGLINE, 'G', gline->gl_user,
1271                gline->gl_host ? "@" : "",
1272                gline->gl_host ? gline->gl_host : "",
1273                gline->gl_expire + TSoffset, gline->gl_lastmod,
1274                gline->gl_lifetime + TSoffset,
1275                gline->gl_state == GLOCAL_ACTIVATED ? ">" :
1276                (gline->gl_state == GLOCAL_DEACTIVATED ? "<" : ""),
1277                GlineIsRemActive(gline) ? '+' : '-',
1278                gline->gl_reason);
1279   }
1280 }
1281
1282 /** Calculate memory used by G-lines.
1283  * @param[out] gl_size Number of bytes used by G-lines.
1284  * @return Number of G-lines in use.
1285  */
1286 int
1287 gline_memory_count(size_t *gl_size)
1288 {
1289   struct Gline *gline;
1290   unsigned int gl = 0;
1291
1292   for (gline = GlobalGlineList; gline; gline = gline->gl_next) {
1293     gl++;
1294     *gl_size += sizeof(struct Gline);
1295     *gl_size += gline->gl_user ? (strlen(gline->gl_user) + 1) : 0;
1296     *gl_size += gline->gl_host ? (strlen(gline->gl_host) + 1) : 0;
1297     *gl_size += gline->gl_reason ? (strlen(gline->gl_reason) + 1) : 0;
1298   }
1299
1300   for (gline = BadChanGlineList; gline; gline = gline->gl_next) {
1301     gl++;
1302     *gl_size += sizeof(struct Gline);
1303     *gl_size += gline->gl_user ? (strlen(gline->gl_user) + 1) : 0;
1304     *gl_size += gline->gl_host ? (strlen(gline->gl_host) + 1) : 0;
1305     *gl_size += gline->gl_reason ? (strlen(gline->gl_reason) + 1) : 0;
1306   }
1307
1308   return gl;
1309 }