ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/ermyth/modules/nickserv/enforce.C
Revision: 1.9
Committed: Sun Sep 16 18:54:43 2007 UTC (19 years ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.8: +12 -5 lines
Log Message:
#defines to enum

File Contents

# User Rev Content
1 pippijn 1.9 /**
2     * enforce.C: This file contains code for the NickServ RELEASE/ENFORCE functions.
3 pippijn 1.1 *
4 pippijn 1.9 * Copyright © 2007 Pippijn van Steenhoven / The Ermyth Team
5     * Rights to this code are as documented in COPYING.
6 pippijn 1.1 *
7     * This does nickserv enforcement on registered nicks if the ENFORCE option
8     * has been enabled. Users who do not identify within 30-60 seconds have
9     * their nick changed to Guest<num>.
10     * If the ircd or protocol module do not support forced nick changes,
11     * they are killed instead.
12     * Enforcement of the nick is only supported for ircds that support
13     * holdnick_sts(), currently bahamut, charybdis, hybrid, inspircd11,
14     * solidircd, ratbox and unreal (i.e. making sure they can't change back
15     * immediately). Consequently this module is of little use for other ircds.
16     * Note: For hybrid and ratbox, and charybdis before 2.1, the
17     * RELEASE command to remove an enforcer prematurely is not supported,
18     * although it pretends to be successful.
19 pippijn 1.9 *
20     *
21     * Portions of this file were derived from sources bearing the following license:
22     * Copyright © 2005-2007 Atheme Development Group
23     * Rights to this code are as documented in doc/pod/license.pod.
24     *
25     * $Id$
26 pippijn 1.1 */
27    
28     #include "atheme.h"
29 pippijn 1.4 #include <ermyth/module.h>
30 pippijn 1.1 #include <account/mynick.h>
31     #include <account/myuser.h>
32 pippijn 1.4 #include <common/random.h>
33 pippijn 1.1
34 pippijn 1.9 static char const rcsid[] = "$Id: enforce.C,v 1.8 2007-09-09 20:05:52 pippijn Exp $";
35 pippijn 1.1
36 pippijn 1.4 REGISTER_MODULE ("nickserv/enforce", false, "The Ermyth Team <http://ermyth.schmorp.de>");
37 pippijn 1.1
38 pippijn 1.4 #define SHOW_CORRECT_TIMEOUT_BUT_BE_SLOW 0
39 pippijn 1.1
40     #define ENFORCE_TIMEOUT 30
41     #define ENFORCE_CHECK_FREQ 5
42    
43 pippijn 1.4 struct enforce_timeout_t : zero_initialised
44 pippijn 1.1 {
45     char nick[NICKLEN];
46     char host[HOSTLEN];
47     time_t timelimit;
48     node_t node;
49 pippijn 1.4 };
50 pippijn 1.1
51     list_t enforce_list;
52    
53     static void guest_nickname (user_t *u);
54    
55     static void ns_cmd_set_enforce (sourceinfo_t *si, int parc, char *parv[]);
56     static void ns_cmd_release (sourceinfo_t *si, int parc, char *parv[]);
57    
58     static void enforce_timeout_check (void *arg);
59 pippijn 1.4
60     command_t const ns_set_enforce = { "ENFORCE", N_("Enables or disables automatic protection of a nickname."), AC_NONE, 1, ns_cmd_set_enforce };
61     command_t const ns_release = { "RELEASE", N_("Releases a services enforcer."), AC_NONE, 2, ns_cmd_release };
62    
63     E cmdvec ns_cmdtree;
64     E cmdvec ns_set_cmdtree;
65     E helpvec ns_helptree;
66 pippijn 1.1
67     /* sends an FNC for the given user */
68     static void
69     guest_nickname (user_t *u)
70     {
71     char gnick[NICKLEN];
72     int tries;
73    
74     /* Generate a new guest nickname and check if it already exists
75     * This will try to generate a new nickname 30 different times
76     * if nicks are in use. If it runs into 30 nicks in use, maybe
77     * you shouldn't use this module. */
78     for (tries = 0; tries < 30; tries++)
79     {
80 pippijn 1.4 snprintf (gnick, sizeof gnick, "Guest%d", gen_rand32 () % 100000);
81 pippijn 1.1 if (!user_find_named (gnick))
82     break;
83     }
84 pippijn 1.4 phandler->fnc_sts (nicksvs.me->me, u, gnick, FNC_FORCE);
85 pippijn 1.1 }
86    
87     static void
88     ns_cmd_set_enforce (sourceinfo_t *si, int parc, char *parv[])
89     {
90     char *setting = parv[0];
91    
92     if (!setting)
93     {
94 pippijn 1.6 command_fail (si, fault::needmoreparams, STR_INSUFFICIENT_PARAMS, "ENFORCE");
95     command_fail (si, fault::needmoreparams, _("Syntax: SET ENFORCE ON|OFF"));
96 pippijn 1.1 return;
97     }
98    
99     if (!si->smu)
100     {
101 pippijn 1.6 command_fail (si, fault::noprivs, _("You are not logged in."));
102 pippijn 1.1 return;
103     }
104    
105     if (strcasecmp (setting, "ON") == 0)
106     {
107     if (si->smu->find_metadata ("private:doenforce"))
108 pippijn 1.6 command_fail (si, fault::nochange, _("ENFORCE is already enabled."));
109 pippijn 1.1 else
110     {
111     si->smu->add_metadata ("private:doenforce", "1");
112     command_success_nodata (si, _("ENFORCE is now enabled."));
113     }
114     }
115     else if (strcasecmp (setting, "OFF") == 0)
116     {
117     if (si->smu->del_metadata ("private:doenforce"))
118     command_success_nodata (si, _("ENFORCE is now disabled."));
119     else
120 pippijn 1.6 command_fail (si, fault::nochange, _("ENFORCE is already disabled."));
121 pippijn 1.1 }
122     else
123 pippijn 1.6 command_fail (si, fault::badparams, _("Unknown value for ENFORCE. Expected values are ON or OFF."));
124 pippijn 1.1 }
125    
126     static void
127     ns_cmd_release (sourceinfo_t *si, int parc, char *parv[])
128     {
129     mynick_t *mn;
130     char *target = parv[0];
131     char *password = parv[1];
132     user_t *u;
133     node_t *n, *tn;
134     enforce_timeout_t *timeout;
135    
136     /* Absolutely do not do anything like this if nicks
137     * are not considered owned */
138     if (nicksvs.no_nick_ownership)
139     {
140 pippijn 1.6 command_fail (si, fault::noprivs, _("RELEASE is disabled."));
141 pippijn 1.1 return;
142     }
143    
144     if (!target && si->smu != NULL)
145     target = si->smu->name;
146     if (!target)
147     {
148 pippijn 1.6 command_fail (si, fault::needmoreparams, STR_INSUFFICIENT_PARAMS, "RELEASE");
149     command_fail (si, fault::needmoreparams, _("Syntax: RELEASE <nick> [password]"));
150 pippijn 1.1 return;
151     }
152    
153     u = user_find_named (target);
154 pippijn 1.8 mn = mynick_t::find (target);
155 pippijn 1.1
156     if (!mn)
157     {
158 pippijn 1.6 command_fail (si, fault::nosuch_target, _("\2%s\2 is not a registered nickname."), target);
159 pippijn 1.1 return;
160     }
161    
162     if (u == si->su)
163     {
164 pippijn 1.6 command_fail (si, fault::noprivs, _("You cannot RELEASE yourself."));
165 pippijn 1.1 return;
166     }
167 pippijn 1.4 if ((si->smu == mn->owner) || mn->owner->verify_password (password))
168 pippijn 1.1 {
169     /* if this (nick, host) is waiting to be enforced, remove it */
170     LIST_FOREACH_SAFE (n, tn, enforce_list.head)
171     {
172     timeout = static_cast<enforce_timeout_t *> (n->data);
173     if (!irccasecmp (mn->nick, timeout->nick) && (!strcmp (u->host, timeout->host) || !strcmp (u->vhost, timeout->host)))
174     {
175     node_del (&timeout->node, &enforce_list);
176 pippijn 1.4 delete timeout;
177 pippijn 1.1 }
178     }
179     if (u == NULL || is_internal_client (u))
180     {
181     logcommand (si, CMDLOG_DO, "RELEASE %s", target);
182 pippijn 1.4 phandler->holdnick_sts (si->service->me, 0, target, mn->owner);
183 pippijn 1.1 command_success_nodata (si, _("\2%s\2 has been released."), target);
184     }
185     else
186     {
187     notice (nicksvs.nick, target, "%s has released your nickname.", get_source_mask (si));
188     guest_nickname (u);
189     command_success_nodata (si, _("%s has been released."), target);
190     logcommand (si, CMDLOG_DO, "RELEASE %s!%s@%s", u->nick, u->user, u->vhost);
191     }
192     return;
193     }
194     if (!password)
195     {
196 pippijn 1.6 command_fail (si, fault::needmoreparams, STR_INSUFFICIENT_PARAMS, "RELEASE");
197     command_fail (si, fault::needmoreparams, _("Syntax: RELEASE <nickname> [password]"));
198 pippijn 1.1 return;
199     }
200     else
201     {
202     logcommand (si, CMDLOG_DO, "failed RELEASE %s (bad password)", target);
203 pippijn 1.6 command_fail (si, fault::authfail, _("Invalid password for \2%s\2."), target);
204 pippijn 1.1 }
205     }
206    
207     void
208     enforce_timeout_check (void *arg)
209     {
210     node_t *n, *tn;
211     enforce_timeout_t *timeout;
212     user_t *u;
213     mynick_t *mn;
214     bool valid;
215    
216     LIST_FOREACH_SAFE (n, tn, enforce_list.head)
217     {
218     timeout = static_cast<enforce_timeout_t *> (n->data);
219     if (timeout->timelimit > NOW)
220     break; /* assume sorted list */
221     u = user_find_named (timeout->nick);
222 pippijn 1.8 mn = mynick_t::find (timeout->nick);
223 pippijn 1.1 valid = u != NULL && mn != NULL && (!strcmp (u->host, timeout->host) || !strcmp (u->vhost, timeout->host));
224     node_del (&timeout->node, &enforce_list);
225 pippijn 1.4 delete timeout;
226 pippijn 1.1 if (!valid)
227     continue;
228     if (is_internal_client (u))
229     continue;
230     if (u->myuser == mn->owner)
231     continue;
232 pippijn 1.5 if (mn->owner->access_verify (u))
233 pippijn 1.1 continue;
234     if (!mn->owner->find_metadata ("private:doenforce"))
235     continue;
236    
237     notice (nicksvs.nick, u->nick, "You failed to identify in time for the nickname %s", mn->nick);
238     guest_nickname (u);
239 pippijn 1.4 phandler->holdnick_sts (nicksvs.me->me, 3600, u->nick, mn->owner);
240 pippijn 1.1 }
241     }
242    
243     static void
244 pippijn 1.4 show_enforce (mynick_t *mn, myuser_t *mu, sourceinfo_t *si)
245 pippijn 1.1 {
246 pippijn 1.4 if (!mu->find_metadata ("private:doenforce"))
247     command_success_nodata (si, "%s has enabled nick protection", mu->name);
248 pippijn 1.1 }
249    
250 pippijn 1.4 static bool
251     check_registration (sourceinfo_t *si, char const * const account, char const * const email)
252 pippijn 1.1 {
253 pippijn 1.4 if (!strncasecmp (account, "Guest", 5) && isdigit (account[5]))
254 pippijn 1.1 {
255 pippijn 1.6 command_fail (si, fault::badparams, "The nick \2%s\2 is reserved and cannot be registered.", account);
256 pippijn 1.4 return false;
257 pippijn 1.1 }
258 pippijn 1.4
259     return true;
260 pippijn 1.1 }
261    
262     static void
263 pippijn 1.4 check_enforce (mynick_t *mn, user_t *u)
264 pippijn 1.1 {
265     enforce_timeout_t *timeout;
266 pippijn 1.4 #if SHOW_CORRECT_TIMEOUT_BUT_BE_SLOW
267 pippijn 1.1 enforce_timeout_t *timeout2;
268     #endif
269    
270     /* nick is a service, ignore it */
271 pippijn 1.4 if (is_internal_client (u))
272 pippijn 1.1 return;
273    
274 pippijn 1.4 if (!mn->owner->find_metadata ("private:doenforce"))
275 pippijn 1.1 return;
276    
277     /* check if it's already in enforce_list */
278     timeout = NULL;
279 pippijn 1.4 #if SHOW_CORRECT_TIMEOUT_BUT_BE_SLOW
280 pippijn 1.1 /* don't do this now, it's O(n^2) in the number of users using
281     * a nick without access at a time */
282     LIST_FOREACH (n, enforce_list.head)
283     {
284     timeout2 = n->data;
285 pippijn 1.4 if (!irccasecmp (mn->nick, timeout2->nick) && (!strcmp (u->host, timeout2->host) || !strcmp (u->vhost, timeout2->host)))
286 pippijn 1.1 {
287     timeout = timeout2;
288     break;
289     }
290     }
291     #endif
292    
293     if (timeout == NULL)
294     {
295 pippijn 1.4 timeout = new enforce_timeout_t;
296     strlcpy (timeout->nick, mn->nick, sizeof timeout->nick);
297     strlcpy (timeout->host, u->host, sizeof timeout->host);
298 pippijn 1.1 /* the following ENFORCE_TIMEOUT must be constant,
299     * otherwise the timeouts will not be sorted and
300     * enforce_timeout_check() will break */
301     timeout->timelimit = NOW + ENFORCE_TIMEOUT;
302     node_add (timeout, &timeout->node, &enforce_list);
303     }
304    
305 pippijn 1.4 notice (nicksvs.nick, u->nick, "You have %d seconds to identify to your nickname before it is changed.", timeout->timelimit - NOW);
306 pippijn 1.1 }
307    
308 pippijn 1.4 static void
309 pippijn 1.7 idcheck_foreach_cb (myuser_t::pair_type &it)
310 pippijn 1.1 {
311 pippijn 1.4 myuser_t *mu = it.second;
312 pippijn 1.1
313     mu->del_metadata ("private:idcheck");
314     mu->del_metadata ("private:enforcer");
315     }
316    
317 pippijn 1.4 bool
318     _modinit (module *m)
319 pippijn 1.1 {
320     /* Leave this for compatibility with old versions of this code
321     * -- jilles
322     */
323 pippijn 1.7 std::for_each (myuser_t::map.begin (), myuser_t::map.end (), idcheck_foreach_cb);
324 pippijn 1.1
325     /* Absolutely do not do anything like this if nicks
326     * are not considered owned */
327     if (nicksvs.no_nick_ownership)
328     {
329 pippijn 1.4 slog (LG_ERROR, "%s: nicks are not configured to be owned", m->name);
330     return false;
331 pippijn 1.1 }
332    
333     event_add ("enforce_timeout_check", enforce_timeout_check, NULL, ENFORCE_CHECK_FREQ);
334     /*event_add("manage_bots", manage_bots, NULL, 30); */
335     ns_cmdtree << ns_release;
336     ns_set_cmdtree << ns_set_enforce;
337     help_addentry (ns_helptree, "RELEASE", "help/nickserv/release", NULL);
338     help_addentry (ns_helptree, "SET ENFORCE", "help/nickserv/set_enforce", NULL);
339 pippijn 1.4
340     mynick_t::callback.info.attach (show_enforce);
341     user_t::callback.can_register.attach (check_registration);
342     mynick_t::callback.enforce.attach (check_enforce);
343    
344     return true;
345 pippijn 1.1 }
346    
347     void
348     _moddeinit ()
349     {
350     event_delete (enforce_timeout_check, NULL);
351     ns_cmdtree >> ns_release;
352     ns_set_cmdtree >> ns_set_enforce;
353     help_delentry (ns_helptree, "RELEASE");
354     help_delentry (ns_helptree, "SET ENFORCE");
355 pippijn 1.4
356     mynick_t::callback.info.detach (show_enforce);
357     user_t::callback.can_register.detach (check_registration);
358     mynick_t::callback.enforce.detach (check_enforce);
359 pippijn 1.1 }