#define KEY_COOKIE_TIMEOUT "cookie_timeout"
#define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
#define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
+#define KEY_OUNREGISTER_INACTIVE "ounregister_inactive"
+#define KEY_OUNREGISTER_FLAGS "ounregister_flags"
#define KEY_ID "id"
#define KEY_PASSWD "passwd"
#define KEY_ALLOWAUTH "allowauth"
#define KEY_EPITHET "epithet"
#define KEY_TABLE_WIDTH "table_width"
-#define KEY_ANNOUNCEMENTS "announcements"
#define KEY_MAXLOGINS "maxlogins"
#define KEY_FAKEHOST "fakehost"
#define KEY_NOTES "notes"
#define KEY_NOTE_SET "set"
#define KEY_NOTE_SETTER "setter"
#define KEY_NOTE_NOTE "note"
+#define KEY_KARMA "karma"
#define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
{ "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
{ "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
{ "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
+ { "NSMSG_HANDLEINFO_KARMA", " Karma: %d" },
{ "NSMSG_HANDLEINFO_VACATION", " On vacation." },
{ "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
{ "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
{ "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
{ "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
{ "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
+ { "NSMSG_NOTE_EXPIRES", "Note %d (%s ago by %s, expires %s): %s" },
+ { "NSMSG_NOTE", "Note %d (%s ago by %s): %s" },
+ { "NSMSG_NOTE_COUNT", "%u note(s) for %s." },
{ "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
{ "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
{ "NSMSG_WEAK_PASSWORD", "WARNING: You are using a password that is considered weak (easy to guess). It is STRONGLY recommended you change it (now, if not sooner) by typing \"/msg $S@$s PASS oldpass newpass\" (with your current password and a new password)." },
{ "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
{ "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
{ "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
+ { "NSMSG_UNREGISTER_MUST_FORCE", "Account $b%s$b is not inactive or has special flags set; use FORCE to unregister it." },
+ { "NSMSG_UNREGISTER_CANNOT_FORCE", "Account $b%s$b is not inactive or has special flags set; have an IRCOp use FORCE to unregister it." },
+ { "NSMSG_UNREGISTER_NODELETE", "Account $b%s$b is protected from unregistration." },
{ "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
{ "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
{ "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
{ "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
{ "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
{ "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
- { "NSMSG_INVALID_ANNOUNCE", "$b%s$b is an invalid announcements value." },
{ "NSMSG_SET_INFO", "$bINFO: $b%s" },
{ "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
{ "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
{ "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
{ "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
{ "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
- { "NSMSG_SET_ANNOUNCEMENTS", "$bANNOUNCEMENTS: $b%s" },
{ "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
{ "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
{ "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
{ "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
{ "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
{ "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
+ { "NSMSG_INVALID_KARMA", "$b%s$b is not a valid karma modifier." },
+ { "NSMSG_SET_KARMA", "$bKARMA: $b%d$b" },
{ "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
{ "NSEMAIL_ACTIVATION_BODY", "This email has been sent to verify that this email address belongs to the person who tried to register an account on %1$s. Your cookie is:\n %2$s\nTo verify your email address and complete the account registration, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nThis command is only used once to complete your account registration, and never again. Once you have run this command, you will need to authenticate everytime you reconnect to the network. To do this, you will have to type this command every time you reconnect:\n /msg %3$s@%4$s AUTH %5$s your-password\n Please remember to fill in 'your-password' with the actual password you gave to us when you registered.\n\nIf you did NOT request this account, you do not need to do anything. Please contact the %1$s staff if you have questions, and be sure to check our website." },
{ "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
unsigned long auto_reclaim_delay;
unsigned char default_maxlogins;
unsigned char hard_maxlogins;
+ unsigned long ounregister_inactive;
+ unsigned long ounregister_flags;
} nickserv_conf;
/* We have 2^32 unique account IDs to use. */
hi = calloc(1, sizeof(*hi));
hi->userlist_style = HI_DEFAULT_STYLE;
- hi->announcements = '?';
hi->handle = strdup(handle);
safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
hi->infoline = NULL;
} else if (hi != user->handle_info)
return 1;
+ if (IsOper(user))
+ reply("NSMSG_HANDLEINFO_KARMA", hi->karma);
+
if (nickserv_conf.email_enabled)
reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
reply(type);
}
- if (!hi->notes) {
- reply("NSMSG_HANDLEINFO_NO_NOTES");
- } else {
- struct handle_note *prev, *note;
-
- WALK_NOTES(hi, prev, note) {
- char set_time[INTERVALLEN];
- intervalString(set_time, now - note->set, user->handle_info);
- if (note->expires) {
- char exp_time[INTERVALLEN];
- intervalString(exp_time, note->expires - now, user->handle_info);
- reply("NSMSG_HANDLEINFO_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
- } else {
- reply("NSMSG_HANDLEINFO_NOTE", note->id, set_time, note->setter, note->note);
+ if (oper_has_access(user, cmd->parent->bot, 0, 1) || IsSupport(user)) {
+ if (!hi->notes) {
+ reply("NSMSG_HANDLEINFO_NO_NOTES");
+ } else {
+ struct handle_note *prev, *note;
+
+ WALK_NOTES(hi, prev, note) {
+ char set_time[INTERVALLEN];
+ intervalString(set_time, now - note->set, user->handle_info);
+ if (note->expires) {
+ char exp_time[INTERVALLEN];
+ intervalString(exp_time, note->expires - now, user->handle_info);
+ reply("NSMSG_HANDLEINFO_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
+ } else {
+ reply("NSMSG_HANDLEINFO_NOTE", note->id, set_time, note->setter, note->note);
+ }
}
}
}
return 1;
}
+static NICKSERV_FUNC(cmd_notes)
+{
+ struct handle_info *hi;
+ struct handle_note *prev, *note;
+ unsigned int hits;
+
+ NICKSERV_MIN_PARMS(2);
+ if (!(hi = get_victim_oper(user, argv[1])))
+ return 0;
+ hits = 0;
+ WALK_NOTES(hi, prev, note) {
+ char set_time[INTERVALLEN];
+ intervalString(set_time, now - note->set, user->handle_info);
+ if (note->expires) {
+ char exp_time[INTERVALLEN];
+ intervalString(exp_time, note->expires - now, user->handle_info);
+ reply("NSMSG_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
+ } else {
+ reply("NSMSG_NOTE", note->id, set_time, note->setter, note->note);
+ }
+ ++hits;
+ }
+ reply("NSMSG_NOTE_COUNT", hits, argv[1]);
+ return 1;
+}
+
static NICKSERV_FUNC(cmd_rename_handle)
{
struct handle_info *hi;
unsigned int i;
char *set_display[] = {
"INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
- "EMAIL", "ANNOUNCEMENTS", "MAXLOGINS", "LANGUAGE"
+ "EMAIL", "MAXLOGINS", "LANGUAGE"
};
send_message(user, nickserv, "NSMSG_SETTING_LIST");
static NICKSERV_FUNC(cmd_oset)
{
struct handle_info *hi;
+ struct svccmd *subcmd;
option_func_t *opt;
+ char cmdname[MAXLEN];
NICKSERV_MIN_PARMS(2);
return 0;
}
+ sprintf(cmdname, "%s %s", cmd->name, argv[2]);
+ subcmd = dict_find(cmd->parent->commands, cmdname, NULL);
+ if (subcmd && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, NULL, SVCCMD_NOISY))
+ return 0;
+
return opt(user, hi, 1, argc-2, argv+2);
}
return 1;
}
-static OPTION_FUNC(opt_announcements)
-{
- const char *choice;
-
- if (argc > 1) {
- if (enabled_string(argv[1]))
- hi->announcements = 'y';
- else if (disabled_string(argv[1]))
- hi->announcements = 'n';
- else if (!strcmp(argv[1], "?") || !irccasecmp(argv[1], "default"))
- hi->announcements = '?';
- else {
- send_message(user, nickserv, "NSMSG_INVALID_ANNOUNCE", argv[1]);
- return 0;
- }
- }
-
- switch (hi->announcements) {
- case 'y': choice = user_find_message(user, "MSG_ON"); break;
- case 'n': choice = user_find_message(user, "MSG_OFF"); break;
- case '?': choice = "default"; break;
- default: choice = "unknown"; break;
- }
- send_message(user, nickserv, "NSMSG_SET_ANNOUNCEMENTS", choice);
- return 1;
-}
-
static OPTION_FUNC(opt_password)
{
if (!override) {
return 1;
}
+static OPTION_FUNC(opt_karma)
+{
+ if (!override) {
+ send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
+ return 0;
+ }
+
+ if (argc > 1) {
+ if (argv[1][0] == '+' && isdigit(argv[1][1])) {
+ hi->karma += strtoul(argv[1] + 1, NULL, 10);
+ } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
+ hi->karma -= strtoul(argv[1] + 1, NULL, 10);
+ } else {
+ send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
+ }
+ }
+
+ send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
+ return 1;
+}
+
int
oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
{
struct handle_info *hi;
char reason[MAXLEN];
+ int force;
NICKSERV_MIN_PARMS(2);
if (!(hi = get_victim_oper(user, argv[1])))
return 0;
+
+ if (HANDLE_FLAGGED(hi, NODELETE)) {
+ reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
+ return 0;
+ }
+
+ force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
+ if (!force &&
+ ((hi->flags & nickserv_conf.ounregister_flags)
+ || hi->users
+ || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
+ reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
+ return 0;
+ }
+
snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
global_message(MESSAGE_RECIPIENT_STAFF, reason);
nickserv_unregister_handle(hi, user);
assert(hi->id);
#endif
saxdb_start_record(ctx, iter_key(it), 0);
- if (hi->announcements != '?') {
- flags[0] = hi->announcements;
- flags[1] = 0;
- saxdb_write_string(ctx, KEY_ANNOUNCEMENTS, flags);
- }
if (hi->cookie) {
struct handle_cookie *cookie = hi->cookie;
char *type;
if (hi->last_quit_host[0])
saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
+ if (hi->karma != 0)
+ saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
if (hi->masks->used)
saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
if (hi->maxlogins)
if (hi_from->lastseen > hi_to->lastseen)
hi_to->lastseen = hi_from->lastseen;
+ /* New karma is the sum of the two original karmas. */
+ hi_to->karma += hi_from->karma;
+
/* Does a fakehost carry over? (This intentionally doesn't set it
* for users previously attached to hi_to. They'll just have to
* reconnect.)
}
struct nickserv_discrim {
- unsigned int limit, min_level, max_level;
unsigned long flags_on, flags_off;
time_t min_registered, max_registered;
time_t lastseen;
+ unsigned int limit;
+ int min_level, max_level;
+ int min_karma, max_karma;
enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
const char *nickmask;
const char *hostmask;
+ const char *fakehostmask;
const char *handlemask;
const char *emailmask;
};
discrim = malloc(sizeof(*discrim));
memset(discrim, 0, sizeof(*discrim));
discrim->min_level = 0;
- discrim->max_level = ~0;
+ discrim->max_level = INT_MAX;
discrim->limit = 50;
discrim->min_registered = 0;
discrim->max_registered = INT_MAX;
- discrim->lastseen = now;
+ discrim->lastseen = LONG_MAX;
+ discrim->min_karma = INT_MIN;
+ discrim->max_karma = INT_MAX;
for (i=0; i<argc; i++) {
if (i == argc - 1) {
discrim->hostmask_type = SUPERSET;
}
discrim->hostmask = argv[++i];
+ } else if (!irccasecmp(argv[i], "fakehost")) {
+ if (!irccasecmp(argv[++i], "*")) {
+ discrim->fakehostmask = 0;
+ } else {
+ discrim->fakehostmask = argv[i];
+ }
} else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
if (!irccasecmp(argv[++i], "*")) {
discrim->handlemask = 0;
} else {
send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
}
+ } else if (!irccasecmp(argv[i], "karma")) {
+ const char *cmp = argv[++i];
+ if (cmp[0] == '<') {
+ if (cmp[1] == '=') {
+ discrim->max_karma = strtoul(cmp+2, NULL, 0);
+ } else {
+ discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
+ }
+ } else if (cmp[0] == '=') {
+ discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
+ } else if (cmp[0] == '>') {
+ if (cmp[1] == '=') {
+ discrim->min_karma = strtoul(cmp+2, NULL, 0);
+ } else {
+ discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
+ }
+ } else {
+ send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
+ }
} else {
send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
goto fail;
|| (discrim->max_registered < hi->registered)
|| (discrim->lastseen < (hi->users?now:hi->lastseen))
|| (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
+ || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
|| (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
|| (discrim->min_level > hi->opserv_level)
- || (discrim->max_level < hi->opserv_level)) {
+ || (discrim->max_level < hi->opserv_level)
+ || (discrim->min_karma > hi->karma)
+ || (discrim->max_karma < hi->karma)
+ ) {
return 0;
}
if (discrim->hostmask) {
hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
+ str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
+ hi->karma = str ? strtoul(str, NULL, 0) : 0;
/* We want to read the nicks even if disable_nicks is set. This is so
* that we don't lose the nick data entirely. */
slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
}
str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
- str = database_get_data(obj, KEY_ANNOUNCEMENTS, RECDB_QSTRING);
- hi->announcements = str ? str[0] : '?';
str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
+ str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
+ nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
+ str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
+ if (!str)
+ str = "ShgsfnHbu";
+ nickserv_conf.ounregister_flags = 0;
+ while(*str) {
+ unsigned int pos = handle_inverse_flags[(unsigned char)*str];
+ str++;
+ if(pos)
+ nickserv_conf.ounregister_flags |= 1 << (pos - 1);
+ }
if (!nickserv_conf.disable_nicks) {
str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
+ nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
if (!nickserv_conf.disable_nicks) {
/* nick management commands */
nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
dict_insert(nickserv_opt_dict, "TITLE", opt_title);
dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
}
- dict_insert(nickserv_opt_dict, "ANNOUNCEMENTS", opt_announcements);
dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
+ dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
+ nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
nickserv_handle_dict = dict_new();
dict_set_free_keys(nickserv_handle_dict, free);