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