]> jfr.im git - irc/evilnet/x3.git/blobdiff - src/nickserv.c
setting an epithet now sets the epithet as a SWHOIS message in the ircd (nefarious...
[irc/evilnet/x3.git] / src / nickserv.c
index fe50e2ceb6d0877622b98df93b4b840156fab360..676c5c890fc97aa791e58c734cbd82c9dde55f74 100644 (file)
@@ -3,7 +3,7 @@
  *
  * This file is part of x3.
  *
- * srvx is free software; you can redistribute it and/or modify
+ * x3 is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
 #define KEY_COOKIE_TIMEOUT "cookie_timeout"
 #define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
 #define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
+#define KEY_DEFAULT_STYLE "default_style"
 
 #define KEY_ID "id"
 #define KEY_PASSWD "passwd"
 #define KEY_NICKS "nicks"
 #define KEY_MASKS "masks"
+#define KEY_IGNORES "ignores"
 #define KEY_OPSERV_LEVEL "opserv_level"
 #define KEY_FLAGS "flags"
 #define KEY_REGISTER_ON "register"
 #define KEY_ANNOUNCEMENTS "announcements"
 #define KEY_MAXLOGINS "maxlogins"
 #define KEY_FAKEHOST "fakehost"
+#define KEY_NOTE_NOTE "note"
+#define KEY_NOTE_SETTER "setter"
+#define KEY_NOTE_DATE "date"
+
 
 #define NICKSERV_VALID_CHARS   "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
 
@@ -203,11 +209,13 @@ static const struct message_entry msgtab[] = {
     { "NSMSG_HANDLEINFO_INFOLINE", "Infoline: %s" },
     { "NSMSG_HANDLEINFO_FLAGS", "Flags: %s" },
     { "NSMSG_HANDLEINFO_EPITHET", "Epithet: %s" },
+    { "NSMSG_HANDLEINFO_NOTE", "Note (by %s on %s): %s " },
     { "NSMSG_HANDLEINFO_FAKEHOST", "Fake host: %s" },
     { "NSMSG_HANDLEINFO_LAST_HOST", "Last quit hostmask: %s" },
     { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", "Last quit hostmask: Unknown" },
     { "NSMSG_HANDLEINFO_NICKS", "Nickname(s): %s" },
     { "NSMSG_HANDLEINFO_MASKS", "Hostmask(s): %s" },
+    { "NSMSG_HANDLEINFO_IGNORES", "Ignore(s): %s" },
     { "NSMSG_HANDLEINFO_CHANNELS", "Channel(s): %s" },
     { "NSMSG_HANDLEINFO_CURRENT", "Current nickname(s): %s" },
     { "NSMSG_HANDLEINFO_DNR", "Do-not-register (by %s): %s" },
@@ -234,6 +242,8 @@ static const struct message_entry msgtab[] = {
     { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
     { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
     { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
+    { "NSMSG_ADDIGNORE_ALREADY", "$b%s$b is already an ignored hostmask in your account." },
+    { "NSMSG_ADDIGNORE_SUCCESS", "Hostmask %s added." },
     { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
     { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
     { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
@@ -296,6 +306,7 @@ static const struct message_entry msgtab[] = {
     { "NSMSG_SET_LANGUAGE", "$bLANGUAGE:     $b%s" },
     { "NSMSG_SET_LEVEL", "$bLEVEL:        $b%d" },
     { "NSMSG_SET_EPITHET", "$bEPITHET:      $b%s" },
+    { "NSMSG_SET_NOTE", "$bNOTE:         $b%s"},
     { "NSMSG_SET_TITLE", "$bTITLE:        $b%s" },
     { "NSMSG_SET_FAKEHOST", "$bFAKEHOST:    $b%s" },
 
@@ -401,6 +412,7 @@ static struct {
     unsigned char hard_maxlogins;
     const char *auto_oper;
     const char *auto_admin;
+    char default_style;
 } nickserv_conf;
 
 /* We have 2^32 unique account IDs to use. */
@@ -418,6 +430,17 @@ canonicalize_hostmask(char *mask)
     return mask;
 }
 
+static struct handle_note *
+nickserv_add_note(const char *setter, time_t date, const char *text)
+{
+    struct handle_note *note = calloc(1, sizeof(*note) + strlen(text));
+
+    strncpy(note->setter, setter, sizeof(note->setter)-1);
+    note->date = date;
+    memcpy(note->note, text, strlen(text));
+    return note;
+}
+
 static struct handle_info *
 register_handle(const char *handle, const char *passwd, UNUSED_ARG(unsigned long id))
 {
@@ -542,12 +565,14 @@ free_handle_info(void *vhi)
 #endif
 
     free_string_list(hi->masks);
+    free_string_list(hi->ignores);
     assert(!hi->users);
 
     while (hi->nicks)
         delete_nick(hi->nicks);
     free(hi->infoline);
     free(hi->epithet);
+    free(hi->note);
     free(hi->fakehost);
     if (hi->cookie) {
         timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
@@ -565,23 +590,30 @@ free_handle_info(void *vhi)
 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
 
 static void
-nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
+nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify, struct userNode *bot)
 {
     unsigned int n;
+    struct userNode *uNode;
 
     for (n=0; n<unreg_func_used; n++)
         unreg_func_list[n](notify, hi);
-    while (hi->users)
+    while (hi->users) {
+        if (nickserv_conf.sync_log) {
+            uNode = GetUserH(hi->users->nick);
+            if (uNode)
+                irc_delete(uNode);
+        }
         set_user_handle_info(hi->users, NULL, 0);
+    }
     if (notify) {
         if (nickserv_conf.disable_nicks)
-            send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
+            send_message(notify, bot, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
         else
-            send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
+            send_message(notify, bot, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
     }
 
     if (nickserv_conf.sync_log)
-      SyncLog("UNREGISTER %s", hi->handle);
+        SyncLog("UNREGISTER %s", hi->handle);
 
     dict_remove(nickserv_handle_dict, hi->handle);
 }
@@ -693,6 +725,7 @@ is_registerable_nick(const char *nick)
     }
     return 1;
 }
+/*  this has been replaced with one in tools.c
 
 static int
 is_valid_email_addr(const char *email)
@@ -700,6 +733,8 @@ is_valid_email_addr(const char *email)
     return strchr(email, '@') != NULL;
 }
 
+*/ 
+
 static const char *
 visible_email_addr(struct userNode *user, struct handle_info *hi)
 {
@@ -906,6 +941,17 @@ apply_fakehost(struct handle_info *handle)
         assign_fakehost(target, fake, 1);
 }
 
+void send_func_list(struct userNode *user)
+{
+    unsigned int n;
+    struct handle_info *old_info;
+
+    old_info = user->handle_info;
+
+    for (n=0; n<auth_func_used; n++)
+        auth_func_list[n](user, old_info);
+}
+
 static void
 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
 {
@@ -949,7 +995,8 @@ set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
     if (GetUserH(user->nick)) {
         for (n=0; n<auth_func_used; n++)
             auth_func_list[n](user, old_info);
-    }
+    } else
+      user->loc = 1;
 
     if (hi) {
         struct nick_info *ni;
@@ -1025,6 +1072,7 @@ nickserv_register(struct userNode *user, struct userNode *settee, const char *ha
     cryptpass(passwd, crypted);
     hi = register_handle(handle, crypted, 0);
     hi->masks = alloc_string_list(1);
+    hi->ignores = alloc_string_list(1);
     hi->users = NULL;
     hi->language = lang_C;
     hi->registered = now;
@@ -1213,6 +1261,7 @@ nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
 
 static NICKSERV_FUNC(cmd_register)
 {
+    irc_in_addr_t ip;
     struct handle_info *hi;
     const char *email_addr, *password;
     char syncpass[MD5_CRYPT_LENGTH];
@@ -1258,7 +1307,7 @@ static NICKSERV_FUNC(cmd_register)
         email_addr = argv[3];
 
         /* Check that the email address looks valid.. */
-        if (!is_valid_email_addr(email_addr)) {
+        if (!valid_email(email_addr)) {
             reply("NSMSG_BAD_EMAIL_ADDR");
             return 0;
         }
@@ -1306,7 +1355,7 @@ static NICKSERV_FUNC(cmd_register)
         string_list_append(hi->masks, strdup("*@*"));
     } else {
         string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
-        if (user->ip.s_addr && user->hostname[strspn(user->hostname, "0123456789.")])
+        if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
             string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
     }
 
@@ -1330,7 +1379,7 @@ static NICKSERV_FUNC(cmd_register)
     if (nickserv_conf.sync_log) {
       cryptpass(password, syncpass);
       /*
-       * An 0 is only sent if theres no email address. Thios should only happen if email functions are
+      * An 0 is only sent if theres no email address. Thios should only happen if email functions are
        * disabled which they wont be for us. Email Required MUST be set on if you are using this.
        * -SiRVulcaN
        */
@@ -1349,19 +1398,26 @@ static NICKSERV_FUNC(cmd_oregister)
     struct userNode *settee;
     struct handle_info *hi;
 
-    NICKSERV_MIN_PARMS(4);
+    NICKSERV_MIN_PARMS(nickserv_conf.email_required ? 4 : 3);
 
     if (!is_valid_handle(argv[1])) {
         reply("NSMSG_BAD_HANDLE", argv[1]);
         return 0;
     }
 
+    if (nickserv_conf.email_required) {
+        if (!valid_email(argv[4])) {
+            reply("NSMSG_BAD_EMAIL_ADDR");
+            return 0;
+        }
+    }
+
     if (strchr(argv[3], '@')) {
        mask = canonicalize_hostmask(strdup(argv[3]));
        if (argc > 4) {
-           settee = GetUserH(argv[4]);
+           settee = GetUserH(nickserv_conf.email_required ? argv[5] : argv[4]);
            if (!settee) {
-               reply("MSG_NICK_UNKNOWN", argv[4]);
+               reply("MSG_NICK_UNKNOWN", nickserv_conf.email_required ? argv[5] : argv[4]);
                 free(mask);
                return 0;
            }
@@ -1380,6 +1436,11 @@ static NICKSERV_FUNC(cmd_oregister)
         return 0;
     }
     if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
+        if (nickserv_conf.email_required) {
+            nickserv_set_email_addr(hi, argv[4]);
+            if (nickserv_conf.sync_log)
+                SyncLog("REGISTER %s %s %s %s", hi->handle, hi->passwd, argv[4], user->info);
+        }
         free(mask);
         return 0;
     }
@@ -1387,6 +1448,75 @@ static NICKSERV_FUNC(cmd_oregister)
     return 1;
 }
 
+static int
+nickserv_ignore(struct userNode *user, struct handle_info *hi, const char *mask)
+{
+    unsigned int i;
+    char *new_mask = canonicalize_hostmask(strdup(mask));
+    for (i=0; i<hi->ignores->used; i++) {
+        if (!irccasecmp(new_mask, hi->ignores->list[i])) {
+            send_message(user, nickserv, "NSMSG_ADDIGNORE_ALREADY", new_mask);
+            free(new_mask);
+            return 0;
+        }
+    }
+    string_list_append(hi->ignores, new_mask);
+    send_message(user, nickserv, "NSMSG_ADDIGNORE_SUCCESS", new_mask);
+
+    irc_silence(user, new_mask, 1);
+    return 1;
+}
+
+static NICKSERV_FUNC(cmd_addignore)
+{
+    NICKSERV_MIN_PARMS(2);
+
+    return nickserv_ignore(user, user->handle_info, argv[1]);
+}
+
+static NICKSERV_FUNC(cmd_oaddignore)
+{
+    struct handle_info *hi;
+
+    NICKSERV_MIN_PARMS(3);
+    if (!(hi = get_victim_oper(user, argv[1])))
+        return 0;
+    return nickserv_ignore(user, hi, argv[2]);
+}
+
+static int
+nickserv_delignore(struct userNode *user, struct handle_info *hi, const char *del_mask)
+{
+    unsigned int i;
+    for (i=0; i<hi->ignores->used; i++) {
+       if (!strcmp(del_mask, hi->ignores->list[i])) {
+           char *old_mask = hi->ignores->list[i];
+           hi->ignores->list[i] = hi->ignores->list[--hi->ignores->used];
+           send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
+            irc_silence(user, old_mask, 0);
+           free(old_mask);
+           return 1;
+       }
+    }
+    send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
+    return 0;
+}
+
+static NICKSERV_FUNC(cmd_delignore)
+{
+    NICKSERV_MIN_PARMS(2);
+    return nickserv_delignore(user, user->handle_info, argv[1]);
+}
+
+static NICKSERV_FUNC(cmd_odelignore)
+{
+    struct handle_info *hi;
+    NICKSERV_MIN_PARMS(3);
+    if (!(hi = get_victim_oper(user, argv[1])))
+        return 0;
+    return nickserv_delignore(user, hi, argv[2]);
+}
+
 static NICKSERV_FUNC(cmd_handleinfo)
 {
     char buff[400];
@@ -1468,6 +1598,16 @@ static NICKSERV_FUNC(cmd_handleinfo)
         reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
     }
 
+    if (IsHelping(user) || IsOper(user))
+    {
+        if (hi->note)
+        {
+            char date[64];
+            strftime(date, 64, "%b %d %Y", localtime(&hi->note->date));
+            reply("NSMSG_HANDLEINFO_NOTE", hi->note->setter, date, hi->note->note);
+        }
+    }
+
     if (hi->fakehost)
         reply("NSMSG_HANDLEINFO_FAKEHOST", (hi->fakehost ? hi->fakehost : handle_find_message(hi, "MSG_NONE")));
 
@@ -1521,6 +1661,26 @@ static NICKSERV_FUNC(cmd_handleinfo)
         reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
     }
 
+    if (hi->ignores->used) {
+        for (i=0; i < hi->ignores->used; i++) {
+            herelen = strlen(hi->ignores->list[i]);
+            if (pos + herelen + 1 > ArrayLength(buff)) {
+                i--;
+                goto print_ignore_buff;
+            }
+            memcpy(buff+pos, hi->ignores->list[i], herelen);
+            pos += herelen; buff[pos++] = ' ';
+            if (i+1 == hi->ignores->used) {
+              print_ignore_buff:
+                buff[pos-1] = 0;
+                reply("NSMSG_HANDLEINFO_IGNORES", buff);
+                pos = 0;
+            }
+        }
+    } else {
+        reply("NSMSG_HANDLEINFO_IGNORES", nsmsg_none);
+    }
+
     if (hi->channels) {
        struct userData *channel, *next;
        char *name;
@@ -1601,6 +1761,7 @@ static NICKSERV_FUNC(cmd_nickinfo)
 static NICKSERV_FUNC(cmd_rename_handle)
 {
     struct handle_info *hi;
+    struct userNode *uNode;
     char msgbuf[MAXLEN], *old_handle;
     unsigned int nn;
 
@@ -1627,6 +1788,15 @@ static NICKSERV_FUNC(cmd_rename_handle)
     for (nn=0; nn<rf_list_used; nn++)
         rf_list[nn](hi, old_handle);
     snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
+
+
+    if (nickserv_conf.sync_log) {
+        for (uNode = hi->users; uNode; uNode = uNode->next_authed)
+            irc_rename(uNode, hi->handle);
+
+        SyncLog("RENAME %s %s", old_handle, hi->handle);
+    }
+
     reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
     global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
     free(old_handle);
@@ -2081,7 +2251,8 @@ static NICKSERV_FUNC(cmd_cookie)
            * This should only happen if an OREGISTER was sent. Require
            * email must be enabled! - SiRVulcaN
            */
-          SyncLog("REGISTER %s %s %s %s", hi->handle, hi->passwd, hi->cookie->data, user->info);
+          if (nickserv_conf.sync_log)
+            SyncLog("REGISTER %s %s %s %s", hi->handle, hi->passwd, hi->cookie->data, user->info);
         }
         nickserv_set_email_addr(hi, hi->cookie->data);
         reply("NSMSG_EMAIL_CHANGED");
@@ -2335,7 +2506,7 @@ set_list(struct userNode *user, struct handle_info *hi, int override)
     option_func_t *opt;
     unsigned int i;
     char *set_display[] = {
-        "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", /* "STYLE", */
+        "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
         "EMAIL", "ANNOUNCEMENTS", "AUTOHIDE", "MAXLOGINS", "LANGUAGE",
         "FAKEHOST", "TITLE", "EPITHET"
     };
@@ -2486,31 +2657,39 @@ static OPTION_FUNC(opt_autohide)
     return 1;
 }
 
-/*
 static OPTION_FUNC(opt_style)
 {
     char *style;
 
     if (argc > 1) {
-       if (!irccasecmp(argv[1], "Zoot"))
-           hi->userlist_style = HI_STYLE_ZOOT;
-       else if (!irccasecmp(argv[1], "def"))
-           hi->userlist_style = HI_STYLE_DEF;
-    }
+        if (!irccasecmp(argv[1], "Clean"))
+            hi->userlist_style = HI_STYLE_CLEAN;
+        else if (!irccasecmp(argv[1], "Advanced"))
+            hi->userlist_style = HI_STYLE_ADVANCED;
+        else if (!irccasecmp(argv[1], "Classic"))
+            hi->userlist_style = HI_STYLE_CLASSIC;
+        else  /* Default to normal */
+            hi->userlist_style = HI_STYLE_NORMAL;
+    } /* TODO: give error if unknow style is chosen */
 
     switch (hi->userlist_style) {
-    case HI_STYLE_DEF:
-       style = "def";
-       break;
-    case HI_STYLE_ZOOT:
-    default:
-       style = "Zoot";
+        case HI_STYLE_ADVANCED:
+            style = "Advanced";
+            break;
+        case HI_STYLE_CLASSIC:
+            style = "Classic";
+            break;
+        case HI_STYLE_CLEAN:
+            style = "Clean";
+            break;
+        case HI_STYLE_NORMAL:
+        default:
+        style = "Normal";
     }
 
     send_message(user, nickserv, "NSMSG_SET_STYLE", style);
     return 1;
 }
-*/
 
 static OPTION_FUNC(opt_announcements)
 {
@@ -2549,6 +2728,9 @@ static OPTION_FUNC(opt_password)
     if (argc > 1)
        cryptpass(argv[1], hi->passwd);
 
+    if (nickserv_conf.sync_log)
+        SyncLog("PASSCHANGE %s %s", hi->handle, hi->passwd);
+
     send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
     return 1;
 }
@@ -2581,7 +2763,7 @@ static OPTION_FUNC(opt_email)
 {
     if (argc > 1) {
         const char *str;
-        if (!is_valid_email_addr(argv[1])) {
+        if (!valid_email(argv[1])) {
             send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
             return 0;
         }
@@ -2679,6 +2861,8 @@ static OPTION_FUNC(opt_epithet)
 {
     if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
         char *epithet;
+        struct userNode *target, *next_un;
+
         if (!override) {
             send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
             return 0;
@@ -2692,6 +2876,12 @@ static OPTION_FUNC(opt_epithet)
             hi->epithet = NULL;
         else
             hi->epithet = strdup(epithet);
+
+        for (target = hi->users; target; target = next_un) {
+          irc_swhois(nickserv, target, hi->epithet);
+
+          next_un = target->next_authed;
+        }
     }
 
     if (hi->epithet)
@@ -2771,6 +2961,31 @@ static OPTION_FUNC(opt_fakehost)
     return 1;
 }
 
+static OPTION_FUNC(opt_note)
+{
+    if (!override) {
+        send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
+        return 0;
+    }
+
+    if (argc > 1) {
+        char *text = unsplit_string(argv + 1, argc - 1, NULL);
+
+        if (hi->note)
+            free(hi->note);
+
+        if ((text[0] == '*') && !text[1])
+            hi->note = NULL;
+        else {
+            if (!(hi->note = nickserv_add_note(user->handle_info->handle, now, text)))
+                hi->note = NULL;
+        }
+    }
+
+    send_message(user, nickserv, "NSMSG_SET_NOTE", hi->note->note);
+    return 1;
+}
+
 static NICKSERV_FUNC(cmd_reclaim)
 {
     struct handle_info *hi;
@@ -2857,7 +3072,7 @@ static NICKSERV_FUNC(cmd_unregister)
     passwd = argv[1];
     argv[1] = "****";
     if (checkpass(passwd, hi->passwd)) {
-        nickserv_unregister_handle(hi, user);
+        nickserv_unregister_handle(hi, user, cmd->parent->bot);
         return 1;
     } else {
        log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
@@ -2873,7 +3088,7 @@ static NICKSERV_FUNC(cmd_ounregister)
     NICKSERV_MIN_PARMS(2);
     if (!(hi = get_victim_oper(user, argv[1])))
         return 0;
-    nickserv_unregister_handle(hi, user);
+    nickserv_unregister_handle(hi, user, cmd->parent->bot);
     return 1;
 }
 
@@ -2971,6 +3186,14 @@ nickserv_saxdb_write(struct saxdb_context *ctx) {
             saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
         if (hi->epithet)
             saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
+        if (hi->note) {
+            saxdb_start_record(ctx, KEY_NOTE_NOTE, 0);
+            saxdb_write_string(ctx, KEY_NOTE_SETTER, hi->note->setter);
+            saxdb_write_int(ctx, KEY_NOTE_DATE, hi->note->date);
+            saxdb_write_string(ctx, KEY_NOTE_NOTE, hi->note->note);
+            saxdb_end_record(ctx);
+        }
+
         if (hi->fakehost)
             saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
         if (hi->flags) {
@@ -2992,6 +3215,8 @@ nickserv_saxdb_write(struct saxdb_context *ctx) {
         saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
         if (hi->masks->used)
             saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
+        if (hi->ignores->used)
+            saxdb_write_string_list(ctx, KEY_IGNORES, hi->ignores);
         if (hi->maxlogins)
             saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
         if (hi->nicks) {
@@ -3019,6 +3244,7 @@ nickserv_saxdb_write(struct saxdb_context *ctx) {
         saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
         saxdb_end_record(ctx);
     }
+
     return 0;
 }
 
@@ -3083,6 +3309,16 @@ static NICKSERV_FUNC(cmd_merge)
             string_list_append(hi_to->masks, strdup(mask));
     }
 
+    /* Merge the ignores. */
+    for (ii=0; ii<hi_from->ignores->used; ii++) {
+        char *ignore = hi_from->ignores->list[ii];
+        for (jj=0; jj<hi_to->ignores->used; jj++)
+            if (match_ircglobs(hi_to->ignores->list[jj], ignore))
+                break;
+        if (jj==hi_to->ignores->used) /* Nothing from the "to" handle covered this mask, so add it. */
+            string_list_append(hi_to->ignores, strdup(ignore));
+    }
+
     /* Merge the lists of authed users. */
     if (hi_to->users) {
         for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
@@ -3152,7 +3388,7 @@ static NICKSERV_FUNC(cmd_merge)
     global_message(MESSAGE_RECIPIENT_STAFF, buffer);
 
     /* Unregister the "from" handle. */
-    nickserv_unregister_handle(hi_from, NULL);
+    nickserv_unregister_handle(hi_from, NULL, cmd->parent->bot);
 
     return 1;
 }
@@ -3375,7 +3611,7 @@ static void
 search_unregister_func (struct userNode *source, struct handle_info *match)
 {
     if (oper_has_access(source, nickserv, match->opserv_level, 0))
-        nickserv_unregister_handle(match, source);
+        nickserv_unregister_handle(match, source, nickserv); // XXX nickserv hard coded
 }
 
 static int
@@ -3491,13 +3727,15 @@ static void
 nickserv_db_read_handle(const char *handle, dict_t obj)
 {
     const char *str;
-    struct string_list *masks, *slist;
+    struct string_list *masks, *slist, *ignores;
     struct handle_info *hi;
     struct userNode *authed_users;
     struct userData *channels;
     unsigned long int id;
     unsigned int ii;
     dict_t subdb;
+    char *setter, *note;
+    time_t date;
 
     str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
     id = str ? strtoul(str, NULL, 0) : 0;
@@ -3527,6 +3765,8 @@ nickserv_db_read_handle(const char *handle, dict_t obj)
     hi->channels = channels;
     masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
     hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
+    ignores = database_get_data(obj, KEY_IGNORES, RECDB_STRING_LIST);
+    hi->ignores = ignores ? string_list_copy(ignores) : alloc_string_list(1);
     str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
     hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
     str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
@@ -3553,7 +3793,7 @@ nickserv_db_read_handle(const char *handle, dict_t obj)
             hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
     }
     str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
-    hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
+    hi->userlist_style = str ? str[0] : HI_DEFAULT_STYLE;
     str = database_get_data(obj, KEY_ANNOUNCEMENTS, RECDB_QSTRING);
     hi->announcements = str ? str[0] : '?';
     str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
@@ -3571,6 +3811,19 @@ nickserv_db_read_handle(const char *handle, dict_t obj)
     str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
     if (str)
         hi->epithet = strdup(str);
+    subdb = database_get_data(obj, KEY_NOTE_NOTE, RECDB_OBJECT);
+    if (subdb) {
+        setter = database_get_data(subdb, KEY_NOTE_SETTER, RECDB_QSTRING);
+        str = database_get_data(subdb, KEY_NOTE_DATE, RECDB_QSTRING);
+        date = str ? (time_t)strtoul(str, NULL, 0) : now;
+        note = database_get_data(subdb, KEY_NOTE_NOTE, RECDB_QSTRING);
+        if (setter && date && note)
+        {
+            if (!(hi->note = nickserv_add_note(setter, date, note)))
+                hi->note = NULL;
+        }
+    }
+
     str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
     if (str)
         hi->fakehost = strdup(str);
@@ -3670,7 +3923,7 @@ expire_handles(UNUSED_ARG(void *data))
         expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
         if ((now - hi->lastseen) > expiry) {
             log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
-            nickserv_unregister_handle(hi, NULL);
+            nickserv_unregister_handle(hi, NULL, NULL);
         }
     }
 
@@ -3851,6 +4104,9 @@ nickserv_conf_read(void)
     str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
     nickserv_conf.titlehost_suffix = str ? str : "example.net";
 
+    str = database_get_data(conf_node, KEY_DEFAULT_STYLE, RECDB_QSTRING);
+    nickserv_conf.default_style = str ? str[0] : HI_DEFAULT_STYLE;
+
     str = database_get_data(conf_node, KEY_AUTO_OPER, RECDB_QSTRING);
     nickserv_conf.auto_oper = str ? str : "";
 
@@ -4060,6 +4316,7 @@ init_nickserv(const char *nick)
     conf_register_reload(nickserv_conf_read);
     nickserv_opt_dict = dict_new();
     nickserv_email_dict = dict_new();
+
     dict_set_free_keys(nickserv_email_dict, free);
     dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
 
@@ -4104,6 +4361,11 @@ init_nickserv(const char *nick)
         dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
     }
     nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
+    /* ignore commands */
+    nickserv_define_func("ADDIGNORE", cmd_addignore, -1, 1, 0);
+    nickserv_define_func("OADDIGNORE", cmd_oaddignore, 0, 1, 0);
+    nickserv_define_func("DELIGNORE", cmd_delignore, -1, 1, 0);
+    nickserv_define_func("ODELIGNORE", cmd_odelignore, 0, 1, 0);
     /* miscellaneous commands */
     nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
     nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
@@ -4117,13 +4379,14 @@ init_nickserv(const char *nick)
     dict_insert(nickserv_opt_dict, "COLOR", opt_color);
     dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
     dict_insert(nickserv_opt_dict, "AUTOHIDE", opt_autohide);
-/*    dict_insert(nickserv_opt_dict, "STYLE", opt_style); */
+    dict_insert(nickserv_opt_dict, "STYLE", opt_style); 
     dict_insert(nickserv_opt_dict, "PASS", opt_password);
     dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
     dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
     dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
     dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
     dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
+    dict_insert(nickserv_opt_dict, "NOTE", opt_note);
     if (nickserv_conf.titlehost_suffix) {
         dict_insert(nickserv_opt_dict, "TITLE", opt_title);
         dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);