ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/src/account/myuser.C
Revision: 1.10
Committed: Sat Sep 22 14:27:30 2007 UTC (16 years, 8 months ago) by pippijn
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.9: +2 -1 lines
Log Message:
split up ermyth into ermyth-modules, libermyth (currently just ermyth-util) and ermyth-core

File Contents

# Content
1 /**
2 * myuser.C: Account management
3 *
4 * Copyright © 2007 Pippijn van Steenhoven / The Ermyth Team
5 * Rights to this code are as documented in COPYING.
6 *
7 *
8 * Portions of this file were derived from sources bearing the following license:
9 * Rights to this code are documented in doc/pod/license.pod.
10 * Copyright © 2005-2007 Atheme Project (http://www.atheme.org)
11 */
12
13 #include "atheme.h"
14 #include <libermyth.h>
15 #include <account/myuser.h>
16 #include <account/mynick.h>
17 #include <account/chanacs.h>
18 #include <account/mychan.h>
19 #include <account/mymemo.h>
20 #include <util/containers.h>
21 #include "authcookie.h"
22
23 myuser_t::map_type myuser_t::map;
24 myuser_t::callbacks myuser_t::callback;
25
26 /*
27 * myuser_t::create(char *name, char *pass, char *email, unsigned int flags)
28 *
29 * Creates an account and adds it to the accounts DTree.
30 *
31 * Inputs:
32 * - an account name
33 * - an account password
34 * - an email to associate with the account or NONE
35 * - flags for the account
36 *
37 * Outputs:
38 * - on success, a new myuser_t object (account)
39 * - on failure, NULL.
40 *
41 * Side Effects:
42 * - the created account is added to the accounts DTree,
43 * this may be undesirable for a factory.
44 *
45 * Caveats:
46 * - if nicksvs.no_nick_ownership is not enabled, the caller is
47 * responsible for adding a nick with the same name
48 */
49 myuser_t *
50 myuser_t::create (char const * const name, char const * const pass, char const * const email, unsigned int flags)
51 {
52 myuser_t *mu;
53 soper_t *soper;
54
55 return_val_if_fail ((mu = myuser_t::find (name)) == NULL, mu);
56
57 if (!(runflags & RF_STARTING))
58 slog (LG_DEBUG, "myuser_t::create(): %s -> %s", name, email);
59
60 mu = new myuser_t;
61
62 strlcpy (mu->name, name, NICKLEN);
63 strlcpy (mu->email, email, EMAILLEN);
64
65 mu->registered = NOW;
66 mu->flags = flags;
67
68 /* If it's already crypted, don't touch the password. Otherwise,
69 * use set_password() to initialize it. Why? Because set_password
70 * will move the user to encrypted passwords if possible. That way,
71 * new registers are immediately protected and the database is
72 * immediately converted the first time we start up with crypto.
73 */
74 if (flags & MU_CRYPTPASS)
75 strlcpy (mu->pass, pass, NICKLEN);
76 else
77 mu->set_password (pass);
78
79 myuser_t::map[mu->name] = mu;
80
81 if ((soper = soper_find_named (mu->name)) != NULL)
82 {
83 slog (LG_DEBUG, "myuser_t::create(): user `%s' has been declared as soper, activating privileges.", mu->name);
84 soper->myuser = mu;
85 mu->soper = soper;
86 }
87
88 cnt.myuser++;
89
90 return mu;
91 }
92
93 /*
94 * myuser_t::_destroy(myuser_t *mu)
95 *
96 * Destroys and removes an account from the accounts DTree.
97 *
98 * Inputs:
99 * - account to destroy
100 *
101 * Outputs:
102 * - nothing
103 *
104 * Side Effects:
105 * - an account is destroyed and removed from the accounts DTree.
106 */
107 void
108 myuser_t::_destroy ()
109 {
110 myuser_t *successor;
111 mychan_t *mc;
112 user_t *u;
113 node_t *n, *tn;
114 chanacs_t *ca;
115
116 if (!(runflags & RF_STARTING))
117 slog (LG_DEBUG, "myuser_t::_destroy(): %s", name);
118
119 /* log them out */
120 while (!logins.empty ())
121 {
122 u = logins.back ();
123 if (!authservice_loaded || !phandler->ircd_on_logout (u->nick, name, NULL))
124 {
125 u->myuser = NULL;
126 logins.erase (u);
127 }
128 }
129
130 /* kill all their channels and chanacs */
131 LIST_FOREACH_SAFE (n, tn, chanacs.head)
132 {
133 ca = static_cast<chanacs_t *> (n->data);
134 mc = ca->mychan;
135
136 /* attempt succession */
137 if (ca->level & CA_FOUNDER && mc->num_founders () == 1 && (successor = mc->pick_successor ()) != NULL)
138 {
139 snoop (_("SUCCESSION: \2%s\2 -> \2%s\2 from \2%s\2"), successor->name, mc->name, name);
140 slog (LG_REGISTER, "myuser_t::_destroy(): giving channel %s to %s (unused %ds, founder %s, chanacs %d)",
141 mc->name,
142 successor->name,
143 NOW - mc->used,
144 name,
145 LIST_LENGTH (&mc->chanacs));
146 if (chansvs.me != NULL)
147 verbose (mc, "Foundership changed to \2%s\2 because \2%s\2 was dropped.", successor->name, mc->founder->name);
148
149 chanacs_change_simple (mc, successor, NULL, CA_FOUNDER_0, 0);
150
151 if (chansvs.me != NULL)
152 successor->notice (chansvs.nick, "You are now founder on \2%s\2 (as \2%s\2).", mc->name, successor->name);
153 object_unref (ca);
154 }
155
156 /* no successor found */
157 else if (ca->level & CA_FOUNDER && mc->num_founders () == 1)
158 {
159 snoop (_("DELETE: \2%s\2 from \2%s\2"), mc->name, name);
160 slog (LG_REGISTER, "myuser_t::_destroy(): deleting channel %s (unused %ds, founder %s, chanacs %d)",
161 mc->name,
162 NOW - mc->used,
163 name,
164 LIST_LENGTH (&mc->chanacs));
165
166 mc->callback.drop (mc);
167 if ((config_options.chan && irccasecmp (mc->name, config_options.chan)) || !config_options.chan)
168 part (mc->name, chansvs.nick);
169 object_unref (mc);
170 }
171 else /* not founder */
172 object_unref (ca);
173 }
174
175 /* remove them from the soper list */
176 if (soper_find (this))
177 soper_delete (soper);
178
179 /* delete the metadata */
180 clear_metadata ();
181
182 /* kill any authcookies */
183 authcookie_destroy_all (this);
184
185 /* delete memos */
186 while (!memos.empty ())
187 {
188 delete memos.back ();
189 memos.pop_back ();
190 }
191
192 /* delete access entries */
193 LIST_FOREACH_SAFE (n, tn, access_list.head)
194 {
195 access_delete ((char *) n->data);
196 }
197
198 /* delete their nicks */
199 LIST_FOREACH_SAFE (n, tn, nicks.head)
200 {
201 static_cast<mynick_t *> (n->data)->refcnt_dec ();
202 }
203
204 /* name is the index for this dtree */
205 myuser_t::map.erase (name);
206
207 delete this;
208
209 cnt.myuser--;
210 }
211
212 /*
213 * myuser_t::find(char const * const name)
214 *
215 * Retrieves an account from the accounts DTree.
216 *
217 * Inputs:
218 * - account name to retrieve
219 *
220 * Outputs:
221 * - account wanted or NULL if it's not in the DTree.
222 *
223 * Side Effects:
224 * - none
225 */
226 myuser_t *
227 myuser_t::find (char const * const name)
228 {
229 myuser_t::map_type::iterator it;
230 if ((it = myuser_t::map.find (name)) != myuser_t::map.end ())
231 return it->second;
232 return NULL;
233 }
234
235 /*
236 * myuser_t::find_ext(char const * const name)
237 *
238 * Same as myuser_t::find() but with nick group support and undernet-style
239 * `=nick' expansion.
240 *
241 * Inputs:
242 * - account name/nick to retrieve or =nick notation for wanted account.
243 *
244 * Outputs:
245 * - account wanted or NULL if it's not in the DTree.
246 *
247 * Side Effects:
248 * - none
249 */
250 myuser_t *
251 myuser_t::find_ext (char const * const name)
252 {
253 user_t *u;
254 mynick_t *mn;
255
256 if (name == NULL)
257 return NULL;
258
259 if (*name == '=')
260 {
261 u = user_find_named (name + 1);
262 return u != NULL ? u->myuser : NULL;
263 }
264 else if (nicksvs.no_nick_ownership)
265 return myuser_t::find (name);
266 else
267 {
268 mn = mynick_t::find (name);
269 return mn != NULL ? mn->owner : NULL;
270 }
271 }
272
273 /*
274 * myuser_notice(char *from, myuser_t *target, char *fmt, ...)
275 *
276 * Sends a notice to all users logged into an account.
277 *
278 * Inputs:
279 * - source of message
280 * - target account
281 * - format of message
282 * - zero+ arguments for the formatter
283 *
284 * Outputs:
285 * - nothing
286 *
287 * Side Effects:
288 * - a notice is sent to all users logged into the account.
289 */
290 void
291 myuser_t::notice (char const * const from, char const * const fmt, ...) const
292 {
293 va_list ap;
294 myuser_t::login_vector::const_iterator it = logins.begin ();
295 myuser_t::login_vector::const_iterator et = logins.end ();
296
297 va_start (ap, fmt);
298 while (it != et)
299 {
300 notice (from, (*it)->nick, fmt, ap);
301 ++it;
302 }
303 va_end (ap);
304 }
305
306 unsigned
307 myuser_t::num_channels ()
308 {
309 node_t *n;
310 chanacs_t *ca;
311 int count = 0;
312
313 LIST_FOREACH (n, chanacs.head)
314 {
315 ca = static_cast<chanacs_t *> (n->data);
316 if (ca->level & CA_FOUNDER)
317 count++;
318 }
319
320 return count;
321 }
322
323 /*
324 * myuser_access_verify()
325 *
326 * Inputs:
327 * - user to verify, account to verify against
328 *
329 * Outputs:
330 * - true if user matches an accesslist entry
331 * - false otherwise
332 *
333 * Side Effects:
334 * - last login time updated if user matches
335 */
336 bool
337 myuser_t::access_verify (user_t *u)
338 {
339 node_t *n;
340 char buf[USERLEN + HOSTLEN];
341 char buf2[USERLEN + HOSTLEN];
342 char buf3[USERLEN + HOSTLEN];
343
344 if (u == NULL)
345 {
346 slog (LG_DEBUG, "myuser_t::access_verify(): invalid parameters: u = %p", u);
347 return false;
348 }
349
350 if (!use_myuser_access)
351 return false;
352
353 snprintf (buf, sizeof buf, "%s@%s", u->user, u->vhost);
354 snprintf (buf2, sizeof buf2, "%s@%s", u->user, u->host);
355 snprintf (buf3, sizeof buf3, "%s@%s", u->user, u->ip);
356
357 LIST_FOREACH (n, access_list.head)
358 {
359 char *entry = (char *) n->data;
360
361 if (!match (entry, buf) || !match (entry, buf2) || !match (entry, buf3) || !match_cidr (entry, buf3))
362 {
363 lastlogin = NOW;
364 return true;
365 }
366 }
367
368 return false;
369 }
370
371 /*
372 * myuser_access_add()
373 *
374 * Inputs:
375 * - account to attach access mask to, access mask itself
376 *
377 * Outputs:
378 * - false: me.mdlimit is reached (too many access entries)
379 * - true : success
380 *
381 * Side Effects:
382 * - an access mask is added to an account.
383 */
384 bool
385 myuser_t::access_add (char const * const mask)
386 {
387 node_t *n;
388 char *msk;
389
390 if (mask == NULL)
391 {
392 slog (LG_DEBUG, "myuser_t::access_add(): invalid parameters: mask = %p", mask);
393 return false;
394 }
395
396 if (LIST_LENGTH (&access_list) > me.mdlimit)
397 {
398 slog (LG_DEBUG, "myuser_t::access_add(): access entry limit reached for %s", name);
399 return false;
400 }
401
402 msk = sstrdup (mask);
403 n = node_create ();
404 node_add (msk, n, &access_list);
405
406 cnt.myuser_access++;
407
408 return true;
409 }
410
411 /*
412 * myuser_access_find()
413 *
414 * Inputs:
415 * - account to find access mask in, access mask
416 *
417 * Outputs:
418 * - pointer to found access mask or NULL if not found
419 *
420 * Side Effects:
421 * - none
422 */
423 char *
424 myuser_t::access_find (char const * const mask)
425 {
426 node_t *n;
427
428 if (mask == NULL)
429 {
430 slog (LG_DEBUG, "myuser_t::access_find(): invalid parameters: mask = %p", mask);
431 return NULL;
432 }
433
434 LIST_FOREACH (n, access_list.head)
435 {
436 char *entry = (char *) n->data;
437
438 if (!strcasecmp (entry, mask))
439 return entry;
440 }
441 return NULL;
442 }
443
444 /*
445 * myuser_access_delete()
446 *
447 * Inputs:
448 * - account to delete access mask from, access mask itself
449 *
450 * Outputs:
451 * - none
452 *
453 * Side Effects:
454 * - an access mask is added to an account.
455 */
456 void
457 myuser_t::access_delete (char const * const mask)
458 {
459 node_t *n, *tn;
460
461 if (mask == NULL)
462 {
463 slog (LG_DEBUG, "myuser_t::access_delete(): invalid parameters: mask = %p", mask);
464 return;
465 }
466
467 LIST_FOREACH_SAFE (n, tn, access_list.head)
468 {
469 char *entry = (char *) n->data;
470
471 if (!strcasecmp (entry, mask))
472 {
473 node_del (n, &access_list);
474 node_free (n);
475 sfree (entry);
476
477 cnt.myuser_access--;
478
479 return;
480 }
481 }
482 }
483
484 // frees memory used by myusers on shutdown
485 void
486 myuser_t::cleanup ()
487 {
488 myuser_t::map_type::iterator it = myuser_t::map.begin ();
489 myuser_t::map_type::iterator et = myuser_t::map.end ();
490
491 while (it != et)
492 {
493 myuser_t *mu = it->second;
494 delete mu;
495 ++it;
496 }
497 }