fix possible crash on user deletion
[srvx.git] / src / global.c
index dc7dc4f0f381c73c3a9d3c842478f7cf3cbe7192..4f263e2dec2fb5688ba5a7df34cb69ddeed7e7ca 100644 (file)
 #include "saxdb.h"
 #include "timeq.h"
 
-#define GLOBAL_CONF_NAME       "services/global"
+#define GLOBAL_CONF_NAME    "services/global"
 
-#define GLOBAL_DB              "global.db"
-#define GLOBAL_TEMP_DB         "global.db.new"
+#define GLOBAL_DB           "global.db"
+#define GLOBAL_TEMP_DB      "global.db.new"
 
 /* Global options */
-#define KEY_DB_BACKUP_FREQ     "db_backup_freq"
-#define KEY_ANNOUNCEMENTS_DEFAULT "announcements_default"
-#define KEY_NICK               "nick"
+#define KEY_DB_BACKUP_FREQ  "db_backup_freq"
+#define KEY_NICK            "nick"
 
 /* Message data */
-#define KEY_FLAGS              "flags"
-#define KEY_POSTED             "posted"
-#define KEY_DURATION           "duration"
-#define KEY_FROM               "from"
-#define KEY_MESSAGE            "message"
+#define KEY_FLAGS       "flags"
+#define KEY_POSTED      "posted"
+#define KEY_DURATION    "duration"
+#define KEY_FROM        "from"
+#define KEY_MESSAGE     "message"
 
 /* Clarification: Notices are immediate, they are sent to matching users
    _once_, then forgotten. Messages are stored in Global's database and
@@ -65,35 +64,54 @@ static const struct message_entry msgtab[] = {
 #define GLOBAL_SYNTAX()   svccmd_send_help(user, global, cmd)
 #define GLOBAL_FUNC(NAME) MODCMD_FUNC(NAME)
 
+struct globalMessage
+{
+    unsigned long   id;
+    long            flags;
+
+    unsigned long   posted;
+    char            posted_s[24];
+    unsigned long   duration;
+
+    char            *from;
+    char            *message;
+
+    struct globalMessage    *prev;
+    struct globalMessage    *next;
+};
+
 struct userNode *global;
 
 static struct module *global_module;
 static struct service *global_service;
 static struct globalMessage *messageList;
 static long messageCount;
-static time_t last_max_alert;
+static unsigned long last_max_alert;
 static struct log_type *G_LOG;
 
 static struct
 {
     unsigned long db_backup_frequency;
-    unsigned int announcements_default : 1;
 } global_conf;
 
-#define global_notice(target, format...) send_message(target , global , ## format)
+#if defined(GCC_VARMACROS)
+# define global_notice(target, ARGS...) send_message(target, global, ARGS)
+#elif defined(C99_VARMACROS)
+# define global_notice(target, ...) send_message(target, global, __VA_ARGS__)
+#endif
 
 void message_expire(void *data);
 
 static struct globalMessage*
-message_add(long flags, time_t posted, unsigned long duration, char *from, const char *msg)
+message_add(long flags, unsigned long posted, unsigned long duration, char *from, const char *msg)
 {
     struct globalMessage *message;
+    time_t feh;
 
     message = malloc(sizeof(struct globalMessage));
-
     if(!message)
     {
-       return NULL;
+        return NULL;
     }
 
     message->id = messageCount++;
@@ -103,11 +121,16 @@ message_add(long flags, time_t posted, unsigned long duration, char *from, const
     message->from = strdup(from);
     message->message = strdup(msg);
 
+    if ((flags & MESSAGE_OPTION_IMMEDIATE) == 0) {
+        feh = message->posted;
+        strftime(message->posted_s, sizeof(message->posted_s),
+                 "%I:%M %p, %m/%d/%Y", localtime(&feh));
+    }
+
     if(messageList)
     {
-       messageList->prev = message;
+        messageList->prev = message;
     }
-
     message->prev = NULL;
     message->next = messageList;
 
@@ -115,7 +138,7 @@ message_add(long flags, time_t posted, unsigned long duration, char *from, const
 
     if(duration)
     {
-       timeq_add(now + duration, message_expire, message);
+        timeq_add(now + duration, message_expire, message);
     }
 
     return message;
@@ -126,7 +149,7 @@ message_del(struct globalMessage *message)
 {
     if(message->duration)
     {
-       timeq_del(0, NULL, message, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
+        timeq_del(0, NULL, message, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
     }
 
     if(message->prev) message->prev->next = message->next;
@@ -152,65 +175,68 @@ message_create(struct userNode *user, unsigned int argc, char *argv[])
 {
     unsigned long duration = 0;
     char *text = NULL;
+    char *sender;
     long flags = 0;
     unsigned int i;
 
+    sender = user->handle_info->handle;
+
     for(i = 0; i < argc; i++)
     {
-       if((i + 1) > argc)
-       {
-           global_notice(user, "MSG_MISSING_PARAMS", argv[argc]);
-           return NULL;
-       }
-
-       if(!irccasecmp(argv[i], "text"))
-       {
-           i++;
-           text = unsplit_string(argv + i, argc - i, NULL);
-           break;
-       } else if (!irccasecmp(argv[i], "sourceless")) {
-           i++;
-           flags |= MESSAGE_OPTION_SOURCELESS;
-       } else if (!irccasecmp(argv[i], "target")) {
-           i++;
-
-           if(!irccasecmp(argv[i], "all")) {
-               flags |= MESSAGE_RECIPIENT_ALL;
-           } else if(!irccasecmp(argv[i], "users")) {
-               flags |= MESSAGE_RECIPIENT_LUSERS;
-           } else if(!irccasecmp(argv[i], "helpers")) {
-               flags |= MESSAGE_RECIPIENT_HELPERS;
-           } else if(!irccasecmp(argv[i], "opers")) {
-               flags |= MESSAGE_RECIPIENT_OPERS;
-           } else if(!irccasecmp(argv[i], "staff") || !irccasecmp(argv[i], "privileged")) {
-               flags |= MESSAGE_RECIPIENT_STAFF;
-           } else if(!irccasecmp(argv[i], "channels")) {
-               flags |= MESSAGE_RECIPIENT_CHANNELS;
-            } else if(!irccasecmp(argv[i], "announcement") || !irccasecmp(argv[i], "announce")) {
-                flags |= MESSAGE_RECIPIENT_ANNOUNCE;
-           } else {
-               global_notice(user, "GMSG_INVALID_TARGET", argv[i]);
-               return NULL;
-           }
-       } else if (irccasecmp(argv[i], "duration") == 0) {
-           duration = ParseInterval(argv[++i]);
-       } else {
-           global_notice(user, "MSG_INVALID_CRITERIA", argv[i]);
-           return NULL;
-       }
+        if((i + 1) > argc)
+        {
+            global_notice(user, "MSG_MISSING_PARAMS", argv[argc]);
+            return NULL;
+        }
+
+        if(!irccasecmp(argv[i], "text"))
+        {
+            i++;
+            text = unsplit_string(argv + i, argc - i, NULL);
+            break;
+        } else if (!irccasecmp(argv[i], "sourceless")) {
+            i++;
+            flags |= MESSAGE_OPTION_SOURCELESS;
+        } else if (!irccasecmp(argv[i], "target")) {
+            i++;
+
+            if(!irccasecmp(argv[i], "all")) {
+                flags |= MESSAGE_RECIPIENT_ALL;
+            } else if(!irccasecmp(argv[i], "users")) {
+                flags |= MESSAGE_RECIPIENT_LUSERS;
+            } else if(!irccasecmp(argv[i], "helpers")) {
+                flags |= MESSAGE_RECIPIENT_HELPERS;
+            } else if(!irccasecmp(argv[i], "opers")) {
+                flags |= MESSAGE_RECIPIENT_OPERS;
+            } else if(!irccasecmp(argv[i], "staff") || !irccasecmp(argv[i], "privileged")) {
+                flags |= MESSAGE_RECIPIENT_STAFF;
+            } else if(!irccasecmp(argv[i], "channels")) {
+                flags |= MESSAGE_RECIPIENT_CHANNELS;
+            } else {
+                global_notice(user, "GMSG_INVALID_TARGET", argv[i]);
+                return NULL;
+            }
+        } else if (irccasecmp(argv[i], "duration") == 0) {
+            duration = ParseInterval(argv[++i]);
+        } else if (irccasecmp(argv[i], "from") == 0) {
+            sender = argv[++i];
+        } else {
+            global_notice(user, "MSG_INVALID_CRITERIA", argv[i]);
+            return NULL;
+        }
     }
 
     if(!flags)
     {
-       flags = MESSAGE_RECIPIENT_LUSERS;
+        flags = MESSAGE_RECIPIENT_LUSERS;
     }
 
     if(!text) {
-       global_notice(user, "GMSG_MESSAGE_REQUIRED");
-       return NULL;
+        global_notice(user, "GMSG_MESSAGE_REQUIRED");
+        return NULL;
     }
 
-    return message_add(flags, now, duration, user->handle_info->handle, text);
+    return message_add(flags, now, duration, sender, text);
 }
 
 static const char *
@@ -218,31 +244,27 @@ messageType(const struct globalMessage *message)
 {
     if((message->flags & MESSAGE_RECIPIENT_ALL) == MESSAGE_RECIPIENT_ALL)
     {
-       return "all";
+        return "all";
     }
     if((message->flags & MESSAGE_RECIPIENT_STAFF) == MESSAGE_RECIPIENT_STAFF)
     {
-       return "staff";
-    }
-    else if(message->flags & MESSAGE_RECIPIENT_ANNOUNCE)
-    {
-        return "announcement";
+        return "staff";
     }
     else if(message->flags & MESSAGE_RECIPIENT_OPERS)
     {
-       return "opers";
+        return "opers";
     }
     else if(message->flags & MESSAGE_RECIPIENT_HELPERS)
     {
-       return "helpers";
+        return "helpers";
     }
     else if(message->flags & MESSAGE_RECIPIENT_LUSERS)
     {
-       return "users";
+        return "users";
     }
     else
     {
-       return "channels";
+        return "channels";
     }
 }
 
@@ -251,19 +273,14 @@ notice_target(const char *target, struct globalMessage *message)
 {
     if(!(message->flags & MESSAGE_OPTION_SOURCELESS))
     {
-       if(message->flags & MESSAGE_OPTION_IMMEDIATE)
-       {
-           send_target_message(0, target, global, "GMSG_NOTICE_SOURCE", messageType(message), message->from);
-       }
-       else
-       {
-           char posted[24];
-           struct tm tm;
-
-           localtime_r(&message->posted, &tm);
-           strftime(posted, sizeof(posted), "%I:%M %p, %m/%d/%Y", &tm);
-           send_target_message(0, target, global, "GMSG_MESSAGE_SOURCE", messageType(message), message->from, posted);
-       }
+        if(message->flags & MESSAGE_OPTION_IMMEDIATE)
+        {
+            send_target_message(0, target, global, "GMSG_NOTICE_SOURCE", messageType(message), message->from);
+        }
+        else
+        {
+            send_target_message(0, target, global, "GMSG_MESSAGE_SOURCE", messageType(message), message->from, message->posted_s);
+        }
     }
 
     send_target_message(4, target, global, "%s", message->message);
@@ -275,7 +292,7 @@ notice_channel(const char *key, void *data, void *extra)
     struct chanNode *channel = data;
     /* It should be safe to assume channel is not NULL. */
     if(channel->channel_info)
-       notice_target(key, extra);
+         notice_target(key, extra);
     return 0;
 }
 
@@ -284,55 +301,40 @@ message_send(struct globalMessage *message)
 {
     struct userNode *user;
     unsigned long n;
-    dict_iterator_t it;
 
     if(message->flags & MESSAGE_RECIPIENT_CHANNELS)
     {
-       dict_foreach(channels, notice_channel, message);
+        dict_foreach(channels, notice_channel, message);
     }
 
     if(message->flags & MESSAGE_RECIPIENT_LUSERS)
     {
-       notice_target("$*", message);
-       return;
-    }
-
-    if(message->flags & MESSAGE_RECIPIENT_ANNOUNCE)
-    {
-        char announce;
-
-        for (it = dict_first(clients); it; it = iter_next(it)) {
-            user = iter_data(it);
-            if (user->uplink == self) continue;
-            announce = user->handle_info ? user->handle_info->announcements : '?';
-            if (announce == 'n') continue;
-            if ((announce == '?') && !global_conf.announcements_default) continue;
-            notice_target(user->nick, message);
-        }
+        notice_target("$*", message);
+        return;
     }
 
     if(message->flags & MESSAGE_RECIPIENT_OPERS)
     {
-       for(n = 0; n < curr_opers.used; n++)
-       {
-           user = curr_opers.list[n];
-
-           if(user->uplink != self)
-           {
-               notice_target(user->nick, message);
-           }
-       }
+        for(n = 0; n < curr_opers.used; n++)
+        {
+            user = curr_opers.list[n];
+
+            if(user->uplink != self)
+            {
+                notice_target(user->nick, message);
+            }
+        }
     }
 
     if(message->flags & MESSAGE_RECIPIENT_HELPERS)
     {
-       for(n = 0; n < curr_helpers.used; n++)
-       {
-           user = curr_helpers.list[n];
+        for(n = 0; n < curr_helpers.used; n++)
+        {
+            user = curr_helpers.list[n];
             if (IsOper(user))
                 continue;
-           notice_target(user->nick, message);
-       }
+            notice_target(user->nick, message);
+        }
     }
 }
 
@@ -342,11 +344,11 @@ global_message(long targets, char *text)
     struct globalMessage *message;
 
     if(!targets || !global)
-       return;
+        return;
 
     message = message_add(targets | MESSAGE_OPTION_SOURCELESS, now, 0, "", text);
     if(!message)
-       return;
+        return;
 
     message_send(message);
     message_del(message);
@@ -356,38 +358,44 @@ static GLOBAL_FUNC(cmd_notice)
 {
     struct globalMessage *message = NULL;
     const char *recipient = NULL, *text;
+    char *sender;
     long target = 0;
 
     assert(argc >= 3);
+    sender = user->handle_info->handle;
     if(!irccasecmp(argv[1], "all")) {
-       target = MESSAGE_RECIPIENT_ALL;
+        target = MESSAGE_RECIPIENT_ALL;
     } else if(!irccasecmp(argv[1], "users")) {
-       target = MESSAGE_RECIPIENT_LUSERS;
+        target = MESSAGE_RECIPIENT_LUSERS;
     } else if(!irccasecmp(argv[1], "helpers")) {
-       target = MESSAGE_RECIPIENT_HELPERS;
+        target = MESSAGE_RECIPIENT_HELPERS;
     } else if(!irccasecmp(argv[1], "opers")) {
-       target = MESSAGE_RECIPIENT_OPERS;
+        target = MESSAGE_RECIPIENT_OPERS;
     } else if(!irccasecmp(argv[1], "staff") || !irccasecmp(argv[1], "privileged")) {
-       target |= MESSAGE_RECIPIENT_HELPERS | MESSAGE_RECIPIENT_OPERS;
-    } else if(!irccasecmp(argv[1], "announcement") || !irccasecmp(argv[1], "announce")) {
-        target |= MESSAGE_RECIPIENT_ANNOUNCE;
+        target |= MESSAGE_RECIPIENT_HELPERS | MESSAGE_RECIPIENT_OPERS;
     } else if(!irccasecmp(argv[1], "channels")) {
-       target = MESSAGE_RECIPIENT_CHANNELS;
+        target = MESSAGE_RECIPIENT_CHANNELS;
     } else {
-       global_notice(user, "GMSG_INVALID_TARGET", argv[1]);
-       return 0;
+        global_notice(user, "GMSG_INVALID_TARGET", argv[1]);
+        return 0;
+    }
+    if(!irccasecmp(argv[2], "from")) {
+        if (argc < 5) {
+            reply("MSG_MISSING_PARAMS", argv[0]);
+            GLOBAL_SYNTAX();
+            return 0;
+        }
+        sender = argv[3];
+        text = unsplit_string(argv + 4, argc - 4, NULL);
+    } else {
+        text = unsplit_string(argv + 2, argc - 2, NULL);
     }
 
-    text = unsplit_string(argv + 2, argc - 2, NULL);
-    message = message_add(target | MESSAGE_OPTION_IMMEDIATE, now, 0, user->handle_info->handle, text);
-
+    message = message_add(target | MESSAGE_OPTION_IMMEDIATE, now, 0, sender, text);
     if(!message)
-    {
-       return 0;
-    }
+        return 0;
 
     recipient = messageType(message);
-
     message_send(message);
     message_del(message);
 
@@ -417,7 +425,7 @@ static GLOBAL_FUNC(cmd_list)
 
     if(!messageList)
     {
-       global_notice(user, "GMSG_NO_MESSAGES");
+        global_notice(user, "GMSG_NO_MESSAGES");
         return 1;
     }
 
@@ -447,13 +455,13 @@ static GLOBAL_FUNC(cmd_list)
             strcpy(buffer, "Never.");
         table.contents[nn][2] = strdup(buffer);
         table.contents[nn][3] = message->from;
-       length = strlen(message->message);
-       safestrncpy(buffer, message->message, sizeof(buffer));
-       if(length > (sizeof(buffer) - 4))
-       {
-           buffer[sizeof(buffer) - 1] = 0;
-           buffer[sizeof(buffer) - 2] = buffer[sizeof(buffer) - 3] = buffer[sizeof(buffer) - 4] = '.';
-       }
+        length = strlen(message->message);
+        safestrncpy(buffer, message->message, sizeof(buffer));
+        if(length > (sizeof(buffer) - 4))
+        {
+            buffer[sizeof(buffer) - 1] = 0;
+            buffer[sizeof(buffer) - 2] = buffer[sizeof(buffer) - 3] = buffer[sizeof(buffer) - 4] = '.';
+        }
         table.contents[nn][4] = strdup(buffer);
     }
     table_send(global, user->nick, 0, NULL, table);
@@ -480,12 +488,12 @@ static GLOBAL_FUNC(cmd_remove)
 
     for(message = messageList; message; message = message->next)
     {
-       if(message->id == id)
-       {
-           message_del(message);
-           global_notice(user, "GMSG_MESSAGE_DELETED", argv[1]);
-           return 1;
-       }
+        if(message->id == id)
+        {
+            message_del(message);
+            global_notice(user, "GMSG_MESSAGE_DELETED", argv[1]);
+            return 1;
+        }
     }
 
     global_notice(user, "GMSG_ID_INVALID", argv[1]);
@@ -500,15 +508,15 @@ send_messages(struct userNode *user, long mask, int obstreperize)
 
     while(message)
     {
-       if(message->flags & mask)
-       {
+        if(message->flags & mask)
+        {
             if (obstreperize && !count)
                 send_target_message(0, user->nick, global, "GMSG_MOTD_HEADER");
-           notice_target(user->nick, message);
-           count++;
-       }
+            notice_target(user->nick, message);
+            count++;
+        }
 
-       message = message->next;
+        message = message->next;
     }
     if (obstreperize && count)
         send_target_message(0, user->nick, global, "GMSG_MOTD_FOOTER");
@@ -521,25 +529,25 @@ static GLOBAL_FUNC(cmd_messages)
     unsigned int count;
 
     if(IsOper(user))
-       mask |= MESSAGE_RECIPIENT_OPERS;
+        mask |= MESSAGE_RECIPIENT_OPERS;
 
     if(IsHelper(user))
-       mask |= MESSAGE_RECIPIENT_HELPERS;
+        mask |= MESSAGE_RECIPIENT_HELPERS;
 
     count = send_messages(user, mask, 0);
     if(count)
-       global_notice(user, "GMSG_MESSAGE_COUNT", count);
+        global_notice(user, "GMSG_MESSAGE_COUNT", count);
     else
-       global_notice(user, "GMSG_NO_MESSAGES");
+        global_notice(user, "GMSG_NO_MESSAGES");
 
     return 1;
 }
 
-static int
+static void
 global_process_user(struct userNode *user)
 {
     if(IsLocal(user) || self->uplink->burst || user->uplink->burst)
-        return 0;
+        return;
     send_messages(user, MESSAGE_RECIPIENT_LUSERS, 1);
 
     /* only alert on new usercount if the record was broken in the last
@@ -547,21 +555,19 @@ global_process_user(struct userNode *user)
      */
     if((now - max_clients_time) <= 30 && (now - last_max_alert) > 30)
     {
-       char *message;
-       message = alloca(36);
-       sprintf(message, "New user count record: %d", max_clients);
-       global_message(MESSAGE_RECIPIENT_OPERS, message);
-       last_max_alert = now;
+        char *message;
+        message = alloca(36);
+        sprintf(message, "New user count record: %d", max_clients);
+        global_message(MESSAGE_RECIPIENT_OPERS, message);
+        last_max_alert = now;
     }
-
-    return 0;
 }
 
 static void
 global_process_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
 {
     if(IsHelper(user))
-       send_messages(user, MESSAGE_RECIPIENT_HELPERS, 0);
+        send_messages(user, MESSAGE_RECIPIENT_HELPERS, 0);
 }
 
 static void
@@ -579,14 +585,12 @@ global_conf_read(void)
     const char *str;
 
     if (!(conf_node = conf_get_data(GLOBAL_CONF_NAME, RECDB_OBJECT))) {
-       log_module(G_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", GLOBAL_CONF_NAME);
-       return;
+        log_module(G_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", GLOBAL_CONF_NAME);
+        return;
     }
 
     str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
     global_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
-    str = database_get_data(conf_node, KEY_ANNOUNCEMENTS_DEFAULT, RECDB_QSTRING);
-    global_conf.announcements_default = str ? enabled_string(str) : 1;
 
     str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
     if(global && str)
@@ -597,7 +601,7 @@ static int
 global_saxdb_read(struct dict *db)
 {
     struct record_data *hir;
-    time_t posted;
+    unsigned long posted;
     long flags;
     unsigned long duration;
     char *str, *from, *message;
@@ -606,11 +610,11 @@ global_saxdb_read(struct dict *db)
     for(it=dict_first(db); it; it=iter_next(it))
     {
         hir = iter_data(it);
-       if(hir->type != RECDB_OBJECT)
-       {
-           log_module(G_LOG, LOG_WARNING, "Unexpected rectype %d for %s.", hir->type, iter_key(it));
+        if(hir->type != RECDB_OBJECT)
+        {
+            log_module(G_LOG, LOG_WARNING, "Unexpected rectype %d for %s.", hir->type, iter_key(it));
             continue;
-       }
+        }
 
         str = database_get_data(hir->d.object, KEY_FLAGS, RECDB_QSTRING);
         flags = str ? strtoul(str, NULL, 0) : 0;
@@ -624,7 +628,7 @@ global_saxdb_read(struct dict *db)
         from = database_get_data(hir->d.object, KEY_FROM, RECDB_QSTRING);
         message = database_get_data(hir->d.object, KEY_MESSAGE, RECDB_QSTRING);
 
-       message_add(flags, posted, duration, from, message);
+        message_add(flags, posted, duration, from, message);
     }
     return 0;
 }
@@ -674,7 +678,8 @@ init_global(const char *nick)
 
     if(nick)
     {
-        global = AddService(nick, "Global Services", NULL);
+        const char *modes = conf_get_data("services/global/modes", RECDB_QSTRING);
+        global = AddLocalUser(nick, nick, NULL, "Global Services", modes);
         global_service = service_register(global);
     }
     saxdb_register("Global", global_saxdb_read, global_saxdb_write);