From 93c1b8e63fd363a61a0eac9ecf8883d389d0cc5a Mon Sep 17 00:00:00 2001 From: Michael Poole Date: Sun, 11 Mar 2007 19:23:20 -0400 Subject: [PATCH] Add account notes. src/nickserv.h (struct handle_note): New structure. (struct handle_info): Add field to point to account's notes. src/nickserv.c (KEY_NOTE*): New configuration key macros. (WALK_NOTES): New macro to make a single pass through notes while deleting ones that have expired. (msgtab): Add entries to support commands related to account notes. (free_handle_info): Release notes on an account. (cmd_handleinfo): Show notes, or say there are none. (cmd_addnote): New command-handler function. (cmd_delnote): New command-handler function. (nickserv_saxdb_write): Write notes to the database. (nickserv_db_read_handle): Read notes from the database. (init_nickserv): Register new functions. src/nickserv.help (ADDNOTE): Document new command. (DELNOTE): Likewise. --- src/nickserv.c | 173 +++++++++++++++++++++++++++++++++++++++++++++- src/nickserv.h | 10 +++ src/nickserv.help | 8 +++ 3 files changed, 190 insertions(+), 1 deletion(-) diff --git a/src/nickserv.c b/src/nickserv.c index bf22dd6..1ab07c8 100644 --- a/src/nickserv.c +++ b/src/nickserv.c @@ -99,6 +99,11 @@ #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 NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_" @@ -200,6 +205,9 @@ static const struct message_entry msgtab[] = { { "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" }, @@ -248,6 +256,10 @@ static const struct message_entry msgtab[] = { { "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." }, @@ -360,6 +372,14 @@ static struct { /* 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) { @@ -387,7 +407,7 @@ register_handle(const char *handle, const char *passwd, UNUSED_ARG(unsigned long 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; } } @@ -507,6 +527,11 @@ free_handle_info(void *vhi) 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); @@ -1330,6 +1355,24 @@ static NICKSERV_FUNC(cmd_handleinfo) 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' */ @@ -2671,6 +2714,72 @@ static NICKSERV_FUNC(cmd_vacation) 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; @@ -2709,6 +2818,21 @@ nickserv_saxdb_write(struct saxdb_context *ctx) { 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) @@ -3316,6 +3440,7 @@ nickserv_db_read_handle(const char *handle, dict_t obj) 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; @@ -3355,6 +3480,50 @@ nickserv_db_read_handle(const char *handle, dict_t obj) 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 @@ -3800,6 +3969,8 @@ init_nickserv(const char *nick) 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, 750, 1, 0); + nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0); + nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0); if (!nickserv_conf.disable_nicks) { /* nick management commands */ nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0); diff --git a/src/nickserv.h b/src/nickserv.h index 9c1f6e9..fd88a32 100644 --- a/src/nickserv.h +++ b/src/nickserv.h @@ -75,12 +75,22 @@ struct handle_cookie { char cookie[COOKIELEN+1]; }; +struct handle_note { + struct handle_note *next; + time_t expires; + time_t set; + int id; + char setter[NICKSERV_HANDLE_LEN+1]; + char note[1]; +}; + struct handle_info { struct nick_info *nicks; struct string_list *masks; struct userNode *users; struct userData *channels; struct handle_cookie *cookie; + struct handle_note *notes; struct language *language; char *email_addr; char *epithet; diff --git a/src/nickserv.help b/src/nickserv.help index 7de7c13..b4a1b7e 100644 --- a/src/nickserv.help +++ b/src/nickserv.help @@ -510,3 +510,11 @@ "Merge contents of $bdbfilename$b into in-memory database. Any accounts in both will be $bOVERWRITTEN$b with the information from $bdbfilename$b, although authed users will be authed to the new account.", "This command is only accessible to IRC operators.", "$uSee Also:$u write"); +"ADDNOTE" ("/msg $N ADDNOTE ", + "Add a note to an account.", + "This command is only accessible to helpers and IRC operators.", + "$uSee Also:$u delnote"); +"DELNOTE" ("/msg $N DELNOTE ", + "Removes a note from an account.", + "This command is only accessible to helpers and IRC operators.", + "$uSee Also:$u delnote"); -- 2.20.1