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