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