fixed multiple compiler warnings
[srvx.git] / src / mod-helpserv.c
index 5e8a5139a5206415af674308475081973bbde587..bd1cf1c3ffde831e7cc1993e3efe6ad635adf984 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.
  *
@@ -53,6 +53,7 @@ const char *helpserv_module_deps[] = { NULL };
 #define KEY_NICK "nick"
 #define KEY_DB_BADCHANS "badchans"
 #define KEY_HELP_CHANNEL "help_channel"
+#define KEY_PUBLIC_CHANNEL "public_channel"
 #define KEY_PAGE_DEST "page_dest"
 #define KEY_CMDWORD "cmdword"
 #define KEY_PERSIST_LENGTH "persist_length"
@@ -85,13 +86,14 @@ const char *helpserv_module_deps[] = { NULL };
 #define KEY_PRIVMSG_ONLY "privmsg_only"
 #define KEY_REQ_ON_JOIN "req_on_join"
 #define KEY_AUTO_VOICE "auto_voice"
+#define KEY_AUTO_JOIN "auto_join"
 #define KEY_AUTO_DEVOICE "auto_devoice"
 #define KEY_LAST_ACTIVE "last_active"
 
 /* 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." },
@@ -123,6 +125,10 @@ static const struct message_entry msgtab[] = {
     { "HSMSG_EXPIRATION_DONE", "%d eligible HelpServ bots have retired." },
     { "HSMSG_BAD_WEEKDAY", "I do not know which day of the week $b%s$b is." },
     { "HSMSG_WEEK_STARTS", "$b%s$b's weeks start on $b%s$b." },
+    { "HSMSG_MODSTATS_BAD_FIELD", "The specified field does not exist." },
+    { "HSMSG_MODSTATS_BAD_WEEK", "The specified week is invalid." },
+    { "HSMSG_MODSTATS_NEGATIVE", "This modification would result in a negative value." },
+    { "HSMSG_MODSTATS_SUCCESS", "$b%s$b's stats have been modified successfully." },
 
 /* Registration */
     { "HSMSG_ILLEGAL_NICK", "$b%s$b is an illegal nick; cannot use it." },
@@ -166,7 +172,9 @@ static const struct message_entry msgtab[] = {
     { "HSMSG_SET_PRIVMSGONLY",    "$bPrivmsgOnly     $b %s" },
     { "HSMSG_SET_REQONJOIN",      "$bReqOnJoin       $b %s" },
     { "HSMSG_SET_AUTOVOICE",      "$bAutoVoice       $b %s" },
+    { "HSMSG_SET_AUTOJOIN",       "$bAutoJoin        $b %s" },
     { "HSMSG_SET_AUTODEVOICE",    "$bAutoDevoice     $b %s" },
+                               { "HSMSG_SET_PUBLICCHAN",     "$bPublicChan      $b %s" },
     { "HSMSG_PAGE_NOTICE", "notice" },
     { "HSMSG_PAGE_PRIVMSG", "privmsg" },
     { "HSMSG_PAGE_ONOTICE", "onotice" },
@@ -218,6 +226,7 @@ static const struct message_entry msgtab[] = {
     { "HSMSG_REQ_PERSIST_PART", "Everything you tell me until you are helped (or you leave %s) will be recorded. If you part %s, your request will be lost." },
     { "HSMSG_REQ_PERSIST_HANDLE", "Everything you tell me until you are helped will be recorded." },
     { "HSMSG_REQ_MAXLEN", "Sorry, but your request has reached the maximum number of lines. Please wait to be assigned to a helper and continue explaining your request to them." },
+    { "HSMSQ_REQ_TEXT_ADDED", "Message from $b%s:$b %s" },
     { "HSMSG_REQ_FOUND_ANOTHER", "Request ID#%lu has been closed. $S detected that you also have request ID#%lu open. If you send $S a message, it will be associated with that request." },
 
 /* Messages that are inserted into request text */
@@ -241,7 +250,7 @@ static const struct message_entry msgtab[] = {
     { "HSMSG_PAGE_HELPER_GONE_2", "Request ID#%lu from $b%s$b (Not authed) $bhas been unassigned$b, as its helper, %s has %s." },
     { "HSMSG_PAGE_HELPER_GONE_3", "Request ID#%lu from an offline user (Account %s) $bhas been unassigned$b, as its helper, %s has %s." },
     { "HSMSG_PAGE_HELPER_GONE_4", "Request ID#%lu from an offline user (No account) $bhas been unassigned$b, as its helper, %s has %s." },
-    { "HSMSG_PAGE_WHINE_HEADER", "$b%u unhandled request(s)$b waiting at least $b%s$b (%u total)" },
+    { "HSMSG_PAGE_WHINE_HEADER", "$b%u unhandled request(s)$b waiting at least $b%s$b (%u unhandled, %u total)" },
     { "HSMSG_PAGE_IDLE_HEADER", "$b%u users$b in %s $bidle at least %s$b:" },
     { "HSMSG_PAGE_EMPTYALERT", "$b%s has no helpers present (%u unhandled request(s))$b" },
     { "HSMSG_PAGE_ONLYTRIALALERT", "$b%s has no full helpers present (%d trial(s) present; %u unhandled request(s))$b" },
@@ -476,10 +485,11 @@ static struct {
     const char *reqlogfile;
     unsigned long db_backup_frequency;
     unsigned int expire_age;
+    unsigned long modstats_level;
     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;
@@ -494,6 +504,7 @@ struct helpserv_bot {
     struct userNode *helpserv;
 
     struct chanNode *helpchan;
+    struct chanNode *publicchan;
 
     struct chanNode *page_targets[PGSRC_COUNT];
     enum page_type page_types[PGSRC_COUNT];
@@ -502,7 +513,7 @@ struct helpserv_bot {
     enum notification_type notify;
 
     /* This is a default; it can be changed on a per-request basis */
-    enum persistence_type persist_types[PERSIST_T_COUNT];
+    enum persistence_length persist_lengths[PERSIST_T_COUNT];
 
     dict_t users; /* indexed by handle */
 
@@ -515,12 +526,13 @@ struct helpserv_bot {
     unsigned int privmsg_only : 1;
     unsigned int req_on_join : 1;
     unsigned int auto_voice : 1;
+    unsigned int auto_join : 1;
     unsigned int auto_devoice : 1;
 
     unsigned int helpchan_empty : 1;
 
-    time_t registered;
-    time_t last_active;
+    unsigned long registered;
+    unsigned long last_active;
     char *registrar;
 };
 
@@ -531,7 +543,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 */
@@ -562,9 +574,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) \
@@ -581,16 +593,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;
@@ -623,31 +635,43 @@ 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) { \
+        if(!_target->next_authed || GetUserMode(helper->hs->helpchan, _target)) {\
+          send_message(_target, (helper)->hs->helpserv, ARGS); \
+          break; \
+        } \
+    } } 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) { \
+        if(!_target->next_authed || GetUserMode(helper->hs->helpchan, _target)) {\
+          send_message(_target, (helper)->hs->helpserv, __VA_ARGS__); \
+          break; \
+        } \
     } } 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 {
@@ -659,7 +683,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");
@@ -696,10 +720,10 @@ static void helpserv_log_request(struct helpserv_request *req, const char *reaso
 
     assert(req != NULL);
     assert(reason != NULL);
-    if (!(ctx = saxdb_open_context(reqlog_f)))
+    if (!reqlog_f || !(ctx = saxdb_open_context(reqlog_f)))
         return;
-    sprintf(key, "%s-" FMT_TIME_T "-%lu", req->hs->helpserv->nick, req->opened, req->id);
-    if ((res = setjmp(ctx->jbuf)) != 0) {
+    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);
@@ -720,11 +744,31 @@ static void helpserv_log_request(struct helpserv_request *req, const char *reaso
         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);
-        fflush(reqlog_f);
+        saxdb_close_context(ctx, 0);
     }
 }
 
+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).
@@ -821,7 +865,7 @@ static struct helpserv_request * smart_get_request(struct helpserv_bot *hs, stru
 
 static struct helpserv_request * create_request(struct userNode *user, struct helpserv_bot *hs, int from_join) {
     struct helpserv_request *req = calloc(1, sizeof(struct helpserv_request));
-    char lbuf[3][MAX_LINE_SIZE], unh[INTERVALLEN];
+    char lbuf[3][MAX_LINE_SIZE], req_id[INTERVALLEN];
     struct helpserv_reqlist *reqlist, *hand_reqlist;
     const unsigned int from_opserv = 0;
     const char *fmt;
@@ -829,8 +873,8 @@ static struct helpserv_request * create_request(struct userNode *user, struct he
     assert(req);
 
     req->id = ++hs->last_requestid;
-    sprintf(unh, "%lu", req->id);
-    dict_insert(hs->requests, strdup(unh), req);
+    sprintf(req_id, "%lu", req->id);
+    dict_insert(hs->requests, strdup(req_id), req);
 
     if (hs->id_wrap) {
         unsigned long i;
@@ -864,7 +908,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. */
@@ -924,32 +968,31 @@ 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, user->handle_info);
+        intervalString(req_id, now - hs->unhandled->opened, user->handle_info);
         fmt = user_find_message(user, "HSMSG_REQ_UNHANDLED_TIME");
-        sprintf(lbuf[1], fmt, unh);
+        sprintf(lbuf[1], fmt, req_id);
     } else {
         fmt = user_find_message(user, "HSMSG_REQ_NO_UNHANDLED");
-        sprintf(lbuf[1], fmt);
+        sprintf(lbuf[1], "%s", fmt);
     }
-    switch (hs->persist_types[PERSIST_T_REQUEST]) {
+    switch (hs->persist_lengths[PERSIST_T_REQUEST]) {
         case PERSIST_PART:
             fmt = user_find_message(user, "HSMSG_REQ_PERSIST_PART");
             sprintf(lbuf[2], fmt, hs->helpchan->name, hs->helpchan->name);
             break;
         case PERSIST_QUIT:
             fmt = user_find_message(user, "HSMSG_REQ_PERSIST_QUIT");
-            sprintf(lbuf[2], fmt);
+            sprintf(lbuf[2], "%s", fmt);
             break;
         default:
             log_module(HS_LOG, LOG_ERROR, "%s has an invalid req_persist.", hs->helpserv->nick);
         case PERSIST_CLOSE:
             if (user->handle_info) {
                 fmt = user_find_message(user, "HSMSG_REQ_PERSIST_HANDLE");
-                sprintf(lbuf[2], fmt);
             } else {
                 fmt = user_find_message(user, "HSMSG_REQ_PERSIST_QUIT");
-                sprintf(lbuf[2], fmt);
             }
+            sprintf(lbuf[2], "%s", fmt);
             break;
     }
     helpserv_message(hs, user, MSGTYPE_REQ_OPENED);
@@ -967,7 +1010,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;
@@ -1023,8 +1066,11 @@ static void helpserv_usermsg(struct userNode *user, struct helpserv_bot *hs, cha
             helpserv_msguser(user, "HSMSG_USERCMD_NO_REQUEST");
             return;
         }
-        if ((hs->persist_types[PERSIST_T_REQUEST] == PERSIST_PART) && !GetUserMode(hs->helpchan, user)) {
-            helpserv_msguser(user, "HSMSG_REQ_YOU_NOT_IN_HELPCHAN_OPEN", hs->helpchan->name);
+        if ((hs->persist_lengths[PERSIST_T_REQUEST] == PERSIST_PART) && !GetUserMode(hs->helpchan, user) && (!hs->publicchan || (hs->publicchan && !GetUserMode(hs->publicchan, user)))) {
+             if(hs->publicchan)
+                helpserv_msguser(user, "HSMSG_REQ_YOU_NOT_IN_HELPCHAN_OPEN", hs->publicchan->name);
+             else
+                helpserv_msguser(user, "HSMSG_REQ_YOU_NOT_IN_HELPCHAN_OPEN", hs->helpchan->name);
             return;
         }
 
@@ -1062,40 +1108,58 @@ 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));
+        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));
     }
 
     req->updated = now;
-    if (!hs->req_maxlen || req->text->used < hs->req_maxlen)
+    if (!hs->req_maxlen || req->text->used < hs->req_maxlen) {
+        struct userNode *likely_helper;
+
         string_list_append(req->text, strdup(text));
-    else
+        /* Find somebody likely to be the helper */
+        if (!req->helper)
+            likely_helper = NULL;
+        else if ((likely_helper = req->helper->handle->users) && !likely_helper->next_authed) {
+            /* only one user it could be :> */
+        } else for (likely_helper = req->helper->handle->users; likely_helper; likely_helper = likely_helper->next_authed)
+            if (GetUserMode(hs->helpchan, likely_helper))
+                break;
+
+        if(likely_helper)
+            send_target_message(1, likely_helper->nick, hs->helpserv, "HSMSQ_REQ_TEXT_ADDED", user->nick, text);
+
+    } else
         helpserv_msguser(user, "HSMSG_REQ_MAXLEN");
 }
 
 /* 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 */
 
@@ -1113,7 +1177,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;
 
@@ -1133,8 +1198,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);
     }
 }
 
@@ -1234,7 +1299,7 @@ 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)
@@ -1275,7 +1340,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;
 }
@@ -1306,7 +1371,7 @@ static void helpserv_del_user(struct helpserv_bot *hs, struct helpserv_user *hs_
 }
 
 static int cmd_add_user(struct helpserv_bot *hs, int from_opserv, struct userNode *user, enum helpserv_level level, int argc, char *argv[]) {
-    struct helpserv_user *actor, *new_user;
+    struct helpserv_user *actor;
     struct handle_info *handle;
 
     REQUIRE_PARMS(2);
@@ -1334,7 +1399,7 @@ static int cmd_add_user(struct helpserv_bot *hs, int from_opserv, struct userNod
         return 0;
     }
 
-    new_user = helpserv_add_user(hs, handle, level);
+    helpserv_add_user(hs, handle, level);
 
     helpserv_notice(user, "HSMSG_ADDED_USER", helpserv_level2str(level), handle->handle);
     return 1;
@@ -1549,7 +1614,7 @@ static void free_request(void *data) {
     struct helpserv_request *req = data;
 
     /* Logging */
-    if (shutting_down && (req->hs->persist_types[PERSIST_T_REQUEST] != PERSIST_CLOSE || !req->handle)) {
+    if (shutting_down && (req->hs->persist_lengths[PERSIST_T_REQUEST] != PERSIST_CLOSE || !req->handle)) {
         helpserv_log_request(req, "srvx shutdown");
     }
 
@@ -1788,6 +1853,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)
@@ -1801,7 +1867,8 @@ 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));
+    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");
@@ -1817,7 +1884,7 @@ static int helpserv_assign(int from_opserv, struct helpserv_bot *hs, struct user
 
     if (!user->handle_info)
         return 0;
-    if ((hs->persist_types[PERSIST_T_HELPER] == PERSIST_PART) && !GetUserMode(hs->helpchan, user)) {
+    if ((hs->persist_lengths[PERSIST_T_HELPER] == PERSIST_PART) && !GetUserMode(hs->helpchan, user)) {
         struct helpserv_user *hsuser_actor = GetHSUser(hs, actor->handle_info);
         if (hsuser_actor->level < HlManager) {
             helpserv_notice(user, "HSMSG_REQ_YOU_NOT_IN_HELPCHAN", hs->helpchan->name);
@@ -1844,7 +1911,11 @@ static int helpserv_assign(int from_opserv, struct helpserv_bot *hs, struct user
     req->helper = GetHSUser(hs, user->handle_info);
     assert(req->helper);
     req->assigned = now;
-
+    
+    if (req->user && hs->auto_join) {
+        irc_svsjoin(hs->helpserv,req->user,hs->helpchan);
+    }
+    
     if (old_helper) {
         helpserv_notice(user, "HSMSG_REQ_REASSIGNED", req->id, old_helper->handle->handle);
         req->helper->reassigned_to[0]++;
@@ -1883,7 +1954,7 @@ static int helpserv_assign(int from_opserv, struct helpserv_bot *hs, struct user
         if ((change.args[0].u.member = GetUserMode(hs->helpchan, req->user)))
             mod_chanmode_announce(hs->helpserv, hs->helpchan, &change);
     }
-
+    
     return 1;
 }
 
@@ -1971,7 +2042,7 @@ static HELPSERV_FUNC(cmd_reassign) {
         return 0;
     }
 
-    if ((hs->persist_types[PERSIST_T_HELPER] == PERSIST_PART) && !GetUserMode(hs->helpchan, user) && (hs_user->level < HlManager)) {
+    if ((hs->persist_lengths[PERSIST_T_HELPER] == PERSIST_PART) && !GetUserMode(hs->helpchan, user) && (hs_user->level < HlManager)) {
         helpserv_notice(user, "HSMSG_REQ_HIM_NOT_IN_HELPCHAN", targetuser->nick, hs->helpchan->name);
         return 0;
     }
@@ -1985,6 +2056,7 @@ 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);
 
@@ -1998,7 +2070,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);
@@ -2296,7 +2369,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);
@@ -2387,7 +2460,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 +2468,7 @@ static void run_whine_interval(void *data) {
         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;
@@ -2420,7 +2493,7 @@ static void run_whine_interval(void *data) {
                 tbl.contents[i][3] = strdup(unh_time);
             }
 
-            helpserv_page(PGSRC_ALERT, "HSMSG_PAGE_WHINE_HEADER", reqlist.used, strwhinedelay, queuesize);
+            helpserv_page(PGSRC_ALERT, "HSMSG_PAGE_WHINE_HEADER", reqlist.used, strwhinedelay, queuesize, dict_size(hs->requests));
             table_send(hs->helpserv, hs->page_targets[PGSRC_ALERT]->name, 0, page_type_funcs[hs->page_types[PGSRC_ALERT]], tbl);
 
             for (i=1; i <= reqlist.used; i++) {
@@ -2428,7 +2501,7 @@ static void run_whine_interval(void *data) {
                 free((char *)tbl.contents[i][3]);
             }
 #else
-            helpserv_page(PGSRC_ALERT, "HSMSG_PAGE_WHINE_HEADER", reqlist.used, strwhinedelay, queuesize);
+            helpserv_page(PGSRC_ALERT, "HSMSG_PAGE_WHINE_HEADER", reqlist.used, strwhinedelay, queuesize, dict_size(hs->requests));
 #endif
         }
 
@@ -2500,7 +2573,7 @@ static void run_whine_interval(void *data) {
                 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);
 
@@ -2599,7 +2672,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, "+iok", helpserv_conf.description, NULL))) {
+    if (!(hs->helpserv = AddLocalUser(nick, nick, NULL, helpserv_conf.description, NULL))) {
         free(hs);
         return NULL;
     }
@@ -2635,7 +2708,6 @@ static struct helpserv_bot *register_helpserv(const char *nick, const char *help
         dict_insert(helpserv_bots_bychan_dict, hs->helpchan->name, botlist);
     }
     helpserv_botlist_append(botlist, hs);
-
     return hs;
 }
 
@@ -2643,7 +2715,7 @@ static HELPSERV_FUNC(cmd_register) {
     char *nick, *helpchan, reason[MAXLEN];
     struct handle_info *handle;
 
-    REQUIRE_PARMS(4);
+    REQUIRE_PARMS(3);
     nick = argv[1];
     if (!is_valid_nick(nick)) {
         helpserv_notice(user, "HSMSG_ILLEGAL_NICK", nick);
@@ -2704,7 +2776,7 @@ static void helpserv_free_bot(void *data) {
 }
 
 static void helpserv_unregister(struct helpserv_bot *bot, const char *quit_fmt, const char *global_fmt, const char *actor) {
-    char reason[MAXLEN], channame[CHANNELLEN], botname[NICKLEN];
+    char reason[MAXLEN], channame[CHANNELLEN], *botname;
     struct helpserv_botlist *botlist;
     size_t len;
 
@@ -2712,8 +2784,7 @@ static void helpserv_unregister(struct helpserv_bot *bot, const char *quit_fmt,
     helpserv_botlist_remove(botlist, bot);
     if (!botlist->used)
         dict_remove(helpserv_bots_bychan_dict, bot->helpchan->name);
-    len = strlen(bot->helpserv->nick) + 1;
-    safestrncpy(botname, bot->helpserv->nick, len);
+    botname=bot->helpserv->nick;
     len = strlen(bot->helpchan->name) + 1;
     safestrncpy(channame, bot->helpchan->name, len);
     snprintf(reason, sizeof(reason), quit_fmt, actor);
@@ -2844,6 +2915,75 @@ static HELPSERV_FUNC(cmd_weekstart) {
     return changed;
 }
 
+static HELPSERV_FUNC(cmd_modstats) {
+    struct handle_info *hi;
+    struct helpserv_user *victim;
+    const char *field_name;
+    int week, mod;
+    unsigned int *field = NULL;
+    char *errptr;
+
+    REQUIRE_PARMS(5);
+    if (!oper_has_access(user, (from_opserv ? opserv : hs->helpserv), helpserv_conf.modstats_level, 0))
+        return 0;
+    if (!(hi = helpserv_get_handle_info(user, argv[1])))
+        return 0;
+    if (!(victim = GetHSUser(hs, hi))) {
+        helpserv_notice(user, "HSMSG_NOT_IN_USERLIST", hi->handle, hs->helpserv->nick);
+        return 0;
+    }
+
+    field_name = argv[2];
+    if (!strcasecmp(argv[3], "total"))
+        week = 4;
+    else if(!strcasecmp(argv[3], "current"))
+        week = 0;
+    else {
+        week = strtoul(argv[3], &errptr, 0);
+        if (*errptr != '\0') {
+            helpserv_notice(user, "HSMSG_MODSTATS_BAD_WEEK");
+            return 0;
+        }
+    }
+    mod = strtol(argv[4], NULL, 0);
+
+    if (week < 0 || week > 4) {
+        helpserv_notice(user, "HSMSG_MODSTATS_BAD_WEEK");
+        return 0;
+    }
+
+    if (!strcasecmp(field_name, "time")) {
+        if (victim->join_time && (week == 0 || week == 4)) {
+            victim->time_per_week[0] += now - victim->join_time;
+            victim->time_per_week[4] += now - victim->join_time;
+            victim->join_time = now;
+        }
+        field = victim->time_per_week;
+    }
+    else if (!strcasecmp(field_name, "picked") || !strcasecmp(field_name, "picked_up") || !strcasecmp(field_name, "reqs"))
+        field = victim->picked_up;
+    else if (!strcasecmp(field_name, "closed"))
+        field = victim->closed;
+    else if (!strcasecmp(field_name, "ra_from") || !strcasecmp(field_name, "reassigned_from"))
+        field = victim->reassigned_from;
+    else if (!strcasecmp(field_name, "ra_to") || !strcasecmp(field_name, "reassigned_to"))
+        field = victim->reassigned_to;
+    else {
+        helpserv_notice(user, "HSMSG_MODSTATS_BAD_FIELD");
+        return 0;
+    }
+
+    if (mod < 0 && mod < -(int)field[week]) {
+        helpserv_notice(user, "HSMSG_MODSTATS_NEGATIVE");
+        return 0;
+    }
+
+    field[week] += mod;
+    helpserv_notice(user, "HSMSG_MODSTATS_SUCCESS", victim->handle->handle);
+
+    return (mod != 0);
+}
+
 static void set_page_target(struct helpserv_bot *hs, enum page_source idx, const char *target) {
     struct chanNode *new_target, *old_target;
 
@@ -3070,11 +3210,11 @@ static int opt_persist(struct userNode *user, struct helpserv_bot *hs, int from_
             helpserv_notice(user, "HSMSG_INVALID_OPTION", argv[0]);
             return 0;
         }
-        hs->persist_types[idx] = new_pers;
+        hs->persist_lengths[idx] = new_pers;
         changed = 1;
     }
-    helpserv_notice(user, persistence_types[idx].print_name,
-                    user_find_message(user, persistence_lengths[hs->persist_types[idx]].print_name));
+    helpserv_notice(user, persistence_lengths[idx].print_name,
+                    user_find_message(user, persistence_lengths[hs->persist_lengths[idx]].print_name));
     return changed;
 }
 
@@ -3162,23 +3302,72 @@ static HELPSERV_OPTION(opt_auto_voice) {
     OPTION_BINARY(hs->auto_voice, "HSMSG_SET_AUTOVOICE");
 }
 
+static HELPSERV_OPTION(opt_auto_join) {
+    OPTION_BINARY(hs->auto_join, "HSMSG_SET_AUTOJOIN");
+}
+
 static HELPSERV_OPTION(opt_auto_devoice) {
     OPTION_BINARY(hs->auto_devoice, "HSMSG_SET_AUTODEVOICE");
 }
 
+static HELPSERV_OPTION(opt_publicchan) {
+ char *publicchan;
+ int changed=0;
+ if (argc > 0) {
+  publicchan = argv[0];
+               if(strcmp(publicchan, "*")) {
+         if (!IsChannelName(publicchan)) {
+    helpserv_notice(user, "HSMSG_ILLEGAL_CHANNEL", publicchan);
+    HELPSERV_SYNTAX();
+    return 0;
+   }
+   if (opserv_bad_channel(publicchan)) {
+    helpserv_notice(user, "HSMSG_ILLEGAL_CHANNEL", publicchan);
+    return 0;
+   }
+               }
+               if (!hs->publicchan || (hs->publicchan && irccasecmp(hs->publicchan->name, publicchan))) {
+   if(hs->publicchan) {
+                 //there is another public chan o.O
+                        //part
+                        DelChannelUser(hs->helpserv, hs->publicchan, "unregistered.", 0);
+                               hs->publicchan = NULL;
+                }
+                       changed = 1;
+                       if(strcmp(publicchan, "*")) {
+                        if (!(hs->publicchan = GetChannel(publicchan))) {
+     hs->publicchan = AddChannel(publicchan, now, NULL, NULL);
+     AddChannelUser(hs->helpserv, hs->publicchan)->modes |= MODE_CHANOP;
+    } else {
+     struct mod_chanmode change;
+     mod_chanmode_init(&change);
+     change.argc = 1;
+     change.args[0].mode = MODE_CHANOP;
+     change.args[0].u.member = AddChannelUser(hs->helpserv, hs->publicchan);
+     mod_chanmode_announce(hs->helpserv, hs->publicchan, &change);
+                        }
+                       }
+  }
+ } else {
+        changed = 0;
+       }
+       helpserv_notice(user, "HSMSG_SET_PUBLICCHAN", (hs->publicchan) ? hs->publicchan->name : user_find_message(user,"MSG_NONE")); \
+ return changed;
+}
+
 static HELPSERV_FUNC(cmd_set) {
     helpserv_option_func_t *opt;
 
     if (argc < 2) {
         unsigned int i;
         helpserv_option_func_t *display[] = {
-            opt_pagetarget_command, opt_pagetarget_alert, opt_pagetarget_status,
+            opt_publicchan, opt_pagetarget_command, opt_pagetarget_alert, opt_pagetarget_status,
             opt_pagetype, opt_alert_page_type, opt_status_page_type,
             opt_greeting, opt_req_opened, opt_req_assigned, opt_req_closed,
             opt_idle_delay, opt_whine_delay, opt_whine_interval,
             opt_empty_interval, opt_stale_delay, opt_request_persistence,
             opt_helper_persistence, opt_notification, opt_id_wrap,
-            opt_req_maxlen, opt_privmsg_only, opt_req_on_join, opt_auto_voice,
+            opt_req_maxlen, opt_privmsg_only, opt_req_on_join, opt_auto_voice, opt_auto_join,
             opt_auto_devoice
         };
 
@@ -3396,12 +3585,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) {
@@ -3457,7 +3646,7 @@ helpserv_bot_write(const char *key, void *data, void *extra) {
     saxdb_end_record(ctx);
 
     /* Open requests */
-    if (hs->persist_types[PERSIST_T_REQUEST] == PERSIST_CLOSE) {
+    if (hs->persist_lengths[PERSIST_T_REQUEST] == PERSIST_CLOSE) {
         saxdb_start_record(ctx, KEY_REQUESTS, 0);
         dict_foreach(hs->requests, request_write_helper, ctx);
         saxdb_end_record(ctx);
@@ -3465,6 +3654,7 @@ helpserv_bot_write(const char *key, void *data, void *extra) {
 
     /* Other settings and state */
     saxdb_write_string(ctx, KEY_HELP_CHANNEL, hs->helpchan->name);
+    if(hs->publicchan) saxdb_write_string(ctx, KEY_PUBLIC_CHANNEL, hs->publicchan->name);
     slist = alloc_string_list(PGSRC_COUNT);
     for (pagesrc=0; pagesrc<PGSRC_COUNT; pagesrc++) {
         struct chanNode *target = hs->page_targets[pagesrc];
@@ -3487,7 +3677,7 @@ helpserv_bot_write(const char *key, void *data, void *extra) {
         saxdb_write_int(ctx, interval_types[inttype].db_name, hs->intervals[inttype]);
     }
     for (persisttype=0; persisttype<PERSIST_T_COUNT; persisttype++) {
-        const char *persist = persistence_lengths[hs->persist_types[persisttype]].db_name;
+        const char *persist = persistence_lengths[hs->persist_lengths[persisttype]].db_name;
         saxdb_write_string(ctx, persistence_types[persisttype].db_name, persist);
     }
     saxdb_write_string(ctx, KEY_NOTIFICATION, notification_types[hs->notify].db_name);
@@ -3500,6 +3690,7 @@ helpserv_bot_write(const char *key, void *data, void *extra) {
     saxdb_write_int(ctx, KEY_PRIVMSG_ONLY, hs->privmsg_only);
     saxdb_write_int(ctx, KEY_REQ_ON_JOIN, hs->req_on_join);
     saxdb_write_int(ctx, KEY_AUTO_VOICE, hs->auto_voice);
+    saxdb_write_int(ctx, KEY_AUTO_JOIN, hs->auto_join);
     saxdb_write_int(ctx, KEY_AUTO_DEVOICE, hs->auto_devoice);
     saxdb_write_int(ctx, KEY_LAST_ACTIVE, hs->last_active);
 
@@ -3520,7 +3711,7 @@ helpserv_saxdb_write(struct saxdb_context *ctx) {
 static int helpserv_bot_read(const char *key, void *data, UNUSED_ARG(void *extra)) {
     struct record_data *br = data, *raw_record;
     struct helpserv_bot *hs;
-    char *registrar, *helpchannel_name, *str;
+    char *registrar, *helpchannel_name, *publicchannel_name, *str;
     dict_t users, requests;
     enum page_source pagesrc;
     enum message_type msgtype;
@@ -3541,6 +3732,25 @@ static int helpserv_bot_read(const char *key, void *data, UNUSED_ARG(void *extra
 
     hs = register_helpserv(key, helpchannel_name, registrar);
 
+                               publicchannel_name = database_get_data(GET_RECORD_OBJECT(br), KEY_PUBLIC_CHANNEL, RECDB_QSTRING);
+    if (publicchannel_name) {
+                                if(!IsChannelName(publicchannel_name)) {
+        log_module(HS_LOG, LOG_ERROR, "%s has an invalid channel name.", key);
+        return 0;
+                                       } else {
+                                        if (!(hs->publicchan = GetChannel(publicchannel_name))) {
+       hs->publicchan = AddChannel(publicchannel_name, now, NULL, NULL);
+       AddChannelUser(hs->helpserv, hs->publicchan)->modes |= MODE_CHANOP;
+      } else {
+       struct mod_chanmode change;
+       mod_chanmode_init(&change);
+       change.argc = 1;
+       change.args[0].mode = MODE_CHANOP;
+       change.args[0].u.member = AddChannelUser(hs->helpserv, hs->publicchan);
+       mod_chanmode_announce(hs->helpserv, hs->publicchan, &change);
+                                               }
+                                       }
+    }
     raw_record = dict_find(GET_RECORD_OBJECT(br), KEY_PAGE_DEST, NULL);
     switch (raw_record ? raw_record->type : RECDB_INVALID) {
     case RECDB_QSTRING:
@@ -3585,13 +3795,13 @@ static int helpserv_bot_read(const char *key, void *data, UNUSED_ARG(void *extra
 
     for (persisttype=0; persisttype<PERSIST_T_COUNT; persisttype++) {
         str = database_get_data(GET_RECORD_OBJECT(br), persistence_types[persisttype].db_name, RECDB_QSTRING);
-        hs->persist_types[persisttype] = str ? persistence_from_name(str) : PERSIST_QUIT;
+        hs->persist_lengths[persisttype] = str ? persistence_from_name(str) : PERSIST_QUIT;
     }
     str = database_get_data(GET_RECORD_OBJECT(br), KEY_NOTIFICATION, RECDB_QSTRING);
     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);
@@ -3607,10 +3817,12 @@ static int helpserv_bot_read(const char *key, void *data, UNUSED_ARG(void *extra
     hs->req_on_join = str ? enabled_string(str) : 0;
     str = database_get_data(GET_RECORD_OBJECT(br), KEY_AUTO_VOICE, RECDB_QSTRING);
     hs->auto_voice = str ? enabled_string(str) : 0;
+    str = database_get_data(GET_RECORD_OBJECT(br), KEY_AUTO_JOIN, RECDB_QSTRING);
+    hs->auto_join = str ? enabled_string(str) : 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);
 
@@ -3631,7 +3843,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 +3860,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))
@@ -3658,6 +3870,8 @@ static void helpserv_conf_read(void) {
 
     str = database_get_data(conf_node, "expiration", RECDB_QSTRING);
     helpserv_conf.expire_age = ParseInterval(str ? str : "60d");
+    str = database_get_data(conf_node, "modstats_level", RECDB_QSTRING);
+    helpserv_conf.modstats_level = str ? strtoul(str, NULL, 0) : 850;
     str = database_get_data(conf_node, "user_escape", RECDB_QSTRING);
     helpserv_conf.user_escape = str ? str[0] : '@';
 
@@ -3672,10 +3886,10 @@ static void helpserv_conf_read(void) {
 }
 
 static struct helpserv_cmd *
-helpserv_define_func(const char *name, helpserv_func_t *func, enum helpserv_level access, long flags) {
+helpserv_define_func(const char *name, helpserv_func_t *func, enum helpserv_level level, long flags) {
     struct helpserv_cmd *cmd = calloc(1, sizeof(struct helpserv_cmd));
 
-    cmd->access = access;
+    cmd->access = level;
     cmd->weight = 1.0;
     cmd->func = func;
     cmd->flags = flags;
@@ -3699,13 +3913,15 @@ static void handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason)) {
             hs = botlist->list[i];
             if (!hs->helpserv)
                 continue;
-            if (hs->persist_types[PERSIST_T_REQUEST] != PERSIST_PART)
+            if (hs->persist_lengths[PERSIST_T_REQUEST] != PERSIST_PART)
                 continue;
 
             for (it=dict_first(hs->requests); it; it=iter_next(it)) {
                 struct helpserv_request *req = iter_data(it);
 
                 if (mn->user != req->user)
+                    continue;
+                                                                                                                               if (GetUserMode(hs->helpchan, mn->user)) //publicchan
                     continue;
                 if (req->text->used) {
                     helpserv_message(hs, mn->user, MSGTYPE_REQ_DROPPED);
@@ -3719,7 +3935,7 @@ static void handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason)) {
             }
         }
     }
-    
+
     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];
@@ -3742,11 +3958,11 @@ static void handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason)) {
             for (it=dict_first(hs->requests); it; it=iter_next(it)) {
                 struct helpserv_request *req=iter_data(it);
 
-                if ((hs->persist_types[PERSIST_T_HELPER] == PERSIST_PART)
+                if ((hs->persist_lengths[PERSIST_T_HELPER] == PERSIST_PART)
                     && (req->helper == hs_user)) {
-                    char reason[CHANNELLEN + 8];
-                    sprintf(reason, "parted %s", mn->channel->name);
-                    helpserv_page_helper_gone(hs, req, reason);
+                    char our_reason[CHANNELLEN + 8];
+                    sprintf(our_reason, "parted %s", mn->channel->name);
+                    helpserv_page_helper_gone(hs, req, our_reason);
                 }
             }
 
@@ -3799,7 +4015,7 @@ static void handle_quit(struct userNode *user, UNUSED_ARG(struct userNode *kille
         for (i=0; i < n; i++) {
             struct helpserv_request *req = reqlist->list[0];
 
-            if ((req->hs->persist_types[PERSIST_T_REQUEST] == PERSIST_QUIT) || !req->handle) {
+            if ((req->hs->persist_lengths[PERSIST_T_REQUEST] == PERSIST_QUIT) || !req->handle) {
                 char buf[12];
                 sprintf(buf, "%lu", req->id);
 
@@ -3833,7 +4049,7 @@ static void handle_quit(struct userNode *user, UNUSED_ARG(struct userNode *kille
             for (it=dict_first(hs->requests); it; it=iter_next(it)) {
                 struct helpserv_request *req=iter_data(it);
 
-                if ((hs->persist_types[PERSIST_T_HELPER] == PERSIST_QUIT) && (req->helper == hs_user)) {
+                if ((hs->persist_lengths[PERSIST_T_HELPER] == PERSIST_QUIT) && (req->helper == hs_user)) {
                     helpserv_page_helper_gone(hs, req, "disconnected");
                 }
             }
@@ -3845,8 +4061,7 @@ static void associate_requests_bybot(struct helpserv_bot *hs, struct userNode *u
     struct helpserv_reqlist *reqlist, *hand_reqlist=NULL;
     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;
     }
@@ -3896,6 +4111,7 @@ static void associate_requests_bybot(struct helpserv_bot *hs, struct userNode *u
         if (!nicknewest || (nicknewest->opened < req->opened))
             nicknewest = req;
 
+        
         if (hs->auto_voice && req->helper)
         {
             struct mod_chanmode change;
@@ -3910,7 +4126,7 @@ static void associate_requests_bybot(struct helpserv_bot *hs, struct userNode *u
     if ((force_greet && nicknewest) || (newest && (nicknewest == newest))) {
         /* Let the user know. Either the user is forced to be greeted, or the
          * above has changed which request will get their next message. */
-        helpserv_msguser(user, "HSMSG_GREET_EXISTING_REQ", hs->helpchan->name, nicknewest->id);
+        //helpserv_msguser(user, "HSMSG_GREET_EXISTING_REQ", hs->helpchan->name, nicknewest->id);
     }
 }
 
@@ -3938,7 +4154,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;
 
@@ -3975,7 +4191,7 @@ static int handle_join(struct modeNode *mNode) {
 
             if ((reqlist = dict_find(helpserv_reqs_bynick_dict, user->nick, NULL))) {
                 for (j=0; j < reqlist->used; j++)
-                    if (reqlist->list[i]->hs == hs)
+                    if (reqlist->list[j]->hs == hs)
                         break;
                 if (j < reqlist->used)
                     continue;
@@ -4031,7 +4247,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];
@@ -4105,6 +4321,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;
@@ -4112,7 +4329,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);
                 }
 
@@ -4152,7 +4370,7 @@ static void handle_nickserv_auth(struct userNode *user, struct handle_info *old_
         struct helpserv_request *req = reqlist->list[i];
         struct helpserv_bot *hs=req->hs;
 
-        if (!old_handle || hs->persist_types[PERSIST_T_REQUEST] == PERSIST_PART || hs->persist_types[PERSIST_T_REQUEST] == PERSIST_QUIT) {
+        if (!old_handle || hs->persist_lengths[PERSIST_T_REQUEST] == PERSIST_PART || hs->persist_lengths[PERSIST_T_REQUEST] == PERSIST_QUIT) {
             /* The request needs to be assigned to the new handle; either it
              * only persists until part/quit (so it makes sense to keep it as
              * close to the user as possible, and if it's made persistent later
@@ -4176,7 +4394,7 @@ static void handle_nickserv_auth(struct userNode *user, struct handle_info *old_
             if (old_handle) {
                 char buf[CHANNELLEN + 14];
 
-                if (hs->persist_types[PERSIST_T_REQUEST] == PERSIST_PART) {
+                if (hs->persist_lengths[PERSIST_T_REQUEST] == PERSIST_PART) {
                     sprintf(buf, "part channel %s", hs->helpchan->name);
                 } else {
                     strcpy(buf, "quit irc");
@@ -4279,7 +4497,7 @@ static void handle_nickserv_unreg(struct userNode *user, struct handle_info *han
                 }
                 helpserv_reqlist_append(req->parent_nick_list, req);
 
-                if (hs->persist_types[PERSIST_T_REQUEST] == PERSIST_CLOSE)
+                if (hs->persist_lengths[PERSIST_T_REQUEST] == PERSIST_CLOSE)
                     helpserv_msguser(req->user, "HSMSG_REQ_WARN_UNREG", handle->handle, hs->helpchan->name, req->id);
             } else {
                 if (handle->users) {
@@ -4375,7 +4593,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
@@ -4389,15 +4607,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);
 
@@ -4405,7 +4625,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. */
@@ -4496,6 +4716,7 @@ int helpserv_init() {
     helpserv_define_func("BOTS", cmd_bots, HlOper, CMD_FROM_OPSERV_ONLY|CMD_IGNORE_EVENT);
     helpserv_define_func("EXPIRE", cmd_expire, HlOper, CMD_FROM_OPSERV_ONLY);
     helpserv_define_func("WEEKSTART", cmd_weekstart, HlTrial, CMD_NEED_BOT);
+    helpserv_define_func("MODSTATS", cmd_modstats, HlOwner, CMD_NEED_BOT);
 
     helpserv_option_dict = dict_new();
     helpserv_define_option("PAGETARGET", opt_pagetarget_command);
@@ -4522,14 +4743,16 @@ int helpserv_init() {
     helpserv_define_option("PRIVMSGONLY", opt_privmsg_only);
     helpserv_define_option("REQONJOIN", opt_req_on_join);
     helpserv_define_option("AUTOVOICE", opt_auto_voice);
+    helpserv_define_option("AUTOJOIN", opt_auto_join);
     helpserv_define_option("AUTODEVOICE", opt_auto_devoice);
+                               helpserv_define_option("PUBLICCHAN", opt_publicchan);
 
     helpserv_usercmd_dict = dict_new();
     dict_insert(helpserv_usercmd_dict, "WAIT", usercmd_wait);
 
     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);
 
@@ -4547,7 +4770,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);
     }