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 |
} |