]> jfr.im git - irc/evilnet/x3.git/blobdiff - src/nickserv.c
http://sourceforge.net/tracker/index.php?func=detail&aid=1174897&group_id=4905&atid...
[irc/evilnet/x3.git] / src / nickserv.c
index 2127aeadddbc004a3d64a6f6636ba68f6fc849c9..26e631480fcb1a263eadd177056e9997bcd0199c 100644 (file)
@@ -1,7 +1,7 @@
 /* nickserv.c - Nick/authentication service
  * Copyright 2000-2004 srvx Development Team
  *
- * This file is part of srvx.
+ * This file is part of x3.
  *
  * srvx is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -50,6 +50,8 @@
 #define KEY_SET_TITLE_LEVEL "set_title_level"
 #define KEY_SET_FAKEHOST_LEVEL "set_fakehost_level"
 #define KEY_TITLEHOST_SUFFIX "titlehost_suffix"
+#define KEY_AUTO_OPER "auto_oper"
+#define KEY_AUTO_ADMIN "auto_admin"
 #define KEY_FLAG_LEVELS "flag_levels"
 #define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq"
 #define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq"
@@ -66,6 +68,7 @@
 #define KEY_EMAIL_VISIBLE_LEVEL "email_visible_level"
 #define KEY_EMAIL_ENABLED "email_enabled"
 #define KEY_EMAIL_REQUIRED "email_required"
+#define KEY_SYNC_LOG "sync_log"
 #define KEY_COOKIE_TIMEOUT "cookie_timeout"
 #define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
 #define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
@@ -109,7 +112,7 @@ DEFINE_LIST(handle_info_list, struct handle_info*);
 #define NICKSERV_MIN_PARMS(N) do { \
   if (argc < N) { \
     reply("MSG_MISSING_PARAMS", argv[0]); \
-    svccmd_send_help(user, nickserv, cmd); \
+    svccmd_send_help_brief(user, nickserv, cmd); \
     return 0; \
   } } while (0)
 
@@ -133,7 +136,7 @@ static const struct message_entry msgtab[] = {
     { "NSMSG_HANDLE_TOLONG", "The account name %s is too long. Account names must be %lu charactors or less."},
     { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
     { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
-    { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
+    { "NSMSG_PASSWORD_DICTIONARY", "Your password is too simple. You must choose a password that is not just a word or name." },
     { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
     { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
     { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
@@ -150,17 +153,19 @@ static const struct message_entry msgtab[] = {
     { "NSMSG_COOKIE_LIVE", "Account $b%s$b already has a cookie active.  Please either finish using that cookie, wait for it to expire, or auth to the account and use the $bdelcookie$b command." },
     { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding.  Please use the cookie or wait for it to expire." },
     { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
+    { "NSMSG_NO_COOKIE_FOREIGN", "The account $b%s$b does not have any cookie issued right now." },
     { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
     { "NSMSG_BAD_COOKIE", "That cookie is not the right one.  Please make sure you are copying it EXACTLY from the email; it is case-sensitive, so $bABC$b is different from $babc$b." },
     { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered).  You are now authenticated to your account." },
     { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
     { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
-    { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
+    { "NSMSG_EMAIL_OVERUSED", "That email address already has an account. Use RESETPASS if you forgot your password." },
     { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
     { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
     { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry.  I am confused.  Please report this bug." },
     { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
     { "NSMSG_ATE_COOKIE", "I ate the cookie for your account.  You may now have another." },
+    { "NSMSG_ATE_FOREIGN_COOKIE", "I ate the cookie for account $b%s$b.  It may now have another." },
     { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
     { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
     { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
@@ -182,29 +187,30 @@ static const struct message_entry msgtab[] = {
     { "NSMSG_TITLE_INVALID", "Titles cannot contain any dots; please choose another." },
     { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." },
     { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
-    { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
-    { "NSMSG_HANDLEINFO_ID", "  Account ID: %lu" },
-    { "NSMSG_HANDLEINFO_REGGED", "  Registered on: %s" },
-    { "NSMSG_HANDLEINFO_LASTSEEN", "  Last seen: %s" },
-    { "NSMSG_HANDLEINFO_LASTSEEN_NOW", "  Last seen: Right now!" },
-    { "NSMSG_HANDLEINFO_VACATION", "  On vacation." },
-    { "NSMSG_HANDLEINFO_EMAIL_ADDR", "  Email address: %s" },
-    { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", "  Cookie: There is currently an activation cookie issued for this account" },
-    { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", "  Cookie: There is currently a password change cookie issued for this account" },
-    { "NSMSG_HANDLEINFO_COOKIE_EMAIL", "  Cookie: There is currently an email change cookie issued for this account" },
-    { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", "  Cookie: There is currently an allowauth cookie issued for this account" },
-    { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", "  Cookie: There is currently an unknown cookie issued for this account" },
-    { "NSMSG_HANDLEINFO_INFOLINE", "  Infoline: %s" },
-    { "NSMSG_HANDLEINFO_FLAGS", "  Flags: %s" },
-    { "NSMSG_HANDLEINFO_EPITHET", "  Epithet: %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_CHANNELS", "  Channel(s): %s" },
-    { "NSMSG_HANDLEINFO_CURRENT", "  Current nickname(s): %s" },
-    { "NSMSG_HANDLEINFO_DNR", "  Do-not-register (by %s): %s" },
+    { "NSMSG_HANDLEINFO_ON", "$bAccount Information for %s$b" },
+    { "NSMSG_HANDLEINFO_END", "----------End of Account Info-----------" },
+    { "NSMSG_HANDLEINFO_ID", "Account ID: %lu" },
+    { "NSMSG_HANDLEINFO_REGGED", "Registered on: %s" },
+    { "NSMSG_HANDLEINFO_LASTSEEN", "Last seen: %s" },
+    { "NSMSG_HANDLEINFO_LASTSEEN_NOW", "Last seen: Right now!" },
+    { "NSMSG_HANDLEINFO_VACATION", "On vacation." },
+    { "NSMSG_HANDLEINFO_EMAIL_ADDR", "Email address: %s" },
+    { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", "Cookie: There is currently an activation cookie issued for this account" },
+    { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", "Cookie: There is currently a password change cookie issued for this account" },
+    { "NSMSG_HANDLEINFO_COOKIE_EMAIL", "Cookie: There is currently an email change cookie issued for this account" },
+    { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", "Cookie: There is currently an allowauth cookie issued for this account" },
+    { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", "Cookie: There is currently an unknown cookie issued for this account" },
+    { "NSMSG_HANDLEINFO_INFOLINE", "Infoline: %s" },
+    { "NSMSG_HANDLEINFO_FLAGS", "Flags: %s" },
+    { "NSMSG_HANDLEINFO_EPITHET", "Epithet: %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_CHANNELS", "Channel(s): %s" },
+    { "NSMSG_HANDLEINFO_CURRENT", "Current nickname(s): %s" },
+    { "NSMSG_HANDLEINFO_DNR", "Do-not-register (by %s): %s" },
     { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
     { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
     { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
@@ -270,7 +276,9 @@ static const struct message_entry msgtab[] = {
     { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
     { "NSMSG_RECLAIMED_KILL",  "Disconnected %s from the network." },
     { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
-    { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
+    { "NSMSG_SETTING_LIST", "$b$N account settings$b" },
+    { "NSMSG_SETTING_LIST_HEADER", "----------------------------------------" },
+    { "NSMSG_SETTING_LIST_END",    "-------------End Of Settings------------" },
     { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
     { "NSMSG_INVALID_ANNOUNCE", "$b%s$b is an invalid announcements value." },
     { "NSMSG_SET_INFO", "$bINFO:         $b%s" },
@@ -280,6 +288,7 @@ static const struct message_entry msgtab[] = {
     { "NSMSG_SET_PRIVMSG", "$bPRIVMSG:      $b%s" },
     { "NSMSG_SET_STYLE", "$bSTYLE:        $b%s" },
     { "NSMSG_SET_ANNOUNCEMENTS", "$bANNOUNCEMENTS: $b%s" },
+    { "NSMSG_SET_AUTOHIDE", "$bAUTOHIDE: $b%s" },
     { "NSMSG_SET_PASSWORD", "$bPASSWORD:     $b%s" },
     { "NSMSG_SET_FLAGS", "$bFLAGS:        $b%s" },
     { "NSMSG_SET_EMAIL", "$bEMAIL:        $b%s" },
@@ -289,6 +298,10 @@ static const struct message_entry msgtab[] = {
     { "NSMSG_SET_EPITHET", "$bEPITHET:      $b%s" },
     { "NSMSG_SET_TITLE", "$bTITLE:        $b%s" },
     { "NSMSG_SET_FAKEHOST", "$bFAKEHOST:    $b%s" },
+
+    { "NSMSG_AUTO_OPER", "You have been auto-opered" },
+    { "NSMSG_AUTO_OPER_ADMIN", "You have been auto-admined" },
+
     { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
     { "NSEMAIL_ACTIVATION_BODY", 
         "This email has been sent to verify that this email address belongs to the person who tried to register an account on %1$s.  Your cookie is:\n"
@@ -306,7 +319,7 @@ static const struct message_entry msgtab[] = {
         "This email has been sent to verify that this email address belongs to the person who tried to register an account on %1$s.  Your cookie is:\n"
         "%2$s\n"
         "To verify your email address and complete the account registration, visit the following URL:\n"
-        "http://www.afternet.org/play/index.php?option=com_registration&task=activate&username=%5$s&cookie=%2$s\n"
+        "http://www.afternet.org/index.php?option=com_registration&task=activate&username=%5$s&cookie=%2$s\n"
         "\n"
         "If you did NOT request this account, you do not need to do anything.\n"
         "Please contact the %1$s staff if you have questions, and be sure to check our website." },
@@ -320,12 +333,14 @@ static const struct message_entry msgtab[] = {
     { "NSEMAIL_PASSWORD_CHANGE_BODY_WEB", 
         "This email has been sent to verify that you wish to change the password on your account %5$s.  Your cookie is %2$s.\n"
         "To complete the password change, click the following URL:\n"
-        "http://www.afternet.org/play/index.php?option=com_registration&task=passcookie&username=%5$s&cookie=%2$s\n"
+        "http://www.afternet.org/index.php?option=com_registration&task=passcookie&username=%5$s&cookie=%2$s\n"
         "If you did NOT request your password to be changed, you do not need to do anything.\n"
         "Please contact the %1$s staff if you have questions." },
     { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
+#ifdef stupid_verify_old_email        
     { "NSEMAIL_EMAIL_CHANGE_BODY_NEW", "This email has been sent to verify that your email address belongs to the same person as account %5$s on %1$s.  The SECOND HALF of your cookie is %2$.*6$s.\nTo verify your address as associated with this account, log on to %1$s and type the following command:\n    /msg %3$s@%4$s COOKIE %5$s ?????%2$.*6$s\n(Replace the ????? with the FIRST HALF of the cookie, as sent to your OLD email address.)\nIf you did NOT request this email address to be associated with this account, you do not need to do anything.  Please contact the %1$s staff if you have questions." },
     { "NSEMAIL_EMAIL_CHANGE_BODY_OLD", "This email has been sent to verify that you want to change your email for account %5$s on %1$s from this address to %7$s.  The FIRST HALF of your cookie is %2$.*6$s\nTo verify your new address as associated with this account, log on to %1$s and type the following command:\n    /msg %3$s@%4$s COOKIE %5$s %2$.*6$s?????\n(Replace the ????? with the SECOND HALF of the cookie, as sent to your NEW email address.)\nIf you did NOT request this change of email address, you do not need to do anything.  Please contact the %1$s staff if you have questions." },
+#endif
     { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
     { "NSEMAIL_EMAIL_VERIFY_BODY", "This email has been sent to verify that this address belongs to the same person as %5$s on %1$s.  Your cookie is %2$s.\nTo verify your address as associated with this account, log on to %1$s and type the following command:\n    /msg %3$s@%4$s COOKIE %5$s %2$s\nIf you did NOT request this email address to be associated with this account, you do not need to do anything.  Please contact the %1$s staff if you have questions." },
     { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
@@ -354,6 +369,7 @@ static struct {
     unsigned int default_hostmask : 1;
     unsigned int warn_nick_owned : 1;
     unsigned int warn_clone_auth : 1;
+    unsigned int sync_log : 1;
     unsigned long nicks_per_handle;
     unsigned long password_min_length;
     unsigned long password_min_digits;
@@ -383,6 +399,8 @@ static struct {
     unsigned long auto_reclaim_delay;
     unsigned char default_maxlogins;
     unsigned char hard_maxlogins;
+    const char *auto_oper;
+    const char *auto_admin;
 } nickserv_conf;
 
 /* We have 2^32 unique account IDs to use. */
@@ -561,6 +579,10 @@ nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
         else
             send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
     }
+
+    if (nickserv_conf.sync_log)
+      SyncLog("UNREGISTER %s", hi->handle);
+
     dict_remove(nickserv_handle_dict, hi->handle);
 }
 
@@ -1026,6 +1048,27 @@ nickserv_bake_cookie(struct handle_cookie *cookie)
     timeq_add(cookie->expires, nickserv_free_cookie, cookie);
 }
 
+/* Contributed by the great sneep of afternet ;) */
+/* Since this gets used in a URL, we want to avoid stuff that confuses
+ * email clients such as ] and ?. a-z, 0-9 only.
+ */
+void genpass(char *str, int len)
+{
+        int i = 0;
+        char c = 0;
+
+        for(i = 0; i < len; i++)
+        {
+                do
+                {
+                        c = (char)((float)rand() / (float)RAND_MAX * (float)256);
+                } while(!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')));
+                str[i] = c;
+        }
+        str[i] = '\0';
+        return;
+}
+
 static void
 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data, int weblink)
 {
@@ -1043,9 +1086,14 @@ nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_
     cookie->hi = hi;
     cookie->type = type;
     cookie->data = cookie_data ? strdup(cookie_data) : NULL;
+
     cookie->expires = now + nickserv_conf.cookie_timeout;
-    inttobase64(cookie->cookie, rand(), 5);
-    inttobase64(cookie->cookie+5, rand(), 5);
+    /* Adding dedicated password gen function for more control -Rubin */
+    genpass(cookie->cookie, 10);
+    /*
+     *inttobase64(cookie->cookie, rand(), 5);
+     *inttobase64(cookie->cookie+5, rand(), 5);
+     */
 
     netname = nickserv_conf.network_name;
     subject[0] = 0;
@@ -1078,6 +1126,7 @@ nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_
     case EMAIL_CHANGE:
         misc = hi->email_addr;
         hi->email_addr = cookie->data;
+#ifdef stupid_verify_old_email        
         if (misc) {
             send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
             fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
@@ -1088,6 +1137,7 @@ nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_
             fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
             snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
         } else {
+#endif
             send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
             fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
             snprintf(subject, sizeof(subject), fmt, netname);
@@ -1095,7 +1145,9 @@ nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_
             snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
             sendmail(nickserv, hi, subject, body, 1);
             subject[0] = 0;
+#ifdef stupid_verify_old_email
         }
+#endif
         hi->email_addr = misc;
         break;
     case ALLOWAUTH:
@@ -1156,6 +1208,7 @@ static NICKSERV_FUNC(cmd_register)
 {
     struct handle_info *hi;
     const char *email_addr, *password;
+    char syncpass[MD5_CRYPT_LENGTH];
     int no_auth, weblink;
 
     if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
@@ -1267,6 +1320,19 @@ static NICKSERV_FUNC(cmd_register)
     /* Set registering flag.. */
     user->modes |= FLAGS_REGISTERING; 
 
+    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
+       * disabled which they wont be for us. Email Required MUST be set on if you are using this.
+       * -SiRVulcaN
+       */
+      SyncLog("REGISTER %s %s %s %s", hi->handle, syncpass, email_addr ? email_addr : "0", user->info);
+    }
+
+    /* this wont work if email is required .. */
+    process_adduser_pending(user);
+
     return 1;
 }
 
@@ -1333,6 +1399,7 @@ static NICKSERV_FUNC(cmd_handleinfo)
 
     nsmsg_none = handle_find_message(hi, "MSG_NONE");
     reply("NSMSG_HANDLEINFO_ON", hi->handle);
+    reply("MSG_BAR");
 #ifdef WITH_PROTOCOL_BAHAMUT
     reply("NSMSG_HANDLEINFO_ID", hi->id);
 #endif
@@ -1355,8 +1422,10 @@ static NICKSERV_FUNC(cmd_handleinfo)
             reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
         if (!oper_outranks(user, hi))
             return 1;
-    } else if (hi != user->handle_info)
+    } else if (hi != user->handle_info) {
+        reply("NSMSG_HANDLEINFO_END");
         return 1;
+    }
 
     if (nickserv_conf.email_enabled)
         reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
@@ -1459,7 +1528,7 @@ static NICKSERV_FUNC(cmd_handleinfo)
            }
             if (IsUserSuspended(channel))
                 buff[pos++] = '-';
-            pos += sprintf(buff+pos, "%d:%s ", channel->access, name);
+            pos += sprintf(buff+pos, "%s:%s ", user_level_name_from_level(channel->access), name);
            if (next == NULL) {
              print_chans_buff:
                buff[pos-1] = 0;
@@ -1489,6 +1558,7 @@ static NICKSERV_FUNC(cmd_handleinfo)
        }
     }
 
+    reply("NSMSG_HANDLEINFO_END");
     return 1;
 }
 
@@ -1665,7 +1735,7 @@ static NICKSERV_FUNC(cmd_auth)
         pw_arg = 1;
     } else {
         reply("MSG_MISSING_PARAMS", argv[0]);
-        svccmd_send_help(user, nickserv, cmd);
+        svccmd_send_help_brief(user, nickserv, cmd);
         return 0;
     }
     if (!hi) {
@@ -1732,7 +1802,34 @@ static NICKSERV_FUNC(cmd_auth)
         reply("NSMSG_WEAK_PASSWORD");
     if (hi->passwd[0] != '$')
         cryptpass(passwd, hi->passwd);
+
+   /* If a channel was waiting for this user to auth, 
+    * finish adding them */
+    process_adduser_pending(user);
+
     reply("NSMSG_AUTH_SUCCESS");
+
+    
+    /* Set +x if autohide is on */
+    if(HANDLE_FLAGGED(hi, AUTOHIDE))
+        irc_umode(user, "+x");
+
+    if(!IsOper(user)) /* If they arnt already opered.. */
+    {
+        /* Auto Oper users with Opserv access -Life4Christ 8-10-2005  */
+        if( nickserv_conf.auto_admin[0] && hi->opserv_level >= opserv_conf_admin_level())
+        {
+            irc_umode(user,nickserv_conf.auto_admin);
+            reply("NSMSG_AUTO_OPER_ADMIN");
+        }
+        else if (nickserv_conf.auto_oper[0] && hi->opserv_level > 0)
+        {
+            irc_umode(user,nickserv_conf.auto_oper);
+            reply("NSMSG_AUTO_OPER");
+        }
+    }
+
+   /* Wipe out the pass for the logs */
     argv[pw_arg] = "****";
     return 1;
 }
@@ -1860,6 +1957,26 @@ static NICKSERV_FUNC(cmd_delcookie)
     return 1;
 }
 
+static NICKSERV_FUNC(cmd_odelcookie)
+{
+    struct handle_info *hi;
+
+    NICKSERV_MIN_PARMS(2);
+
+    if (!(hi = get_victim_oper(user, argv[1])))
+        return 0;
+
+    if (!hi->cookie) {
+        reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle);
+        return 0;
+    }
+
+    nickserv_eat_cookie(hi->cookie);
+    reply("NSMSG_ATE_FOREIGN_COOKIE", hi->handle);
+
+    return 1;
+}
+
 static NICKSERV_FUNC(cmd_resetpass)
 {
     struct handle_info *hi;
@@ -1941,15 +2058,28 @@ static NICKSERV_FUNC(cmd_cookie)
         safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
         set_user_handle_info(user, hi, 1);
         reply("NSMSG_HANDLE_ACTIVATED");
+        if (nickserv_conf.sync_log)
+          SyncLog("ACCOUNTACC %s", hi->handle);
         break;
     case PASSWORD_CHANGE:
         set_user_handle_info(user, hi, 1);
         safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
         reply("NSMSG_PASSWORD_CHANGED");
+        if (nickserv_conf.sync_log)
+          SyncLog("PASSCHANGE %s %s", hi->handle, hi->passwd);
         break;
     case EMAIL_CHANGE:
+        if (!hi->email_addr && nickserv_conf.sync_log) {
+          /*
+           * 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);
+        }
         nickserv_set_email_addr(hi, hi->cookie->data);
         reply("NSMSG_EMAIL_CHANGED");
+        if (nickserv_conf.sync_log)
+          SyncLog("EMAILCHANGE %s %s", hi->handle, hi->cookie->data);
         break;
     case ALLOWAUTH:
         set_user_handle_info(user, hi, 1);
@@ -1963,6 +2093,8 @@ static NICKSERV_FUNC(cmd_cookie)
 
     nickserv_eat_cookie(hi->cookie);
 
+    process_adduser_pending(user);
+
     return 1;
 }
 
@@ -2030,6 +2162,8 @@ static NICKSERV_FUNC(cmd_pass)
        return 0;
     }
     cryptpass(new_pass, hi->passwd);
+    if (nickserv_conf.sync_log)
+      SyncLog("PASSCHANGE %s %s", hi->handle, hi->passwd);
     argv[1] = "****";
     reply("NSMSG_PASS_SUCCESS");
     return 1;
@@ -2194,16 +2328,19 @@ 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",
-        "EMAIL", "ANNOUNCEMENTS", "MAXLOGINS", "LANGUAGE"
+        "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", /* "STYLE", */
+        "EMAIL", "ANNOUNCEMENTS", "AUTOHIDE", "MAXLOGINS", "LANGUAGE",
+        "FAKEHOST", "TITLE", "EPITHET"
     };
 
     send_message(user, nickserv, "NSMSG_SETTING_LIST");
+    send_message(user, nickserv, "NSMSG_SETTING_LIST_HEADER");
 
     /* Do this so options are presented in a consistent order. */
     for (i = 0; i < ArrayLength(set_display); ++i)
        if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
            opt(user, hi, override, 0, NULL);
+    send_message(user, nickserv, "NSMSG_SETTING_LIST_END");
 }
 
 static NICKSERV_FUNC(cmd_set)
@@ -2325,6 +2462,24 @@ static OPTION_FUNC(opt_privmsg)
     return 1;
 }
 
+static OPTION_FUNC(opt_autohide)
+{
+    if (argc > 1) {
+       if (enabled_string(argv[1]))
+           HANDLE_SET_FLAG(hi, AUTOHIDE);
+        else if (disabled_string(argv[1]))
+           HANDLE_CLEAR_FLAG(hi, AUTOHIDE);
+       else {
+           send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
+           return 0;
+       }
+    }
+
+    send_message(user, nickserv, "NSMSG_SET_AUTOHIDE", user_find_message(user, HANDLE_FLAGGED(hi, AUTOHIDE) ? "MSG_ON" : "MSG_OFF"));
+    return 1;
+}
+
+/*
 static OPTION_FUNC(opt_style)
 {
     char *style;
@@ -2348,6 +2503,7 @@ static OPTION_FUNC(opt_style)
     send_message(user, nickserv, "NSMSG_SET_STYLE", style);
     return 1;
 }
+*/
 
 static OPTION_FUNC(opt_announcements)
 {
@@ -2514,13 +2670,15 @@ static OPTION_FUNC(opt_level)
 
 static OPTION_FUNC(opt_epithet)
 {
-    if (!override) {
-        send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
-        return 0;
-    }
-
     if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
-        char *epithet = unsplit_string(argv+1, argc-1, NULL);
+        char *epithet;
+        if (!override) {
+            send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
+            return 0;
+        }
+
+        epithet = unsplit_string(argv+1, argc-1, NULL);
+
         if (hi->epithet)
             free(hi->epithet);
         if ((epithet[0] == '*') && !epithet[1])
@@ -2540,12 +2698,12 @@ static OPTION_FUNC(opt_title)
 {
     const char *title;
 
-    if (!override) {
-        send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
-        return 0;
-    }
-
     if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
+        if (!override) {
+            send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
+            return 0;
+        }
+
         title = argv[1];
         if (strchr(title, '.')) {
             send_message(user, nickserv, "NSMSG_TITLE_INVALID");
@@ -2580,12 +2738,12 @@ static OPTION_FUNC(opt_fakehost)
 {
     const char *fake;
 
-    if (!override) {
-        send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
-        return 0;
-    }
-
     if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
+        if (!override) {
+            send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
+            return 0;
+        }
+
         fake = argv[1];
         if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) {
             send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
@@ -2974,6 +3132,13 @@ static NICKSERV_FUNC(cmd_merge)
     if (hi_from->lastseen > hi_to->lastseen)
         hi_to->lastseen = hi_from->lastseen;
 
+    /* Does a fakehost carry over?  (This intentionally doesn't set it
+     * for users previously attached to hi_to.  They'll just have to
+     * reconnect.)
+     */
+    if (hi_from->fakehost && !hi_to->fakehost)
+        hi_to->fakehost = strdup(hi_from->fakehost);
+
     /* Notify of success. */
     sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
     reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
@@ -3322,6 +3487,7 @@ nickserv_db_read_handle(const char *handle, dict_t obj)
     struct string_list *masks, *slist;
     struct handle_info *hi;
     struct userNode *authed_users;
+    struct userData *channels;
     unsigned long int id;
     unsigned int ii;
     dict_t subdb;
@@ -3335,10 +3501,13 @@ nickserv_db_read_handle(const char *handle, dict_t obj)
     }
     if ((hi = get_handle_info(handle))) {
         authed_users = hi->users;
+        channels = hi->channels;
         hi->users = NULL;
+        hi->channels = NULL;
         dict_remove(nickserv_handle_dict, hi->handle);
     } else {
         authed_users = NULL;
+        channels = NULL;
     }
     hi = register_handle(handle, str, id);
     if (authed_users) {
@@ -3348,6 +3517,7 @@ nickserv_db_read_handle(const char *handle, dict_t obj)
             authed_users = authed_users->next_authed;
         }
     }
+    hi->channels = channels;
     masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
     hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
     str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
@@ -3661,6 +3831,8 @@ nickserv_conf_read(void)
     nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
     str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
     nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
+    str = database_get_data(conf_node, KEY_SYNC_LOG, RECDB_QSTRING);
+    nickserv_conf.sync_log = str ? enabled_string(str) : 0;
     str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
     nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
     str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
@@ -3671,6 +3843,13 @@ nickserv_conf_read(void)
     nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
     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_AUTO_OPER, RECDB_QSTRING);
+    nickserv_conf.auto_oper = str ? str : "";
+
+    str = database_get_data(conf_node, KEY_AUTO_ADMIN, RECDB_QSTRING);
+    nickserv_conf.auto_admin = str ? str : "";
+
     str = conf_get_data("server/network", RECDB_QSTRING);
     nickserv_conf.network_name = str ? str : "some IRC network";
     if (!nickserv_conf.auth_policer_params) {
@@ -3914,6 +4093,7 @@ init_nickserv(const char *nick)
         nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 0);
         nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 0);
         nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
+        nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
         dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
     }
     nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
@@ -3929,7 +4109,8 @@ init_nickserv(const char *nick)
     dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
     dict_insert(nickserv_opt_dict, "COLOR", opt_color);
     dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
-    dict_insert(nickserv_opt_dict, "STYLE", opt_style);
+    dict_insert(nickserv_opt_dict, "AUTOHIDE", opt_autohide);
+/*    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);