X-Git-Url: http://git.pk910.de/?a=blobdiff_plain;f=src%2Fnickserv.c;h=5da64a780a3291a57df60ac1684492d401cdaee7;hb=13698718d16cae97ed4df135fc20af386883b241;hp=894c34badc74ba68de1d23c94652ffc41c176992;hpb=93e4294cd139ae41eefba84129372b7f1aadf730;p=srvx.git diff --git a/src/nickserv.c b/src/nickserv.c index 894c34b..5da64a7 100644 --- a/src/nickserv.c +++ b/src/nickserv.c @@ -1,5 +1,5 @@ /* nickserv.c - Nick/authentication service - * Copyright 2000-2006 srvx Development Team + * Copyright 2000-2008 srvx Development Team * * This file is part of srvx. * @@ -48,14 +48,16 @@ #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" +#define KEY_SET_FAKEIDENT_LEVEL "set_fakeident_level" #define KEY_TITLEHOST_SUFFIX "titlehost_suffix" #define KEY_FLAG_LEVELS "flag_levels" -#define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq" +#define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq" #define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq" -#define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay" +#define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay" #define KEY_ACCOUNT_EXPIRE_DELAY "account_expire_delay" #define KEY_NOCHAN_HANDLE_EXPIRE_DELAY "nochan_handle_expire_delay" #define KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY "nochan_account_expire_delay" @@ -73,16 +75,21 @@ #define KEY_EMAIL_SEARCH_LEVEL "email_search_level" #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" @@ -100,20 +107,26 @@ #define KEY_TABLE_WIDTH "table_width" #define KEY_MAXLOGINS "maxlogins" #define KEY_FAKEHOST "fakehost" +#define KEY_FAKEIDENT "fakeident" #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 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-_" +#define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_" #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME) #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[]) typedef OPTION_FUNC(option_func_t); -DEFINE_LIST(handle_info_list, struct handle_info*); +DEFINE_LIST(handle_info_list, struct handle_info*) #define NICKSERV_MIN_PARMS(N) do { \ if (argc < N) { \ @@ -158,6 +171,7 @@ static const struct message_entry msgtab[] = { { "NSMSG_COOKIE_LIVE", "Account $b%s$b already has a cookie active. Please either finish using that cookie, wait for it to expire, or auth to the account and use the $bdelcookie$b command." }, { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." }, { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." }, + { "NSMSG_NO_COOKIE_FOREIGN", "The account $b%s$b does not have any cookie issued right now." }, { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." }, { "NSMSG_BAD_COOKIE", "That cookie is not the right one. Please make sure you are copying it EXACTLY from the email; it is case-sensitive, so $bABC$b is different from $babc$b." }, { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." }, @@ -169,6 +183,7 @@ static const struct message_entry msgtab[] = { { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." }, { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." }, { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." }, + { "NSMSG_ATE_COOKIE_FOREIGN", "I ate the cookie for account $b%s$b. It may now have another." }, { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." }, { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." }, { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." }, @@ -189,7 +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" }, @@ -204,9 +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" }, @@ -274,7 +297,7 @@ static const struct message_entry msgtab[] = { { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." }, { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." }, { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." }, - { "NSMSG_DB_MERGED", "$N merged DB from %s (in "FMT_TIME_T".%03lu seconds)." }, + { "NSMSG_DB_MERGED", "$N merged DB from %s (in %lu.%03lu seconds)." }, { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." }, { "NSMSG_BAD_HANDLE", "Account $b%s$b not registered because it is in use by a network service, is too long, or contains invalid characters." }, { "NSMSG_BAD_NICK", "Nickname $b%s$b not registered because it is in use by a network service, is too long, or contains invalid characters." }, @@ -295,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" }, @@ -306,9 +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" }, @@ -324,6 +353,9 @@ static const struct message_entry msgtab[] = { { "NSEMAIL_ALLOWAUTH_BODY", "This email has been sent to let you authenticate (auth) to account %5$s on %1$s. Your cookie is %2$s.\nTo auth to that account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nIf you did NOT request this authorization, you do not need to do anything. Please contact the %1$s staff if you have questions." }, { "CHECKPASS_YES", "Yes." }, { "CHECKPASS_NO", "No." }, + { "CHECKEMAIL_NOT_SET", "No email set." }, + { "CHECKEMAIL_YES", "Yes." }, + { "CHECKEMAIL_NO", "No." }, { NULL, NULL } }; @@ -337,6 +369,11 @@ static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum r static void nickserv_reclaim_p(void *data); static int nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask); +enum handle_ts_mode { + TS_IGNORE, + TS_IRCU +}; + static struct { unsigned int disable_nicks : 1; unsigned int valid_handle_regex_set : 1; @@ -360,26 +397,39 @@ 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; + unsigned long set_fakeident_level; 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; struct policer_params *auth_policer_params; enum reclaim_action reclaim_action; enum reclaim_action auto_reclaim_action; + enum handle_ts_mode handle_ts_mode; unsigned long auto_reclaim_delay; unsigned char default_maxlogins; 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; @@ -396,19 +446,18 @@ canonicalize_hostmask(char *mask) { char *out = mask, *temp; if ((temp = strchr(mask, '!'))) { - temp++; - while (*temp) *out++ = *temp++; - *out++ = 0; + temp++; + while (*temp) *out++ = *temp++; + *out++ = 0; } return mask; } static struct handle_info * -register_handle(const char *handle, const char *passwd, UNUSED_ARG(unsigned long id)) +register_handle(const char *handle, const char *passwd, unsigned long id) { struct handle_info *hi; -#ifdef WITH_PROTOCOL_BAHAMUT char id_base64[IDLEN + 1]; do { @@ -432,7 +481,6 @@ register_handle(const char *handle, const char *passwd, UNUSED_ARG(unsigned long id = 0; } } while(!id); -#endif hi = calloc(1, sizeof(*hi)); hi->userlist_style = HI_DEFAULT_STYLE; @@ -441,10 +489,10 @@ register_handle(const char *handle, const char *passwd, UNUSED_ARG(unsigned long hi->infoline = NULL; dict_insert(nickserv_handle_dict, hi->handle, hi); -#ifdef WITH_PROTOCOL_BAHAMUT hi->id = id; + hi->website = NULL; + hi->devnull = NULL; dict_insert(nickserv_id_dict, strdup(id_base64), hi); -#endif return hi; } @@ -473,15 +521,15 @@ delete_nick(struct nick_info *ni) } /* Remove ni from the nick_info linked list. */ if (ni == ni->owner->nicks) { - ni->owner->nicks = ni->next; + ni->owner->nicks = ni->next; } else { - last = ni->owner->nicks; - next = last->next; - while (next != ni) { - last = next; - next = last->next; - } - last->next = next->next; + last = ni->owner->nicks; + next = last->next; + while (next != ni) { + last = next; + next = last->next; + } + last->next = next->next; } dict_remove(nickserv_nick_dict, ni->nick); } @@ -493,13 +541,13 @@ void reg_unreg_func(unreg_func_t func) { if (unreg_func_used == unreg_func_size) { - if (unreg_func_size) { - unreg_func_size <<= 1; - unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t)); - } else { - unreg_func_size = 8; - unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t)); - } + if (unreg_func_size) { + unreg_func_size <<= 1; + unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t)); + } else { + unreg_func_size = 8; + unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t)); + } } unreg_func_list[unreg_func_used++] = func; } @@ -517,13 +565,10 @@ static void free_handle_info(void *vhi) { struct handle_info *hi = vhi; - -#ifdef WITH_PROTOCOL_BAHAMUT char id[IDLEN + 1]; inttobase64(id, hi->id, IDLEN); dict_remove(nickserv_id_dict, id); -#endif free_string_list(hi->masks); assert(!hi->users); @@ -533,6 +578,9 @@ 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); nickserv_free_cookie(hi->cookie); @@ -548,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); } @@ -606,21 +674,38 @@ oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_le } if (!IsOper(user) && (!IsHelping(user) || min_level)) { - if (!quiet) + if (!quiet) send_message(user, bot, "NSMSG_NO_ACCESS"); - return 0; + return 0; } if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) { - if (!quiet) + if (!quiet) send_message(user, bot, "MSG_OPER_SUSPENDED"); - return 0; + return 0; } if (user->handle_info->opserv_level < min_level) { - if (!quiet) + if (!quiet) send_message(user, bot, "NSMSG_NO_ACCESS"); - return 0; + return 0; + } + + 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; @@ -636,7 +721,7 @@ is_valid_handle(const char *handle) return 0; /* check against maximum length */ if (strlen(handle) > NICKSERV_HANDLE_LEN) - return 0; + return 0; /* for consistency, only allow account names that could be nicks */ if (!is_valid_nick(handle)) return 0; @@ -680,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 * @@ -718,9 +845,9 @@ smart_get_handle_info(struct userNode *service, struct userNode *user, const cha return 0; } if (IsLocal(target)) { - if (IsService(target)) + if (IsService(target)) send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick); - else + else send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick); return 0; } @@ -756,8 +883,8 @@ get_victim_oper(struct userNode *user, const char *target) if (!(hi = smart_get_handle_info(nickserv, user, target))) return 0; if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) { - send_message(user, nickserv, "MSG_OPER_SUSPENDED"); - return 0; + send_message(user, nickserv, "MSG_OPER_SUSPENDED"); + return 0; } return oper_outranks(user, hi) ? hi : NULL; } @@ -768,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++) @@ -776,8 +903,8 @@ valid_user_for(struct userNode *user, struct handle_info *hi) return 1; /* If they are allowauthed to this account, allow it (removing the aa). */ if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) { - dict_remove(nickserv_allow_auth_dict, user->nick); - return 2; + dict_remove(nickserv_allow_auth_dict, user->nick); + return 2; } /* The user is not allowed to use this account. */ return 0; @@ -808,16 +935,16 @@ is_secure_password(const char *handle, const char *pass, struct userNode *user) return 0; } for (i=0; ifakehost[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; } +static char * +generate_fakeident(struct handle_info *handle, struct userNode *user) +{ + static char buffer[USERLEN+1]; + + if (!handle->fakeident) { + if (!user) + return NULL; + safestrncpy(buffer, user->ident, sizeof(buffer)); + return buffer; + } + return handle->fakeident; +} + static void -apply_fakehost(struct handle_info *handle) +apply_fakehost(struct handle_info *handle, struct userNode *user) { struct userNode *target; - char *fake; + char *fakehost, *fakeident; if (!handle->users) return; - fake = generate_fakehost(handle); - for (target = handle->users; target; target = target->next_authed) - assign_fakehost(target, fake, 1); + + fakehost = generate_fakehost(handle); + + if (user) { + fakeident = generate_fakeident(handle, user); + assign_fakehost(user, fakehost, fakeident, 0, 1); + return; + } + + for (target = handle->users; target; target = target->next_authed) { + fakeident = generate_fakeident(handle, target); + assign_fakehost(target, fakehost, fakeident, 0, 1); + } } static void @@ -904,25 +1059,28 @@ set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp) return; if (user->handle_info) { - struct userNode *other; + struct userNode *other; - if (IsHelper(user)) + if (IsHelper(user)) userList_remove(&curr_helpers, user); - /* remove from next_authed linked list */ - if (user->handle_info->users == user) { - user->handle_info->users = user->next_authed; - } else { - for (other = user->handle_info->users; - other->next_authed != user; - other = other->next_authed) ; - other->next_authed = user->next_authed; - } + /* remove from next_authed linked list */ + if (user->handle_info->users == user) { + user->handle_info->users = user->next_authed; + } else if (user->handle_info->users != NULL) { + for (other = user->handle_info->users; + other->next_authed != user; + other = other->next_authed) ; + other->next_authed = user->next_authed; + } else { + /* No users authed to the account - can happen if they get + * killed for authing. */ + } /* if nobody left on old handle, and they're not an oper, remove !god */ if (!user->handle_info->users && !user->handle_info->opserv_level) HANDLE_CLEAR_FLAG(user->handle_info, HELPING); /* record them as being last seen at this time */ - user->handle_info->lastseen = now; + user->handle_info->lastseen = now; /* and record their hostmask */ snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname); } @@ -930,8 +1088,11 @@ set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp) user->handle_info = hi; if (hi && !hi->users && !hi->opserv_level) HANDLE_CLEAR_FLAG(hi, HELPING); - for (n=0; ndead) + return; + } if (hi) { struct nick_info *ni; @@ -941,43 +1102,33 @@ set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp) for (other = hi->users; other; other = other->next_authed) send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname); } - user->next_authed = hi->users; - hi->users = user; - hi->lastseen = now; - if (IsHelper(user)) + user->next_authed = hi->users; + hi->users = user; + hi->lastseen = now; + if (IsHelper(user) && !userList_contains(&curr_helpers, user)) userList_append(&curr_helpers, user); - if (hi->fakehost || old_info) - apply_fakehost(hi); + if (hi->fakehost || hi->fakeident || old_info) + apply_fakehost(hi, user); if (stamp) { -#ifdef WITH_PROTOCOL_BAHAMUT - /* Stamp users with their account ID. */ - char id[IDLEN + 1]; - inttobase64(id, hi->id, IDLEN); -#elif WITH_PROTOCOL_P10 - /* Stamp users with their account name. */ - char *id = hi->handle; -#else - const char *id = "???"; -#endif if (!nickserv_conf.disable_nicks) { - struct nick_info *ni; - for (ni = hi->nicks; ni; ni = ni->next) { - if (!irccasecmp(user->nick, ni->nick)) { + struct nick_info *ni2; + for (ni2 = hi->nicks; ni2; ni2 = ni2->next) { + if (!irccasecmp(user->nick, ni2->nick)) { user->modes |= FLAGS_REGNICK; break; } } } - StampUser(user, id); + StampUser(user, hi->handle, hi->registered, hi->id); } if ((ni = get_nick_info(user->nick)) && (ni->owner == hi)) timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN); } else { /* We cannot clear the user's account ID, unfortunately. */ - user->next_authed = NULL; + user->next_authed = NULL; } } @@ -989,8 +1140,8 @@ nickserv_register(struct userNode *user, struct userNode *settee, const char *ha char crypted[MD5_CRYPT_LENGTH]; if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) { - send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle); - return 0; + send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle); + return 0; } if (!is_secure_password(handle, passwd, user)) @@ -1155,9 +1306,9 @@ static NICKSERV_FUNC(cmd_register) int no_auth; if (!IsOper(user) && !dict_size(nickserv_handle_dict)) { - /* Require the first handle registered to belong to someone +o. */ - reply("NSMSG_REQUIRE_OPER"); - return 0; + /* Require the first handle registered to belong to someone +o. */ + reply("NSMSG_REQUIRE_OPER"); + return 0; } if (user->handle_info) { @@ -1167,7 +1318,7 @@ static NICKSERV_FUNC(cmd_register) if (IsRegistering(user)) { reply("NSMSG_ALREADY_REGISTERING"); - return 0; + return 0; } if (IsStamped(user)) { @@ -1231,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]); } @@ -1263,46 +1415,67 @@ 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], '@')) { - mask = canonicalize_hostmask(strdup(argv[3])); - if (argc > 4) { - settee = GetUserH(argv[4]); - if (!settee) { - reply("MSG_NICK_UNKNOWN", argv[4]); + mask = canonicalize_hostmask(strdup(argv[3])); + if (argc > 4) { + settee = GetUserH(argv[4]); + if (!settee) { + reply("MSG_NICK_UNKNOWN", argv[4]); free(mask); - return 0; - } - } else { - settee = NULL; - } + return 0; + } + } else { + settee = NULL; + } } else if ((settee = GetUserH(argv[3]))) { - mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT); + mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT); } else { - reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]); - return 0; + reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]); + return 0; } if (settee && settee->handle_info) { reply("NSMSG_USER_PREV_AUTH", settee->nick); 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; } @@ -1313,6 +1486,7 @@ static NICKSERV_FUNC(cmd_handleinfo) struct userNode *target, *next_un; struct handle_info *hi; const char *nsmsg_none; + time_t feh; if (argc < 2) { if (!(hi = user->handle_info)) { @@ -1325,19 +1499,23 @@ static NICKSERV_FUNC(cmd_handleinfo) nsmsg_none = handle_find_message(hi, "MSG_NONE"); reply("NSMSG_HANDLEINFO_ON", hi->handle); -#ifdef WITH_PROTOCOL_BAHAMUT - reply("NSMSG_HANDLEINFO_ID", hi->id); -#endif - reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi->registered)); + feh = hi->registered; + reply("NSMSG_HANDLEINFO_REGGED", ctime(&feh)); if (!hi->users) { - intervalString(buff, now - hi->lastseen, user->handle_info); - reply("NSMSG_HANDLEINFO_LASTSEEN", buff); + intervalString(buff, now - hi->lastseen, user->handle_info); + reply("NSMSG_HANDLEINFO_LASTSEEN", buff); } else { - reply("NSMSG_HANDLEINFO_LASTSEEN_NOW"); + reply("NSMSG_HANDLEINFO_LASTSEEN_NOW"); } 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"); @@ -1368,7 +1546,10 @@ static NICKSERV_FUNC(cmd_handleinfo) reply(type); } - if (oper_has_access(user, cmd->parent->bot, 0, 1) || IsSupport(user)) { + if (oper_has_access(user, cmd->parent->bot, 601, 1)) + reply("NSMSG_HANDLEINFO_ID", hi->id); + + if (oper_has_access(user, cmd->parent->bot, 0, 1) || IsStaff(user)) { if (!hi->notes) { reply("NSMSG_HANDLEINFO_NO_NOTES"); } else { @@ -1389,16 +1570,16 @@ static NICKSERV_FUNC(cmd_handleinfo) } if (hi->flags) { - unsigned long flen = 1; - char flags[34]; /* 32 bits possible plus '+' and '\0' */ - flags[0] = '+'; - for (i=0, flen=1; handle_flags[i]; i++) - if (hi->flags & 1 << i) + unsigned long flen = 1; + char flags[34]; /* 32 bits possible plus '+' and '\0' */ + flags[0] = '+'; + for (i=0, flen=1; handle_flags[i]; i++) + if (hi->flags & 1 << i) flags[flen++] = handle_flags[i]; - flags[flen] = 0; - reply("NSMSG_HANDLEINFO_FLAGS", flags); + flags[flen] = 0; + reply("NSMSG_HANDLEINFO_FLAGS", flags); } else { - reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none); + reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none); } if (HANDLE_FLAGGED(hi, SUPPORT_HELPER) @@ -1407,8 +1588,12 @@ static NICKSERV_FUNC(cmd_handleinfo) reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_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); @@ -1416,28 +1601,28 @@ static NICKSERV_FUNC(cmd_handleinfo) reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN"); if (nickserv_conf.disable_nicks) { - /* nicks disabled; don't show anything about registered nicks */ + /* nicks disabled; don't show anything about registered nicks */ } else if (hi->nicks) { - struct nick_info *ni, *next_ni; - for (ni = hi->nicks; ni; ni = next_ni) { - herelen = strlen(ni->nick); - if (pos + herelen + 1 > ArrayLength(buff)) { - next_ni = ni; - goto print_nicks_buff; - } else { - next_ni = ni->next; - } - memcpy(buff+pos, ni->nick, herelen); - pos += herelen; buff[pos++] = ' '; - if (!next_ni) { - print_nicks_buff: - buff[pos-1] = 0; - reply("NSMSG_HANDLEINFO_NICKS", buff); - pos = 0; - } - } + struct nick_info *ni, *next_ni; + for (ni = hi->nicks; ni; ni = next_ni) { + herelen = strlen(ni->nick); + if (pos + herelen + 1 > ArrayLength(buff)) { + next_ni = ni; + goto print_nicks_buff; + } else { + next_ni = ni->next; + } + memcpy(buff+pos, ni->nick, herelen); + pos += herelen; buff[pos++] = ' '; + if (!next_ni) { + print_nicks_buff: + buff[pos-1] = 0; + reply("NSMSG_HANDLEINFO_NICKS", buff); + pos = 0; + } + } } else { - reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none); + reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none); } if (hi->masks->used) { @@ -1461,50 +1646,50 @@ static NICKSERV_FUNC(cmd_handleinfo) } if (hi->channels) { - struct userData *channel, *next; - char *name; - - for (channel = hi->channels; channel; channel = next) { - next = channel->u_next; - name = channel->channel->channel->name; - herelen = strlen(name); - if (pos + herelen + 7 > ArrayLength(buff)) { - next = channel; + struct userData *chan, *next; + char *name; + + for (chan = hi->channels; chan; chan = next) { + next = chan->u_next; + name = chan->channel->channel->name; + herelen = strlen(name); + if (pos + herelen + 7 > ArrayLength(buff)) { + next = chan; goto print_chans_buff; - } - if (IsUserSuspended(channel)) + } + if (IsUserSuspended(chan)) buff[pos++] = '-'; - pos += sprintf(buff+pos, "%d:%s ", channel->access, name); - if (next == NULL) { - print_chans_buff: - buff[pos-1] = 0; - reply("NSMSG_HANDLEINFO_CHANNELS", buff); - pos = 0; - } - } + pos += sprintf(buff+pos, "%d:%s ", chan->access, name); + if (next == NULL) { + print_chans_buff: + buff[pos-1] = 0; + reply("NSMSG_HANDLEINFO_CHANNELS", buff); + pos = 0; + } + } } else { - reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none); + reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none); } for (target = hi->users; target; target = next_un) { - herelen = strlen(target->nick); - if (pos + herelen + 1 > ArrayLength(buff)) { - next_un = target; - goto print_cnick_buff; - } else { - next_un = target->next_authed; - } - memcpy(buff+pos, target->nick, herelen); - pos += herelen; buff[pos++] = ' '; - if (!next_un) { - print_cnick_buff: - buff[pos-1] = 0; - reply("NSMSG_HANDLEINFO_CURRENT", buff); - pos = 0; - } + herelen = strlen(target->nick); + if (pos + herelen + 1 > ArrayLength(buff)) { + next_un = target; + goto print_cnick_buff; + } else { + next_un = target->next_authed; + } + memcpy(buff+pos, target->nick, herelen); + pos += herelen; buff[pos++] = ' '; + if (!next_un) { + print_cnick_buff: + buff[pos-1] = 0; + reply("NSMSG_HANDLEINFO_CURRENT", buff); + pos = 0; + } } - return 1; + return 1 | ((hi != user->handle_info) ? CMD_LOG_STAFF : 0); } static NICKSERV_FUNC(cmd_userinfo) @@ -1513,13 +1698,13 @@ static NICKSERV_FUNC(cmd_userinfo) NICKSERV_MIN_PARMS(2); if (!(target = GetUserH(argv[1]))) { - reply("MSG_NICK_UNKNOWN", argv[1]); - return 0; + reply("MSG_NICK_UNKNOWN", argv[1]); + return 0; } if (target->handle_info) - reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle); + reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle); else - reply("NSMSG_USERINFO_NOT_AUTHED", target->nick); + reply("NSMSG_USERINFO_NOT_AUTHED", target->nick); return 1; } @@ -1529,8 +1714,8 @@ static NICKSERV_FUNC(cmd_nickinfo) NICKSERV_MIN_PARMS(2); if (!(ni = get_nick_info(argv[1]))) { - reply("MSG_NICK_UNKNOWN", argv[1]); - return 0; + reply("MSG_NICK_UNKNOWN", argv[1]); + return 0; } reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle); return 1; @@ -1579,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]); @@ -1589,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; } @@ -1610,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; @@ -1709,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) @@ -1719,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; @@ -1746,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; @@ -1851,6 +2220,26 @@ static NICKSERV_FUNC(cmd_delcookie) return 1; } +static NICKSERV_FUNC(cmd_odelcookie) +{ + struct handle_info *hi; + + NICKSERV_MIN_PARMS(2); + + if (!(hi = get_victim_oper(user, argv[1]))) + return 0; + + if (!hi->cookie) { + reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle); + return 0; + } + + nickserv_eat_cookie(hi->cookie); + reply("NSMSG_ATE_COOKIE_FOREIGN", hi->handle); + return 1; +} + + static NICKSERV_FUNC(cmd_resetpass) { struct handle_info *hi; @@ -1971,8 +2360,8 @@ static NICKSERV_FUNC(cmd_oregnick) { } ni = dict_find(nickserv_nick_dict, nick, NULL); if (ni) { - reply("NSMSG_NICK_EXISTS", nick); - return 0; + reply("NSMSG_NICK_EXISTS", nick); + return 0; } register_nick(nick, target); reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle); @@ -1995,8 +2384,8 @@ static NICKSERV_FUNC(cmd_regnick) { } ni = dict_find(nickserv_nick_dict, user->nick, NULL); if (ni) { - reply("NSMSG_NICK_EXISTS", user->nick); - return 0; + reply("NSMSG_NICK_EXISTS", user->nick); + return 0; } register_nick(user->nick, user->handle_info); reply("NSMSG_REGNICK_SUCCESS", user->nick); @@ -2016,8 +2405,8 @@ static NICKSERV_FUNC(cmd_pass) if (!is_secure_password(hi->handle, new_pass, user)) return 0; if (!checkpass(old_pass, hi->passwd)) { argv[1] = "BADPASS"; - reply("NSMSG_PASSWORD_INVALID"); - return 0; + reply("NSMSG_PASSWORD_INVALID"); + return 0; } cryptpass(new_pass, hi->passwd); argv[1] = "****"; @@ -2073,17 +2462,17 @@ nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_ { unsigned int i; for (i=0; imasks->used; i++) { - if (!strcmp(del_mask, hi->masks->list[i])) { - char *old_mask = hi->masks->list[i]; - if (hi->masks->used == 1 && !force) { - send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST"); - return 0; - } - hi->masks->list[i] = hi->masks->list[--hi->masks->used]; - send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask); - free(old_mask); - return 1; - } + if (!strcmp(del_mask, hi->masks->list[i])) { + char *old_mask = hi->masks->list[i]; + if (hi->masks->used == 1 && !force) { + send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST"); + return 0; + } + hi->masks->list[i] = hi->masks->list[--hi->masks->used]; + send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask); + free(old_mask); + return 1; + } } send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND"); return 0; @@ -2110,26 +2499,26 @@ nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const unsigned long added, removed, flag; for (added=removed=nn=0; str[nn]; nn++) { - switch (str[nn]) { - case '+': add = 1; break; - case '-': add = 0; break; - default: - if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) { - send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]); - return 0; - } + switch (str[nn]) { + case '+': add = 1; break; + case '-': add = 0; break; + default: + if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) { + send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]); + return 0; + } if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) { /* cheesy avoidance of looking up the flag name.. */ send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]); return 0; } flag = 1 << (pos - 1); - if (add) + if (add) added |= flag, removed &= ~flag; - else + else removed |= flag, added &= ~flag; - break; - } + break; + } } *padded = added; *premoved = removed; @@ -2154,14 +2543,10 @@ nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char * struct channelList *schannels; unsigned int ii; schannels = chanserv_support_channels(); - for (uNode = hi->users; uNode; uNode = uNode->next_authed) { - for (ii = 0; ii < schannels->used; ++ii) - if (GetUserMode(schannels->list[ii], uNode)) - break; - if (ii < schannels->used) + for (ii = 0; ii < schannels->used; ++ii) + if (find_handle_in_channel(schannels->list[ii], hi, NULL)) break; - } - if (!uNode) + if (ii == schannels->used) HANDLE_CLEAR_FLAG(hi, HELPING); } @@ -2185,15 +2570,15 @@ 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"); /* Do this so options are presented in a consistent order. */ for (i = 0; i < ArrayLength(set_display); ++i) - if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL))) - opt(user, hi, override, 0, NULL); + if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL))) + opt(user, hi, override, 0, NULL); } static NICKSERV_FUNC(cmd_set) @@ -2203,11 +2588,11 @@ static NICKSERV_FUNC(cmd_set) hi = user->handle_info; if (argc < 2) { - set_list(user, hi, 0); - return 1; + set_list(user, hi, 0); + return 1; } if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) { - reply("NSMSG_INVALID_OPTION", argv[1]); + reply("NSMSG_INVALID_OPTION", argv[1]); return 0; } return opt(user, hi, 0, argc-1, argv+1); @@ -2226,12 +2611,12 @@ static NICKSERV_FUNC(cmd_oset) return 0; if (argc < 3) { - set_list(user, hi, 0); - return 1; + set_list(user, hi, 0); + return 1; } if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) { - reply("NSMSG_INVALID_OPTION", argv[2]); + reply("NSMSG_INVALID_OPTION", argv[2]); return 0; } @@ -2247,12 +2632,12 @@ static OPTION_FUNC(opt_info) { const char *info; if (argc > 1) { - if ((argv[1][0] == '*') && (argv[1][1] == 0)) { + if ((argv[1][0] == '*') && (argv[1][1] == 0)) { free(hi->infoline); hi->infoline = NULL; - } else { - hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL)); - } + } else { + hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL)); + } } info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE"); @@ -2260,10 +2645,86 @@ 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) - hi->screen_width = strtoul(argv[1], NULL, 0); + hi->screen_width = strtoul(argv[1], NULL, 0); if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE)) hi->screen_width = MIN_LINE_SIZE; @@ -2277,7 +2738,7 @@ static OPTION_FUNC(opt_width) static OPTION_FUNC(opt_tablewidth) { if (argc > 1) - hi->table_width = strtoul(argv[1], NULL, 0); + hi->table_width = strtoul(argv[1], NULL, 0); if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE)) hi->table_width = MIN_LINE_SIZE; @@ -2291,14 +2752,14 @@ static OPTION_FUNC(opt_tablewidth) static OPTION_FUNC(opt_color) { if (argc > 1) { - if (enabled_string(argv[1])) - HANDLE_SET_FLAG(hi, MIRC_COLOR); + if (enabled_string(argv[1])) + HANDLE_SET_FLAG(hi, MIRC_COLOR); else if (disabled_string(argv[1])) - HANDLE_CLEAR_FLAG(hi, MIRC_COLOR); - else { - send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]); - return 0; - } + HANDLE_CLEAR_FLAG(hi, MIRC_COLOR); + else { + send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]); + return 0; + } } send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF")); @@ -2308,38 +2769,55 @@ static OPTION_FUNC(opt_color) static OPTION_FUNC(opt_privmsg) { if (argc > 1) { - if (enabled_string(argv[1])) - HANDLE_SET_FLAG(hi, USE_PRIVMSG); + if (enabled_string(argv[1])) + HANDLE_SET_FLAG(hi, USE_PRIVMSG); else if (disabled_string(argv[1])) - HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG); - else { - send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]); - return 0; - } + HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG); + else { + send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]); + return 0; + } } send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF")); 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; if (argc > 1) { - if (!irccasecmp(argv[1], "Zoot")) - hi->userlist_style = HI_STYLE_ZOOT; - else if (!irccasecmp(argv[1], "def")) - hi->userlist_style = HI_STYLE_DEF; + if (!irccasecmp(argv[1], "Zoot")) + hi->userlist_style = HI_STYLE_ZOOT; + else if (!irccasecmp(argv[1], "def")) + hi->userlist_style = HI_STYLE_DEF; } switch (hi->userlist_style) { case HI_STYLE_DEF: - style = "def"; - break; + style = "def"; + break; case HI_STYLE_ZOOT: default: - style = "Zoot"; + style = "Zoot"; } send_message(user, nickserv, "NSMSG_SET_STYLE", style); @@ -2349,14 +2827,16 @@ static OPTION_FUNC(opt_style) static OPTION_FUNC(opt_password) { if (!override) { - send_message(user, nickserv, "NSMSG_USE_CMD_PASS"); - return 0; + send_message(user, nickserv, "NSMSG_USE_CMD_PASS"); + return 0; } if (argc > 1) - cryptpass(argv[1], hi->passwd); + cryptpass(argv[1], hi->passwd); send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***"); + argv[1] = "****"; + return 1; } @@ -2366,12 +2846,12 @@ static OPTION_FUNC(opt_flags) unsigned int ii, flen; if (!override) { - send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]); - return 0; + send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]); + return 0; } if (argc > 1) - nickserv_apply_flags(user, hi, argv[1]); + nickserv_apply_flags(user, hi, argv[1]); for (ii = flen = 0; handle_flags[ii]; ii++) if (hi->flags & (1 << ii)) @@ -2489,13 +2969,31 @@ 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; if (!override) { - send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]); - return 0; + send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]); + return 0; } res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0; @@ -2503,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) { @@ -2543,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; } @@ -2556,7 +3068,7 @@ static OPTION_FUNC(opt_title) hi->fakehost[0] = '.'; strcpy(hi->fakehost+1, title); } - apply_fakehost(hi); + apply_fakehost(hi, NULL); } else if (hi->fakehost && (hi->fakehost[0] == '.')) title = hi->fakehost + 1; else @@ -2569,7 +3081,8 @@ static OPTION_FUNC(opt_title) static OPTION_FUNC(opt_fakehost) { - const char *fake; + char mask[USERLEN + HOSTLEN + 2]; + char *host, *ident; if (!override) { send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]); @@ -2577,34 +3090,109 @@ static OPTION_FUNC(opt_fakehost) } if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) { - fake = argv[1]; - if ((strlen(fake) > HOSTLEN) || (fake[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) { + /* 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 (ident && strlen(ident) > USERLEN) { + send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN); + return 0; + } + + if (host && ((strlen(host) > HOSTLEN) || (host[0] == '.'))) { send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN); return 0; } - free(hi->fakehost); - if (!strcmp(fake, "*")) - 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 + host = generate_fakehost(hi); + + if (ident) { + free(hi->fakeident); + if (!strcmp(ident, "*")) + hi->fakeident = NULL; + else + hi->fakeident = strdup(ident); + ident = hi->fakeident; + } + else + ident = generate_fakeident(hi, NULL); + + apply_fakehost(hi, NULL); + } else { + host = generate_fakehost(hi); + ident = generate_fakeident(hi, NULL); + } + if (!host) + host = (char *) user_find_message(user, "MSG_NONE"); + if(ident) + send_message(user, nickserv, "NSMSG_SET_FAKEIDENTHOST", ident, host); + else + send_message(user, nickserv, "NSMSG_SET_FAKEHOST", host); + return 1; +} + +static OPTION_FUNC(opt_fakeident) +{ + const char *ident; + + if (!override) { + send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]); + return 0; + } + + if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) { + ident = argv[1]; + if (strlen(ident) > USERLEN) { + send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN); + return 0; + } + free(hi->fakeident); + if (!strcmp(ident, "*")) + hi->fakeident = NULL; else - hi->fakehost = strdup(fake); - fake = hi->fakehost; - apply_fakehost(hi); + hi->fakeident = strdup(ident); + ident = hi->fakeident; + apply_fakehost(hi, NULL); } else - fake = generate_fakehost(hi); - if (!fake) - fake = user_find_message(user, "MSG_NONE"); - send_message(user, nickserv, "NSMSG_SET_FAKEHOST", fake); + ident = generate_fakeident(hi, NULL); /* NULL if no fake ident set */ + if (!ident) + ident = user_find_message(user, "MSG_NONE"); + send_message(user, nickserv, "NSMSG_SET_FAKEIDENT", ident); return 1; } 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]); @@ -2643,12 +3231,12 @@ static NICKSERV_FUNC(cmd_unregnick) nick = (argc < 2) ? user->nick : (const char*)argv[1]; ni = dict_find(nickserv_nick_dict, nick, NULL); if (!ni) { - reply("NSMSG_UNKNOWN_NICK", nick); - return 0; + reply("NSMSG_UNKNOWN_NICK", nick); + return 0; } if (hi != ni->owner) { - reply("NSMSG_NOT_YOUR_NICK", nick); - return 0; + reply("NSMSG_NOT_YOUR_NICK", nick); + return 0; } reply("NSMSG_UNREGNICK_SUCCESS", ni->nick); delete_nick(ni); @@ -2661,8 +3249,8 @@ static NICKSERV_FUNC(cmd_ounregnick) NICKSERV_MIN_PARMS(2); if (!(ni = get_nick_info(argv[1]))) { - reply("NSMSG_NICK_NOT_REGISTERED", argv[1]); - return 0; + reply("NSMSG_NICK_NOT_REGISTERED", argv[1]); + return 0; } if (!oper_outranks(user, ni->owner)) return 0; @@ -2684,8 +3272,8 @@ static NICKSERV_FUNC(cmd_unregister) nickserv_unregister_handle(hi, user); return 1; } else { - log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle); - reply("NSMSG_PASSWORD_INVALID"); + log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle); + reply("NSMSG_PASSWORD_INVALID"); return 0; } } @@ -2786,7 +3374,13 @@ static NICKSERV_FUNC(cmd_addnote) hi = get_victim_oper(user, argv[1]); if (!hi) return 0; - duration = ParseInterval(argv[2]); + if(!strcmp(argv[2], "0")) + duration = 0; + else if(!(duration = ParseInterval(argv[2]))) + { + reply("MSG_INVALID_DURATION", argv[2]); + return 0; + } if (duration > 2*365*86400) { reply("NSMSG_EXCESSIVE_DURATION", argv[2]); return 0; @@ -2846,9 +3440,7 @@ nickserv_saxdb_write(struct saxdb_context *ctx) { for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) { hi = iter_data(it); -#ifdef WITH_PROTOCOL_BAHAMUT - assert(hi->id); -#endif + assert(hi->id != 0); saxdb_start_record(ctx, iter_key(it), 0); if (hi->cookie) { struct handle_cookie *cookie = hi->cookie; @@ -2892,6 +3484,8 @@ nickserv_saxdb_write(struct saxdb_context *ctx) { saxdb_write_string(ctx, KEY_EPITHET, hi->epithet); if (hi->fakehost) saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost); + if (hi->fakeident) + saxdb_write_string(ctx, KEY_FAKEIDENT, hi->fakeident); if (hi->flags) { int ii, flen; @@ -2901,11 +3495,13 @@ nickserv_saxdb_write(struct saxdb_context *ctx) { flags[flen] = 0; saxdb_write_string(ctx, KEY_FLAGS, flags); } -#ifdef WITH_PROTOCOL_BAHAMUT saxdb_write_int(ctx, KEY_ID, hi->id); -#endif 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); @@ -2927,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); @@ -2938,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; @@ -3056,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; @@ -3069,6 +3686,8 @@ static NICKSERV_FUNC(cmd_merge) */ if (hi_from->fakehost && !hi_to->fakehost) hi_to->fakehost = strdup(hi_from->fakehost); + if (hi_from->fakeident && !hi_to->fakeident) + hi_to->fakeident = strdup(hi_from->fakeident); /* Notify of success. */ sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle); @@ -3081,10 +3700,25 @@ 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; - time_t min_registered, max_registered; - time_t lastseen; + unsigned long min_registered, max_registered; + unsigned long lastseen; unsigned int limit; int min_level, max_level; int min_karma, max_karma; @@ -3092,11 +3726,14 @@ struct nickserv_discrim { const char *nickmask; 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; @@ -3117,8 +3754,8 @@ nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[]) discrim->max_level = INT_MAX; discrim->limit = 50; discrim->min_registered = 0; - discrim->max_registered = INT_MAX; - discrim->lastseen = LONG_MAX; + discrim->max_registered = ULONG_MAX; + discrim->lastseen = ULONG_MAX; discrim->min_karma = INT_MIN; discrim->max_karma = INT_MAX; @@ -3131,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] == '<') { @@ -3174,12 +3845,12 @@ nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[]) goto fail; } discrim->hostmask_type = SUPERSET; - } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) { - if (i == argc - 1) { - send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]); - goto fail; - } - discrim->hostmask_type = LASTQUIT; + } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) { + if (i == argc - 1) { + send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]); + goto fail; + } + discrim->hostmask_type = LASTQUIT; } else { i--; discrim->hostmask_type = SUPERSET; @@ -3191,6 +3862,24 @@ nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[]) } else { discrim->fakehostmask = argv[i]; } + } else if (!irccasecmp(argv[i], "fakeident")) { + if (!irccasecmp(argv[++i], "*")) { + discrim->fakeidentmask = 0; + } 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; @@ -3266,6 +3955,9 @@ nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi) || (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->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) @@ -3284,8 +3976,8 @@ nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi) && !irccasecmp(discrim->hostmask, mask)) break; else if ((discrim->hostmask_type == SUPERSET) && (match_ircglobs(mask, discrim->hostmask))) break; - else if ((discrim->hostmask_type == LASTQUIT) - && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break; + else if ((discrim->hostmask_type == LASTQUIT) + && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break; } if (i==hi->masks->used) return 0; } @@ -3311,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++; } } @@ -3319,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); @@ -3418,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; } @@ -3445,6 +4216,63 @@ static MODCMD_FUNC(cmd_checkpass) return 1; } +static MODCMD_FUNC(cmd_checkemail) +{ + struct handle_info *hi; + + NICKSERV_MIN_PARMS(3); + if (!(hi = modcmd_get_handle_info(user, argv[1]))) { + return 0; + } + if (!hi->email_addr) + reply("CHECKEMAIL_NOT_SET"); + else if (!irccasecmp(argv[2], hi->email_addr)) + reply("CHECKEMAIL_YES"); + else + reply("CHECKEMAIL_NO"); + 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) { @@ -3452,8 +4280,8 @@ nickserv_db_read_handle(const char *handle, dict_t obj) struct string_list *masks, *slist; struct handle_info *hi; struct userNode *authed_users; - struct userData *channels; - unsigned long int id; + struct userData *channel_list; + unsigned long id; unsigned int ii; dict_t subdb; @@ -3466,13 +4294,13 @@ nickserv_db_read_handle(const char *handle, dict_t obj) } if ((hi = get_handle_info(handle))) { authed_users = hi->users; - channels = hi->channels; + channel_list = hi->channels; hi->users = NULL; hi->channels = NULL; dict_remove(nickserv_handle_dict, hi->handle); } else { authed_users = NULL; - channels = NULL; + channel_list = NULL; } hi = register_handle(handle, str, id); if (authed_users) { @@ -3482,7 +4310,7 @@ nickserv_db_read_handle(const char *handle, dict_t obj) authed_users = authed_users->next_authed; } } - hi->channels = channels; + hi->channels = channel_list; 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); @@ -3491,13 +4319,21 @@ 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 ? (time_t)strtoul(str, NULL, 0) : now; + hi->registered = str ? 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; + hi->lastseen = str ? 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 @@ -3532,6 +4368,9 @@ 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); + str = database_get_data(obj, KEY_FAKEIDENT, RECDB_QSTRING); + if (str) + hi->fakeident = strdup(str); /* Read the "cookie" sub-database (if it exists). */ subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT); if (subdb) { @@ -3585,13 +4424,13 @@ nickserv_db_read_handle(const char *handle, dict_t obj) const char *setter; const char *text; const char *set; - const char *id; + const char *note_id; dict_t notedb; - id = iter_key(it); + note_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); + log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", note_id, hi->handle); continue; } expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING); @@ -3599,14 +4438,14 @@ nickserv_db_read_handle(const char *handle, dict_t obj) 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); + log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", 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); + note->id = strtoul(note_id, NULL, 10); safestrncpy(note->setter, setter, sizeof(note->setter)); strcpy(note->note, text); if (last_note) @@ -3616,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 @@ -3647,10 +4488,10 @@ static NICKSERV_FUNC(cmd_mergedb) stop.tv_sec -= start.tv_sec; stop.tv_usec -= start.tv_usec; if (stop.tv_usec < 0) { - stop.tv_sec -= 1; - stop.tv_usec += 1000000; + stop.tv_sec -= 1; + stop.tv_usec += 1000000; } - reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000); + reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000); return 1; } @@ -3658,7 +4499,7 @@ static void expire_handles(UNUSED_ARG(void *data)) { dict_iterator_t it, next; - time_t expiry; + unsigned long expiry; struct handle_info *hi; for (it=dict_first(nickserv_handle_dict); it; it=next) { @@ -3690,8 +4531,7 @@ nickserv_load_dict(const char *fname) log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno)); return; } - while (!feof(file)) { - fgets(line, sizeof(line), file); + while (fgets(line, sizeof(line), file)) { if (!line[0]) continue; if (line[strlen(line)-1] == '\n') @@ -3724,8 +4564,8 @@ nickserv_conf_read(void) dict_iterator_t it; if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) { - log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME); - return; + log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME); + return; } str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING); if (!str) @@ -3769,12 +4609,16 @@ 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); nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900; str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING); nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000; + str = database_get_data(conf_node, KEY_SET_FAKEIDENT_LEVEL, RECDB_QSTRING); + nickserv_conf.set_fakeident_level = str ? strtoul(str, NULL, 0) : 1000; str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING); if (!str) str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING); @@ -3793,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); @@ -3805,6 +4651,13 @@ nickserv_conf_read(void) if(pos) nickserv_conf.ounregister_flags |= 1 << (pos - 1); } + str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING); + if (!str) + nickserv_conf.handle_ts_mode = TS_IGNORE; + else if (!irccasecmp(str, "ircu")) + nickserv_conf.handle_ts_mode = TS_IRCU; + else + nickserv_conf.handle_ts_mode = TS_IGNORE; 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; @@ -3862,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) { @@ -3910,52 +4763,74 @@ nickserv_reclaim_p(void *data) { nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action); } -static int +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))) - return 0; + return; if (user->handle_info == ni->owner) { user->modes |= FLAGS_REGNICK; irc_regnick(user); - return 0; + return; } if (nickserv_conf.warn_nick_owned) send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle); if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE) - return 0; + return; if (nickserv_conf.auto_reclaim_delay) timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user); else nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action); - return 0; -} - -int -handle_new_user(struct userNode *user) -{ - return check_user_nick(user); } void -handle_account(struct userNode *user, const char *stamp) +handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial) { - struct handle_info *hi; + struct handle_info *hi = NULL; -#ifdef WITH_PROTOCOL_P10 - hi = dict_find(nickserv_handle_dict, stamp, NULL); -#else - hi = dict_find(nickserv_id_dict, stamp, NULL); -#endif + if (stamp != NULL) + hi = dict_find(nickserv_handle_dict, stamp, NULL); + if ((hi == NULL) && (serial != 0)) { + char id[IDLEN + 1]; + inttobase64(id, serial, IDLEN); + hi = dict_find(nickserv_id_dict, id, NULL); + } if (hi) { + if ((nickserv_conf.handle_ts_mode == TS_IRCU) + && (timestamp != hi->registered)) { + return; + } if (HANDLE_FLAGGED(hi, SUSPENDED)) { return; } set_user_handle_info(user, hi, 0); } else { - log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp); + log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial); } } @@ -3973,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); @@ -4029,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 @@ -4036,7 +4929,7 @@ init_nickserv(const char *nick) { unsigned int i; NS_LOG = log_register_type("NickServ", "file:nickserv.log"); - reg_new_user_func(handle_new_user); + reg_new_user_func(check_user_nick); reg_nick_change_func(handle_nick_change); reg_del_user_func(nickserv_remove_user); reg_account_func(handle_account); @@ -4077,12 +4970,12 @@ init_nickserv(const char *nick) 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); - nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0); - nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0); - nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0); - nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0); + /* nick management commands */ + nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0); + nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0); + nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0); + nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0); + nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0); nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0); } if (nickserv_conf.email_enabled) { @@ -4090,6 +4983,7 @@ init_nickserv(const char *nick) nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1); nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1); nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0); + nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0); dict_insert(nickserv_opt_dict, "EMAIL", opt_email); } nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0); @@ -4099,6 +4993,9 @@ init_nickserv(const char *nick) nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0); 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); @@ -4106,15 +5003,21 @@ 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); } dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins); dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);