]>
Commit | Line | Data |
---|---|---|
189935b1 | 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,v 1.61 2005/09/22 20:42:21 entrope Exp $ | |
23 | */ | |
24 | #include "config.h" | |
25 | ||
26 | #include "gline.h" | |
8ebc3d85 | 27 | #include "channel.h" |
189935b1 | 28 | #include "client.h" |
29 | #include "ircd.h" | |
30 | #include "ircd_alloc.h" | |
31 | #include "ircd_features.h" | |
32 | #include "ircd_log.h" | |
33 | #include "ircd_reply.h" | |
34 | #include "ircd_snprintf.h" | |
35 | #include "ircd_string.h" | |
36 | #include "match.h" | |
37 | #include "numeric.h" | |
38 | #include "s_bsd.h" | |
39 | #include "s_debug.h" | |
40 | #include "s_misc.h" | |
41 | #include "s_stats.h" | |
42 | #include "send.h" | |
43 | #include "struct.h" | |
44 | #include "msg.h" | |
45 | #include "numnicks.h" | |
46 | #include "numeric.h" | |
47 | #include "whocmds.h" | |
8ebc3d85 | 48 | #include "hash.h" |
189935b1 | 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 | /** Find canonical user and host for a string. | |
74 | * If \a userhost starts with '$', assign \a userhost to *user_p and NULL to *host_p. | |
75 | * Otherwise, if \a userhost contains '@', assign the earlier part of it to *user_p and the rest to *host_p. | |
76 | * Otherwise, assign \a def_user to *user_p and \a userhost to *host_p. | |
77 | * | |
78 | * @param[in] userhost Input string from user. | |
98de26a3 | 79 | * @param[out] nick_p Gets pointer to nick part of hostmask. |
189935b1 | 80 | * @param[out] user_p Gets pointer to user (or channel/realname) part of hostmask. |
81 | * @param[out] host_p Gets point to host part of hostmask (may be assigned NULL). | |
82 | * @param[in] def_user Default value for user part. | |
83 | */ | |
84 | static void | |
98de26a3 | 85 | canon_userhost(char *userhost, char **nick_p, char **user_p, char **host_p, char *def_user) |
189935b1 | 86 | { |
98de26a3 | 87 | char *tmp, *s; |
189935b1 | 88 | |
89 | if (*userhost == '$') { | |
90 | *user_p = userhost; | |
91 | *host_p = NULL; | |
98de26a3 | 92 | *nick_p = NULL; |
189935b1 | 93 | return; |
94 | } | |
95 | ||
98de26a3 | 96 | if ((tmp = strchr(userhost, '!'))) { |
97 | *nick_p = userhost; | |
189935b1 | 98 | *(tmp++) = '\0'; |
98de26a3 | 99 | } else { |
100 | *nick_p = def_user; | |
101 | tmp = userhost; | |
102 | } | |
103 | ||
104 | if (!(s = strchr(tmp, '@'))) { | |
105 | *user_p = def_user; | |
189935b1 | 106 | *host_p = tmp; |
98de26a3 | 107 | } else { |
108 | *user_p = tmp; | |
109 | *(s++) = '\0'; | |
110 | *host_p = s; | |
189935b1 | 111 | } |
112 | } | |
113 | ||
114 | /** Create a Gline structure. | |
98de26a3 | 115 | * @param[in] nick Nick part of mask. |
189935b1 | 116 | * @param[in] user User part of mask. |
117 | * @param[in] host Host part of mask (NULL if not applicable). | |
118 | * @param[in] reason Reason for G-line. | |
119 | * @param[in] expire Expiration timestamp. | |
120 | * @param[in] lastmod Last modification timestamp. | |
121 | * @param[in] flags Bitwise combination of GLINE_* bits. | |
122 | * @return Newly allocated G-line. | |
123 | */ | |
124 | static struct Gline * | |
98de26a3 | 125 | make_gline(char *nick, char *user, char *host, char *reason, time_t expire, time_t lastmod, |
189935b1 | 126 | unsigned int flags) |
127 | { | |
128 | struct Gline *gline, *sgline, *after = 0; | |
129 | ||
98de26a3 | 130 | if (!(flags & (GLINE_BADCHAN|GLINE_REALNAME))) { /* search for overlapping glines first */ |
189935b1 | 131 | |
132 | for (gline = GlobalGlineList; gline; gline = sgline) { | |
133 | sgline = gline->gl_next; | |
134 | ||
135 | if (gline->gl_expire <= CurrentTime) | |
136 | gline_free(gline); | |
137 | else if (((gline->gl_flags & GLINE_LOCAL) != (flags & GLINE_LOCAL)) || | |
138 | (gline->gl_host && !host) || (!gline->gl_host && host)) | |
139 | continue; | |
98de26a3 | 140 | else if (!mmatch(gline->gl_nick, nick) && /* gline contains new mask */ |
141 | !mmatch(gline->gl_user, user) | |
189935b1 | 142 | && (gline->gl_host == NULL || !mmatch(gline->gl_host, host))) { |
143 | if (expire <= gline->gl_expire) /* will expire before wider gline */ | |
144 | return 0; | |
145 | else | |
146 | after = gline; /* stick new gline after this one */ | |
98de26a3 | 147 | } else if (!mmatch(nick, gline->gl_nick) && /* new mask contains gline */ |
148 | !mmatch(user, gline->gl_user) | |
189935b1 | 149 | && (gline->gl_host==NULL || !mmatch(host, gline->gl_host)) |
150 | && gline->gl_expire <= expire) /* old expires before new */ | |
151 | gline_free(gline); /* save some memory */ | |
152 | } | |
153 | } | |
154 | ||
155 | gline = (struct Gline *)MyMalloc(sizeof(struct Gline)); /* alloc memory */ | |
156 | assert(0 != gline); | |
157 | ||
158 | DupString(gline->gl_reason, reason); /* initialize gline... */ | |
159 | gline->gl_expire = expire; | |
160 | gline->gl_lastmod = lastmod; | |
161 | gline->gl_flags = flags & GLINE_MASK; | |
162 | ||
163 | if (flags & GLINE_BADCHAN) { /* set a BADCHAN gline */ | |
164 | DupString(gline->gl_user, user); /* first, remember channel */ | |
165 | gline->gl_host = 0; | |
166 | ||
167 | gline->gl_next = BadChanGlineList; /* then link it into list */ | |
168 | gline->gl_prev_p = &BadChanGlineList; | |
169 | if (BadChanGlineList) | |
170 | BadChanGlineList->gl_prev_p = &gline->gl_next; | |
171 | BadChanGlineList = gline; | |
172 | } else { | |
98de26a3 | 173 | if (*user!='$') |
174 | DupString(gline->gl_nick, nick); /* remember them... */ | |
175 | else | |
176 | gline->gl_nick = 0; | |
177 | DupString(gline->gl_user, user); | |
189935b1 | 178 | if (*user != '$') |
179 | DupString(gline->gl_host, host); | |
180 | else | |
181 | gline->gl_host = NULL; | |
182 | ||
183 | if (*user != '$' && ipmask_parse(host, &gline->gl_addr, &gline->gl_bits)) | |
184 | gline->gl_flags |= GLINE_IPMASK; | |
185 | ||
186 | if (after) { | |
187 | gline->gl_next = after->gl_next; | |
188 | gline->gl_prev_p = &after->gl_next; | |
189 | if (after->gl_next) | |
190 | after->gl_next->gl_prev_p = &gline->gl_next; | |
191 | after->gl_next = gline; | |
192 | } else { | |
193 | gline->gl_next = GlobalGlineList; /* then link it into list */ | |
194 | gline->gl_prev_p = &GlobalGlineList; | |
195 | if (GlobalGlineList) | |
196 | GlobalGlineList->gl_prev_p = &gline->gl_next; | |
197 | GlobalGlineList = gline; | |
198 | } | |
199 | } | |
200 | ||
201 | return gline; | |
202 | } | |
203 | ||
204 | /** Check local clients against a new G-line. | |
8ebc3d85 | 205 | * If the G-line is inactive, return immediately. |
206 | * Otherwise, if any users match it, disconnect them or kick them if the G-line is a BADCHAN. | |
189935b1 | 207 | * @param[in] cptr Peer connect that sent the G-line. |
208 | * @param[in] sptr Client that originated the G-line. | |
209 | * @param[in] gline New G-line to check. | |
210 | * @return Zero, unless \a sptr G-lined himself, in which case CPTR_KILLED. | |
211 | */ | |
212 | static int | |
213 | do_gline(struct Client *cptr, struct Client *sptr, struct Gline *gline) | |
214 | { | |
215 | struct Client *acptr; | |
216 | int fd, retval = 0, tval; | |
217 | ||
189935b1 | 218 | if (!GlineIsActive(gline)) /* no action taken on inactive glines */ |
219 | return 0; | |
220 | ||
8ebc3d85 | 221 | if (GlineIsBadChan(gline)) { |
222 | /* Handle BADCHAN gline */ | |
223 | struct Channel *chptr,*nchptr; | |
224 | struct Membership *member,*nmember; | |
189935b1 | 225 | |
8ebc3d85 | 226 | if (string_has_wildcards(gline->gl_user)) { |
227 | for(chptr=GlobalChannelList;chptr;chptr=nchptr) { | |
228 | nchptr=chptr->next; | |
229 | if (match(gline->gl_user, chptr->chname)) | |
230 | continue; | |
231 | for (member=chptr->members;member;member=nmember) { | |
232 | nmember=member->next_member; | |
233 | if (!MyUser(member->user) || IsZombie(member) || IsAnOper(member->user)) | |
189935b1 | 234 | continue; |
8ebc3d85 | 235 | sendcmdto_serv_butone(&me, CMD_KICK, NULL, "%H %C :Badchanneled (%s)", chptr, member->user, gline->gl_reason); |
236 | sendcmdto_channel_butserv_butone(&me, CMD_KICK, chptr, NULL, 0, "%H %C :Badchanneled (%s)", chptr, member->user, gline->gl_reason); | |
237 | make_zombie(member, member->user, &me, &me, chptr); | |
238 | retval=1; | |
189935b1 | 239 | } |
8ebc3d85 | 240 | } |
241 | } else { | |
242 | if ((chptr=FindChannel(gline->gl_user))) { | |
243 | for (member=chptr->members;member;member=nmember) { | |
244 | nmember=member->next_member; | |
245 | if (!MyUser(member->user) || IsZombie(member) || IsAnOper(member->user)) | |
189935b1 | 246 | continue; |
8ebc3d85 | 247 | sendcmdto_serv_butone(&me, CMD_KICK, NULL, "%H %C :Badchanneled (%s)", chptr, member->user, gline->gl_reason); |
248 | sendcmdto_channel_butserv_butone(&me, CMD_KICK, chptr, NULL, 0, "%H %C :Badchanneled (%s)", chptr, member->user, gline->gl_reason); | |
249 | make_zombie(member, member->user, &me, &me, chptr); | |
250 | retval=1; | |
189935b1 | 251 | } |
252 | } | |
8ebc3d85 | 253 | } |
254 | } else { | |
255 | for (fd = HighestFd; fd >= 0; --fd) { | |
256 | /* | |
257 | * get the users! | |
258 | */ | |
259 | if ((acptr = LocalClientArray[fd])) { | |
260 | if (!cli_user(acptr)) | |
261 | continue; | |
189935b1 | 262 | |
8ebc3d85 | 263 | if (GlineIsRealName(gline)) { /* Realname Gline */ |
264 | Debug((DEBUG_DEBUG,"Realname Gline: %s %s",(cli_info(acptr)), | |
265 | gline->gl_user+2)); | |
266 | if (match(gline->gl_user+2, cli_info(acptr)) != 0) | |
267 | continue; | |
268 | Debug((DEBUG_DEBUG,"Matched!")); | |
269 | } else { /* Host/IP gline */ | |
98de26a3 | 270 | if (cli_name(acptr) && |
271 | match(gline->gl_nick, cli_name(acptr)) !=0) | |
272 | continue; | |
273 | ||
8ebc3d85 | 274 | if (cli_user(acptr)->username && |
275 | match(gline->gl_user, (cli_user(acptr))->realusername) != 0) | |
276 | continue; | |
189935b1 | 277 | |
8ebc3d85 | 278 | if (GlineIsIpMask(gline)) { |
279 | if (!ipmask_check(&cli_ip(acptr), &gline->gl_addr, gline->gl_bits)) | |
280 | continue; | |
281 | } | |
282 | else { | |
283 | if (match(gline->gl_host, cli_sockhost(acptr)) != 0) | |
284 | continue; | |
285 | } | |
286 | } | |
189935b1 | 287 | |
8ebc3d85 | 288 | /* ok, here's one that got G-lined */ |
289 | send_reply(acptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP, ":%s", | |
290 | gline->gl_reason); | |
291 | ||
292 | /* let the ops know about it */ | |
293 | sendto_opmask_butone(0, SNO_GLINE, "G-line active for %s", | |
294 | get_client_name(acptr, SHOW_IP)); | |
295 | ||
296 | /* and get rid of him */ | |
297 | /* Asuka - Reimplement HEAD_IN_SAND_GLINE from Lain */ | |
298 | if ((tval = exit_client_msg(cptr, acptr, &me, | |
299 | feature_bool(FEAT_HIS_GLINE) ? "G-lined" : "G-lined (%s)", gline->gl_reason))) | |
300 | retval = tval; /* retain killed status */ | |
301 | } | |
189935b1 | 302 | } |
303 | } | |
304 | return retval; | |
305 | } | |
306 | ||
307 | /** | |
308 | * Implements the mask checking applied to local G-lines. | |
309 | * Basically, host masks must have a minimum of two non-wild domain | |
310 | * fields, and IP masks must have a minimum of 16 bits. If the mask | |
311 | * has even one wild-card, OVERRIDABLE is returned, assuming the other | |
312 | * check doesn't fail. | |
313 | * @param[in] mask G-line mask to check. | |
314 | * @return One of CHECK_REJECTED, CHECK_OVERRIDABLE, or CHECK_APPROVED. | |
315 | */ | |
316 | static int | |
317 | gline_checkmask(char *mask) | |
318 | { | |
319 | unsigned int flags = MASK_IP; | |
320 | unsigned int dots = 0; | |
321 | unsigned int ipmask = 0; | |
322 | ||
323 | for (; *mask; mask++) { /* go through given mask */ | |
324 | if (*mask == '.') { /* it's a separator; advance positional wilds */ | |
325 | flags = (flags & ~MASK_WILD_MASK) | ((flags << 1) & MASK_WILD_MASK); | |
326 | dots++; | |
327 | ||
328 | if ((flags & (MASK_IP | MASK_WILDS)) == MASK_IP) | |
329 | ipmask += 8; /* It's an IP with no wilds, count bits */ | |
330 | } else if (*mask == '*' || *mask == '?') | |
331 | flags |= MASK_WILD_0 | MASK_WILDS; /* found a wildcard */ | |
332 | else if (*mask == '/') { /* n.n.n.n/n notation; parse bit specifier */ | |
333 | ++mask; | |
334 | ipmask = strtoul(mask, &mask, 10); | |
335 | ||
336 | /* sanity-check to date */ | |
337 | if (*mask || (flags & (MASK_WILDS | MASK_IP)) != MASK_IP) | |
338 | return CHECK_REJECTED; | |
339 | if (!dots) { | |
340 | if (ipmask > 128) | |
341 | return CHECK_REJECTED; | |
342 | if (ipmask < 128) | |
343 | flags |= MASK_WILDS; | |
344 | } else { | |
345 | if (dots != 3 || ipmask > 32) | |
346 | return CHECK_REJECTED; | |
347 | if (ipmask < 32) | |
348 | flags |= MASK_WILDS; | |
349 | } | |
350 | ||
351 | flags |= MASK_HALT; /* Halt the ipmask calculation */ | |
352 | break; /* get out of the loop */ | |
353 | } else if (!IsIP6Char(*mask)) { | |
354 | flags &= ~MASK_IP; /* not an IP anymore! */ | |
355 | ipmask = 0; | |
356 | } | |
357 | } | |
358 | ||
359 | /* Sanity-check quads */ | |
360 | if (dots > 3 || (!(flags & MASK_WILDS) && dots < 3)) { | |
361 | flags &= ~MASK_IP; | |
362 | ipmask = 0; | |
363 | } | |
364 | ||
365 | /* update bit count if necessary */ | |
366 | if ((flags & (MASK_IP | MASK_WILDS | MASK_HALT)) == MASK_IP) | |
367 | ipmask += 8; | |
368 | ||
369 | /* Check to see that it's not too wide of a mask */ | |
370 | if (flags & MASK_WILDS && | |
371 | ((!(flags & MASK_IP) && (dots < 2 || flags & MASK_WILD_MASK)) || | |
372 | (flags & MASK_IP && ipmask < 16))) | |
373 | return CHECK_REJECTED; /* to wide, reject */ | |
374 | ||
375 | /* Ok, it's approved; require override if it has wildcards, though */ | |
376 | return flags & MASK_WILDS ? CHECK_OVERRIDABLE : CHECK_APPROVED; | |
377 | } | |
378 | ||
379 | /** Forward a G-line to other servers. | |
380 | * @param[in] cptr Client that sent us the G-line. | |
381 | * @param[in] sptr Client that originated the G-line. | |
382 | * @param[in] gline G-line to forward. | |
383 | * @return Zero. | |
384 | */ | |
385 | int | |
386 | gline_propagate(struct Client *cptr, struct Client *sptr, struct Gline *gline) | |
387 | { | |
388 | if (GlineIsLocal(gline) || (IsUser(sptr) && !gline->gl_lastmod)) | |
389 | return 0; | |
390 | ||
391 | if (gline->gl_lastmod) | |
98de26a3 | 392 | sendcmdto_serv_butone(sptr, CMD_GLINE, cptr, "* %c%s%s%s%s%s %Tu %Tu :%s", |
393 | GlineIsRemActive(gline) ? '+' : '-', | |
394 | GlineIsBadChan(gline)|GlineIsRealName(gline) ? "" : gline->gl_nick, | |
395 | GlineIsBadChan(gline)|GlineIsRealName(gline) ? "" : "!", | |
396 | gline->gl_user, | |
397 | gline->gl_host ? "@" : "", | |
189935b1 | 398 | gline->gl_host ? gline->gl_host : "", |
399 | gline->gl_expire - CurrentTime, gline->gl_lastmod, | |
400 | gline->gl_reason); | |
401 | else | |
402 | sendcmdto_serv_butone(sptr, CMD_GLINE, cptr, | |
403 | (GlineIsRemActive(gline) ? | |
98de26a3 | 404 | "* +%s%s%s%s%s %Tu :%s" : "* -%s%s%s%s%s"), |
405 | GlineIsBadChan(gline)|GlineIsRealName(gline) ? "" : gline->gl_nick, | |
406 | GlineIsBadChan(gline)|GlineIsRealName(gline) ? "" : "!", | |
189935b1 | 407 | gline->gl_user, |
408 | gline->gl_host ? "@" : "", | |
409 | gline->gl_host ? gline->gl_host : "", | |
410 | gline->gl_expire - CurrentTime, gline->gl_reason); | |
411 | ||
412 | return 0; | |
413 | } | |
414 | ||
415 | /** Create a new G-line and add it to global lists. | |
416 | * \a userhost may be in one of four forms: | |
417 | * \li A channel name, to add a BadChan. | |
418 | * \li A string starting with $R and followed by a mask to match against their realname. | |
419 | * \li A user\@IP mask (user\@ part optional) to create an IP-based ban. | |
420 | * \li A user\@host mask (user\@ part optional) to create a hostname ban. | |
421 | * | |
422 | * @param[in] cptr Client that sent us the G-line. | |
423 | * @param[in] sptr Client that originated the G-line. | |
424 | * @param[in] userhost Text mask for the G-line. | |
425 | * @param[in] reason Reason for G-line. | |
426 | * @param[in] expire Duration of G-line in seconds. | |
427 | * @param[in] lastmod Last modification time of G-line. | |
428 | * @param[in] flags Bitwise combination of GLINE_* flags. | |
429 | * @return Zero or CPTR_KILLED, depending on whether \a sptr is suicidal. | |
430 | */ | |
431 | int | |
432 | gline_add(struct Client *cptr, struct Client *sptr, char *userhost, | |
433 | char *reason, time_t expire, time_t lastmod, unsigned int flags) | |
434 | { | |
435 | struct Gline *agline; | |
98de26a3 | 436 | char uhmask[NICKLEN + USERLEN + HOSTLEN + 3]; |
437 | char *nick, *user, *host; | |
189935b1 | 438 | int tmp; |
439 | ||
440 | assert(0 != userhost); | |
441 | assert(0 != reason); | |
442 | ||
443 | if (*userhost == '#' || *userhost == '&') { | |
444 | if ((flags & GLINE_LOCAL) && !HasPriv(sptr, PRIV_LOCAL_BADCHAN)) | |
445 | return send_reply(sptr, ERR_NOPRIVILEGES); | |
446 | ||
447 | flags |= GLINE_BADCHAN; | |
448 | user = userhost; | |
449 | host = 0; | |
450 | } else if (*userhost == '$') { | |
451 | switch (*userhost == '$' ? userhost[1] : userhost[3]) { | |
452 | case 'R': flags |= GLINE_REALNAME; break; | |
453 | default: | |
454 | /* uh, what to do here? */ | |
455 | /* The answer, my dear Watson, is we throw a protocol_violation() | |
456 | -- hikari */ | |
457 | if (IsServer(cptr)) | |
458 | return protocol_violation(sptr,"%s has been smoking the sweet leaf and sent me a whacky gline",cli_name(sptr)); | |
459 | else { | |
460 | sendto_opmask_butone(NULL, SNO_GLINE, "%s has been smoking the sweet leaf and sent me a whacky gline", cli_name(sptr)); | |
461 | return 0; | |
462 | } | |
463 | break; | |
464 | } | |
465 | user = (*userhost =='$' ? userhost : userhost+2); | |
466 | host = 0; | |
467 | } else { | |
98de26a3 | 468 | canon_userhost(userhost, &nick, &user, &host, "*"); |
189935b1 | 469 | if (sizeof(uhmask) < |
470 | ircd_snprintf(0, uhmask, sizeof(uhmask), "%s@%s", user, host)) | |
471 | return send_reply(sptr, ERR_LONGMASK); | |
472 | else if (MyUser(sptr) || (IsUser(sptr) && flags & GLINE_LOCAL)) { | |
473 | switch (gline_checkmask(host)) { | |
474 | case CHECK_OVERRIDABLE: /* oper overrided restriction */ | |
475 | if (flags & GLINE_OPERFORCE) | |
476 | break; | |
477 | /*FALLTHROUGH*/ | |
478 | case CHECK_REJECTED: | |
479 | return send_reply(sptr, ERR_MASKTOOWIDE, uhmask); | |
480 | break; | |
481 | } | |
482 | ||
483 | if ((tmp = count_users(uhmask)) >= | |
484 | feature_int(FEAT_GLINEMAXUSERCOUNT) && !(flags & GLINE_OPERFORCE)) | |
485 | return send_reply(sptr, ERR_TOOMANYUSERS, tmp); | |
486 | } | |
487 | } | |
488 | ||
489 | /* | |
490 | * You cannot set a negative (or zero) expire time, nor can you set an | |
491 | * expiration time for greater than GLINE_MAX_EXPIRE. | |
492 | */ | |
493 | if (!(flags & GLINE_FORCE) && (expire <= 0 || expire > GLINE_MAX_EXPIRE)) { | |
494 | if (!IsServer(sptr) && MyConnect(sptr)) | |
495 | send_reply(sptr, ERR_BADEXPIRE, expire); | |
496 | return 0; | |
497 | } | |
498 | ||
499 | expire += CurrentTime; /* convert from lifetime to timestamp */ | |
500 | ||
501 | /* Inform ops... */ | |
502 | sendto_opmask_butone(0, ircd_strncmp(reason, "AUTO", 4) ? SNO_GLINE : | |
98de26a3 | 503 | SNO_AUTO, "%s adding %s %s for %s%s%s%s%s, expiring at " |
189935b1 | 504 | "%Tu: %s", |
505 | (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ? | |
506 | cli_name(sptr) : | |
507 | cli_name((cli_user(sptr))->server), | |
508 | (flags & GLINE_LOCAL) ? "local" : "global", | |
98de26a3 | 509 | (flags & GLINE_BADCHAN) ? "BADCHAN" : "GLINE", |
510 | (flags & (GLINE_BADCHAN|GLINE_REALNAME)) ? "" : nick, | |
511 | (flags & (GLINE_BADCHAN|GLINE_REALNAME)) ? "" : "!", | |
512 | user, | |
189935b1 | 513 | (flags & (GLINE_BADCHAN|GLINE_REALNAME)) ? "" : "@", |
514 | (flags & (GLINE_BADCHAN|GLINE_REALNAME)) ? "" : host, | |
515 | expire + TSoffset, reason); | |
516 | ||
517 | /* and log it */ | |
518 | log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE, | |
98de26a3 | 519 | "%#C adding %s %s for %s%s%s%s%s, expiring at %Tu: %s", sptr, |
189935b1 | 520 | flags & GLINE_LOCAL ? "local" : "global", |
98de26a3 | 521 | flags & GLINE_BADCHAN ? "BADCHAN" : "GLINE", |
522 | flags & (GLINE_BADCHAN|GLINE_REALNAME) ? "" : nick, | |
523 | flags & (GLINE_BADCHAN|GLINE_REALNAME) ? "" : "!", | |
524 | user, | |
189935b1 | 525 | flags & (GLINE_BADCHAN|GLINE_REALNAME) ? "" : "@", |
526 | flags & (GLINE_BADCHAN|GLINE_REALNAME) ? "" : host, | |
527 | expire + TSoffset, reason); | |
528 | ||
529 | /* make the gline */ | |
98de26a3 | 530 | agline = make_gline(nick, user, host, reason, expire, lastmod, flags); |
189935b1 | 531 | |
532 | if (!agline) /* if it overlapped, silently return */ | |
533 | return 0; | |
534 | ||
535 | gline_propagate(cptr, sptr, agline); | |
536 | ||
537 | return do_gline(cptr, sptr, agline); /* knock off users if necessary */ | |
538 | } | |
539 | ||
540 | /** Activate a currently inactive G-line. | |
541 | * @param[in] cptr Peer that told us to activate the G-line. | |
542 | * @param[in] sptr Client that originally thought it was a good idea. | |
543 | * @param[in] gline G-line to activate. | |
544 | * @param[in] lastmod New value for last modification timestamp. | |
545 | * @param[in] flags 0 if the activation should be propagated, GLINE_LOCAL if not. | |
546 | * @return Zero, unless \a sptr had a death wish (in which case CPTR_KILLED). | |
547 | */ | |
548 | int | |
549 | gline_activate(struct Client *cptr, struct Client *sptr, struct Gline *gline, | |
550 | time_t lastmod, unsigned int flags) | |
551 | { | |
552 | unsigned int saveflags = 0; | |
553 | ||
554 | assert(0 != gline); | |
555 | ||
556 | saveflags = gline->gl_flags; | |
557 | ||
558 | if (flags & GLINE_LOCAL) | |
559 | gline->gl_flags &= ~GLINE_LDEACT; | |
560 | else { | |
561 | gline->gl_flags |= GLINE_ACTIVE; | |
562 | ||
563 | if (gline->gl_lastmod) { | |
564 | if (gline->gl_lastmod >= lastmod) /* force lastmod to increase */ | |
565 | gline->gl_lastmod++; | |
566 | else | |
567 | gline->gl_lastmod = lastmod; | |
568 | } | |
569 | } | |
570 | ||
571 | if ((saveflags & GLINE_ACTMASK) == GLINE_ACTIVE) | |
572 | return 0; /* was active to begin with */ | |
573 | ||
574 | /* Inform ops and log it */ | |
98de26a3 | 575 | sendto_opmask_butone(0, SNO_GLINE, "%s activating global %s for %s%s%s%s%s, " |
189935b1 | 576 | "expiring at %Tu: %s", |
577 | (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ? | |
578 | cli_name(sptr) : | |
579 | cli_name((cli_user(sptr))->server), | |
580 | GlineIsBadChan(gline) ? "BADCHAN" : "GLINE", | |
98de26a3 | 581 | GlineIsBadChan(gline)|GlineIsRealName(gline) ? "" : gline->gl_nick, |
582 | GlineIsBadChan(gline)|GlineIsRealName(gline) ? "" : "!", | |
189935b1 | 583 | gline->gl_user, gline->gl_host ? "@" : "", |
584 | gline->gl_host ? gline->gl_host : "", | |
585 | gline->gl_expire + TSoffset, gline->gl_reason); | |
586 | ||
587 | log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE, | |
98de26a3 | 588 | "%#C activating global %s for %s%s%s%s%s, expiring at %Tu: %s", sptr, |
589 | GlineIsBadChan(gline) ? "BADCHAN" : "GLINE", | |
590 | GlineIsBadChan(gline)|GlineIsRealName(gline) ? "" : gline->gl_nick, | |
591 | GlineIsBadChan(gline)|GlineIsRealName(gline) ? "" : "!", | |
592 | gline->gl_user, | |
189935b1 | 593 | gline->gl_host ? "@" : "", |
594 | gline->gl_host ? gline->gl_host : "", | |
595 | gline->gl_expire + TSoffset, gline->gl_reason); | |
596 | ||
597 | if (!(flags & GLINE_LOCAL)) /* don't propagate local changes */ | |
598 | gline_propagate(cptr, sptr, gline); | |
599 | ||
600 | return do_gline(cptr, sptr, gline); | |
601 | } | |
602 | ||
603 | /** Deactivate a G-line. | |
604 | * @param[in] cptr Peer that gave us the message. | |
605 | * @param[in] sptr Client that initiated the deactivation. | |
606 | * @param[in] gline G-line to deactivate. | |
607 | * @param[in] lastmod New value for G-line last modification timestamp. | |
608 | * @param[in] flags GLINE_LOCAL to only deactivate locally, 0 to propagate. | |
609 | * @return Zero. | |
610 | */ | |
611 | int | |
612 | gline_deactivate(struct Client *cptr, struct Client *sptr, struct Gline *gline, | |
613 | time_t lastmod, unsigned int flags) | |
614 | { | |
615 | unsigned int saveflags = 0; | |
616 | char *msg; | |
617 | ||
618 | assert(0 != gline); | |
619 | ||
620 | saveflags = gline->gl_flags; | |
621 | ||
622 | if (GlineIsLocal(gline)) | |
623 | msg = "removing local"; | |
624 | else if (!gline->gl_lastmod && !(flags & GLINE_LOCAL)) { | |
625 | msg = "removing global"; | |
626 | gline->gl_flags &= ~GLINE_ACTIVE; /* propagate a -<mask> */ | |
627 | } else { | |
628 | msg = "deactivating global"; | |
629 | ||
630 | if (flags & GLINE_LOCAL) | |
631 | gline->gl_flags |= GLINE_LDEACT; | |
632 | else { | |
633 | gline->gl_flags &= ~GLINE_ACTIVE; | |
634 | ||
635 | if (gline->gl_lastmod) { | |
636 | if (gline->gl_lastmod >= lastmod) | |
637 | gline->gl_lastmod++; | |
638 | else | |
639 | gline->gl_lastmod = lastmod; | |
640 | } | |
641 | } | |
642 | ||
643 | if ((saveflags & GLINE_ACTMASK) != GLINE_ACTIVE) | |
644 | return 0; /* was inactive to begin with */ | |
645 | } | |
646 | ||
647 | /* Inform ops and log it */ | |
98de26a3 | 648 | sendto_opmask_butone(0, SNO_GLINE, "%s %s %s for %s%s%s%s%s, expiring at %Tu: " |
189935b1 | 649 | "%s", |
650 | (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ? | |
651 | cli_name(sptr) : | |
652 | cli_name((cli_user(sptr))->server), | |
653 | msg, GlineIsBadChan(gline) ? "BADCHAN" : "GLINE", | |
98de26a3 | 654 | GlineIsBadChan(gline)|GlineIsRealName(gline) ? "" : gline->gl_nick, |
655 | GlineIsBadChan(gline)|GlineIsRealName(gline) ? "" : "!", | |
189935b1 | 656 | gline->gl_user, gline->gl_host ? "@" : "", |
657 | gline->gl_host ? gline->gl_host : "", | |
658 | gline->gl_expire + TSoffset, gline->gl_reason); | |
659 | ||
660 | log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE, | |
98de26a3 | 661 | "%#C %s %s for %s%s%s%s%s, expiring at %Tu: %s", sptr, msg, |
662 | GlineIsBadChan(gline) ? "BADCHAN" : "GLINE", | |
663 | GlineIsBadChan(gline)|GlineIsRealName(gline) ? "" : gline->gl_nick, | |
664 | GlineIsBadChan(gline)|GlineIsRealName(gline) ? "" : "!", | |
665 | gline->gl_user, | |
189935b1 | 666 | gline->gl_host ? "@" : "", |
667 | gline->gl_host ? gline->gl_host : "", | |
668 | gline->gl_expire + TSoffset, gline->gl_reason); | |
669 | ||
670 | if (!(flags & GLINE_LOCAL)) /* don't propagate local changes */ | |
671 | gline_propagate(cptr, sptr, gline); | |
672 | ||
673 | /* if it's a local gline or a Uworld gline (and not locally deactivated).. */ | |
674 | if (GlineIsLocal(gline) || (!gline->gl_lastmod && !(flags & GLINE_LOCAL))) | |
675 | gline_free(gline); /* get rid of it */ | |
676 | ||
677 | return 0; | |
678 | } | |
679 | ||
680 | /** Find a G-line for a particular mask, guided by certain flags. | |
681 | * Certain bits in \a flags are interpreted specially: | |
682 | * <dl> | |
683 | * <dt>GLINE_ANY</dt><dd>Search both BadChans and user G-lines.</dd> | |
684 | * <dt>GLINE_BADCHAN</dt><dd>Search BadChans.</dd> | |
685 | * <dt>GLINE_GLOBAL</dt><dd>Only match global G-lines.</dd> | |
686 | * <dt>GLINE_LASTMOD</dt><dd>Only match G-lines with a last modification time.</dd> | |
687 | * <dt>GLINE_EXACT</dt><dd>Require an exact match of G-line mask.</dd> | |
688 | * <dt>anything else</dt><dd>Search user G-lines.</dd> | |
689 | * </dl> | |
690 | * @param[in] userhost Mask to search for. | |
691 | * @param[in] flags Bitwise combination of GLINE_* flags. | |
692 | * @return First matching G-line, or NULL if none are found. | |
693 | */ | |
694 | struct Gline * | |
695 | gline_find(char *userhost, unsigned int flags) | |
696 | { | |
697 | struct Gline *gline; | |
698 | struct Gline *sgline; | |
98de26a3 | 699 | char *nick, *user, *host, *t_uh; |
189935b1 | 700 | |
701 | if (flags & (GLINE_BADCHAN | GLINE_ANY)) { | |
702 | for (gline = BadChanGlineList; gline; gline = sgline) { | |
703 | sgline = gline->gl_next; | |
704 | ||
705 | if (gline->gl_expire <= CurrentTime) | |
706 | gline_free(gline); | |
707 | else if ((flags & GLINE_GLOBAL && gline->gl_flags & GLINE_LOCAL) || | |
708 | (flags & GLINE_LASTMOD && !gline->gl_lastmod)) | |
709 | continue; | |
710 | else if ((flags & GLINE_EXACT ? ircd_strcmp(gline->gl_user, userhost) : | |
711 | match(gline->gl_user, userhost)) == 0) | |
712 | return gline; | |
713 | } | |
714 | } | |
715 | ||
716 | if ((flags & (GLINE_BADCHAN | GLINE_ANY)) == GLINE_BADCHAN || | |
717 | *userhost == '#' || *userhost == '&') | |
718 | return 0; | |
719 | ||
720 | DupString(t_uh, userhost); | |
98de26a3 | 721 | canon_userhost(t_uh, &nick, &user, &host, "*"); |
189935b1 | 722 | |
723 | for (gline = GlobalGlineList; gline; gline = sgline) { | |
724 | sgline = gline->gl_next; | |
725 | ||
726 | if (gline->gl_expire <= CurrentTime) | |
727 | gline_free(gline); | |
728 | else if ((flags & GLINE_GLOBAL && gline->gl_flags & GLINE_LOCAL) || | |
729 | (flags & GLINE_LASTMOD && !gline->gl_lastmod)) | |
730 | continue; | |
731 | else if (flags & GLINE_EXACT) { | |
732 | if (((gline->gl_host && host && ircd_strcmp(gline->gl_host, host) == 0) | |
733 | || (!gline->gl_host && !host)) && | |
98de26a3 | 734 | ((!user && ircd_strcmp(gline->gl_user, "*") == 0) || |
735 | (user && ircd_strcmp(gline->gl_user, user) == 0)) && | |
736 | ((!nick && gline->gl_nick && ircd_strcmp(gline->gl_nick, "*") == 0) || | |
737 | (nick && gline->gl_nick && ircd_strcmp(gline->gl_nick, nick) == 0) || (!gline->gl_nick && !nick))) | |
189935b1 | 738 | break; |
739 | } else { | |
740 | if (((gline->gl_host && host && match(gline->gl_host, host) == 0) | |
741 | || (!gline->gl_host && !host)) && | |
98de26a3 | 742 | ((!user && ircd_strcmp(gline->gl_user, "*") == 0) || |
743 | (user && match(gline->gl_user, user) == 0)) && | |
744 | ((!nick && gline->gl_nick && ircd_strcmp(gline->gl_nick, "*") == 0) || | |
745 | (nick && gline->gl_nick && (match(gline->gl_nick, nick) == 0)) || (!gline->gl_nick && !nick))) | |
189935b1 | 746 | break; |
747 | } | |
748 | } | |
749 | ||
750 | MyFree(t_uh); | |
751 | ||
752 | return gline; | |
753 | } | |
754 | ||
755 | /** Find a matching G-line for a user. | |
756 | * @param[in] cptr Client to compare against. | |
757 | * @param[in] flags Bitwise combination of GLINE_GLOBAL and/or | |
758 | * GLINE_LASTMOD to limit matches. | |
759 | * @return Matching G-line, or NULL if none are found. | |
760 | */ | |
761 | struct Gline * | |
762 | gline_lookup(struct Client *cptr, unsigned int flags) | |
763 | { | |
764 | struct Gline *gline; | |
765 | struct Gline *sgline; | |
766 | ||
767 | for (gline = GlobalGlineList; gline; gline = sgline) { | |
768 | sgline = gline->gl_next; | |
769 | ||
770 | if (gline->gl_expire <= CurrentTime) { | |
771 | gline_free(gline); | |
772 | continue; | |
773 | } | |
774 | ||
775 | if ((flags & GLINE_GLOBAL && gline->gl_flags & GLINE_LOCAL) || | |
776 | (flags & GLINE_LASTMOD && !gline->gl_lastmod)) | |
777 | continue; | |
778 | ||
779 | if (GlineIsRealName(gline)) { | |
780 | Debug((DEBUG_DEBUG,"realname gline: '%s' '%s'",gline->gl_user,cli_info(cptr))); | |
781 | if (match(gline->gl_user+2, cli_info(cptr)) != 0) | |
782 | continue; | |
783 | } | |
784 | else { | |
98de26a3 | 785 | if (match(gline->gl_nick, cli_name(cptr)) != 0) |
786 | continue; | |
787 | if (match(gline->gl_user, (cli_user(cptr))->realusername) != 0) | |
189935b1 | 788 | continue; |
789 | ||
790 | if (GlineIsIpMask(gline)) { | |
791 | if (!ipmask_check(&cli_ip(cptr), &gline->gl_addr, gline->gl_bits)) | |
792 | continue; | |
793 | } | |
794 | else { | |
795 | if (match(gline->gl_host, (cli_user(cptr))->realhost) != 0) | |
796 | continue; | |
797 | } | |
798 | } | |
799 | if (GlineIsActive(gline)) | |
800 | return gline; | |
801 | } | |
802 | /* | |
803 | * No Glines matched | |
804 | */ | |
805 | return 0; | |
806 | } | |
807 | ||
808 | /** Delink and free a G-line. | |
809 | * @param[in] gline G-line to free. | |
810 | */ | |
811 | void | |
812 | gline_free(struct Gline *gline) | |
813 | { | |
814 | assert(0 != gline); | |
815 | ||
816 | *gline->gl_prev_p = gline->gl_next; /* squeeze this gline out */ | |
817 | if (gline->gl_next) | |
818 | gline->gl_next->gl_prev_p = gline->gl_prev_p; | |
819 | ||
820 | MyFree(gline->gl_user); /* free up the memory */ | |
821 | if (gline->gl_host) | |
822 | MyFree(gline->gl_host); | |
823 | MyFree(gline->gl_reason); | |
824 | MyFree(gline); | |
825 | } | |
826 | ||
827 | /** Burst all known global G-lines to another server. | |
828 | * @param[in] cptr Destination of burst. | |
829 | */ | |
830 | void | |
831 | gline_burst(struct Client *cptr) | |
832 | { | |
833 | struct Gline *gline; | |
834 | struct Gline *sgline; | |
835 | ||
836 | for (gline = GlobalGlineList; gline; gline = sgline) { /* all glines */ | |
837 | sgline = gline->gl_next; | |
838 | ||
839 | if (gline->gl_expire <= CurrentTime) /* expire any that need expiring */ | |
840 | gline_free(gline); | |
841 | else if (!GlineIsLocal(gline) && gline->gl_lastmod) | |
98de26a3 | 842 | sendcmdto_one(&me, CMD_GLINE, cptr, "* %c%s%s%s%s%s %Tu %Tu :%s", |
843 | GlineIsRemActive(gline) ? '+' : '-', | |
844 | gline->gl_nick ? gline->gl_nick : "", | |
845 | gline->gl_nick ? "!" : "", | |
846 | gline->gl_user, | |
847 | ||
189935b1 | 848 | gline->gl_host ? "@" : "", |
849 | gline->gl_host ? gline->gl_host : "", | |
850 | gline->gl_expire - CurrentTime, gline->gl_lastmod, | |
851 | gline->gl_reason); | |
852 | } | |
853 | ||
854 | for (gline = BadChanGlineList; gline; gline = sgline) { /* all glines */ | |
855 | sgline = gline->gl_next; | |
856 | ||
857 | if (gline->gl_expire <= CurrentTime) /* expire any that need expiring */ | |
858 | gline_free(gline); | |
859 | else if (!GlineIsLocal(gline) && gline->gl_lastmod) | |
860 | sendcmdto_one(&me, CMD_GLINE, cptr, "* %c%s %Tu %Tu :%s", | |
861 | GlineIsRemActive(gline) ? '+' : '-', gline->gl_user, | |
862 | gline->gl_expire - CurrentTime, gline->gl_lastmod, | |
863 | gline->gl_reason); | |
864 | } | |
865 | } | |
866 | ||
867 | /** Send a G-line to another server. | |
868 | * @param[in] cptr Who to inform of the G-line. | |
869 | * @param[in] gline G-line to send. | |
870 | * @return Zero. | |
871 | */ | |
872 | int | |
873 | gline_resend(struct Client *cptr, struct Gline *gline) | |
874 | { | |
875 | if (GlineIsLocal(gline) || !gline->gl_lastmod) | |
876 | return 0; | |
877 | ||
91f2e709 | 878 | sendcmdto_one(&me, CMD_GLINE, cptr, "* %c%s%s%s%s%s %Tu %Tu :%s", |
879 | GlineIsRemActive(gline) ? '+' : '-', | |
880 | GlineIsBadChan(gline)|GlineIsRealName(gline) ? "" : gline->gl_nick, | |
881 | GlineIsBadChan(gline)|GlineIsRealName(gline) ? "" : "!", | |
882 | gline->gl_user, | |
883 | gline->gl_host ? "@" : "", | |
189935b1 | 884 | gline->gl_host ? gline->gl_host : "", |
91f2e709 | 885 | gline->gl_expire - CurrentTime, gline->gl_lastmod, |
886 | gline->gl_reason); | |
189935b1 | 887 | |
888 | return 0; | |
889 | } | |
890 | ||
891 | /** Display one or all G-lines to a user. | |
892 | * If \a userhost is not NULL, only send the first matching G-line. | |
893 | * Otherwise send the whole list. | |
894 | * @param[in] sptr User asking for G-line list. | |
895 | * @param[in] userhost G-line mask to search for (or NULL). | |
896 | * @return Zero. | |
897 | */ | |
898 | int | |
899 | gline_list(struct Client *sptr, char *userhost) | |
900 | { | |
901 | struct Gline *gline; | |
902 | struct Gline *sgline; | |
903 | ||
904 | if (userhost) { | |
905 | if (!(gline = gline_find(userhost, GLINE_ANY))) /* no such gline */ | |
906 | return send_reply(sptr, ERR_NOSUCHGLINE, userhost); | |
907 | ||
908 | /* send gline information along */ | |
98de26a3 | 909 | send_reply(sptr, RPL_GLIST, |
910 | GlineIsBadChan(gline) | GlineIsRealName(gline) ? "" : gline->gl_nick, | |
911 | GlineIsBadChan(gline) | GlineIsRealName(gline) ? "" : "!", | |
912 | gline->gl_user, GlineIsBadChan(gline) || GlineIsRealName(gline) ? "" : "@", | |
189935b1 | 913 | gline->gl_host ? gline->gl_host : "", |
914 | gline->gl_expire + TSoffset, | |
915 | GlineIsLocal(gline) ? cli_name(&me) : "*", | |
916 | GlineIsActive(gline) ? '+' : '-', gline->gl_reason); | |
917 | } else { | |
918 | for (gline = GlobalGlineList; gline; gline = sgline) { | |
919 | sgline = gline->gl_next; | |
920 | ||
921 | if (gline->gl_expire <= CurrentTime) | |
922 | gline_free(gline); | |
923 | else | |
98de26a3 | 924 | send_reply(sptr, RPL_GLIST, |
925 | gline->gl_nick ? gline->gl_nick : "", | |
926 | gline->gl_nick ? "!" : "", | |
927 | gline->gl_user, | |
189935b1 | 928 | gline->gl_host ? "@" : "", |
929 | gline->gl_host ? gline->gl_host : "", | |
930 | gline->gl_expire + TSoffset, | |
931 | GlineIsLocal(gline) ? cli_name(&me) : "*", | |
932 | GlineIsActive(gline) ? '+' : '-', gline->gl_reason); | |
933 | } | |
934 | ||
935 | for (gline = BadChanGlineList; gline; gline = sgline) { | |
936 | sgline = gline->gl_next; | |
937 | ||
938 | if (gline->gl_expire <= CurrentTime) | |
939 | gline_free(gline); | |
940 | else | |
98de26a3 | 941 | send_reply(sptr, RPL_GLIST, gline->gl_user, "", "", "", "", |
189935b1 | 942 | gline->gl_expire + TSoffset, |
943 | GlineIsLocal(gline) ? cli_name(&me) : "*", | |
944 | GlineIsActive(gline) ? '+' : '-', gline->gl_reason); | |
945 | } | |
946 | } | |
947 | ||
948 | /* end of gline information */ | |
949 | return send_reply(sptr, RPL_ENDOFGLIST); | |
950 | } | |
951 | ||
952 | /** Statistics callback to list G-lines. | |
953 | * @param[in] sptr Client requesting statistics. | |
954 | * @param[in] sd Stats descriptor for request (ignored). | |
955 | * @param[in] param Extra parameter from user (ignored). | |
956 | */ | |
957 | void | |
958 | gline_stats(struct Client *sptr, const struct StatDesc *sd, | |
959 | char *param) | |
960 | { | |
961 | struct Gline *gline; | |
962 | struct Gline *sgline; | |
963 | ||
964 | for (gline = GlobalGlineList; gline; gline = sgline) { | |
965 | sgline = gline->gl_next; | |
966 | ||
967 | if (gline->gl_expire <= CurrentTime) | |
968 | gline_free(gline); | |
969 | else | |
98de26a3 | 970 | send_reply(sptr, RPL_STATSGLINE, 'G', |
971 | gline->gl_nick ? gline->gl_nick : "", | |
972 | gline->gl_nick ? "!" : "", | |
973 | gline->gl_user, | |
189935b1 | 974 | gline->gl_host ? "@" : "", |
975 | gline->gl_host ? gline->gl_host : "", | |
976 | gline->gl_expire + TSoffset, gline->gl_reason); | |
977 | } | |
978 | } | |
979 | ||
980 | /** Calculate memory used by G-lines. | |
981 | * @param[out] gl_size Number of bytes used by G-lines. | |
982 | * @return Number of G-lines in use. | |
983 | */ | |
984 | int | |
985 | gline_memory_count(size_t *gl_size) | |
986 | { | |
987 | struct Gline *gline; | |
988 | unsigned int gl = 0; | |
989 | ||
990 | for (gline = GlobalGlineList; gline; gline = gline->gl_next) | |
991 | { | |
992 | gl++; | |
993 | *gl_size += sizeof(struct Gline); | |
98de26a3 | 994 | *gl_size += gline->gl_nick ? (strlen(gline->gl_nick) +1) : 0; |
189935b1 | 995 | *gl_size += gline->gl_user ? (strlen(gline->gl_user) + 1) : 0; |
996 | *gl_size += gline->gl_host ? (strlen(gline->gl_host) + 1) : 0; | |
997 | *gl_size += gline->gl_reason ? (strlen(gline->gl_reason) + 1) : 0; | |
998 | } | |
999 | return gl; | |
1000 | } | |
98de26a3 | 1001 | |
1002 | struct Gline * | |
1003 | IsNickGlined(struct Client *cptr, char *nick) | |
1004 | { | |
1005 | struct Gline *gline; | |
1006 | struct Gline *sgline; | |
1007 | ||
1008 | for (gline = GlobalGlineList; gline; gline = sgline) { | |
1009 | sgline = gline->gl_next; | |
1010 | ||
1011 | if (gline->gl_expire <= CurrentTime) { | |
1012 | gline_free(gline); | |
1013 | continue; | |
1014 | } | |
1015 | ||
1016 | if (GlineIsRealName(gline)) /* skip realname glines */ | |
1017 | continue; | |
1018 | ||
1019 | if (!ircd_strcmp(gline->gl_nick, "*")) /* skip glines w. wildcarded nick */ | |
1020 | continue; | |
1021 | ||
1022 | if (match(gline->gl_nick, nick) != 0) | |
1023 | continue; | |
1024 | ||
1025 | if (match(gline->gl_user, (cli_user(cptr))->username) != 0) | |
1026 | continue; | |
1027 | ||
1028 | if (GlineIsIpMask(gline)) { | |
1029 | if (!ipmask_check(&(cli_ip(cptr)), &gline->gl_addr, gline->gl_bits)) | |
1030 | continue; | |
1031 | } | |
1032 | else { | |
1033 | if (match(gline->gl_host, (cli_user(cptr))->realhost) != 0) | |
1034 | continue; | |
1035 | } | |
1036 | return gline; | |
1037 | } | |
1038 | /* | |
1039 | * No Glines matched | |
1040 | */ | |
1041 | return 0; | |
1042 | } | |
1043 |