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