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