/* Routines to handle the NickServ SET command. * * IRC Services is copyright (c) 1996-2007 Andrew Church. * E-mail: * Parts written by Andrew Kempe and others. * This program is free but copyrighted software; see the file COPYING for * details. */ #include "services.h" #include "modules.h" #include "language.h" #include "encrypt.h" #include "modules/operserv/operserv.h" #include "nickserv.h" #include "ns-local.h" /*************************************************************************/ static Module *module; static int cb_set = -1; static int cb_set_email = -1; static int cb_unset = -1; /*************************************************************************/ static void do_set_password(User *u, NickGroupInfo *ngi, NickInfo *ni, char *param); static void do_set_language(User *u, NickGroupInfo *ngi, char *param); static void do_set_url(User *u, NickGroupInfo *ngi, char *param); static void do_set_email(User *u, NickGroupInfo *ngi, char *param); static void do_set_info(User *u, NickGroupInfo *ngi, char *param); static void do_set_kill(User *u, NickGroupInfo *ngi, char *param); static void do_set_secure(User *u, NickGroupInfo *ngi, char *param); static void do_set_private(User *u, NickGroupInfo *ngi, char *param); static void do_set_hide(User *u, NickGroupInfo *ngi, char *param, char *setting); static void do_set_timezone(User *u, NickGroupInfo *ngi, char *param); static void do_set_noexpire(User *u, NickInfo *ni, char *param); /*************************************************************************/ /*************************************************************************/ void do_set(User *u) { char *cmd = strtok(NULL, " "); char *param = strtok_remaining(); char *extra = NULL; NickInfo *ni; NickGroupInfo *ngi; int is_servadmin = is_services_admin(u); if (readonly) { notice_lang(s_NickServ, u, NICK_SET_DISABLED); return; } if (is_servadmin && param && strchr(param, ' ') && (ni = get_nickinfo(cmd)) ) { cmd = strtok(param, " "); param = strtok_remaining(); } else { ni = u->ni; } if (cmd && stricmp(cmd, "INFO") != 0) { param = strtok(param, " "); extra = strtok(NULL, " "); } if (!cmd || !param || (stricmp(cmd,"HIDE")==0 && !extra)) { if (is_oper(u)) syntax_error(s_NickServ, u, "SET", NICK_SET_OPER_SYNTAX); else syntax_error(s_NickServ, u, "SET", NICK_SET_SYNTAX); } else if (!ni) { notice_lang(s_NickServ, u, NICK_NOT_REGISTERED); } else if (ni->status & NS_VERBOTEN) { notice_lang(s_NickServ, u, NICK_X_FORBIDDEN, ni->nick); } else if (!(ngi = get_ngi(ni))) { notice_lang(s_NickServ, u, INTERNAL_ERROR); } else if (!is_servadmin && !user_identified(u) && !(stricmp(cmd,"EMAIL")==0 && user_ident_nomail(u))) { notice_lang(s_NickServ, u, NICK_IDENTIFY_REQUIRED, s_NickServ); } else if (call_callback_5(module, cb_set, u, ni, ngi, cmd, param) > 0) { return; } else if (stricmp(cmd, "PASSWORD") == 0) { do_set_password(u, ngi, ni, param); } else if (stricmp(cmd, "LANGUAGE") == 0) { do_set_language(u, ngi, param); } else if (stricmp(cmd, "URL") == 0) { do_set_url(u, ngi, param); } else if (stricmp(cmd, "EMAIL") == 0) { do_set_email(u, ngi, param); } else if (stricmp(cmd, "INFO") == 0) { do_set_info(u, ngi, param); } else if (stricmp(cmd, "KILL") == 0) { do_set_kill(u, ngi, param); } else if (stricmp(cmd, "SECURE") == 0) { do_set_secure(u, ngi, param); } else if (stricmp(cmd, "PRIVATE") == 0) { do_set_private(u, ngi, param); } else if (stricmp(cmd, "HIDE") == 0) { do_set_hide(u, ngi, param, extra); } else if (stricmp(cmd, "TIMEZONE") == 0) { do_set_timezone(u, ngi, param); } else if (stricmp(cmd, "NOEXPIRE") == 0) { do_set_noexpire(u, ni, param); } else { if (is_servadmin) notice_lang(s_NickServ, u, NICK_SET_UNKNOWN_OPTION_OR_BAD_NICK, strupper(cmd)); else notice_lang(s_NickServ, u, NICK_SET_UNKNOWN_OPTION, strupper(cmd)); } } /*************************************************************************/ void do_unset(User *u) { char *cmd = strtok(NULL, " "); char *extra = strtok(NULL, " "); NickInfo *ni; NickGroupInfo *ngi; int is_servadmin = is_services_admin(u); if (readonly) { notice_lang(s_NickServ, u, NICK_SET_DISABLED); return; } if (is_servadmin && extra && (ni = get_nickinfo(cmd))) { cmd = extra; extra = strtok(NULL, " "); } else { ni = u->ni; } if (!cmd || extra) { syntax_error(s_NickServ, u, "UNSET", NSRequireEmail ? NICK_UNSET_SYNTAX_REQ_EMAIL : NICK_UNSET_SYNTAX); } else if (!ni) { notice_lang(s_NickServ, u, NICK_NOT_REGISTERED); } else if (ni->status & NS_VERBOTEN) { notice_lang(s_NickServ, u, NICK_X_FORBIDDEN, ni->nick); } else if (!(ngi = get_ngi(ni))) { notice_lang(s_NickServ, u, INTERNAL_ERROR); } else if (!is_servadmin && !user_identified(u)) { notice_lang(s_NickServ, u, NICK_IDENTIFY_REQUIRED, s_NickServ); } else if (call_callback_4(module, cb_unset, u, ni, ngi, cmd) > 0) { return; } else if (stricmp(cmd, "URL") == 0) { do_set_url(u, ngi, NULL); } else if (stricmp(cmd, "EMAIL") == 0) { if (NSRequireEmail) { if (ni != u->ni) notice_lang(s_NickServ, u, NICK_UNSET_EMAIL_OTHER_BAD); else notice_lang(s_NickServ, u, NICK_UNSET_EMAIL_BAD); } else { do_set_email(u, ngi, NULL); } } else if (stricmp(cmd, "INFO") == 0) { do_set_info(u, ngi, NULL); } else { syntax_error(s_NickServ, u, "UNSET", NSRequireEmail ? NICK_UNSET_SYNTAX_REQ_EMAIL : NICK_UNSET_SYNTAX); } } /*************************************************************************/ static void do_set_password(User *u, NickGroupInfo *ngi, NickInfo *ni, char *param) { int len = strlen(param), max; if (NSSecureAdmins && u->ni != ni && nick_is_services_admin(ni) && !is_services_root(u) ) { notice_lang(s_NickServ, u, PERMISSION_DENIED); return; } else if (stricmp(param, ni->nick) == 0 || (StrictPasswords && len < 5)) { notice_lang(s_NickServ, u, MORE_OBSCURE_PASSWORD); return; } /* Password length checks and truncation (see do_register()) */ max = encrypt_check_len(len, PASSMAX); if ((max == 0 && len > PASSMAX-1) || max > PASSMAX-1) max = PASSMAX-1; if (max > 0) { memset(param+max, 0, len-max); len = max; notice_lang(s_NickServ, u, PASSWORD_TRUNCATED, max); } if (encrypt(param, len, ngi->pass, PASSMAX) < 0) { memset(param, 0, len); module_log("Failed to encrypt password for %s (set)", ni->nick); notice_lang(s_NickServ, u, NICK_SET_PASSWORD_FAILED); return; } put_nickgroupinfo(ngi); if (NSShowPassword) notice_lang(s_NickServ, u, NICK_SET_PASSWORD_CHANGED_TO, param); else notice_lang(s_NickServ, u, NICK_SET_PASSWORD_CHANGED); memset(param, 0, strlen(param)); if (u->ni != ni) { module_log("%s!%s@%s used SET PASSWORD as Services admin on %s", u->nick, u->username, u->host, ni->nick); if (WallSetpass) { wallops(s_NickServ, "\2%s\2 used SET PASSWORD as Services admin " "on \2%s\2", u->nick, ni->nick); } } } /*************************************************************************/ static void do_set_language(User *u, NickGroupInfo *ngi, char *param) { int langnum; if (param[strspn(param, "0123456789")] != 0) { /* i.e. not a number */ syntax_error(s_NickServ, u, "SET LANGUAGE", NICK_SET_LANGUAGE_SYNTAX); return; } langnum = atoi(param)-1; if (langnum < 0 || langnum >= NUM_LANGS || langlist[langnum] < 0) { notice_lang(s_NickServ, u, NICK_SET_LANGUAGE_UNKNOWN, langnum+1, s_NickServ); return; } ngi->language = langlist[langnum]; put_nickgroupinfo(ngi); notice_lang(s_NickServ, u, NICK_SET_LANGUAGE_CHANGED, getstring(ngi,LANG_NAME)); } /*************************************************************************/ static void do_set_url(User *u, NickGroupInfo *ngi, char *param) { const char *nick = ngi_mainnick(ngi); if (param && !valid_url(param)) { notice_lang(s_NickServ, u, BAD_URL); return; } free(ngi->url); if (param) { ngi->url = sstrdup(param); notice_lang(s_NickServ, u, NICK_SET_URL_CHANGED, nick, param); } else { ngi->url = NULL; notice_lang(s_NickServ, u, NICK_UNSET_URL, nick); } put_nickgroupinfo(ngi); } /*************************************************************************/ static void do_set_email(User *u, NickGroupInfo *ngi, char *param) { const char *nick = ngi_mainnick(ngi); char oldemail[BUFSIZE]; if (param && !valid_email(param)) { notice_lang(s_NickServ, u, BAD_EMAIL); return; } if (param && !is_services_admin(u)) { int n = count_nicks_with_email(param); if (n < 0) { notice_lang(s_NickServ, u, NICK_SET_EMAIL_UNAUTHED); return; } else if (NSRegEmailMax && n >= NSRegEmailMax) { notice_lang(s_NickServ, u, NICK_SET_EMAIL_TOO_MANY_NICKS, param, n, NSRegEmailMax); return; } } if (ngi->email) { strscpy(oldemail, ngi->email, sizeof(oldemail)); free(ngi->email); } else { *oldemail = 0; } if (param) { ngi->email = sstrdup(param); if (*oldemail) { module_log("%s E-mail address changed from %s to %s by %s!%s@%s", nick, oldemail, param, u->nick, u->username, u->host); } else { module_log("%s E-mail address set to %s by %s!%s@%s", nick, param, u->nick, u->username, u->host); } notice_lang(s_NickServ, u, NICK_SET_EMAIL_CHANGED, nick, param); if (!is_services_admin(u)) { /* Note: this (like the SET EMAIL callback in mail-auth) * assumes that only the user or a servadmin can change the * E-mail address; if the user isn't a servadmin, therefore, * they must be doing it to their own nick, and we use/modify * the User structure. */ if (user_ident_nomail(u)) { u->ni->authstat &= ~NA_IDENT_NOMAIL; u->ni->authstat |= NA_IDENTIFIED; } } } else { ngi->email = NULL; if (*oldemail) { module_log("%s E-mail address cleared by %s!%s@%s (was %s)", nick, u->nick, u->username, u->host, oldemail); } notice_lang(s_NickServ, u, NICK_UNSET_EMAIL, nick); } put_nickgroupinfo(ngi); call_callback_2(module, cb_set_email, u, ngi); } /*************************************************************************/ static void do_set_info(User *u, NickGroupInfo *ngi, char *param) { const char *nick = ngi_mainnick(ngi); free(ngi->info); if (param) { ngi->info = sstrdup(param); notice_lang(s_NickServ, u, NICK_SET_INFO_CHANGED, nick, param); } else { ngi->info = NULL; notice_lang(s_NickServ, u, NICK_UNSET_INFO, nick); } put_nickgroupinfo(ngi); } /*************************************************************************/ static void do_set_kill(User *u, NickGroupInfo *ngi, char *param) { if (stricmp(param, "ON") == 0) { ngi->flags |= NF_KILLPROTECT; ngi->flags &= ~(NF_KILL_QUICK | NF_KILL_IMMED); notice_lang(s_NickServ, u, NICK_SET_KILL_ON); } else if (stricmp(param, "QUICK") == 0) { ngi->flags |= NF_KILLPROTECT | NF_KILL_QUICK; ngi->flags &= ~NF_KILL_IMMED; notice_lang(s_NickServ, u, NICK_SET_KILL_QUICK); } else if (stricmp(param, "IMMED") == 0) { if (NSAllowKillImmed) { ngi->flags |= NF_KILLPROTECT | NF_KILL_QUICK | NF_KILL_IMMED; notice_lang(s_NickServ, u, NICK_SET_KILL_IMMED); } else { notice_lang(s_NickServ, u, NICK_SET_KILL_IMMED_DISABLED); return; } } else if (stricmp(param, "OFF") == 0) { ngi->flags &= ~(NF_KILLPROTECT | NF_KILL_QUICK | NF_KILL_IMMED); notice_lang(s_NickServ, u, NICK_SET_KILL_OFF); } else { syntax_error(s_NickServ, u, "SET KILL", NSAllowKillImmed ? NICK_SET_KILL_IMMED_SYNTAX : NICK_SET_KILL_SYNTAX); return; } put_nickgroupinfo(ngi); } /*************************************************************************/ static void do_set_secure(User *u, NickGroupInfo *ngi, char *param) { if (stricmp(param, "ON") == 0) { ngi->flags |= NF_SECURE; notice_lang(s_NickServ, u, NICK_SET_SECURE_ON); } else if (stricmp(param, "OFF") == 0) { ngi->flags &= ~NF_SECURE; notice_lang(s_NickServ, u, NICK_SET_SECURE_OFF); } else { syntax_error(s_NickServ, u, "SET SECURE", NICK_SET_SECURE_SYNTAX); return; } put_nickgroupinfo(ngi); } /*************************************************************************/ static void do_set_private(User *u, NickGroupInfo *ngi, char *param) { if (stricmp(param, "ON") == 0) { ngi->flags |= NF_PRIVATE; notice_lang(s_NickServ, u, NICK_SET_PRIVATE_ON); } else if (stricmp(param, "OFF") == 0) { ngi->flags &= ~NF_PRIVATE; notice_lang(s_NickServ, u, NICK_SET_PRIVATE_OFF); } else { syntax_error(s_NickServ, u, "SET PRIVATE", NICK_SET_PRIVATE_SYNTAX); return; } put_nickgroupinfo(ngi); } /*************************************************************************/ static void do_set_hide(User *u, NickGroupInfo *ngi, char *param, char *setting) { int flag, onmsg, offmsg; if (stricmp(param, "EMAIL") == 0) { flag = NF_HIDE_EMAIL; onmsg = NICK_SET_HIDE_EMAIL_ON; offmsg = NICK_SET_HIDE_EMAIL_OFF; } else if (stricmp(param, "USERMASK") == 0) { flag = NF_HIDE_MASK; onmsg = NICK_SET_HIDE_MASK_ON; offmsg = NICK_SET_HIDE_MASK_OFF; } else if (stricmp(param, "QUIT") == 0) { flag = NF_HIDE_QUIT; onmsg = NICK_SET_HIDE_QUIT_ON; offmsg = NICK_SET_HIDE_QUIT_OFF; } else { syntax_error(s_NickServ, u, "SET HIDE", NICK_SET_HIDE_SYNTAX); return; } if (stricmp(setting, "ON") == 0) { ngi->flags |= flag; notice_lang(s_NickServ, u, onmsg, s_NickServ); } else if (stricmp(setting, "OFF") == 0) { ngi->flags &= ~flag; notice_lang(s_NickServ, u, offmsg, s_NickServ); } else { syntax_error(s_NickServ, u, "SET HIDE", NICK_SET_HIDE_SYNTAX); return; } put_nickgroupinfo(ngi); } /*************************************************************************/ /* Timezone definitions. */ static struct { const char *name; int16 offset; } timezones[] = { {"GMT-12",-720}, {"GMT-11",-660}, {"GMT-10",-600}, {"HST",-600}, {"GMT-9", -540}, {"GMT-8", -480}, {"PST",-480}, {"GMT-7", -420}, {"MST",-420}, {"PDT",-420}, {"GMT-6", -360}, {"CST",-360}, {"MDT",-360}, {"GMT-5", -300}, {"EST",-300}, {"CDT",-300}, {"GMT-4", -240}, {"AST",-240}, {"EDT",-240}, {"GMT-3", -180}, {"BRT",-180}, {"ADT",-180}, {"GMT-2", -120}, {"BRST",-120}, {"GMT-1", -60}, {"GMT+0", 0}, {"GMT", 0}, {"UTC", 0}, {"WET", 0}, {"GMT+1", 60}, {"MET", 60}, {"BST", 60}, {"IST", 60}, {"GMT+2", 120}, {"EET", 120}, {"GMT+3", 180}, {"MSK", 180}, {"GMT+4", 240}, {"MSD", 240}, {"GMT+5", 300}, {"GMT+6", 360}, {"GMT+7", 420}, {"GMT+8", 480}, {"GMT+9", 540}, {"JST", 540}, {"KST", 540}, {"GMT+10", 600}, {"GMT+11", 660}, {"GMT+12", 720}, {"NZST",720}, {"GMT+13", 780}, {"NZDT",780}, { NULL } }; static void do_set_timezone(User *u, NickGroupInfo *ngi, char *param) { char *s; int i, j; char timebuf[BUFSIZE]; if (stricmp(param, "DEFAULT") == 0) { ngi->timezone = TIMEZONE_DEFAULT; notice_lang(s_NickServ, u, NICK_SET_TIMEZONE_DEFAULT); return; } else if (*param == '+' || *param == '-') { i = strtol(param+1, &s, 10); if (*s == ':') { if (s[1]>='0' && s[1]<='5' && s[2]>='0' && s[2]<='9') j = strtol(s+1, &s, 10); else j = -1; } else { j = 0; } if (i < 0 || i > 23 || j < 0 || j > 59 || *s) { syntax_error(s_NickServ, u, "SET TIMEZONE", NICK_SET_TIMEZONE_SYNTAX); return; } ngi->timezone = i*60 + j; if (*param == '-') ngi->timezone = -ngi->timezone; } else { for (i = 0; timezones[i].name; i++) { if (stricmp(param, timezones[i].name) == 0) break; } if (!timezones[i].name) { syntax_error(s_NickServ, u, "SET TIMEZONE", NICK_SET_TIMEZONE_SYNTAX); return; } ngi->timezone = timezones[i].offset; } /* This is tricky, because we want the calling user's language but the * target user's timezone. */ if (valid_ngi(u)) { j = (ngi->timezone - u->ngi->timezone) * 60; } else { /* just in case... */ time_t tmp = 0; struct tm *tm = localtime(&tmp); tmp = tm->tm_hour*3600 + tm->tm_min*60 + tm->tm_sec; if (tm->tm_year < 70) tmp -= 86400; j = ngi->timezone*60 - tmp; } strftime_lang(timebuf, sizeof(timebuf), u->ngi, STRFTIME_DATE_TIME_FORMAT, time(NULL) + j); if (ngi->timezone < 0) i = -ngi->timezone; else i = ngi->timezone; notice_lang(s_NickServ, u, NICK_SET_TIMEZONE_TO, ngi->timezone<0 ? '-' : '+', i/60, i%60, timebuf); put_nickgroupinfo(ngi); } /*************************************************************************/ static void do_set_noexpire(User *u, NickInfo *ni, char *param) { if (!is_services_admin(u)) { notice_lang(s_NickServ, u, PERMISSION_DENIED); return; } if (!param) { syntax_error(s_NickServ, u, "SET NOEXPIRE", NICK_SET_NOEXPIRE_SYNTAX); return; } if (stricmp(param, "ON") == 0) { ni->status |= NS_NOEXPIRE; notice_lang(s_NickServ, u, NICK_SET_NOEXPIRE_ON, ni->nick); } else if (stricmp(param, "OFF") == 0) { ni->status &= ~NS_NOEXPIRE; notice_lang(s_NickServ, u, NICK_SET_NOEXPIRE_OFF, ni->nick); } else { syntax_error(s_NickServ, u, "SET NOEXPIRE", NICK_SET_NOEXPIRE_SYNTAX); return; } put_nickinfo(ni); } /*************************************************************************/ /*************************************************************************/ int init_set(Module *my_module) { module = my_module; cb_set = register_callback(module, "SET"); cb_set_email = register_callback(module, "SET EMAIL"); cb_unset = register_callback(module, "UNSET"); if (cb_set < 0 || cb_set_email < 0 || cb_unset < 0) { module_log("set: Unable to register callbacks"); exit_set(); return 0; } return 1; } /*************************************************************************/ void exit_set() { unregister_callback(module, cb_unset); unregister_callback(module, cb_set_email); unregister_callback(module, cb_set); } /*************************************************************************/