fix possible crash on user deletion
[srvx.git] / src / mod-memoserv.c
index 1fc9b1fb4fa94e47f20a0aaef1decffd28482804..78c7887e446827ba5ad20d9cb0f4b294daf27fcf 100644 (file)
 #define KEY_RECIPIENT "to"
 #define KEY_FROM "from"
 #define KEY_MESSAGE "msg"
+#undef KEY_READ /* thanks microsoft! */
 #define KEY_READ "read"
+#define KEY_ACCOUNTS "accounts"
+#define KEY_MESSAGES "messages"
 
 static const struct message_entry msgtab[] = {
+    { "MSMSG_CANNOT_SEND_SELF", "You cannot send to yourself." },
     { "MSMSG_CANNOT_SEND", "You cannot send to account $b%s$b." },
     { "MSMSG_MEMO_SENT", "Message sent to $b%s$b." },
     { "MSMSG_NO_MESSAGES", "You have no messages." },
@@ -74,7 +78,7 @@ static const struct message_entry msgtab[] = {
     { "MSMSG_EXPIRY", "Messages will be expired when they are %s old (%d seconds)." },
     { "MSMSG_MESSAGES_EXPIRED", "$b%lu$b message(s) expired." },
     { "MSMSG_MEMOS_INBOX", "You have $b%d$b new message(s) in your inbox and %d old messages.  Use /msg $S LIST to list them." },
-    { "MSMSG_NEW_MESSAGE", "You have a new message from $b%s$b." },
+    { "MSMSG_NEW_MESSAGE", "You have a new message from $b%s$b (ID $b%d$b)." },
     { "MSMSG_DELETED_ALL", "Deleted all of your messages." },
     { "MSMSG_USE_CONFIRM", "Please use /msg $S DELETE * $bCONFIRM$b to delete $uall$u of your messages." },
     { "MSMSG_STATUS_TOTAL", "I have $b%u$b memos in my database." },
@@ -90,12 +94,12 @@ struct memo {
     struct memo_account *recipient;
     struct memo_account *sender;
     char *message;
-    time_t sent;
+    unsigned long sent;
     unsigned int is_read : 1;
 };
 
 DECLARE_LIST(memoList, struct memo*);
-DEFINE_LIST(memoList, struct memo*);
+DEFINE_LIST(memoList, struct memo*)
 
 /* memo_account.flags fields */
 #define MEMO_NOTIFY_NEW   1
@@ -111,13 +115,15 @@ struct memo_account {
 
 static struct {
     struct userNode *bot;
-    int message_expiry;
+    unsigned long message_expiry;
 } memoserv_conf;
 
 const char *memoserv_module_deps[] = { NULL };
 static struct module *memoserv_module;
 static struct log_type *MS_LOG;
-static unsigned long memosSent, memosExpired;
+static unsigned long memoCount;
+static unsigned long memosSent;
+static unsigned long memosExpired;
 static struct dict *memos; /* memo_account->handle->handle -> memo_account */
 
 static struct memo_account *
@@ -145,6 +151,7 @@ delete_memo(struct memo *memo)
     memoList_remove(&memo->sender->sent, memo);
     free(memo->message);
     free(memo);
+    memoCount--;
 }
 
 static void
@@ -166,10 +173,10 @@ do_expire(void)
 {
     dict_iterator_t it;
     for (it = dict_first(memos); it; it = iter_next(it)) {
-        struct memo_account *acct = iter_data(it);
+        struct memo_account *account = iter_data(it);
         unsigned int ii;
-        for (ii = 0; ii < acct->sent.used; ++ii) {
-            struct memo *memo = acct->sent.list[ii];
+        for (ii = 0; ii < account->sent.used; ++ii) {
+            struct memo *memo = account->sent.list[ii];
             if ((now - memo->sent) > memoserv_conf.message_expiry) {
                 delete_memo(memo);
                 memosExpired++;
@@ -189,7 +196,7 @@ expire_memos(UNUSED_ARG(void *data))
 }
 
 static struct memo*
-add_memo(time_t sent, struct memo_account *recipient, struct memo_account *sender, char *message)
+add_memo(unsigned long sent, struct memo_account *recipient, struct memo_account *sender, char *message)
 {
     struct memo *memo;
 
@@ -204,23 +211,29 @@ add_memo(time_t sent, struct memo_account *recipient, struct memo_account *sende
     memo->sent = sent;
     memo->message = strdup(message);
     memosSent++;
+    memoCount++;
     return memo;
 }
 
 static int
-memoserv_can_send(struct userNode *bot, struct userNode *user, struct memo_account *acct)
+memoserv_can_send(struct userNode *bot, struct userNode *user, struct memo_account *account)
 {
     extern struct userData *_GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended);
     struct userData *dest;
 
     if (!user->handle_info)
         return 0;
-    if (!(acct->flags & MEMO_DENY_NONCHANNEL))
+    if (user->handle_info == account->handle) {
+        send_message(user, bot, "MSMSG_CANNOT_SEND_SELF");
+        return 0;
+    }
+    if (!(account->flags & MEMO_DENY_NONCHANNEL))
         return 1;
-    for (dest = acct->handle->channels; dest; dest = dest->u_next)
-        if (_GetChannelUser(dest->channel, user->handle_info, 1, 0))
+    for (dest = account->handle->channels; dest; dest = dest->u_next) {
+        if (dest->seen && _GetChannelUser(dest->channel, user->handle_info, 1, 0))
             return 1;
-    send_message(user, bot, "MSMSG_CANNOT_SEND", acct->handle->handle);
+    }
+    send_message(user, bot, "MSMSG_CANNOT_SEND", account->handle->handle);
     return 0;
 }
 
@@ -262,7 +275,7 @@ static MODCMD_FUNC(cmd_send)
     if (ma->flags & MEMO_NOTIFY_NEW) {
         struct userNode *other;
         for (other = ma->handle->users; other; other = other->next_authed)
-            send_message(other, cmd->parent->bot, "MSMSG_NEW_MESSAGE", user->nick);
+            send_message(other, cmd->parent->bot, "MSMSG_NEW_MESSAGE", user->nick, ma->recvd.used - 1);
     }
     reply("MSMSG_MEMO_SENT", ma->handle->handle);
     return 1;
@@ -274,15 +287,15 @@ static MODCMD_FUNC(cmd_list)
     struct memo *memo;
     unsigned int ii;
     char posted[24];
-    struct tm tm;
+    time_t feh;
 
     if (!(ma = memoserv_get_account(user->handle_info)))
         return 0;
     reply("MSMSG_LIST_HEAD");
     for (ii = 0; (ii < ma->recvd.used) && (ii < 15); ++ii) {
         memo = ma->recvd.list[ii];
-        localtime_r(&memo->sent, &tm);
-        strftime(posted, sizeof(posted), "%I:%M %p, %m/%d/%Y", &tm);
+        feh = memo->sent;
+        strftime(posted, sizeof(posted), "%I:%M %p, %m/%d/%Y", localtime(&feh));
         reply("MSMSG_LIST_FORMAT", ii, memo->sender->handle->handle, posted);
     }
     if (ii == 0)
@@ -300,14 +313,14 @@ static MODCMD_FUNC(cmd_read)
     unsigned int memoid;
     struct memo *memo;
     char posted[24];
-    struct tm tm;
+    time_t memo_sent;
 
     if (!(ma = memoserv_get_account(user->handle_info)))
         return 0;
     if (!(memo = find_memo(user, cmd, ma, argv[1], &memoid)))
         return 0;
-    localtime_r(&memo->sent, &tm);
-    strftime(posted, sizeof(posted), "%I:%M %p, %m/%d/%Y", &tm);
+    memo_sent = memo->sent;
+    strftime(posted, sizeof(posted), "%I:%M %p, %m/%d/%Y", localtime(&memo_sent));
     reply("MSMSG_MEMO_HEAD", memoid, memo->sender->handle->handle, posted);
     send_message_type(4, user, cmd->parent->bot, "%s", memo->message);
     memo->is_read = 1;
@@ -436,7 +449,7 @@ static MODCMD_FUNC(cmd_set_private)
 
 static MODCMD_FUNC(cmd_status)
 {
-    reply("MSMSG_STATUS_TOTAL", dict_size(memos));
+    reply("MSMSG_STATUS_TOTAL", memoCount);
     reply("MSMSG_STATUS_EXPIRED", memosExpired);
     reply("MSMSG_STATUS_SENT", memosSent);
     return 1;
@@ -459,14 +472,14 @@ memoserv_conf_read(void)
 }
 
 static int
-memoserv_saxdb_read(struct dict *db)
+memoserv_saxdb_read_messages(struct dict *db)
 {
     char *str;
     struct handle_info *sender, *recipient;
     struct record_data *hir;
     struct memo *memo;
     dict_iterator_t it;
-    time_t sent;
+    unsigned long sent;
 
     for (it = dict_first(db); it; it = iter_next(it)) {
         hir = iter_data(it);
@@ -479,7 +492,7 @@ memoserv_saxdb_read(struct dict *db)
             log_module(MS_LOG, LOG_ERROR, "Date sent not present in memo %s; skipping", iter_key(it));
             continue;
         }
-        sent = atoi(str);
+        sent = strtoul(str, NULL, 0);
 
         if (!(str = database_get_data(hir->d.object, KEY_RECIPIENT, RECDB_QSTRING))) {
             log_module(MS_LOG, LOG_ERROR, "Recipient not present in memo %s; skipping", iter_key(it));
@@ -509,20 +522,79 @@ memoserv_saxdb_read(struct dict *db)
     return 0;
 }
 
+static void
+memoserv_saxdb_read_accounts(struct dict *db)
+{
+    struct memo_account *ma;
+    struct handle_info *hi;
+    struct record_data *rd;
+    dict_iterator_t it;
+    const char *str;
+
+    for (it = dict_first(db); it; it = iter_next(it)) {
+        hi = get_handle_info(iter_key(it));
+        if (hi == NULL) {
+            log_module(MS_LOG, LOG_WARNING, "No account known for %s.", iter_key(it));
+            continue;
+        }
+
+        ma = memoserv_get_account(hi);
+        if (ma == NULL) {
+            log_module(MS_LOG, LOG_WARNING, "Unable to allocate memory for account %s.", iter_key(it));
+            continue;
+        }
+
+        rd = iter_data(it);
+        if (rd->type == RECDB_QSTRING) {
+            str = rd->d.qstring;
+        } else {
+            log_module(MS_LOG, LOG_WARNING, "Unexpected rectype %d for accounts/%s.", rd->type, iter_key(it));
+            continue;
+        }
+
+        if (str != NULL)
+            ma->flags = strtol(str, NULL, 0);
+    }
+}
+
+static int
+memoserv_saxdb_read(struct dict *db)
+{
+    struct dict *obj;
+
+    obj = database_get_data(db, KEY_ACCOUNTS, RECDB_OBJECT);
+    if (obj == NULL) {
+        return memoserv_saxdb_read_messages(db);
+    } else {
+        memoserv_saxdb_read_accounts(obj);
+        obj = database_get_data(db, KEY_MESSAGES, RECDB_OBJECT);
+        return memoserv_saxdb_read_messages(obj);
+    }
+}
+
 static int
 memoserv_saxdb_write(struct saxdb_context *ctx)
 {
     dict_iterator_t it;
     struct memo_account *ma;
     struct memo *memo;
-    char str[7];
+    char str[17];
     unsigned int id = 0, ii;
 
+    saxdb_start_record(ctx, "accounts", 1);
+    for (it = dict_first(memos); it; it = iter_next(it)) {
+        ma = iter_data(it);
+        saxdb_write_int(ctx, ma->handle->handle, ma->flags);
+    }
+    saxdb_end_record(ctx);
+
+    saxdb_start_record(ctx, "messages", 1);
     for (it = dict_first(memos); it; it = iter_next(it)) {
         ma = iter_data(it);
         for (ii = 0; ii < ma->recvd.used; ++ii) {
             memo = ma->recvd.list[ii];
-            saxdb_start_record(ctx, inttobase64(str, id++, sizeof(str)), 0);
+            snprintf(str, sizeof(str), "%x", id++);
+            saxdb_start_record(ctx, str, 0);
             saxdb_write_int(ctx, KEY_SENT, memo->sent);
             saxdb_write_string(ctx, KEY_RECIPIENT, memo->recipient->handle->handle);
             saxdb_write_string(ctx, KEY_FROM, memo->sender->handle->handle);
@@ -532,6 +604,8 @@ memoserv_saxdb_write(struct saxdb_context *ctx)
             saxdb_end_record(ctx);
         }
     }
+    saxdb_end_record(ctx);
+
     return 0;
 }