/* nickserv.c - Nick/authentication service
- * Copyright 2000-2004 srvx Development Team
+ * Copyright 2000-2006 srvx Development Team
*
* This file is part of srvx.
*
#include "modcmd.h"
#include "opserv.h" /* for gag_create(), opserv_bad_channel() */
#include "saxdb.h"
-#include "sendmail.h"
+#include "mail.h"
#include "timeq.h"
#ifdef HAVE_REGEX_H
-#include <regex.h>
+# include <regex.h>
+#else
+# include "rx/rxposix.h"
#endif
#define NICKSERV_CONF_NAME "services/nickserv"
#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_EXPIRES "expires"
+#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_HANDLEINFO_EPITHET", " Epithet: %s" },
{ "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" },
{ "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
+ { "NSMSG_HANDLEINFO_NO_NOTES", " Notes: None" },
+ { "NSMSG_HANDLEINFO_NOTE_EXPIRES", " Note %d (%s ago by %s, expires %s): %s" },
+ { "NSMSG_HANDLEINFO_NOTE", " Note %d (%s ago by %s): %s" },
{ "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
{ "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
{ "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
{ "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_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
{ "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
{ "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
+ { "NSMSG_EXCESSIVE_DURATION", "$b%s$b is too long for this command." },
+ { "NSMSG_NOTE_ADDED", "Note $b%d$b added to $b%s$b." },
+ { "NSMSG_NOTE_REMOVED", "Note $b%d$b removed from $b%s$b." },
+ { "NSMSG_NO_SUCH_NOTE", "Account $b%s$b does not have a note with ID $b%d$b." },
{ "NSMSG_NO_ACCESS", "Access denied." },
{ "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
{ "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
{ "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" },
};
static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
static void nickserv_reclaim_p(void *data);
+static int nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask);
static struct {
unsigned int disable_nicks : 1;
/* We have 2^32 unique account IDs to use. */
unsigned long int highest_id = 0;
+#define WALK_NOTES(HANDLE, PREV, NOTE) \
+ for (PREV = NULL, NOTE = (HANDLE)->notes; NOTE != NULL; PREV = NOTE, NOTE = NOTE->next) \
+ if (NOTE->expires && NOTE->expires < now) { \
+ if (PREV) PREV->next = NOTE->next; else (HANDLE)->notes = NOTE->next; \
+ free(NOTE); \
+ if (!(NOTE = PREV ? PREV : (HANDLE)->notes)) break; \
+ } else
+
static char *
canonicalize_hostmask(char *mask)
{
id = 1 + highest_id++;
} else {
/* Note: highest_id is and must always be the highest ID. */
- if(id > highest_id) {
+ if (id > highest_id) {
highest_id = id;
}
}
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;
timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
nickserv_free_cookie(hi->cookie);
}
+ while (hi->notes) {
+ struct handle_note *note = hi->notes;
+ hi->notes = note->next;
+ free(note);
+ }
if (hi->email_addr) {
struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
handle_info_list_remove(hil, hi);
{
unsigned int i, len;
unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
+ int p;
+
len = strlen(pass);
if (len < nickserv_conf.password_min_length) {
if (user)
send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
return 0;
}
- dict_find(nickserv_conf.weak_password_dict, pass, &i);
- if (i) {
+ dict_find(nickserv_conf.weak_password_dict, pass, &p);
+ if (p) {
if (user)
send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
return 0;
snprintf(subject, sizeof(subject), fmt, netname);
fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
- sendmail(nickserv, hi, subject, body, 1);
+ mail_send(nickserv, hi, subject, body, 1);
fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
} else {
snprintf(subject, sizeof(subject), fmt, netname);
fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
- sendmail(nickserv, hi, subject, body, 1);
+ mail_send(nickserv, hi, subject, body, 1);
subject[0] = 0;
}
hi->email_addr = misc;
break;
}
if (subject[0])
- sendmail(nickserv, hi, subject, body, first_time);
+ mail_send(nickserv, hi, subject, body, first_time);
nickserv_bake_cookie(cookie);
}
static NICKSERV_FUNC(cmd_register)
{
+ irc_in_addr_t ip;
struct handle_info *hi;
const char *email_addr, *password;
int no_auth;
}
/* .. and that we are allowed to send to it. */
- if ((str = sendmail_prohibited_address(email_addr))) {
+ if ((str = mail_prohibited_address(email_addr))) {
reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
return 0;
}
string_list_append(hi->masks, strdup("*@*"));
} else {
string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
- if (user->ip.s_addr && user->hostname[strspn(user->hostname, "0123456789.")])
+ if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
}
nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
/* Set registering flag.. */
- user->modes |= FLAGS_REGISTERING;
+ user->modes |= FLAGS_REGISTERING;
return 1;
}
struct userNode *settee;
struct handle_info *hi;
- NICKSERV_MIN_PARMS(4);
+ NICKSERV_MIN_PARMS(3);
if (!is_valid_handle(argv[1])) {
reply("NSMSG_BAD_HANDLE", argv[1]);
return 0;
}
- if (strchr(argv[3], '@')) {
+ if (argc < 4) {
+ mask = NULL;
+ settee = NULL;
+ } else if (strchr(argv[3], '@')) {
mask = canonicalize_hostmask(strdup(argv[3]));
if (argc > 4) {
settee = GetUserH(argv[4]);
free(mask);
return 0;
}
- string_list_append(hi->masks, mask);
+ if (mask)
+ string_list_append(hi->masks, mask);
return 1;
}
struct do_not_register *dnr;
if ((dnr = chanserv_is_dnr(NULL, hi)))
reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
- if (!oper_outranks(user, hi))
+ if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
return 1;
} 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 (hi->flags) {
unsigned long flen = 1;
char flags[34]; /* 32 bits possible plus '+' and '\0' */
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;
reply("NSMSG_WEAK_PASSWORD");
if (hi->passwd[0] != '$')
cryptpass(passwd, hi->passwd);
- reply("NSMSG_AUTH_SUCCESS");
+ if (!hi->masks->used) {
+ irc_in_addr_t ip;
+ string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
+ if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
+ string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
+ }
argv[pw_arg] = "****";
+ reply("NSMSG_AUTH_SUCCESS");
return 1;
}
nickserv_set_email_addr(hi, hi->cookie->data);
reply("NSMSG_EMAIL_CHANGED");
break;
- case ALLOWAUTH:
+ case ALLOWAUTH: {
+ char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
set_user_handle_info(user, hi, 1);
+ nickserv_addmask(user, hi, mask);
reply("NSMSG_AUTH_SUCCESS");
+ free(mask);
break;
+ }
default:
reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
}
static int
-nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask)
+nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
{
unsigned int i;
for (i=0; i<hi->masks->used; i++) {
if (!strcmp(del_mask, hi->masks->list[i])) {
char *old_mask = hi->masks->list[i];
- if (hi->masks->used == 1) {
+ if (hi->masks->used == 1 && !force) {
send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
return 0;
}
static NICKSERV_FUNC(cmd_delmask)
{
NICKSERV_MIN_PARMS(2);
- return nickserv_delmask(user, user->handle_info, argv[1]);
+ return nickserv_delmask(user, user->handle_info, argv[1], 0);
}
static NICKSERV_FUNC(cmd_odelmask)
NICKSERV_MIN_PARMS(3);
if (!(hi = get_victim_oper(user, argv[1])))
return 0;
- return nickserv_delmask(user, hi, argv[2]);
+ return nickserv_delmask(user, hi, argv[2], 1);
}
int
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) {
send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
return 0;
}
- if ((str = sendmail_prohibited_address(argv[1]))) {
+ if ((str = mail_prohibited_address(argv[1]))) {
send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
return 0;
}
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))
reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
return 0;
}
- if (ni->owner->opserv_level >= user->handle_info->opserv_level) {
- reply("MSG_USER_OUTRANKED", ni->nick);
- return 0;
- }
+ if (!oper_outranks(user, ni->owner))
+ return 0;
reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
delete_nick(ni);
return 1;
static NICKSERV_FUNC(cmd_ounregister)
{
struct handle_info *hi;
+ char reason[MAXLEN];
NICKSERV_MIN_PARMS(2);
if (!(hi = get_victim_oper(user, argv[1])))
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);
return 1;
}
return 1;
}
+static NICKSERV_FUNC(cmd_addnote)
+{
+ struct handle_info *hi;
+ unsigned long duration;
+ char text[MAXLEN];
+ unsigned int id;
+ struct handle_note *prev;
+ struct handle_note *note;
+
+ /* Parse parameters and figure out values for note's fields. */
+ NICKSERV_MIN_PARMS(4);
+ hi = get_victim_oper(user, argv[1]);
+ if (!hi)
+ return 0;
+ duration = ParseInterval(argv[2]);
+ if (duration > 2*365*86400) {
+ reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
+ return 0;
+ }
+ unsplit_string(argv + 3, argc - 3, text);
+ WALK_NOTES(hi, prev, note) {}
+ id = prev ? (prev->id + 1) : 1;
+
+ /* Create the new note structure. */
+ note = calloc(1, sizeof(*note) + strlen(text));
+ note->next = NULL;
+ note->expires = duration ? (now + duration) : 0;
+ note->set = now;
+ note->id = id;
+ safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
+ strcpy(note->note, text);
+ if (prev)
+ prev->next = note;
+ else
+ hi->notes = note;
+ reply("NSMSG_NOTE_ADDED", id, hi->handle);
+ return 1;
+}
+
+static NICKSERV_FUNC(cmd_delnote)
+{
+ struct handle_info *hi;
+ struct handle_note *prev;
+ struct handle_note *note;
+ int id;
+
+ NICKSERV_MIN_PARMS(3);
+ hi = get_victim_oper(user, argv[1]);
+ if (!hi)
+ return 0;
+ id = strtoul(argv[2], NULL, 10);
+ WALK_NOTES(hi, prev, note) {
+ if (id == note->id) {
+ if (prev)
+ prev->next = note->next;
+ else
+ hi->notes = note->next;
+ free(note);
+ reply("NSMSG_NOTE_REMOVED", id, hi->handle);
+ return 1;
+ }
+ }
+ reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
+ return 0;
+}
+
static int
nickserv_saxdb_write(struct saxdb_context *ctx) {
dict_iterator_t it;
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;
saxdb_end_record(ctx);
}
}
+ if (hi->notes) {
+ struct handle_note *prev, *note;
+ saxdb_start_record(ctx, KEY_NOTES, 0);
+ WALK_NOTES(hi, prev, note) {
+ snprintf(flags, sizeof(flags), "%d", note->id);
+ saxdb_start_record(ctx, flags, 0);
+ if (note->expires)
+ saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
+ saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
+ saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
+ saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
+ saxdb_end_record(ctx);
+ }
+ saxdb_end_record(ctx);
+ }
if (hi->email_addr)
saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
if (hi->epithet)
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.)
+ */
+ if (hi_from->fakehost && !hi_to->fakehost)
+ hi_to->fakehost = strdup(hi_from->fakehost);
+
/* Notify of success. */
sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
}
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) {
struct string_list *masks, *slist;
struct handle_info *hi;
struct userNode *authed_users;
+ struct userData *channels;
unsigned long int id;
unsigned int ii;
dict_t subdb;
}
if ((hi = get_handle_info(handle))) {
authed_users = hi->users;
+ channels = hi->channels;
hi->users = NULL;
+ hi->channels = NULL;
dict_remove(nickserv_handle_dict, hi->handle);
} else {
authed_users = NULL;
+ channels = NULL;
}
hi = register_handle(handle, str, id);
if (authed_users) {
authed_users = authed_users->next_authed;
}
}
+ hi->channels = channels;
masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
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);
str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
if (str)
hi->fakehost = strdup(str);
+ /* Read the "cookie" sub-database (if it exists). */
subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
if (subdb) {
const char *data, *type, *expires, *cookie_str;
else
nickserv_free_cookie(cookie);
}
+ /* Read the "notes" sub-database (if it exists). */
+ subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
+ if (subdb) {
+ dict_iterator_t it;
+ struct handle_note *last_note;
+ struct handle_note *note;
+
+ last_note = NULL;
+ for (it = dict_first(subdb); it; it = iter_next(it)) {
+ const char *expires;
+ const char *setter;
+ const char *text;
+ const char *set;
+ const char *id;
+ dict_t notedb;
+
+ id = iter_key(it);
+ notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
+ if (!notedb) {
+ log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", id, hi->handle);
+ continue;
+ }
+ expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
+ setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
+ text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
+ set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
+ if (!setter || !text || !set) {
+ log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", id, hi->handle);
+ continue;
+ }
+ note = calloc(1, sizeof(*note) + strlen(text));
+ note->next = NULL;
+ note->expires = expires ? strtoul(expires, NULL, 10) : 0;
+ note->set = strtoul(set, NULL, 10);
+ note->id = strtoul(id, NULL, 10);
+ safestrncpy(note->setter, setter, sizeof(note->setter));
+ strcpy(note->note, text);
+ if (last_note)
+ last_note->next = note;
+ else
+ hi->notes = note;
+ last_note = note;
+ }
+ }
}
static int
break;
case RECLAIM_KILL:
msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
- irc_kill(nickserv, user, msg);
+ DelUser(user, nickserv, 1, msg);
break;
}
}
nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
- nickserv_define_func("MERGE", cmd_merge, 0, 1, 0);
+ 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);
if (nick) {
const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
- nickserv = AddService(nick, modes ? modes : NULL, "Nick Services", NULL);
+ nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
nickserv_service = service_register(nickserv);
}
saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);