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