Avoid crashing when a user is killed by an on-auth callback.
[srvx.git] / src / nickserv.c
index 9ab83c80d7f67702153cd9100580ffc55a9d129e..0ba125e5bfd8658657383b308111f0ef880e2556 100644 (file)
@@ -1,5 +1,5 @@
 /* nickserv.c - Nick/authentication service
- * Copyright 2000-2006 srvx Development Team
+ * Copyright 2000-2008 srvx Development Team
  *
  * This file is part of srvx.
  *
@@ -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"
@@ -342,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;
@@ -378,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;
@@ -409,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
     {
@@ -437,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;
@@ -446,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;
 }
@@ -522,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);
@@ -917,11 +917,14 @@ set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
         /* remove from next_authed linked list */
         if (user->handle_info->users == user) {
             user->handle_info->users = user->next_authed;
-        } else {
+        } else if (user->handle_info->users != NULL) {
             for (other = user->handle_info->users;
                  other->next_authed != user;
                  other = other->next_authed) ;
             other->next_authed = user->next_authed;
+        } else {
+            /* No users authed to the account - can happen if they get
+             * killed for authing. */
         }
         /* if nobody left on old handle, and they're not an oper, remove !god */
         if (!user->handle_info->users && !user->handle_info->opserv_level)
@@ -935,8 +938,11 @@ set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
     user->handle_info = hi;
     if (hi && !hi->users && !hi->opserv_level)
         HANDLE_CLEAR_FLAG(hi, HELPING);
-    for (n=0; n<auth_func_used; n++)
+    for (n=0; n<auth_func_used; n++) {
         auth_func_list[n](user, old_info);
+        if (user->dead)
+            return;
+    }
     if (hi) {
         struct nick_info *ni;
 
@@ -956,26 +962,16 @@ set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
             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) {
-                    if (!irccasecmp(user->nick, ni->nick)) {
+                struct nick_info *ni2;
+                for (ni2 = hi->nicks; ni2; ni2 = ni2->next) {
+                    if (!irccasecmp(user->nick, ni2->nick)) {
                         user->modes |= FLAGS_REGNICK;
                         break;
                     }
                 }
             }
-            StampUser(user, id);
+            StampUser(user, hi->handle, hi->registered, hi->id);
         }
 
         if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
@@ -1331,9 +1327,6 @@ 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
     feh = hi->registered;
     reply("NSMSG_HANDLEINFO_REGGED", ctime(&feh));
 
@@ -1375,6 +1368,9 @@ static NICKSERV_FUNC(cmd_handleinfo)
         reply(type);
     }
 
+    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");
@@ -1468,20 +1464,20 @@ static NICKSERV_FUNC(cmd_handleinfo)
     }
 
     if (hi->channels) {
-        struct userData *channel, *next;
+        struct userData *chan, *next;
         char *name;
 
-        for (channel = hi->channels; channel; channel = next) {
-            next = channel->u_next;
-            name = channel->channel->channel->name;
+        for (chan = hi->channels; chan; chan = next) {
+            next = chan->u_next;
+            name = chan->channel->channel->name;
             herelen = strlen(name);
             if (pos + herelen + 7 > ArrayLength(buff)) {
-                next = channel;
+                next = chan;
                 goto print_chans_buff;
             }
-            if (IsUserSuspended(channel))
+            if (IsUserSuspended(chan))
                 buff[pos++] = '-';
-            pos += sprintf(buff+pos, "%d:%s ", channel->access, name);
+            pos += sprintf(buff+pos, "%d:%s ", chan->access, name);
             if (next == NULL) {
               print_chans_buff:
                 buff[pos-1] = 0;
@@ -2181,14 +2177,10 @@ nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *
         struct channelList *schannels;
         unsigned int ii;
         schannels = chanserv_support_channels();
-        for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
-            for (ii = 0; ii < schannels->used; ++ii)
-                if (GetUserMode(schannels->list[ii], uNode))
-                    break;
-            if (ii < schannels->used)
+        for (ii = 0; ii < schannels->used; ++ii)
+            if (find_handle_in_channel(schannels->list[ii], hi, NULL))
                 break;
-        }
-        if (!uNode)
+        if (ii == schannels->used)
             HANDLE_CLEAR_FLAG(hi, HELPING);
     }
 
@@ -2879,9 +2871,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;
@@ -2934,9 +2924,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])
@@ -3484,7 +3472,6 @@ static MODCMD_FUNC(cmd_checkemail)
 
     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)
@@ -3504,8 +3491,8 @@ 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;
+    struct userData *channel_list;
+    unsigned long id;
     unsigned int ii;
     dict_t subdb;
 
@@ -3518,13 +3505,13 @@ nickserv_db_read_handle(const char *handle, dict_t obj)
     }
     if ((hi = get_handle_info(handle))) {
         authed_users = hi->users;
-        channels = hi->channels;
+        channel_list = hi->channels;
         hi->users = NULL;
         hi->channels = NULL;
         dict_remove(nickserv_handle_dict, hi->handle);
     } else {
         authed_users = NULL;
-        channels = NULL;
+        channel_list = NULL;
     }
     hi = register_handle(handle, str, id);
     if (authed_users) {
@@ -3534,7 +3521,7 @@ nickserv_db_read_handle(const char *handle, dict_t obj)
             authed_users = authed_users->next_authed;
         }
     }
-    hi->channels = channels;
+    hi->channels = channel_list;
     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);
@@ -3637,13 +3624,13 @@ nickserv_db_read_handle(const char *handle, dict_t obj)
             const char *setter;
             const char *text;
             const char *set;
-            const char *id;
+            const char *note_id;
             dict_t notedb;
 
-            id = iter_key(it);
+            note_id = iter_key(it);
             notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
             if (!notedb) {
-                log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", id, hi->handle);
+                log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", note_id, hi->handle);
                 continue;
             }
             expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
@@ -3651,14 +3638,14 @@ nickserv_db_read_handle(const char *handle, dict_t obj)
             text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
             set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
             if (!setter || !text || !set) {
-                log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", id, hi->handle);
+                log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", note_id, hi->handle);
                 continue;
             }
             note = calloc(1, sizeof(*note) + strlen(text));
             note->next = NULL;
             note->expires = expires ? strtoul(expires, NULL, 10) : 0;
             note->set = strtoul(set, NULL, 10);
-            note->id = strtoul(id, NULL, 10);
+            note->id = strtoul(note_id, NULL, 10);
             safestrncpy(note->setter, setter, sizeof(note->setter));
             strcpy(note->note, text);
             if (last_note)
@@ -3742,8 +3729,7 @@ nickserv_load_dict(const char *fname)
         log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
         return;
     }
-    while (!feof(file)) {
-        fgets(line, sizeof(line), file);
+    while (fgets(line, sizeof(line), file)) {
         if (!line[0])
             continue;
         if (line[strlen(line)-1] == '\n')
@@ -3857,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;
@@ -3962,52 +3955,51 @@ nickserv_reclaim_p(void *data) {
         nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
 }
 
-static int
+static void
 check_user_nick(struct userNode *user) {
     struct nick_info *ni;
     user->modes &= ~FLAGS_REGNICK;
     if (!(ni = get_nick_info(user->nick)))
-        return 0;
+        return;
     if (user->handle_info == ni->owner) {
         user->modes |= FLAGS_REGNICK;
         irc_regnick(user);
-        return 0;
+        return;
     }
     if (nickserv_conf.warn_nick_owned)
         send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
     if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
-        return 0;
+        return;
     if (nickserv_conf.auto_reclaim_delay)
         timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
     else
         nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
-    return 0;
-}
-
-int
-handle_new_user(struct userNode *user)
-{
-    return check_user_nick(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);
     }
 }
 
@@ -4088,7 +4080,7 @@ init_nickserv(const char *nick)
 {
     unsigned int i;
     NS_LOG = log_register_type("NickServ", "file:nickserv.log");
-    reg_new_user_func(handle_new_user);
+    reg_new_user_func(check_user_nick);
     reg_nick_change_func(handle_nick_change);
     reg_del_user_func(nickserv_remove_user);
     reg_account_func(handle_account);