X-Git-Url: http://git.pk910.de/?a=blobdiff_plain;f=src%2Fnickserv.c;h=5da64a780a3291a57df60ac1684492d401cdaee7;hb=13698718d16cae97ed4df135fc20af386883b241;hp=b1f0d294382812ce2a9bc8e818b070fa0a8108cd;hpb=55dbd232d16ec5afa4cfd2e98388c58dcc93c928;p=srvx.git diff --git a/src/nickserv.c b/src/nickserv.c index b1f0d29..5da64a7 100644 --- a/src/nickserv.c +++ b/src/nickserv.c @@ -48,6 +48,7 @@ #define KEY_VALID_NICK_REGEX "valid_nick_regex" #define KEY_DB_BACKUP_FREQ "db_backup_freq" #define KEY_MODOPER_LEVEL "modoper_level" +#define KEY_MODSTAFF_LEVEL "modstaff_level" #define KEY_SET_EPITHET_LEVEL "set_epithet_level" #define KEY_SET_TITLE_LEVEL "set_title_level" #define KEY_SET_FAKEHOST_LEVEL "set_fakehost_level" @@ -75,16 +76,20 @@ #define KEY_OUNREGISTER_INACTIVE "ounregister_inactive" #define KEY_OUNREGISTER_FLAGS "ounregister_flags" #define KEY_HANDLE_TS_MODE "account_timestamp_mode" +#define KEY_MAX_AUTHLOG_LEN "max_authlog_len" #define KEY_ID "id" #define KEY_PASSWD "passwd" #define KEY_NICKS "nicks" #define KEY_MASKS "masks" #define KEY_OPSERV_LEVEL "opserv_level" +#define KEY_STAFF_LEVEL "staff_level" #define KEY_FLAGS "flags" #define KEY_REGISTER_ON "register" #define KEY_LAST_SEEN "lastseen" #define KEY_INFO "info" +#define KEY_DEVNULL "devnull" +#define KEY_WEBSITE "website" #define KEY_USERLIST_STYLE "user_style" #define KEY_SCREEN_WIDTH "screen_width" #define KEY_LAST_AUTHED_HOST "last_authed_host" @@ -109,6 +114,11 @@ #define KEY_NOTE_SETTER "setter" #define KEY_NOTE_NOTE "note" #define KEY_KARMA "karma" +#define KEY_AUTHLOG "authlog" +#define KEY_AUTHLOG_LOGIN_TIME "login_time" +#define KEY_AUTHLOG_LOGOUT_TIME "logout_time" +#define KEY_AUTHLOG_HOSTMASK "hostmask" +#define KEY_AUTHLOG_QUIT_REASON "quit_reason" #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_" @@ -194,8 +204,10 @@ static const struct message_entry msgtab[] = { { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." }, { "NSMSG_TITLE_INVALID", "Titles cannot contain any dots; please choose another." }, { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." }, + { "NSMSG_TITLE_TRUNCATED_RENAME", "That account name combined with the user's title would result in a truncated host; please choose a shorter account name." }, { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." }, { "NSMSG_FAKEIDENT_INVALID", "Fake idents must be shorter than %d characters." }, + { "NSMSG_FAKEMASK_INVALID", "Fake ident@hosts must be shorter than %d characters." }, { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" }, { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" }, { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" }, @@ -210,10 +222,14 @@ static const struct message_entry msgtab[] = { { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" }, { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" }, { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" }, + { "NSMSG_HANDLEINFO_DEVNULL", " DevNull Class: %s" }, + { "NSMSG_HANDLEINFO_WEBSITE", " Website: %s" }, + { "NSMSG_HANDLEINFO_ACCESS", " Access: %i" }, { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" }, { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" }, { "NSMSG_HANDLEINFO_FAKEIDENT", " Fake ident: %s" }, { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" }, + { "NSMSG_HANDLEINFO_FAKEIDENTHOST", " Fake host: %s@%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" }, @@ -302,6 +318,9 @@ static const struct message_entry msgtab[] = { { "NSMSG_SETTING_LIST", "$b$N account settings:$b" }, { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." }, { "NSMSG_SET_INFO", "$bINFO: $b%s" }, + { "NSMSG_SET_DEVNULL", "$bDEVNULL: $b%s" }, + { "NSMSG_SET_AUTOHIDE", "$bAUTOHIDE: $b%s" }, + { "NSMSG_SET_WEBSITE", "$bWEBSITE: $b%s" }, { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" }, { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" }, { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" }, @@ -313,10 +332,12 @@ static const struct message_entry msgtab[] = { { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" }, { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" }, { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" }, + { "NSMSG_SET_STAFFLEVEL", "$bSTAFF_LEVEL: $b%d" }, { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" }, { "NSMSG_SET_TITLE", "$bTITLE: $b%s" }, { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" }, { "NSMSG_SET_FAKEIDENT", "$bFAKEIDENT: $b%s" }, + { "NSMSG_SET_FAKEIDENTHOST", "$bFAKEHOST: $b%s@%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" }, @@ -376,6 +397,7 @@ static struct { unsigned long handle_expire_delay; unsigned long nochan_handle_expire_delay; unsigned long modoper_level; + unsigned long modstaff_level; unsigned long set_epithet_level; unsigned long set_title_level; unsigned long set_fakehost_level; @@ -383,7 +405,6 @@ static struct { unsigned long handles_per_email; unsigned long email_search_level; const char *network_name; - const char *titlehost_suffix; regex_t valid_handle_regex; regex_t valid_nick_regex; dict_t weak_password_dict; @@ -396,8 +417,19 @@ static struct { unsigned char hard_maxlogins; unsigned long ounregister_inactive; unsigned long ounregister_flags; + unsigned int max_authlog_len; } nickserv_conf; +struct pendingLOCUser { + struct handle_info *handle_info; + unsigned long time; + struct authlogEntry *authlog; + struct pendingLOCUser *next; +}; + +const char *titlehost_suffix = NULL; +static struct pendingLOCUser *pendingLOCUsers = NULL; + /* We have 2^32 unique account IDs to use. */ unsigned long int highest_id = 0; @@ -458,6 +490,8 @@ register_handle(const char *handle, const char *passwd, unsigned long id) dict_insert(nickserv_handle_dict, hi->handle, hi); hi->id = id; + hi->website = NULL; + hi->devnull = NULL; dict_insert(nickserv_id_dict, strdup(id_base64), hi); return hi; @@ -544,6 +578,8 @@ free_handle_info(void *vhi) free(hi->infoline); free(hi->epithet); free(hi->fakehost); + free(hi->devnull); + free(hi->website); free(hi->fakeident); if (hi->cookie) { timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0); @@ -560,6 +596,26 @@ free_handle_info(void *vhi) if (!hil->used) dict_remove(nickserv_email_dict, hi->email_addr); } + struct authlogEntry *authlog, *next; + for(authlog = hi->authlog; authlog; authlog = next) { + next = authlog->next; + struct pendingLOCUser *pending, *prev_pending = NULL; + for(pending = pendingLOCUsers; pending; pending = pending->next) { + if(pending->authlog == authlog) { + if(prev_pending) + prev_pending->next = pending->next; + else + pendingLOCUsers = pending->next; + free(pending); + break; + } + prev_pending = pending; + } + free((char *) authlog->hostmask); + if(authlog->quit_reason) + free((char *) authlog->quit_reason); + free(authlog); + } free(hi); } @@ -638,6 +694,23 @@ oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_le return 1; } +int +staff_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) { + if (!user->handle_info) { + if (!quiet) + send_message(user, bot, "MSG_AUTHENTICATE"); + return 0; + } + + if (user->handle_info->staff_level < min_level) { + if (!quiet) + send_message(user, bot, "NSMSG_NO_ACCESS"); + return 0; + } + + return 1; +} + static int is_valid_handle(const char *handle) { @@ -692,9 +765,51 @@ is_registerable_nick(const char *nick) } static int -is_valid_email_addr(const char *email) +is_valid_email_addr(const char *org_email) { - return strchr(email, '@') != NULL; + char email[strlen(org_email)+1]; + strcpy(email, org_email); + //validate email address + //1st check: there need to be one @ + char *p1 = strchr(email, '@'); + if(!p1 || strchr(p1+1, '@')) return 0; + *p1 = '\0'; + //2nd check: username (bevore @) must be at least 1 char long and out of part_chars + char *part_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._%+-"; + int i; + if(p1 - email == 0) return 0; + for(i = 0; i < (p1 - email); i++) { + if(!strchr(part_chars, email[i])) return 0; + } + //3rd check: there need to be at least 1 dot in the domain part and all characters out of part_chars + part_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-"; + char *p2 = NULL; + p1++; + i = 0; + while(*p1) { + if(*p1 == '.') { + if(!i) return 0; + i = 0; + p2 = p1; + } else if(!strchr(part_chars, *p1)) + return 0; + else + i++; + p1++; + } + if(!p2) return 0; + //4th check: TLD must be <= 5 chars, no special chars + i = 0; + p2++; + while(*p2) { + if(!isalpha(*p2)) + return 0; + else + i++; + p2++; + } + if(i > 5) return 0; + return 1; } static const char * @@ -780,7 +895,7 @@ valid_user_for(struct userNode *user, struct handle_info *hi) unsigned int ii; /* If no hostmasks on the account, allow it. */ - if (!hi->masks->used) + if (!hi->masks->used || IsDummy(user)) return 1; /* If any hostmask matches, allow it. */ for (ii=0; iimasks->used; ii++) @@ -884,7 +999,11 @@ generate_fakehost(struct handle_info *handle) return buffer; } else if (handle->fakehost[0] == '.') { /* A leading dot indicates the stored value is actually a title. */ - snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, nickserv_conf.titlehost_suffix); + snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, titlehost_suffix); + return buffer; + } else if (handle->fakehost[0] == '$') { + /* A leading $ indicates the stored value begins with the user handle. */ + snprintf(buffer, sizeof(buffer), "%s%s", handle->handle, handle->fakehost+1); return buffer; } return handle->fakehost; @@ -1263,16 +1382,17 @@ static NICKSERV_FUNC(cmd_register) return 0; /* Add any masks they should get. */ if (nickserv_conf.default_hostmask) { - string_list_append(hi->masks, strdup("*@*")); + nickserv_addmask(NULL, hi, strdup("*@*")); } else { - string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT)); + nickserv_addmask(NULL, hi, 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)); + nickserv_addmask(NULL, hi, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT)); } /* If they're the first to register, give them level 1000. */ if (dict_size(nickserv_handle_dict) == 1) { hi->opserv_level = 1000; + hi->staff_level = 1000; reply("NSMSG_ROOT_HANDLE", argv[1]); } @@ -1295,15 +1415,34 @@ static NICKSERV_FUNC(cmd_oregister) char *mask; struct userNode *settee; struct handle_info *hi; + const char *pass, *email; NICKSERV_MIN_PARMS(3); + pass = argv[2]; + argv[2] = "****"; + if (!is_valid_handle(argv[1])) { reply("NSMSG_BAD_HANDLE", argv[1]); return 0; } - if (argc < 4) { + if (argc < 5 || !nickserv_conf.email_enabled) { + email = NULL; + } else { + const char *str; + email = argv[4]; + if (!is_valid_email_addr(email)) { + send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR"); + return 0; + } + if ((str = mail_prohibited_address(email))) { + send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", email, str); + return 0; + } + } + + if (argc < 4 || !strcmp(argv[3], "*")) { mask = NULL; settee = NULL; } else if (strchr(argv[3], '@')) { @@ -1329,12 +1468,14 @@ static NICKSERV_FUNC(cmd_oregister) free(mask); return 0; } - if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) { + if (!(hi = nickserv_register(user, settee, argv[1], pass, 0))) { free(mask); return 0; } if (mask) string_list_append(hi->masks, mask); + if (email) + nickserv_set_email_addr(hi, email); return 1; } @@ -1369,6 +1510,12 @@ static NICKSERV_FUNC(cmd_handleinfo) } reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none)); + if ((oper_has_access(user, cmd->parent->bot, 200, 1)) || IsNetworkHelper(user)) + reply("NSMSG_HANDLEINFO_DEVNULL", (hi->devnull ? hi->devnull : nsmsg_none)); + if (user->handle_info && HANDLE_FLAGGED(user->handle_info, BOT)) + reply("NSMSG_HANDLEINFO_WEBSITE", (hi->website ? hi->website : nsmsg_none)); + if(hi->opserv_level > 0 && user->handle_info && HANDLE_FLAGGED(user->handle_info, BOT)) + reply("NSMSG_HANDLEINFO_ACCESS", hi->opserv_level); if (HANDLE_FLAGGED(hi, FROZEN)) reply("NSMSG_HANDLEINFO_VACATION"); @@ -1441,11 +1588,12 @@ static NICKSERV_FUNC(cmd_handleinfo) reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none)); } - if (hi->fakeident) - reply("NSMSG_HANDLEINFO_FAKEIDENT", (hi->fakeident ? hi->fakeident : handle_find_message(hi, "MSG_NONE"))); - - if (hi->fakehost) - reply("NSMSG_HANDLEINFO_FAKEHOST", (hi->fakehost ? hi->fakehost : handle_find_message(hi, "MSG_NONE"))); + if (hi->fakeident && hi->fakehost) + reply("NSMSG_HANDLEINFO_FAKEIDENTHOST", hi->fakeident, hi->fakehost); + else if (hi->fakeident) + reply("NSMSG_HANDLEINFO_FAKEIDENT", hi->fakeident); + else if (hi->fakehost) + reply("NSMSG_HANDLEINFO_FAKEHOST", hi->fakehost); if (hi->last_quit_host[0]) reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host); @@ -1616,6 +1764,12 @@ static NICKSERV_FUNC(cmd_rename_handle) reply("NSMSG_HANDLE_EXISTS", argv[2]); return 0; } + if (hi->fakehost && hi->fakehost[0] == '.' && + (strlen(argv[2]) + strlen(hi->fakehost+1) + + strlen(titlehost_suffix) + 2) > HOSTLEN) { + send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED_RENAME"); + return 0; + } dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1); hi->handle = strdup(argv[2]); @@ -1626,6 +1780,7 @@ static NICKSERV_FUNC(cmd_rename_handle) reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle); global_message(MESSAGE_RECIPIENT_STAFF, msgbuf); free(old_handle); + apply_fakehost(hi, NULL); return 1; } @@ -1647,6 +1802,50 @@ reg_failpw_func(failpw_func_t func) failpw_func_list[failpw_func_used++] = func; } +static struct authlogEntry *authlog_add(struct handle_info *hi, struct userNode *user, const char *mask) { + if(!hi || (!user && !mask)) return NULL; + if(!mask) + mask = generate_hostmask(user, GENMASK_USENICK|GENMASK_STRICT_IDENT|GENMASK_NO_HIDING|GENMASK_STRICT_HOST); + struct authlogEntry *authlog, *next, *prev = NULL; + authlog = malloc(sizeof(*authlog)); + authlog->login_time = now; + authlog->logout_time = 0; + authlog->hostmask = mask; + authlog->quit_reason = NULL; + authlog->user = user; + authlog->next = hi->authlog; + hi->authlog = authlog; + unsigned int i = 0; + for(authlog = hi->authlog; authlog; authlog = next) { + i++; + next = authlog->next; + if(i > nickserv_conf.max_authlog_len) { + struct pendingLOCUser *pending, *prev_pending = NULL; + for(pending = pendingLOCUsers; pending; pending = pending->next) { + if(pending->authlog == authlog) { + if(prev_pending) + prev_pending->next = pending->next; + else + pendingLOCUsers = pending->next; + free(pending); + break; + } + prev_pending = pending; + } + free((char *) authlog->hostmask); + if(authlog->quit_reason) + free((char *) authlog->quit_reason); + if(prev) + prev->next = authlog->next; + else + hi->authlog = authlog->next; + free(authlog); + } else + prev = authlog; + } + return hi->authlog; +} + static NICKSERV_FUNC(cmd_auth) { int pw_arg, used, maxlogins; @@ -1746,6 +1945,10 @@ static NICKSERV_FUNC(cmd_auth) return 1; } } + if (HANDLE_FLAGGED(hi, AUTOHIDE)) { + //ok we have a fakehost set... but we need to set mode +x + irc_svsmode(nickserv,user,"+x"); + } set_user_handle_info(user, hi, 1); if (nickserv_conf.email_required && !hi->email_addr) @@ -1756,15 +1959,61 @@ static NICKSERV_FUNC(cmd_auth) cryptpass(passwd, hi->passwd); 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)); + nickserv_addmask(NULL, hi, 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)); + nickserv_addmask(NULL, hi, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT)); } + authlog_add(hi, user, NULL); argv[pw_arg] = "****"; reply("NSMSG_AUTH_SUCCESS"); return 1; } +struct handle_info *checklogin(const char *user, const char *pass, const char *numeric, const char *hostmask, const char *ipmask) +{ + struct handle_info *hi; + unsigned int match = 0, ii = 0; + hi = dict_find(nickserv_handle_dict, user, NULL); + if(!hi) + return NULL; + /* If no hostmasks on the account, allow it. */ + if (hi->masks->used) { + /* If any hostmask matches, allow it. */ + for (ii=0; iimasks->used; ii++) + if (match_ircglob(hostmask, hi->masks->list[ii]) || match_ircglob(ipmask, hi->masks->list[ii])) { + match = 1; + break; + } + if(!match) + return NULL; + } + if(!checkpass(pass, hi->passwd)) + return NULL; + if (HANDLE_FLAGGED(hi, SUSPENDED)) + return NULL; + char *ptr = malloc(strlen(hostmask)+1); + strcpy(ptr, hostmask); + struct authlogEntry *authlog = authlog_add(hi, NULL, ptr); + struct pendingLOCUser *pending; + if(authlog && (pending = malloc(sizeof(*pending)))) { + pending->handle_info = hi; + pending->time = now; + pending->authlog = authlog; + pending->next = pendingLOCUsers; + pendingLOCUsers = pending; + } + return hi; +} + +char *getfakehost(const char *user) +{ + struct handle_info *hi; + hi = dict_find(nickserv_handle_dict, user, NULL); + if(!hi) + return 0; + return generate_fakehost(hi); +} + static allowauth_func_t *allowauth_func_list; static unsigned int allowauth_func_size = 0, allowauth_func_used = 0; @@ -1783,6 +2032,89 @@ reg_allowauth_func(allowauth_func_t func) allowauth_func_list[allowauth_func_used++] = func; } +static int cmd_authlog_func(struct userNode *user, struct svccmd *cmd, struct handle_info *hi); + +static MODCMD_FUNC(cmd_authlog) +{ + return cmd_authlog_func(user, cmd, user->handle_info); +} + +static MODCMD_FUNC(cmd_oauthlog) { + struct handle_info *hi; + + NICKSERV_MIN_PARMS(2); + + if (!(hi = get_victim_oper(user, argv[1]))) + return 0; + + return cmd_authlog_func(user, cmd, hi); +} + +static int cmd_authlog_func(struct userNode *user, struct svccmd *cmd, struct handle_info *hi) { + struct helpfile_table tbl; + struct authlogEntry *authlog; + int i = 0; + + for(authlog = hi->authlog; authlog; authlog = authlog->next) { + i++; + } + + tbl.length = i+1; + tbl.width = 4; + tbl.flags = 0; + tbl.flags = TABLE_NO_FREE; + tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0])); + tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0])); + tbl.contents[0][0] = "Hostmask"; + tbl.contents[0][1] = "Login"; + tbl.contents[0][2] = "Logout"; + tbl.contents[0][3] = "Quit Reason"; + + if(!tbl.length) { + table_send(cmd->parent->bot, user->nick, 0, NULL, tbl); + reply("MSG_NONE"); + free(tbl.contents[0]); + free(tbl.contents); + return 0; + } + + char *str, *ptr; + char intervalBuf[INTERVALLEN]; + i = 0; + for(authlog = hi->authlog; authlog; authlog = authlog->next) { + tbl.contents[++i] = malloc(tbl.width * sizeof(tbl.contents[0][0])); + tbl.contents[i][0] = authlog->hostmask; + str = intervalString(intervalBuf, now - authlog->login_time, hi); + ptr = malloc(strlen(str)+1); + strcpy(ptr, str); + tbl.contents[i][1] = ptr; + if(authlog->logout_time) + str = intervalString(intervalBuf, now - authlog->logout_time, hi); + else if(!authlog->user) + str = "Unknown"; + else { + sprintf(intervalBuf, "Never (%s)", authlog->user->nick); + str = intervalBuf; + } + ptr = malloc(strlen(str)+1); + strcpy(ptr, str); + tbl.contents[i][2] = ptr; + tbl.contents[i][3] = (authlog->quit_reason ? authlog->quit_reason : "-"); + } + + table_send(cmd->parent->bot, user->nick, 0, NULL, tbl); + for(i = 1; i < tbl.length; ++i) + { + free((char *) tbl.contents[i][1]); + free((char *) tbl.contents[i][2]); + free(tbl.contents[i]); + } + free(tbl.contents[0]); + free(tbl.contents); + + return 0; +} + static NICKSERV_FUNC(cmd_allowauth) { struct userNode *target; @@ -2238,7 +2570,7 @@ set_list(struct userNode *user, struct handle_info *hi, int override) unsigned int i; char *set_display[] = { "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE", - "EMAIL", "MAXLOGINS", "LANGUAGE" + "EMAIL", "MAXLOGINS", "LANGUAGE", "DEVNULL" }; send_message(user, nickserv, "NSMSG_SETTING_LIST"); @@ -2313,6 +2645,82 @@ static OPTION_FUNC(opt_info) return 1; } +static OPTION_FUNC(opt_devnull) +{ + const char *devnull; + + if (argc > 1) { + if (!override) { + send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]); + return 0; + } + if ((argv[1][0] == '*') && (argv[1][1] == 0)) { + free(hi->devnull); + hi->devnull = NULL; + } else { + devnull = unsplit_string(argv+1, argc-1, NULL); + if(devnull_check(devnull) == 1) { + if(hi->devnull) + free(hi->devnull); + hi->devnull = strdup(devnull); + } + } + } + + devnull = hi->devnull ? hi->devnull : user_find_message(user, "MSG_NONE"); + send_message(user, nickserv, "NSMSG_SET_DEVNULL", devnull); + return 1; +} + +void nickserv_devnull_delete(char *name) { + dict_iterator_t it; + struct handle_info *hi; + + for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) { + hi = iter_data(it); + if (hi->devnull && !irccasecmp(name, hi->devnull)) { + free(hi->devnull); + hi->devnull = NULL; + } + } +} + +void nickserv_devnull_rename(char *oldname, char *newname) { + dict_iterator_t it; + struct handle_info *hi; + + for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) { + hi = iter_data(it); + if (hi->devnull && !irccasecmp(oldname, hi->devnull)) { + hi->devnull = strdup(newname); + } + } +} + +static OPTION_FUNC(opt_website) +{ + const char *website; + + if (argc > 1) { + if (!HANDLE_FLAGGED(user->handle_info, BOT)) { + send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]); + return 0; + } + if ((argv[1][0] == '*') && (argv[1][1] == 0)) { + free(hi->website); + hi->website = NULL; + } else { + website = unsplit_string(argv+1, argc-1, NULL); + hi->website = strdup(website); + } + } + if (HANDLE_FLAGGED(user->handle_info, BOT)) { + website = hi->website ? hi->website : user_find_message(user, "MSG_NONE"); + send_message(user, nickserv, "NSMSG_SET_WEBSITE", website); + } + return 1; +} + static OPTION_FUNC(opt_width) { if (argc > 1) @@ -2375,6 +2783,23 @@ static OPTION_FUNC(opt_privmsg) return 1; } +static OPTION_FUNC(opt_autohide) +{ + if (argc > 1) { + if (enabled_string(argv[1])) + HANDLE_SET_FLAG(hi, AUTOHIDE); + else if (disabled_string(argv[1])) + HANDLE_CLEAR_FLAG(hi, AUTOHIDE); + else { + send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]); + return 0; + } + } + + send_message(user, nickserv, "NSMSG_SET_AUTOHIDE", user_find_message(user, HANDLE_FLAGGED(hi, AUTOHIDE) ? "MSG_ON" : "MSG_OFF")); + return 1; +} + static OPTION_FUNC(opt_style) { char *style; @@ -2410,6 +2835,8 @@ static OPTION_FUNC(opt_password) cryptpass(argv[1], hi->passwd); send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***"); + argv[1] = "****"; + return 1; } @@ -2542,6 +2969,24 @@ oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_i return 1; } +int +oper_try_set_staff_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) { + if (!oper_has_access(user, bot, nickserv_conf.modstaff_level, 0)) + return 0; + if ((user->handle_info->opserv_level < target->opserv_level) + || ((user->handle_info->opserv_level == target->opserv_level) + && (user->handle_info->opserv_level < 1000))) { + send_message(user, bot, "MSG_USER_OUTRANKED", target->handle); + return 0; + } + if (target->staff_level == new_level) + return 0; + log_module(NS_LOG, LOG_INFO, "Account %s setting staff level for account %s to %d (from %d).", + user->handle_info->handle, target->handle, new_level, target->staff_level); + target->staff_level = new_level; + return 1; +} + static OPTION_FUNC(opt_level) { int res; @@ -2556,6 +3001,20 @@ static OPTION_FUNC(opt_level) return res; } +static OPTION_FUNC(opt_staff_level) +{ + int res; + + if (!override) { + send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]); + return 0; + } + + res = (argc > 1) ? oper_try_set_staff_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0; + send_message(user, nickserv, "NSMSG_SET_STAFFLEVEL", hi->staff_level); + return res; +} + static OPTION_FUNC(opt_epithet) { if (!override) { @@ -2596,7 +3055,7 @@ static OPTION_FUNC(opt_title) return 0; } if ((strlen(user->handle_info->handle) + strlen(title) + - strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) { + strlen(titlehost_suffix) + 2) > HOSTLEN) { send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED"); return 0; } @@ -2631,33 +3090,47 @@ static OPTION_FUNC(opt_fakehost) } if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) { + if(strlen(argv[1]) >= sizeof(mask)) { + send_message(user, nickserv, "NSMSG_FAKEMASK_INVALID", USERLEN + HOSTLEN + 1); + return 0; + } + safestrncpy(mask, argv[1], sizeof(mask)); - if ((host = strrchr(mask, '@')) && host != mask && - oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) { - ident = mask; - *host++ = '\0'; + if ((host = strrchr(mask, '@')) && host != mask) { + /* If ident@host was used and the user doesn't have access to set idents, do not change anything. */ + if (!oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) { + host = NULL; + ident = NULL; + } else { + ident = mask; + *host++ = '\0'; + } } else { ident = NULL; host = mask; } - if ((strlen(host) > HOSTLEN) || (host[0] == '.')) { - send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN); + if (ident && strlen(ident) > USERLEN) { + send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN); return 0; } - if (ident && strlen(ident) > USERLEN) { - send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN); + if (host && ((strlen(host) > HOSTLEN) || (host[0] == '.'))) { + send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN); return 0; } - free(hi->fakehost); - if (!strcmp(host, "*")) - hi->fakehost = NULL; + if (host && host[0]) { + free(hi->fakehost); + if (!strcmp(host, "*")) + hi->fakehost = NULL; + else + hi->fakehost = strdup(host); + host = hi->fakehost; + } else - hi->fakehost = strdup(host); - host = hi->fakehost; + host = generate_fakehost(hi); if (ident) { free(hi->fakeident); @@ -2667,6 +3140,8 @@ static OPTION_FUNC(opt_fakehost) hi->fakeident = strdup(ident); ident = hi->fakeident; } + else + ident = generate_fakeident(hi, NULL); apply_fakehost(hi, NULL); } else { @@ -2675,9 +3150,10 @@ static OPTION_FUNC(opt_fakehost) } if (!host) host = (char *) user_find_message(user, "MSG_NONE"); - send_message(user, nickserv, "NSMSG_SET_FAKEHOST", host); - if (ident) - send_message(user, nickserv, "NSMSG_SET_FAKEIDENT", ident); + if(ident) + send_message(user, nickserv, "NSMSG_SET_FAKEIDENTHOST", ident, host); + else + send_message(user, nickserv, "NSMSG_SET_FAKEHOST", host); return 1; } @@ -2713,12 +3189,10 @@ static OPTION_FUNC(opt_fakeident) static NICKSERV_FUNC(cmd_reclaim) { - struct handle_info *hi; struct nick_info *ni; struct userNode *victim; NICKSERV_MIN_PARMS(2); - hi = user->handle_info; ni = dict_find(nickserv_nick_dict, argv[1], 0); if (!ni) { reply("NSMSG_UNKNOWN_NICK", argv[1]); @@ -3024,6 +3498,10 @@ nickserv_saxdb_write(struct saxdb_context *ctx) { saxdb_write_int(ctx, KEY_ID, hi->id); if (hi->infoline) saxdb_write_string(ctx, KEY_INFO, hi->infoline); + if (hi->devnull) + saxdb_write_string(ctx, KEY_DEVNULL, hi->devnull); + if (hi->website) + saxdb_write_string(ctx, KEY_WEBSITE, hi->website); 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); @@ -3045,6 +3523,8 @@ nickserv_saxdb_write(struct saxdb_context *ctx) { } if (hi->opserv_level) saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level); + if (hi->staff_level) + saxdb_write_int(ctx, KEY_STAFF_LEVEL, hi->staff_level); if (hi->language != lang_C) saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name); saxdb_write_string(ctx, KEY_PASSWD, hi->passwd); @@ -3056,6 +3536,21 @@ nickserv_saxdb_write(struct saxdb_context *ctx) { flags[0] = hi->userlist_style; flags[1] = 0; saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags); + if(hi->authlog) { + saxdb_start_record(ctx, KEY_AUTHLOG, 0); + struct authlogEntry *authlog; + int i = 0; + for(authlog = hi->authlog; authlog; authlog = authlog->next) { + saxdb_start_record(ctx, strtab(++i), 0); + saxdb_write_int(ctx, KEY_AUTHLOG_LOGIN_TIME, authlog->login_time); + saxdb_write_int(ctx, KEY_AUTHLOG_LOGOUT_TIME, authlog->logout_time); + saxdb_write_string(ctx, KEY_AUTHLOG_HOSTMASK, authlog->hostmask); + if(authlog->quit_reason) + saxdb_write_string(ctx, KEY_AUTHLOG_QUIT_REASON, authlog->quit_reason); + saxdb_end_record(ctx); + } + saxdb_end_record(ctx); //END KEY_AUTHLOG + } saxdb_end_record(ctx); } return 0; @@ -3174,6 +3669,10 @@ static NICKSERV_FUNC(cmd_merge) if (hi_from->opserv_level > hi_to->opserv_level) hi_to->opserv_level = hi_from->opserv_level; + /* Do they get a staff level promotion? */ + if (hi_from->staff_level > hi_to->staff_level) + hi_to->staff_level = hi_from->staff_level; + /* What about last seen time? */ if (hi_from->lastseen > hi_to->lastseen) hi_to->lastseen = hi_from->lastseen; @@ -3201,7 +3700,22 @@ static NICKSERV_FUNC(cmd_merge) return 1; } +#define NICKSERV_DISCRIM_FIELDS_AUTH 0x01 +#define NICKSERV_DISCRIM_FIELDS_EMAIL 0x02 +#define NICKSERV_DISCRIM_FIELDS_SEEN 0x04 +#define NICKSERV_DISCRIM_FIELDS_ACCESS 0x08 +#define NICKSERV_DISCRIM_FIELDS_FAKEHOST 0x10 +#define NICKSERV_DISCRIM_FIELDS_WEBSITE 0x20 +#define NICKSERV_DISCRIM_FIELDS_DEVNULL 0x40 + +#define NICKSERV_DISCRIM_FIELD_COUNT 7 + struct nickserv_discrim { + unsigned int show_fields; + struct helpfile_table *output_table; + int output_table_pos; + unsigned int output_table_free_fields; + unsigned long flags_on, flags_off; unsigned long min_registered, max_registered; unsigned long lastseen; @@ -3213,11 +3727,13 @@ struct nickserv_discrim { const char *hostmask; const char *fakehostmask; const char *fakeidentmask; + const char *website; + const char *devnullclass; const char *handlemask; const char *emailmask; }; -typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi); +typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi, struct nickserv_discrim *discrim); struct discrim_apply_info { struct nickserv_discrim *discrim; @@ -3252,6 +3768,40 @@ nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[]) discrim->limit = strtoul(argv[++i], NULL, 0); } else if (!irccasecmp(argv[i], "flags")) { nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off); + } else if (!irccasecmp(argv[i], "fields")) { + char *fields = argv[++i]; + char *delimiter = strstr(fields, ","); + while(1) { + if(delimiter) + *delimiter = '\0'; + if(!irccasecmp(fields, "auth")) + discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_AUTH; + else if(!irccasecmp(fields, "email")) + discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_EMAIL; + else if(!irccasecmp(fields, "seen")) + discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_SEEN; + else if(!irccasecmp(fields, "access")) + discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_ACCESS; + else if(!irccasecmp(fields, "fakehost")) + discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_FAKEHOST; + else if(!irccasecmp(fields, "website") && IsBot(user)) + discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_WEBSITE; + else if(!irccasecmp(fields, "devnull")) + discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_DEVNULL; + else { + send_message(user, nickserv, "MSG_INVALID_FIELD", fields); + goto fail; + } + if(delimiter) { + *delimiter = ','; + fields = delimiter+1; + if(*fields) { + delimiter = strstr(fields, ","); + continue; + } + } + break; + } } else if (!irccasecmp(argv[i], "registered")) { const char *cmp = argv[++i]; if (cmp[0] == '<') { @@ -3318,6 +3868,18 @@ nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[]) } else { discrim->fakeidentmask = argv[i]; } + } else if (!irccasecmp(argv[i], "website")) { + if (!irccasecmp(argv[++i], "*")) { + discrim->website = 0; + } else { + discrim->website = argv[i]; + } + } else if (!irccasecmp(argv[i], "devnull")) { + if (!irccasecmp(argv[++i], "*")) { + discrim->devnullclass = 0; + } else { + discrim->devnullclass = argv[i]; + } } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) { if (!irccasecmp(argv[++i], "*")) { discrim->handlemask = 0; @@ -3394,6 +3956,8 @@ nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi) || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask)) || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask))) || (discrim->fakeidentmask && (!hi->fakeident || !match_ircglob(hi->fakeident, discrim->fakeidentmask))) + || (discrim->website && (!hi->website || !match_ircglob(hi->website, discrim->website))) + || (discrim->devnullclass && (!hi->devnull || !match_ircglob(hi->devnull, discrim->devnullclass))) || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask))) || (discrim->min_level > hi->opserv_level) || (discrim->max_level < hi->opserv_level) @@ -3439,7 +4003,7 @@ nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func ds it = next) { next = iter_next(it); if (nickserv_discrim_match(discrim, iter_data(it))) { - dsf(source, iter_data(it)); + dsf(source, iter_data(it), discrim); matched++; } } @@ -3447,18 +4011,51 @@ nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func ds } static void -search_print_func(struct userNode *source, struct handle_info *match) +search_print_func(struct userNode *source, struct handle_info *match, struct nickserv_discrim *discrim) { - send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle); + if(discrim->show_fields) { + //custom fields + if(discrim->output_table) { + discrim->output_table->contents[++discrim->output_table_pos] = malloc(discrim->output_table->width * sizeof(discrim->output_table->contents[0][0])); + int i = 0; + if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_AUTH) + discrim->output_table->contents[discrim->output_table_pos][i++] = match->handle; + if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_EMAIL) + discrim->output_table->contents[discrim->output_table_pos][i++] = (match->email_addr ? match->email_addr : "*"); + if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_SEEN) { + char *seen; + char seenBuf[INTERVALLEN]; + if(match->users) { + seen = "Here"; + } else if(match->lastseen == 0) { + seen = "Never"; + } else { + seen = intervalString(seenBuf, now - match->lastseen, source->handle_info); + } + discrim->output_table_free_fields |= 1 << i; + discrim->output_table->contents[discrim->output_table_pos][i++] = strdup(seen); + } + if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_ACCESS) + discrim->output_table->contents[discrim->output_table_pos][i++] = strtab(match->opserv_level); + if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_FAKEHOST) + discrim->output_table->contents[discrim->output_table_pos][i++] = (match->fakehost ? match->fakehost : "*"); + if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_WEBSITE) + discrim->output_table->contents[discrim->output_table_pos][i++] = (match->website ? match->website : "*"); + if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_DEVNULL) + discrim->output_table->contents[discrim->output_table_pos][i++] = (match->devnull ? match->devnull : "*"); + + } + } else + send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle); } static void -search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match)) +search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match), UNUSED_ARG(struct nickserv_discrim *discrim)) { } static void -search_unregister_func (struct userNode *source, struct handle_info *match) +search_unregister_func (struct userNode *source, struct handle_info *match, UNUSED_ARG(struct nickserv_discrim *discrim)) { if (oper_has_access(source, nickserv, match->opserv_level, 0)) nickserv_unregister_handle(match, source); @@ -3546,12 +4143,58 @@ static NICKSERV_FUNC(cmd_search) discrim->limit = INT_MAX; matches = nickserv_discrim_search(discrim, action, user); - + + if(discrim->show_fields) { + int width = 0; + int ii; + for(ii = 0; ii < NICKSERV_DISCRIM_FIELD_COUNT; ii++) { + if(discrim->show_fields & (1 << ii)) width++; + } + discrim->output_table = malloc(sizeof(discrim->output_table[0])); + discrim->output_table->length = matches+1; + discrim->output_table->width = width; + discrim->output_table->flags = TABLE_NO_FREE; + discrim->output_table->contents = malloc(discrim->output_table->length * sizeof(discrim->output_table->contents[0])); + discrim->output_table->contents[0] = malloc(discrim->output_table->width * sizeof(discrim->output_table->contents[0][0])); + + ii = 0; + if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_AUTH) + discrim->output_table->contents[0][ii++] = "Auth"; + if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_EMAIL) + discrim->output_table->contents[0][ii++] = "EMail"; + if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_SEEN) + discrim->output_table->contents[0][ii++] = "Seen"; + if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_ACCESS) + discrim->output_table->contents[0][ii++] = "Access"; + if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_FAKEHOST) + discrim->output_table->contents[0][ii++] = "Fakehost"; + if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_WEBSITE) + discrim->output_table->contents[0][ii++] = "Website"; + if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_DEVNULL) + discrim->output_table->contents[0][ii++] = "DevNull"; + + nickserv_discrim_search(discrim, action, user); + + table_send(nickserv, user->nick, 0, NULL, *discrim->output_table); + + for(ii = 1; ii < discrim->output_table->length; ++ii) { + int ij; + for(ij = 0; ij < NICKSERV_DISCRIM_FIELD_COUNT; ij++) { + if(discrim->output_table_free_fields & (1 << ij)) + free((char*)discrim->output_table->contents[ii][ij]); + } + free(discrim->output_table->contents[ii]); + } + free(discrim->output_table->contents[0]); + free(discrim->output_table->contents); + free(discrim->output_table); + } if (matches) reply("MSG_MATCH_COUNT", matches); else reply("MSG_NO_MATCHES"); + free(discrim); return 0; } @@ -3590,6 +4233,45 @@ static MODCMD_FUNC(cmd_checkemail) return 1; } +static int +nickserv_db_read_authlog(UNUSED_ARG(const char *key), void *data, void *extra) +{ + struct record_data *rd = data; + struct handle_info *hi = extra; + const char *str; + struct authlogEntry *authlog; + authlog = malloc(sizeof(*authlog)); + + str = database_get_data(rd->d.object, KEY_AUTHLOG_LOGIN_TIME, RECDB_QSTRING); + authlog->login_time = str ? strtoul(str, NULL, 0) : 0; + + str = database_get_data(rd->d.object, KEY_AUTHLOG_LOGOUT_TIME, RECDB_QSTRING); + authlog->logout_time = str ? strtoul(str, NULL, 0) : 0; + + str = database_get_data(rd->d.object, KEY_AUTHLOG_HOSTMASK, RECDB_QSTRING); + authlog->hostmask = str ? strdup(str) : NULL; + + str = database_get_data(rd->d.object, KEY_AUTHLOG_QUIT_REASON, RECDB_QSTRING); + authlog->quit_reason = str ? strdup(str) : NULL; + + authlog->user = NULL; + + authlog->next = NULL; + + //append it to the end of the list... + struct authlogEntry *authlog_entry; + if(!hi->authlog) { + hi->authlog = authlog; + } else { + for(authlog_entry = hi->authlog; authlog_entry; authlog_entry = authlog_entry->next) { + if(!authlog_entry->next) { + authlog_entry->next = authlog; + break; + } + } + } + return 0; +} static void nickserv_db_read_handle(const char *handle, dict_t obj) @@ -3637,9 +4319,17 @@ nickserv_db_read_handle(const char *handle, dict_t obj) hi->language = language_find(str ? str : "C"); str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING); hi->opserv_level = str ? strtoul(str, NULL, 0) : 0; + str = database_get_data(obj, KEY_STAFF_LEVEL, RECDB_QSTRING); + hi->staff_level = str ? strtoul(str, NULL, 0) : 0; str = database_get_data(obj, KEY_INFO, RECDB_QSTRING); if (str) hi->infoline = strdup(str); + str = database_get_data(obj, KEY_WEBSITE, RECDB_QSTRING); + if (str) + hi->website = strdup(str); + str = database_get_data(obj, KEY_DEVNULL, RECDB_QSTRING); + if (str) + hi->devnull = strdup(str); str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING); hi->registered = str ? strtoul(str, NULL, 0) : now; str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING); @@ -3765,6 +4455,8 @@ nickserv_db_read_handle(const char *handle, dict_t obj) last_note = note; } } + if ((subdb = database_get_data(obj, KEY_AUTHLOG, RECDB_OBJECT))) + dict_foreach(subdb, nickserv_db_read_authlog, hi); } static int @@ -3917,6 +4609,8 @@ nickserv_conf_read(void) nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200; str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING); nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900; + str = database_get_data(conf_node, KEY_MODSTAFF_LEVEL, RECDB_QSTRING); + nickserv_conf.modstaff_level = str ? strtoul(str, NULL, 0) : 800; str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING); nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1; str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING); @@ -3943,6 +4637,8 @@ nickserv_conf_read(void) 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_MAX_AUTHLOG_LEN, RECDB_QSTRING); + nickserv_conf.max_authlog_len = str ? strtoul(str, NULL, 0) : 30; 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); @@ -4019,7 +4715,7 @@ nickserv_conf_read(void) str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING); nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600; str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING); - nickserv_conf.titlehost_suffix = str ? str : "example.net"; + titlehost_suffix = str ? str : "example.net"; str = conf_get_data("server/network", RECDB_QSTRING); nickserv_conf.network_name = str ? str : "some IRC network"; if (!nickserv_conf.auth_policer_params) { @@ -4069,6 +4765,29 @@ nickserv_reclaim_p(void *data) { static void check_user_nick(struct userNode *user) { + //check if this user is a pending LOC user + if(pendingLOCUsers) { + struct pendingLOCUser *pending, *next, *prev = NULL; + int remove; + for(pending = pendingLOCUsers; pending; pending = next) { + next = pending->next; + remove = 0; + if(user->handle_info == pending->handle_info) { + pending->authlog->user = user; + free((char*) pending->authlog->hostmask); + pending->authlog->hostmask = generate_hostmask(user, GENMASK_USENICK|GENMASK_STRICT_IDENT|GENMASK_NO_HIDING|GENMASK_STRICT_HOST); + remove = 1; + } else if(now - pending->time > 10) + remove = 1; + if(remove) { + if(prev) + prev->next = next; + else + pendingLOCUsers = next; + free(pending); + } + } + } struct nick_info *ni; user->modes &= ~FLAGS_REGNICK; if (!(ni = get_nick_info(user->nick))) @@ -4129,8 +4848,20 @@ handle_nick_change(struct userNode *user, const char *old_nick) } void -nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why)) +nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), const char *why) { + if(user->handle_info) { + //check if theres an open authlog entry + struct authlogEntry *authlog; + for(authlog = user->handle_info->authlog; authlog; authlog = authlog->next) { + if(authlog->user == user) { + authlog->user = NULL; + authlog->logout_time = now; + authlog->quit_reason = strdup(why); + break; + } + } + } dict_remove(nickserv_allow_auth_dict, user->nick); timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN); set_user_handle_info(user, NULL, 0); @@ -4185,6 +4916,12 @@ nickserv_db_cleanup(void) regfree(&nickserv_conf.valid_handle_regex); if (nickserv_conf.valid_nick_regex_set) regfree(&nickserv_conf.valid_nick_regex); + struct pendingLOCUser *pending, *next; + for(pending = pendingLOCUsers; pending; pending = next) { + next = pending->next; + free(pending); + } + pendingLOCUsers = NULL; } void @@ -4257,6 +4994,8 @@ init_nickserv(const char *nick) nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0); nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0); nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0); + nickserv_define_func("AUTHLOG", cmd_authlog, -1, 1, 0); + nickserv_define_func("OAUTHLOG", cmd_oauthlog, 0, 1, 0); /* other options */ dict_insert(nickserv_opt_dict, "INFO", opt_info); dict_insert(nickserv_opt_dict, "WIDTH", opt_width); @@ -4264,13 +5003,18 @@ init_nickserv(const char *nick) dict_insert(nickserv_opt_dict, "COLOR", opt_color); dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg); dict_insert(nickserv_opt_dict, "STYLE", opt_style); + dict_insert(nickserv_opt_dict, "AUTOHIDE", opt_autohide); dict_insert(nickserv_opt_dict, "PASS", opt_password); dict_insert(nickserv_opt_dict, "PASSWORD", opt_password); dict_insert(nickserv_opt_dict, "FLAGS", opt_flags); + dict_insert(nickserv_opt_dict, "WEBSITE", opt_website); + dict_insert(nickserv_opt_dict, "DEVNULL", opt_devnull); dict_insert(nickserv_opt_dict, "ACCESS", opt_level); dict_insert(nickserv_opt_dict, "LEVEL", opt_level); + dict_insert(nickserv_opt_dict, "STAFF", opt_staff_level); + dict_insert(nickserv_opt_dict, "STAFF_LEVEL", opt_staff_level); dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet); - if (nickserv_conf.titlehost_suffix) { + if (titlehost_suffix) { dict_insert(nickserv_opt_dict, "TITLE", opt_title); dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost); dict_insert(nickserv_opt_dict, "FAKEIDENT", opt_fakeident);