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