1 /* groupserv.c - group services
2  * Copyright (C) 2010 Atheme Development Group
3  */
4 
5 #include "atheme.h"
6 #include "groupserv_main.h"
7 
8 gflags_t ga_flags[] = {
9 	{ 'F', GA_FOUNDER },
10 	{ 'f', GA_FLAGS },
11 	{ 's', GA_SET },
12 	{ 'c', GA_CHANACS },
13 	{ 'm', GA_MEMOS },
14 	{ 'v', GA_VHOST },
15 	{ 'i', GA_INVITE },
16 	{ 'b', GA_BAN },
17 	{ 'A', GA_ACLVIEW },
18 	{ 0, 0 }
19 };
20 
21 gflags_t mg_flags[] = {
22 	{ 'r', MG_REGNOLIMIT },
23 	{ 'a', MG_ACSNOLIMIT },
24 	{ 'o', MG_OPEN },
25 	{ 'p', MG_PUBLIC },
26 	{ 0, 0 }
27 };
28 
29 groupserv_config_t gs_config;
30 
31 mowgli_heap_t *mygroup_heap, *groupacs_heap;
32 
mygroups_init(void)33 void mygroups_init(void)
34 {
35 	mygroup_heap = mowgli_heap_create(sizeof(mygroup_t), HEAP_USER, BH_NOW);
36 	groupacs_heap = mowgli_heap_create(sizeof(groupacs_t), HEAP_CHANACS, BH_NOW);
37 }
38 
mygroups_deinit(void)39 void mygroups_deinit(void)
40 {
41 	mowgli_heap_destroy(mygroup_heap);
42 	mowgli_heap_destroy(groupacs_heap);
43 }
44 
mygroup_delete(mygroup_t * mg)45 static void mygroup_delete(mygroup_t *mg)
46 {
47 	mowgli_node_t *n, *tn;
48 
49 	myentity_del(entity(mg));
50 
51 	MOWGLI_ITER_FOREACH_SAFE(n, tn, mg->acs.head)
52 	{
53 		groupacs_t *ga = n->data;
54 
55 		mowgli_node_delete(&ga->gnode, &mg->acs);
56 		mowgli_node_delete(&ga->unode, myentity_get_membership_list(ga->mt));
57 		object_unref(ga);
58 	}
59 
60 	metadata_delete_all(mg);
61 	strshare_unref(entity(mg)->name);
62 	mowgli_heap_free(mygroup_heap, mg);
63 }
64 
mygroup_add(const char * name)65 mygroup_t *mygroup_add(const char *name)
66 {
67 	return mygroup_add_id(NULL, name);
68 }
69 
mygroup_add_id(const char * id,const char * name)70 mygroup_t *mygroup_add_id(const char *id, const char *name)
71 {
72 	mygroup_t *mg;
73 
74 	mg = mowgli_heap_alloc(mygroup_heap);
75 	object_init(object(mg), NULL, (destructor_t) mygroup_delete);
76 
77 	entity(mg)->type = ENT_GROUP;
78 
79 	if (id)
80 	{
81 		if (!myentity_find_uid(id))
82 			mowgli_strlcpy(entity(mg)->id, id, sizeof(entity(mg)->id));
83 		else
84 			entity(mg)->id[0] = '\0';
85 	}
86 	else
87 		entity(mg)->id[0] = '\0';
88 
89 	entity(mg)->name = strshare_get(name);
90 	myentity_put(entity(mg));
91 
92 	mygroup_set_chanacs_validator(entity(mg));
93 
94 	mg->regtime = CURRTIME;
95 
96 	return mg;
97 }
98 
mygroup_find(const char * name)99 mygroup_t *mygroup_find(const char *name)
100 {
101 	myentity_t *mg = myentity_find(name);
102 
103 	if (mg == NULL)
104 		return NULL;
105 
106 	if (!isgroup(mg))
107 		return NULL;
108 
109 	return group(mg);
110 }
111 
groupacs_des(groupacs_t * ga)112 static void groupacs_des(groupacs_t *ga)
113 {
114 	metadata_delete_all(ga);
115 	mowgli_heap_free(groupacs_heap, ga);
116 }
117 
groupacs_add(mygroup_t * mg,myentity_t * mt,unsigned int flags)118 groupacs_t *groupacs_add(mygroup_t *mg, myentity_t *mt, unsigned int flags)
119 {
120 	groupacs_t *ga;
121 
122 	return_val_if_fail(mg != NULL, NULL);
123 	return_val_if_fail(mt != NULL, NULL);
124 
125 	ga = mowgli_heap_alloc(groupacs_heap);
126 	object_init(object(ga), NULL, (destructor_t) groupacs_des);
127 
128 	ga->mg = mg;
129 	ga->mt = mt;
130 	ga->flags = flags;
131 
132 	mowgli_node_add(ga, &ga->gnode, &mg->acs);
133 	mowgli_node_add(ga, &ga->unode, myentity_get_membership_list(mt));
134 
135 	return ga;
136 }
137 
groupacs_find(mygroup_t * mg,myentity_t * mt,unsigned int flags,bool allow_recurse)138 groupacs_t *groupacs_find(mygroup_t *mg, myentity_t *mt, unsigned int flags, bool allow_recurse)
139 {
140 	mowgli_node_t *n;
141 	groupacs_t *out = NULL;
142 
143 	return_val_if_fail(mg != NULL, NULL);
144 	return_val_if_fail(mt != NULL, NULL);
145 
146 	mg->visited = true;
147 
148 	MOWGLI_ITER_FOREACH(n, mg->acs.head)
149 	{
150 		groupacs_t *ga = n->data;
151 
152 		if (out != NULL)
153 			break;
154 
155 		if (isgroup(ga->mt) && allow_recurse && !(group(ga->mt)->visited))
156 		{
157 			groupacs_t *ga2;
158 
159 			ga2 = groupacs_find(group(ga->mt), mt, flags, allow_recurse);
160 
161 			if (ga2 != NULL)
162 				out = ga;
163 		}
164 		else
165 		{
166 			if (flags)
167 			{
168 				if (ga->mt == mt && ga->mg == mg && (ga->flags & flags))
169 					out = ga;
170 			}
171 			else if (ga->mt == mt && ga->mg == mg)
172 				out = ga;
173 		}
174 	}
175 
176 	mg->visited = false;
177 
178 	return out;
179 }
180 
groupacs_delete(mygroup_t * mg,myentity_t * mt)181 void groupacs_delete(mygroup_t *mg, myentity_t *mt)
182 {
183 	groupacs_t *ga;
184 
185 	ga = groupacs_find(mg, mt, 0, false);
186 	if (ga != NULL)
187 	{
188 		mowgli_node_delete(&ga->gnode, &mg->acs);
189 		mowgli_node_delete(&ga->unode, myentity_get_membership_list(mt));
190 		object_unref(ga);
191 	}
192 }
193 
groupacs_sourceinfo_has_flag(mygroup_t * mg,sourceinfo_t * si,unsigned int flag)194 bool groupacs_sourceinfo_has_flag(mygroup_t *mg, sourceinfo_t *si, unsigned int flag)
195 {
196 	return groupacs_find(mg, entity(si->smu), flag, true) != NULL;
197 }
198 
groupacs_sourceinfo_flags(mygroup_t * mg,sourceinfo_t * si)199 unsigned int groupacs_sourceinfo_flags(mygroup_t *mg, sourceinfo_t *si)
200 {
201 	groupacs_t *ga;
202 
203 	ga = groupacs_find(mg, entity(si->smu), 0, true);
204 	if (ga == NULL)
205 		return 0;
206 
207 	return ga->flags;
208 }
209 
mygroup_count_flag(mygroup_t * mg,unsigned int flag)210 unsigned int mygroup_count_flag(mygroup_t *mg, unsigned int flag)
211 {
212 	mowgli_node_t *n;
213 	unsigned int count = 0;
214 
215 	return_val_if_fail(mg != NULL, 0);
216 
217 	/* optimization: if flags = 0, then that means select everyone, so just
218 	 * return the list length.
219 	 */
220 	if (flag == 0)
221 		return MOWGLI_LIST_LENGTH(&mg->acs);
222 
223 	MOWGLI_ITER_FOREACH(n, mg->acs.head)
224 	{
225 		groupacs_t *ga = n->data;
226 
227 		if (ga->flags & flag)
228 			count++;
229 	}
230 
231 	return count;
232 }
233 
myentity_get_membership_list(myentity_t * mt)234 mowgli_list_t *myentity_get_membership_list(myentity_t *mt)
235 {
236 	mowgli_list_t *l;
237 
238 	l = privatedata_get(mt, "groupserv:membership");
239 	if (l != NULL)
240 		return l;
241 
242 	l = mowgli_list_create();
243 	privatedata_set(mt, "groupserv:membership", l);
244 
245 	return l;
246 }
247 
mygroup_founder_names(mygroup_t * mg)248 const char *mygroup_founder_names(mygroup_t *mg)
249 {
250         mowgli_node_t *n;
251         groupacs_t *ga;
252         static char names[512];
253 
254         names[0] = '\0';
255         MOWGLI_ITER_FOREACH(n, mg->acs.head)
256         {
257                 ga = n->data;
258                 if (ga->mt != NULL && ga->flags & GA_FOUNDER)
259                 {
260                         if (names[0] != '\0')
261                                 mowgli_strlcat(names, ", ", sizeof names);
262                         mowgli_strlcat(names, ga->mt->name, sizeof names);
263                 }
264         }
265         return names;
266 }
267 
myentity_count_group_flag(myentity_t * mt,unsigned int flagset)268 unsigned int myentity_count_group_flag(myentity_t *mt, unsigned int flagset)
269 {
270 	mowgli_list_t *l;
271 	mowgli_node_t *n;
272 	unsigned int count = 0;
273 
274 	l = myentity_get_membership_list(mt);
275 	MOWGLI_ITER_FOREACH(n, l->head)
276 	{
277 		groupacs_t *ga = n->data;
278 
279 		if (ga->mt == mt && ga->flags & flagset)
280 			count++;
281 	}
282 
283 	return count;
284 }
285 
gs_flags_parser(char * flagstring,bool allow_minus,unsigned int flags)286 unsigned int gs_flags_parser(char *flagstring, bool allow_minus, unsigned int flags)
287 {
288 	char *c;
289 	unsigned int dir = 0;
290 	unsigned int flag;
291 	unsigned char n;
292 
293 	c = flagstring;
294 	while (*c)
295 	{
296 		flag = 0;
297 		n = 0;
298 		switch(*c)
299 		{
300 		case '+':
301 			dir = 0;
302 			break;
303 		case '-':
304 			if (allow_minus)
305 				dir = 1;
306 			break;
307 		case '*':
308 			if (dir)
309 				flags = 0;
310 			else
311 			{
312 				/* preserve existing flags except GA_BAN */
313 				flags |= GA_ALL;
314 				flags &= ~GA_BAN;
315 			}
316 			break;
317 		default:
318 			while (ga_flags[n].ch != 0 && flag == 0)
319 			{
320 				if (ga_flags[n].ch == *c)
321 					flag = ga_flags[n].value;
322 				else
323 					n++;
324 			}
325 			if (flag == 0)
326 				break;
327 			if (dir)
328 				flags &= ~flag;
329 			else
330 				flags |= flag;
331 			break;
332 		}
333 
334 		c++;
335 	}
336 
337 	return flags;
338 }
339 
remove_group_chanacs(mygroup_t * mg)340 void remove_group_chanacs(mygroup_t *mg)
341 {
342 	chanacs_t *ca;
343 	mychan_t *mc;
344 	myuser_t *successor;
345 	mowgli_node_t *n, *tn;
346 
347 	/* kill all their channels and chanacs */
348 	MOWGLI_ITER_FOREACH_SAFE(n, tn, entity(mg)->chanacs.head)
349 	{
350 		ca = n->data;
351 		mc = ca->mychan;
352 
353 		/* attempt succession */
354 		if (ca->level & CA_FOUNDER && mychan_num_founders(mc) == 1 && (successor = mychan_pick_successor(mc)) != NULL)
355 		{
356 			slog(LG_INFO, _("SUCCESSION: \2%s\2 to \2%s\2 from \2%s\2"), mc->name, entity(successor)->name, entity(mg)->name);
357 			slog(LG_VERBOSE, "myuser_delete(): giving channel %s to %s (unused %lds, founder %s, chanacs %zu)",
358 					mc->name, entity(successor)->name,
359 					(long)(CURRTIME - mc->used),
360 					entity(mg)->name,
361 					MOWGLI_LIST_LENGTH(&mc->chanacs));
362 			if (chansvs.me != NULL)
363 				verbose(mc, "Foundership changed to \2%s\2 because \2%s\2 was dropped.", entity(successor)->name, entity(mg)->name);
364 
365 			chanacs_change_simple(mc, entity(successor), NULL, CA_FOUNDER_0, 0, NULL);
366 			if (chansvs.me != NULL)
367 				myuser_notice(chansvs.nick, successor, "You are now founder on \2%s\2 (as \2%s\2).", mc->name, entity(successor)->name);
368 			object_unref(ca);
369 		}
370 		/* no successor found */
371 		else if (ca->level & CA_FOUNDER && mychan_num_founders(mc) == 1)
372 		{
373 			slog(LG_REGISTER, _("DELETE: \2%s\2 from \2%s\2"), mc->name, entity(mg)->name);
374 			slog(LG_VERBOSE, "myuser_delete(): deleting channel %s (unused %lds, founder %s, chanacs %zu)",
375 					mc->name, (long)(CURRTIME - mc->used),
376 					entity(mg)->name,
377 					MOWGLI_LIST_LENGTH(&mc->chanacs));
378 
379 			hook_call_channel_drop(mc);
380 			if (mc->chan != NULL && !(mc->chan->flags & CHAN_LOG))
381 				part(mc->name, chansvs.nick);
382 			object_unref(mc);
383 		}
384 		else /* not founder */
385 			object_unref(ca);
386 	}
387 }
388 
389 /*
390  * mygroup_rename(mygroup_t *mg, const char *name)
391  *
392  * Renames a group.
393  *
394  * Inputs:
395  *      - group to rename
396  *      - new name
397  *
398  * Outputs:
399  *      - nothing
400  *
401  * Side Effects:
402  *      - a group is renamed.
403  */
mygroup_rename(mygroup_t * mg,const char * name)404 void mygroup_rename(mygroup_t *mg, const char *name)
405 {
406 	stringref newname;
407 	char nb[NICKLEN];
408 
409 	return_if_fail(mg != NULL);
410 	return_if_fail(name != NULL);
411 	return_if_fail(strlen(name) < NICKLEN);
412 
413 	mowgli_strlcpy(nb, entity(mg)->name, NICKLEN);
414 	newname = strshare_get(name);
415 
416 	myentity_del(entity(mg));
417 
418 	strshare_unref(entity(mg)->name);
419 	entity(mg)->name = newname;
420 
421 	myentity_put(entity(mg));
422 }
423