2 * IRC - Internet Relay Chat, ircd/gline.c
3 * Copyright (C) 1990 Jarkko Oikarinen and
4 * University of Oulu, Finland
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)
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.
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.
21 * @brief Implementation of Gline manipulation functions.
22 * @version $Id: gline.c 1904 2009-02-09 00:03:34Z entrope $
31 #include "ircd_alloc.h"
32 #include "ircd_features.h"
34 #include "ircd_reply.h"
35 #include "ircd_snprintf.h"
36 #include "ircd_string.h"
50 /* #include <assert.h> -- Now using assert in ircd_log.h */
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 */
59 #define MASK_WILD_0 0x01 /**< Wildcards in the last position */
60 #define MASK_WILD_1 0x02 /**< Wildcards in the next-to-last position */
62 #define MASK_WILD_MASK 0x03 /**< Mask out the positional wildcards */
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 */
68 /** List of user G-lines. */
69 struct Gline* GlobalGlineList = 0;
70 /** List of BadChan G-lines. */
71 struct Gline* BadChanGlineList = 0;
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.
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.
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.
85 #ifdef OLD_OGN_IRCU_COMPAT
86 #define GL_EXPIRED(gl) ((gl)->gl_lifetime <= CurrentTime)
88 #define GL_EXPIRED(gl) ((gl)->gl_lifetime <= CurrentTime || (gl)->gl_expire <= CurrentTime)
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 */ \
97 /* Record has expired, so free the G-line */ \
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 */ \
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.
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.
117 canon_userhost(char *userhost, char **user_p, char **host_p, char *def_user)
121 if (*userhost == '$') {
127 if (!(tmp = strchr(userhost, '@'))) {
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.
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)
154 gline = (struct Gline *)MyMalloc(sizeof(struct Gline)); /* alloc memory */
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 */
164 if (flags & GLINE_BADCHAN) { /* set a BADCHAN gline */
165 DupString(gline->gl_user, user); /* first, remember channel */
166 gline->gl_host = NULL;
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;
174 DupString(gline->gl_user, user); /* remember them... */
176 DupString(gline->gl_host, host);
178 gline->gl_host = NULL;
180 if (*user != '$' && ipmask_parse(host, &gline->gl_addr, &gline->gl_bits))
181 gline->gl_flags |= GLINE_IPMASK;
183 gline->gl_next = GlobalGlineList; /* then link it into list */
184 gline->gl_prev_p = &GlobalGlineList;
186 GlobalGlineList->gl_prev_p = &gline->gl_next;
187 GlobalGlineList = gline;
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.
202 do_gline(struct Client *cptr, struct Client *sptr, struct Gline *gline)
204 struct Client *acptr;
205 int fd, retval = 0, tval;
206 struct Membership *member, *nmember;
207 struct Channel *chptr, *nchptr;
209 if (feature_bool(FEAT_DISABLE_GLINES))
210 return 0; /* G-lines are disabled */
212 if (!GlineIsActive(gline)) /* no action taken on inactive glines */
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);
228 if(!string_has_wildcards(GlineUser(gline))) break;
234 for (fd = HighestFd; fd >= 0; --fd) {
238 if ((acptr = LocalClientArray[fd])) {
239 if (!cli_user(acptr))
242 if (GlineIsRealName(gline)) { /* Realname Gline */
243 Debug((DEBUG_DEBUG,"Realname Gline: %s %s",(cli_info(acptr)),
245 if (match(gline->gl_user+2, cli_info(acptr)) != 0)
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)
253 if (GlineIsIpMask(gline)) {
254 if (!ipmask_check(&cli_ip(acptr), &gline->gl_addr, gline->gl_bits))
258 if (match(gline->gl_host, cli_sockhost(acptr)) != 0)
263 /* ok, here's one that got G-lined */
264 send_reply(acptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP, ":%s",
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));
271 /* and get rid of him */
272 if ((tval = exit_client_msg(cptr, acptr, &me, "G-lined (%s)",
274 retval = tval; /* retain killed status */
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.
290 gline_checkmask(char *mask)
292 unsigned int flags = MASK_IP;
293 unsigned int dots = 0;
294 unsigned int ipmask = 0;
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);
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 */
307 ipmask = strtoul(mask, &mask, 10);
309 /* sanity-check to date */
310 if (*mask || (flags & (MASK_WILDS | MASK_IP)) != MASK_IP)
311 return CHECK_REJECTED;
314 return CHECK_REJECTED;
318 if (dots != 3 || ipmask > 32)
319 return CHECK_REJECTED;
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! */
332 /* Sanity-check quads */
333 if (dots > 3 || (!(flags & MASK_WILDS) && dots < 3)) {
338 /* update bit count if necessary */
339 if ((flags & (MASK_IP | MASK_WILDS | MASK_HALT)) == MASK_IP)
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 */
348 /* Ok, it's approved; require override if it has wildcards, though */
349 return flags & MASK_WILDS ? CHECK_OVERRIDABLE : CHECK_APPROVED;
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.
359 gline_propagate(struct Client *cptr, struct Client *sptr, struct Gline *gline)
361 if (GlineIsLocal(gline))
364 assert(gline->gl_lastmod);
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);
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);
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.
391 count_users(char *mask, int flags)
393 struct irc_in_addr ipmask;
394 struct Client *acptr;
397 char namebuf[USERLEN + HOSTLEN + 2];
398 char ipbuf[USERLEN + SOCKIPLEN + 2];
399 unsigned char ipmask_len;
401 ipmask_valid = ipmask_parse(mask, &ipmask, &ipmask_len);
402 for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) {
405 if ((flags & GLINE_LOCAL) && !MyConnect(acptr))
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)));
413 if (!match(mask, namebuf)
414 || !match(mask, ipbuf)
415 || (ipmask_valid && ipmask_check(&cli_ip(acptr), &ipmask, ipmask_len)))
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.
427 count_realnames(const char *mask)
429 struct Client *acptr;
435 matchcomp(cmask, &minlen, NULL, mask);
436 for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) {
439 if (strlen(cli_info(acptr)) < minlen)
441 if (!matchexec(cli_info(acptr), cmask, minlen))
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.
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.
465 gline_add(struct Client *cptr, struct Client *sptr, char *userhost,
466 char *reason, time_t expire, time_t lastmod, time_t lifetime,
469 struct Gline *agline;
470 char uhmask[USERLEN + HOSTLEN + 2];
474 assert(0 != userhost);
476 assert(((flags & (GLINE_GLOBAL | GLINE_LOCAL)) == GLINE_GLOBAL) ||
477 ((flags & (GLINE_GLOBAL | GLINE_LOCAL)) == GLINE_LOCAL));
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));
483 if (*userhost == '#' || *userhost == '&') {
484 if ((flags & GLINE_LOCAL) && !HasPriv(sptr, PRIV_LOCAL_BADCHAN))
485 return send_reply(sptr, ERR_NOPRIVILEGES);
487 flags |= GLINE_BADCHAN;
490 } else if (*userhost == '$') {
491 switch (userhost[1]) {
492 case 'R': flags |= GLINE_REALNAME; break;
494 /* uh, what to do here? */
495 /* The answer, my dear Watson, is we throw a protocol_violation()
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));
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);
511 canon_userhost(userhost, &user, &host, "*");
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)
522 return send_reply(sptr, ERR_MASKTOOWIDE, uhmask);
526 if ((tmp = count_users(uhmask, flags)) >=
527 feature_int(FEAT_GLINEMAXUSERCOUNT) && !(flags & GLINE_OPERFORCE))
528 return send_reply(sptr, ERR_TOOMANYUSERS, tmp);
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
537 if(expire <= CurrentTime) {
538 if(!IsServer(sptr) && MyConnect(sptr))
539 send_reply(sptr, ERR_BADEXPIRE, expire);
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;
548 if (!lifetime) /* no lifetime set, use expiration time */
551 /* lifetime is already an absolute timestamp */
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 "
557 (feature_bool(FEAT_HIS_SNOTICES) || IsServer(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);
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);
577 agline = make_gline(user, host, reason, expire, lastmod, lifetime, flags);
579 /* since we've disabled overlapped G-line checking, agline should
584 gline_propagate(cptr, sptr, agline);
586 return do_gline(cptr, sptr, agline); /* knock off users if necessary */
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).
598 gline_activate(struct Client *cptr, struct Client *sptr, struct Gline *gline,
599 time_t lastmod, unsigned int flags)
601 unsigned int saveflags = 0;
605 saveflags = gline->gl_flags;
607 if (flags & GLINE_LOCAL)
608 gline->gl_flags &= ~GLINE_LDEACT;
610 gline->gl_flags |= GLINE_ACTIVE;
612 if (gline->gl_lastmod) {
613 if (gline->gl_lastmod >= lastmod) /* force lastmod to increase */
616 gline->gl_lastmod = lastmod;
620 if ((saveflags & GLINE_ACTMASK) == GLINE_ACTIVE)
621 return 0; /* was active to begin with */
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)) ?
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);
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);
641 if (!(flags & GLINE_LOCAL)) /* don't propagate local changes */
642 gline_propagate(cptr, sptr, gline);
644 return do_gline(cptr, sptr, gline);
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.
656 gline_deactivate(struct Client *cptr, struct Client *sptr, struct Gline *gline,
657 time_t lastmod, unsigned int flags)
659 unsigned int saveflags = 0;
664 saveflags = gline->gl_flags;
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> */
672 msg = "deactivating global";
674 if (flags & GLINE_LOCAL)
675 gline->gl_flags |= GLINE_LDEACT;
677 gline->gl_flags &= ~GLINE_ACTIVE;
679 if (gline->gl_lastmod) {
680 if (gline->gl_lastmod >= lastmod)
683 gline->gl_lastmod = lastmod;
687 if ((saveflags & GLINE_ACTMASK) != GLINE_ACTIVE)
688 return 0; /* was inactive to begin with */
691 /* Inform ops and log it */
692 sendto_opmask_butone(0, SNO_GLINE, "%s %s %s for %s%s%s, expiring at %Tu: "
694 (feature_bool(FEAT_HIS_SNOTICES) || IsServer(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);
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);
709 if (!(flags & GLINE_LOCAL)) /* don't propagate local changes */
710 gline_propagate(cptr, sptr, gline);
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 */
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.
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)
736 char buf[BUFSIZE], *op = "";
737 int pos = 0, disable_gl = 0;
740 assert(!GlineIsLocal(gline));
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));
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... */
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.
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;
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.
786 flags |= GLINE_LIFETIME;
789 flags &= ~GLINE_EXPIRE;
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... */
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 */
803 lifetime = IRCD_MAX(lifetime, expire); /* set lifetime to the max */
805 #ifdef OLD_OGN_IRCU_COMPAT
806 flags |= GLINE_LIFETIME;
808 /* OK, let's see which is greater... */
809 if (lifetime > gline->gl_lifetime)
810 flags |= GLINE_LIFETIME; /* have to update lifetime */
812 flags &= ~GLINE_LIFETIME; /* no change to lifetime */
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 */
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 */
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>"))))));
840 /* If there are no changes to perform, do no changes */
841 if (!(flags & GLINE_UPDATE) && action == GLINE_MODIFY)
844 /* Now we know what needs to be changed, so let's process the changes... */
846 /* Start by updating lastmod, if indicated... */
847 if (action != GLINE_LOCAL_ACTIVATE && action != GLINE_LOCAL_DEACTIVATE)
848 gline->gl_lastmod = lastmod;
850 /* Then move on to activity status changes... */
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 */
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 */
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");
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");
876 case GLINE_MODIFY: /* no change to activity status */
880 /* Handle expiration changes... */
881 if (flags & GLINE_EXPIRE) {
882 gline->gl_expire = expire; /* save new expiration time */
884 pos += ircd_snprintf(0, buf + pos, sizeof(buf) - pos,
885 "%s%s changing expiration time to %Tu",
887 pos && !(flags & (GLINE_LIFETIME | GLINE_REASON)) ?
888 " and" : "", expire);
891 /* Next, handle lifetime changes... */
892 if (flags & GLINE_LIFETIME) {
893 gline->gl_lifetime = lifetime; /* save new lifetime */
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);
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 */
906 pos += ircd_snprintf(0, buf + pos, sizeof(buf) - pos,
907 "%s%s changing reason to \"%s\"",
908 pos ? ";" : "", pos ? " and" : "", reason);
911 /* All right, inform ops... */
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.
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 : "");
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);
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 : "",
944 /* We'll be simple for this release, but we can update this to change
945 * the propagation syntax on future updates
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);
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);
967 /* OK, let's do the G-line... */
968 if(disable_gl) return 0;
969 else return do_gline(cptr, sptr, gline);
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.
979 gline_destroy(struct Client *cptr, struct Client *sptr, struct Gline *gline)
982 assert(GlineIsLocal(gline));
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 : "");
996 gline_free(gline); /* get rid of the G-line */
998 return 0; /* convenience return */
1001 /** Find a G-line for a particular mask, guided by certain flags.
1002 * Certain bits in \a flags are interpreted specially:
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>
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.
1017 gline_find(char *userhost, unsigned int flags)
1019 struct Gline *gline = 0;
1020 struct Gline *sgline;
1021 char *user, *host, *t_uh;
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))
1028 else if ((flags & GLINE_EXACT ? ircd_strcmp(gline->gl_user, userhost) :
1029 match(gline->gl_user, userhost)) == 0)
1034 if ((flags & (GLINE_BADCHAN | GLINE_ANY)) == GLINE_BADCHAN ||
1035 *userhost == '#' || *userhost == '&')
1038 DupString(t_uh, userhost);
1039 canon_userhost(t_uh, &user, &host, "*");
1041 gliter(GlobalGlineList, gline, sgline) {
1042 if ((flags & (GlineIsLocal(gline) ? GLINE_GLOBAL : GLINE_LOCAL)) ||
1043 (flags & GLINE_LASTMOD && !gline->gl_lastmod))
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))
1051 if (((gline->gl_host && host && match(gline->gl_host, host) == 0)
1052 || (!gline->gl_host && !host)) &&
1053 (match(gline->gl_user, user) == 0))
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.
1070 gline_lookup(struct Client *cptr, unsigned int flags)
1072 struct Gline *gline;
1073 struct Gline *sgline;
1075 gliter(GlobalGlineList, gline, sgline) {
1076 if ((flags & GLINE_GLOBAL && gline->gl_flags & GLINE_LOCAL) ||
1077 (flags & GLINE_LASTMOD && !gline->gl_lastmod))
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)
1086 if (match(gline->gl_user, (cli_user(cptr))->username) != 0)
1089 if (GlineIsIpMask(gline)) {
1090 if (!ipmask_check(&cli_ip(cptr), &gline->gl_addr, gline->gl_bits))
1094 if (match(gline->gl_host, (cli_user(cptr))->realhost) != 0)
1098 if (GlineIsActive(gline))
1107 /** Delink and free a G-line.
1108 * @param[in] gline G-line to free.
1111 gline_free(struct Gline *gline)
1115 *gline->gl_prev_p = gline->gl_next; /* squeeze this gline out */
1117 gline->gl_next->gl_prev_p = gline->gl_prev_p;
1119 MyFree(gline->gl_user); /* free up the memory */
1121 MyFree(gline->gl_host);
1122 MyFree(gline->gl_reason);
1126 /** Burst all known global G-lines to another server.
1127 * @param[in] cptr Destination of burst.
1130 gline_burst(struct Client *cptr)
1132 struct Gline *gline;
1133 struct Gline *sgline;
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);
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);
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);
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);
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.
1178 gline_resend(struct Client *cptr, struct Gline *gline)
1180 if (GlineIsLocal(gline) || !gline->gl_lastmod)
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);
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);
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).
1210 gline_list(struct Client *sptr, char *userhost)
1212 struct Gline *gline;
1213 struct Gline *sgline;
1216 if (!(gline = gline_find(userhost, GLINE_ANY))) /* no such gline */
1217 return send_reply(sptr, ERR_NOSUCHGLINE, userhost);
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);
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);
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);
1253 /* end of gline information */
1254 return send_reply(sptr, RPL_ENDOFGLIST);
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).
1263 gline_stats(struct Client *sptr, const struct StatDesc *sd,
1266 struct Gline *gline;
1267 struct Gline *sgline;
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) ? '+' : '-',
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.
1287 gline_memory_count(size_t *gl_size)
1289 struct Gline *gline;
1290 unsigned int gl = 0;
1292 for (gline = GlobalGlineList; gline; gline = gline->gl_next) {
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;
1300 for (gline = BadChanGlineList; gline; gline = gline->gl_next) {
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;