1 /*
2 * atheme-services: A collection of minimalist IRC services
3 * account.c: Account management
4 *
5 * Copyright (c) 2005-2007 Atheme Project (http://www.atheme.org)
6 *
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
12 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
13 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
14 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
15 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
16 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
17 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
18 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
19 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
20 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
21 * POSSIBILITY OF SUCH DAMAGE.
22 */
23
24 #include "atheme.h"
25 #include "uplink.h" /* XXX, for sendq_flush(curr_uplink->conn); */
26 #include "datastream.h"
27 #include "privs.h"
28 #include "authcookie.h"
29
30 mowgli_patricia_t *nicklist;
31 mowgli_patricia_t *oldnameslist;
32 mowgli_patricia_t *mclist;
33 mowgli_patricia_t *certfplist;
34
35 mowgli_heap_t *myuser_heap; /* HEAP_USER */
36 mowgli_heap_t *mynick_heap; /* HEAP_USER */
37 mowgli_heap_t *mycertfp_heap; /* HEAP_USER */
38 mowgli_heap_t *myuser_name_heap; /* HEAP_USER / 2 */
39 mowgli_heap_t *mychan_heap; /* HEAP_CHANNEL */
40 mowgli_heap_t *chanacs_heap; /* HEAP_CHANACS */
41
42 /*
43 * init_accounts()
44 *
45 * Initializes the accounts DTree.
46 *
47 * Inputs:
48 * - none
49 *
50 * Outputs:
51 * - none
52 *
53 * Side Effects:
54 * - if the DTree initialization fails, services will abort.
55 */
init_accounts(void)56 void init_accounts(void)
57 {
58 myuser_heap = sharedheap_get(sizeof(myuser_t));
59 mynick_heap = sharedheap_get(sizeof(myuser_t));
60 myuser_name_heap = sharedheap_get(sizeof(myuser_name_t));
61 mychan_heap = sharedheap_get(sizeof(mychan_t));
62 chanacs_heap = sharedheap_get(sizeof(chanacs_t));
63 mycertfp_heap = sharedheap_get(sizeof(mycertfp_t));
64
65 if (myuser_heap == NULL || mynick_heap == NULL || mychan_heap == NULL
66 || chanacs_heap == NULL || mycertfp_heap == NULL)
67 {
68 slog(LG_ERROR, "init_accounts(): block allocator failure.");
69 exit(EXIT_FAILURE);
70 }
71
72 nicklist = mowgli_patricia_create(irccasecanon);
73 oldnameslist = mowgli_patricia_create(irccasecanon);
74 mclist = mowgli_patricia_create(irccasecanon);
75 certfplist = mowgli_patricia_create(strcasecanon);
76 }
77
78 /*
79 * myuser_add(const char *name, const char *pass, const char *email,
80 * unsigned int flags)
81 *
82 * Creates an account and adds it to the accounts DTree.
83 *
84 * Inputs:
85 * - an account name
86 * - an account password
87 * - an email to associate with the account or NONE
88 * - flags for the account
89 *
90 * Outputs:
91 * - on success, a new myuser_t object (account)
92 * - on failure, NULL.
93 *
94 * Side Effects:
95 * - the created account is added to the accounts DTree,
96 * this may be undesirable for a factory.
97 *
98 * Caveats:
99 * - if nicksvs.no_nick_ownership is not enabled, the caller is
100 * responsible for adding a nick with the same name
101 */
102
myuser_add(const char * name,const char * pass,const char * email,unsigned int flags)103 myuser_t *myuser_add(const char *name, const char *pass, const char *email, unsigned int flags)
104 {
105 return myuser_add_id(NULL, name, pass, email, flags);
106 }
107
108 /*
109 * myuser_add_id(const char *id, const char *name, const char *pass,
110 * const char *email, unsigned int flags)
111 *
112 * Like myuser_add, but lets you specify the new entity's UID.
113 */
myuser_add_id(const char * id,const char * name,const char * pass,const char * email,unsigned int flags)114 myuser_t *myuser_add_id(const char *id, const char *name, const char *pass, const char *email, unsigned int flags)
115 {
116 myuser_t *mu;
117 soper_t *soper;
118
119 return_val_if_fail((mu = myuser_find(name)) == NULL, mu);
120
121 if (!(runflags & RF_STARTING))
122 slog(LG_DEBUG, "myuser_add(): %s -> %s", name, email);
123
124 mu = mowgli_heap_alloc(myuser_heap);
125 object_init(object(mu), name, (destructor_t) myuser_delete);
126
127 entity(mu)->type = ENT_USER;
128 entity(mu)->name = strshare_get(name);
129 mu->email = strshare_get(email);
130 mu->email_canonical = canonicalize_email(email);
131 if (id)
132 {
133 if (myentity_find_uid(id) == NULL)
134 mowgli_strlcpy(entity(mu)->id, id, sizeof(entity(mu)->id));
135 else
136 entity(mu)->id[0] = '\0';
137 }
138 else
139 entity(mu)->id[0] = '\0';
140
141 mu->registered = CURRTIME;
142 mu->flags = flags;
143 if (mu->flags & MU_ENFORCE)
144 {
145 mu->flags &= ~MU_ENFORCE;
146 metadata_add(mu, "private:doenforce", "1");
147 }
148 mu->language = NULL; /* default */
149
150 /* If it's already crypted, don't touch the password. Otherwise,
151 * use set_password() to initialize it. Why? Because set_password
152 * will move the user to encrypted passwords if possible. That way,
153 * new registers are immediately protected and the database is
154 * immediately converted the first time we start up with crypto.
155 */
156 if (flags & MU_CRYPTPASS)
157 mowgli_strlcpy(mu->pass, pass, PASSLEN);
158 else
159 set_password(mu, pass);
160
161 myentity_put(entity(mu));
162
163 if ((soper = soper_find_named(entity(mu)->name)) != NULL)
164 {
165 slog(LG_DEBUG, "myuser_add(): user `%s' has been declared as soper, activating privileges.", entity(mu)->name);
166 soper->myuser = mu;
167 mu->soper = soper;
168 }
169
170 myuser_name_restore(entity(mu)->name, mu);
171
172 cnt.myuser++;
173
174 return mu;
175 }
176
177 /*
178 * myuser_delete(myuser_t *mu)
179 *
180 * Destroys and removes an account from the accounts DTree.
181 *
182 * Inputs:
183 * - account to destroy
184 *
185 * Outputs:
186 * - nothing
187 *
188 * Side Effects:
189 * - an account is destroyed and removed from the accounts DTree.
190 */
myuser_delete(myuser_t * mu)191 void myuser_delete(myuser_t *mu)
192 {
193 myuser_t *successor;
194 mychan_t *mc;
195 mynick_t *mn;
196 user_t *u;
197 mowgli_node_t *n, *tn;
198 mymemo_t *memo;
199 chanacs_t *ca;
200 char nicks[200];
201
202 return_if_fail(mu != NULL);
203
204 if (!(runflags & RF_STARTING))
205 slog(LG_DEBUG, "myuser_delete(): %s", entity(mu)->name);
206
207 myuser_name_remember(entity(mu)->name, mu);
208
209 hook_call_myuser_delete(mu);
210
211 /* log them out */
212 MOWGLI_ITER_FOREACH_SAFE(n, tn, mu->logins.head)
213 {
214 u = (user_t *)n->data;
215 if (!authservice_loaded || !ircd_on_logout(u, entity(mu)->name))
216 {
217 u->myuser = NULL;
218 mowgli_node_delete(n, &mu->logins);
219 mowgli_node_free(n);
220 }
221 }
222
223 /* kill all their channels and chanacs */
224 MOWGLI_ITER_FOREACH_SAFE(n, tn, entity(mu)->chanacs.head)
225 {
226 ca = n->data;
227 mc = ca->mychan;
228
229 /* attempt succession */
230 if (ca->level & CA_FOUNDER && mychan_num_founders(mc) == 1 && (successor = mychan_pick_successor(mc)) != NULL)
231 {
232 slog(LG_INFO, _("SUCCESSION: \2%s\2 to \2%s\2 from \2%s\2"), mc->name, entity(successor)->name, entity(mu)->name);
233 slog(LG_VERBOSE, "myuser_delete(): giving channel %s to %s (unused %lds, founder %s, chanacs %zu)",
234 mc->name, entity(successor)->name,
235 (long)(CURRTIME - mc->used),
236 entity(mu)->name,
237 MOWGLI_LIST_LENGTH(&mc->chanacs));
238 if (chansvs.me != NULL)
239 verbose(mc, "Foundership changed to \2%s\2 because \2%s\2 was dropped.", entity(successor)->name, entity(mu)->name);
240
241 /* CA_FOUNDER | CA_FLAGS is the minimum required for full control; let chanserv take care of assigning the rest via founder_flags */
242 chanacs_change_simple(mc, entity(successor), NULL, CA_FOUNDER | CA_FLAGS, 0, NULL);
243 hook_call_channel_succession((
244 &(hook_channel_succession_req_t){
245 .mc = mc,
246 .mu = successor
247 }));
248
249 if (chansvs.me != NULL)
250 myuser_notice(chansvs.nick, successor, "You are now founder on \2%s\2 (as \2%s\2).", mc->name, entity(successor)->name);
251 object_unref(ca);
252 }
253 /* no successor found */
254 else if (ca->level & CA_FOUNDER && mychan_num_founders(mc) == 1)
255 {
256 slog(LG_REGISTER, _("DELETE: \2%s\2 from \2%s\2"), mc->name, entity(mu)->name);
257 slog(LG_VERBOSE, "myuser_delete(): deleting channel %s (unused %lds, founder %s, chanacs %zu)",
258 mc->name, (long)(CURRTIME - mc->used),
259 entity(mu)->name,
260 MOWGLI_LIST_LENGTH(&mc->chanacs));
261
262 hook_call_channel_drop(mc);
263 if (mc->chan != NULL && !(mc->chan->flags & CHAN_LOG))
264 part(mc->name, chansvs.nick);
265 object_unref(mc);
266 }
267 else /* not founder */
268 object_unref(ca);
269 }
270
271 /* remove them from the soper list */
272 if (soper_find(mu))
273 soper_delete(mu->soper);
274
275 metadata_delete_all(mu);
276
277 /* kill any authcookies */
278 authcookie_destroy_all(mu);
279
280 /* delete memos */
281 MOWGLI_ITER_FOREACH_SAFE(n, tn, mu->memos.head)
282 {
283 memo = (mymemo_t *)n->data;
284
285 mowgli_node_delete(n, &mu->memos);
286 mowgli_node_free(n);
287 free(memo);
288 }
289
290 /* delete access entries */
291 MOWGLI_ITER_FOREACH_SAFE(n, tn, mu->access_list.head)
292 myuser_access_delete(mu, (char *)n->data);
293
294 /* delete certfp entries */
295 MOWGLI_ITER_FOREACH_SAFE(n, tn, mu->cert_fingerprints.head)
296 mycertfp_delete((mycertfp_t *) n->data);
297
298 /* delete their nicks and report them */
299 nicks[0] = '\0';
300 MOWGLI_ITER_FOREACH_SAFE(n, tn, mu->nicks.head)
301 {
302 mn = n->data;
303 if (irccasecmp(mn->nick, entity(mu)->name))
304 {
305 slog(LG_VERBOSE, "myuser_delete(): deleting nick %s (unused %lds, owner %s)",
306 mn->nick,
307 (long)(CURRTIME - mn->lastseen),
308 entity(mu)->name);
309 if (strlen(nicks) + strlen(mn->nick) + 3 >= sizeof nicks)
310 {
311 slog(LG_REGISTER, _("DELETE: \2%s\2 from \2%s\2"), nicks, entity(mu)->name);
312 nicks[0] = '\0';
313 }
314 if (nicks[0] != '\0')
315 mowgli_strlcat(nicks, ", ", sizeof nicks);
316 mowgli_strlcat(nicks, "\2", sizeof nicks);
317 mowgli_strlcat(nicks, mn->nick, sizeof nicks);
318 mowgli_strlcat(nicks, "\2", sizeof nicks);
319 }
320 object_unref(mn);
321 }
322 if (nicks[0] != '\0')
323 slog(LG_REGISTER, _("DELETE: \2%s\2 from \2%s\2"), nicks, entity(mu)->name);
324
325 /* entity(mu)->name is the index for this dtree */
326 myentity_del(entity(mu));
327
328 strshare_unref(mu->email);
329 strshare_unref(mu->email_canonical);
330 strshare_unref(entity(mu)->name);
331
332 mowgli_heap_free(myuser_heap, mu);
333
334 cnt.myuser--;
335 }
336
337 /*
338 * myuser_rename(myuser_t *mu, const char *name)
339 *
340 * Renames an account in the accounts DTree.
341 *
342 * Inputs:
343 * - account to rename
344 * - new name; if nickname ownership is enabled, this must be
345 * irccmp-equal to a nick registered to the account
346 *
347 * Outputs:
348 * - nothing
349 *
350 * Side Effects:
351 * - an account is renamed.
352 * - online users are logged out and in again
353 */
myuser_rename(myuser_t * mu,const char * name)354 void myuser_rename(myuser_t *mu, const char *name)
355 {
356 mowgli_node_t *n, *tn;
357 user_t *u;
358 hook_user_rename_t data;
359 stringref newname;
360 char nb[NICKLEN];
361
362 return_if_fail(mu != NULL);
363 return_if_fail(name != NULL);
364 return_if_fail(strlen(name) < NICKLEN);
365
366 mowgli_strlcpy(nb, entity(mu)->name, NICKLEN);
367 newname = strshare_get(name);
368
369 if (authservice_loaded)
370 {
371 MOWGLI_ITER_FOREACH_SAFE(n, tn, mu->logins.head)
372 {
373 u = n->data;
374 ircd_on_logout(u, entity(mu)->name);
375 }
376 }
377 myentity_del(entity(mu));
378
379 strshare_unref(entity(mu)->name);
380 entity(mu)->name = newname;
381
382 myentity_put(entity(mu));
383 if (authservice_loaded)
384 {
385 MOWGLI_ITER_FOREACH(n, mu->logins.head)
386 {
387 u = n->data;
388 ircd_on_login(u, mu, NULL);
389 }
390 }
391
392 data.mu = mu;
393 data.oldname = nb;
394 hook_call_user_rename(&data);
395 }
396
397 /*
398 * myuser_set_email(myuser_t *mu, const char *name)
399 *
400 * Changes the email address of an account.
401 *
402 * Inputs:
403 * - account to change
404 * - new email address; this must be valid
405 *
406 * Outputs:
407 * - nothing
408 *
409 * Side Effects:
410 * - email address is changed
411 */
myuser_set_email(myuser_t * mu,const char * newemail)412 void myuser_set_email(myuser_t *mu, const char *newemail)
413 {
414 return_if_fail(mu != NULL);
415 return_if_fail(newemail != NULL);
416
417 strshare_unref(mu->email);
418 strshare_unref(mu->email_canonical);
419
420 mu->email = strshare_get(newemail);
421 mu->email_canonical = canonicalize_email(newemail);
422 }
423
424 /*
425 * myuser_find_ext(const char *name)
426 *
427 * Same as myuser_find() but with nick group support and undernet-style
428 * `=nick' expansion.
429 *
430 * Inputs:
431 * - account name/nick to retrieve or =nick notation for wanted account.
432 *
433 * Outputs:
434 * - account wanted or NULL if it's not in the DTree.
435 *
436 * Side Effects:
437 * - none
438 */
myuser_find_ext(const char * name)439 myuser_t *myuser_find_ext(const char *name)
440 {
441 user_t *u;
442 mynick_t *mn;
443
444 return_val_if_fail(name != NULL, NULL);
445
446 if (*name == '=')
447 {
448 u = user_find_named(name + 1);
449 return u != NULL ? u->myuser : NULL;
450 }
451 else if (*name == '?')
452 {
453 return myuser_find_uid(name + 1);
454 }
455 else if (nicksvs.no_nick_ownership)
456 return myuser_find(name);
457 else
458 {
459 mn = mynick_find(name);
460 return mn != NULL ? mn->owner : NULL;
461 }
462 }
463
464 /*
465 * myuser_notice(const char *from, myuser_t *target, const char *fmt, ...)
466 *
467 * Sends a notice to all users logged into an account.
468 *
469 * Inputs:
470 * - source of message
471 * - target account
472 * - format of message
473 * - zero+ arguments for the formatter
474 *
475 * Outputs:
476 * - nothing
477 *
478 * Side Effects:
479 * - a notice is sent to all users logged into the account.
480 */
myuser_notice(const char * from,myuser_t * target,const char * fmt,...)481 void myuser_notice(const char *from, myuser_t *target, const char *fmt, ...)
482 {
483 va_list ap;
484 char buf[BUFSIZE];
485 mowgli_node_t *n;
486 user_t *u;
487
488 return_if_fail(from != NULL);
489 return_if_fail(target != NULL);
490 return_if_fail(fmt != NULL);
491
492 /* have to reformat it here, can't give a va_list to notice() :(
493 * -- jilles
494 */
495 va_start(ap, fmt);
496 vsnprintf(buf, BUFSIZE, fmt, ap);
497 va_end(ap);
498
499 MOWGLI_ITER_FOREACH(n, target->logins.head)
500 {
501 u = (user_t *)n->data;
502 notice(from, u->nick, "%s", buf);
503 }
504 }
505
506 /*
507 * myuser_access_verify()
508 *
509 * Inputs:
510 * - user to verify, account to verify against
511 *
512 * Outputs:
513 * - true if user matches an accesslist entry
514 * - false otherwise
515 *
516 * Side Effects:
517 * - none
518 */
519 bool
myuser_access_verify(user_t * u,myuser_t * mu)520 myuser_access_verify(user_t *u, myuser_t *mu)
521 {
522 mowgli_node_t *n;
523 char buf[USERLEN+HOSTLEN];
524 char buf2[USERLEN+HOSTLEN];
525 char buf3[USERLEN+HOSTLEN];
526 char buf4[USERLEN+HOSTLEN];
527
528 return_val_if_fail(u != NULL, false);
529 return_val_if_fail(mu != NULL, false);
530
531 if (!use_myuser_access)
532 return false;
533
534 if (metadata_find(mu, "private:freeze:freezer"))
535 return false;
536
537 snprintf(buf, sizeof buf, "%s@%s", u->user, u->vhost);
538 snprintf(buf2, sizeof buf2, "%s@%s", u->user, u->host);
539 snprintf(buf3, sizeof buf3, "%s@%s", u->user, u->ip);
540 snprintf(buf4, sizeof buf4, "%s@%s", u->user, u->chost);
541
542 MOWGLI_ITER_FOREACH(n, mu->access_list.head)
543 {
544 char *entry = (char *) n->data;
545
546 if (!match(entry, buf) || !match(entry, buf2) || !match(entry, buf3) || !match(entry, buf4) || !match_cidr(entry, buf3))
547 return true;
548 }
549
550 return false;
551 }
552
553 /*
554 * myuser_access_add()
555 *
556 * Inputs:
557 * - account to attach access mask to, access mask itself
558 *
559 * Outputs:
560 * - false: me.mdlimit is reached (too many access entries)
561 * - true : success
562 *
563 * Side Effects:
564 * - an access mask is added to an account.
565 */
566 bool
myuser_access_add(myuser_t * mu,const char * mask)567 myuser_access_add(myuser_t *mu, const char *mask)
568 {
569 mowgli_node_t *n;
570 char *msk;
571
572 return_val_if_fail(mu != NULL, false);
573 return_val_if_fail(mask != NULL, false);
574
575 if (MOWGLI_LIST_LENGTH(&mu->access_list) > me.mdlimit)
576 {
577 slog(LG_DEBUG, "myuser_access_add(): access entry limit reached for %s", entity(mu)->name);
578 return false;
579 }
580
581 msk = sstrdup(mask);
582 n = mowgli_node_create();
583 mowgli_node_add(msk, n, &mu->access_list);
584
585 cnt.myuser_access++;
586
587 return true;
588 }
589
590 /*
591 * myuser_access_find()
592 *
593 * Inputs:
594 * - account to find access mask in, access mask
595 *
596 * Outputs:
597 * - pointer to found access mask or NULL if not found
598 *
599 * Side Effects:
600 * - none
601 */
602 char *
myuser_access_find(myuser_t * mu,const char * mask)603 myuser_access_find(myuser_t *mu, const char *mask)
604 {
605 mowgli_node_t *n;
606
607 return_val_if_fail(mu != NULL, NULL);
608 return_val_if_fail(mask != NULL, NULL);
609
610 MOWGLI_ITER_FOREACH(n, mu->access_list.head)
611 {
612 char *entry = (char *) n->data;
613
614 if (!strcasecmp(entry, mask))
615 return entry;
616 }
617 return NULL;
618 }
619
620 /*
621 * myuser_access_delete()
622 *
623 * Inputs:
624 * - account to delete access mask from, access mask itself
625 *
626 * Outputs:
627 * - none
628 *
629 * Side Effects:
630 * - an access mask is added to an account.
631 */
632 void
myuser_access_delete(myuser_t * mu,const char * mask)633 myuser_access_delete(myuser_t *mu, const char *mask)
634 {
635 mowgli_node_t *n, *tn;
636
637 return_if_fail(mu != NULL);
638 return_if_fail(mask != NULL);
639
640 MOWGLI_ITER_FOREACH_SAFE(n, tn, mu->access_list.head)
641 {
642 char *entry = (char *) n->data;
643
644 if (!strcasecmp(entry, mask))
645 {
646 mowgli_node_delete(n, &mu->access_list);
647 mowgli_node_free(n);
648 free(entry);
649
650 cnt.myuser_access--;
651
652 return;
653 }
654 }
655 }
656
657 /***************
658 * M Y N I C K *
659 ***************/
660
661 /*
662 * mynick_add(myuser_t *mu, const char *name)
663 *
664 * Creates a nick registration for the given account and adds it to the
665 * nicks DTree.
666 *
667 * Inputs:
668 * - an account pointer
669 * - a nickname
670 *
671 * Outputs:
672 * - on success, a new mynick_t object
673 * - on failure, NULL.
674 *
675 * Side Effects:
676 * - the created nick is added to the nick DTree and to the
677 * account's list.
678 */
mynick_add(myuser_t * mu,const char * name)679 mynick_t *mynick_add(myuser_t *mu, const char *name)
680 {
681 mynick_t *mn;
682
683 return_val_if_fail((mn = mynick_find(name)) == NULL, mn);
684
685 if (!(runflags & RF_STARTING))
686 slog(LG_DEBUG, "mynick_add(): %s -> %s", name, entity(mu)->name);
687
688 mn = mowgli_heap_alloc(mynick_heap);
689 object_init(object(mn), name, (destructor_t) mynick_delete);
690
691 mowgli_strlcpy(mn->nick, name, NICKLEN);
692 mn->owner = mu;
693 mn->registered = CURRTIME;
694
695 mowgli_patricia_add(nicklist, mn->nick, mn);
696 mowgli_node_add(mn, &mn->node, &mu->nicks);
697
698 myuser_name_restore(mn->nick, mu);
699
700 cnt.mynick++;
701
702 return mn;
703 }
704
705 /*
706 * mynick_delete(mynick_t *mn)
707 *
708 * Destroys and removes a nick from the nicks DTree and the account.
709 *
710 * Inputs:
711 * - nick to destroy
712 *
713 * Outputs:
714 * - nothing
715 *
716 * Side Effects:
717 * - a nick is destroyed and removed from the nicks DTree and the account.
718 */
mynick_delete(mynick_t * mn)719 void mynick_delete(mynick_t *mn)
720 {
721 return_if_fail(mn != NULL);
722
723 if (!(runflags & RF_STARTING))
724 slog(LG_DEBUG, "mynick_delete(): %s", mn->nick);
725
726 myuser_name_remember(mn->nick, mn->owner);
727
728 mowgli_patricia_delete(nicklist, mn->nick);
729 mowgli_node_delete(&mn->node, &mn->owner->nicks);
730
731 mowgli_heap_free(mynick_heap, mn);
732
733 cnt.mynick--;
734 }
735
736 /*************************
737 * M Y U S E R _ N A M E *
738 *************************/
739
740 static void myuser_name_delete(myuser_name_t *mun);
741
742 /*
743 * myuser_name_add(const char *name)
744 *
745 * Creates a record for the given name and adds it to the oldnames DTree.
746 *
747 * Inputs:
748 * - a nick or account name
749 *
750 * Outputs:
751 * - on success, a new myuser_name_t object
752 * - on failure, NULL.
753 *
754 * Side Effects:
755 * - the created record is added to the oldnames DTree.
756 */
myuser_name_add(const char * name)757 myuser_name_t *myuser_name_add(const char *name)
758 {
759 myuser_name_t *mun;
760
761 return_val_if_fail((mun = myuser_name_find(name)) == NULL, mun);
762
763 if (!(runflags & RF_STARTING))
764 slog(LG_DEBUG, "myuser_name_add(): %s", name);
765
766 mun = mowgli_heap_alloc(myuser_name_heap);
767 object_init(object(mun), name, (destructor_t) myuser_name_delete);
768
769 mowgli_strlcpy(mun->name, name, NICKLEN);
770
771 mowgli_patricia_add(oldnameslist, mun->name, mun);
772
773 cnt.myuser_name++;
774
775 return mun;
776 }
777
778 /*
779 * myuser_name_delete(myuser_name_t *mun)
780 *
781 * Destroys and removes a name from the oldnames DTree.
782 *
783 * Inputs:
784 * - record to destroy
785 *
786 * Outputs:
787 * - nothing
788 *
789 * Side Effects:
790 * - a record is destroyed and removed from the oldnames DTree.
791 */
myuser_name_delete(myuser_name_t * mun)792 static void myuser_name_delete(myuser_name_t *mun)
793 {
794 return_if_fail(mun != NULL);
795
796 if (!(runflags & RF_STARTING))
797 slog(LG_DEBUG, "myuser_name_delete(): %s", mun->name);
798
799 mowgli_patricia_delete(oldnameslist, mun->name);
800
801 metadata_delete_all(mun);
802
803 mowgli_heap_free(myuser_name_heap, mun);
804
805 cnt.myuser_name--;
806 }
807
808 /*
809 * myuser_name_remember(const char *name, myuser_t *mu)
810 *
811 * If the given account has any information worth saving, creates a record
812 * for the given name and adds it to the oldnames DTree.
813 *
814 * Inputs:
815 * - a nick or account name
816 * - an account pointer
817 *
818 * Outputs:
819 * - none
820 *
821 * Side Effects:
822 * - a record may be added to the oldnames DTree.
823 */
myuser_name_remember(const char * name,myuser_t * mu)824 void myuser_name_remember(const char *name, myuser_t *mu)
825 {
826 myuser_name_t *mun;
827 metadata_t *md;
828
829 if (myuser_name_find(name))
830 return;
831
832 md = metadata_find(mu, "private:mark:setter");
833 if (md == NULL)
834 return;
835
836 mun = myuser_name_add(name);
837
838 metadata_add(mun, md->name, md->value);
839 md = metadata_find(mu, "private:mark:reason");
840 if (md != NULL)
841 metadata_add(mun, md->name, md->value);
842 md = metadata_find(mu, "private:mark:timestamp");
843 if (md != NULL)
844 metadata_add(mun, md->name, md->value);
845
846 return;
847 }
848
849 /*
850 * myuser_name_restore(const char *name, myuser_t *mu)
851 *
852 * If the given name is in the oldnames DTree, restores information from it
853 * into the given account.
854 *
855 * Inputs:
856 * - a nick or account name
857 * - an account pointer
858 *
859 * Outputs:
860 * - none
861 *
862 * Side Effects:
863 * - if present, the record will be removed from the oldnames DTree.
864 */
myuser_name_restore(const char * name,myuser_t * mu)865 void myuser_name_restore(const char *name, myuser_t *mu)
866 {
867 myuser_name_t *mun;
868 metadata_t *md, *md2;
869 mowgli_patricia_iteration_state_t state;
870 char *copy;
871
872 mun = myuser_name_find(name);
873 if (mun == NULL)
874 return;
875
876 md = metadata_find(mu, "private:mark:reason");
877 md2 = metadata_find(mun, "private:mark:reason");
878 if (md != NULL && md2 != NULL && strcmp(md->value, md2->value))
879 {
880 wallops(_("Not restoring mark \2\"%s\"\2 for account \2%s\2 (name \2%s\2) which is already marked"), md2->value, entity(mu)->name, name);
881 slog(LG_INFO, _("MARK:FORGET: \2\"%s\"\2 for \2%s (%s)\2 (already marked)"), md2->value, name, entity(mu)->name);
882 slog(LG_VERBOSE, "myuser_name_restore(): not restoring mark \"%s\" for account %s (name %s) which is already marked",
883 md2->value, entity(mu)->name, name);
884 }
885 else if (md == NULL && md2 != NULL)
886 {
887 slog(LG_INFO, _("MARK:RESTORE: \2\"%s\"\2 for \2%s (%s)\2"), md2->value, name, entity(mu)->name);
888 slog(LG_VERBOSE, "myuser_name_restore(): restoring mark \"%s\" for account %s (name %s)",
889 md2->value, entity(mu)->name, name);
890 }
891
892 if (object(mun)->metadata)
893 {
894 MOWGLI_PATRICIA_FOREACH(md, &state, object(mun)->metadata)
895 {
896 /* prefer current metadata to saved */
897 if (!metadata_find(mu, md->name))
898 {
899 if (strcmp(md->name, "private:mark:reason") ||
900 !strncmp(md->value, "(restored) ", 11))
901 metadata_add(mu, md->name, md->value);
902 else
903 {
904 copy = smalloc(strlen(md->value) + 12);
905 memcpy(copy, "(restored) ", 11);
906 strcpy(copy + 11, md->value);
907 metadata_add(mu, md->name, copy);
908 free(copy);
909 }
910 }
911 }
912 }
913
914 object_unref(mun);
915
916 return;
917 }
918
919 /*******************
920 * M Y C E R T F P *
921 *******************/
922
mycertfp_add(myuser_t * mu,const char * certfp)923 mycertfp_t *mycertfp_add(myuser_t *mu, const char *certfp)
924 {
925 mycertfp_t *mcfp;
926
927 return_val_if_fail(mu != NULL, NULL);
928 return_val_if_fail(certfp != NULL, NULL);
929
930 mcfp = mowgli_heap_alloc(mycertfp_heap);
931 mcfp->mu = mu;
932 mcfp->certfp = sstrdup(certfp);
933
934 mowgli_node_add(mcfp, &mcfp->node, &mu->cert_fingerprints);
935 mowgli_patricia_add(certfplist, mcfp->certfp, mcfp);
936
937 return mcfp;
938 }
939
mycertfp_delete(mycertfp_t * mcfp)940 void mycertfp_delete(mycertfp_t *mcfp)
941 {
942 return_if_fail(mcfp != NULL);
943 return_if_fail(mcfp->mu != NULL);
944 return_if_fail(mcfp->certfp != NULL);
945
946 mowgli_node_delete(&mcfp->node, &mcfp->mu->cert_fingerprints);
947 mowgli_patricia_delete(certfplist, mcfp->certfp);
948
949 free(mcfp->certfp);
950 mowgli_heap_free(mycertfp_heap, mcfp);
951 }
952
mycertfp_find(const char * certfp)953 mycertfp_t *mycertfp_find(const char *certfp)
954 {
955 return_val_if_fail(certfp != NULL, NULL);
956
957 return mowgli_patricia_retrieve(certfplist, certfp);
958 }
959
960 /***************
961 * M Y C H A N *
962 ***************/
963
964 /* private destructor for mychan_t. */
mychan_delete(mychan_t * mc)965 static void mychan_delete(mychan_t *mc)
966 {
967 mowgli_node_t *n, *tn;
968
969 return_if_fail(mc != NULL);
970
971 if (!(runflags & RF_STARTING))
972 slog(LG_DEBUG, "mychan_delete(): %s", mc->name);
973
974 if (mc->chan != NULL)
975 mc->chan->mychan = NULL;
976
977 /* remove the chanacs shiz */
978 MOWGLI_ITER_FOREACH_SAFE(n, tn, mc->chanacs.head)
979 object_unref(n->data);
980
981 metadata_delete_all(mc);
982
983 mowgli_patricia_delete(mclist, mc->name);
984
985 strshare_unref(mc->name);
986
987 mowgli_heap_free(mychan_heap, mc);
988
989 cnt.mychan--;
990 }
991
mychan_add(char * name)992 mychan_t *mychan_add(char *name)
993 {
994 mychan_t *mc;
995
996 return_val_if_fail(name != NULL, NULL);
997 return_val_if_fail((mc = mychan_find(name)) == NULL, mc);
998
999 if (!(runflags & RF_STARTING))
1000 slog(LG_DEBUG, "mychan_add(): %s", name);
1001
1002 mc = mowgli_heap_alloc(mychan_heap);
1003
1004 object_init(object(mc), name, (destructor_t) mychan_delete);
1005 mc->name = strshare_get(name);
1006 mc->registered = CURRTIME;
1007 mc->chan = channel_find(name);
1008
1009 if (mc->chan != NULL)
1010 mc->chan->mychan = mc;
1011
1012 mowgli_patricia_add(mclist, mc->name, mc);
1013
1014 cnt.mychan++;
1015
1016 return mc;
1017 }
1018
1019
1020 /* Check if there is anyone on the channel fulfilling the conditions.
1021 * Fairly expensive, but this is sometimes necessary to avoid
1022 * inappropriate drops. -- jilles */
mychan_isused(mychan_t * mc)1023 bool mychan_isused(mychan_t *mc)
1024 {
1025 mowgli_node_t *n;
1026 channel_t *c;
1027 chanuser_t *cu;
1028
1029 return_val_if_fail(mc != NULL, false);
1030
1031 c = mc->chan;
1032 if (c == NULL)
1033 return false;
1034 MOWGLI_ITER_FOREACH(n, c->members.head)
1035 {
1036 cu = n->data;
1037 if (chanacs_user_flags(mc, cu->user) & CA_USEDUPDATE)
1038 return true;
1039 }
1040 return false;
1041 }
1042
mychan_num_founders(mychan_t * mc)1043 unsigned int mychan_num_founders(mychan_t *mc)
1044 {
1045 mowgli_node_t *n;
1046 chanacs_t *ca;
1047 unsigned int count = 0;
1048
1049 return_val_if_fail(mc != NULL, 0);
1050
1051 MOWGLI_ITER_FOREACH(n, mc->chanacs.head)
1052 {
1053 ca = n->data;
1054 if (ca->entity != NULL && ca->level & CA_FOUNDER)
1055 count++;
1056 }
1057 return count;
1058 }
1059
mychan_founder_names(mychan_t * mc)1060 const char *mychan_founder_names(mychan_t *mc)
1061 {
1062 mowgli_node_t *n;
1063 chanacs_t *ca;
1064 static char names[512];
1065
1066 return_val_if_fail(mc != NULL, NULL);
1067
1068 names[0] = '\0';
1069 MOWGLI_ITER_FOREACH(n, mc->chanacs.head)
1070 {
1071 ca = n->data;
1072 if (ca->entity != NULL && ca->level & CA_FOUNDER)
1073 {
1074 if (names[0] != '\0')
1075 mowgli_strlcat(names, ", ", sizeof names);
1076 mowgli_strlcat(names, entity(ca->entity)->name, sizeof names);
1077 }
1078 }
1079 return names;
1080 }
1081
add_auto_flags(unsigned int flags)1082 static unsigned int add_auto_flags(unsigned int flags)
1083 {
1084 if (flags & CA_OP)
1085 flags |= CA_AUTOOP;
1086 if (flags & CA_HALFOP)
1087 flags |= CA_AUTOHALFOP;
1088 if (flags & CA_VOICE)
1089 flags |= CA_AUTOVOICE;
1090 return flags;
1091 }
1092
1093 /* When to consider a user recently seen */
1094 #define RECENTLY_SEEN (7 * 86400)
1095
1096 /* Find a user fulfilling the conditions who can take another channel */
mychan_pick_candidate(mychan_t * mc,unsigned int minlevel)1097 myuser_t *mychan_pick_candidate(mychan_t *mc, unsigned int minlevel)
1098 {
1099 mowgli_node_t *n;
1100 chanacs_t *ca;
1101 myentity_t *mt, *hi_mt;
1102 unsigned int level, hi_level;
1103 bool recent_ok = false;
1104 bool hi_recent_ok = false;
1105
1106 return_val_if_fail(mc != NULL, NULL);
1107
1108 hi_mt = NULL;
1109 hi_level = 0;
1110 MOWGLI_ITER_FOREACH(n, mc->chanacs.head)
1111 {
1112 ca = n->data;
1113 if (ca->level & CA_AKICK)
1114 continue;
1115 mt = ca->entity;
1116 if (mt == NULL || ca->level & CA_FOUNDER)
1117 continue;
1118 if ((ca->level & minlevel) != minlevel)
1119 continue;
1120
1121 /* now have a user with requested flags */
1122 if (isuser(mt))
1123 recent_ok = MOWGLI_LIST_LENGTH(&user(mt)->logins) > 0 ||
1124 CURRTIME - user(mt)->lastlogin < RECENTLY_SEEN;
1125
1126 level = add_auto_flags(ca->level);
1127 /* compare with previous candidate, this user must be
1128 * "better" to beat the previous. this means that ties
1129 * are broken in favour of the user added first.
1130 */
1131 if (hi_mt != NULL)
1132 {
1133 /* previous candidate has flags this user doesn't? */
1134 if (hi_level & ~level)
1135 continue;
1136 /* if equal flags, recent_ok must be better */
1137 if (hi_level == level && (!recent_ok || hi_recent_ok))
1138 continue;
1139 }
1140 if (myentity_can_register_channel(mt))
1141 {
1142 hi_mt = mt;
1143 hi_level = level;
1144 hi_recent_ok = recent_ok;
1145 }
1146 }
1147 return user(hi_mt);
1148 }
1149
1150 /* Pick a suitable successor
1151 * Note: please do not make this dependent on currently being in
1152 * the channel or on IRC; this would give an unfair advantage to
1153 * 24*7 clients and bots.
1154 * -- jilles */
mychan_pick_successor(mychan_t * mc)1155 myuser_t *mychan_pick_successor(mychan_t *mc)
1156 {
1157 myuser_t *mu;
1158 hook_channel_succession_req_t req;
1159
1160 return_val_if_fail(mc != NULL, NULL);
1161
1162 /* allow a hook to override/augment the succession. */
1163 req.mc = mc;
1164 req.mu = NULL;
1165 hook_call_channel_pick_successor(&req);
1166 if (req.mu != NULL)
1167 return req.mu;
1168
1169 /* value +R higher than other flags
1170 * (old successor has this, but not sop, and help file mentions this)
1171 */
1172 mu = mychan_pick_candidate(mc, CA_RECOVER);
1173 if (mu != NULL)
1174 return mu;
1175 /* +f is also a powerful flag */
1176 mu = mychan_pick_candidate(mc, CA_FLAGS);
1177 if (mu != NULL)
1178 return mu;
1179 /* I guess +q means something */
1180 if (ca_all & CA_USEOWNER)
1181 {
1182 mu = mychan_pick_candidate(mc, CA_USEOWNER | CA_AUTOOP);
1183 if (mu != NULL)
1184 return mu;
1185 }
1186 /* an op perhaps? */
1187 mu = mychan_pick_candidate(mc, CA_OP);
1188 if (mu != NULL)
1189 return mu;
1190 /* just a user with access */
1191 return mychan_pick_candidate(mc, 0);
1192 }
1193
mychan_get_mlock(mychan_t * mc)1194 const char *mychan_get_mlock(mychan_t *mc)
1195 {
1196 static char buf[BUFSIZE];
1197 char params[BUFSIZE];
1198 metadata_t *md;
1199 char *p, *q, *qq;
1200 int dir;
1201
1202 return_val_if_fail(mc != NULL, NULL);
1203
1204 *buf = 0;
1205 *params = 0;
1206
1207 md = metadata_find(mc, "private:mlockext");
1208 if (md != NULL && strlen(md->value) > 450)
1209 md = NULL;
1210
1211 dir = MTYPE_NUL;
1212
1213 if (mc->mlock_on)
1214 {
1215 if (dir != MTYPE_ADD)
1216 dir = MTYPE_ADD, mowgli_strlcat(buf, "+", sizeof buf);
1217 mowgli_strlcat(buf, flags_to_string(mc->mlock_on), sizeof buf);
1218 }
1219
1220 if (mc->mlock_limit)
1221 {
1222 if (dir != MTYPE_ADD)
1223 dir = MTYPE_ADD, mowgli_strlcat(buf, "+", sizeof buf);
1224 mowgli_strlcat(buf, "l", sizeof buf);
1225 mowgli_strlcat(params, " ", sizeof params);
1226 mowgli_strlcat(params, number_to_string(mc->mlock_limit), sizeof params);
1227 }
1228
1229 if (mc->mlock_key)
1230 {
1231 if (dir != MTYPE_ADD)
1232 dir = MTYPE_ADD, mowgli_strlcat(buf, "+", sizeof buf);
1233 mowgli_strlcat(buf, "k", sizeof buf);
1234 mowgli_strlcat(params, " *", sizeof params);
1235 }
1236
1237 if (md)
1238 {
1239 p = md->value;
1240 q = buf + strlen(buf);
1241 while (*p != '\0')
1242 {
1243 if (p[1] != ' ' && p[1] != '\0')
1244 {
1245 if (dir != MTYPE_ADD)
1246 dir = MTYPE_ADD, *q++ = '+';
1247 *q++ = *p++;
1248 mowgli_strlcat(params, " ", sizeof params);
1249 qq = params + strlen(params);
1250 while (*p != '\0' && *p != ' ')
1251 *qq++ = *p++;
1252 *qq = '\0';
1253 }
1254 else
1255 {
1256 p++;
1257 while (*p != '\0' && *p != ' ')
1258 p++;
1259 }
1260 while (*p == ' ')
1261 p++;
1262 }
1263 *q = '\0';
1264 }
1265
1266 if (mc->mlock_off)
1267 {
1268 if (dir != MTYPE_DEL)
1269 dir = MTYPE_DEL, mowgli_strlcat(buf, "-", sizeof buf);
1270 mowgli_strlcat(buf, flags_to_string(mc->mlock_off), sizeof buf);
1271 if (mc->mlock_off & CMODE_LIMIT)
1272 mowgli_strlcat(buf, "l", sizeof buf);
1273 if (mc->mlock_off & CMODE_KEY)
1274 mowgli_strlcat(buf, "k", sizeof buf);
1275 }
1276
1277 if (md)
1278 {
1279 p = md->value;
1280 q = buf + strlen(buf);
1281 while (*p != '\0')
1282 {
1283 if (p[1] == ' ' || p[1] == '\0')
1284 {
1285 if (dir != MTYPE_DEL)
1286 dir = MTYPE_DEL, *q++ = '-';
1287 *q++ = *p;
1288 }
1289 p++;
1290 while (*p != '\0' && *p != ' ')
1291 p++;
1292 while (*p == ' ')
1293 p++;
1294 }
1295 *q = '\0';
1296 }
1297
1298 mowgli_strlcat(buf, params, BUFSIZE);
1299
1300 if (*buf == '\0')
1301 mowgli_strlcpy(buf, "+", BUFSIZE);
1302
1303 return buf;
1304 }
1305
mychan_get_sts_mlock(mychan_t * mc)1306 const char *mychan_get_sts_mlock(mychan_t *mc)
1307 {
1308 static char mlock[BUFSIZE];
1309 metadata_t *md;
1310
1311 return_val_if_fail(mc != NULL, NULL);
1312
1313 mlock[0] = '\0';
1314
1315 if (mc->mlock_on)
1316 mowgli_strlcat(mlock, flags_to_string(mc->mlock_on), sizeof(mlock));
1317 if (mc->mlock_off)
1318 mowgli_strlcat(mlock, flags_to_string(mc->mlock_off), sizeof(mlock));
1319 if (mc->mlock_limit || mc->mlock_off & CMODE_LIMIT)
1320 mowgli_strlcat(mlock, "l", sizeof(mlock));
1321 if (mc->mlock_key || mc->mlock_off & CMODE_KEY)
1322 mowgli_strlcat(mlock, "k", sizeof(mlock));
1323 if ((md = metadata_find(mc, "private:mlockext")))
1324 {
1325 char *p = md->value;
1326 char *q = mlock + strlen(mlock);
1327 while (*p != '\0')
1328 {
1329 *q++ = *p++;
1330 while (*p != ' ' && *p != '\0')
1331 p++;
1332 while (*p == ' ')
1333 p++;
1334 }
1335 *q = '\0';
1336 }
1337
1338 return mlock;
1339 }
1340
1341 /*****************
1342 * C H A N A C S *
1343 *****************/
1344
1345 /* private destructor for chanacs_t */
chanacs_delete(chanacs_t * ca)1346 static void chanacs_delete(chanacs_t *ca)
1347 {
1348 return_if_fail(ca != NULL);
1349 return_if_fail(ca->mychan != NULL);
1350
1351 if (!(runflags & RF_STARTING))
1352 slog(LG_DEBUG, "chanacs_delete(): %s -> %s [%s]", ca->mychan->name,
1353 ca->entity != NULL ? entity(ca->entity)->name : ca->host,
1354 ca->entity != NULL ? "entity" : "hostmask");
1355 mowgli_node_delete(&ca->cnode, &ca->mychan->chanacs);
1356
1357 if (ca->entity != NULL)
1358 {
1359 mowgli_node_delete(&ca->unode, &ca->entity->chanacs);
1360
1361 if (isdynamic(ca->entity))
1362 object_unref(ca->entity);
1363 }
1364
1365 if (ca->setter != NULL)
1366 strshare_unref(ca->setter);
1367
1368 metadata_delete_all(ca);
1369
1370 if (ca->host != NULL)
1371 free(ca->host);
1372
1373 mowgli_heap_free(chanacs_heap, ca);
1374
1375 cnt.chanacs--;
1376 }
1377
1378 /*
1379 * chanacs_add(mychan_t *mychan, myuser_t *myuser, unsigned int level, time_t ts, myentity_t *setter)
1380 *
1381 * Creates an access entry mapping between a user and channel.
1382 *
1383 * Inputs:
1384 * - a channel to create a mapping for
1385 * - a user to create a mapping for
1386 * - a bitmask which describes the access entry's privileges
1387 * - a timestamp for this access entry
1388 *
1389 * Outputs:
1390 * - a chanacs_t object which describes the mapping
1391 *
1392 * Side Effects:
1393 * - the channel access list is updated for mychan.
1394 */
chanacs_add(mychan_t * mychan,myentity_t * mt,unsigned int level,time_t ts,myentity_t * setter)1395 chanacs_t *chanacs_add(mychan_t *mychan, myentity_t *mt, unsigned int level, time_t ts, myentity_t *setter)
1396 {
1397 chanacs_t *ca;
1398
1399 return_val_if_fail(mychan != NULL && mt != NULL, NULL);
1400
1401 if (*mychan->name != '#')
1402 {
1403 slog(LG_DEBUG, "chanacs_add(): got non #channel: %s", mychan->name);
1404 return NULL;
1405 }
1406
1407 if (!(runflags & RF_STARTING))
1408 slog(LG_DEBUG, "chanacs_add(): %s -> %s", mychan->name, mt->name);
1409
1410 ca = mowgli_heap_alloc(chanacs_heap);
1411
1412 object_init(object(ca), mt->name, (destructor_t) chanacs_delete);
1413 ca->mychan = mychan;
1414 ca->entity = isdynamic(mt) ? object_ref(mt) : mt;
1415 ca->host = NULL;
1416 ca->level = level & ca_all;
1417 ca->tmodified = ts;
1418 ca->setter = setter != NULL ? strshare_ref(setter->name) : NULL;
1419
1420 mowgli_node_add(ca, &ca->cnode, &mychan->chanacs);
1421 mowgli_node_add(ca, &ca->unode, &mt->chanacs);
1422
1423 cnt.chanacs++;
1424
1425 return ca;
1426 }
1427
1428 /*
1429 * chanacs_add_host(mychan_t *mychan, char *host, unsigned int level, time_t ts)
1430 *
1431 * Creates an access entry mapping between a hostmask and channel.
1432 *
1433 * Inputs:
1434 * - a channel to create a mapping for
1435 * - a hostmask to create a mapping for
1436 * - a bitmask which describes the access entry's privileges
1437 * - a timestamp for this access entry
1438 *
1439 * Outputs:
1440 * - a chanacs_t object which describes the mapping
1441 *
1442 * Side Effects:
1443 * - the channel access list is updated for mychan.
1444 */
chanacs_add_host(mychan_t * mychan,const char * host,unsigned int level,time_t ts,myentity_t * setter)1445 chanacs_t *chanacs_add_host(mychan_t *mychan, const char *host, unsigned int level, time_t ts, myentity_t *setter)
1446 {
1447 chanacs_t *ca;
1448
1449 return_val_if_fail(mychan != NULL && host != NULL, NULL);
1450
1451 if (*mychan->name != '#')
1452 {
1453 slog(LG_DEBUG, "chanacs_add_host(): got non #channel: %s", mychan->name);
1454 return NULL;
1455 }
1456
1457 if (!(runflags & RF_STARTING))
1458 slog(LG_DEBUG, "chanacs_add_host(): %s -> %s", mychan->name, host);
1459
1460 ca = mowgli_heap_alloc(chanacs_heap);
1461
1462 object_init(object(ca), host, (destructor_t) chanacs_delete);
1463 ca->mychan = mychan;
1464 ca->entity = NULL;
1465 ca->host = sstrdup(host);
1466 ca->level = level & ca_all;
1467 ca->tmodified = ts;
1468 ca->setter = setter != NULL ? strshare_ref(setter->name) : NULL;
1469
1470 mowgli_node_add(ca, &ca->cnode, &mychan->chanacs);
1471
1472 cnt.chanacs++;
1473
1474 return ca;
1475 }
1476
chanacs_find(mychan_t * mychan,myentity_t * mt,unsigned int level)1477 chanacs_t *chanacs_find(mychan_t *mychan, myentity_t *mt, unsigned int level)
1478 {
1479 mowgli_node_t *n;
1480 chanacs_t *ca;
1481
1482 return_val_if_fail(mychan != NULL && mt != NULL, NULL);
1483
1484 if ((ca = chanacs_find_literal(mychan, mt, level)) != NULL)
1485 return ca;
1486
1487 MOWGLI_ITER_FOREACH(n, mychan->chanacs.head)
1488 {
1489 entity_chanacs_validation_vtable_t *vt;
1490
1491 ca = (chanacs_t *)n->data;
1492
1493 if (ca->entity == NULL)
1494 continue;
1495
1496 vt = myentity_get_chanacs_validator(ca->entity);
1497 if (level != 0x0)
1498 {
1499 if ((vt->match_entity(ca, mt) != NULL) && ((ca->level & level) == level))
1500 return ca;
1501 }
1502 else if (vt->match_entity(ca, mt) != NULL)
1503 return ca;
1504 }
1505
1506 return NULL;
1507 }
1508
chanacs_entity_flags(mychan_t * mychan,myentity_t * mt)1509 unsigned int chanacs_entity_flags(mychan_t *mychan, myentity_t *mt)
1510 {
1511 mowgli_node_t *n;
1512 chanacs_t *ca;
1513 unsigned int result = 0;
1514
1515 return_val_if_fail(mychan != NULL && mt != NULL, 0);
1516
1517 MOWGLI_ITER_FOREACH(n, mychan->chanacs.head)
1518 {
1519 entity_chanacs_validation_vtable_t *vt;
1520
1521 ca = (chanacs_t *)n->data;
1522
1523 if (ca->entity == NULL)
1524 continue;
1525 if (ca->entity == mt)
1526 result |= ca->level;
1527 else
1528 {
1529 vt = myentity_get_chanacs_validator(ca->entity);
1530 if (vt->match_entity(ca, mt) != NULL)
1531 result |= ca->level;
1532 }
1533 }
1534
1535 slog(LG_DEBUG, "chanacs_entity_flags(%s, %s): return %s", mychan->name, mt->name, bitmask_to_flags(result));
1536
1537 return result;
1538 }
1539
chanacs_find_literal(mychan_t * mychan,myentity_t * mt,unsigned int level)1540 chanacs_t *chanacs_find_literal(mychan_t *mychan, myentity_t *mt, unsigned int level)
1541 {
1542 mowgli_node_t *n;
1543 chanacs_t *ca;
1544
1545 return_val_if_fail(mychan != NULL && mt != NULL, NULL);
1546
1547 MOWGLI_ITER_FOREACH(n, mychan->chanacs.head)
1548 {
1549 ca = (chanacs_t *)n->data;
1550
1551 if (level != 0x0)
1552 {
1553 if (ca->entity == mt && ((ca->level & level) == level))
1554 return ca;
1555 }
1556 else if (ca->entity == mt)
1557 return ca;
1558 }
1559
1560 return NULL;
1561 }
1562
chanacs_find_host(mychan_t * mychan,const char * host,unsigned int level)1563 chanacs_t *chanacs_find_host(mychan_t *mychan, const char *host, unsigned int level)
1564 {
1565 mowgli_node_t *n;
1566 chanacs_t *ca;
1567
1568 return_val_if_fail(mychan != NULL && host != NULL, NULL);
1569
1570 MOWGLI_ITER_FOREACH(n, mychan->chanacs.head)
1571 {
1572 ca = (chanacs_t *)n->data;
1573
1574 if (level != 0x0)
1575 {
1576 if ((ca->entity == NULL) && (!match(ca->host, host)) && ((ca->level & level) == level))
1577 return ca;
1578 }
1579 else if ((ca->entity == NULL) && (!match(ca->host, host)))
1580 return ca;
1581 }
1582
1583 return NULL;
1584 }
1585
chanacs_host_flags(mychan_t * mychan,const char * host)1586 unsigned int chanacs_host_flags(mychan_t *mychan, const char *host)
1587 {
1588 mowgli_node_t *n;
1589 chanacs_t *ca;
1590 unsigned int result = 0;
1591
1592 return_val_if_fail(mychan != NULL && host != NULL, 0);
1593
1594 MOWGLI_ITER_FOREACH(n, mychan->chanacs.head)
1595 {
1596 ca = (chanacs_t *)n->data;
1597
1598 if (ca->entity == NULL && !match(ca->host, host))
1599 result |= ca->level;
1600 }
1601
1602 return result;
1603 }
1604
chanacs_find_host_literal(mychan_t * mychan,const char * host,unsigned int level)1605 chanacs_t *chanacs_find_host_literal(mychan_t *mychan, const char *host, unsigned int level)
1606 {
1607 mowgli_node_t *n;
1608 chanacs_t *ca;
1609
1610 if ((!mychan) || (!host))
1611 return NULL;
1612
1613 MOWGLI_ITER_FOREACH(n, mychan->chanacs.head)
1614 {
1615 ca = (chanacs_t *)n->data;
1616
1617 if (level != 0x0)
1618 {
1619 if ((ca->entity == NULL) && (!strcasecmp(ca->host, host)) && ((ca->level & level) == level))
1620 return ca;
1621 }
1622 else if ((ca->entity == NULL) && (!strcasecmp(ca->host, host)))
1623 return ca;
1624 }
1625
1626 return NULL;
1627 }
1628
chanacs_find_host_by_user(mychan_t * mychan,user_t * u,unsigned int level)1629 chanacs_t *chanacs_find_host_by_user(mychan_t *mychan, user_t *u, unsigned int level)
1630 {
1631 mowgli_node_t *n;
1632 chanacs_t *ca;
1633
1634 return_val_if_fail(mychan != NULL && u != NULL, 0);
1635
1636 for (n = next_matching_host_chanacs(mychan, u, mychan->chanacs.head); n != NULL; n = next_matching_host_chanacs(mychan, u, n->next))
1637 {
1638 ca = n->data;
1639 if ((ca->level & level) == level)
1640 return ca;
1641 }
1642
1643 return NULL;
1644 }
1645
chanacs_host_flags_by_user(mychan_t * mychan,user_t * u)1646 static unsigned int chanacs_host_flags_by_user(mychan_t *mychan, user_t *u)
1647 {
1648 mowgli_node_t *n;
1649 unsigned int result = 0;
1650 chanacs_t *ca;
1651
1652 return_val_if_fail(mychan != NULL && u != NULL, 0);
1653
1654 for (n = next_matching_host_chanacs(mychan, u, mychan->chanacs.head); n != NULL; n = next_matching_host_chanacs(mychan, u, n->next))
1655 {
1656 ca = n->data;
1657 result |= ca->level;
1658 }
1659
1660 slog(LG_DEBUG, "chanacs_host_flags_by_user(%s, %s): return %s", mychan->name, u->nick, bitmask_to_flags(result));
1661
1662 return result;
1663 }
1664
chanacs_find_by_mask(mychan_t * mychan,const char * mask,unsigned int level)1665 chanacs_t *chanacs_find_by_mask(mychan_t *mychan, const char *mask, unsigned int level)
1666 {
1667 myentity_t *mt;
1668 chanacs_t *ca;
1669
1670 return_val_if_fail(mychan != NULL && mask != NULL, NULL);
1671
1672 mt = myentity_find(mask);
1673 if (mt != NULL)
1674 {
1675 ca = chanacs_find_literal(mychan, mt, level);
1676
1677 if (ca)
1678 return ca;
1679 }
1680
1681 return chanacs_find_host_literal(mychan, mask, level);
1682 }
1683
chanacs_user_has_flag(mychan_t * mychan,user_t * u,unsigned int level)1684 bool chanacs_user_has_flag(mychan_t *mychan, user_t *u, unsigned int level)
1685 {
1686 myentity_t *mt;
1687
1688 return_val_if_fail(mychan != NULL && u != NULL, false);
1689
1690 mt = entity(u->myuser);
1691 if (mt != NULL)
1692 {
1693 if (chanacs_entity_has_flag(mychan, mt, level))
1694 return true;
1695 }
1696
1697 if (chanacs_user_flags(mychan, u) & level)
1698 return true;
1699
1700 return false;
1701 }
1702
chanacs_entity_flags_by_user(mychan_t * mychan,user_t * u)1703 static unsigned int chanacs_entity_flags_by_user(mychan_t *mychan, user_t *u)
1704 {
1705 mowgli_node_t *n;
1706 unsigned int result = 0;
1707
1708 return_val_if_fail(mychan != NULL, 0);
1709 return_val_if_fail(u != NULL, 0);
1710
1711 MOWGLI_ITER_FOREACH(n, mychan->chanacs.head)
1712 {
1713 chanacs_t *ca = n->data;
1714 myentity_t *mt;
1715 entity_chanacs_validation_vtable_t *vt;
1716
1717 if (ca->entity == NULL)
1718 continue;
1719
1720 mt = ca->entity;
1721 vt = myentity_get_chanacs_validator(mt);
1722
1723 if (vt->match_user && vt->match_user(ca, u) != NULL)
1724 result |= ca->level;
1725 }
1726
1727 slog(LG_DEBUG, "chanacs_entity_flags_by_user(%s, %s): return %s", mychan->name, u->nick, bitmask_to_flags(result));
1728
1729 return result;
1730 }
1731
chanacs_user_flags(mychan_t * mychan,user_t * u)1732 unsigned int chanacs_user_flags(mychan_t *mychan, user_t *u)
1733 {
1734 myentity_t *mt;
1735 unsigned int result = 0;
1736
1737 return_val_if_fail(mychan != NULL && u != NULL, 0);
1738
1739 mt = entity(u->myuser);
1740 if (mt != NULL)
1741 result |= chanacs_entity_flags(mychan, mt);
1742
1743 result |= chanacs_entity_flags_by_user(mychan, u);
1744
1745 /* the user is pending e-mail verification. so, we want to filter out all flags
1746 * other than CA_AKICK (+b). that way they have no effective access. --kaniini
1747 */
1748 if (u->myuser != NULL && (u->myuser->flags & MU_WAITAUTH))
1749 result &= ~(ca_all & ~CA_AKICK);
1750
1751 result |= chanacs_host_flags_by_user(mychan, u);
1752
1753 slog(LG_DEBUG, "chanacs_user_flags(%s, %s): return %s", mychan->name, u->nick, bitmask_to_flags(result));
1754
1755 return result;
1756 }
1757
chanacs_source_flags(mychan_t * mychan,sourceinfo_t * si)1758 unsigned int chanacs_source_flags(mychan_t *mychan, sourceinfo_t *si)
1759 {
1760 if (si->su != NULL)
1761 {
1762 return chanacs_user_flags(mychan, si->su);
1763 }
1764 else
1765 {
1766 if (entity(si->smu))
1767 return chanacs_entity_flags(mychan, entity(si->smu));
1768 else
1769 return 0;
1770 }
1771 }
1772
1773 /* Look for the chanacs exactly matching mu or host (exactly one of mu and
1774 * host must be non-NULL). If not found, and create is true, create a new
1775 * chanacs with no flags.
1776 */
chanacs_open(mychan_t * mychan,myentity_t * mt,const char * hostmask,bool create,myentity_t * setter)1777 chanacs_t *chanacs_open(mychan_t *mychan, myentity_t *mt, const char *hostmask, bool create, myentity_t *setter)
1778 {
1779 chanacs_t *ca;
1780
1781 /* wrt the second assert: only one of mu or hostmask can be not-NULL --nenolod */
1782 return_val_if_fail(mychan != NULL, false);
1783 return_val_if_fail((mt != NULL && hostmask == NULL) || (mt == NULL && hostmask != NULL), false);
1784
1785 if (mt != NULL)
1786 {
1787 ca = chanacs_find_literal(mychan, mt, 0);
1788 if (ca != NULL)
1789 return ca;
1790 else if (create)
1791 return chanacs_add(mychan, mt, 0, CURRTIME, setter);
1792 }
1793 else
1794 {
1795 ca = chanacs_find_host_literal(mychan, hostmask, 0);
1796 if (ca != NULL)
1797 return ca;
1798 else if (create)
1799 return chanacs_add_host(mychan, hostmask, 0, CURRTIME, setter);
1800 }
1801 return NULL;
1802 }
1803
1804 /* Change channel access
1805 *
1806 * Either mu or hostmask must be specified.
1807 * Add the flags in *addflags and remove the flags in *removeflags, updating
1808 * these to reflect the actual change. Only allow changes to restrictflags.
1809 * Returns true if successful, false if an unallowed change was attempted.
1810 * -- jilles */
chanacs_modify(chanacs_t * ca,unsigned int * addflags,unsigned int * removeflags,unsigned int restrictflags)1811 bool chanacs_modify(chanacs_t *ca, unsigned int *addflags, unsigned int *removeflags, unsigned int restrictflags)
1812 {
1813 return_val_if_fail(ca != NULL, false);
1814 return_val_if_fail(addflags != NULL && removeflags != NULL, false);
1815
1816 *addflags &= ~ca->level;
1817 *removeflags &= ca->level & ~*addflags;
1818 /* no change? */
1819 if ((*addflags | *removeflags) == 0)
1820 return true;
1821 /* attempting to add bad flag? */
1822 if (~restrictflags & *addflags)
1823 return false;
1824 /* attempting to remove bad flag? */
1825 if (~restrictflags & *removeflags)
1826 return false;
1827 /* attempting to manipulate user with more privs? */
1828 if (~restrictflags & ca->level)
1829 return false;
1830 ca->level = (ca->level | *addflags) & ~*removeflags;
1831 ca->tmodified = CURRTIME;
1832
1833 return true;
1834 }
1835
1836 /* version that doesn't return the changes made */
chanacs_modify_simple(chanacs_t * ca,unsigned int addflags,unsigned int removeflags)1837 bool chanacs_modify_simple(chanacs_t *ca, unsigned int addflags, unsigned int removeflags)
1838 {
1839 unsigned int a, r;
1840
1841 a = addflags & ca_all;
1842 r = removeflags & ca_all;
1843 return chanacs_modify(ca, &a, &r, ca_all);
1844 }
1845
1846 /* Change channel access
1847 *
1848 * Either mu or hostmask must be specified.
1849 * Add the flags in *addflags and remove the flags in *removeflags, updating
1850 * these to reflect the actual change. Only allow changes to restrictflags.
1851 * Returns true if successful, false if an unallowed change was attempted.
1852 * -- jilles */
chanacs_change(mychan_t * mychan,myentity_t * mt,const char * hostmask,unsigned int * addflags,unsigned int * removeflags,unsigned int restrictflags,myentity_t * setter)1853 bool chanacs_change(mychan_t *mychan, myentity_t *mt, const char *hostmask, unsigned int *addflags, unsigned int *removeflags, unsigned int restrictflags, myentity_t *setter)
1854 {
1855 chanacs_t *ca;
1856 hook_channel_acl_req_t req;
1857
1858 /* wrt the second assert: only one of mu or hostmask can be not-NULL --nenolod */
1859 return_val_if_fail(mychan != NULL, false);
1860 return_val_if_fail((mt != NULL && hostmask == NULL) || (mt == NULL && hostmask != NULL), false);
1861 return_val_if_fail(addflags != NULL && removeflags != NULL, false);
1862
1863 if (mt != NULL)
1864 {
1865 ca = chanacs_find_literal(mychan, mt, 0);
1866 if (ca == NULL)
1867 {
1868 *removeflags = 0;
1869 /* no change? */
1870 if ((*addflags | *removeflags) == 0)
1871 return true;
1872 /* attempting to add bad flag? */
1873 if (~restrictflags & *addflags)
1874 return false;
1875 chanacs_add(mychan, mt, *addflags, CURRTIME, setter);
1876 }
1877 else
1878 {
1879 *addflags &= ~ca->level;
1880 *removeflags &= ca->level & ~*addflags;
1881 /* no change? */
1882 if ((*addflags | *removeflags) == 0)
1883 return true;
1884 /* attempting to add bad flag? */
1885 if (~restrictflags & *addflags)
1886 return false;
1887 /* attempting to remove bad flag? */
1888 if (~restrictflags & *removeflags)
1889 return false;
1890 /* attempting to manipulate user with more privs? */
1891 if (~restrictflags & ca->level)
1892 return false;
1893 ca->level = (ca->level | *addflags) & ~*removeflags;
1894 ca->tmodified = CURRTIME;
1895 if (ca->level == 0)
1896 object_unref(ca);
1897 }
1898 }
1899 else /* hostmask != NULL */
1900 {
1901 ca = chanacs_find_host_literal(mychan, hostmask, 0);
1902 if (ca == NULL)
1903 {
1904 *removeflags = 0;
1905 /* no change? */
1906 if ((*addflags | *removeflags) == 0)
1907 return true;
1908 /* attempting to add bad flag? */
1909 if (~restrictflags & *addflags)
1910 return false;
1911 chanacs_add_host(mychan, hostmask, *addflags, CURRTIME, setter);
1912 }
1913 else
1914 {
1915 *addflags &= ~ca->level;
1916 *removeflags &= ca->level & ~*addflags;
1917 /* no change? */
1918 if ((*addflags | *removeflags) == 0)
1919 return true;
1920 /* attempting to add bad flag? */
1921 if (~restrictflags & *addflags)
1922 return false;
1923 /* attempting to remove bad flag? */
1924 if (~restrictflags & *removeflags)
1925 return false;
1926 /* attempting to manipulate user with more privs? */
1927 if (~restrictflags & ca->level)
1928 return false;
1929 ca->level = (ca->level | *addflags) & ~*removeflags;
1930 ca->tmodified = CURRTIME;
1931 if (ca->level == 0)
1932 object_unref(ca);
1933 }
1934 }
1935 return true;
1936 }
1937
1938 /* version that doesn't return the changes made */
chanacs_change_simple(mychan_t * mychan,myentity_t * mt,const char * hostmask,unsigned int addflags,unsigned int removeflags,myentity_t * setter)1939 bool chanacs_change_simple(mychan_t *mychan, myentity_t *mt, const char *hostmask, unsigned int addflags, unsigned int removeflags, myentity_t *setter)
1940 {
1941 unsigned int a, r;
1942
1943 a = addflags & ca_all;
1944 r = removeflags & ca_all;
1945 return chanacs_change(mychan, mt, hostmask, &a, &r, ca_all, setter);
1946 }
1947
expire_myuser_cb(myentity_t * mt,void * unused)1948 static int expire_myuser_cb(myentity_t *mt, void *unused)
1949 {
1950 hook_expiry_req_t req;
1951 myuser_t *mu = user(mt);
1952
1953 return_val_if_fail(isuser(mt), 0);
1954
1955 /* If they're logged in, update lastlogin time.
1956 * To decrease db traffic, may want to only do
1957 * this if the account would otherwise be
1958 * deleted. -- jilles
1959 */
1960 if (MOWGLI_LIST_LENGTH(&mu->logins) > 0)
1961 {
1962 mu->lastlogin = CURRTIME;
1963 return 0;
1964 }
1965
1966 if (MU_HOLD & mu->flags)
1967 return 0;
1968
1969 req.data.mu = mu;
1970 req.do_expire = 1;
1971 hook_call_user_check_expire(&req);
1972
1973 if (!req.do_expire)
1974 return 0;
1975
1976 if ((nicksvs.expiry > 0 && mu->lastlogin < CURRTIME && (unsigned int)(CURRTIME - mu->lastlogin) >= nicksvs.expiry) ||
1977 (mu->flags & MU_WAITAUTH && CURRTIME - mu->registered >= 86400))
1978 {
1979 /* Don't expire accounts with privs on them in atheme.conf,
1980 * otherwise someone can reregister
1981 * them and take the privs -- jilles */
1982 if (is_conf_soper(mu))
1983 return 0;
1984
1985 slog(LG_REGISTER, _("EXPIRE: \2%s\2 from \2%s\2 "), entity(mu)->name, mu->email);
1986 slog(LG_VERBOSE, "expire_check(): expiring account %s (unused %ds, email %s, nicks %zu, chanacs %zu)",
1987 entity(mu)->name, (int)(CURRTIME - mu->lastlogin),
1988 mu->email, MOWGLI_LIST_LENGTH(&mu->nicks),
1989 MOWGLI_LIST_LENGTH(&entity(mu)->chanacs));
1990 object_dispose(mu);
1991 }
1992
1993 return 0;
1994 }
1995
expire_check(void * arg)1996 void expire_check(void *arg)
1997 {
1998 mynick_t *mn;
1999 mychan_t *mc;
2000 user_t *u;
2001 mowgli_patricia_iteration_state_t state;
2002 hook_expiry_req_t req;
2003
2004 /* Let them know about this and the likely subsequent db_save()
2005 * right away -- jilles */
2006 if (curr_uplink != NULL && curr_uplink->conn != NULL)
2007 sendq_flush(curr_uplink->conn);
2008
2009 myentity_foreach_t(ENT_USER, expire_myuser_cb, NULL);
2010
2011 MOWGLI_PATRICIA_FOREACH(mn, &state, nicklist)
2012 {
2013 req.do_expire = 1;
2014 req.data.mn = mn;
2015
2016 hook_call_nick_check_expire(&req);
2017
2018 if (!req.do_expire)
2019 continue;
2020
2021 if (nicksvs.expiry > 0 && mn->lastseen < CURRTIME &&
2022 (unsigned int)(CURRTIME - mn->lastseen) >= nicksvs.expiry)
2023 {
2024 if (MU_HOLD & mn->owner->flags)
2025 continue;
2026
2027 /* do not drop main nick like this */
2028 if (!irccasecmp(mn->nick, entity(mn->owner)->name))
2029 continue;
2030
2031 u = user_find_named(mn->nick);
2032 if (u != NULL && u->myuser == mn->owner)
2033 {
2034 /* still logged in, bleh */
2035 mn->lastseen = CURRTIME;
2036 mn->owner->lastlogin = CURRTIME;
2037 continue;
2038 }
2039
2040 slog(LG_REGISTER, _("EXPIRE: \2%s\2 from \2%s\2"), mn->nick, entity(mn->owner)->name);
2041 slog(LG_VERBOSE, "expire_check(): expiring nick %s (unused %lds, account %s)",
2042 mn->nick, (long)(CURRTIME - mn->lastseen),
2043 entity(mn->owner)->name);
2044 object_unref(mn);
2045 }
2046 }
2047
2048 MOWGLI_PATRICIA_FOREACH(mc, &state, mclist)
2049 {
2050 req.do_expire = 1;
2051 req.data.mc = mc;
2052
2053 hook_call_channel_check_expire(&req);
2054
2055 if (!req.do_expire)
2056 continue;
2057
2058 if ((CURRTIME - mc->used) >= 86400 - 3660)
2059 {
2060 /* keep last used time accurate to
2061 * within a day, making sure an active
2062 * channel will never get "Last used"
2063 * in /cs info -- jilles */
2064 if (mychan_isused(mc))
2065 {
2066 mc->used = CURRTIME;
2067 slog(LG_DEBUG, "expire_check(): updating last used time on %s because it appears to be still in use", mc->name);
2068 continue;
2069 }
2070 }
2071
2072 if (chansvs.expiry > 0 && mc->used < CURRTIME &&
2073 (unsigned int)(CURRTIME - mc->used) >= chansvs.expiry)
2074 {
2075 if (MC_HOLD & mc->flags)
2076 continue;
2077
2078 slog(LG_REGISTER, _("EXPIRE: \2%s\2 from \2%s\2"), mc->name, mychan_founder_names(mc));
2079 slog(LG_VERBOSE, "expire_check(): expiring channel %s (unused %lds, founder %s, chanacs %zu)",
2080 mc->name, (long)(CURRTIME - mc->used),
2081 mychan_founder_names(mc),
2082 MOWGLI_LIST_LENGTH(&mc->chanacs));
2083
2084 hook_call_channel_drop(mc);
2085 if (mc->chan != NULL && !(mc->chan->flags & CHAN_LOG))
2086 part(mc->name, chansvs.nick);
2087
2088 object_unref(mc);
2089 }
2090 }
2091 }
2092
check_myuser_cb(myentity_t * mt,void * unused)2093 static int check_myuser_cb(myentity_t *mt, void *unused)
2094 {
2095 myuser_t *mu = user(mt);
2096 mowgli_node_t *n;
2097 mynick_t *mn, *mn1;
2098
2099 return_val_if_fail(isuser(mt), 0);
2100
2101 if (!nicksvs.no_nick_ownership)
2102 {
2103 mn1 = NULL;
2104 MOWGLI_ITER_FOREACH(n, mu->nicks.head)
2105 {
2106 mn = n->data;
2107 if (mn->registered < mu->registered)
2108 mu->registered = mn->registered;
2109 if (mn->lastseen > mu->lastlogin)
2110 mu->lastlogin = mn->lastseen;
2111 if (!irccasecmp(entity(mu)->name, mn->nick))
2112 mn1 = mn;
2113 }
2114 mn = mn1 != NULL ? mn1 : mynick_find(entity(mu)->name);
2115 if (mn == NULL)
2116 {
2117 slog(LG_REGISTER, "db_check(): adding missing nick %s", entity(mu)->name);
2118 mn = mynick_add(mu, entity(mu)->name);
2119 mn->registered = mu->registered;
2120 mn->lastseen = mu->lastlogin;
2121 }
2122 else if (mn->owner != mu)
2123 {
2124 slog(LG_REGISTER, "db_check(): replacing nick %s owned by %s with %s", mn->nick, entity(mn->owner)->name, entity(mu)->name);
2125 object_unref(mn);
2126 mn = mynick_add(mu, entity(mu)->name);
2127 mn->registered = mu->registered;
2128 mn->lastseen = mu->lastlogin;
2129 }
2130 }
2131
2132 return 0;
2133 }
2134
db_check(void)2135 void db_check(void)
2136 {
2137 myentity_foreach_t(ENT_USER, check_myuser_cb, NULL);
2138 }
2139
2140 /* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
2141 * vim:ts=8
2142 * vim:sw=8
2143 * vim:noexpandtab
2144 */
2145