-/* helpserv.c - Support Helper assistant service
- * Copyright 2002-2003 srvx Development Team
+/* mod-helpserv.c - Support Helper assistant service
+ * Copyright 2002-2003, 2006 srvx Development Team
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of srvx.
+ *
+ * srvx is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version. Important limitations are
- * listed in the COPYING file that accompanies this software.
+ * (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, email srvx-maintainers@srvx.net.
+ * along with srvx; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
/* Wishlist for helpserv.c
{ "HSMSG_STATS_TIME", "$uTime spent helping in %s:$u" },
{ "HSMSG_STATS_REQS", "$uRequest activity statistics:$u" },
+/* Status report headers */
+ { "HSMSG_STATS_REPORT_0", "Stats report for current week" },
+ { "HSMSG_STATS_REPORT_1", "Stats report for one week ago" },
+ { "HSMSG_STATS_REPORT_2", "Stats report for two weeks ago" },
+ { "HSMSG_STATS_REPORT_3", "Stats report for three weeks ago" },
+
/* Responses to user commands */
{ "HSMSG_YOU_BEING_HELPED", "You are already being helped." },
{ "HSMSG_YOU_BEING_HELPED_BY", "You are already being helped by $b%s$b." },
};
static const char *statsreport_week[] = {
- "Stats report for current week",
- "Stats report for one week ago",
- "Stats report for two weeks ago",
- "Stats report for three weeks ago"
+ "HSMSG_STATS_REPORT_0",
+ "HSMSG_STATS_REPORT_1",
+ "HSMSG_STATS_REPORT_2",
+ "HSMSG_STATS_REPORT_3"
};
static struct {
static time_t 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
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);
+ if ((res = setjmp(ctx->jbuf)) != 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);
+ fflush(reqlog_f);
}
- 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);
}
/* Searches for a request by number, nick, or account (num|nick|*account).
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 {
else
send_message_type(4, user, hs->helpserv, "%s %s %s", lbuf[0], lbuf[1], lbuf[2]);
- if (hs->req_on_join && req == hs->unhandled && hs->helpchan_empty) {
+ if (hs->req_on_join && req == hs->unhandled && hs->helpchan_empty && !user->uplink->burst) {
timeq_del(0, run_empty_interval, hs, TIMEQ_IGNORE_WHEN);
run_empty_interval(hs);
}
}
/* Handle a message from a user to a HelpServ bot. */
-static void helpserv_usermsg(struct userNode *user, struct helpserv_bot *hs, char *text) {
+static void helpserv_usermsg(struct userNode *user, struct helpserv_bot *hs, const char *text) {
const int from_opserv = 0; /* for helpserv_notice */
struct helpserv_request *req=NULL, *newest=NULL;
struct helpserv_reqlist *reqlist, *hand_reqlist;
char buf[MAX_LINE_SIZE], updatestr[INTERVALLEN], timestr[MAX_LINE_SIZE];
strftime(timestr, MAX_LINE_SIZE, HSFMT_TIME, localtime(&req->opened));
- intervalString(updatestr, now - req->updated);
+ 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);
}
/* Handle messages direct to a HelpServ bot. */
-static void helpserv_botmsg(struct userNode *user, struct userNode *target, char *text, UNUSED_ARG(int server_qualified)) {
+static void helpserv_botmsg(struct userNode *user, struct userNode *target, const char *text, UNUSED_ARG(int server_qualified)) {
struct helpserv_bot *hs;
struct helpserv_cmd *cmd;
struct helpserv_user *hs_user;
char *argv[MAXNUMPARAMS];
+ char tmpline[MAXLEN];
int argc, argv_shift;
const int from_opserv = 0; /* for helpserv_notice */
}
argv_shift = 1;
- argc = split_line(text, false, ArrayLength(argv)-argv_shift, argv+argv_shift);
+ safestrncpy(tmpline, text, sizeof(tmpline));
+ argc = split_line(tmpline, false, ArrayLength(argv)-argv_shift, argv+argv_shift);
if (!argc)
return;
if (!cmd->func) {
helpserv_notice(user, "HSMSG_INTERNAL_COMMAND", argv[argv_shift]);
} else if (cmd->func(user, hs, 0, argc, argv+argv_shift)) {
- unsplit_string(argv+argv_shift, argc, text);
- log_audit(HS_LOG, LOG_COMMAND, user, hs->helpserv, hs->helpchan->name, 0, text);
+ unsplit_string(argv+argv_shift, argc, tmpline);
+ log_audit(HS_LOG, LOG_COMMAND, user, hs->helpserv, hs->helpchan->name, 0, tmpline);
}
}
return;
}
- for (other = req->hs->unhandled, pos = -1, count = 0;
+ for (other = req->hs->unhandled, pos = -1, count = 0;
other;
other = other->next_unhandled, ++count) {
if (other == req)
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);
}
struct modeNode *mn = GetUserMode(hs->helpchan, req_user);
if ((!newest || !newest->helper) && mn && (mn->modes & MODE_VOICE)) {
struct mod_chanmode change;
- change.modes_set = change.modes_clear = 0;
+ 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);
}
}
}
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");
}
else
helpserv_notice(user, "HSMSG_REQ_INFO_2e");
strftime(buf, MAX_LINE_SIZE, HSFMT_TIME, localtime(&req->opened));
- intervalString(buf2, now - req->opened);
+ 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++)
if (req->user && hs->auto_voice) {
struct mod_chanmode change;
- change.modes_set = change.modes_clear = 0;
+ 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);
}
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;
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;
AddChannelUser(hs->helpserv, hs->helpchan)->modes |= MODE_CHANOP;
} else if (!helpserv_in_channel(hs, old_helpchan)) {
struct mod_chanmode change;
- change.modes_set = change.modes_clear = 0;
+ 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);
}
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);
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);
helpserv_msguser(req->user, "HSMSG_REQ_UNASSIGNED", req->id, reason);
if (hs->auto_devoice && mn && (mn->modes & MODE_VOICE)) {
struct mod_chanmode change;
- change.modes_set = change.modes_clear = 0;
+ 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)
if (reqlist.used) {
char strwhinedelay[INTERVALLEN];
- intervalString(strwhinedelay, (time_t)hs->intervals[INTERVAL_WHINE_DELAY]);
+ intervalString(strwhinedelay, (time_t)hs->intervals[INTERVAL_WHINE_DELAY], NULL);
#if ANNOYING_ALERT_PAGES
tbl.length = reqlist.used + 1;
tbl.width = 4;
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);
}
}
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, (time_t)hs->intervals[INTERVAL_IDLE_DELAY], NULL);
helpserv_page(PGSRC_STATUS, "HSMSG_PAGE_IDLE_HEADER", mode_list.used, hs->helpchan->name, stridledelay);
table_send(hs->helpserv, hs->page_targets[PGSRC_STATUS]->name, 0, page_types[hs->page_types[PGSRC_STATUS]].func, tbl);
* it's a harmless default */
hs = calloc(1, sizeof(struct helpserv_bot));
- if (!(hs->helpserv = AddService(nick, helpserv_conf.description))) {
+ if (!(hs->helpserv = AddLocalUser(nick, nick, NULL, helpserv_conf.description, NULL))) {
free(hs);
return NULL;
}
AddChannelUser(hs->helpserv, hs->helpchan)->modes |= MODE_CHANOP;
} else {
struct mod_chanmode change;
- change.modes_set = change.modes_clear = 0;
+ 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);
}
DelChannelUser(hs->helpserv, old_target, "Changing page target.", 0);
if (new_target && !helpserv_in_channel(hs, new_target)) {
struct mod_chanmode change;
- change.modes_set = change.modes_clear = 0;
+ 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;
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;
}
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"));
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))
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));
}
}
}
/* 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;
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));
}
}
}
-
- 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,
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);
}
}
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);
struct helpserv_request *newest=NULL, *nicknewest=NULL;
unsigned int i;
const int from_opserv = 0; /* For helpserv_notice */
-
+
if (!(user->handle_info && (hand_reqlist = dict_find(helpserv_reqs_byhand_dict, user->handle_info->handle, NULL))) && !force_greet) {
return;
}
if (hs->auto_voice && req->helper)
{
struct mod_chanmode change;
- change.modes_set = change.modes_clear = 0;
+ 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);
}
}
if (IsLocal(user))
return 0;
-
+
if (!(botlist = dict_find(helpserv_bots_bychan_dict, chan->name, NULL)))
return 0;
for (i=0; i < userlist->used; i++)
dict_insert(userlist->list[i]->hs->users, handle->handle, userlist->list[i]);
}
-
+
if (reqlist) {
for (i=0; i < reqlist->used; i++) {
struct helpserv_request *req=reqlist->list[i];
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);
}
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);