/* mod-helpserv.c - Support Helper assistant service
- * Copyright 2002-2003 srvx Development Team
+ * Copyright 2002-2003, 2006 srvx Development Team
*
* This file is part of srvx.
*
/* General */
#define HSFMT_TIME "%a, %d %b %Y %H:%M:%S %Z"
static const struct message_entry msgtab[] = {
- { "HSMSG_READHELP_SUCCESS", "Read HelpServ help database in "FMT_TIME_T".%03ld seconds." },
+ { "HSMSG_READHELP_SUCCESS", "Read HelpServ help database in %lu.%03lu seconds." },
{ "HSMSG_INVALID_BOT", "This command requires a valid HelpServ bot name." },
{ "HSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel; cannot use it." },
{ "HSMSG_INTERNAL_COMMAND", "$b%s$b appears to be an internal HelpServ command, sorry." },
char user_escape;
} helpserv_conf;
-static time_t last_stats_update;
+static unsigned long last_stats_update;
static int shutting_down;
static FILE *reqlog_f;
static struct log_type *HS_LOG;
unsigned int helpchan_empty : 1;
- time_t registered;
- time_t last_active;
+ unsigned long registered;
+ unsigned long last_active;
char *registrar;
};
unsigned int week_start : 3;
enum helpserv_level level;
/* statistics */
- time_t join_time; /* when they joined, or 0 if not in channel */
+ unsigned long join_time; /* when they joined, or 0 if not in channel */
/* [0] through [3] are n weeks ago, [4] is the total of everything before that */
unsigned int time_per_week[5]; /* how long they've were in the channel the past 4 weeks */
unsigned int picked_up[5]; /* how many requests they have picked up */
struct handle_info *handle;
unsigned long id;
- time_t opened;
- time_t assigned;
- time_t updated;
+ unsigned long opened;
+ unsigned long assigned;
+ unsigned long updated;
};
#define DEFINE_LIST_ALLOC(STRUCTNAME) \
}
DECLARE_LIST(helpserv_botlist, struct helpserv_bot *);
-DEFINE_LIST(helpserv_botlist, struct helpserv_bot *);
-DEFINE_LIST_ALLOC(helpserv_botlist);
+DEFINE_LIST(helpserv_botlist, struct helpserv_bot *)
+DEFINE_LIST_ALLOC(helpserv_botlist)
DECLARE_LIST(helpserv_reqlist, struct helpserv_request *);
-DEFINE_LIST(helpserv_reqlist, struct helpserv_request *);
-DEFINE_LIST_ALLOC(helpserv_reqlist);
+DEFINE_LIST(helpserv_reqlist, struct helpserv_request *)
+DEFINE_LIST_ALLOC(helpserv_reqlist)
DECLARE_LIST(helpserv_userlist, struct helpserv_user *);
-DEFINE_LIST(helpserv_userlist, struct helpserv_user *);
-DEFINE_LIST_ALLOC(helpserv_userlist);
+DEFINE_LIST(helpserv_userlist, struct helpserv_user *)
+DEFINE_LIST_ALLOC(helpserv_userlist)
struct helpfile *helpserv_helpfile;
static struct module *helpserv_module;
return 0; }
/* For messages going to users being helped */
-#define helpserv_msguser(target, format...) send_message_type((from_opserv ? 0 : hs->privmsg_only), (target), (from_opserv ? opserv : hs->helpserv) , ## format)
-#define helpserv_user_reply(format...) send_message_type(req->hs->privmsg_only, req->user, req->hs->helpserv , ## format)
+#if defined(GCC_VARMACROS)
+# define helpserv_msguser(target, ARGS...) send_message_type((from_opserv ? 0 : hs->privmsg_only), (target), (from_opserv ? opserv : hs->helpserv), ARGS)
+# define helpserv_user_reply(ARGS...) send_message_type(req->hs->privmsg_only, req->user, req->hs->helpserv, ARGS)
/* For messages going to helpers */
-#define helpserv_notice(target, format...) send_message((target), (from_opserv ? opserv : hs->helpserv) , ## format)
-#define helpserv_notify(helper, format...) do { struct userNode *_target; for (_target = (helper)->handle->users; _target; _target = _target->next_authed) { \
- send_message(_target, (helper)->hs->helpserv, ## format); \
+# define helpserv_notice(target, ARGS...) send_message((target), (from_opserv ? opserv : hs->helpserv), ARGS)
+# define helpserv_notify(helper, ARGS...) do { struct userNode *_target; for (_target = (helper)->handle->users; _target; _target = _target->next_authed) { \
+ send_message(_target, (helper)->hs->helpserv, ARGS); \
} } while (0)
+# define helpserv_page(TYPE, ARGS...) do { \
+ int msg_type=0; struct chanNode *target=helpserv_get_page_type(hs, (TYPE), &msg_type); \
+ if (target) send_target_message(msg_type, target->name, hs->helpserv, ARGS); \
+ } while (0)
+#elif defined(C99_VARMACROS)
+# define helpserv_msguser(target, ...) send_message_type((from_opserv ? 0 : hs->privmsg_only), (target), (from_opserv ? opserv : hs->helpserv), __VA_ARGS__)
+# define helpserv_user_reply(...) send_message_type(req->hs->privmsg_only, req->user, req->hs->helpserv, __VA_ARGS__)
+/* For messages going to helpers */
+# define helpserv_notice(target, ...) send_message((target), (from_opserv ? opserv : hs->helpserv), __VA_ARGS__)
+# define helpserv_notify(helper, ...) do { struct userNode *_target; for (_target = (helper)->handle->users; _target; _target = _target->next_authed) { \
+ send_message(_target, (helper)->hs->helpserv, __VA_ARGS__); \
+ } } while (0)
+# define helpserv_page(TYPE, ...) do { \
+ int msg_type=0; struct chanNode *target=helpserv_get_page_type(hs, (TYPE), &msg_type); \
+ if (target) send_target_message(msg_type, target->name, hs->helpserv, __VA_ARGS__); \
+ } while (0)
+#endif
#define helpserv_message(hs, target, id) do { if ((hs)->messages[id]) { \
if (from_opserv) \
send_message_type(4, (target), opserv, "%s", (hs)->messages[id]); \
else \
send_message_type(4 | hs->privmsg_only, (target), hs->helpserv, "%s", (hs)->messages[id]); \
} } while (0)
-#define helpserv_page(TYPE, FORMAT...) do { \
- struct chanNode *target=NULL; int msg_type=0; \
- target = hs->page_targets[TYPE]; \
- switch (hs->page_types[TYPE]) { \
- case PAGE_NOTICE: msg_type = 0; break; \
- case PAGE_PRIVMSG: msg_type = 1; break; \
- case PAGE_ONOTICE: msg_type = 2; break; \
- default: log_module(HS_LOG, LOG_ERROR, "helpserv_page() called but %s has an invalid page type %d.", hs->helpserv->nick, TYPE); \
- case PAGE_NONE: target = NULL; break; \
- } \
- if (target) send_target_message(msg_type, target->name, hs->helpserv, ## FORMAT); \
- } while (0)
#define helpserv_get_handle_info(user, text) smart_get_handle_info((from_opserv ? opserv : hs->helpserv) , (user), (text))
struct helpserv_cmd {
static void run_empty_interval(void *data);
-static void helpserv_interval(char *output, time_t interval) {
+static void helpserv_interval(char *output, unsigned long interval) {
int num_hours = interval / 3600;
int num_minutes = (interval % 3600) / 60;
sprintf(output, "%u hour%s, %u minute%s", num_hours, num_hours == 1 ? "" : "s", num_minutes, num_minutes == 1 ? "" : "s");
assert(reason != NULL);
if (!(ctx = saxdb_open_context(reqlog_f)))
return;
- sprintf(key, "%s-" FMT_TIME_T "-%lu", req->hs->helpserv->nick, req->opened, req->id);
+ sprintf(key, "%s-%lu-%lu", req->hs->helpserv->nick, (unsigned long)req->opened, req->id);
if ((res = setjmp(ctx->jbuf)) != 0) {
log_module(HS_LOG, LOG_ERROR, "Unable to log helpserv request: %s.", strerror(res));
} else {
}
}
+static struct chanNode *helpserv_get_page_type(struct helpserv_bot *hs, enum page_source type, int *msg_type)
+{
+ switch (hs->page_types[type]) {
+ case PAGE_NOTICE:
+ *msg_type = 0;
+ break;
+ case PAGE_PRIVMSG:
+ *msg_type = 1;
+ break;
+ case PAGE_ONOTICE:
+ *msg_type = 2;
+ break;
+ default:
+ log_module(HS_LOG, LOG_ERROR, "helpserv_page() called but %s has an invalid page type %d.", hs->helpserv->nick, type);
+ /* and fall through */
+ case PAGE_NONE:
+ return NULL;
+ }
+ return hs->page_targets[type];
+}
+
/* Searches for a request by number, nick, or account (num|nick|*account).
* As there can potentially be >1 match, it takes a reqlist. The return
* value is the "best" request found (explained in the comment block below).
req->user = user;
req->handle = user->handle_info;
if (from_join && self->burst) {
- extern time_t burst_begin;
+ extern unsigned long burst_begin;
/* We need to keep all the requests during a burst join together,
* even if the burst takes more than 1 second. ircu seems to burst
* in reverse-join order. */
else
send_message_type(4, user, hs->helpserv, "%s %s %s", lbuf[0], lbuf[1], lbuf[2]);
- if (hs->req_on_join && req == hs->unhandled && hs->helpchan_empty) {
+ if (hs->req_on_join && req == hs->unhandled && hs->helpchan_empty && !user->uplink->burst) {
timeq_del(0, run_empty_interval, hs, TIMEQ_IGNORE_WHEN);
run_empty_interval(hs);
}
}
/* Handle a message from a user to a HelpServ bot. */
-static void helpserv_usermsg(struct userNode *user, struct helpserv_bot *hs, char *text) {
+static void helpserv_usermsg(struct userNode *user, struct helpserv_bot *hs, const char *text) {
const int from_opserv = 0; /* for helpserv_notice */
struct helpserv_request *req=NULL, *newest=NULL;
struct helpserv_reqlist *reqlist, *hand_reqlist;
helpserv_msguser(user, "HSMSG_USERCMD_UNKNOWN", cmdname);
return;
} else if (hs->intervals[INTERVAL_STALE_DELAY]
- && (req->updated < (time_t)(now - hs->intervals[INTERVAL_STALE_DELAY]))
+ && (req->updated < now - hs->intervals[INTERVAL_STALE_DELAY])
&& (!hs->req_maxlen || req->text->used < hs->req_maxlen)) {
char buf[MAX_LINE_SIZE], updatestr[INTERVALLEN], timestr[MAX_LINE_SIZE];
+ time_t feh;
- strftime(timestr, MAX_LINE_SIZE, HSFMT_TIME, localtime(&req->opened));
+ feh = req->opened;
+ strftime(timestr, MAX_LINE_SIZE, HSFMT_TIME, localtime(&feh));
intervalString(updatestr, now - req->updated, user->handle_info);
if (req->helper && (hs->notify >= NOTIFY_USER))
if (user->handle_info)
helpserv_notify(req->helper, "HSMSG_PAGE_UPD_REQUEST_AUTHED", req->id, user->nick, user->handle_info->handle, timestr, updatestr);
else
- helpserv_notify(req->helper, "HSMSG_PAGE_UPD_REQUESTNOT_AUTHED", req->id, user->nick, timestr, updatestr);
+ helpserv_notify(req->helper, "HSMSG_PAGE_UPD_REQUEST_NOT_AUTHED", req->id, user->nick, timestr, updatestr);
else
if (user->handle_info)
helpserv_page(PGSRC_STATUS, "HSMSG_PAGE_UPD_REQUEST_AUTHED", req->id, user->nick, user->handle_info->handle, timestr, updatestr);
else
helpserv_page(PGSRC_STATUS, "HSMSG_PAGE_UPD_REQUEST_NOT_AUTHED", req->id, user->nick, timestr, updatestr);
- strftime(timestr, MAX_LINE_SIZE, HSFMT_TIME, localtime(&now));
+ feh = now;
+ strftime(timestr, MAX_LINE_SIZE, HSFMT_TIME, localtime(&feh));
snprintf(buf, MAX_LINE_SIZE, "[Stale request updated at %s]", timestr);
string_list_append(req->text, strdup(buf));
}
}
/* Handle messages direct to a HelpServ bot. */
-static void helpserv_botmsg(struct userNode *user, struct userNode *target, char *text, UNUSED_ARG(int server_qualified)) {
+static void helpserv_botmsg(struct userNode *user, struct userNode *target, const char *text, UNUSED_ARG(int server_qualified)) {
struct helpserv_bot *hs;
struct helpserv_cmd *cmd;
struct helpserv_user *hs_user;
char *argv[MAXNUMPARAMS];
+ char tmpline[MAXLEN];
int argc, argv_shift;
const int from_opserv = 0; /* for helpserv_notice */
}
argv_shift = 1;
- argc = split_line(text, false, ArrayLength(argv)-argv_shift, argv+argv_shift);
+ safestrncpy(tmpline, text, sizeof(tmpline));
+ argc = split_line(tmpline, false, ArrayLength(argv)-argv_shift, argv+argv_shift);
if (!argc)
return;
if (!cmd->func) {
helpserv_notice(user, "HSMSG_INTERNAL_COMMAND", argv[argv_shift]);
} else if (cmd->func(user, hs, 0, argc, argv+argv_shift)) {
- unsplit_string(argv+argv_shift, argc, text);
- log_audit(HS_LOG, LOG_COMMAND, user, hs->helpserv, hs->helpchan->name, 0, text);
+ unsplit_string(argv+argv_shift, argc, tmpline);
+ log_audit(HS_LOG, LOG_COMMAND, user, hs->helpserv, hs->helpchan->name, 0, tmpline);
}
}
return;
}
- for (other = req->hs->unhandled, pos = -1, count = 0;
+ for (other = req->hs->unhandled, pos = -1, count = 0;
other;
other = other->next_unhandled, ++count) {
if (other == req)
stop.tv_sec -= 1;
stop.tv_usec += 1000000;
}
- helpserv_notice(user, "HSMSG_READHELP_SUCCESS", stop.tv_sec, stop.tv_usec/1000);
+ helpserv_notice(user, "HSMSG_READHELP_SUCCESS", (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
return 1;
}
unsigned int nn;
char buf[MAX_LINE_SIZE];
char buf2[INTERVALLEN];
+ time_t feh;
if (req->user)
if (req->handle)
helpserv_notice(user, "HSMSG_REQ_INFO_2d", req->handle->handle);
else
helpserv_notice(user, "HSMSG_REQ_INFO_2e");
- strftime(buf, MAX_LINE_SIZE, HSFMT_TIME, localtime(&req->opened));
+ feh = req->opened;
+ strftime(buf, MAX_LINE_SIZE, HSFMT_TIME, localtime(&feh));
intervalString(buf2, now - req->opened, user->handle_info);
helpserv_notice(user, "HSMSG_REQ_INFO_3", buf, buf2);
helpserv_notice(user, "HSMSG_REQ_INFO_4");
REQUIRE_PARMS(2);
- assert(hs_user);
-
if (!(req = smart_get_request(hs, hs_user, argv[1], &num_requests))) {
helpserv_notice(user, "HSMSG_REQ_INVALID", argv[1]);
return 0;
struct helpserv_request *req;
struct helpserv_user *hs_user=GetHSUser(hs, user->handle_info);
int num_requests=0;
+ time_t feh;
REQUIRE_PARMS(3);
- assert(hs_user);
-
if (!(req = smart_get_request(hs, hs_user, argv[1], &num_requests))) {
helpserv_notice(user, "HSMSG_REQ_INVALID", argv[1]);
return 0;
note = unsplit_string(argv+2, argc-2, NULL);
- strftime(timestr, MAX_LINE_SIZE, HSFMT_TIME, localtime(&now));
+ feh = now;
+ strftime(timestr, MAX_LINE_SIZE, HSFMT_TIME, localtime(&feh));
snprintf(text, MAX_LINE_SIZE, "[Helper note at %s]:", timestr);
string_list_append(req->text, strdup(text));
snprintf(text, MAX_LINE_SIZE, " <%s> %s", user->handle_info->handle, note);
struct helpserv_user *owner=NULL;
bot = iter_data(it);
-
+
for (it2=dict_first(bot->users); it2; it2=iter_next(it2)) {
if (((struct helpserv_user *)iter_data(it2))->level == HlOwner) {
owner = iter_data(it2);
for (unh = hs->unhandled; unh; unh = unh->next_unhandled) {
queuesize++;
- if ((now - unh->opened) >= (time_t)hs->intervals[INTERVAL_WHINE_DELAY]) {
+ if ((now - unh->opened) >= hs->intervals[INTERVAL_WHINE_DELAY]) {
helpserv_reqlist_append(&reqlist, unh);
}
}
if (reqlist.used) {
char strwhinedelay[INTERVALLEN];
- intervalString(strwhinedelay, (time_t)hs->intervals[INTERVAL_WHINE_DELAY], NULL);
+ intervalString(strwhinedelay, hs->intervals[INTERVAL_WHINE_DELAY], NULL);
#if ANNOYING_ALERT_PAGES
tbl.length = reqlist.used + 1;
tbl.width = 4;
tbl.contents[i][3] = strdup(idle_time);
}
- intervalString(stridledelay, (time_t)hs->intervals[INTERVAL_IDLE_DELAY], NULL);
+ intervalString(stridledelay, hs->intervals[INTERVAL_IDLE_DELAY], NULL);
helpserv_page(PGSRC_STATUS, "HSMSG_PAGE_IDLE_HEADER", mode_list.used, hs->helpchan->name, stridledelay);
table_send(hs->helpserv, hs->page_targets[PGSRC_STATUS]->name, 0, page_types[hs->page_types[PGSRC_STATUS]].func, tbl);
* it's a harmless default */
hs = calloc(1, sizeof(struct helpserv_bot));
- if (!(hs->helpserv = AddService(nick, "+iok", helpserv_conf.description, NULL))) {
+ if (!(hs->helpserv = AddLocalUser(nick, nick, NULL, helpserv_conf.description, NULL))) {
free(hs);
return NULL;
}
log_module(HS_LOG, LOG_ERROR, "Request %s:%s has a nonexistant opening time. Using time(NULL).", hs->helpserv->nick, key);
request->opened = time(NULL);
} else {
- request->opened = (time_t)strtoul(str, NULL, 0);
+ request->opened = strtoul(str, NULL, 0);
}
str = database_get_data(rd->d.object, KEY_REQUEST_ASSIGNED, RECDB_QSTRING);
if (str)
- request->assigned = (time_t)strtoul(str, NULL, 0);
+ request->assigned = strtoul(str, NULL, 0);
str = database_get_data(rd->d.object, KEY_REQUEST_HELPER, RECDB_QSTRING);
if (str) {
hs->notify = str ? notification_from_name(str) : NOTIFY_NONE;
str = database_get_data(GET_RECORD_OBJECT(br), KEY_REGISTERED, RECDB_QSTRING);
if (str)
- hs->registered = (time_t)strtol(str, NULL, 0);
+ hs->registered = strtol(str, NULL, 0);
str = database_get_data(GET_RECORD_OBJECT(br), KEY_IDWRAP, RECDB_QSTRING);
if (str)
hs->id_wrap = strtoul(str, NULL, 0);
str = database_get_data(GET_RECORD_OBJECT(br), KEY_AUTO_DEVOICE, RECDB_QSTRING);
hs->auto_devoice = str ? enabled_string(str) : 0;
str = database_get_data(GET_RECORD_OBJECT(br), KEY_LAST_ACTIVE, RECDB_QSTRING);
- hs->last_active = str ? atoi(str) : now;
+ hs->last_active = str ? strtoul(str, NULL, 0) : now;
dict_foreach(users, user_read_helper, hs);
}
str = database_get_data(conf_db, KEY_LAST_STATS_UPDATE, RECDB_QSTRING);
- last_stats_update = str ? (time_t)strtol(str, NULL, 0) : now;
+ last_stats_update = str ? strtoul(str, NULL, 0) : now;
return 0;
}
helpserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
str = database_get_data(conf_node, "description", RECDB_QSTRING);
- helpserv_conf.description = str;
+ helpserv_conf.description = str ? str : "Help Queue Manager";
str = database_get_data(conf_node, "reqlogfile", RECDB_QSTRING);
if (str && strlen(str))
}
}
}
-
+
if (mn->user->handle_info && (userlist = dict_find(helpserv_users_byhand_dict, mn->user->handle_info->handle, NULL))) {
for (i=0; i < userlist->used; i++) {
struct helpserv_user *hs_user = userlist->list[i];
struct helpserv_request *newest=NULL, *nicknewest=NULL;
unsigned int i;
const int from_opserv = 0; /* For helpserv_notice */
-
+
if (!(user->handle_info && (hand_reqlist = dict_find(helpserv_reqs_byhand_dict, user->handle_info->handle, NULL))) && !force_greet) {
return;
}
if (IsLocal(user))
return 0;
-
+
if (!(botlist = dict_find(helpserv_bots_bychan_dict, chan->name, NULL)))
return 0;
for (i=0; i < userlist->used; i++)
dict_insert(userlist->list[i]->hs->users, handle->handle, userlist->list[i]);
}
-
+
if (reqlist) {
for (i=0; i < reqlist->used; i++) {
struct helpserv_request *req=reqlist->list[i];
for (j=1; j <= helper_reqs.used; j++) {
struct helpserv_request *req=helper_reqs.list[j-1];
char reqid[12], timestr[MAX_LINE_SIZE];
+ time_t feh;
tbl.contents[j] = alloca(tbl.width * sizeof(**tbl.contents));
tbl.contents[j][0] = req->hs->helpserv->nick;
tbl.contents[j][1] = strdup(reqid);
tbl.contents[j][2] = req->user ? req->user->nick : "Not online";
tbl.contents[j][3] = req->handle ? req->handle->handle : "Not authed";
- strftime(timestr, MAX_LINE_SIZE, HSFMT_TIME, localtime(&req->opened));
+ feh = req->opened;
+ strftime(timestr, MAX_LINE_SIZE, HSFMT_TIME, localtime(&feh));
tbl.contents[j][4] = strdup(timestr);
}
}
}
-static time_t helpserv_next_stats(time_t after_when) {
+static unsigned long helpserv_next_stats(time_t after_when) {
struct tm *timeinfo = localtime(&after_when);
/* This works because mktime(3) says it will accept out-of-range values
}
/* If data != NULL, then don't add to the timeq */
-static void helpserv_run_stats(time_t when) {
- struct tm when_s;
+static void helpserv_run_stats(unsigned long when) {
struct helpserv_bot *hs;
struct helpserv_user *hs_user;
+ time_t feh;
+ unsigned int day;
int i;
dict_iterator_t it, it2;
last_stats_update = when;
- localtime_r(&when, &when_s);
+ feh = when;
+ day = localtime(&feh)->tm_wday;
for (it=dict_first(helpserv_bots_dict); it; it=iter_next(it)) {
hs = iter_data(it);
hs_user = iter_data(it2);
/* Skip the helper if it's not their week-start day. */
- if (hs_user->week_start != when_s.tm_wday)
+ if (hs_user->week_start != day)
continue;
/* Adjust their credit if they are in-channel at rollover. */
helpserv_bots_dict = dict_new();
dict_set_free_data(helpserv_bots_dict, helpserv_free_bot);
-
+
helpserv_bots_bychan_dict = dict_new();
dict_set_free_data(helpserv_bots_bychan_dict, helpserv_botlist_free);
/* Make up for downtime... though this will only really affect the
* time_per_week */
if (last_stats_update && (helpserv_next_stats(last_stats_update) < now)) {
- time_t statsrun = last_stats_update;
+ unsigned long statsrun = last_stats_update;
while ((statsrun = helpserv_next_stats(statsrun)) < now)
helpserv_run_stats(statsrun);
}