/* global.c - Global notice service
* Copyright 2000-2004 srvx Development Team
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of srvx.
+ *
+ * srvx is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version. Important limitations are
- * listed in the COPYING file that accompanies this software.
+ * (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, email srvx-maintainers@srvx.net.
+ * along with srvx; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
#include "conf.h"
#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
#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++;
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;
if(duration)
{
- timeq_add(now + duration, message_expire, message);
+ timeq_add(now + duration, message_expire, message);
}
return 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;
{
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 *
{
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";
}
}
{
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);
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;
}
{
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);
+ }
}
}
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);
{
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);
if(!messageList)
{
- global_notice(user, "GMSG_NO_MESSAGES");
+ global_notice(user, "GMSG_NO_MESSAGES");
return 1;
}
table.contents[nn][0] = strdup(buffer);
table.contents[nn][1] = messageType(message);
if(message->duration)
- {
- intervalString(buffer, message->posted + message->duration - now);
- }
+ intervalString(buffer, message->posted + message->duration - now, user->handle_info);
else
- {
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);
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]);
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");
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
*/
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
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)
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;
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;
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;
}
if(nick)
{
- global = AddService(nick, "Global Services");
- global_service = service_register(global, 0);
+ 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);
reg_exit_func(global_db_cleanup);