Add close_file arg to saxdb_close_context(); allocate all saxdb contexts from heap.
[srvx.git] / src / mod-helpserv.c
index 5bf57eb9326ad194f52b465d8121a087f7036ef8..e360db963379bcac14bb7b58b505aeeaa83c342b 100644 (file)
@@ -1,5 +1,5 @@
 /* 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.
  *
@@ -91,7 +91,7 @@ const char *helpserv_module_deps[] = { NULL };
 /* 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." },
@@ -479,10 +479,9 @@ static struct {
     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 saxdb_context *reqlog_ctx;
 static struct log_type *HS_LOG;
 
 #define CMD_NEED_BOT            0x001
@@ -520,8 +519,8 @@ struct helpserv_bot {
 
     unsigned int helpchan_empty : 1;
 
-    time_t registered;
-    time_t last_active;
+    unsigned long registered;
+    unsigned long last_active;
     char *registrar;
 };
 
@@ -532,7 +531,7 @@ struct helpserv_user {
     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 */
@@ -563,9 +562,9 @@ struct helpserv_request {
     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) \
@@ -582,16 +581,16 @@ void STRUCTNAME##_free(void *data) {\
 }
 
 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;
@@ -624,31 +623,37 @@ static HELPSERV_FUNC(cmd_help);
         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 {
@@ -660,7 +665,7 @@ 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");
@@ -692,33 +697,58 @@ static struct helpserv_user *GetHSUser(struct helpserv_bot *hs, struct handle_in
 static void helpserv_log_request(struct helpserv_request *req, const char *reason) {
     char key[27+NICKLEN];
     char userhost[USERLEN+HOSTLEN+2];
+    struct saxdb_context *ctx;
+    int res;
 
-    if (!reqlog_ctx || !req)
+    assert(req != NULL);
+    assert(reason != NULL);
+    if (!(ctx = saxdb_open_context(reqlog_f)))
         return;
-    if (!reason)
-        reason = "";
-
-    sprintf(key, "%s-" FMT_TIME_T "-%lu", req->hs->helpserv->nick, req->opened, req->id);
-    saxdb_start_record(reqlog_ctx, key, 1);
-    if (req->helper) {
-        saxdb_write_string(reqlog_ctx, KEY_REQUEST_HELPER, req->helper->handle->handle);
-        saxdb_write_int(reqlog_ctx, KEY_REQUEST_ASSIGNED, req->assigned);
-    }
-    if (req->handle) {
-        saxdb_write_string(reqlog_ctx, KEY_REQUEST_HANDLE, req->handle->handle);
-    }
-    if (req->user) {
-        saxdb_write_string(reqlog_ctx, KEY_REQUEST_NICK, req->user->nick);
-        sprintf(userhost, "%s@%s", req->user->ident, req->user->hostname);
-        saxdb_write_string(reqlog_ctx, KEY_REQUEST_USERHOST, userhost);
+    sprintf(key, "%s-%lu-%lu", req->hs->helpserv->nick, (unsigned long)req->opened, req->id);
+    if ((res = setjmp(*saxdb_jmp_buf(ctx))) != 0) {
+        log_module(HS_LOG, LOG_ERROR, "Unable to log helpserv request: %s.", strerror(res));
+    } else {
+        saxdb_start_record(ctx, key, 1);
+        if (req->helper) {
+            saxdb_write_string(ctx, KEY_REQUEST_HELPER, req->helper->handle->handle);
+            saxdb_write_int(ctx, KEY_REQUEST_ASSIGNED, req->assigned);
+        }
+        if (req->handle) {
+            saxdb_write_string(ctx, KEY_REQUEST_HANDLE, req->handle->handle);
+        }
+        if (req->user) {
+            saxdb_write_string(ctx, KEY_REQUEST_NICK, req->user->nick);
+            sprintf(userhost, "%s@%s", req->user->ident, req->user->hostname);
+            saxdb_write_string(ctx, KEY_REQUEST_USERHOST, userhost);
+        }
+        saxdb_write_int(ctx, KEY_REQUEST_OPENED, req->opened);
+        saxdb_write_int(ctx, KEY_REQUEST_CLOSED, now);
+        saxdb_write_string(ctx, KEY_REQUEST_CLOSEREASON, reason);
+        saxdb_write_string_list(ctx, KEY_REQUEST_TEXT, req->text);
+        saxdb_end_record(ctx);
+        saxdb_close_context(ctx, 0);
     }
-    saxdb_write_int(reqlog_ctx, KEY_REQUEST_OPENED, req->opened);
-    saxdb_write_int(reqlog_ctx, KEY_REQUEST_CLOSED, now);
-    saxdb_write_string(reqlog_ctx, KEY_REQUEST_CLOSEREASON, reason);
-    saxdb_write_string_list(reqlog_ctx, KEY_REQUEST_TEXT, req->text);
-    saxdb_end_record(reqlog_ctx);
+}
 
-    fflush(reqlog_f);
+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).
@@ -860,7 +890,7 @@ static struct helpserv_request * create_request(struct userNode *user, struct he
     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. */
@@ -920,7 +950,7 @@ static struct helpserv_request * create_request(struct userNode *user, struct he
         sprintf(lbuf[0], fmt, req->id);
     }
     if (req != hs->unhandled) {
-        intervalString(unh, now - hs->unhandled->opened);
+        intervalString(unh, now - hs->unhandled->opened, user->handle_info);
         fmt = user_find_message(user, "HSMSG_REQ_UNHANDLED_TIME");
         sprintf(lbuf[1], fmt, unh);
     } else {
@@ -954,7 +984,7 @@ static struct helpserv_request * create_request(struct userNode *user, struct he
     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);
     }
@@ -963,7 +993,7 @@ static struct helpserv_request * create_request(struct userNode *user, struct he
 }
 
 /* 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;
@@ -1058,23 +1088,26 @@ static void helpserv_usermsg(struct userNode *user, struct helpserv_bot *hs, cha
             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));
-        intervalString(updatestr, now - req->updated);
+        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));
     }
@@ -1087,11 +1120,12 @@ static void helpserv_usermsg(struct userNode *user, struct helpserv_bot *hs, cha
 }
 
 /* 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 */
 
@@ -1109,7 +1143,8 @@ static void helpserv_botmsg(struct userNode *user, struct userNode *target, char
     }
 
     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;
 
@@ -1129,8 +1164,8 @@ static void helpserv_botmsg(struct userNode *user, struct userNode *target, char
     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);
     }
 }
 
@@ -1230,14 +1265,14 @@ static HELPSERV_USERCMD(usercmd_wait) {
         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)
             pos = count;
     }
     assert(pos >= 0);
-    intervalString(buf, now - req->hs->unhandled->opened);
+    intervalString(buf, now - req->hs->unhandled->opened, req->user->handle_info);
     helpserv_user_reply("HSMSG_WAIT_STATUS", pos+1, count, buf);
 }
 
@@ -1271,7 +1306,7 @@ static HELPSERV_FUNC(cmd_readhelp) {
         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;
 }
@@ -1672,7 +1707,7 @@ static HELPSERV_FUNC(cmd_close) {
             mod_chanmode_init(&change);
             change.argc = 1;
             change.args[0].mode = MODE_REMOVE | MODE_VOICE;
-            change.args[0].member = mn;
+            change.args[0].u.member = mn;
             mod_chanmode_announce(hs->helpserv, hs->helpchan, &change);
         }
     }
@@ -1764,7 +1799,7 @@ static HELPSERV_FUNC(cmd_list) {
         }
         tbl.contents[line][1] = strdup(username);
         tbl.contents[line][2] = req->helper ? req->helper->handle->handle : "(Unassigned)";
-        intervalString(opentime, now - req->opened);
+        intervalString(opentime, now - req->opened, user->handle_info);
         tbl.contents[line][3] = strdup(opentime);
         tbl.contents[line][4] = ((req->user || req->handle->users) ? "Online" : "Offline");
     }
@@ -1784,6 +1819,7 @@ static void helpserv_show(int from_opserv, struct helpserv_bot *hs, struct userN
     unsigned int nn;
     char buf[MAX_LINE_SIZE];
     char buf2[INTERVALLEN];
+    time_t feh;
 
     if (req->user)
         if (req->handle)
@@ -1797,8 +1833,9 @@ static void helpserv_show(int from_opserv, struct helpserv_bot *hs, struct userN
             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));
-    intervalString(buf2, now - 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");
     for (nn=0; nn < req->text->used; nn++)
@@ -1876,7 +1913,7 @@ static int helpserv_assign(int from_opserv, struct helpserv_bot *hs, struct user
         mod_chanmode_init(&change);
         change.argc = 1;
         change.args[0].mode = MODE_VOICE;
-        if ((change.args[0].member = GetUserMode(hs->helpchan, req->user)))
+        if ((change.args[0].u.member = GetUserMode(hs->helpchan, req->user)))
             mod_chanmode_announce(hs->helpserv, hs->helpchan, &change);
     }
 
@@ -1900,8 +1937,6 @@ static HELPSERV_FUNC(cmd_show) {
 
     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;
@@ -1983,11 +2018,10 @@ static HELPSERV_FUNC(cmd_addnote) {
     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;
@@ -1998,7 +2032,8 @@ static HELPSERV_FUNC(cmd_addnote) {
 
     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);
@@ -2253,7 +2288,7 @@ static HELPSERV_FUNC(cmd_move) {
             mod_chanmode_init(&change);
             change.argc = 1;
             change.args[0].mode = MODE_CHANOP;
-            change.args[0].member = AddChannelUser(hs->helpserv, hs->helpchan);
+            change.args[0].u.member = AddChannelUser(hs->helpserv, hs->helpchan);
             mod_chanmode_announce(hs->helpserv, hs->helpchan, &change);
         }
 
@@ -2296,7 +2331,7 @@ static HELPSERV_FUNC(cmd_bots) {
         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);
@@ -2309,7 +2344,7 @@ static HELPSERV_FUNC(cmd_bots) {
         tbl.contents[i][1] = bot->helpchan->name;
         tbl.contents[i][2] = owner ? owner->handle->handle : "None";
         tbl.contents[i][3] = alloca(INTERVALLEN);
-        intervalString((char*)tbl.contents[i][3], now - bot->last_active);
+        intervalString((char*)tbl.contents[i][3], now - bot->last_active, user->handle_info);
     }
 
     table_send((from_opserv ? opserv : hs->helpserv), user->nick, 0, NULL, tbl);
@@ -2332,7 +2367,7 @@ static void helpserv_page_helper_gone(struct helpserv_bot *hs, struct helpserv_r
             mod_chanmode_init(&change);
             change.argc = 1;
             change.args[0].mode = MODE_REMOVE | MODE_VOICE;
-            change.args[0].member = mn;
+            change.args[0].u.member = mn;
             mod_chanmode_announce(hs->helpserv, hs->helpchan, &change);
         }
         if(req->handle)
@@ -2387,7 +2422,7 @@ static void run_whine_interval(void *data) {
 
         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);
             }
         }
@@ -2395,7 +2430,7 @@ static void run_whine_interval(void *data) {
         if (reqlist.used) {
             char strwhinedelay[INTERVALLEN];
 
-            intervalString(strwhinedelay, (time_t)hs->intervals[INTERVAL_WHINE_DELAY]);
+            intervalString(strwhinedelay, hs->intervals[INTERVAL_WHINE_DELAY], NULL);
 #if ANNOYING_ALERT_PAGES
             tbl.length = reqlist.used + 1;
             tbl.width = 4;
@@ -2416,7 +2451,7 @@ static void run_whine_interval(void *data) {
                 tbl.contents[i][0] = strdup(reqid);
                 tbl.contents[i][1] = unh->user ? unh->user->nick : "Not online";
                 tbl.contents[i][2] = unh->handle ? unh->handle->handle : "Not authed";
-                intervalString(unh_time, now - unh->opened);
+                intervalString(unh_time, now - unh->opened, NULL);
                 tbl.contents[i][3] = strdup(unh_time);
             }
 
@@ -2496,11 +2531,11 @@ static void run_whine_interval(void *data) {
                 }
                 tbl.contents[i][2] = strdup(reqid);
 
-                intervalString(idle_time, now - mn->idle_since);
+                intervalString(idle_time, now - mn->idle_since, NULL);
                 tbl.contents[i][3] = strdup(idle_time);
             }
 
-            intervalString(stridledelay, (time_t)hs->intervals[INTERVAL_IDLE_DELAY]);
+            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);
 
@@ -2599,7 +2634,7 @@ static struct helpserv_bot *register_helpserv(const char *nick, const char *help
      * it's a harmless default */
     hs = calloc(1, sizeof(struct helpserv_bot));
 
-    if (!(hs->helpserv = AddService(nick, helpserv_conf.description))) {
+    if (!(hs->helpserv = AddLocalUser(nick, nick, NULL, helpserv_conf.description, NULL))) {
         free(hs);
         return NULL;
     }
@@ -2614,7 +2649,7 @@ static struct helpserv_bot *register_helpserv(const char *nick, const char *help
         mod_chanmode_init(&change);
         change.argc = 1;
         change.args[0].mode = MODE_CHANOP;
-        change.args[0].member = AddChannelUser(hs->helpserv, hs->helpchan);
+        change.args[0].u.member = AddChannelUser(hs->helpserv, hs->helpchan);
         mod_chanmode_announce(hs->helpserv, hs->helpchan, &change);
     }
 
@@ -2871,7 +2906,7 @@ static void set_page_target(struct helpserv_bot *hs, enum page_source idx, const
         mod_chanmode_init(&change);
         change.argc = 1;
         change.args[0].mode = MODE_CHANOP;
-        change.args[0].member = AddChannelUser(hs->helpserv, new_target);
+        change.args[0].u.member = AddChannelUser(hs->helpserv, new_target);
         mod_chanmode_announce(hs->helpserv, new_target, &change);
     }
     hs->page_targets[idx] = new_target;
@@ -2996,7 +3031,7 @@ static int opt_interval(struct userNode *user, struct helpserv_bot *hs, int from
             return 0;
         }
         if (new_int && new_int < min) {
-            intervalString(buf, min);
+            intervalString(buf, min, user->handle_info);
             helpserv_notice(user, "HSMSG_INVALID_INTERVAL", user_find_message(user, interval_types[idx].print_name), buf);
             return 0;
         }
@@ -3004,7 +3039,7 @@ static int opt_interval(struct userNode *user, struct helpserv_bot *hs, int from
         changed = 1;
     }
     if (hs->intervals[idx]) {
-        intervalString(buf, hs->intervals[idx]);
+        intervalString(buf, hs->intervals[idx], user->handle_info);
         helpserv_notice(user, interval_types[idx].print_name, buf);
     } else
         helpserv_notice(user, interval_types[idx].print_name, user_find_message(user, "HSMSG_0_DISABLED"));
@@ -3396,12 +3431,12 @@ static int request_read_helper(const char *key, void *data, void *extra) {
         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) {
@@ -3591,7 +3626,7 @@ static int helpserv_bot_read(const char *key, void *data, UNUSED_ARG(void *extra
     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);
@@ -3610,7 +3645,7 @@ static int helpserv_bot_read(const char *key, void *data, UNUSED_ARG(void *extra
     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);
 
@@ -3631,7 +3666,7 @@ helpserv_saxdb_read(struct dict *conf_db) {
     }
 
     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;
 }
 
@@ -3648,7 +3683,7 @@ static void helpserv_conf_read(void) {
     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))
@@ -3661,20 +3696,13 @@ static void helpserv_conf_read(void) {
     str = database_get_data(conf_node, "user_escape", RECDB_QSTRING);
     helpserv_conf.user_escape = str ? str[0] : '@';
 
-    if (reqlog_ctx) {
-        saxdb_close_context(reqlog_ctx);
-        reqlog_ctx = NULL;
-    }
     if (reqlog_f) {
         fclose(reqlog_f);
         reqlog_f = NULL;
     }
-    if (helpserv_conf.reqlogfile) {
-        if (!(reqlog_f = fopen(helpserv_conf.reqlogfile, "a"))) {
-            log_module(HS_LOG, LOG_ERROR, "Unable to open request logfile (%s): %s", helpserv_conf.reqlogfile, strerror(errno));
-        } else {
-            reqlog_ctx = saxdb_open_context(reqlog_f);
-        }
+    if (helpserv_conf.reqlogfile
+        && !(reqlog_f = fopen(helpserv_conf.reqlogfile, "a"))) {
+        log_module(HS_LOG, LOG_ERROR, "Unable to open request logfile (%s): %s", helpserv_conf.reqlogfile, strerror(errno));
     }
 }
 
@@ -3692,13 +3720,13 @@ helpserv_define_func(const char *name, helpserv_func_t *func, enum helpserv_leve
 }
 
 /* Drop requests that persist until part when a user leaves the chan */
-static void handle_part(struct userNode *user, struct chanNode *chan, UNUSED_ARG(const char *reason)) {
+static void handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason)) {
     struct helpserv_botlist *botlist;
     struct helpserv_userlist *userlist;
     const int from_opserv = 0; /* for helpserv_notice */
     unsigned int i;
 
-    if ((botlist = dict_find(helpserv_bots_bychan_dict, chan->name, NULL))) {
+    if ((botlist = dict_find(helpserv_bots_bychan_dict, mn->channel->name, NULL))) {
         for (i=0; i < botlist->used; i++) {
             struct helpserv_bot *hs;
             dict_iterator_t it;
@@ -3712,13 +3740,13 @@ static void handle_part(struct userNode *user, struct chanNode *chan, UNUSED_ARG
             for (it=dict_first(hs->requests); it; it=iter_next(it)) {
                 struct helpserv_request *req = iter_data(it);
 
-                if (user != req->user)
+                if (mn->user != req->user)
                     continue;
                 if (req->text->used) {
-                    helpserv_message(hs, user, MSGTYPE_REQ_DROPPED);
-                    helpserv_msguser(user, "HSMSG_REQ_DROPPED_PART", chan->name, req->id);
+                    helpserv_message(hs, mn->user, MSGTYPE_REQ_DROPPED);
+                    helpserv_msguser(mn->user, "HSMSG_REQ_DROPPED_PART", mn->channel->name, req->id);
                     if (req->helper && (hs->notify >= NOTIFY_DROP))
-                        helpserv_notify(req->helper, "HSMSG_NOTIFY_REQ_DROP_PART", req->id, user->nick);
+                        helpserv_notify(req->helper, "HSMSG_NOTIFY_REQ_DROP_PART", req->id, mn->user->nick);
                 }
                 helpserv_log_request(req, "Dropped");
                 dict_remove(hs->requests, iter_key(it));
@@ -3726,14 +3754,14 @@ static void handle_part(struct userNode *user, struct chanNode *chan, UNUSED_ARG
             }
         }
     }
-    
-    if (user->handle_info && (userlist = dict_find(helpserv_users_byhand_dict, user->handle_info->handle, NULL))) {
+
+    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_bot *hs = hs_user->hs;
             dict_iterator_t it;
 
-            if ((hs->helpserv == NULL) || (hs->helpchan != chan) || find_handle_in_channel(hs->helpchan, user->handle_info, user))
+            if ((hs->helpserv == NULL) || (hs->helpchan != mn->channel) || find_handle_in_channel(hs->helpchan, mn->user->handle_info, mn->user))
                 continue;
 
             /* In case of the clock being set back for whatever reason,
@@ -3752,7 +3780,7 @@ static void handle_part(struct userNode *user, struct chanNode *chan, UNUSED_ARG
                 if ((hs->persist_types[PERSIST_T_HELPER] == PERSIST_PART)
                     && (req->helper == hs_user)) {
                     char reason[CHANNELLEN + 8];
-                    sprintf(reason, "parted %s", chan->name);
+                    sprintf(reason, "parted %s", mn->channel->name);
                     helpserv_page_helper_gone(hs, req, reason);
                 }
             }
@@ -3768,9 +3796,9 @@ static void handle_part(struct userNode *user, struct chanNode *chan, UNUSED_ARG
                         unh = unh->next_unhandled;
 
                     if (num_trials) {
-                        helpserv_page(PGSRC_ALERT, "HSMSG_PAGE_FIRSTONLYTRIALALERT", hs->helpchan->name, user->nick, num_trials, num_unh);
+                        helpserv_page(PGSRC_ALERT, "HSMSG_PAGE_FIRSTONLYTRIALALERT", hs->helpchan->name, mn->user->nick, num_trials, num_unh);
                     } else {
-                        helpserv_page(PGSRC_ALERT, "HSMSG_PAGE_FIRSTEMPTYALERT", hs->helpchan->name, user->nick, num_unh);
+                        helpserv_page(PGSRC_ALERT, "HSMSG_PAGE_FIRSTEMPTYALERT", hs->helpchan->name, mn->user->nick, num_unh);
                     }
                     if (num_unh || !hs->req_on_join) {
                         timeq_del(0, run_empty_interval, hs, TIMEQ_IGNORE_WHEN);
@@ -3853,7 +3881,7 @@ static void associate_requests_bybot(struct helpserv_bot *hs, struct userNode *u
     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;
     }
@@ -3909,7 +3937,7 @@ static void associate_requests_bybot(struct helpserv_bot *hs, struct userNode *u
             mod_chanmode_init(&change);
             change.argc = 1;
             change.args[0].mode = MODE_VOICE;
-            if ((change.args[0].member = GetUserMode(hs->helpchan, user)))
+            if ((change.args[0].u.member = GetUserMode(hs->helpchan, user)))
                 mod_chanmode_announce(hs->helpserv, hs->helpchan, &change);
         }
     }
@@ -3945,7 +3973,7 @@ static int handle_join(struct modeNode *mNode) {
 
     if (IsLocal(user))
         return 0;
-    
+
     if (!(botlist = dict_find(helpserv_bots_bychan_dict, chan->name, NULL)))
         return 0;
 
@@ -4038,7 +4066,7 @@ static void handle_nickserv_rename(struct handle_info *handle, const char *old_h
         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];
@@ -4112,6 +4140,7 @@ static void handle_nickserv_auth(struct userNode *user, struct handle_info *old_
                 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;
@@ -4119,7 +4148,8 @@ static void handle_nickserv_auth(struct userNode *user, struct handle_info *old_
                     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);
                 }
 
@@ -4382,7 +4412,7 @@ static void handle_nickserv_failpw(struct userNode *user, struct handle_info *ha
     }
 }
 
-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
@@ -4396,15 +4426,17 @@ static time_t helpserv_next_stats(time_t after_when) {
 }
 
 /* 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);
 
@@ -4412,7 +4444,7 @@ static void helpserv_run_stats(time_t when) {
             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. */
@@ -4461,8 +4493,6 @@ static void helpserv_db_cleanup(void) {
     dict_delete(helpserv_reqs_byhand_dict);
     dict_delete(helpserv_users_byhand_dict);
 
-    if (reqlog_ctx)
-        saxdb_close_context(reqlog_ctx);
     if (reqlog_f)
         fclose(reqlog_f);
 }
@@ -4538,7 +4568,7 @@ int helpserv_init() {
 
     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);
 
@@ -4556,7 +4586,7 @@ int helpserv_init() {
     /* 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);
     }