/* HostServ functions
 *
 * (C) 2003 Anope Team
 * Contact us at info@anope.org
 *
 * Please read COPYING and README for furhter details.
 *
 * Based on the original code of Epona by Lara.
 * Based on the original code of Services by Andy Church. 
 * 
 * $Id: hostserv.c,v 1.53 2004/02/15 22:40:46 dane Exp $ 
 *
 */

/*************************************************************************/
#include "services.h"
#include "pseudo.h"

#define HOST_VERSION 3
#define HASH(nick)	((tolower((nick)[0])&31)<<5 | (tolower((nick)[1])&31))

void load_hs_dbase_v1(dbFILE * f);
void load_hs_dbase_v2(dbFILE * f);
void load_hs_dbase_v3(dbFILE * f);

static int guestnum;            /* Current guest number */
HostCore *head = NULL;          /* head of the HostCore list */
HostCore *createHostCorelist(HostCore * next, char *nick, char *vIdent,
                             char *vHost, char *creator, int32 tmp_time);
HostCore *findHostCore(HostCore * head, char *nick, boolean * found);
HostCore *insertHostCore(HostCore * head, HostCore * prev, char *nick,
                         char *vIdent, char *vHost, char *creator,
                         int32 tmp_time);
HostCore *deleteHostCore(HostCore * head, HostCore * prev);
void delHostCore(char *nick);

static void send_on(char *nick, char *vIdent, char *vhost);
static void send_off(User * u);

char *getvIdent(char *nick);
char *getvHost(char *nick);

int is_host_setter(User * u);
int is_host_remover(User * u);

static int do_help(User * u);
static int do_set(User * u);
static int do_on(User * u);
int do_on_id(User * u);
static void set_lastmask(User * u);
static int do_off(User * u);
static int do_del(User * u);
static int do_group(User * u);
static int listOut(User * u);
int do_hs_sync(NickCore * nc, char *vIdent, char *hostmask, char *creator,
               time_t time);
int do_setall(User * u);
int do_delall(User * u);
void moduleAddHostServCmds(void);
/*************************************************************************/
void moduleAddHostServCmds(void)
{
    Command *c;
    c = createCommand("HELP", do_help, NULL, -1, -1, -1, -1, -1);
    addCoreCommand(HOSTSERV, c);
    c = createCommand("SET", do_set, is_host_setter, HOST_HELP_SET, -1, -1,
                      -1, -1);
    addCoreCommand(HOSTSERV, c);
    c = createCommand("GROUP", do_group, NULL, HOST_HELP_GROUP, -1, -1, -1,
                      -1);
    addCoreCommand(HOSTSERV, c);
    c = createCommand("SETALL", do_setall, is_host_setter,
                      HOST_HELP_SETALL, -1, -1, -1, -1);
    addCoreCommand(HOSTSERV, c);
    c = createCommand("DELALL", do_delall, is_host_remover,
                      HOST_HELP_DELALL, -1, -1, -1, -1);
    addCoreCommand(HOSTSERV, c);
    c = createCommand("ON", do_on, NULL, HOST_HELP_ON, -1, -1, -1, -1);
    addCoreCommand(HOSTSERV, c);
    c = createCommand("OFF", do_off, NULL, HOST_HELP_OFF, -1, -1, -1, -1);
    addCoreCommand(HOSTSERV, c);
    c = createCommand("DEL", do_del, is_host_remover, HOST_HELP_DEL, -1,
                      -1, -1, -1);
    addCoreCommand(HOSTSERV, c);
    c = createCommand("LIST", listOut, is_services_oper, HOST_HELP_LIST,
                      -1, -1, -1, -1);
    addCoreCommand(HOSTSERV, c);
}

/*************************************************************************/

/*************************************************************************/
/* HostServ initialization. */
void hostserv_init(void)
{
    moduleAddHostServCmds();
    guestnum = time(NULL);
    while (guestnum > 9999999)
        guestnum -= 10000000;
}

/*************************************************************************/
/* Main HostServ routine. */
void hostserv(User * u, char *buf)
{
    char *cmd, *s;

    cmd = strtok(buf, " ");

    if (!cmd) {
        return;
    } else if (stricmp(cmd, "\1PING") == 0) {
        if (!(s = strtok(NULL, "")))
            s = "\1";
        notice(s_HostServ, u->nick, "\1PING %s", s);
    } else if (skeleton) {
        notice_lang(s_HostServ, u, SERVICE_OFFLINE, s_HostServ);
    } else {
#ifdef HAS_VHOST
        mod_run_cmd(s_HostServ, u, HOSTSERV, cmd);
#else
        notice_lang(s_HostServ, u, SERVICE_OFFLINE, s_HostServ);
#endif
    }
}

/*************************************************************************/
/*	Start of Linked List routines					 */
/*************************************************************************/
HostCore *createHostCorelist(HostCore * next, char *nick, char *vIdent,
                             char *vHost, char *creator, int32 tmp_time)
{

    next = malloc(sizeof(HostCore));
    if (next == NULL) {
        wallops(s_HostServ,
                "Unable to allocate memory to create the vHost LL, problems i sense..");
    } else {
        next->nick = malloc(sizeof(char) * strlen(nick) + 1);
        next->vHost = malloc(sizeof(char) * strlen(vHost) + 1);
        next->creator = malloc(sizeof(char) * strlen(creator) + 1);
        if (vIdent)
            next->vIdent = malloc(sizeof(char) * strlen(vIdent) + 1);
        if ((next->nick == NULL) || (next->vHost == NULL)
            || (next->creator == NULL)) {
            wallops(s_HostServ,
                    "Unable to allocate memory to create the vHost LL, problems i sense..");
            return NULL;
        }
        strcpy(next->nick, nick);
        strcpy(next->vHost, vHost);
        strcpy(next->creator, creator);
        if (vIdent) {
            if ((next->vIdent == NULL)) {
                wallops(s_HostServ,
                        "Unable to allocate memory to create the vHost LL, problems i sense..");
                return NULL;
            }
            strcpy(next->vIdent, vIdent);
        } else {
            next->vIdent = NULL;
        }
        next->time = tmp_time;
        next->next = NULL;
        return next;
    }
    return NULL;
}

/*************************************************************************/
/**
 * Returns either NULL for the head, or the location of the *PREVIOUS*
 * record, this is where we need to insert etc..
 *
 * -rob
 **/
HostCore *findHostCore(HostCore * head, char *nick, boolean * found)
{

    HostCore *previous, *current;

    *found = false;
    current = head;
    previous = current;

    while (current != NULL) {
        if (stricmp(nick, current->nick) == 0) {
            *found = true;
            break;
        } else if (stricmp(nick, current->nick) < 0) {
            /* we know were not gonna find it now.... */
            break;
        } else {
            previous = current;
            current = current->next;
        }
    }
    if (current == head) {
        return NULL;
    } else {
        return previous;
    }
}

/*************************************************************************/
HostCore *insertHostCore(HostCore * head, HostCore * prev, char *nick,
                         char *vIdent, char *vHost, char *creator,
                         int32 tmp_time)
{

    HostCore *newCore, *tmp;

    newCore = malloc(sizeof(HostCore));
    if (newCore == NULL) {
        wallops(s_HostServ,
                "Unable to allocate memory to insert into the vHost LL, problems i sense..");
        return NULL;
    } else {
        newCore->nick = malloc(sizeof(char) * strlen(nick) + 1);
        newCore->vHost = malloc(sizeof(char) * strlen(vHost) + 1);
        newCore->creator = malloc(sizeof(char) * strlen(creator) + 1);
        if (vIdent)
            newCore->vIdent = malloc(sizeof(char) * strlen(vIdent) + 1);
        if ((newCore->nick == NULL) || (newCore->vHost == NULL)
            || (newCore->creator == NULL)) {
            wallops(s_HostServ,
                    "Unable to allocate memory to create the vHost LL, problems i sense..");
            return NULL;
        }
        strcpy(newCore->nick, nick);
        strcpy(newCore->vHost, vHost);
        strcpy(newCore->creator, creator);
        if (vIdent) {
            if ((newCore->vIdent == NULL)) {
                wallops(s_HostServ,
                        "Unable to allocate memory to create the vHost LL, problems i sense..");
                return NULL;
            }
            strcpy(newCore->vIdent, vIdent);
        } else {
            newCore->vIdent = NULL;
        }
        newCore->time = tmp_time;
        if (prev == NULL) {
            tmp = head;
            head = newCore;
            newCore->next = tmp;
        } else {
            tmp = prev->next;
            prev->next = newCore;
            newCore->next = tmp;
        }
    }
    return head;
}

/*************************************************************************/
HostCore *deleteHostCore(HostCore * head, HostCore * prev)
{

    HostCore *tmp;

    if (prev == NULL) {
        tmp = head;
        head = head->next;
    } else {
        tmp = prev->next;
        prev->next = tmp->next;
    }
    free(tmp->vHost);
    free(tmp->nick);
    free(tmp->creator);
    if (tmp->vIdent) {
        free(tmp->vIdent);
    }
    free(tmp);
    return head;
}

/*************************************************************************/
void addHostCore(char *nick, char *vIdent, char *vhost, char *creator,
                 int32 tmp_time)
{
    HostCore *tmp;
    boolean found = false;

    if (head == NULL) {
        head =
            createHostCorelist(head, nick, vIdent, vhost, creator,
                               tmp_time);
    } else {
        tmp = findHostCore(head, nick, &found);
        if (!found) {
            head =
                insertHostCore(head, tmp, nick, vIdent, vhost, creator,
                               tmp_time);
        } else {
            head = deleteHostCore(head, tmp);   /* delete the old entry */
            addHostCore(nick, vIdent, vhost, creator, tmp_time);        /* recursive call to add new entry */
        }
    }
}

/*************************************************************************/
char *getvHost(char *nick)
{
    HostCore *tmp;
    boolean found = false;
    tmp = findHostCore(head, nick, &found);
    if (found) {
        if (tmp == NULL)
            return head->vHost;
        else
            return tmp->next->vHost;
    }
    return NULL;
}

/*************************************************************************/
char *getvIdent(char *nick)
{
    HostCore *tmp;
    boolean found = false;
    tmp = findHostCore(head, nick, &found);
    if (found) {
        if (tmp == NULL)
            return head->vIdent;
        else
            return tmp->next->vIdent;
    }
    return NULL;
}

/*************************************************************************/
int listOut(User * u)
{
    char *key = strtok(NULL, "");
    struct tm *tm;
    char buf[BUFSIZE];
    int counter = 1;
    int from = 0, to = 0;
    char *tmp = NULL;
    char *s = NULL;
    int display_counter = 0;

    HostCore *current;

    current = head;
    if (current == NULL)
        notice_lang(s_HostServ, u, HOST_EMPTY);
    else {
                /**
		 * Do a check for a range here, then in the next loop
		 * we'll only display what has been requested..
		 **/
        if (key) {
            if (key[0] == '#') {
                tmp = myStrGetOnlyToken((key + 1), '-', 0);     /* Read FROM out */
                if (!tmp) {
                    return MOD_CONT;
                }
                for (s = tmp; *s; s++) {
                    if (!isdigit(*s)) {
                        return MOD_CONT;
                    }
                }
                from = atoi(tmp);
                tmp = myStrGetTokenRemainder(key, '-', 1);      /* Read TO out */
                if (!tmp) {
                    return MOD_CONT;
                }
                for (s = tmp; *s; s++) {
                    if (!isdigit(*s)) {
                        return MOD_CONT;
                    }
                }
                to = atoi(tmp);
                key = NULL;
            }
        }

        while (current != NULL) {
            if (key) {
                if (((match_wild_nocase(key, current->nick))
                     || (match_wild_nocase(key, current->vHost)))
                    && (display_counter < NSListMax)) {
                    display_counter++;
                    tm = localtime(&current->time);
                    strftime(buf, sizeof(buf),
                             getstring(NULL,
                                       STRFTIME_DATE_TIME_FORMAT), tm);
                    if (current->vIdent) {
                        notice_lang(s_HostServ, u, HOST_IDENT_ENTRY,
                                    counter, current->nick,
                                    current->vIdent, current->vHost,
                                    current->creator, buf);
                    } else {
                        notice_lang(s_HostServ, u, HOST_ENTRY, counter,
                                    current->nick, current->vHost,
                                    current->creator, buf);
                    }
                }
            } else {
                        /**
			 * List the host if its in the display range, and not more
			 * than NSListMax records have been displayed...
			 **/
                if ((((counter >= from) && (counter <= to))
                     || ((from == 0) && (to == 0)))
                    && (display_counter < NSListMax)) {
                    display_counter++;
                    tm = localtime(&current->time);
                    strftime(buf, sizeof(buf),
                             getstring(NULL, STRFTIME_DATE_TIME_FORMAT),
                             tm);
                    if (current->vIdent) {
                        notice_lang(s_HostServ, u, HOST_IDENT_ENTRY,
                                    counter, current->nick,
                                    current->vIdent, current->vHost,
                                    current->creator, buf);
                    } else {
                        notice_lang(s_HostServ, u, HOST_ENTRY, counter,
                                    current->nick, current->vHost,
                                    current->creator, buf);
                    }
                }
            }
            counter++;
            current = current->next;
        }
        if (key) {
            notice_lang(s_HostServ, u, HOST_LIST_KEY_FOOTER, key,
                        display_counter);
        } else {
            if (from != 0) {
                notice_lang(s_HostServ, u, HOST_LIST_RANGE_FOOTER, from,
                            to);
            } else {
                notice_lang(s_HostServ, u, HOST_LIST_FOOTER,
                            display_counter);
            }
        }
    }
    return MOD_CONT;
}

/*************************************************************************/
void delHostCore(char *nick)
{
#ifdef USE_RDB
    static char clause[128];
#endif
    HostCore *tmp;
    boolean found = false;
    tmp = findHostCore(head, nick, &found);
    if (found) {
        head = deleteHostCore(head, tmp);

#ifdef USE_RDB
        /* Reflect this change in the database right away. */
        if (rdb_open()) {

            snprintf(clause, sizeof(clause), "nick='%s'", nick);
            rdb_scrub_table("anope_hs_core", clause);
            rdb_close();
        }
#endif

    }

}

/*************************************************************************/
/*	End of Linked List routines					 */
/*************************************************************************/
/*************************************************************************/
/*	Start of Load/Save routines					 */
/*************************************************************************/
#define SAFE(x) do {					\
    if ((x) < 0) {					\
	if (!forceload)					\
	    fatal("Read error on %s", HostDBName);	\
	failed = 1;					\
	break;						\
    }							\
} while (0)

void load_hs_dbase(void)
{
    dbFILE *f;
    int ver;

    if (!(f = open_db(s_HostServ, HostDBName, "r", HOST_VERSION))) {
        return;
    }
    ver = get_file_version(f);

    if (ver == 1) {
        load_hs_dbase_v1(f);
    } else if (ver == 2) {
        load_hs_dbase_v2(f);
    } else if (ver == 3) {
        load_hs_dbase_v3(f);
    }
    close_db(f);
}

void load_hs_dbase_v1(dbFILE * f)
{
    int c;
    int failed = 0;
    int32 tmp;

    char *nick;
    char *vHost;

    tmp = time(NULL);

    alog("Attempting to load V1 HS Database");
    while (!failed && (c = getc_db(f)) == 1) {

        if (c == 1) {
            SAFE(read_string(&nick, f));
            SAFE(read_string(&vHost, f));
            addHostCore(nick, NULL, vHost, "Unknown", tmp);     /* could get a speed increase by not searching the list */
            free(nick);         /* as we know the db is in alphabetical order... */
            free(vHost);
        } else {
            fatal("Invalid format in %s %d", HostDBName, c);
        }
    }
}

void load_hs_dbase_v2(dbFILE * f)
{
    int c;
    int failed = 0;

    char *nick;
    char *vHost;
    char *creator;
    int32 time;

    alog("Attempting to load V2 HS Database");
    while (!failed && (c = getc_db(f)) == 1) {

        if (c == 1) {
            SAFE(read_string(&nick, f));
            SAFE(read_string(&vHost, f));
            SAFE(read_string(&creator, f));
            SAFE(read_int32(&time, f));
            addHostCore(nick, NULL, vHost, creator, time);      /* could get a speed increase by not searching the list */
            free(nick);         /* as we know the db is in alphabetical order... */
            free(vHost);
            free(creator);
        } else {
            fatal("Invalid format in %s %d", HostDBName, c);
        }
    }
}

void load_hs_dbase_v3(dbFILE * f)
{
    int c;
    int failed = 0;

    char *nick;
    char *vHost;
    char *creator;
    char *vIdent;
    int32 time;

    alog("Attempting to load V3 HS Database");
    while (!failed && (c = getc_db(f)) == 1) {
        if (c == 1) {
            SAFE(read_string(&nick, f));
            SAFE(read_string(&vIdent, f));
            SAFE(read_string(&vHost, f));
            SAFE(read_string(&creator, f));
            SAFE(read_int32(&time, f));
            addHostCore(nick, vIdent, vHost, creator, time);    /* could get a speed increase by not searching the list */
            free(nick);         /* as we know the db is in alphabetical order... */
            free(vHost);
            free(creator);
            free(vIdent);
        } else {
            fatal("Invalid format in %s %d", HostDBName, c);
        }
    }
}

#undef SAFE
/*************************************************************************/
#define SAFE(x) do {						\
    if ((x) < 0) {						\
	restore_db(f);						\
	log_perror("Write error on %s", HostDBName);		\
	if (time(NULL) - lastwarn > WarningTimeout) {		\
	    wallops(NULL, "Write error on %s: %s", HostDBName,	\
			strerror(errno));			\
	    lastwarn = time(NULL);				\
	}							\
	return;							\
    }								\
} while (0)

void save_hs_dbase(void)
{
    dbFILE *f;
    static time_t lastwarn = 0;
    HostCore *current;

    if (!(f = open_db(s_HostServ, HostDBName, "w", HOST_VERSION)))
        return;

    current = head;
    while (current != NULL) {
        SAFE(write_int8(1, f));
        SAFE(write_string(current->nick, f));
        SAFE(write_string(current->vIdent, f));
        SAFE(write_string(current->vHost, f));
        SAFE(write_string(current->creator, f));
        SAFE(write_int32(current->time, f));
        current = current->next;
    }
    SAFE(write_int8(0, f));
    close_db(f);

}

#undef SAFE

void save_hs_rdb_dbase(void)
{
#ifdef USE_RDB
    HostCore *current;

    if (!rdb_open())
        return;

    rdb_clear_table("anope_hs_core");

    current = head;
    while (current != NULL) {
        rdb_save_hs_core(current);
        current = current->next;
    }
    rdb_close();
#endif
}

/*************************************************************************/
/*	End of Load/Save Functions					 */
/*************************************************************************/
/*************************************************************************/
/*	Start of Generic Functions					 */
/*************************************************************************/
int do_setall(User * u)
{

    char *nick = strtok(NULL, " ");
    char *rawhostmask = strtok(NULL, " ");
    char *hostmask = smalloc(HOSTMAX);

    NickAlias *na;
    int32 tmp_time;
    char *s;

    char *vIdent = NULL;

    if (!nick || !rawhostmask) {
        notice_lang(s_HostServ, u, HOST_SETALL_SYNTAX, s_HostServ);
        return MOD_CONT;
    }

    vIdent = myStrGetOnlyToken(rawhostmask, '@', 0);    /* Get the first substring, @ as delimiter */
    if (vIdent) {
        rawhostmask = myStrGetTokenRemainder(rawhostmask, '@', 1);      /* get the remaining string */
        if (!rawhostmask) {
            notice_lang(s_HostServ, u, HOST_SETALL_SYNTAX, s_HostServ);
            return MOD_CONT;
        }
        if (strlen(vIdent) > USERMAX - 1) {
            notice_lang(s_HostServ, u, HOST_SET_IDENTTOOLONG, USERMAX);
            return MOD_CONT;
        } else {
            for (s = vIdent; *s; s++) {
                if (!isvalidchar(*s)) {
                    notice_lang(s_HostServ, u, HOST_SET_IDENT_ERROR);
                    return MOD_CONT;
                }
            }
        }
#ifndef HAS_VIDENT
        notice_lang(s_HostServ, u, HOST_NO_VIDENT);
        return MOD_CONT;
#endif
    }

    if (strlen(rawhostmask) < HOSTMAX - 1)
        snprintf(hostmask, HOSTMAX - 1, "%s", rawhostmask);
    else {
        notice_lang(s_HostServ, u, HOST_SET_TOOLONG, HOSTMAX);
        return MOD_CONT;
    }

    if (!isValidHost(hostmask, 3)) {
        notice_lang(s_HostServ, u, HOST_SET_ERROR);
        return MOD_CONT;
    }

    tmp_time = time(NULL);

    if ((na = findnick(nick))) {
        alog("vHost for all nicks in group \002%s\002 set to \002%s\002 by oper \002%s\002", nick, hostmask, u->nick);
        do_hs_sync(na->nc, vIdent, hostmask, u->nick, tmp_time);
        if (vIdent) {
            notice_lang(s_HostServ, u, HOST_IDENT_SETALL, nick, vIdent,
                        hostmask);
        } else {
            notice_lang(s_HostServ, u, HOST_SETALL, nick, hostmask);
        }
    } else {
        notice_lang(s_HostServ, u, HOST_NOREG, nick);
    }

    free(hostmask);
    return MOD_CONT;
}

int do_delall(User * u)
{
    int i;
    char *nick = strtok(NULL, " ");
    NickAlias *na;
    NickCore *nc;
    if (!nick) {
        notice_lang(s_HostServ, u, HOST_DELALL_SYNTAX, s_HostServ);
        return MOD_CONT;
    }
    if ((na = findnick(nick))) {
        nc = na->nc;
        for (i = 0; i < nc->aliases.count; i++) {
            na = nc->aliases.list[i];
            delHostCore(na->nick);
        }
        alog("vHosts for all nicks in group \002%s\002 deleted by oper \002%s\002", nc->display, u->nick);
        notice_lang(s_HostServ, u, HOST_DELALL, nc->display);
    } else {
        notice_lang(s_HostServ, u, HOST_NOREG, nick);
    }
    return MOD_CONT;
}

int do_hs_sync(NickCore * nc, char *vIdent, char *hostmask, char *creator,
               time_t time)
{
    int i;
    NickAlias *na;

    for (i = 0; i < nc->aliases.count; i++) {
        na = nc->aliases.list[i];
        addHostCore(na->nick, vIdent, hostmask, creator, time);
    }
    return MOD_CONT;
}

static int do_group(User * u)
{
    NickAlias *na;
    HostCore *tmp;
    char *vHost = NULL;
    char *vIdent = NULL;
    char *creator = NULL;
    time_t time;
    boolean found = false;

    if ((na = findnick(u->nick))) {
        if (na->status & NS_IDENTIFIED) {
            tmp = findHostCore(head, u->nick, &found);
            if (found) {
                if (tmp == NULL) {
                    tmp = head; /* incase first in list */
                } else if (tmp->next) { /* we dont want the previous entry were not inserting! */
                    tmp = tmp->next;    /* jump to the next */
                }

                vHost = sstrdup(tmp->vHost);
                if (tmp->vIdent)
                    vIdent = sstrdup(tmp->vIdent);
                creator = sstrdup(tmp->creator);
                time = tmp->time;

                do_hs_sync(na->nc, vIdent, vHost, creator, time);
                if (tmp->vIdent) {
                    notice_lang(s_HostServ, u, HOST_IDENT_GROUP,
                                na->nc->display, vIdent, vHost);
                } else {
                    notice_lang(s_HostServ, u, HOST_GROUP, na->nc->display,
                                vHost);
                }
                free(vHost);
                if (vIdent)
                    free(vIdent);
                free(creator);

            } else {
                notice_lang(s_HostServ, u, HOST_NOT_ASSIGNED);
            }
        } else {
            notice_lang(s_HostServ, u, HOST_ID);
        }
    } else {
        notice_lang(s_HostServ, u, HOST_NOT_REGED);
    }
    return MOD_CONT;
}

static int do_help(User * u)
{
    char *cmd = strtok(NULL, "");

    if (!cmd) {
        notice_help(s_HostServ, u, HOST_HELP, s_HostServ);
        if ((is_services_oper(u)) || (is_host_setter(u)))
            notice_help(s_HostServ, u, HOST_OPER_HELP);
        if (is_services_admin(u))
            notice_help(s_HostServ, u, HOST_ADMIN_HELP);
        moduleDisplayHelp(6, u);
    } else {
        mod_help_cmd(s_HostServ, u, HOSTSERV, cmd);
    }
    return MOD_CONT;
}

/*************************************************************************/
int do_set(User * u)
{
    char *nick = strtok(NULL, " ");
    char *rawhostmask = strtok(NULL, " ");
    char *hostmask = smalloc(HOSTMAX);

    NickAlias *na;
    int32 tmp_time;
    char *s;

    char *vIdent = NULL;

    if (!nick || !rawhostmask) {
        notice_lang(s_HostServ, u, HOST_SET_SYNTAX, s_HostServ);
        return MOD_CONT;
    }

    vIdent = myStrGetOnlyToken(rawhostmask, '@', 0);    /* Get the first substring, @ as delimiter */
    if (vIdent) {
        rawhostmask = myStrGetTokenRemainder(rawhostmask, '@', 1);      /* get the remaining string */
        if (!rawhostmask) {
            notice_lang(s_HostServ, u, HOST_SET_SYNTAX, s_HostServ);
            return MOD_CONT;
        }
        if (strlen(vIdent) > USERMAX - 1) {
            notice_lang(s_HostServ, u, HOST_SET_IDENTTOOLONG, USERMAX);
            return MOD_CONT;
        } else {
            for (s = vIdent; *s; s++) {
                if (!isvalidchar(*s)) {
                    notice_lang(s_HostServ, u, HOST_SET_IDENT_ERROR);
                    return MOD_CONT;
                }
            }
        }
#ifndef HAS_VIDENT
        notice_lang(s_HostServ, u, HOST_NO_VIDENT);
        return MOD_CONT;
#endif
    }
    if (strlen(rawhostmask) < HOSTMAX - 1)
        snprintf(hostmask, HOSTMAX - 1, "%s", rawhostmask);
    else {
        notice_lang(s_HostServ, u, HOST_SET_TOOLONG, HOSTMAX);
        return MOD_CONT;
    }

    if (!isValidHost(hostmask, 3)) {
        notice_lang(s_HostServ, u, HOST_SET_ERROR);
        return MOD_CONT;
    }


    tmp_time = time(NULL);

    if ((na = findnick(nick))) {
        alog("vHost for user \002%s\002 set to \002%s\002 by oper \002%s\002", nick, hostmask, u->nick);
        addHostCore(nick, vIdent, hostmask, u->nick, tmp_time);
        if (vIdent) {
            notice_lang(s_HostServ, u, HOST_IDENT_SET, nick, vIdent,
                        hostmask);
        } else {
            notice_lang(s_HostServ, u, HOST_SET, nick, hostmask);
        }
    } else {
        notice_lang(s_HostServ, u, HOST_NOREG, nick);
    }
    free(hostmask);
    return MOD_CONT;
}

/*************************************************************************/
int do_on(User * u)
{
    NickAlias *na;
    char *vHost;
    char *vIdent = NULL;
    if ((na = findnick(u->nick))) {
        if (na->status & NS_IDENTIFIED) {
            vHost = getvHost(u->nick);
            vIdent = getvIdent(u->nick);
            if (vHost == NULL) {
                notice_lang(s_HostServ, u, HOST_NOT_ASSIGNED);
            } else {
                if (vIdent) {
                    notice_lang(s_HostServ, u, HOST_IDENT_ACTIVATED,
                                vIdent, vHost);
                } else {
                    notice_lang(s_HostServ, u, HOST_ACTIVATED, vHost);
                }
                send_on(u->nick, vIdent, vHost);
#ifdef HAS_VHOST
                u->vhost = sstrdup(vHost);
#endif
#ifdef HAS_VIDENT
                if (vIdent)
                    u->vident = sstrdup(vIdent);
#endif
                set_lastmask(u);
            }
        } else {
            notice_lang(s_HostServ, u, HOST_ID);
        }
    } else {
        notice_lang(s_HostServ, u, HOST_NOT_REGED);
    }
    return MOD_CONT;
}

/*************************************************************************/
int do_on_id(User * u)
{                               /* we've assumed that the user exists etc.. */
    char *vHost;                /* as were only gonna call this from nsid routine */
    char *vIdent;
    vHost = getvHost(u->nick);
    vIdent = getvIdent(u->nick);
    if (vHost != NULL) {
        if (vIdent) {
            notice_lang(s_HostServ, u, HOST_IDENT_ACTIVATED, vIdent,
                        vHost);
        } else {
            notice_lang(s_HostServ, u, HOST_ACTIVATED, vHost);
        }
        send_on(u->nick, vIdent, vHost);
#ifdef HAS_VHOST
        u->vhost = sstrdup(vHost);
#endif
#ifdef HAS_VIDENT
        if (vIdent)
            u->vident = sstrdup(vIdent);
#endif
        set_lastmask(u);
    }
    return MOD_CONT;
}

/*************************************************************************/
int do_del(User * u)
{
    NickAlias *na;
    char *nick = strtok(NULL, " ");
    if (nick) {
        if ((na = findnick(nick))) {
            alog("vHost for user \002%s\002 deleted by oper \002%s\002",
                 nick, u->nick);
            delHostCore(nick);
            notice_lang(s_HostServ, u, HOST_DEL, nick);
        } else {
            notice_lang(s_HostServ, u, HOST_NOREG, nick);
        }
    } else {
        notice_lang(s_HostServ, u, HOST_DEL_SYNTAX, s_HostServ);
    }
    return MOD_CONT;
}

/*************************************************************************/
int do_off(User * u)
{
    /* put any generic code here... :) */
    send_off(u);
    return MOD_CONT;
}

int is_host_setter(User * u)
{
    int i, j;
    NickAlias *na;

    if (is_services_oper(u)) {
        return 1;
    }
    if (!nick_identified(u)) {
        return 0;
    }

    /* Look through all user's aliases (0000412) */
    for (i = 0; i < u->na->nc->aliases.count; i++) {
        na = u->na->nc->aliases.list[i];
        for (j = 0; j < HostNumber; j++) {
            if (stricmp(HostSetters[j], na->nick) == 0) {
                return 1;
            }
        }
    }

    return 0;
}

int is_host_remover(User * u)
{
    return is_host_setter(u);   /* only here incase we want to split them up later */
}

/*
 * Sets the last_usermak properly. Using virtual ident and/or host 
 */
void set_lastmask(User * u)
{
    if (u->na->last_usermask)
        free(u->na->last_usermask);

    u->na->last_usermask =
        smalloc(strlen(GetIdent(u)) + strlen(GetHost(u)) + 2);
    sprintf(u->na->last_usermask, "%s@%s", GetIdent(u), GetHost(u));

}

/*************************************************************************/
/*	End of Generic Functions					 */
/*************************************************************************/
/*************************************************************************/
/*	Start of Server Functions					 */
/*************************************************************************/
void send_on(char *nick, char *vIdent, char *vhost)
{
#ifdef IRC_UNREAL
    if (vIdent) {
        send_cmd(ServerName, "CHGIDENT %s %s", nick, vIdent);
    }
    send_cmd(ServerName, "CHGHOST %s %s", nick, vhost);
#endif
#ifdef IRC_VIAGRA
    if (vIdent) {
        send_cmd(NULL, "CHGIDENT %s %s", nick, vIdent);
    }
    send_cmd(NULL, "SVSMODE %s +x", nick);
    send_cmd(NULL, "SVSCHGHOST %s %s", nick, vhost);
#endif
#ifdef IRC_ULTIMATE
    if (vIdent) {
        send_cmd(ServerName, "CHGIDENT %s %s", nick, vIdent);
    }
    send_cmd(s_HostServ, "SVSMODE %s +x", nick);
    send_cmd(ServerName, "CHGHOST %s %s", nick, vhost);
#endif
#ifdef IRC_ULTIMATE3
    send_cmd(s_HostServ, "SVSMODE %s +x", nick);
    send_cmd(ServerName, "SETHOST %s %s", nick, vhost);
#endif


}

/*************************************************************************/
void send_off(User * u)
{
#ifdef IRC_UNREAL
    send_cmd(s_HostServ, "SVSMODE %s -xt", u->nick);
    notice_lang(s_HostServ, u, HOST_OFF_UNREAL, u->nick);
    /*
     * tell them to type /mode nick +x to get their original
     * host cloaking back
     */
#endif
#ifdef IRC_VIAGRA
    send_cmd(NULL, "SVSMODE %s -x", u->nick);
    notice_lang(s_HostServ, u, HOST_OFF_UNREAL, u->nick);
#endif
#ifdef IRC_ULTIMATE
    /* UltimateIRCd 2.x does not allow users to control +x */
#endif
#ifdef IRC_ULTIMATE3
    send_cmd(s_HostServ, "SVSMODE %s -x", u->nick);
    notice_lang(s_HostServ, u, HOST_OFF_UNREAL, u->nick);
#endif

}

/*************************************************************************/
/*	End of Server Functions						 */
/*************************************************************************/
