EXTRA_DIST = $(noinst_DATA)
BUILT_SOURCES = arch-version.h
noinst_HEADERS = arch-version.h
-arch-version.h:
- @if [ -e $@ ] ; then OLD_REVISION=`cat $@` ; else OLD_REVISION="" ; fi ; \
- ARCH_REVISION=`tla logs -f | tail -n 1` ; \
- VERSION_CONTENTS="#define ARCH_VERSION \"$$ARCH_REVISION\"" ; \
- if [ "z" != "z$$ARCH_REVISION" -a "z$$OLD_REVISION" != "z$$VERSION_CONTENTS" ] ; then \
- echo "Putting new arch version into $@" ; \
- echo $$VERSION_CONTENTS > $@ ; \
+
+.PHONY: checkversion
+arch-version.h: checkversion
+checkversion:
+ @tla logs -f >/dev/null || exit 0; \
+ TMPFILE=`mktemp arch-version.h.XXXXXX` || exit 1 ; \
+ echo "#define ARCH_VERSION \"`tla logs -f | tail -n 1`\"" >> $$TMPFILE ; \
+ if diff -q arch-version.h $$TMPFILE >/dev/null 2>&1 ; then \
+ rm $$TMPFILE ; \
+ else \
+ echo "Putting new arch version into arch-version.h" ; \
+ rm -f arch-version.h ; \
+ mv $$TMPFILE arch-version.h ; \
fi
EXTRA_srvx_SOURCES = proto-bahamut.c proto-common.c proto-p10.c mod-snoop.c mod-memoserv.c mod-helpserv.c mod-sockcheck.c
"If the note type already exists, it is modified with the new values you specify.",
"$uSee Also:$u removenote");
"GIVEOWNERSHIP" ("/msg $C GIVEOWNERSHIP <#channel> <nick|*account>",
- "Transfer ownership of the channel from you to another user on the channel's userlist. You are demoted to co-owner, and they are promoted to owner.",
+ "Transfer ownership of the channel from you to another user on the channel's userlist. You are demoted to co-owner, and he or she is promoted to owner.",
"You may use *Account instead of Nick as the name argument; the * makes $C use the name of a account directly (useful if the user is not online).",
"$uSee Also:$u clvl, access, users");
"CSUSPEND" ("/msg $C CSUSPEND <#channel> [!]<duration> <reason>",
"$uSee Also:$u unregister, cunsuspend, durations");
"CUNSUSPEND" ("/msg $C CUNSUSPEND <#channel>",
"Restores a channel's $b$C$b registration.",
- "$bSee Also:$b csuspend, unregister");
+ "$uSee Also:$u csuspend, unregister");
"DELBAN" ("/msg $C DELBAN <#channel> <mask|nick>",
"Deletes a ban from the channel ban list. This command works for both permanent and timed bans alike.",
"$uSee Also:$u addban, addtimedban, bans");
"$uSee Also:$u staff");
"KICK" ("/msg $C KICK <#channel> <mask|nick> [reason]",
"Kicks the users matching the given nick or mask with the specified reason. If no reason is provided, a default will be used.",
- "$bSee Also:$b kickban");
+ "$uSee Also:$u kickban");
"KICKBAN" ("/msg $C KICKBAN <#channel> <mask|nick> [reason]",
"Kicks and bans with the specified reason any users with a matching nick or hostmask. If no reason is provided, a default one will be used.",
- "$bSee Also:$b addban, kick");
+ "$uSee Also:$u addban, kick");
"MDELCOOWNER" ("/msg $C MDELCOOWNER <#channel> <pattern>",
"Deletes all coowners with accounts matching the given pattern from the channel user list.",
- "$bSee Also:$b clist, delcoowner");
+ "$uSee Also:$u clist, delcoowner");
"MDELMASTER" ("/msg $C MDELMASTER <#channel> <pattern>",
"Deletes all masters with accounts matching the given pattern from the channel user list.",
- "$bSee Also:$b mdelban, mdelcoowner, mdelop, mdelowner, mdelpeon");
+ "$uSee Also:$u mdelban, mdelcoowner, mdelop, mdelowner, mdelpeon");
"MDELOP" ("/msg $C MDELOP <#channel> <pattern>",
"Deletes all ops with accounts matching the given pattern from the channel user list.");
"MDELOWNER" ("/msg $C MDELOWNER <#channel> <pattern>",
"$uSee Also:$u durations");
"UNBAN" ("/msg $C UNBAN <#channel> <mask|nick>",
"Unbans the specified nick or hostmask. If a nick is given, $b$C$b determines what hostmask(s) to unban.",
- "$bSee Also:$b ban, kick, kickban");
+ "$uSee Also:$u ban, kick, kickban");
"UNBANALL" ("/msg $C UNBANALL <#channel>",
"Clears the specified channel's banlist. If the channel is omitted, then $bunbanall$b will be done in the channel where the command was given.",
- "$bSee Also:$b ban, unban, unbanme");
+ "$uSee Also:$u ban, unban, unbanme");
"UNBANME" ("/msg $C UNBANME <#channel>",
"Unbans your hostmask from the specified channel.",
- "$bSee Also:$b ban, unban");
+ "$uSee Also:$u ban, unban");
"UNREGISTER" ("/msg $C UNREGISTER <#channel> [<confirmation>]",
"Unregisters a channel that is registered with $b$C$b. $bIMPORTANT$b: Once the channel is unregistered, the userlist $bcannot$b be recovered.",
"If you are not network staff, you must add a confimation string to the end of your line to confirm the unregistration. If you leave it out, $C will show the proper confirmation string.",
- "$bSee Also:$b register");
+ "$uSee Also:$u register");
"UNSUSPEND" ("/msg $C UNSUSPEND <#channel> <nick|*account>",
"This restores the target's access to the channel (after it has been suspended).",
"$uSee Also:$u suspend, deluser");
"UNVISITED" ("/msg $C UNVISITED [duration] [limit]",
"Displays up to a certain limit, all channels registered with $b$C$b that have not been visited within a certain duration. If a duration is not provided, a default will be used.",
- "$bSee Also:$b expire, search, durations");
+ "$uSee Also:$u expire, search, durations");
"UP" ("/msg $C UP <#channel>",
"Grants you your normal channel privileges. If your access in the channel is less than the GiveVoice setting, this does nothing. Otherwise, if your access is less than the GiveOps setting, $b$C$b will give you voice. If your access is at least GiveOps, $b$C$b will give you ops.");
"UPALL" ("/msg $C UPALL",
"USERS" ("/msg $C USERS <#channel> [mask]",
"Displays the userlist for the specified channel. If a mask is supplied, only users matching the mask will be shown.",
"$uSee Also:$u clist, mlist, olist, plist, wlist");
-"VERSION" ("/msg $C VERSION",
- "Sends you the srvx version and some additional version information that is specific to $b$C$b.");
"VOICE" ("/msg $C VOICE <#channel> <nick> [nick]...",
"Voices the specified nick in the specified channel. If the channel is omitted, then $bvoice$b will be done in the channel where the command was given.",
"$uSee Also:$u devoice");
"$bSTAFF$b: The message will be sent to helpers and opers.",
"$bCHANNELS$b: The message will be sent to all channels.",
"$bALL$b: A combination of USERS and CHANNELS.");
-"VERSION" ("/msg $G VERSION",
- "$bVERSION$b causes $b$G$b to to send you the srvx version and some additional version information that is specific to $b$G$b.");
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).
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));
}
}
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);
}
"/msg $S SET notify <1 or 0>",
"Decides wether $S should notify you of the messages in your inbox, on authentication with $N AND when someone sends you a new message.");
-"VERSION" ("/msg $S VERSION",
- "Sends you the srvx version and some additional version information that is specific to $b$S$b.");
-
"EXPIRY" ("/msg $S EXPIRY ",
"Sends you the current usage of expiring old messages.");
static struct module *modcmd_module;
static struct modcmd *bind_command, *help_command, *version_command;
static const struct message_entry msgtab[] = {
- { "MCMSG_VERSION", "$b"PACKAGE_STRING"$b ("CODENAME"), Built: " __DATE__ ", " __TIME__"." },
{ "MCMSG_BARE_FLAG", "Flag %.*s must be preceded by a + or -." },
{ "MCMSG_UNKNOWN_FLAG", "Unknown module flag %.*s." },
{ "MCMSG_BAD_OPSERV_LEVEL", "Invalid $O access level %s." },
{ "MCMSG_SERVICE_REMOVED", "Service $b%s$b has been deleted." },
{ "MCMSG_FILE_NOT_OPENED", "Unable to open file $b%s$b for writing." },
{ "MCMSG_MESSAGES_DUMPED", "Messages written to $b%s$b." },
+ { "MCMSG_MESSAGE_DUMP_FAILED", "Message dump failed: %s." },
{ "MCMSG_COMMAND_FLAGS", "Command flags are %s (inferred: %s)." },
{ "MCMSG_COMMAND_ACCOUNT_FLAGS", "Requires account flags +%s, prohibits account flags +%s." },
{ "MCMSG_COMMAND_ACCESS_LEVEL", "Requires channel access %d and $O access %d." },
void
modcmd_privmsg(struct userNode *user, struct userNode *bot, char *text, int server_qualified) {
struct service *service;
+
if (!(service = dict_find(services, bot->nick, NULL))) {
log_module(MAIN_LOG, LOG_ERROR, "modcmd_privmsg got privmsg for unhandled service %s, unregistering.", bot->nick);
reg_privmsg_func(bot, NULL);
return;
}
+
+ if (text[0] == '\x01') {
+ char *term, response[MAXLEN];
+
+ text++; /* Skip leading ^A. */
+ /* Chop off final ^A. */
+ term = strchr(text, '\x01');
+ if (!term)
+ return;
+ *term = '\0';
+ /* Parse out leading text. */
+ term = strchr(text, ' ');
+ if (term) {
+ *term++ = '\0';
+ if (!*term)
+ term = NULL;
+ }
+ /* No dict lookup since these are so few. */
+ if (!irccasecmp(text, "CLIENTINFO")) {
+ /* Use \001 instead of \x01 because apparently \x01C is
+ * interpreted as ASCII FS (\034, decimal 28, hex 1C).
+ */
+ irc_notice_user(bot, user, "\001CLIENTINFO CLIENTINFO PING TIME USERINFO VERSION\x01");
+ } else if (!irccasecmp(text, "PING")) {
+ if (term) {
+ snprintf(response, sizeof(response), "\x01PONG %s\x01", term);
+ irc_notice_user(bot, user, response);
+ } else {
+ irc_notice_user(bot,user, "\x01PONG\x01");
+ }
+ } else if (!irccasecmp(text, "TIME")) {
+ struct tm tm;
+ localtime_r(&now, &tm);
+ strftime(response, sizeof(response), "\x01TIME %a %b %d %H:%M:%S %Y\x01", &tm);
+ irc_notice_user(bot, user, response);
+ } else if (!irccasecmp(text, "USERINFO")) {
+ snprintf(response, sizeof(response), "\x01USERINFO %s\x01", bot->info);
+ irc_notice_user(bot, user, response);
+ } else if (!irccasecmp(text, "VERSION")) {
+ /* This function provides copyright management information
+ * to end users of srvx. You should not alter, disable or
+ * remove this command or its accessibility to normal IRC
+ * users, except to add copyright information pertaining
+ * to changes you make to srvx.
+ */
+ snprintf(response, sizeof(response), "\x01VERSION %s (%s) %s\x01", PACKAGE_STRING, CODENAME, ARCH_VERSION);
+ irc_notice_user(bot, user, response);
+ }
+ return;
+ }
+
if (service->msg_hook && service->msg_hook(user, bot, text, server_qualified))
return;
svccmd_invoke(user, service, NULL, text, server_qualified);
struct saxdb_context *ctx;
dict_iterator_t it;
FILE *pf;
+ int res;
if (!(pf = fopen(fname, "w"))) {
reply("MCMSG_FILE_NOT_OPENED", fname);
reply("MSG_INTERNAL_FAILURE");
return 0;
}
- for (it = dict_first(lang_C->messages); it; it = iter_next(it))
- saxdb_write_string(ctx, iter_key(it), iter_data(it));
- saxdb_close_context(ctx);
- fclose(pf);
- reply("MCMSG_MESSAGES_DUMPED", fname);
- return 1;
+ if ((res = setjmp(ctx->jbuf)) != 0) {
+ ctx->complex.used = 0; /* to avoid false assert()s in close */
+ saxdb_close_context(ctx);
+ fclose(pf);
+ reply("MCMSG_MESSAGE_DUMP_FAILED", strerror(res));
+ return 0;
+ } else {
+ for (it = dict_first(lang_C->messages); it; it = iter_next(it))
+ saxdb_write_string(ctx, iter_key(it), iter_data(it));
+ saxdb_close_context(ctx);
+ fclose(pf);
+ reply("MCMSG_MESSAGES_DUMPED", fname);
+ return 1;
+ }
}
static MODCMD_FUNC(cmd_version) {
/* This function provides copyright management information to end
* users of srvx. You should not alter, disable or remove this
- * command or its accessibility to normal IRC users.
+ * command or its accessibility to normal IRC users, except to add
+ * copyright information pertaining to changes you make to srvx.
*/
- reply("MCMSG_VERSION");
- send_message_type(4, user, cmd->parent->bot, "Copyright 2000-2004 srvx Development Team.\nThe srvx Development Team includes Paul Chang, Adrian Dewhurst, Miles Peterson, Michael Poole and others.\nThe srvx Development Team can be reached at http://sf.net/projects/srvx/ or in #srvx on irc.gamesurge.net.");
+ send_message_type(4, user, cmd->parent->bot, "$b"PACKAGE_STRING"$b ("CODENAME"), Built: "__DATE__", "__TIME__".\nCopyright 2000-2004 srvx Development Team.\nThe srvx Development Team includes Paul Chang, Adrian Dewhurst, Miles Peterson, Michael Poole and others.\nThe srvx Development Team can be reached at http://sf.net/projects/srvx/ or in #srvx on irc.gamesurge.net.");
if ((argc > 1) && !irccasecmp(argv[1], "arch"))
send_message_type(4, user, cmd->parent->bot, "%s", ARCH_VERSION);
return 1;
"$bWARN$b: The list of channels with activity warnings.",
"$bMODULES$b: Shows loaded modules that implement commands.",
"$bSERVICES$b: Shows active service bots.");
-"VERSION" ("/msg $O VERSION ",
- "Sends you the srvx version and all version information for $b$C$b, $b$O$b, $b$N$b, and $b$G$b.");
"INDEX" "${index}";
"SEX" ("$bSEX$b",
putsock(":%s NOTICE %s :%s", from->nick, to, message);
}
+void
+irc_notice_user(struct userNode *from, struct userNode *to, const char *message) {
+ putsock(":%s NOTICE %s :%s", from->nick, to->nick, message);
+}
+
void
irc_wallchops(UNUSED_ARG(struct userNode *from), UNUSED_ARG(const char *to), UNUSED_ARG(const char *message)) {
}
putsock("%s " P10_NOTICE " %s :%s", from->numeric, to, message);
}
+void
+irc_notice_user(struct userNode *from, struct userNode *to, const char *message)
+{
+ putsock("%s " P10_NOTICE " %s :%s", from->numeric, to->numeric, message);
+}
+
void
irc_privmsg(struct userNode *from, const char *to, const char *message)
{
/* messages */
void irc_privmsg(struct userNode *from, const char *to, const char *message);
void irc_notice(struct userNode *from, const char *to, const char *message);
+void irc_notice_user(struct userNode *from, struct userNode *to, const char *message);
void irc_wallchops(struct userNode *from, const char *to, const char *message);
/* channel maintenance */
#include "saxdb.h"
#include "timeq.h"
-DECLARE_LIST(int_list, int);
DEFINE_LIST(int_list, int);
struct saxdb {
struct saxdb *prev;
};
-struct saxdb_context {
- FILE *output;
- unsigned int indent;
- struct int_list complex;
- jmp_buf jbuf;
- /* XXX: If jbuf is ever used, places that use saxdb_open_context() and
- * saxdb_close_context() must be modified to fill it in */
-};
-
#define COMPLEX(CTX) ((CTX)->complex.used ? ((CTX)->complex.list[(CTX)->complex.used-1]) : 1)
static struct saxdb *last_db;
assert(db);
assert(db->filename);
data = parse_database(db->filename);
- if (!data) return;
+ if (!data)
+ return;
if (db->writer == saxdb_mondo_writer) {
mondo_db = data;
} else {
start = time(NULL);
if ((res = setjmp(ctx.jbuf)) || (res2 = db->writer(&ctx))) {
if (res) {
- log_module(MAIN_LOG, LOG_ERROR, "Exception %d caught while writing to %s", res, tmp_fname);
+ log_module(MAIN_LOG, LOG_ERROR, "Error writing to %s: %s", tmp_fname, strerror(res));
} else {
log_module(MAIN_LOG, LOG_ERROR, "Internal error %d while writing to %s", res2, tmp_fname);
}
for (it = dict_first(saxdbs); it; it = iter_next(it)) {
db = iter_data(it);
- if (!db->mondo_section) saxdb_write_db(db);
+ if (!db->mondo_section)
+ saxdb_write_db(db);
}
}
-#define saxdb_put_char(DEST, CH) fputc(CH, (DEST)->output)
-#define saxdb_put_string(DEST, CH) fputs(CH, (DEST)->output)
+#define saxdb_put_char(DEST, CH) do { \
+ if (fputc(CH, (DEST)->output) == EOF) \
+ longjmp((DEST)->jbuf, errno); \
+ } while (0)
+#define saxdb_put_string(DEST, CH) do { \
+ if (fputs(CH, (DEST)->output) == EOF) \
+ longjmp((DEST)->jbuf, errno); \
+ } while (0)
static inline void
saxdb_put_nchars(struct saxdb_context *dest, const char *name, int len) {
- while (len--) {
- fputc(*name++, dest->output);
- }
+ while (len--)
+ if (fputc(*name++, dest->output) == EOF)
+ longjmp(dest->jbuf, errno);
}
static void
assert(str);
saxdb_put_char(dest, '"');
while ((esc = strpbrk(str, "\\\a\b\t\n\v\f\r\""))) {
- if (esc != str) saxdb_put_nchars(dest, str, esc-str);
+ if (esc != str)
+ saxdb_put_nchars(dest, str, esc-str);
saxdb_put_char(dest, '\\');
switch (*esc) {
case '\a': saxdb_put_char(dest, 'a'); break;
#include "recdb.h"
+DECLARE_LIST(int_list, int);
+
struct saxdb;
-struct saxdb_context;
+/* This definition should ONLY be used so callers of
+ * saxdb_open_context() can initialize jbuf properly. */
+struct saxdb_context {
+ FILE *output;
+ unsigned int indent;
+ struct int_list complex;
+ jmp_buf jbuf;
+};
+
#define SAXDB_READER(NAME) int NAME(struct dict *db)
typedef SAXDB_READER(saxdb_reader_func_t);
int argc = 0;
int n;
while (*line && (argc < argv_size)) {
- while (*line == ' ') *line++ = 0;
+ while (*line == ' ')
+ *line++ = 0;
if (*line == ':' && irc_colon && argc > 0) {
/* the rest is a single parameter */
argv[argc++] = line + 1;
argv[argc++] = line;
if (argc >= argv_size)
break;
- while (*line != ' ' && *line) line++;
+ while (*line != ' ' && *line)
+ line++;
}
#ifdef NDEBUG
n = 0;
#else
- for (n=argc; n<argv_size; n++) {
+ for (n=argc; n<argv_size; n++)
argv[n] = (char*)0xFEEDBEEF;
- }
#endif
return argc;
}