Allow NickServ to ignore accounts if the user's account create ts is wrong.
[srvx.git] / src / nickserv.c
index dc6eeb7c9529190cddaaef4295c843e32f485e01..6717a5674b900cf93cec52434da83a5ce00cbf65 100644 (file)
@@ -73,6 +73,7 @@
 #define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
 #define KEY_OUNREGISTER_INACTIVE "ounregister_inactive"
 #define KEY_OUNREGISTER_FLAGS "ounregister_flags"
+#define KEY_HANDLE_TS_MODE "account_timestamp_mode"
 
 #define KEY_ID "id"
 #define KEY_PASSWD "passwd"
 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
 typedef OPTION_FUNC(option_func_t);
 
-DEFINE_LIST(handle_info_list, struct handle_info*);
+DEFINE_LIST(handle_info_list, struct handle_info*)
 
 #define NICKSERV_MIN_PARMS(N) do { \
   if (argc < N) { \
@@ -158,6 +159,7 @@ 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." },
@@ -169,6 +171,7 @@ static const struct message_entry msgtab[] = {
     { "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_COOKIE_FOREIGN", "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." },
@@ -274,7 +277,7 @@ static const struct message_entry msgtab[] = {
     { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
     { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
     { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
-    { "NSMSG_DB_MERGED", "$N merged DB from %s (in "FMT_TIME_T".%03lu seconds)." },
+    { "NSMSG_DB_MERGED", "$N merged DB from %s (in %lu.%03lu seconds)." },
     { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
     { "NSMSG_BAD_HANDLE", "Account $b%s$b not registered because it is in use by a network service, is too long, or contains invalid characters." },
     { "NSMSG_BAD_NICK", "Nickname $b%s$b not registered because it is in use by a network service, is too long, or contains invalid characters." },
@@ -324,6 +327,9 @@ static const struct message_entry msgtab[] = {
     { "NSEMAIL_ALLOWAUTH_BODY", "This email has been sent to let you authenticate (auth) to account %5$s on %1$s.  Your cookie is %2$s.\nTo auth to that 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 authorization, you do not need to do anything.  Please contact the %1$s staff if you have questions." },
     { "CHECKPASS_YES", "Yes." },
     { "CHECKPASS_NO", "No." },
+    { "CHECKEMAIL_NOT_SET", "No email set." },
+    { "CHECKEMAIL_YES", "Yes." },
+    { "CHECKEMAIL_NO", "No." },
     { NULL, NULL }
 };
 
@@ -337,6 +343,11 @@ static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum r
 static void nickserv_reclaim_p(void *data);
 static int nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask);
 
+enum handle_ts_mode {
+    TS_IGNORE,
+    TS_IRCU
+};
+
 static struct {
     unsigned int disable_nicks : 1;
     unsigned int valid_handle_regex_set : 1;
@@ -373,6 +384,7 @@ static struct {
     struct policer_params *auth_policer_params;
     enum reclaim_action reclaim_action;
     enum reclaim_action auto_reclaim_action;
+    enum handle_ts_mode handle_ts_mode;
     unsigned long auto_reclaim_delay;
     unsigned char default_maxlogins;
     unsigned char hard_maxlogins;
@@ -404,11 +416,10 @@ canonicalize_hostmask(char *mask)
 }
 
 static struct handle_info *
-register_handle(const char *handle, const char *passwd, UNUSED_ARG(unsigned long id))
+register_handle(const char *handle, const char *passwd, unsigned long id)
 {
     struct handle_info *hi;
 
-#ifdef WITH_PROTOCOL_BAHAMUT
     char id_base64[IDLEN + 1];
     do
     {
@@ -432,7 +443,6 @@ register_handle(const char *handle, const char *passwd, UNUSED_ARG(unsigned long
             id = 0;
         }
     } while(!id);
-#endif
 
     hi = calloc(1, sizeof(*hi));
     hi->userlist_style = HI_DEFAULT_STYLE;
@@ -441,10 +451,8 @@ register_handle(const char *handle, const char *passwd, UNUSED_ARG(unsigned long
     hi->infoline = NULL;
     dict_insert(nickserv_handle_dict, hi->handle, hi);
 
-#ifdef WITH_PROTOCOL_BAHAMUT
     hi->id = id;
     dict_insert(nickserv_id_dict, strdup(id_base64), hi);
-#endif
 
     return hi;
 }
@@ -517,13 +525,10 @@ static void
 free_handle_info(void *vhi)
 {
     struct handle_info *hi = vhi;
-
-#ifdef WITH_PROTOCOL_BAHAMUT
     char id[IDLEN + 1];
 
     inttobase64(id, hi->id, IDLEN);
     dict_remove(nickserv_id_dict, id);
-#endif
 
     free_string_list(hi->masks);
     assert(!hi->users);
@@ -944,23 +949,13 @@ set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
         user->next_authed = hi->users;
         hi->users = user;
         hi->lastseen = now;
-        if (IsHelper(user))
+        if (IsHelper(user) && !userList_contains(&curr_helpers, user))
             userList_append(&curr_helpers, user);
 
         if (hi->fakehost || old_info)
             apply_fakehost(hi);
 
         if (stamp) {
-#ifdef WITH_PROTOCOL_BAHAMUT
-            /* Stamp users with their account ID. */
-            char id[IDLEN + 1];
-            inttobase64(id, hi->id, IDLEN);
-#elif WITH_PROTOCOL_P10
-            /* Stamp users with their account name. */
-            char *id = hi->handle;
-#else
-            const char *id = "???";
-#endif
             if (!nickserv_conf.disable_nicks) {
                 struct nick_info *ni;
                 for (ni = hi->nicks; ni; ni = ni->next) {
@@ -970,7 +965,7 @@ set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
                     }
                 }
             }
-            StampUser(user, id);
+            StampUser(user, hi->handle, hi->registered, hi->id);
         }
 
         if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
@@ -1313,6 +1308,7 @@ static NICKSERV_FUNC(cmd_handleinfo)
     struct userNode *target, *next_un;
     struct handle_info *hi;
     const char *nsmsg_none;
+    time_t feh;
 
     if (argc < 2) {
         if (!(hi = user->handle_info)) {
@@ -1325,10 +1321,8 @@ static NICKSERV_FUNC(cmd_handleinfo)
 
     nsmsg_none = handle_find_message(hi, "MSG_NONE");
     reply("NSMSG_HANDLEINFO_ON", hi->handle);
-#ifdef WITH_PROTOCOL_BAHAMUT
-    reply("NSMSG_HANDLEINFO_ID", hi->id);
-#endif
-    reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi->registered));
+    feh = hi->registered;
+    reply("NSMSG_HANDLEINFO_REGGED", ctime(&feh));
 
     if (!hi->users) {
         intervalString(buff, now - hi->lastseen, user->handle_info);
@@ -1368,7 +1362,10 @@ static NICKSERV_FUNC(cmd_handleinfo)
         reply(type);
     }
 
-    if (oper_has_access(user, cmd->parent->bot, 0, 1) || IsSupport(user)) {
+    if (oper_has_access(user, cmd->parent->bot, 601, 1))
+        reply("NSMSG_HANDLEINFO_ID", hi->id);
+
+    if (oper_has_access(user, cmd->parent->bot, 0, 1) || IsStaff(user)) {
         if (!hi->notes) {
             reply("NSMSG_HANDLEINFO_NO_NOTES");
         } else {
@@ -1504,7 +1501,7 @@ static NICKSERV_FUNC(cmd_handleinfo)
         }
     }
 
-    return 1;
+    return 1 | ((hi != user->handle_info) ? CMD_LOG_STAFF : 0);
 }
 
 static NICKSERV_FUNC(cmd_userinfo)
@@ -1851,6 +1848,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_COOKIE_FOREIGN", hi->handle);
+    return 1;
+}
+
+
 static NICKSERV_FUNC(cmd_resetpass)
 {
     struct handle_info *hi;
@@ -2852,9 +2869,7 @@ nickserv_saxdb_write(struct saxdb_context *ctx) {
 
     for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
         hi = iter_data(it);
-#ifdef WITH_PROTOCOL_BAHAMUT
-        assert(hi->id);
-#endif
+        assert(hi->id != 0);
         saxdb_start_record(ctx, iter_key(it), 0);
         if (hi->cookie) {
             struct handle_cookie *cookie = hi->cookie;
@@ -2907,9 +2922,7 @@ nickserv_saxdb_write(struct saxdb_context *ctx) {
             flags[flen] = 0;
             saxdb_write_string(ctx, KEY_FLAGS, flags);
         }
-#ifdef WITH_PROTOCOL_BAHAMUT
         saxdb_write_int(ctx, KEY_ID, hi->id);
-#endif
         if (hi->infoline)
             saxdb_write_string(ctx, KEY_INFO, hi->infoline);
         if (hi->last_quit_host[0])
@@ -3089,8 +3102,8 @@ static NICKSERV_FUNC(cmd_merge)
 
 struct nickserv_discrim {
     unsigned long flags_on, flags_off;
-    time_t min_registered, max_registered;
-    time_t lastseen;
+    unsigned long min_registered, max_registered;
+    unsigned long lastseen;
     unsigned int limit;
     int min_level, max_level;
     int min_karma, max_karma;
@@ -3123,8 +3136,8 @@ nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
     discrim->max_level = INT_MAX;
     discrim->limit = 50;
     discrim->min_registered = 0;
-    discrim->max_registered = INT_MAX;
-    discrim->lastseen = LONG_MAX;
+    discrim->max_registered = ULONG_MAX;
+    discrim->lastseen = ULONG_MAX;
     discrim->min_karma = INT_MIN;
     discrim->max_karma = INT_MAX;
 
@@ -3451,6 +3464,25 @@ static MODCMD_FUNC(cmd_checkpass)
     return 1;
 }
 
+static MODCMD_FUNC(cmd_checkemail)
+{
+    struct handle_info *hi;
+
+    NICKSERV_MIN_PARMS(3);
+    if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
+        reply("MSG_HANDLE_UNKNOWN", argv[1]);
+        return 0;
+    }
+    if (!hi->email_addr)
+        reply("CHECKEMAIL_NOT_SET");
+    else if (!irccasecmp(argv[2], hi->email_addr))
+        reply("CHECKEMAIL_YES");
+    else
+        reply("CHECKEMAIL_NO");
+    return 1;
+}
+
+
 static void
 nickserv_db_read_handle(const char *handle, dict_t obj)
 {
@@ -3459,7 +3491,7 @@ nickserv_db_read_handle(const char *handle, dict_t obj)
     struct handle_info *hi;
     struct userNode *authed_users;
     struct userData *channels;
-    unsigned long int id;
+    unsigned long id;
     unsigned int ii;
     dict_t subdb;
 
@@ -3501,9 +3533,9 @@ nickserv_db_read_handle(const char *handle, dict_t obj)
     if (str)
         hi->infoline = strdup(str);
     str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
-    hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
+    hi->registered = str ? strtoul(str, NULL, 0) : now;
     str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
-    hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
+    hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
     str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
     hi->karma = str ? strtoul(str, NULL, 0) : 0;
     /* We want to read the nicks even if disable_nicks is set.  This is so
@@ -3656,7 +3688,7 @@ static NICKSERV_FUNC(cmd_mergedb)
         stop.tv_sec -= 1;
         stop.tv_usec += 1000000;
     }
-    reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
+    reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
     return 1;
 }
 
@@ -3664,7 +3696,7 @@ static void
 expire_handles(UNUSED_ARG(void *data))
 {
     dict_iterator_t it, next;
-    time_t expiry;
+    unsigned long expiry;
     struct handle_info *hi;
 
     for (it=dict_first(nickserv_handle_dict); it; it=next) {
@@ -3811,6 +3843,13 @@ nickserv_conf_read(void)
         if(pos)
             nickserv_conf.ounregister_flags |= 1 << (pos - 1);
     }
+    str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING);
+    if (!str)
+        nickserv_conf.handle_ts_mode = TS_IGNORE;
+    else if (!irccasecmp(str, "ircu"))
+        nickserv_conf.handle_ts_mode = TS_IRCU;
+    else
+        nickserv_conf.handle_ts_mode = TS_IGNORE;
     if (!nickserv_conf.disable_nicks) {
         str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
         nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
@@ -3945,23 +3984,29 @@ handle_new_user(struct userNode *user)
 }
 
 void
-handle_account(struct userNode *user, const char *stamp)
+handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
 {
-    struct handle_info *hi;
+    struct handle_info *hi = NULL;
 
-#ifdef WITH_PROTOCOL_P10
-    hi = dict_find(nickserv_handle_dict, stamp, NULL);
-#else
-    hi = dict_find(nickserv_id_dict, stamp, NULL);
-#endif
+    if (stamp != NULL)
+        hi = dict_find(nickserv_handle_dict, stamp, NULL);
+    if ((hi == NULL) && (serial != 0)) {
+        char id[IDLEN + 1];
+        inttobase64(id, serial, IDLEN);
+        hi = dict_find(nickserv_id_dict, id, NULL);
+    }
 
     if (hi) {
+        if ((nickserv_conf.handle_ts_mode == TS_IRCU)
+            && (timestamp != hi->registered)) {
+            return;
+        }
         if (HANDLE_FLAGGED(hi, SUSPENDED)) {
             return;
         }
         set_user_handle_info(user, hi, 0);
     } else {
-        log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
+        log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial);
     }
 }
 
@@ -4096,6 +4141,7 @@ init_nickserv(const char *nick)
         nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
         nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
         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);
@@ -4105,6 +4151,7 @@ init_nickserv(const char *nick)
     nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
     nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
     nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
+    nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
     /* other options */
     dict_insert(nickserv_opt_dict, "INFO", opt_info);
     dict_insert(nickserv_opt_dict, "WIDTH", opt_width);