updated Helpfile for module HostServ
[srvx.git] / src / mod-hostserv.c
1 /* mod-hostserv.c - HostServ module for srvx
2  * Copyright 2012-2013 pk910, Stricted, NurPech
3  *
4  * This file is part of srvx.
5  *
6  * srvx is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with srvx; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
19  */
20
21 /* Adds new section to srvx.conf:
22  * "modules" {
23  *     "hostserv" {
24  *         "nick" "HostServ";
25  *         "modes" "+iok";
26            "toplevel_access" "600";
27  *     };
28  *  };
29  *
30  * After you started srvx make the bot active:
31  /msg opserv bind hostserv * hostserv.*
32  /msg opserv bind hostserv help *modcmd.help
33  */
34
35 #include "chanserv.h"
36 #include "opserv.h"
37 #include "nickserv.h"
38 #include "conf.h"
39 #include "modcmd.h"
40 #include "saxdb.h"
41 #include "timeq.h"
42 #include "gline.h"
43
44 #define KEY_TOPLEVEL "TopLevel"
45 #define KEY_SECONDLEVEL "SecondLevel"
46 #define KEY_MANAGERS "Manager"
47 #define KEY_ASSIGNMENTS "Assignments"
48 #define KEY_ACTIVE "active"
49 #define KEY_TITLEHOST_SUFFIX "title_suffix"
50
51 static const struct message_entry msgtab[] = {
52     { "HSMSG_ASSIGNED_FAKEHOSTS", "Assigned Fakehosts for User $b%s$b:" },
53     { "HSMSG_ASSIGNED_FAKEHOST", "  $b%s.%s$b" },
54     { "HSMSG_ASSIGNED_FAKEHOST_ACTTIVE", "  $b%s.%s$b (active)" },
55     { "HSMSG_ASSIGNED_FAKEHOST_NOT_ACTIVE", "Fakehost $b%s.%s.$b is not active." },
56     { "HSMSG_ASSIGN_HOWTO", "Use $bset xxx.yyy$b to activate one of the listed fakehosts or $bset *$b to use the default fakehost." },
57     { "HSMSG_ASSIGNED_NONE", "  None." },
58     { "HSMSG_MANAGED_FAKEHOSTS", "Fakehosts managed by User $b%s$b:" },
59     { "HSMSG_MANAGED_TOPLEVEL", "  $b*.%s$b   fakehosts: %d   assignments: %d" },
60     { "HSMSG_MANAGED_TOPLEVEL_OWN", "  $b*.%s$b   fakehosts: %d   assignments: %d   (active)" },
61     { "HSMSG_MANAGED_FAKEHOST", "  $b%s.%s$b   assignments: %d" },
62     { "HSMSG_MANAGE_HOWTO", "Use $bview xxx.yyy$b to view more information about a fakehost group." },
63     { "HSMSG_UNKNOWN_FAKEHOST", "Fakehost $b%s.%s$b is unknown or you have no access to manage it." },
64     { "HSMSG_TOPLEVEL_FAKEHOSTS", "Fakehosts in group $b*.%s$b:" },
65     { "HSMSG_TOPLEVEL_FAKEHOST", "  $b%s.%s$b   assignments: %d   managers: %d" },
66     { "HSMSG_TOPLEVEL_INVALID", "The name of the group you entered is invalid ($b%s$b)" },
67     { "HSMSG_MANAGERS_TOPLEVEL", "Managers of group $b*.%s$b:" },
68     { "HSMSG_MANAGERS_FAKEHOST", "Managers of group $b%s.%s$b:" },
69     { "HSMSG_MANAGERS_MANAGERS", "  %s" },
70     { "HSMSG_FAKEHOST_ASSIGNMENTS", "Assignments in group $b%s.%s$b:" },
71     { "HSMSG_FAKEHOST_ASSIGNMENT", "  $b%s$b (%s.%s.%s)" },
72     { "HSMSG_FAKEHOST_ASSIGNMENT_ACTIVE", "  $b%s$b (%s.%s.%s)   active" },
73     { "HSMSG_FAKEHOST_SET_SUCCESS", "$b%s.%s$b where set successfully." },
74     { "HSMSG_FAKEHOST_TOPLEVEL_ADDED", "Group $b%s$b successfully added." },
75     { "HSMSG_FAKEHOST_TOPLEVEL_ALREADY_EXISTS", "Group $b%s$b already exists." },
76     { "HSMSG_FAKEHOST_TOPLEVEL_DELETED", "Group $b%s$b successfully deleted." },
77     { "HSMSG_FAKEHOST_SECONDLEVEL_ADDED", "Group $b%s.%s$b successfully added." },
78     { "HSMSG_FAKEHOST_SECONDLEVEL_ALREADY_EXISTS", "Group $b%s.%s$b already exists." },
79     { "HSMSG_FAKEHOST_SECONDLEVEL_DELETED", "Group $b%s.%s$b successfully deleted." },
80     { "HSMSG_MANAGER_ALREADY", "$b%s$b is already a manager of %s.%s" },
81     { "HSMSG_MANAGER_ADDED", "$b%s$b is now a manager of %s.%s" },
82     { "HSMSG_MANAGER_NOT", "$b%s$b is not a manager of %s.%s" },
83     { "HSMSG_MANAGER_DELETED", "$b%s$b is no longer a manager of %s.%s" },
84     { "HSMSG_FAKEHOST_ASSIGN_SUCCESS", "Group $b%s.%s$b was assigned successfully." },
85     { "HSMSG_FAKEHOST_UNASSIGN_SUCCESS", "Group $b%s.%s$b was unassigned successfully." },
86     
87     { NULL, NULL }
88 };
89
90 static struct {
91     const char *nick;
92     const char *modes;
93     int toplevel_access;
94 } hostserv_conf;
95
96 const char *hostserv_module_deps[] = { NULL };
97 struct userNode *hostserv;
98 struct helpfile *hostserv_helpfile;
99 static struct module *hostserv_module;
100 static struct service *hostserv_service;
101 static struct log_type *HS_LOG;
102 static struct hs_toplevel *toplevels = NULL;
103 static struct hs_user *hostserv_users = NULL;
104 const char *title_suffix = NULL;
105
106 /* FAKEHOST STRUCTS */
107 struct hs_toplevel {
108     char *fakehost;
109     struct hs_manager *managers;
110     struct hs_secondlevel *secondlevel;
111     struct hs_toplevel *next;
112 };
113
114 struct hs_secondlevel {
115     struct hs_toplevel *toplevel;
116     char *fakehost;
117     struct hs_manager *managers;
118     struct hs_assignment *assignments;
119     struct hs_secondlevel *next;
120 };
121
122 struct hs_assignment {
123     struct hs_secondlevel *secondlevel;
124     struct hs_user *user;
125     int active;
126     struct hs_assignment *next;
127     struct hs_assignment *unext; /* for hs_user */
128 };
129
130 struct hs_manager {
131     char type;
132     void *object;
133     int active;
134     struct hs_user *user;
135     struct hs_manager *next;
136     struct hs_manager *unext; /* for hs_user */
137 };
138
139 struct hs_user {
140     struct handle_info *hi;
141     struct hs_assignment *assignments;
142     struct hs_manager *managements;
143     struct hs_user *next;
144 };
145
146 /* MANAGEMENT FUNCTIONS for FAKEHOST STRUCTS */
147 static void hs_del_secondlevel(struct hs_secondlevel *slfh, int remove_from_tlfh);
148 static void hs_del_manager(struct hs_manager *manager, int remove_from_object);
149 static void hs_del_assignment(struct hs_assignment *assignment, int remove_from_slfh);
150 static void hs_del_user(struct hs_user *user);
151
152 static void apply_fakehost(struct handle_info *handle, struct userNode *user);
153
154 static char *
155 generate_fakehost(struct handle_info *handle)
156 {
157     extern const char *hidden_host_suffix;
158     static char buffer[HOSTLEN+1];
159
160     if (!handle->fakehost) {
161         snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
162         return buffer;
163     } else if (handle->fakehost[0] == '.') {
164         /* A leading dot indicates the stored value is actually a title. */
165         snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, title_suffix);
166         return buffer;
167     } else if (handle->fakehost[0] == '$') {
168         /* A leading $ indicates the stored value begins with the user handle. */
169         snprintf(buffer, sizeof(buffer), "%s%s", handle->handle, handle->fakehost+1);
170         return buffer;
171     }
172     return handle->fakehost;
173 }
174
175 static char *
176 generate_fakeident(struct handle_info *handle, struct userNode *user)
177 {
178     static char buffer[USERLEN+1];
179
180     if (!handle->fakeident) {
181         if (!user)
182             return NULL;
183         safestrncpy(buffer, user->ident, sizeof(buffer));
184         return buffer;
185     }
186     return handle->fakeident;
187 }
188
189 static void
190 apply_fakehost(struct handle_info *handle, struct userNode *user)
191 {
192     struct userNode *target;
193     char *fakehost, *fakeident;
194
195     if (!handle->users)
196         return;
197
198     fakehost = generate_fakehost(handle);
199
200     if (user) {
201         fakeident = generate_fakeident(handle, user);
202         assign_fakehost(user, fakehost, fakeident, 0, 1);
203         return;
204     }
205
206     for (target = handle->users; target; target = target->next_authed) {
207         fakeident = generate_fakeident(handle, target);
208         assign_fakehost(target, fakehost, fakeident, 0, 1);
209     }
210     return;
211 }
212
213 static void hs_free_all() {
214     struct hs_toplevel *tlfh, *next_tlfh;
215     struct hs_secondlevel *slfh, *next_slfh;
216     struct hs_assignment *assng, *next_assng;
217     struct hs_manager *manager, *next_manager;
218     struct hs_user *user, *next_user;
219     for(tlfh = toplevels; tlfh; tlfh = next_tlfh) {
220         next_tlfh = tlfh->next;
221         for(manager = tlfh->managers; manager; manager = next_manager) {
222             next_manager = manager->next;
223             free(manager);
224         }
225         for(slfh = tlfh->secondlevel; slfh; slfh = next_slfh) {
226             next_slfh = slfh->next;
227             for(manager = tlfh->managers; manager; manager = next_manager) {
228                 next_manager = manager->next;
229                 free(manager);
230             }
231             for(assng = slfh->assignments; assng; assng = next_assng) {
232                 next_assng = assng->next;
233                 free(assng);
234             }
235             free(slfh->fakehost);
236             free(slfh);
237         }
238         free(tlfh->fakehost);
239         free(tlfh);
240     }
241     for(user = hostserv_users; user; user = next_user) {
242         next_user = user->next;
243         free(user);
244     }
245     toplevels = NULL;
246     hostserv_users = NULL;
247 }
248
249 static struct hs_toplevel *hs_add_toplevel(const char *name) {
250     struct hs_toplevel *tlfh = calloc(1, sizeof(*tlfh));
251     tlfh->fakehost = strdup(name);
252     tlfh->next = toplevels;
253     toplevels = tlfh;
254     return tlfh;
255 }
256
257 static void hs_del_toplevel(struct hs_toplevel *tlfh) {
258     //unassign all assignments
259     struct hs_secondlevel *slfh, *next_slfh;
260     struct hs_manager *manager, *next_manager;
261     for(manager = tlfh->managers; manager; manager = next_manager) {
262         next_manager = manager->next;
263         hs_del_manager(manager, 0);
264     }
265     for(slfh = tlfh->secondlevel; slfh; slfh = next_slfh) {
266         next_slfh = slfh->next;
267         hs_del_secondlevel(slfh, 0);
268     }
269     
270     struct hs_toplevel *ctlfh, *last_tlfh = NULL;
271     for(ctlfh = toplevels; ctlfh; ctlfh = ctlfh->next) {
272         if(ctlfh == tlfh) {
273             if(last_tlfh)
274                 last_tlfh->next = ctlfh->next;
275             else
276                 toplevels = ctlfh->next;
277         } else
278             last_tlfh = ctlfh;
279     }
280     free(tlfh->fakehost);
281     free(tlfh);
282 }
283
284 static struct hs_secondlevel *hs_add_secondlevel(struct hs_toplevel *tlfh, const char *name) {
285     struct hs_secondlevel *slfh = calloc(1, sizeof(*slfh));
286     slfh->toplevel = tlfh;
287     slfh->fakehost = strdup(name);
288     slfh->next = tlfh->secondlevel;
289     tlfh->secondlevel = slfh;
290     return slfh;
291 }
292
293 static void hs_del_secondlevel(struct hs_secondlevel *slfh, int remove_from_tlfh) {
294     if(remove_from_tlfh) {
295         struct hs_secondlevel *cslfh, *prev_slfh = NULL;
296         for(cslfh = slfh->toplevel->secondlevel; cslfh; cslfh = cslfh->next) {
297             if(cslfh == slfh) {
298                 if(prev_slfh)
299                     prev_slfh->next = slfh->next;
300                 else
301                     slfh->toplevel->secondlevel = slfh->next;
302                 break;
303             } else
304                 prev_slfh = cslfh;
305         }
306     }
307     struct hs_assignment *assng, *next_assng;
308     struct hs_manager *manager, *next_manager;
309     for(manager = slfh->managers; manager; manager = next_manager) {
310         next_manager = manager->next;
311         hs_del_manager(manager, 0);
312     }
313     for(assng = slfh->assignments; assng; assng = next_assng) {
314         next_assng = assng->next;
315         hs_del_assignment(assng, 0);
316     }
317     free(slfh->fakehost);
318     free(slfh);
319 }
320
321 static struct hs_manager *hs_add_manager_toplevel(struct hs_toplevel *tlfh, struct hs_user *user) {
322     struct hs_manager *manager = calloc(1, sizeof(*manager));
323     manager->user = user;
324     manager->type = 1;
325     manager->object = tlfh;
326     manager->unext = user->managements;
327     user->managements = manager;
328     manager->next = tlfh->managers;
329     tlfh->managers = manager;
330     return manager;
331 }
332
333 static struct hs_manager *hs_add_manager_secondlevel(struct hs_secondlevel *slfh, struct hs_user *user) {
334     struct hs_manager *manager = calloc(1, sizeof(*manager));
335     manager->user = user;
336     manager->type = 2;
337     manager->object = slfh;
338     manager->unext = user->managements;
339     user->managements = manager;
340     manager->next = slfh->managers;
341     slfh->managers = manager;
342     return manager;
343 }
344
345 static void hs_del_manager(struct hs_manager *manager, int remove_from_object) {
346     struct hs_manager *cmanager, *prev_manager = NULL;
347     if(remove_from_object) {
348         if(manager->type == 1) {
349             struct hs_toplevel *tlfh = manager->object;
350             for(cmanager = tlfh->managers; cmanager; cmanager = cmanager->next) {
351                 if(cmanager == manager) {
352                     if(prev_manager)
353                         prev_manager->next = manager->next;
354                     else
355                         tlfh->managers = manager->next;
356                     break;
357                 } else
358                     prev_manager = cmanager;
359             }
360         } else if(manager->type == 2) {
361             struct hs_secondlevel *slfh = manager->object;
362             for(cmanager = slfh->managers; cmanager; cmanager = cmanager->next) {
363                 if(cmanager == manager) {
364                     if(prev_manager)
365                         prev_manager->next = manager->next;
366                     else
367                         slfh->managers = manager->next;
368                     break;
369                 } else
370                     prev_manager = cmanager;
371             }
372         }
373         prev_manager = NULL;
374     }
375     if(remove_from_object != 2) {
376         for(cmanager = manager->user->managements; cmanager; cmanager = cmanager->unext) {
377             if(cmanager == manager) {
378                 if(prev_manager)
379                     prev_manager->unext = manager->unext;
380                 else
381                     manager->user->managements = manager->unext;
382                 break;
383             } else
384                 prev_manager = cmanager;
385         }
386         if(manager->user->managements == NULL && manager->user->assignments == NULL)
387             hs_del_user(manager->user);
388     }
389     free(manager);
390 }
391
392 static void activateAssignment(struct hs_assignment *assignment) {
393     struct hs_toplevel *tlfh;
394     struct hs_secondlevel *slfh;
395     struct hs_user *hs_user;
396     struct hs_assignment *assgn;
397     char fakehost[140];
398     char *prefix = "$";
399     
400     hs_user = assignment->user;
401     if(hs_user->assignments) {
402         for(assgn = hs_user->assignments; assgn; assgn = assgn->unext) {
403             assgn->active = 0;
404         }
405     }
406     slfh = assignment->secondlevel;
407     tlfh = slfh->toplevel;
408     sprintf(fakehost, "%s.%s.%s", prefix, slfh->fakehost, tlfh->fakehost);
409     hs_user->hi->fakehost = strdup(fakehost);
410     apply_fakehost(hs_user->hi, NULL);
411     assignment->active = 1;
412 }
413
414 static struct hs_assignment *hs_add_assignment(struct hs_secondlevel *slfh, struct hs_user *user) {
415     struct hs_assignment *assignment = calloc(1, sizeof(*assignment));
416     assignment->secondlevel = slfh;
417     assignment->user = user;
418     if(user->assignments == NULL)
419         assignment->active = 1;
420     assignment->next = slfh->assignments;
421     slfh->assignments = assignment;
422     assignment->unext = user->assignments;
423     user->assignments = assignment;
424     if(assignment->active) {
425         activateAssignment(assignment);
426     }
427     return assignment;
428 }
429
430 static void hs_del_assignment(struct hs_assignment *assignment, int remove_from_slfh) {
431     struct hs_assignment *cassignment, *prev_assignment = NULL;
432     if(remove_from_slfh) {
433         for(cassignment = assignment->secondlevel->assignments; cassignment; cassignment = cassignment->next) {
434             if(cassignment == assignment) {
435                 if(prev_assignment)
436                     prev_assignment->next = assignment->next;
437                 else
438                     assignment->secondlevel->assignments = assignment->next;
439                 break;
440             } else
441                 prev_assignment = cassignment;
442         }
443         prev_assignment = NULL;
444     }
445     if(remove_from_slfh != 2) {
446         prev_assignment = NULL;
447         for(cassignment = assignment->user->assignments; cassignment; cassignment = cassignment->unext) {
448             if(cassignment == assignment) {
449                 if(prev_assignment)
450                     prev_assignment->unext = assignment->unext;
451                 else
452                     assignment->user->assignments = assignment->unext;
453                 break;
454             } else
455                 prev_assignment = cassignment;
456         }
457         
458         if(assignment->active) {
459             struct handle_info *hi;
460             
461             hi = assignment->user->hi;
462             hi->fakehost = NULL;
463             apply_fakehost(hi, NULL);
464         }
465         
466         if(assignment->user->managements == NULL && assignment->user->assignments == NULL)
467             hs_del_user(assignment->user);
468     }
469     free(assignment);
470 }
471
472 static struct hs_user *hs_get_user(struct handle_info *hi, int create) {
473     struct hs_user *cuser;
474     for(cuser = hostserv_users; cuser; cuser = cuser->next) {
475         if(cuser->hi == hi)
476             return cuser;
477     }
478     if(create) {
479         cuser = calloc(1, sizeof(*cuser));
480         cuser->hi = hi;
481         cuser->next = hostserv_users;
482         hostserv_users = cuser;
483         return cuser;
484     } else
485         return NULL;
486 }
487
488 static void hs_del_user(struct hs_user *user) {
489     if(user->managements) {
490         struct hs_manager *manager, *next_manager;
491         for(manager = user->managements; manager; manager = next_manager) {
492             next_manager = manager->unext;
493             hs_del_manager(manager, 2);
494         }
495     }
496     if(user->assignments) {
497         struct hs_assignment *assng, *next_assng;
498         for(assng = user->assignments; assng; assng = next_assng) {
499             next_assng = assng->unext;
500             hs_del_assignment(assng, 2);
501         }
502     }
503     struct hs_user *cuser, *prev_user = NULL;
504     for(cuser = hostserv_users; cuser; cuser = cuser->next) {
505         if(cuser == user) {
506             if(prev_user)
507                 prev_user->next = user->next;
508             else
509                 hostserv_users = user->next;
510             break;
511         } else
512             prev_user = cuser;
513     }
514     free(user);
515 }
516
517 /* END OF MANAGEMENT FUNCTIONS */
518
519 static int check_management_access(struct handle_info *hi, struct hs_toplevel *tlfh, struct hs_secondlevel *slfh) {
520     if(!hi) 
521         return 0;
522     if(hi->opserv_level >= hostserv_conf.toplevel_access) 
523         return 1;
524     struct hs_user *user = hs_get_user(hi, 0);
525     if(!user)
526         return 0;
527     struct hs_manager *manager;
528     if(slfh) {
529         for(manager = user->managements; manager; manager = manager->next) {
530             if(manager->type == 2 && manager->object == slfh) 
531                 return 1;
532         }
533     }
534     if(tlfh) {
535         for(manager = user->managements; manager; manager = manager->next) {
536             if(manager->type == 1 && manager->object == tlfh) 
537                 return 1;
538         }
539     }
540     return 0;
541 }
542
543
544 static void cmd_view_toplevel_information(UNUSED_ARG(struct userNode *user), UNUSED_ARG(struct svccmd *cmd), struct hs_toplevel *tlfh) {
545     reply("HSMSG_TOPLEVEL_FAKEHOSTS", tlfh->fakehost);
546     struct hs_secondlevel *slfh;
547     if(!tlfh->secondlevel)
548         reply("HSMSG_ASSIGNED_NONE");
549     else
550     for(slfh = tlfh->secondlevel; slfh; slfh = slfh->next) {
551         struct hs_manager *cmanager;
552         int managers = 0;
553         for(cmanager = slfh->managers; cmanager; cmanager = cmanager->next)
554             managers++;
555         struct hs_assignment *assignment;
556         int assignments = 0;
557         for(assignment = slfh->assignments; assignment; assignment = assignment->next)
558             assignments++;
559         reply("HSMSG_TOPLEVEL_FAKEHOST", slfh->fakehost, tlfh->fakehost, assignments, managers);
560     }
561     reply("HSMSG_MANAGERS_TOPLEVEL", tlfh->fakehost);
562     struct hs_manager *cmanager;
563     if(!tlfh->managers)
564         reply("HSMSG_ASSIGNED_NONE");
565     else {
566         char managerBuf[351];
567         int managerPos = 0;
568         for(cmanager = tlfh->managers; cmanager; cmanager = cmanager->next) {
569             if(managerPos + strlen(cmanager->user->hi->handle) + 2 >= 350) {
570                 reply("HSMSG_MANAGERS_MANAGERS", managerBuf);
571                 managerPos = 0;
572             }
573             managerPos += sprintf(managerBuf + managerPos, (managerPos ? ", %s" : "%s"), cmanager->user->hi->handle);
574         }
575         reply("HSMSG_MANAGERS_MANAGERS", managerBuf);
576     }
577 }
578
579 static void cmd_view_secondlevel_information(UNUSED_ARG(struct userNode *user), UNUSED_ARG(struct svccmd *cmd), struct hs_secondlevel *slfh) {
580     reply("HSMSG_FAKEHOST_ASSIGNMENTS", slfh->fakehost, slfh->toplevel->fakehost);
581     struct hs_assignment *assignment;
582     for(assignment = slfh->assignments; assignment; assignment = assignment->next) {
583         reply((assignment->active ? "HSMSG_FAKEHOST_ASSIGNMENT_ACTIVE" : "HSMSG_FAKEHOST_ASSIGNMENT"), assignment->user->hi->handle, assignment->user->hi->handle, slfh->fakehost, slfh->toplevel->fakehost);
584     }
585     reply("HSMSG_MANAGERS_FAKEHOST", slfh->fakehost, slfh->toplevel->fakehost);
586     struct hs_manager *cmanager;
587     if(!slfh->managers)
588         reply("HSMSG_ASSIGNED_NONE");
589     else {
590         char managerBuf[351];
591         int managerPos = 0;
592         for(cmanager = slfh->managers; cmanager; cmanager = cmanager->next) {
593             if(managerPos + strlen(cmanager->user->hi->handle) + 2 >= 350) {
594                 reply("HSMSG_MANAGERS_MANAGERS", managerBuf);
595                 managerPos = 0;
596             }
597             managerPos += sprintf(managerBuf + managerPos, (managerPos ? ", %s" : "%s"), cmanager->user->hi->handle);
598         }
599         reply("HSMSG_MANAGERS_MANAGERS", managerBuf);
600     }
601 }
602
603 static MODCMD_FUNC(cmd_addhost) {
604     struct handle_info *hi;
605     if (!(hi = user->handle_info)) {
606         reply("NSMSG_MUST_AUTH");
607         return 0;
608     }
609     char *slfh_name = argv[1];
610     char *tlfh_name = strchr(argv[1], '.');
611     if(!tlfh_name) {
612         reply("HSMSG_TOPLEVEL_INVALID", slfh_name);
613         return 0;
614     }
615     *tlfh_name = '\0';
616     tlfh_name++;
617     if(strchr(tlfh_name, '.')) {
618         reply("HSMSG_UNKNOWN_FAKEHOST", slfh_name, tlfh_name);
619         return 0;
620     }
621     if(!irccasecmp(slfh_name, "*")) {
622         if(hi->opserv_level >= hostserv_conf.toplevel_access) {
623             struct hs_toplevel *tlfh;
624             for(tlfh = toplevels; tlfh; tlfh = tlfh->next) {
625                 if(!irccasecmp(tlfh->fakehost, tlfh_name)) break;
626             }
627             if(tlfh) {
628                 reply("HSMSG_FAKEHOST_TOPLEVEL_ALREADY_EXISTS", slfh_name, tlfh_name);
629                 return 0;
630             }
631             hs_add_toplevel(tlfh_name);
632             reply("HSMSG_FAKEHOST_TOPLEVEL_ADDED", tlfh_name);
633             return 1;
634         }
635         reply("HSMSG_UNKNOWN_FAKEHOST", slfh_name, tlfh_name);
636         return 0;
637     } else {
638         struct hs_secondlevel *slfh;
639         struct hs_toplevel *tlfh;
640         for(tlfh = toplevels; tlfh; tlfh = tlfh->next) {
641             if(!irccasecmp(tlfh->fakehost, tlfh_name)) break;
642         }
643         if(!tlfh) {
644             reply("HSMSG_UNKNOWN_FAKEHOST", slfh_name, tlfh_name);
645             return 0;
646         }
647         for(slfh = tlfh->secondlevel; slfh; slfh = slfh->next) {
648             if(!irccasecmp(slfh->fakehost, slfh_name)) break;
649         }
650         if(slfh) {
651             reply("HSMSG_UNKNOWN_FAKEHOST", slfh_name, tlfh_name);
652             return 0;
653         }
654         if(!check_management_access(hi, tlfh, NULL)) {
655             reply("HSMSG_FAKEHOST_SECONDLEVEL_ALREADY_EXISTS", slfh_name, tlfh_name);
656             return 0;
657         }
658         hs_add_secondlevel(tlfh, slfh_name);
659         reply("HSMSG_FAKEHOST_SECONDLEVEL_ADDED", slfh_name, tlfh_name);
660     }
661     return 0;
662 }
663
664 static MODCMD_FUNC(cmd_delhost) {
665     struct handle_info *hi;
666     if (!(hi = user->handle_info)) {
667         reply("NSMSG_MUST_AUTH");
668         return 0;
669     }
670     char *slfh_name = argv[1];
671     char *tlfh_name = strchr(argv[1], '.');
672     *tlfh_name = '\0';
673     tlfh_name++;
674     if(strchr(tlfh_name, '.')) {
675         reply("HSMSG_UNKNOWN_FAKEHOST", slfh_name, tlfh_name);
676         return 0;
677     }
678     struct hs_toplevel *tlfh;
679     for(tlfh = toplevels; tlfh; tlfh = tlfh->next) {
680         if(!irccasecmp(tlfh->fakehost, tlfh_name)) break;
681     }
682     if(!tlfh) {
683         reply("HSMSG_UNKNOWN_FAKEHOST", slfh_name, tlfh_name);
684         return 0;
685     }
686     if(!irccasecmp(slfh_name, "*")) {
687         if(hi->opserv_level < hostserv_conf.toplevel_access) {
688             reply("HSMSG_UNKNOWN_FAKEHOST", slfh_name, tlfh_name);
689             return 0;
690         }
691         hs_del_toplevel(tlfh);
692         reply("HSMSG_FAKEHOST_TOPLEVEL_DELETED", tlfh_name);
693         return 1;
694     } else {
695         struct hs_secondlevel *slfh;
696         for(slfh = tlfh->secondlevel; slfh; slfh = slfh->next) {
697             if(!irccasecmp(slfh->fakehost, slfh_name)) break;
698         }
699         if(!slfh || !check_management_access(hi, tlfh, NULL)) {
700             reply("HSMSG_UNKNOWN_FAKEHOST", slfh_name, tlfh_name);
701             return 0;
702         }
703         hs_del_secondlevel(slfh, 1);
704         reply("HSMSG_FAKEHOST_SECONDLEVEL_DELETED", slfh_name, tlfh_name);
705     }
706     return 0;
707 }
708
709 static MODCMD_FUNC(cmd_view) {
710     struct handle_info *hi;
711     if(argc >= 2 && !strchr(argv[1], '.')) {
712         if (!(hi = modcmd_get_handle_info(user, argv[1])))
713             return 0;
714     } else if(argc >= 2) {
715         if (!(hi = user->handle_info)) {
716             reply("NSMSG_MUST_AUTH");
717             return 0;
718         }
719         char *slfh_name = argv[1];
720         char *tlfh_name = strchr(argv[1], '.');
721         *tlfh_name = '\0';
722         tlfh_name++;
723         if(strchr(tlfh_name, '.')) {
724             reply("HSMSG_UNKNOWN_FAKEHOST", slfh_name, tlfh_name);
725             return 0;
726         }
727         struct hs_toplevel *tlfh;
728         for(tlfh = toplevels; tlfh; tlfh = tlfh->next) {
729             if(!irccasecmp(tlfh->fakehost, tlfh_name)) break;
730         }
731         if(!tlfh) {
732             reply("HSMSG_UNKNOWN_FAKEHOST", slfh_name, tlfh_name);
733             return 0;
734         }
735         if(!irccasecmp(slfh_name, "*")) {
736             if(!check_management_access(hi, tlfh, NULL)) {
737                 reply("HSMSG_UNKNOWN_FAKEHOST", slfh_name, tlfh_name);
738                 return 0;
739             }
740             cmd_view_toplevel_information(user, cmd, tlfh);
741             return 1;
742         } else {
743             struct hs_secondlevel *slfh;
744             for(slfh = tlfh->secondlevel; slfh; slfh = slfh->next) {
745                 if(!irccasecmp(slfh->fakehost, slfh_name)) break;
746             }
747             if(!slfh || !check_management_access(hi, tlfh, slfh)) {
748                 reply("HSMSG_UNKNOWN_FAKEHOST", slfh_name, tlfh_name);
749                 return 0;
750             }
751             cmd_view_secondlevel_information(user, cmd, slfh);
752             return 1;
753         }
754     } else {
755         if (!(hi = user->handle_info)) {
756             reply("NSMSG_MUST_AUTH");
757             return 0;
758         }
759     }
760     struct hs_user *huser = hs_get_user(hi, 0);
761     reply("HSMSG_ASSIGNED_FAKEHOSTS", hi->handle);
762     int assigncount = 0;
763     if(huser) {
764         struct hs_assignment *assignment;
765         for(assignment = huser->assignments; assignment; assignment = assignment->unext) {
766             reply((assignment->active ? "HSMSG_ASSIGNED_FAKEHOST_ACTTIVE" : "HSMSG_ASSIGNED_FAKEHOST"), assignment->secondlevel->fakehost, assignment->secondlevel->toplevel->fakehost);
767             assigncount++;
768         }
769         if(assigncount && huser->hi == user->handle_info)
770             reply("HSMSG_ASSIGN_HOWTO");
771     }
772     if(!assigncount)
773         reply("HSMSG_ASSIGNED_NONE");
774     if(user->handle_info == hi && hi->opserv_level >= hostserv_conf.toplevel_access) {
775         struct hs_toplevel *tlfh;
776         for(tlfh = toplevels; tlfh; tlfh = tlfh->next) {
777             struct hs_secondlevel *slfh;
778             struct hs_assignment *assignment;
779             int slfhs = 0, assignments = 0;
780             for(slfh = tlfh->secondlevel; slfh; slfh = slfh->next) {
781                 slfhs++;
782                 for(assignment = slfh->assignments; assignment; assignment = assignment->next)
783                     assignments++;
784             }
785             reply("HSMSG_MANAGED_TOPLEVEL", tlfh->fakehost, slfhs, assignments);
786         }
787         reply("HSMSG_MANAGE_HOWTO");
788     } else if(huser && huser->managements) {
789         reply("HSMSG_MANAGED_FAKEHOSTS", hi->handle);
790         struct hs_manager *manager;
791         for(manager = huser->managements; manager; manager = manager->unext) {
792             if(manager->type == 1) {
793                 struct hs_toplevel *tlfh = manager->object;
794                 struct hs_secondlevel *slfh;
795                 struct hs_assignment *assignment;
796                 int slfhs = 0, assignments = 0;
797                 for(slfh = tlfh->secondlevel; slfh; slfh = slfh->next) {
798                     slfhs++;
799                     for(assignment = slfh->assignments; assignment; assignment = assignment->next)
800                         assignments++;
801                 }
802                 reply("HSMSG_MANAGED_TOPLEVEL", tlfh->fakehost, slfhs, assignments);
803             }
804         }
805         for(manager = huser->managements; manager; manager = manager->next) {
806             if(manager->type == 2) {
807                 struct hs_secondlevel *slfh = manager->object;
808                 struct hs_toplevel *tlfh = slfh->toplevel;
809                 //check if the user is already a manager of the tlfh
810                 struct hs_manager *cmanager;
811                 for(cmanager = tlfh->managers; cmanager; cmanager = cmanager->next) {
812                     if(cmanager->user == huser) break;
813                 }
814                 if(cmanager) continue;
815                 struct hs_assignment *assignment;
816                 int assignments = 0;
817                 for(assignment = slfh->assignments; assignment; assignment = assignment->next)
818                     assignments++;
819                 reply("HSMSG_MANAGED_FAKEHOST", slfh->fakehost, tlfh->fakehost, assignments);
820             }
821         }
822         if(huser->hi == user->handle_info)
823             reply("HSMSG_MANAGE_HOWTO");
824     }
825     return 1;
826 }
827
828 static MODCMD_FUNC(cmd_addmanager) {
829     struct handle_info *hi;
830     char *fakehost;
831     if(argc >= 3) {
832         if(!strchr(argv[1], '.')) {
833             if (!(hi = modcmd_get_handle_info(user, argv[1])))
834                 return 0;
835             fakehost = argv[2];
836         } else {
837             if (!(hi = modcmd_get_handle_info(user, argv[2])))
838                 return 0;
839             fakehost = argv[1];
840         }
841         char *slfh_name = fakehost;
842         char *tlfh_name = strchr(fakehost, '.');
843         *tlfh_name = '\0';
844         tlfh_name++;
845         if(strchr(tlfh_name, '.')) {
846             reply("HSMSG_UNKNOWN_FAKEHOST", slfh_name, tlfh_name);
847             return 0;
848         }
849         struct hs_toplevel *tlfh;
850         struct hs_secondlevel *slfh = NULL;
851         for(tlfh = toplevels; tlfh; tlfh = tlfh->next) {
852             if(!irccasecmp(tlfh->fakehost, tlfh_name)) break;
853         }
854         if(!tlfh) {
855             reply("HSMSG_UNKNOWN_FAKEHOST", slfh_name, tlfh_name);
856             return 0;
857         }
858         if(!irccasecmp(slfh_name, "*")) {
859             if(!check_management_access(user->handle_info, tlfh, NULL)) {
860                 reply("HSMSG_UNKNOWN_FAKEHOST", slfh_name, tlfh_name);
861                 return 0;
862             }
863         } else {
864             for(slfh = tlfh->secondlevel; slfh; slfh = slfh->next) {
865                 if(!irccasecmp(slfh->fakehost, slfh_name)) break;
866             }
867             if(!slfh || !check_management_access(user->handle_info, tlfh, slfh)) {
868                 reply("HSMSG_UNKNOWN_FAKEHOST", slfh_name, tlfh_name);
869                 return 0;
870             }
871         }
872         struct hs_user *huser = hs_get_user(hi, 1);
873         struct hs_manager *manager;
874         if(slfh) {
875             for(manager = huser->managements; manager; manager = manager->next) {
876                 if(manager->type == 2 && manager->object == slfh) {
877                     reply("HSMSG_MANAGER_ALREADY", hi->handle, slfh_name, tlfh_name);
878                     return 0;
879                 }
880             }
881         }
882         for(manager = huser->managements; manager; manager = manager->next) {
883             if(manager->type == 1 && manager->object == tlfh) {
884                 reply("HSMSG_MANAGER_ALREADY", hi->handle, "*", tlfh_name);
885                 return 0;
886             }
887         }
888         if(slfh)
889             hs_add_manager_secondlevel(slfh, huser);
890         else
891             hs_add_manager_toplevel(tlfh, huser);
892         reply("HSMSG_MANAGER_ADDED", hi->handle, slfh_name, tlfh_name);
893         return 1;
894     }
895     return 0;
896 }
897
898 static MODCMD_FUNC(cmd_delmanager) {
899     struct handle_info *hi;
900     char *fakehost;
901     if(!strchr(argv[1], '.')) {
902         if (!(hi = modcmd_get_handle_info(user, argv[1])))
903             return 0;
904         fakehost = argv[2];
905     } else {
906         if (!(hi = modcmd_get_handle_info(user, argv[2])))
907             return 0;
908         fakehost = argv[1];
909     }
910     if(!fakehost) {
911         reply("HSMSG_ASSIGNED_NONE");
912         return 0;
913     }
914     char *slfh_name = fakehost;
915     char *tlfh_name = strchr(fakehost, '.');
916     if(tlfh_name) {
917         *tlfh_name = '\0';
918     } else {
919         reply("HSMSG_ASSIGNED_NONE");
920         return 0;
921     }
922     tlfh_name++;
923     if(strchr(tlfh_name, '.')) {
924         reply("HSMSG_UNKNOWN_FAKEHOST", slfh_name, tlfh_name);
925         return 0;
926     }
927     struct hs_toplevel *tlfh;
928     struct hs_secondlevel *slfh = NULL;
929     for(tlfh = toplevels; tlfh; tlfh = tlfh->next) {
930         if(!irccasecmp(tlfh->fakehost, tlfh_name)) break;
931     }
932     if(!tlfh) {
933         reply("HSMSG_UNKNOWN_FAKEHOST", slfh_name, tlfh_name);
934         return 0;
935     }
936     if(!irccasecmp(slfh_name, "*")) {
937         if(!check_management_access(user->handle_info, tlfh, NULL)) {
938             reply("HSMSG_UNKNOWN_FAKEHOST", slfh_name, tlfh_name);
939             return 0;
940         }
941     } else {
942         for(slfh = tlfh->secondlevel; slfh; slfh = slfh->next) {
943             if(!irccasecmp(slfh->fakehost, slfh_name)) break;
944         }
945         if(!slfh || !check_management_access(user->handle_info, tlfh, slfh)) {
946             reply("HSMSG_UNKNOWN_FAKEHOST", slfh_name, tlfh_name);
947             return 0;
948         }
949     }
950     struct hs_user *huser = hs_get_user(hi, 0);
951     struct hs_manager *manager;
952     if(!huser) {
953         reply("HSMSG_MANAGER_NOT", hi->handle, slfh_name, tlfh_name);
954         return 0;
955     }
956     if(slfh) {
957         for(manager = huser->managements; manager; manager = manager->next) {
958             if(manager->type == 2 && manager->object == slfh) 
959                 break;
960         }
961         if(!manager) {
962             reply("HSMSG_MANAGER_NOT", hi->handle, slfh_name, tlfh_name);
963             return 0;
964         }
965     } else {
966         for(manager = huser->managements; manager; manager = manager->next) {
967             if(manager->type == 1 && manager->object == tlfh) 
968                 break;
969         }
970         if(!manager) {
971             reply("HSMSG_MANAGER_NOT", hi->handle, "*", tlfh_name);
972             return 0;
973         }
974     }
975     hs_del_manager(manager, 1);
976     reply("HSMSG_MANAGER_DELETED", hi->handle, slfh_name, tlfh_name);
977     return 1;
978 }
979
980 static MODCMD_FUNC(cmd_set) {
981     struct handle_info *hi;
982     struct hs_user *hs_user;
983     struct hs_assignment *assignment;
984     struct hs_assignment *assgn;
985     struct hs_toplevel *tlfh;
986     struct hs_secondlevel *slfh;
987     char *fakehost;
988     
989     if (!(hi = user->handle_info)) {
990         reply("NSMSG_MUST_AUTH");
991         return 0;
992     }
993     hs_user = hs_get_user(hi, 0);
994     if(!strcmp(argv[1], "*")) {
995         hi->fakehost = NULL;
996         apply_fakehost(hi, NULL);
997         if(hs_user->assignments) {
998             for(assgn = hs_user->assignments; assgn; assgn = assgn->unext) {
999                 assgn->active = 0;
1000             }
1001         }
1002         return 1;
1003     } else {
1004         if(!strchr(argv[1], '.')) {
1005             return 0;
1006         }
1007         fakehost = argv[1];
1008         char *slfh_name = fakehost;
1009         char *tlfh_name = strchr(fakehost, '.');
1010         *tlfh_name = '\0';
1011         tlfh_name++;
1012         if(strchr(tlfh_name, '.')) {
1013             reply("HSMSG_UNKNOWN_FAKEHOST", slfh_name, tlfh_name);
1014             return 0;
1015         }
1016         for(assignment = hs_user->assignments; assignment; assignment = assignment->unext) {
1017             slfh = assignment->secondlevel;
1018             tlfh = slfh->toplevel;
1019             if(!irccasecmp(slfh_name, slfh->fakehost)) {
1020                 if(!irccasecmp(tlfh_name, tlfh->fakehost)) {
1021                     activateAssignment(assignment);
1022                     reply("HSMSG_FAKEHOST_SET_SUCCESS", slfh->fakehost, tlfh->fakehost);
1023                     return 1;
1024                 }
1025             }
1026         }
1027         reply("HSMSG_UNKNOWN_FAKEHOST", slfh_name, tlfh_name);
1028         return 0;
1029     }
1030 }
1031
1032 static MODCMD_FUNC(cmd_assign) {
1033     struct handle_info *hi;
1034     struct handle_info *hiuser;
1035     char *fakehost;
1036     if(!strchr(argv[1], '.')) {
1037         if (!(hi = modcmd_get_handle_info(user, argv[1])))
1038             return 0;
1039         fakehost = argv[2];
1040     } else {
1041         if (!(hi = modcmd_get_handle_info(user, argv[2])))
1042             return 0;
1043         fakehost = argv[1];
1044     }
1045     if (!(hiuser = user->handle_info)) {
1046         reply("NSMSG_MUST_AUTH");
1047         return 0;
1048     }
1049     if(!fakehost) {
1050         reply("HSMSG_ASSIGNED_NONE");
1051         return 0;
1052     }
1053     char *slfh_name = fakehost;
1054     char *tlfh_name = strchr(fakehost, '.');
1055     if(tlfh_name) {
1056         *tlfh_name = '\0';
1057     } else {
1058         reply("HSMSG_ASSIGNED_NONE");
1059         return 0;
1060     }
1061     tlfh_name++;
1062     if(strchr(tlfh_name, '.')) {
1063         reply("HSMSG_UNKNOWN_FAKEHOST", slfh_name, tlfh_name);
1064         return 0;
1065     }
1066     struct hs_toplevel *tlfh;;
1067     struct hs_secondlevel *slfh;
1068     struct hs_user *hs_user = hs_get_user(hi, 1);
1069     for(tlfh = toplevels; tlfh; tlfh = tlfh->next) {
1070         if(!irccasecmp(tlfh_name, tlfh->fakehost)) {
1071             for(slfh = tlfh->secondlevel; slfh; slfh = slfh->next) {
1072                 if(!irccasecmp(slfh_name, slfh->fakehost)) {
1073                     if(check_management_access(hiuser, tlfh, slfh)) {
1074                         hs_add_assignment(slfh, hs_user);
1075                         reply("HSMSG_FAKEHOST_ASSIGN_SUCCESS", slfh_name, tlfh_name);
1076                         return 1;
1077                     }
1078                     reply("HSMSG_UNKNOWN_FAKEHOST", slfh_name, tlfh_name);
1079                     return 0;
1080                 }
1081             }
1082         }
1083     }
1084     reply("HSMSG_UNKNOWN_FAKEHOST", slfh_name, tlfh_name);
1085     return 0;
1086 }
1087
1088 static MODCMD_FUNC(cmd_unassign) {
1089     struct handle_info *hi;
1090     struct handle_info *hiuser;
1091     char *fakehost;
1092     if(!strchr(argv[1], '.')) {
1093         if (!(hi = modcmd_get_handle_info(user, argv[1])))
1094             return 0;
1095         fakehost = argv[2];
1096     } else {
1097         if (!(hi = modcmd_get_handle_info(user, argv[2])))
1098             return 0;
1099         fakehost = argv[1];
1100     }
1101     if (!(hiuser = user->handle_info)) {
1102         reply("NSMSG_MUST_AUTH");
1103         return 0;
1104     }
1105     if(!fakehost) {
1106         reply("HSMSG_ASSIGNED_NONE");
1107         return 0;
1108     }
1109     char *slfh_name = fakehost;
1110     char *tlfh_name = strchr(fakehost, '.');
1111     if(tlfh_name) {
1112         *tlfh_name = '\0';
1113     } else {
1114         reply("HSMSG_ASSIGNED_NONE");
1115         return 0;
1116     }
1117     tlfh_name++;
1118     if(strchr(tlfh_name, '.')) {
1119         reply("HSMSG_UNKNOWN_FAKEHOST", slfh_name, tlfh_name);
1120         return 0;
1121     }
1122     struct hs_assignment *assignment;
1123     struct hs_user *hs_user = hs_get_user(hi, 0);
1124     for(assignment = hs_user->assignments; assignment; assignment = assignment->unext) {
1125         if(!irccasecmp(slfh_name, assignment->secondlevel->fakehost)) {
1126             if(!irccasecmp(tlfh_name, assignment->secondlevel->toplevel->fakehost)) {
1127                 if(check_management_access(hiuser, assignment->secondlevel->toplevel, assignment->secondlevel)) {
1128                     hs_del_assignment(assignment, 1);
1129                     reply("HSMSG_FAKEHOST_UNASSIGN_SUCCESS", slfh_name, tlfh_name);
1130                     return 1;
1131                 }
1132                 reply("HSMSG_UNKNOWN_FAKEHOST", slfh_name, tlfh_name);
1133                 return 0;
1134             }
1135         }
1136     }
1137     reply("HSMSG_UNKNOWN_FAKEHOST", slfh_name, tlfh_name);
1138     return 0;
1139 }
1140
1141 static void hostserv_conf_read(void)
1142 {
1143     dict_t conf_node;
1144     const char *str;
1145
1146     str = "modules/hostserv";
1147     if (!(conf_node = conf_get_data(str, RECDB_OBJECT))) {
1148         log_module(HS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", str);
1149         return;
1150     }
1151
1152     str = database_get_data(conf_node, "nick", RECDB_QSTRING);
1153     if(hostserv_conf.nick && strcmp(hostserv_conf.nick, str)) {
1154         //nick changed
1155     }
1156     hostserv_conf.nick = str;
1157     
1158     str = database_get_data(conf_node, "modes", RECDB_QSTRING);
1159     hostserv_conf.modes = (str ? str : NULL);
1160     
1161     str = database_get_data(conf_node, "toplevel_access", RECDB_QSTRING);
1162     unsigned int toplevel_access = atoi(str);
1163     hostserv_conf.toplevel_access = (toplevel_access ? toplevel_access : 600);
1164     
1165     /*str = database_get_data(conf_node, "description", RECDB_QSTRING);
1166     hostserv_conf.description = (str ? str : NULL);*/
1167     str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
1168     title_suffix = str ? str : "example.net";
1169 }
1170
1171 static int hostserv_saxdb_read_secondlevel(const char *name, void *data, UNUSED_ARG(void *extra));
1172 static int hostserv_saxdb_read_assignments(const char *name, void *data, UNUSED_ARG(void *extra));
1173
1174 static int hostserv_saxdb_read_toplevel(const char *name, void *data, UNUSED_ARG(void *extra)) {
1175     struct record_data *rd = data;
1176     struct hs_toplevel *tlfh;
1177     struct hs_manager *managerTL;
1178     struct hs_user *user;
1179     struct dict *object;
1180
1181      if (rd->type == RECDB_OBJECT) {
1182         dict_t db = GET_RECORD_OBJECT(rd);
1183         dict_iterator_t it;
1184         
1185         tlfh = hs_add_toplevel(name);
1186         
1187         if ((object = database_get_data(db, KEY_MANAGERS, RECDB_OBJECT))) {
1188             for (it = dict_first(object); it; it = iter_next(it)) {
1189                 user = hs_get_user(get_handle_info(iter_key(it)), 1);
1190                 //rd = iter_data(it);
1191                 /* nothing in here, yet */
1192                 managerTL = hs_add_manager_toplevel(tlfh, user);
1193                 if (database_get_data(db, KEY_ACTIVE, RECDB_QSTRING))
1194                     managerTL->active = 1;
1195                 else
1196                     managerTL->active = 0;
1197             }
1198         }
1199         
1200         if ((object = database_get_data(db, KEY_SECONDLEVEL, RECDB_OBJECT)))
1201             dict_foreach(object, hostserv_saxdb_read_secondlevel, tlfh);
1202     }
1203     return 0;
1204 }
1205
1206 static int hostserv_saxdb_read_secondlevel(const char *name, void *data, UNUSED_ARG(void *extra)) {
1207     struct record_data *rd = data;
1208     struct hs_toplevel *tlfh = extra;
1209     struct hs_secondlevel *slfh;
1210     struct hs_manager *managerSL;
1211     struct hs_user *user;
1212     struct dict *object;
1213
1214     if (rd->type == RECDB_OBJECT) {
1215         dict_t db = GET_RECORD_OBJECT(rd);
1216         dict_iterator_t it;
1217         
1218         slfh = hs_add_secondlevel(tlfh, name);
1219         
1220         if ((object = database_get_data(db, KEY_MANAGERS, RECDB_OBJECT))) {
1221             for (it = dict_first(object); it; it = iter_next(it)) {
1222                 user = hs_get_user(get_handle_info(iter_key(it)), 1);
1223                 //rd = iter_data(it);
1224                 /* nothing in here, yet */
1225                 managerSL = hs_add_manager_secondlevel(slfh, user);
1226                 if (database_get_data(db, KEY_ACTIVE, RECDB_QSTRING))
1227                     managerSL->active = 1;
1228                 else
1229                     managerSL->active = 0;
1230             }
1231         }
1232         
1233         if ((object = database_get_data(db, KEY_ASSIGNMENTS, RECDB_OBJECT)))
1234             dict_foreach(object, hostserv_saxdb_read_assignments, slfh);
1235     }
1236     return 0;
1237 }
1238
1239 static int hostserv_saxdb_read_assignments(const char *name, void *data, UNUSED_ARG(void *extra)) {
1240     struct record_data *rd = data;
1241     struct hs_secondlevel *slfh = extra;
1242     struct hs_user *user;
1243     struct hs_assignment *assng;
1244     
1245     if (rd->type == RECDB_OBJECT) {
1246         dict_t db = GET_RECORD_OBJECT(rd);
1247         
1248         user = hs_get_user(get_handle_info(name), 1);
1249         assng = hs_add_assignment(slfh, user);
1250         
1251         if (database_get_data(db, KEY_ACTIVE, RECDB_QSTRING))
1252             assng->active = 1;
1253         else
1254             assng->active = 0;
1255     }
1256     
1257     return 0;
1258 }
1259
1260 static int
1261 hostserv_saxdb_read(struct dict *db)
1262 {
1263     struct dict *object;
1264
1265     if ((object = database_get_data(db, KEY_TOPLEVEL, RECDB_OBJECT)))
1266         dict_foreach(object, hostserv_saxdb_read_toplevel, NULL);
1267
1268     return 1;
1269 }
1270
1271 static int
1272 hostserv_saxdb_write(struct saxdb_context *ctx)
1273 {
1274     struct hs_toplevel *tlfh;
1275     struct hs_secondlevel *slfh;
1276     struct hs_assignment *assng;
1277     struct hs_manager *manager;
1278
1279     saxdb_start_record(ctx, KEY_TOPLEVEL, 1);
1280     for(tlfh = toplevels; tlfh; tlfh = tlfh->next) {
1281         saxdb_start_record(ctx, tlfh->fakehost, 1);
1282         
1283         saxdb_start_record(ctx, KEY_MANAGERS, 1);
1284         for(manager = tlfh->managers; manager; manager = manager->next) {
1285             saxdb_start_record(ctx, manager->user->hi->handle, 0);
1286             //additional manager information?
1287             if(manager->active)
1288                 saxdb_write_int(ctx, KEY_ACTIVE, manager->active);
1289             saxdb_end_record(ctx);
1290         }
1291         saxdb_end_record(ctx);
1292         
1293         saxdb_start_record(ctx, KEY_SECONDLEVEL, 1);
1294         for(slfh = tlfh->secondlevel; slfh; slfh = slfh->next) {
1295             saxdb_start_record(ctx, slfh->fakehost, 1);
1296             
1297             saxdb_start_record(ctx, KEY_MANAGERS, 1);
1298             for(manager = slfh->managers; manager; manager = manager->next) {
1299                 saxdb_start_record(ctx, manager->user->hi->handle, 0);
1300                 //additional manager information?
1301                 if(manager->active)
1302                     saxdb_write_int(ctx, KEY_ACTIVE, manager->active);
1303                 saxdb_end_record(ctx);
1304             }
1305             saxdb_end_record(ctx);
1306             
1307             saxdb_start_record(ctx, KEY_ASSIGNMENTS, 1);
1308             for(assng = slfh->assignments; assng; assng = assng->next) {
1309                 saxdb_start_record(ctx, assng->user->hi->handle, 0);
1310                 //additional assignment information?
1311                 if(assng->active)
1312                     saxdb_write_int(ctx, KEY_ACTIVE, assng->active);
1313                 saxdb_end_record(ctx);
1314             }
1315             saxdb_end_record(ctx);
1316             
1317             saxdb_end_record(ctx);
1318         }
1319         saxdb_end_record(ctx);
1320         
1321         saxdb_end_record(ctx);
1322     }
1323     saxdb_end_record(ctx);
1324     
1325     return 0;
1326 }
1327
1328
1329 static void hostserv_db_cleanup(void) {
1330     hs_free_all();
1331 }
1332
1333 int hostserv_init() {
1334     HS_LOG = log_register_type("HostServ", "file:hostserv.log");
1335     
1336     const char *nick, *modes;
1337     if((nick = conf_get_data("modules/hostserv/nick", RECDB_QSTRING))) {
1338         modes = conf_get_data("modules/hostserv/modes", RECDB_QSTRING);
1339         hostserv = AddLocalUser(nick, nick, NULL, "Host Service", modes);
1340         hostserv_service = service_register(hostserv);
1341         hostserv_service->trigger = '*';
1342     }
1343         
1344     conf_register_reload(hostserv_conf_read);
1345     reg_exit_func(hostserv_db_cleanup);
1346     saxdb_register("HostServ", hostserv_saxdb_read, hostserv_saxdb_write);
1347     hostserv_module = module_register("HostServ", HS_LOG, "mod-hostserv.help", NULL);
1348     modcmd_register(hostserv_module, "view", cmd_view, 0, MODCMD_REQUIRE_AUTHED, NULL);
1349     modcmd_register(hostserv_module, "addmanager", cmd_addmanager, 3, MODCMD_REQUIRE_AUTHED, NULL);
1350     modcmd_register(hostserv_module, "delmanager", cmd_delmanager, 3, MODCMD_REQUIRE_AUTHED, NULL);
1351     modcmd_register(hostserv_module, "set", cmd_set, 2, MODCMD_REQUIRE_AUTHED, NULL);
1352     modcmd_register(hostserv_module, "assign", cmd_assign, 3, MODCMD_REQUIRE_AUTHED, NULL);
1353     modcmd_register(hostserv_module, "unassign", cmd_unassign, 3, MODCMD_REQUIRE_AUTHED, NULL);
1354     modcmd_register(hostserv_module, "addhost", cmd_addhost, 2, MODCMD_REQUIRE_AUTHED, NULL);
1355     modcmd_register(hostserv_module, "delhost", cmd_delhost, 2, MODCMD_REQUIRE_AUTHED, NULL);
1356     message_register_table(msgtab);
1357     return 1;
1358 }
1359
1360 int hostserv_finalize(void) {
1361     dict_t conf_node;
1362     const char *str;
1363
1364     str = "modules/hostserv";
1365     if (!(conf_node = conf_get_data(str, RECDB_OBJECT))) {
1366         log_module(HS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", str);
1367         return 0;
1368     }
1369
1370     str = database_get_data(conf_node, "nick", RECDB_QSTRING);
1371     if (str) hostserv_conf.nick = str;
1372     
1373     str = database_get_data(conf_node, "modes", RECDB_QSTRING);
1374     if (str) hostserv_conf.modes = str; 
1375     return 1;
1376 }