]> jfr.im git - irc/evilnet/x3.git/blame - src/nickserv.c
fix for 1551717- automode'ing when authing in a channel
[irc/evilnet/x3.git] / src / nickserv.c
CommitLineData
d76ed9a9 1/* nickserv.c - Nick/authentication service
2 * Copyright 2000-2004 srvx Development Team
3 *
83ff05c3 4 * This file is part of x3.
d76ed9a9 5 *
d0f04f71 6 * x3 is free software; you can redistribute it and/or modify
d76ed9a9 7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
19 */
20
21#include "chanserv.h"
22#include "conf.h"
23#include "global.h"
24#include "modcmd.h"
25#include "opserv.h" /* for gag_create(), opserv_bad_channel() */
26#include "saxdb.h"
27#include "sendmail.h"
28#include "timeq.h"
29
35305a49 30#include <tre/regex.h>
d76ed9a9 31
32#define NICKSERV_CONF_NAME "services/nickserv"
33
34#define KEY_DISABLE_NICKS "disable_nicks"
35#define KEY_DEFAULT_HOSTMASK "default_hostmask"
36#define KEY_NICKS_PER_HANDLE "nicks_per_handle"
37#define KEY_NICKS_PER_ACCOUNT "nicks_per_account"
38#define KEY_PASSWORD_MIN_LENGTH "password_min_length"
39#define KEY_PASSWORD_MIN_DIGITS "password_min_digits"
40#define KEY_PASSWORD_MIN_UPPER "password_min_upper"
41#define KEY_PASSWORD_MIN_LOWER "password_min_lower"
42#define KEY_VALID_HANDLE_REGEX "valid_handle_regex"
43#define KEY_VALID_ACCOUNT_REGEX "valid_account_regex"
44#define KEY_VALID_NICK_REGEX "valid_nick_regex"
bf93ca8d 45#define KEY_VALID_FAKEHOST_REGEX "valid_fakehost_regex"
d76ed9a9 46#define KEY_DB_BACKUP_FREQ "db_backup_freq"
47#define KEY_MODOPER_LEVEL "modoper_level"
48#define KEY_SET_EPITHET_LEVEL "set_epithet_level"
49#define KEY_SET_TITLE_LEVEL "set_title_level"
50#define KEY_SET_FAKEHOST_LEVEL "set_fakehost_level"
7637f48f 51#define KEY_DENIED_FAKEHOST_WORDS "denied_fakehost_words"
d76ed9a9 52#define KEY_TITLEHOST_SUFFIX "titlehost_suffix"
5a1daaab 53#define KEY_AUTO_OPER "auto_oper"
54#define KEY_AUTO_ADMIN "auto_admin"
d76ed9a9 55#define KEY_FLAG_LEVELS "flag_levels"
56#define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq"
57#define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq"
58#define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay"
59#define KEY_ACCOUNT_EXPIRE_DELAY "account_expire_delay"
60#define KEY_NOCHAN_HANDLE_EXPIRE_DELAY "nochan_handle_expire_delay"
61#define KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY "nochan_account_expire_delay"
62#define KEY_DICT_FILE "dict_file"
63#define KEY_NICK "nick"
64#define KEY_LANGUAGE "language"
65#define KEY_AUTOGAG_ENABLED "autogag_enabled"
66#define KEY_AUTOGAG_DURATION "autogag_duration"
67#define KEY_AUTH_POLICER "auth_policer"
68#define KEY_EMAIL_VISIBLE_LEVEL "email_visible_level"
69#define KEY_EMAIL_ENABLED "email_enabled"
70#define KEY_EMAIL_REQUIRED "email_required"
8dc1d9ae 71#define KEY_SYNC_LOG "sync_log"
d76ed9a9 72#define KEY_COOKIE_TIMEOUT "cookie_timeout"
73#define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
74#define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
338a82b5 75#define KEY_DEFAULT_STYLE "default_style"
d76ed9a9 76
77#define KEY_ID "id"
78#define KEY_PASSWD "passwd"
79#define KEY_NICKS "nicks"
80#define KEY_MASKS "masks"
5177fd21 81#define KEY_IGNORES "ignores"
d76ed9a9 82#define KEY_OPSERV_LEVEL "opserv_level"
83#define KEY_FLAGS "flags"
84#define KEY_REGISTER_ON "register"
85#define KEY_LAST_SEEN "lastseen"
86#define KEY_INFO "info"
87#define KEY_USERLIST_STYLE "user_style"
88#define KEY_SCREEN_WIDTH "screen_width"
89#define KEY_LAST_AUTHED_HOST "last_authed_host"
90#define KEY_LAST_QUIT_HOST "last_quit_host"
91#define KEY_EMAIL_ADDR "email_addr"
92#define KEY_COOKIE "cookie"
93#define KEY_COOKIE_DATA "data"
94#define KEY_COOKIE_TYPE "type"
95#define KEY_COOKIE_EXPIRES "expires"
96#define KEY_ACTIVATION "activation"
97#define KEY_PASSWORD_CHANGE "password change"
98#define KEY_EMAIL_CHANGE "email change"
99#define KEY_ALLOWAUTH "allowauth"
100#define KEY_EPITHET "epithet"
101#define KEY_TABLE_WIDTH "table_width"
102#define KEY_ANNOUNCEMENTS "announcements"
103#define KEY_MAXLOGINS "maxlogins"
104#define KEY_FAKEHOST "fakehost"
2362161a 105#define KEY_NOTE_NOTE "note"
106#define KEY_NOTE_SETTER "setter"
107#define KEY_NOTE_DATE "date"
108
d76ed9a9 109
110#define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
111
112#define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
c092fcad 113#define OPTION_FUNC(NAME) int NAME(struct svccmd *cmd, struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
d76ed9a9 114typedef OPTION_FUNC(option_func_t);
115
116DEFINE_LIST(handle_info_list, struct handle_info*);
117
118#define NICKSERV_MIN_PARMS(N) do { \
119 if (argc < N) { \
120 reply("MSG_MISSING_PARAMS", argv[0]); \
180e0971 121 svccmd_send_help_brief(user, nickserv, cmd); \
d76ed9a9 122 return 0; \
123 } } while (0)
124
125struct userNode *nickserv;
126struct userList curr_helpers;
127const char *handle_flags = HANDLE_FLAGS;
128
7637f48f 129extern struct string_list *autojoin_channels;
d76ed9a9 130static struct module *nickserv_module;
131static struct service *nickserv_service;
132static struct log_type *NS_LOG;
133static dict_t nickserv_handle_dict; /* contains struct handle_info* */
134static dict_t nickserv_id_dict; /* contains struct handle_info* */
135static dict_t nickserv_nick_dict; /* contains struct nick_info* */
136static dict_t nickserv_opt_dict; /* contains option_func_t* */
137static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
138static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
139static char handle_inverse_flags[256];
140static unsigned int flag_access_levels[32];
141static const struct message_entry msgtab[] = {
142 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
d0cb2fb6 143 { "NSMSG_HANDLE_TOLONG", "The account name %s is too long. Account names must be %lu charactors or less."},
d76ed9a9 144 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
145 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
4bffb7bd 146 { "NSMSG_PASSWORD_DICTIONARY", "Your password is too simple. You must choose a password that is not just a word or name." },
d76ed9a9 147 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
148 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
149 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
150 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
151 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
152 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
153 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
154 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
155 { "NSMSG_USE_COOKIE_REGISTER", "To activate your account, you must check your email for the \"cookie\" that has been mailed to it. When you have it, use the $bcookie$b command to complete registration." },
156 { "NSMSG_USE_COOKIE_RESETPASS", "A cookie has been mailed to your account's email address. You must check your email and use the $bcookie$b command to confirm the password change." },
157 { "NSMSG_USE_COOKIE_EMAIL_1", "A cookie has been mailed to the new address you requested. To finish setting your email address, please check your email for the cookie and use the $bcookie$b command to verify." },
158 { "NSMSG_USE_COOKIE_EMAIL_2", "A cookie has been generated, and half mailed to each your old and new addresses. To finish changing your email address, please check your email for the cookie and use the $bcookie$b command to verify." },
159 { "NSMSG_USE_COOKIE_AUTH", "A cookie has been generated and sent to your email address. Once you have checked your email and received the cookie, auth using the $bcookie$b command." },
160 { "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." },
161 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
162 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
34938510 163 { "NSMSG_NO_COOKIE_FOREIGN", "The account $b%s$b does not have any cookie issued right now." },
d76ed9a9 164 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
165 { "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." },
166 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
167 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
168 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
b1bf690d 169 { "NSMSG_EMAIL_OVERUSED", "That email address already has an account. Use RESETPASS if you forgot your password." },
d76ed9a9 170 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
171 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
172 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
173 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
174 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
34938510 175 { "NSMSG_ATE_FOREIGN_COOKIE", "I ate the cookie for account $b%s$b. It may now have another." },
d76ed9a9 176 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
177 { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
d762299d 178 { "NSMSG_REGISTER_BAD_NICKMASK", "You must provide a hostmask, or online nick to generate one automatically. (or set a default hostmask in the config such as *@*)." },
d76ed9a9 179 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
180 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
181 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
182 { "NSMSG_USE_AUTHCOOKIE", "Your hostmask is not valid for account $b%1$s$b. Please use the $bauthcookie$b command to grant yourself access. (/msg $S authcookie %1$s)" },
183 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
184 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
185 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
186 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
187 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
0b587959 188 { "NSMSG_BAD_ADVANCED", "Advanced must be either 1 to enable it or 0 to disable it." },
d76ed9a9 189 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
190 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
191 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
192 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
193 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
194 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
116d100f 195 { "NSMSG_TITLE_INVALID", "Titles may contain only a-z, A-Z, 0-9, and '-'. Please choose another." },
a32da4c7 196 { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." },
d76ed9a9 197 { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
de9510bc 198 { "NSMSG_HANDLEINFO_ON", "$bAccount Information for %s$b" },
199 { "NSMSG_HANDLEINFO_END", "----------End of Account Info-----------" },
200 { "NSMSG_HANDLEINFO_ID", "Account ID: %lu" },
201 { "NSMSG_HANDLEINFO_REGGED", "Registered on: %s" },
202 { "NSMSG_HANDLEINFO_LASTSEEN", "Last seen: %s" },
203 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", "Last seen: Right now!" },
204 { "NSMSG_HANDLEINFO_VACATION", "On vacation." },
205 { "NSMSG_HANDLEINFO_EMAIL_ADDR", "Email address: %s" },
206 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", "Cookie: There is currently an activation cookie issued for this account" },
207 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", "Cookie: There is currently a password change cookie issued for this account" },
208 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", "Cookie: There is currently an email change cookie issued for this account" },
209 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", "Cookie: There is currently an allowauth cookie issued for this account" },
210 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", "Cookie: There is currently an unknown cookie issued for this account" },
211 { "NSMSG_HANDLEINFO_INFOLINE", "Infoline: %s" },
212 { "NSMSG_HANDLEINFO_FLAGS", "Flags: %s" },
213 { "NSMSG_HANDLEINFO_EPITHET", "Epithet: %s" },
2362161a 214 { "NSMSG_HANDLEINFO_NOTE", "Note (by %s on %s): %s " },
de9510bc 215 { "NSMSG_HANDLEINFO_FAKEHOST", "Fake host: %s" },
216 { "NSMSG_HANDLEINFO_LAST_HOST", "Last quit hostmask: %s" },
217 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", "Last quit hostmask: Unknown" },
218 { "NSMSG_HANDLEINFO_NICKS", "Nickname(s): %s" },
219 { "NSMSG_HANDLEINFO_MASKS", "Hostmask(s): %s" },
5177fd21 220 { "NSMSG_HANDLEINFO_IGNORES", "Ignore(s): %s" },
de9510bc 221 { "NSMSG_HANDLEINFO_CHANNELS", "Channel(s): %s" },
222 { "NSMSG_HANDLEINFO_CURRENT", "Current nickname(s): %s" },
223 { "NSMSG_HANDLEINFO_DNR", "Do-not-register (by %s): %s" },
d76ed9a9 224 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
225 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
226 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
227 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
228 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
229 { "NSMSG_WEAK_PASSWORD", "WARNING: You are using a password that is considered weak (easy to guess). It is STRONGLY recommended you change it (now, if not sooner) by typing \"/msg $S@$s PASS oldpass newpass\" (with your current password and a new password)." },
230 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
231 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
232 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
233 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
234 { "NSMSG_AUTH_ALLOWED_MSG", "You may now authenticate to account $b%s$b by typing $b/msg $N@$s auth %s password$b (using your password). If you will be using this computer regularly, please type $b/msg $N addmask$b (AFTER you auth) to permanently add your hostmask." },
235 { "NSMSG_AUTH_ALLOWED_EMAIL", "You may also (after you auth) type $b/msg $N set email user@your.isp$b to set an email address. This will let you use the $bauthcookie$b command to be authenticated in the future." },
236 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
237 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
238 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
239 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
240 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
241 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
242 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
243 { "NSMSG_PASS_SUCCESS", "Password changed." },
244 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
245 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
246 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
5177fd21 247 { "NSMSG_ADDIGNORE_ALREADY", "$b%s$b is already an ignored hostmask in your account." },
248 { "NSMSG_ADDIGNORE_SUCCESS", "Hostmask %s added." },
d76ed9a9 249 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
250 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
251 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
252 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
253 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
254 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
255 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
256 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
257 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
258 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
259 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
260 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
261 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
262 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
263 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
264 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
265 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
266 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
267 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
268 { "NSMSG_NO_ACCESS", "Access denied." },
269 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
270 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
271 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
272 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
273 { "NSMSG_DB_MERGED", "$N merged DB from %s (in "FMT_TIME_T".%03lu seconds)." },
274 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
275 { "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." },
276 { "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." },
277 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
278 { "NSMSG_FAIL_RENAME", "Account $b%s$b not renamed to $b%s$b because it is in use by a network services, or contains invalid characters." },
279 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
280 { "NSMSG_SEARCH_MATCH", "Match: %s" },
281 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
282 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
283 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
284 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
285 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
286 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
287 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
288 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
289 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
290 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
4bffb7bd 291 { "NSMSG_SETTING_LIST", "$b$N account settings$b" },
292 { "NSMSG_SETTING_LIST_HEADER", "----------------------------------------" },
293 { "NSMSG_SETTING_LIST_END", "-------------End Of Settings------------" },
d76ed9a9 294 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
295 { "NSMSG_INVALID_ANNOUNCE", "$b%s$b is an invalid announcements value." },
296 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
297 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
298 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
299 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
300 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
301 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
302 { "NSMSG_SET_ANNOUNCEMENTS", "$bANNOUNCEMENTS: $b%s" },
0b587959 303 { "NSMSG_SET_AUTOHIDE", "$bAUTOHIDE: $b%s" },
d76ed9a9 304 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
305 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
306 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
307 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
0b587959 308 { "NSMSG_SET_ADVANCED", "$bADVANCED: $b%s" },
d76ed9a9 309 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
310 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
311 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
2362161a 312 { "NSMSG_SET_NOTE", "$bNOTE: $b%s"},
d76ed9a9 313 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
314 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
5a1daaab 315
316 { "NSMSG_AUTO_OPER", "You have been auto-opered" },
317 { "NSMSG_AUTO_OPER_ADMIN", "You have been auto-admined" },
318
d76ed9a9 319 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
1c0d5243 320 { "NSEMAIL_ACTIVATION_BODY",
321 "This email has been sent to verify that this email address belongs to the person who tried to register an account on %1$s. Your cookie is:\n"
322 "%2$s\n"
323 "To verify your email address and complete the account registration, log on to %1$s and type the following command:\n"
324 "/msg %3$s@%4$s COOKIE %5$s %2$s\n"
325 "This command is only used once to complete your account registration, and never again. Once you have run this command, you will need to authenticate everytime you reconnect to the network. To do this, you will have to type this command every time you reconnect:\n"
326 "/msg %3$s@%4$s AUTH %5$s your-password\n"
327 "(Please remember to fill in 'your-password' with the actual password you gave to us when you registered.)\n"
328 "OR configure Login-On-Connect (see http://www.afternet.org/login-on-connect for instructions) to connect pre-logged in every time.\n"
329 "\n"
330 "If you did NOT request this account, you do not need to do anything.\n"
331 "Please contact the %1$s staff if you have questions, and be sure to check our website." },
332 { "NSEMAIL_ACTIVATION_BODY_WEB",
333 "This email has been sent to verify that this email address belongs to the person who tried to register an account on %1$s. Your cookie is:\n"
334 "%2$s\n"
335 "To verify your email address and complete the account registration, visit the following URL:\n"
51db18e0 336 "http://www.afternet.org/index.php?option=com_registration&task=activate&username=%5$s&cookie=%2$s\n"
1c0d5243 337 "\n"
338 "If you did NOT request this account, you do not need to do anything.\n"
339 "Please contact the %1$s staff if you have questions, and be sure to check our website." },
d76ed9a9 340 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
1c0d5243 341 { "NSEMAIL_PASSWORD_CHANGE_BODY",
342 "This email has been sent to verify that you wish to change the password on your account %5$s. Your cookie is %2$s.\n"
343 "To complete the password change, log on to %1$s and type the following command:\n"
344 "/msg %3$s@%4$s COOKIE %5$s %2$s\n"
345 "If you did NOT request your password to be changed, you do not need to do anything.\n"
346 "Please contact the %1$s staff if you have questions." },
347 { "NSEMAIL_PASSWORD_CHANGE_BODY_WEB",
348 "This email has been sent to verify that you wish to change the password on your account %5$s. Your cookie is %2$s.\n"
349 "To complete the password change, click the following URL:\n"
51db18e0 350 "http://www.afternet.org/index.php?option=com_registration&task=passcookie&username=%5$s&cookie=%2$s\n"
1c0d5243 351 "If you did NOT request your password to be changed, you do not need to do anything.\n"
352 "Please contact the %1$s staff if you have questions." },
d76ed9a9 353 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
0212adab 354#ifdef stupid_verify_old_email
d76ed9a9 355 { "NSEMAIL_EMAIL_CHANGE_BODY_NEW", "This email has been sent to verify that your email address belongs to the same person as account %5$s on %1$s. The SECOND HALF of your cookie is %2$.*6$s.\nTo verify your address as associated with this account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s ?????%2$.*6$s\n(Replace the ????? with the FIRST HALF of the cookie, as sent to your OLD email address.)\nIf you did NOT request this email address to be associated with this account, you do not need to do anything. Please contact the %1$s staff if you have questions." },
356 { "NSEMAIL_EMAIL_CHANGE_BODY_OLD", "This email has been sent to verify that you want to change your email for account %5$s on %1$s from this address to %7$s. The FIRST HALF of your cookie is %2$.*6$s\nTo verify your new address as associated with this account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$.*6$s?????\n(Replace the ????? with the SECOND HALF of the cookie, as sent to your NEW email address.)\nIf you did NOT request this change of email address, you do not need to do anything. Please contact the %1$s staff if you have questions." },
0212adab 357#endif
d76ed9a9 358 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
359 { "NSEMAIL_EMAIL_VERIFY_BODY", "This email has been sent to verify that this address belongs to the same person as %5$s on %1$s. Your cookie is %2$s.\nTo verify your address as associated with this 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 email address to be associated with this account, you do not need to do anything. Please contact the %1$s staff if you have questions." },
360 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
361 { "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." },
7637f48f 362 { "NSMSG_NOT_VALID_FAKEHOST_DOT", "$b%s$b is not a valid vhost. (needs at least one dot)" },
363 { "NSMSG_NOT_VALID_FAKEHOST_AT", "$b%s$b is not a valid vhost. (it can not have a '@')" },
364 { "NSMSG_DENIED_FAKEHOST_WORD", "Access denied because there's a prohibited word in $b%s$b (%s)." },
365 { "NSMSG_NOT_VALID_FAKEHOST_LEN", "$b%s$b is not a valid vhost. (can only be 63 characters)" },
366 { "NSMSG_NOT_VALID_FAKEHOST_TLD_LEN", "$b%s$b is not a valid vhost. (TLD can only be 4 characters and less)" },
bf93ca8d 367 { "NSMSG_NOT_VALID_FAKEHOST_REGEX", "$b%s$b is not allowed by the admin, consult the valid vhost regex pattern in the config file under nickserv/valid_fakehost_regex." },
d76ed9a9 368 { "CHECKPASS_YES", "Yes." },
369 { "CHECKPASS_NO", "No." },
08895577 370 { "NSMSG_DEFCON_NO_NEW_NICKS", "You cannot register new %s at this time, please try again soon" },
d76ed9a9 371 { NULL, NULL }
372};
373
374enum reclaim_action {
375 RECLAIM_NONE,
376 RECLAIM_WARN,
377 RECLAIM_SVSNICK,
378 RECLAIM_KILL
379};
380static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
381static void nickserv_reclaim_p(void *data);
382
383static struct {
384 unsigned int disable_nicks : 1;
385 unsigned int valid_handle_regex_set : 1;
386 unsigned int valid_nick_regex_set : 1;
bf93ca8d 387 unsigned int valid_fakehost_regex_set : 1;
d76ed9a9 388 unsigned int autogag_enabled : 1;
389 unsigned int email_enabled : 1;
390 unsigned int email_required : 1;
391 unsigned int default_hostmask : 1;
392 unsigned int warn_nick_owned : 1;
393 unsigned int warn_clone_auth : 1;
8dc1d9ae 394 unsigned int sync_log : 1;
d76ed9a9 395 unsigned long nicks_per_handle;
396 unsigned long password_min_length;
397 unsigned long password_min_digits;
398 unsigned long password_min_upper;
399 unsigned long password_min_lower;
400 unsigned long db_backup_frequency;
401 unsigned long handle_expire_frequency;
402 unsigned long autogag_duration;
403 unsigned long email_visible_level;
404 unsigned long cookie_timeout;
405 unsigned long handle_expire_delay;
406 unsigned long nochan_handle_expire_delay;
407 unsigned long modoper_level;
408 unsigned long set_epithet_level;
409 unsigned long set_title_level;
410 unsigned long set_fakehost_level;
411 unsigned long handles_per_email;
412 unsigned long email_search_level;
413 const char *network_name;
414 const char *titlehost_suffix;
415 regex_t valid_handle_regex;
416 regex_t valid_nick_regex;
bf93ca8d 417 regex_t valid_fakehost_regex;
d76ed9a9 418 dict_t weak_password_dict;
419 struct policer_params *auth_policer_params;
420 enum reclaim_action reclaim_action;
421 enum reclaim_action auto_reclaim_action;
422 unsigned long auto_reclaim_delay;
423 unsigned char default_maxlogins;
424 unsigned char hard_maxlogins;
5a1daaab 425 const char *auto_oper;
426 const char *auto_admin;
338a82b5 427 char default_style;
7637f48f 428 struct string_list *denied_fakehost_words;
d76ed9a9 429} nickserv_conf;
430
431/* We have 2^32 unique account IDs to use. */
432unsigned long int highest_id = 0;
433
434static char *
435canonicalize_hostmask(char *mask)
436{
437 char *out = mask, *temp;
438 if ((temp = strchr(mask, '!'))) {
439 temp++;
440 while (*temp) *out++ = *temp++;
441 *out++ = 0;
442 }
443 return mask;
444}
445
2362161a 446static struct handle_note *
447nickserv_add_note(const char *setter, time_t date, const char *text)
448{
449 struct handle_note *note = calloc(1, sizeof(*note) + strlen(text));
450
451 strncpy(note->setter, setter, sizeof(note->setter)-1);
452 note->date = date;
453 memcpy(note->note, text, strlen(text));
454 return note;
455}
456
d76ed9a9 457static struct handle_info *
458register_handle(const char *handle, const char *passwd, UNUSED_ARG(unsigned long id))
459{
460 struct handle_info *hi;
461
d76ed9a9 462 hi = calloc(1, sizeof(*hi));
463 hi->userlist_style = HI_DEFAULT_STYLE;
464 hi->announcements = '?';
465 hi->handle = strdup(handle);
466 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
467 hi->infoline = NULL;
468 dict_insert(nickserv_handle_dict, hi->handle, hi);
469
d76ed9a9 470 return hi;
471}
472
473static void
474register_nick(const char *nick, struct handle_info *owner)
475{
476 struct nick_info *ni;
477 ni = malloc(sizeof(struct nick_info));
478 safestrncpy(ni->nick, nick, sizeof(ni->nick));
479 ni->owner = owner;
480 ni->next = owner->nicks;
481 owner->nicks = ni;
482 dict_insert(nickserv_nick_dict, ni->nick, ni);
483}
484
d76ed9a9 485static void
486delete_nick(struct nick_info *ni)
487{
488 struct nick_info *last, *next;
489 struct userNode *user;
490 /* Check to see if we should mark a user as unregistered. */
491 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
492 user->modes &= ~FLAGS_REGNICK;
493 irc_regnick(user);
494 }
495 /* Remove ni from the nick_info linked list. */
496 if (ni == ni->owner->nicks) {
497 ni->owner->nicks = ni->next;
498 } else {
499 last = ni->owner->nicks;
500 next = last->next;
501 while (next != ni) {
502 last = next;
503 next = last->next;
504 }
505 last->next = next->next;
506 }
507 dict_remove(nickserv_nick_dict, ni->nick);
508}
509
510static unreg_func_t *unreg_func_list;
511static unsigned int unreg_func_size = 0, unreg_func_used = 0;
512
513void
514reg_unreg_func(unreg_func_t func)
515{
516 if (unreg_func_used == unreg_func_size) {
517 if (unreg_func_size) {
518 unreg_func_size <<= 1;
519 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
520 } else {
521 unreg_func_size = 8;
522 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
523 }
524 }
525 unreg_func_list[unreg_func_used++] = func;
526}
527
528static void
529nickserv_free_cookie(void *data)
530{
531 struct handle_cookie *cookie = data;
532 if (cookie->hi) cookie->hi->cookie = NULL;
533 if (cookie->data) free(cookie->data);
534 free(cookie);
535}
536
537static void
538free_handle_info(void *vhi)
539{
540 struct handle_info *hi = vhi;
541
d76ed9a9 542 free_string_list(hi->masks);
5177fd21 543 free_string_list(hi->ignores);
d76ed9a9 544 assert(!hi->users);
545
546 while (hi->nicks)
547 delete_nick(hi->nicks);
548 free(hi->infoline);
549 free(hi->epithet);
2362161a 550 free(hi->note);
d76ed9a9 551 free(hi->fakehost);
552 if (hi->cookie) {
553 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
554 nickserv_free_cookie(hi->cookie);
555 }
556 if (hi->email_addr) {
557 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
558 handle_info_list_remove(hil, hi);
559 if (!hil->used)
560 dict_remove(nickserv_email_dict, hi->email_addr);
561 }
562 free(hi);
563}
564
565static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
566
567static void
258d1427 568nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify, struct userNode *bot)
d76ed9a9 569{
570 unsigned int n;
a45e6ec7 571 struct userNode *uNode;
d76ed9a9 572
573 for (n=0; n<unreg_func_used; n++)
574 unreg_func_list[n](notify, hi);
a45e6ec7 575 while (hi->users) {
576 if (nickserv_conf.sync_log) {
577 uNode = GetUserH(hi->users->nick);
578 if (uNode)
579 irc_delete(uNode);
580 }
d76ed9a9 581 set_user_handle_info(hi->users, NULL, 0);
a45e6ec7 582 }
d76ed9a9 583 if (notify) {
584 if (nickserv_conf.disable_nicks)
258d1427 585 send_message(notify, bot, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
d76ed9a9 586 else
258d1427 587 send_message(notify, bot, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
d76ed9a9 588 }
8dc1d9ae 589
590 if (nickserv_conf.sync_log)
a45e6ec7 591 SyncLog("UNREGISTER %s", hi->handle);
8dc1d9ae 592
d76ed9a9 593 dict_remove(nickserv_handle_dict, hi->handle);
594}
595
596struct handle_info*
597get_handle_info(const char *handle)
598{
599 return dict_find(nickserv_handle_dict, handle, 0);
600}
601
602struct nick_info*
603get_nick_info(const char *nick)
604{
605 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
606}
607
608struct modeNode *
609find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
610{
611 unsigned int nn;
612 struct modeNode *mn;
613
614 for (nn=0; nn<channel->members.used; ++nn) {
615 mn = channel->members.list[nn];
616 if ((mn->user != except) && (mn->user->handle_info == handle))
617 return mn;
618 }
619 return NULL;
620}
621
622int
623oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
624 if (!user->handle_info) {
625 if (!quiet)
626 send_message(user, bot, "MSG_AUTHENTICATE");
627 return 0;
628 }
629
630 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
631 if (!quiet)
632 send_message(user, bot, "NSMSG_NO_ACCESS");
633 return 0;
634 }
635
636 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
637 if (!quiet)
638 send_message(user, bot, "MSG_OPER_SUSPENDED");
639 return 0;
640 }
641
642 if (user->handle_info->opserv_level < min_level) {
643 if (!quiet)
644 send_message(user, bot, "NSMSG_NO_ACCESS");
645 return 0;
646 }
647
648 return 1;
649}
650
651static int
652is_valid_handle(const char *handle)
653{
654 struct userNode *user;
655 /* cant register a juped nick/service nick as handle, to prevent confusion */
656 user = GetUserH(handle);
657 if (user && IsLocal(user))
658 return 0;
659 /* check against maximum length */
660 if (strlen(handle) > NICKSERV_HANDLE_LEN)
661 return 0;
662 /* for consistency, only allow account names that could be nicks */
663 if (!is_valid_nick(handle))
664 return 0;
665 /* disallow account names that look like bad words */
666 if (opserv_bad_channel(handle))
667 return 0;
668 /* test either regex or containing all valid chars */
669 if (nickserv_conf.valid_handle_regex_set) {
670 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
671 if (err) {
672 char buff[256];
673 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
674 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
675 }
676 return !err;
677 } else {
678 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
679 }
680}
681
682static int
683is_registerable_nick(const char *nick)
684{
685 /* make sure it could be used as an account name */
686 if (!is_valid_handle(nick))
687 return 0;
688 /* check length */
689 if (strlen(nick) > NICKLEN)
690 return 0;
691 /* test either regex or as valid handle */
692 if (nickserv_conf.valid_nick_regex_set) {
693 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
694 if (err) {
695 char buff[256];
696 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
697 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
698 }
699 return !err;
700 }
701 return 1;
702}
4c26ef3e 703/* this has been replaced with one in tools.c
d76ed9a9 704
705static int
706is_valid_email_addr(const char *email)
707{
708 return strchr(email, '@') != NULL;
709}
710
4c26ef3e 711*/
712
d76ed9a9 713static const char *
714visible_email_addr(struct userNode *user, struct handle_info *hi)
715{
716 if (hi->email_addr) {
717 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
718 return hi->email_addr;
719 } else {
720 return "Set.";
721 }
722 } else {
723 return "Not set.";
724 }
725}
726
727struct handle_info *
728smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
729{
730 struct handle_info *hi;
731 struct userNode *target;
732
733 switch (*name) {
734 case '*':
735 if (!(hi = get_handle_info(++name))) {
736 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
737 return 0;
738 }
739 return hi;
740 default:
741 if (!(target = GetUserH(name))) {
742 send_message(user, service, "MSG_NICK_UNKNOWN", name);
743 return 0;
744 }
745 if (IsLocal(target)) {
746 if (IsService(target))
747 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
748 else
749 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
750 return 0;
751 }
752 if (!(hi = target->handle_info)) {
753 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
754 return 0;
755 }
756 return hi;
757 }
758}
759
760int
c092fcad 761oper_outranks(struct svccmd *cmd, struct userNode *user, struct handle_info *hi) {
d76ed9a9 762 if (user->handle_info->opserv_level > hi->opserv_level)
763 return 1;
764 if (user->handle_info->opserv_level == hi->opserv_level) {
765 if ((user->handle_info->opserv_level == 1000)
766 || (user->handle_info == hi)
767 || ((user->handle_info->opserv_level == 0)
768 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
769 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
770 return 1;
771 }
772 }
c092fcad 773 reply("MSG_USER_OUTRANKED", hi->handle);
d76ed9a9 774 return 0;
775}
776
e3e5ba49 777struct handle_info *
c092fcad 778get_victim_oper(struct svccmd *cmd, struct userNode *user, const char *target)
d76ed9a9 779{
780 struct handle_info *hi;
781 if (!(hi = smart_get_handle_info(nickserv, user, target)))
782 return 0;
783 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
784 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
785 return 0;
786 }
c092fcad 787 return oper_outranks(cmd, user, hi) ? hi : NULL;
d76ed9a9 788}
789
790static int
791valid_user_for(struct userNode *user, struct handle_info *hi)
792{
793 unsigned int ii;
794
795 /* If no hostmasks on the account, allow it. */
796 if (!hi->masks->used)
797 return 1;
798 /* If any hostmask matches, allow it. */
799 for (ii=0; ii<hi->masks->used; ii++)
800 if (user_matches_glob(user, hi->masks->list[ii], 0))
801 return 1;
802 /* If they are allowauthed to this account, allow it (removing the aa). */
803 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
804 dict_remove(nickserv_allow_auth_dict, user->nick);
805 return 2;
806 }
807 /* The user is not allowed to use this account. */
808 return 0;
809}
810
811static int
812is_secure_password(const char *handle, const char *pass, struct userNode *user)
813{
814 unsigned int i, len;
815 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
697f4c9a 816 int p;
817
d76ed9a9 818 len = strlen(pass);
819 if (len < nickserv_conf.password_min_length) {
820 if (user)
821 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
822 return 0;
823 }
824 if (!irccasecmp(pass, handle)) {
825 if (user)
826 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
827 return 0;
828 }
697f4c9a 829 dict_find(nickserv_conf.weak_password_dict, pass, &p);
830 if (p) {
d76ed9a9 831 if (user)
832 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
833 return 0;
834 }
835 for (i=0; i<len; i++) {
836 if (isdigit(pass[i]))
837 cnt_digits++;
838 if (isupper(pass[i]))
839 cnt_upper++;
840 if (islower(pass[i]))
841 cnt_lower++;
842 }
843 if ((cnt_lower < nickserv_conf.password_min_lower)
844 || (cnt_upper < nickserv_conf.password_min_upper)
845 || (cnt_digits < nickserv_conf.password_min_digits)) {
846 if (user)
847 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
848 return 0;
849 }
850 return 1;
851}
852
853static auth_func_t *auth_func_list;
854static unsigned int auth_func_size = 0, auth_func_used = 0;
855
856void
857reg_auth_func(auth_func_t func)
858{
859 if (auth_func_used == auth_func_size) {
860 if (auth_func_size) {
861 auth_func_size <<= 1;
862 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
863 } else {
864 auth_func_size = 8;
865 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
866 }
867 }
868 auth_func_list[auth_func_used++] = func;
869}
870
871static handle_rename_func_t *rf_list;
872static unsigned int rf_list_size, rf_list_used;
873
874void
875reg_handle_rename_func(handle_rename_func_t func)
876{
877 if (rf_list_used == rf_list_size) {
878 if (rf_list_size) {
879 rf_list_size <<= 1;
880 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
881 } else {
882 rf_list_size = 8;
883 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
884 }
885 }
886 rf_list[rf_list_used++] = func;
887}
888
889static char *
890generate_fakehost(struct handle_info *handle)
891{
f16ad9e7 892 struct userNode *target;
d76ed9a9 893 extern const char *hidden_host_suffix;
894 static char buffer[HOSTLEN+1];
f16ad9e7 895 char *data;
896 int style = 1;
d76ed9a9 897
898 if (!handle->fakehost) {
f16ad9e7 899 data = conf_get_data("server/hidden_host_type", RECDB_QSTRING);
900 if (data)
901 style = atoi(data);
902
903 if (style == 1)
904 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
905 else if (style == 2) {
906 /* Due to the way fakehost is coded theres no way i can
907 get the exact user, so for now ill just take the first
908 authed user. */
909 for (target = handle->users; target; target = target->next_authed)
910 break;
911
912 snprintf(buffer, sizeof(buffer), "%s", target->crypthost);
913 }
d76ed9a9 914 return buffer;
915 } else if (handle->fakehost[0] == '.') {
916 /* A leading dot indicates the stored value is actually a title. */
917 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, nickserv_conf.titlehost_suffix);
918 return buffer;
919 }
920 return handle->fakehost;
921}
922
923static void
924apply_fakehost(struct handle_info *handle)
925{
926 struct userNode *target;
927 char *fake;
928
929 if (!handle->users)
930 return;
931 fake = generate_fakehost(handle);
932 for (target = handle->users; target; target = target->next_authed)
933 assign_fakehost(target, fake, 1);
934}
935
3fdd6a74 936void send_func_list(struct userNode *user)
937{
938 unsigned int n;
939 struct handle_info *old_info;
940
941 old_info = user->handle_info;
942
943 for (n=0; n<auth_func_used; n++)
944 auth_func_list[n](user, old_info);
945}
946
d76ed9a9 947static void
948set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
949{
950 unsigned int n;
951 struct handle_info *old_info;
952
953 /* This can happen if somebody uses COOKIE while authed, or if
954 * they re-auth to their current handle (which is silly, but users
955 * are like that). */
956 if (user->handle_info == hi)
957 return;
958
959 if (user->handle_info) {
960 struct userNode *other;
961
962 if (IsHelper(user))
963 userList_remove(&curr_helpers, user);
964
965 /* remove from next_authed linked list */
966 if (user->handle_info->users == user) {
967 user->handle_info->users = user->next_authed;
968 } else {
969 for (other = user->handle_info->users;
970 other->next_authed != user;
971 other = other->next_authed) ;
972 other->next_authed = user->next_authed;
973 }
974 /* if nobody left on old handle, and they're not an oper, remove !god */
975 if (!user->handle_info->users && !user->handle_info->opserv_level)
976 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
977 /* record them as being last seen at this time */
978 user->handle_info->lastseen = now;
979 /* and record their hostmask */
980 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
981 }
982 old_info = user->handle_info;
983 user->handle_info = hi;
984 if (hi && !hi->users && !hi->opserv_level)
985 HANDLE_CLEAR_FLAG(hi, HELPING);
c0b25bda 986
987 if (GetUserH(user->nick)) {
988 for (n=0; n<auth_func_used; n++)
989 auth_func_list[n](user, old_info);
3fdd6a74 990 } else
991 user->loc = 1;
c0b25bda 992
d76ed9a9 993 if (hi) {
994 struct nick_info *ni;
995
996 HANDLE_CLEAR_FLAG(hi, FROZEN);
997 if (nickserv_conf.warn_clone_auth) {
998 struct userNode *other;
999 for (other = hi->users; other; other = other->next_authed)
1000 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
1001 }
c0b25bda 1002
d76ed9a9 1003 user->next_authed = hi->users;
1004 hi->users = user;
1005 hi->lastseen = now;
1006 if (IsHelper(user))
1007 userList_append(&curr_helpers, user);
1008
1009 if (hi->fakehost || old_info)
1010 apply_fakehost(hi);
1011
1012 if (stamp) {
7827220c 1013#ifdef WITH_PROTOCOL_P10
d76ed9a9 1014 /* Stamp users with their account name. */
1015 char *id = hi->handle;
1016#else
1017 const char *id = "???";
1018#endif
1019 if (!nickserv_conf.disable_nicks) {
1020 struct nick_info *ni;
1021 for (ni = hi->nicks; ni; ni = ni->next) {
1022 if (!irccasecmp(user->nick, ni->nick)) {
1023 user->modes |= FLAGS_REGNICK;
1024 break;
1025 }
1026 }
1027 }
b21e2cfe 1028 StampUser(user, id, hi->registered);
d76ed9a9 1029 }
1030
1031 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
1032 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
1033 } else {
1034 /* We cannot clear the user's account ID, unfortunately. */
1035 user->next_authed = NULL;
1036 }
1037}
1038
1039static struct handle_info*
1040nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
1041{
1042 struct handle_info *hi;
1043 struct nick_info *ni;
1044 char crypted[MD5_CRYPT_LENGTH];
1045
1046 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
1047 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
1048 return 0;
1049 }
1050
d0cb2fb6 1051 if(strlen(handle) > 15)
1052 {
1053 send_message(user, nickserv, "NSMSG_HANDLE_TOLONG", handle, 15);
1054 return 0;
1055 }
1056
d76ed9a9 1057 if (!is_secure_password(handle, passwd, user))
1058 return 0;
1059
1060 cryptpass(passwd, crypted);
1061 hi = register_handle(handle, crypted, 0);
1062 hi->masks = alloc_string_list(1);
5177fd21 1063 hi->ignores = alloc_string_list(1);
d76ed9a9 1064 hi->users = NULL;
1065 hi->language = lang_C;
1066 hi->registered = now;
1067 hi->lastseen = now;
1068 hi->flags = HI_DEFAULT_FLAGS;
1069 if (settee && !no_auth)
1070 set_user_handle_info(settee, hi, 1);
1071
1072 if (user != settee)
1073 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
1074 else if (nickserv_conf.disable_nicks)
1075 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
1076 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
1077 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
1078 else {
1079 register_nick(user->nick, hi);
1080 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
1081 }
1082 if (settee && (user != settee))
1083 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
1084 return hi;
1085}
1086
1087static void
1088nickserv_bake_cookie(struct handle_cookie *cookie)
1089{
1090 cookie->hi->cookie = cookie;
1091 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
1092}
1093
51db18e0 1094/* Contributed by the great sneep of afternet ;) */
1095/* Since this gets used in a URL, we want to avoid stuff that confuses
1096 * email clients such as ] and ?. a-z, 0-9 only.
1097 */
1098void genpass(char *str, int len)
1099{
1100 int i = 0;
1101 char c = 0;
1102
1103 for(i = 0; i < len; i++)
1104 {
1105 do
1106 {
1107 c = (char)((float)rand() / (float)RAND_MAX * (float)256);
1108 } while(!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')));
1109 str[i] = c;
1110 }
1111 str[i] = '\0';
1112 return;
1113}
1114
d76ed9a9 1115static void
1c0d5243 1116nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data, int weblink)
d76ed9a9 1117{
1118 struct handle_cookie *cookie;
1119 char subject[128], body[4096], *misc;
1120 const char *netname, *fmt;
1121 int first_time = 0;
1122
1123 if (hi->cookie) {
1124 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1125 return;
1126 }
1127
1128 cookie = calloc(1, sizeof(*cookie));
1129 cookie->hi = hi;
1130 cookie->type = type;
1131 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
51db18e0 1132
d76ed9a9 1133 cookie->expires = now + nickserv_conf.cookie_timeout;
51db18e0 1134 /* Adding dedicated password gen function for more control -Rubin */
1135 genpass(cookie->cookie, 10);
1136 /*
1137 *inttobase64(cookie->cookie, rand(), 5);
1138 *inttobase64(cookie->cookie+5, rand(), 5);
1139 */
d76ed9a9 1140
1141 netname = nickserv_conf.network_name;
1142 subject[0] = 0;
1143
1144 switch (cookie->type) {
1145 case ACTIVATION:
1146 hi->passwd[0] = 0; /* invalidate password */
1147 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1148 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1149 snprintf(subject, sizeof(subject), fmt, netname);
1c0d5243 1150
1151 if(weblink)
1152 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY_WEB");
1153 else
1154 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1155
d76ed9a9 1156 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1157 first_time = 1;
1158 break;
1159 case PASSWORD_CHANGE:
1160 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1161 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1162 snprintf(subject, sizeof(subject), fmt, netname);
1c0d5243 1163 if(weblink)
1164 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY_WEB");
1165 else
1166 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
d76ed9a9 1167 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1168 break;
1169 case EMAIL_CHANGE:
1170 misc = hi->email_addr;
1171 hi->email_addr = cookie->data;
0212adab 1172#ifdef stupid_verify_old_email
d76ed9a9 1173 if (misc) {
1174 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1175 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1176 snprintf(subject, sizeof(subject), fmt, netname);
1177 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1178 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1179 sendmail(nickserv, hi, subject, body, 1);
1180 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1181 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1182 } else {
0212adab 1183#endif
d76ed9a9 1184 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1185 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1186 snprintf(subject, sizeof(subject), fmt, netname);
1187 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1188 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1189 sendmail(nickserv, hi, subject, body, 1);
1190 subject[0] = 0;
0212adab 1191#ifdef stupid_verify_old_email
d76ed9a9 1192 }
0212adab 1193#endif
d76ed9a9 1194 hi->email_addr = misc;
1195 break;
1196 case ALLOWAUTH:
1197 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1198 snprintf(subject, sizeof(subject), fmt, netname);
1199 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1200 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1201 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1202 break;
1203 default:
1204 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1205 break;
1206 }
1207 if (subject[0])
1208 sendmail(nickserv, hi, subject, body, first_time);
1209 nickserv_bake_cookie(cookie);
1210}
1211
1212static void
1213nickserv_eat_cookie(struct handle_cookie *cookie)
1214{
1215 cookie->hi->cookie = NULL;
1216 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1217 nickserv_free_cookie(cookie);
1218}
1219
1220static void
1221nickserv_free_email_addr(void *data)
1222{
1223 handle_info_list_clean(data);
1224 free(data);
1225}
1226
1227static void
1228nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1229{
1230 struct handle_info_list *hil;
1231 /* Remove from old handle_info_list ... */
1232 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1233 handle_info_list_remove(hil, hi);
1234 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1235 hi->email_addr = NULL;
1236 }
1237 /* Add to the new list.. */
1238 if (new_email_addr) {
1239 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1240 hil = calloc(1, sizeof(*hil));
1241 hil->tag = strdup(new_email_addr);
1242 handle_info_list_init(hil);
1243 dict_insert(nickserv_email_dict, hil->tag, hil);
1244 }
1245 handle_info_list_append(hil, hi);
1246 hi->email_addr = hil->tag;
1247 }
1248}
1249
1250static NICKSERV_FUNC(cmd_register)
1251{
2f61d1d7 1252 irc_in_addr_t ip;
d76ed9a9 1253 struct handle_info *hi;
1254 const char *email_addr, *password;
eb5d6b73 1255 char syncpass[MD5_CRYPT_LENGTH];
1c0d5243 1256 int no_auth, weblink;
d76ed9a9 1257
08895577 1258 if (checkDefCon(DEFCON_NO_NEW_NICKS) && !IsOper(user)) {
1259 reply("NSMSG_DEFCON_NO_NEW_NICKS", nickserv_conf.disable_nicks ? "accounts" : "nicknames");
1260 return 0;
1261 }
1262
d76ed9a9 1263 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1264 /* Require the first handle registered to belong to someone +o. */
1265 reply("NSMSG_REQUIRE_OPER");
1266 return 0;
1267 }
1268
1269 if (user->handle_info) {
1270 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1271 return 0;
1272 }
1273
1274 if (IsRegistering(user)) {
1275 reply("NSMSG_ALREADY_REGISTERING");
1276 return 0;
1277 }
1278
1279 if (IsStamped(user)) {
1280 /* Unauthenticated users might still have been stamped
1281 previously and could therefore have a hidden host;
1282 do not allow them to register a new account. */
1283 reply("NSMSG_STAMPED_REGISTER");
1284 return 0;
1285 }
1286
1287 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1288
1289 if (!is_valid_handle(argv[1])) {
1290 reply("NSMSG_BAD_HANDLE", argv[1]);
1291 return 0;
1292 }
1293
1c0d5243 1294
d76ed9a9 1295 if ((argc >= 4) && nickserv_conf.email_enabled) {
1296 struct handle_info_list *hil;
1297 const char *str;
1298
1299 /* Remember email address. */
1300 email_addr = argv[3];
1301
1302 /* Check that the email address looks valid.. */
4c26ef3e 1303 if (!valid_email(email_addr)) {
d76ed9a9 1304 reply("NSMSG_BAD_EMAIL_ADDR");
1305 return 0;
1306 }
1307
1308 /* .. and that we are allowed to send to it. */
1309 if ((str = sendmail_prohibited_address(email_addr))) {
1310 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1311 return 0;
1312 }
1313
1314 /* If we do email verify, make sure we don't spam the address. */
1315 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1316 unsigned int nn;
1317 for (nn=0; nn<hil->used; nn++) {
1318 if (hil->list[nn]->cookie) {
1319 reply("NSMSG_EMAIL_UNACTIVATED");
1320 return 0;
1321 }
1322 }
1323 if (hil->used >= nickserv_conf.handles_per_email) {
1324 reply("NSMSG_EMAIL_OVERUSED");
1325 return 0;
1326 }
1327 }
1328
1329 no_auth = 1;
1330 } else {
1331 email_addr = 0;
1332 no_auth = 0;
1333 }
1334
1335 password = argv[2];
1336 argv[2] = "****";
1c0d5243 1337 /* Webregister hack - send URL instead of IRC cookie
1338 * commands in email
1339 */
1340 if((argc >= 5) && !strcmp(argv[4],"WEBLINK"))
1341 weblink = 1;
1342 else
1343 weblink = 0;
d76ed9a9 1344 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1345 return 0;
1346 /* Add any masks they should get. */
1347 if (nickserv_conf.default_hostmask) {
1348 string_list_append(hi->masks, strdup("*@*"));
1349 } else {
1350 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
2f61d1d7 1351 if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
d76ed9a9 1352 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1353 }
1354
1355 /* If they're the first to register, give them level 1000. */
1356 if (dict_size(nickserv_handle_dict) == 1) {
1357 hi->opserv_level = 1000;
1358 reply("NSMSG_ROOT_HANDLE", argv[1]);
1359 }
1360
1361 /* Set their email address. */
1362 if (email_addr)
1363 nickserv_set_email_addr(hi, email_addr);
1364
1365 /* If they need to do email verification, tell them. */
1366 if (no_auth)
1c0d5243 1367 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd, weblink);
d76ed9a9 1368
1369 /* Set registering flag.. */
1370 user->modes |= FLAGS_REGISTERING;
1371
8dc1d9ae 1372 if (nickserv_conf.sync_log) {
1373 cryptpass(password, syncpass);
d8d3ee73 1374 /*
a45e6ec7 1375 * An 0 is only sent if theres no email address. Thios should only happen if email functions are
d8d3ee73 1376 * disabled which they wont be for us. Email Required MUST be set on if you are using this.
1377 * -SiRVulcaN
1378 */
1379 SyncLog("REGISTER %s %s %s %s", hi->handle, syncpass, email_addr ? email_addr : "0", user->info);
8dc1d9ae 1380 }
a03d6c77 1381
1382 /* this wont work if email is required .. */
ac3bdc8d 1383 process_adduser_pending(user);
eb5d6b73 1384
d76ed9a9 1385 return 1;
1386}
1387
1388static NICKSERV_FUNC(cmd_oregister)
1389{
d762299d 1390 struct userNode *settee = NULL;
d76ed9a9 1391 struct handle_info *hi;
d762299d 1392 char* account = NULL;
1393 char* pass = NULL;
1394 char* email = NULL;
1395 char* mask = NULL;
1396 char* nick = NULL;
1397
1398 NICKSERV_MIN_PARMS(3);
1399
1400 account = argv[1];
1401 pass = argv[2];
ac5cb8c5 1402 if (nickserv_conf.email_required) {
d762299d 1403 NICKSERV_MIN_PARMS(4);
1404 email = argv[3];
1405 if (argc >= 5) {/* take: "acct pass email mask nick" or "acct pass email mask" or "acct pass email nick" */
1406 if (strchr(argv[4], '@') || argc >= 6) /* If @, its mask not nick */
1407 mask = argv[4];
1408 else
1409 nick = argv[4];
1410 }
1411 if (argc >= 6) {
1412 nick = argv[5];
ac5cb8c5 1413 }
1414 }
d762299d 1415 else {
1416 if (argc >= 4) {/* take: "account pass mask nick" or "account pass mask" or "account pass nick" */
1417 if (strchr(argv[3], '@') || argc >= 5) /* If @, its mask not nick */
1418 mask = argv[3];
1419 else
1420 nick = argv[3];
1421 }
1422 if (argc >= 5) {
1423 nick = argv[4];
1424 }
d76ed9a9 1425 }
d762299d 1426 /* If they passed a nick, look for that user.. */
1427 if (nick && !(settee = GetUserH(nick))) {
1428 reply("MSG_NICK_UNKNOWN", argv[4]);
1429 return 0;
1430 }
1431 /* If the setee is already authed, we cant add a 2nd account for them.. */
d76ed9a9 1432 if (settee && settee->handle_info) {
1433 reply("NSMSG_USER_PREV_AUTH", settee->nick);
d76ed9a9 1434 return 0;
1435 }
d762299d 1436 /* If there is no default mask in the conf, and they didn't pass a mask,
1437 * but we did find a user by nick, generate the mask */
1438 if (!mask) {
1439 if (nickserv_conf.default_hostmask)
1440 mask = "*@*";
1441 else if (settee)
1442 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1443 else {
1444 reply("NSMSG_REGISTER_BAD_NICKMASK");
1445 return 0;
ac5cb8c5 1446 }
d76ed9a9 1447 }
d762299d 1448
1449 if (!(hi = nickserv_register(user, settee, account, pass, 0))) {
1450 return 0; /* error reply handled by above */
1451 }
1452 if (email) {
1453 nickserv_set_email_addr(hi, email);
1454 }
1455 if (mask) {
1456 char* mask_canonicalized = canonicalize_hostmask(strdup(mask));
1457 string_list_append(hi->masks, mask_canonicalized);
1458 }
1459
1460 if (nickserv_conf.sync_log)
1461 SyncLog("REGISTER %s %s %s %s", hi->handle, hi->passwd, email ? email : "@", user->info); /* Send just @ for email if none */
d76ed9a9 1462 return 1;
1463}
1464
5177fd21 1465static int
c092fcad 1466nickserv_ignore(struct svccmd *cmd, struct userNode *user, struct handle_info *hi, char *mask)
5177fd21 1467{
1468 unsigned int i;
92fac64c 1469 struct userNode *target;
d07e3fff 1470 char *new_mask = strdup(pretty_mask(mask));
5177fd21 1471 for (i=0; i<hi->ignores->used; i++) {
1472 if (!irccasecmp(new_mask, hi->ignores->list[i])) {
c092fcad 1473 reply("NSMSG_ADDIGNORE_ALREADY", new_mask);
d07e3fff 1474 free(new_mask);
5177fd21 1475 return 0;
1476 }
1477 }
1478 string_list_append(hi->ignores, new_mask);
c092fcad 1479 reply("NSMSG_ADDIGNORE_SUCCESS", new_mask);
5177fd21 1480
92fac64c 1481 for (target = hi->users; target; target = target->next_authed) {
1482 irc_silence(target, new_mask, 1);
1483 }
5177fd21 1484 return 1;
1485}
1486
1487static NICKSERV_FUNC(cmd_addignore)
1488{
1489 NICKSERV_MIN_PARMS(2);
1490
c092fcad 1491 return nickserv_ignore(cmd, user, user->handle_info, argv[1]);
5177fd21 1492}
1493
1494static NICKSERV_FUNC(cmd_oaddignore)
1495{
1496 struct handle_info *hi;
1497
1498 NICKSERV_MIN_PARMS(3);
c092fcad 1499 if (!(hi = get_victim_oper(cmd, user, argv[1])))
5177fd21 1500 return 0;
668dc38e 1501
c092fcad 1502 return nickserv_ignore(cmd, user, hi, argv[2]);
5177fd21 1503}
1504
1505static int
c092fcad 1506nickserv_delignore(struct svccmd *cmd, struct userNode *user, struct handle_info *hi, char *del_mask)
5177fd21 1507{
1508 unsigned int i;
92fac64c 1509 struct userNode *target;
d07e3fff 1510 char *pmask = strdup(pretty_mask(del_mask));
5177fd21 1511 for (i=0; i<hi->ignores->used; i++) {
d07e3fff 1512 if (!strcmp(pmask, hi->ignores->list[i]) || !strcmp(del_mask, hi->ignores->list[i])) {
5177fd21 1513 char *old_mask = hi->ignores->list[i];
1514 hi->ignores->list[i] = hi->ignores->list[--hi->ignores->used];
c092fcad 1515 reply("NSMSG_DELMASK_SUCCESS", old_mask);
92fac64c 1516 for (target = hi->users; target; target = target->next_authed) {
3f5b8801 1517 irc_silence(target, old_mask, 0);
92fac64c 1518 }
d07e3fff 1519 free(old_mask);
1520 free(pmask);
5177fd21 1521 return 1;
1522 }
1523 }
c092fcad 1524 reply("NSMSG_DELMASK_NOT_FOUND");
5177fd21 1525 return 0;
1526}
1527
1528static NICKSERV_FUNC(cmd_delignore)
1529{
1530 NICKSERV_MIN_PARMS(2);
c092fcad 1531 return nickserv_delignore(cmd, user, user->handle_info, argv[1]);
5177fd21 1532}
1533
1534static NICKSERV_FUNC(cmd_odelignore)
1535{
1536 struct handle_info *hi;
1537 NICKSERV_MIN_PARMS(3);
c092fcad 1538 if (!(hi = get_victim_oper(cmd, user, argv[1])))
5177fd21 1539 return 0;
c092fcad 1540 return nickserv_delignore(cmd, user, hi, argv[2]);
5177fd21 1541}
1542
d76ed9a9 1543static NICKSERV_FUNC(cmd_handleinfo)
1544{
1545 char buff[400];
1546 unsigned int i, pos=0, herelen;
1547 struct userNode *target, *next_un;
1548 struct handle_info *hi;
1549 const char *nsmsg_none;
1550
1551 if (argc < 2) {
1552 if (!(hi = user->handle_info)) {
1553 reply("NSMSG_MUST_AUTH");
1554 return 0;
1555 }
1556 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1557 return 0;
1558 }
1559
1560 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1561 reply("NSMSG_HANDLEINFO_ON", hi->handle);
de9510bc 1562 reply("MSG_BAR");
d76ed9a9 1563 reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi->registered));
1564
1565 if (!hi->users) {
1566 intervalString(buff, now - hi->lastseen, user->handle_info);
1567 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1568 } else {
1569 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1570 }
1571
1572 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1573 if (HANDLE_FLAGGED(hi, FROZEN))
1574 reply("NSMSG_HANDLEINFO_VACATION");
1575
1576 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1577 struct do_not_register *dnr;
1578 if ((dnr = chanserv_is_dnr(NULL, hi)))
1579 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
c092fcad 1580 if (!oper_outranks(cmd, user, hi))
d76ed9a9 1581 return 1;
684e2f02 1582 } else if (hi != user->handle_info) {
1583 reply("NSMSG_HANDLEINFO_END");
d76ed9a9 1584 return 1;
684e2f02 1585 }
d76ed9a9 1586
1587 if (nickserv_conf.email_enabled)
1588 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1589
1590 if (hi->cookie) {
1591 const char *type;
1592 switch (hi->cookie->type) {
1593 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1594 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1595 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1596 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1597 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1598 }
1599 reply(type);
1600 }
1601
1602 if (hi->flags) {
1603 unsigned long flen = 1;
1604 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1605 flags[0] = '+';
1606 for (i=0, flen=1; handle_flags[i]; i++)
1607 if (hi->flags & 1 << i)
1608 flags[flen++] = handle_flags[i];
1609 flags[flen] = 0;
1610 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1611 } else {
1612 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1613 }
1614
1615 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1616 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1617 || (hi->opserv_level > 0)) {
1618 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1619 }
1620
2362161a 1621 if (IsHelping(user) || IsOper(user))
1622 {
1623 if (hi->note)
1624 {
1625 char date[64];
1626 strftime(date, 64, "%b %d %Y", localtime(&hi->note->date));
1627 reply("NSMSG_HANDLEINFO_NOTE", hi->note->setter, date, hi->note->note);
1628 }
1629 }
1630
d76ed9a9 1631 if (hi->fakehost)
1632 reply("NSMSG_HANDLEINFO_FAKEHOST", (hi->fakehost ? hi->fakehost : handle_find_message(hi, "MSG_NONE")));
1633
1634 if (hi->last_quit_host[0])
1635 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1636 else
1637 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1638
1639 if (nickserv_conf.disable_nicks) {
1640 /* nicks disabled; don't show anything about registered nicks */
1641 } else if (hi->nicks) {
1642 struct nick_info *ni, *next_ni;
1643 for (ni = hi->nicks; ni; ni = next_ni) {
1644 herelen = strlen(ni->nick);
1645 if (pos + herelen + 1 > ArrayLength(buff)) {
1646 next_ni = ni;
1647 goto print_nicks_buff;
1648 } else {
1649 next_ni = ni->next;
1650 }
1651 memcpy(buff+pos, ni->nick, herelen);
1652 pos += herelen; buff[pos++] = ' ';
1653 if (!next_ni) {
1654 print_nicks_buff:
1655 buff[pos-1] = 0;
1656 reply("NSMSG_HANDLEINFO_NICKS", buff);
1657 pos = 0;
1658 }
1659 }
1660 } else {
1661 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1662 }
1663
1664 if (hi->masks->used) {
1665 for (i=0; i < hi->masks->used; i++) {
1666 herelen = strlen(hi->masks->list[i]);
1667 if (pos + herelen + 1 > ArrayLength(buff)) {
1668 i--;
1669 goto print_mask_buff;
1670 }
1671 memcpy(buff+pos, hi->masks->list[i], herelen);
1672 pos += herelen; buff[pos++] = ' ';
1673 if (i+1 == hi->masks->used) {
1674 print_mask_buff:
1675 buff[pos-1] = 0;
1676 reply("NSMSG_HANDLEINFO_MASKS", buff);
1677 pos = 0;
1678 }
1679 }
1680 } else {
1681 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1682 }
1683
5177fd21 1684 if (hi->ignores->used) {
1685 for (i=0; i < hi->ignores->used; i++) {
1686 herelen = strlen(hi->ignores->list[i]);
1687 if (pos + herelen + 1 > ArrayLength(buff)) {
1688 i--;
1689 goto print_ignore_buff;
1690 }
1691 memcpy(buff+pos, hi->ignores->list[i], herelen);
1692 pos += herelen; buff[pos++] = ' ';
1693 if (i+1 == hi->ignores->used) {
1694 print_ignore_buff:
1695 buff[pos-1] = 0;
1696 reply("NSMSG_HANDLEINFO_IGNORES", buff);
1697 pos = 0;
1698 }
1699 }
1700 } else {
1701 reply("NSMSG_HANDLEINFO_IGNORES", nsmsg_none);
1702 }
1703
d76ed9a9 1704 if (hi->channels) {
1705 struct userData *channel, *next;
1706 char *name;
1707
1708 for (channel = hi->channels; channel; channel = next) {
1709 next = channel->u_next;
1710 name = channel->channel->channel->name;
1711 herelen = strlen(name);
1712 if (pos + herelen + 7 > ArrayLength(buff)) {
1713 next = channel;
1714 goto print_chans_buff;
1715 }
1716 if (IsUserSuspended(channel))
1717 buff[pos++] = '-';
de9510bc 1718 pos += sprintf(buff+pos, "%s:%s ", user_level_name_from_level(channel->access), name);
d76ed9a9 1719 if (next == NULL) {
1720 print_chans_buff:
1721 buff[pos-1] = 0;
1722 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1723 pos = 0;
1724 }
1725 }
1726 } else {
1727 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1728 }
1729
1730 for (target = hi->users; target; target = next_un) {
1731 herelen = strlen(target->nick);
1732 if (pos + herelen + 1 > ArrayLength(buff)) {
1733 next_un = target;
1734 goto print_cnick_buff;
1735 } else {
1736 next_un = target->next_authed;
1737 }
1738 memcpy(buff+pos, target->nick, herelen);
1739 pos += herelen; buff[pos++] = ' ';
1740 if (!next_un) {
1741 print_cnick_buff:
1742 buff[pos-1] = 0;
1743 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1744 pos = 0;
1745 }
1746 }
1747
de9510bc 1748 reply("NSMSG_HANDLEINFO_END");
d76ed9a9 1749 return 1;
1750}
1751
1752static NICKSERV_FUNC(cmd_userinfo)
1753{
1754 struct userNode *target;
1755
1756 NICKSERV_MIN_PARMS(2);
1757 if (!(target = GetUserH(argv[1]))) {
1758 reply("MSG_NICK_UNKNOWN", argv[1]);
1759 return 0;
1760 }
1761 if (target->handle_info)
1762 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1763 else
1764 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1765 return 1;
1766}
1767
1768static NICKSERV_FUNC(cmd_nickinfo)
1769{
1770 struct nick_info *ni;
1771
1772 NICKSERV_MIN_PARMS(2);
1773 if (!(ni = get_nick_info(argv[1]))) {
1774 reply("MSG_NICK_UNKNOWN", argv[1]);
1775 return 0;
1776 }
1777 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1778 return 1;
1779}
1780
1781static NICKSERV_FUNC(cmd_rename_handle)
1782{
1783 struct handle_info *hi;
a45e6ec7 1784 struct userNode *uNode;
57692f5e 1785 char *old_handle;
d76ed9a9 1786 unsigned int nn;
1787
1788 NICKSERV_MIN_PARMS(3);
c092fcad 1789 if (!(hi = get_victim_oper(cmd, user, argv[1])))
d76ed9a9 1790 return 0;
1791 if (!is_valid_handle(argv[2])) {
1792 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1793 return 0;
1794 }
1795 if (get_handle_info(argv[2])) {
1796 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1797 return 0;
1798 }
d0cb2fb6 1799 if(strlen(argv[2]) > 15)
1800 {
1801 reply("NMSG_HANDLE_TOLONG", argv[2], 15);
1802 return 0;
1803 }
d76ed9a9 1804
1805 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1806 hi->handle = strdup(argv[2]);
1807 dict_insert(nickserv_handle_dict, hi->handle, hi);
1808 for (nn=0; nn<rf_list_used; nn++)
1809 rf_list[nn](hi, old_handle);
a45e6ec7 1810
1811 if (nickserv_conf.sync_log) {
1812 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
1813 irc_rename(uNode, hi->handle);
1814
4ae3fc8b 1815 SyncLog("RENAME %s %s", old_handle, hi->handle);
a45e6ec7 1816 }
1817
d76ed9a9 1818 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
57692f5e 1819 global_message_args(MESSAGE_RECIPIENT_OPERS, "NSMSG_ACCOUNT_RENAMED",
1820 user->handle_info->handle, old_handle, hi->handle);
1821
d76ed9a9 1822 free(old_handle);
1823 return 1;
1824}
1825
1826static failpw_func_t *failpw_func_list;
1827static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1828
1829void
1830reg_failpw_func(failpw_func_t func)
1831{
1832 if (failpw_func_used == failpw_func_size) {
1833 if (failpw_func_size) {
1834 failpw_func_size <<= 1;
1835 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1836 } else {
1837 failpw_func_size = 8;
1838 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1839 }
1840 }
1841 failpw_func_list[failpw_func_used++] = func;
1842}
1843
d9cd0e9d 1844/*
b21e2cfe 1845 * Return hi if the handle/pass pair matches, NULL if it doesnt.
d9cd0e9d 1846 *
1847 * called by nefariouses enhanced AC login-on-connect code
1848 *
1849 */
b21e2cfe 1850struct handle_info *loc_auth(char *handle, char *password)
d9cd0e9d 1851{
1852 int pw_arg, used, maxlogins;
b21e2cfe 1853 unsigned int ii;
1854 int wildmask = 0;
d9cd0e9d 1855 struct handle_info *hi;
d9cd0e9d 1856 struct userNode *other;
d9cd0e9d 1857
1858 hi = dict_find(nickserv_handle_dict, handle, NULL);
1859 pw_arg = 2;
1860 if (!hi) {
b21e2cfe 1861 return NULL;
1862 }
1863
1864 /* We don't know the users hostname, or anything because they
1865 * havn't registered yet. So we can only allow LOC if your
1866 * account has *@* as a hostmask.
1867 */
1868 for (ii=0; ii<hi->masks->used; ii++)
1869 {
1870 if (!strcmp(hi->masks->list[ii], "*@*"))
1871 {
1872 wildmask++;
1873 break;
1874 }
d9cd0e9d 1875 }
b21e2cfe 1876 if(wildmask < 1)
1877 return NULL;
1878
d9cd0e9d 1879 /* Responses from here on look up the language used by the handle they asked about. */
1880 if (!checkpass(password, hi->passwd)) {
b21e2cfe 1881 return NULL;
d9cd0e9d 1882 }
1883 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
b21e2cfe 1884 return NULL;
d9cd0e9d 1885 }
1886 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
d9cd0e9d 1887 for (used = 0, other = hi->users; other; other = other->next_authed) {
1888 if (++used >= maxlogins) {
b21e2cfe 1889 return NULL;
d9cd0e9d 1890 }
1891 }
b21e2cfe 1892 return hi;
d9cd0e9d 1893}
1894
d76ed9a9 1895static NICKSERV_FUNC(cmd_auth)
1896{
1897 int pw_arg, used, maxlogins;
1898 struct handle_info *hi;
1899 const char *passwd;
1900 struct userNode *other;
1901
1902 if (user->handle_info) {
1903 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1904 return 0;
1905 }
1906 if (IsStamped(user)) {
1907 /* Unauthenticated users might still have been stamped
1908 previously and could therefore have a hidden host;
1909 do not allow them to authenticate. */
1910 reply("NSMSG_STAMPED_AUTH");
1911 return 0;
1912 }
1913 if (argc == 3) {
1914 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1915 pw_arg = 2;
1916 } else if (argc == 2) {
1917 if (nickserv_conf.disable_nicks) {
1918 if (!(hi = get_handle_info(user->nick))) {
1919 reply("NSMSG_HANDLE_NOT_FOUND");
1920 return 0;
1921 }
1922 } else {
1923 /* try to look up their handle from their nick */
1924 struct nick_info *ni;
1925 ni = get_nick_info(user->nick);
1926 if (!ni) {
1927 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1928 return 0;
1929 }
1930 hi = ni->owner;
1931 }
1932 pw_arg = 1;
1933 } else {
1934 reply("MSG_MISSING_PARAMS", argv[0]);
b1bf690d 1935 svccmd_send_help_brief(user, nickserv, cmd);
d76ed9a9 1936 return 0;
1937 }
1938 if (!hi) {
1939 reply("NSMSG_HANDLE_NOT_FOUND");
1940 return 0;
1941 }
1942 /* Responses from here on look up the language used by the handle they asked about. */
1943 passwd = argv[pw_arg];
1944 if (!valid_user_for(user, hi)) {
1945 if (hi->email_addr && nickserv_conf.email_enabled)
1946 send_message_type(4, user, cmd->parent->bot,
1947 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1948 hi->handle);
1949 else
1950 send_message_type(4, user, cmd->parent->bot,
1951 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1952 hi->handle);
1953 argv[pw_arg] = "BADMASK";
1954 return 1;
1955 }
1956 if (!checkpass(passwd, hi->passwd)) {
1957 unsigned int n;
1958 send_message_type(4, user, cmd->parent->bot,
1959 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1960 argv[pw_arg] = "BADPASS";
1961 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1962 if (nickserv_conf.autogag_enabled) {
1963 if (!user->auth_policer.params) {
1964 user->auth_policer.last_req = now;
1965 user->auth_policer.params = nickserv_conf.auth_policer_params;
1966 }
1967 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1968 char *hostmask;
1969 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1970 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1971 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1972 free(hostmask);
1973 argv[pw_arg] = "GAGGED";
1974 }
1975 }
1976 return 1;
1977 }
1978 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1979 send_message_type(4, user, cmd->parent->bot,
1980 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1981 argv[pw_arg] = "SUSPENDED";
1982 return 1;
1983 }
1984 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1985 for (used = 0, other = hi->users; other; other = other->next_authed) {
1986 if (++used >= maxlogins) {
1987 send_message_type(4, user, cmd->parent->bot,
1988 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1989 maxlogins);
1990 argv[pw_arg] = "MAXLOGINS";
1991 return 1;
1992 }
1993 }
1994
1995 set_user_handle_info(user, hi, 1);
1996 if (nickserv_conf.email_required && !hi->email_addr)
1997 reply("NSMSG_PLEASE_SET_EMAIL");
1998 if (!is_secure_password(hi->handle, passwd, NULL))
1999 reply("NSMSG_WEAK_PASSWORD");
2000 if (hi->passwd[0] != '$')
2001 cryptpass(passwd, hi->passwd);
ac3bdc8d 2002
5a1daaab 2003 /* If a channel was waiting for this user to auth,
2004 * finish adding them */
ac3bdc8d 2005 process_adduser_pending(user);
5a1daaab 2006
2007 reply("NSMSG_AUTH_SUCCESS");
2008
7fdb7639 2009
2010 /* Set +x if autohide is on */
2011 if(HANDLE_FLAGGED(hi, AUTOHIDE))
2012 irc_umode(user, "+x");
2013
2014 if(!IsOper(user)) /* If they arnt already opered.. */
5a1daaab 2015 {
2016 /* Auto Oper users with Opserv access -Life4Christ 8-10-2005 */
2017 if( nickserv_conf.auto_admin[0] && hi->opserv_level >= opserv_conf_admin_level())
2018 {
2019 irc_umode(user,nickserv_conf.auto_admin);
2020 reply("NSMSG_AUTO_OPER_ADMIN");
2021 }
2022 else if (nickserv_conf.auto_oper[0] && hi->opserv_level > 0)
2023 {
2024 irc_umode(user,nickserv_conf.auto_oper);
2025 reply("NSMSG_AUTO_OPER");
2026 }
2027 }
2028
2029 /* Wipe out the pass for the logs */
d76ed9a9 2030 argv[pw_arg] = "****";
2031 return 1;
2032}
2033
2034static allowauth_func_t *allowauth_func_list;
2035static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
2036
2037void
2038reg_allowauth_func(allowauth_func_t func)
2039{
2040 if (allowauth_func_used == allowauth_func_size) {
2041 if (allowauth_func_size) {
2042 allowauth_func_size <<= 1;
2043 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
2044 } else {
2045 allowauth_func_size = 8;
2046 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
2047 }
2048 }
2049 allowauth_func_list[allowauth_func_used++] = func;
2050}
2051
2052static NICKSERV_FUNC(cmd_allowauth)
2053{
2054 struct userNode *target;
2055 struct handle_info *hi;
2056 unsigned int n;
2057
2058 NICKSERV_MIN_PARMS(2);
2059 if (!(target = GetUserH(argv[1]))) {
2060 reply("MSG_NICK_UNKNOWN", argv[1]);
2061 return 0;
2062 }
2063 if (target->handle_info) {
2064 reply("NSMSG_USER_PREV_AUTH", target->nick);
2065 return 0;
2066 }
2067 if (IsStamped(target)) {
2068 /* Unauthenticated users might still have been stamped
2069 previously and could therefore have a hidden host;
2070 do not allow them to authenticate to an account. */
2071 reply("NSMSG_USER_PREV_STAMP", target->nick);
2072 return 0;
2073 }
2074 if (argc == 2)
2075 hi = NULL;
2076 else if (!(hi = get_handle_info(argv[2]))) {
2077 reply("MSG_HANDLE_UNKNOWN", argv[2]);
2078 return 0;
2079 }
2080 if (hi) {
2081 if (hi->opserv_level > user->handle_info->opserv_level) {
2082 reply("MSG_USER_OUTRANKED", hi->handle);
2083 return 0;
2084 }
2085 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
2086 || (hi->opserv_level > 0))
2087 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
2088 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
2089 return 0;
2090 }
2091 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
2092 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
2093 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
2094 if (nickserv_conf.email_enabled)
2095 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
2096 } else {
2097 if (dict_remove(nickserv_allow_auth_dict, target->nick))
2098 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
2099 else
2100 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
2101 }
2102 for (n=0; n<allowauth_func_used; n++)
2103 allowauth_func_list[n](user, target, hi);
2104 return 1;
2105}
2106
2107static NICKSERV_FUNC(cmd_authcookie)
2108{
2109 struct handle_info *hi;
2110
2111 NICKSERV_MIN_PARMS(2);
2112 if (user->handle_info) {
2113 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
2114 return 0;
2115 }
2116 if (IsStamped(user)) {
2117 /* Unauthenticated users might still have been stamped
2118 previously and could therefore have a hidden host;
2119 do not allow them to authenticate to an account. */
2120 reply("NSMSG_STAMPED_AUTHCOOKIE");
2121 return 0;
2122 }
2123 if (!(hi = get_handle_info(argv[1]))) {
2124 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2125 return 0;
2126 }
2127 if (!hi->email_addr) {
2128 reply("MSG_SET_EMAIL_ADDR");
2129 return 0;
2130 }
1c0d5243 2131 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL, 0);
d76ed9a9 2132 return 1;
2133}
2134
2135static NICKSERV_FUNC(cmd_delcookie)
2136{
2137 struct handle_info *hi;
2138
2139 hi = user->handle_info;
2140 if (!hi->cookie) {
2141 reply("NSMSG_NO_COOKIE");
2142 return 0;
2143 }
2144 switch (hi->cookie->type) {
2145 case ACTIVATION:
2146 case EMAIL_CHANGE:
2147 reply("NSMSG_MUST_TIME_OUT");
2148 break;
2149 default:
2150 nickserv_eat_cookie(hi->cookie);
2151 reply("NSMSG_ATE_COOKIE");
2152 break;
2153 }
2154 return 1;
2155}
2156
34938510 2157static NICKSERV_FUNC(cmd_odelcookie)
2158{
2159 struct handle_info *hi;
2160
2161 NICKSERV_MIN_PARMS(2);
2162
c092fcad 2163 if (!(hi = get_victim_oper(cmd, user, argv[1])))
34938510 2164 return 0;
2165
2166 if (!hi->cookie) {
2167 reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle);
2168 return 0;
2169 }
2170
2171 nickserv_eat_cookie(hi->cookie);
2172 reply("NSMSG_ATE_FOREIGN_COOKIE", hi->handle);
2173
2174 return 1;
2175}
2176
d76ed9a9 2177static NICKSERV_FUNC(cmd_resetpass)
2178{
2179 struct handle_info *hi;
2180 char crypted[MD5_CRYPT_LENGTH];
1c0d5243 2181 int weblink;
d76ed9a9 2182
2183 NICKSERV_MIN_PARMS(3);
1c0d5243 2184 if(argc >= 4 && !strcmp(argv[3], "WEBLINK"))
2185 weblink = 1;
2186 else
2187 weblink = 0;
d76ed9a9 2188 if (user->handle_info) {
2189 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
2190 return 0;
2191 }
2192 if (IsStamped(user)) {
2193 /* Unauthenticated users might still have been stamped
2194 previously and could therefore have a hidden host;
2195 do not allow them to activate an account. */
2196 reply("NSMSG_STAMPED_RESETPASS");
2197 return 0;
2198 }
2199 if (!(hi = get_handle_info(argv[1]))) {
2200 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2201 return 0;
2202 }
2203 if (!hi->email_addr) {
2204 reply("MSG_SET_EMAIL_ADDR");
2205 return 0;
2206 }
2207 cryptpass(argv[2], crypted);
2208 argv[2] = "****";
1c0d5243 2209 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted, weblink);
d76ed9a9 2210 return 1;
2211}
2212
2213static NICKSERV_FUNC(cmd_cookie)
2214{
2215 struct handle_info *hi;
2216 const char *cookie;
2217
2218 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
2219 cookie = argv[1];
2220 } else {
2221 NICKSERV_MIN_PARMS(3);
2222 if (!(hi = get_handle_info(argv[1]))) {
2223 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2224 return 0;
2225 }
2226 cookie = argv[2];
2227 }
2228
2229 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
2230 reply("NSMSG_HANDLE_SUSPENDED");
2231 return 0;
2232 }
2233
2234 if (!hi->cookie) {
2235 reply("NSMSG_NO_COOKIE");
2236 return 0;
2237 }
2238
2239 /* Check validity of operation before comparing cookie to
2240 * prohibit guessing by authed users. */
2241 if (user->handle_info
2242 && (hi->cookie->type != EMAIL_CHANGE)
2243 && (hi->cookie->type != PASSWORD_CHANGE)) {
2244 reply("NSMSG_CANNOT_COOKIE");
2245 return 0;
2246 }
2247
2248 if (strcmp(cookie, hi->cookie->cookie)) {
2249 reply("NSMSG_BAD_COOKIE");
2250 return 0;
2251 }
2252
2253 switch (hi->cookie->type) {
2254 case ACTIVATION:
2255 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2256 set_user_handle_info(user, hi, 1);
2257 reply("NSMSG_HANDLE_ACTIVATED");
8dc1d9ae 2258 if (nickserv_conf.sync_log)
2259 SyncLog("ACCOUNTACC %s", hi->handle);
d76ed9a9 2260 break;
2261 case PASSWORD_CHANGE:
2262 set_user_handle_info(user, hi, 1);
2263 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2264 reply("NSMSG_PASSWORD_CHANGED");
8dc1d9ae 2265 if (nickserv_conf.sync_log)
2266 SyncLog("PASSCHANGE %s %s", hi->handle, hi->passwd);
d76ed9a9 2267 break;
2268 case EMAIL_CHANGE:
d8d3ee73 2269 if (!hi->email_addr && nickserv_conf.sync_log) {
2270 /*
2271 * This should only happen if an OREGISTER was sent. Require
2272 * email must be enabled! - SiRVulcaN
2273 */
ac5cb8c5 2274 if (nickserv_conf.sync_log)
2275 SyncLog("REGISTER %s %s %s %s", hi->handle, hi->passwd, hi->cookie->data, user->info);
d8d3ee73 2276 }
d76ed9a9 2277 nickserv_set_email_addr(hi, hi->cookie->data);
2278 reply("NSMSG_EMAIL_CHANGED");
8dc1d9ae 2279 if (nickserv_conf.sync_log)
2280 SyncLog("EMAILCHANGE %s %s", hi->handle, hi->cookie->data);
d76ed9a9 2281 break;
2282 case ALLOWAUTH:
2283 set_user_handle_info(user, hi, 1);
2284 reply("NSMSG_AUTH_SUCCESS");
2285 break;
2286 default:
2287 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
2288 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
2289 break;
2290 }
2291
2292 nickserv_eat_cookie(hi->cookie);
2293
a03d6c77 2294 process_adduser_pending(user);
2295
d76ed9a9 2296 return 1;
2297}
2298
2299static NICKSERV_FUNC(cmd_oregnick) {
2300 const char *nick;
2301 struct handle_info *target;
2302 struct nick_info *ni;
2303
2304 NICKSERV_MIN_PARMS(3);
2305 if (!(target = modcmd_get_handle_info(user, argv[1])))
2306 return 0;
2307 nick = argv[2];
2308 if (!is_registerable_nick(nick)) {
2309 reply("NSMSG_BAD_NICK", nick);
2310 return 0;
2311 }
2312 ni = dict_find(nickserv_nick_dict, nick, NULL);
2313 if (ni) {
2314 reply("NSMSG_NICK_EXISTS", nick);
2315 return 0;
2316 }
2317 register_nick(nick, target);
2318 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
2319 return 1;
2320}
2321
2322static NICKSERV_FUNC(cmd_regnick) {
2323 unsigned n;
2324 struct nick_info *ni;
2325
2326 if (!is_registerable_nick(user->nick)) {
2327 reply("NSMSG_BAD_NICK", user->nick);
2328 return 0;
2329 }
2330 /* count their nicks, see if it's too many */
2331 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
2332 if (n >= nickserv_conf.nicks_per_handle) {
2333 reply("NSMSG_TOO_MANY_NICKS");
2334 return 0;
2335 }
2336 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
2337 if (ni) {
2338 reply("NSMSG_NICK_EXISTS", user->nick);
2339 return 0;
2340 }
2341 register_nick(user->nick, user->handle_info);
2342 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2343 return 1;
2344}
2345
2346static NICKSERV_FUNC(cmd_pass)
2347{
2348 struct handle_info *hi;
2349 const char *old_pass, *new_pass;
2350
2351 NICKSERV_MIN_PARMS(3);
2352 hi = user->handle_info;
2353 old_pass = argv[1];
2354 new_pass = argv[2];
2355 argv[2] = "****";
2356 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
2357 if (!checkpass(old_pass, hi->passwd)) {
2358 argv[1] = "BADPASS";
2359 reply("NSMSG_PASSWORD_INVALID");
2360 return 0;
2361 }
2362 cryptpass(new_pass, hi->passwd);
8dc1d9ae 2363 if (nickserv_conf.sync_log)
2364 SyncLog("PASSCHANGE %s %s", hi->handle, hi->passwd);
d76ed9a9 2365 argv[1] = "****";
2366 reply("NSMSG_PASS_SUCCESS");
2367 return 1;
2368}
2369
2370static int
c092fcad 2371nickserv_addmask(struct svccmd *cmd, struct userNode *user, struct handle_info *hi, const char *mask)
d76ed9a9 2372{
2373 unsigned int i;
2374 char *new_mask = canonicalize_hostmask(strdup(mask));
2375 for (i=0; i<hi->masks->used; i++) {
2376 if (!irccasecmp(new_mask, hi->masks->list[i])) {
c092fcad 2377 reply("NSMSG_ADDMASK_ALREADY", new_mask);
d76ed9a9 2378 free(new_mask);
2379 return 0;
2380 }
2381 }
2382 string_list_append(hi->masks, new_mask);
c092fcad 2383 reply("NSMSG_ADDMASK_SUCCESS", new_mask);
d76ed9a9 2384 return 1;
2385}
2386
2387static NICKSERV_FUNC(cmd_addmask)
2388{
2389 if (argc < 2) {
2390 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
c092fcad 2391 int res = nickserv_addmask(cmd, user, user->handle_info, mask);
d76ed9a9 2392 free(mask);
2393 return res;
2394 } else {
2395 if (!is_gline(argv[1])) {
2396 reply("NSMSG_MASK_INVALID", argv[1]);
2397 return 0;
2398 }
c092fcad 2399 return nickserv_addmask(cmd, user, user->handle_info, argv[1]);
d76ed9a9 2400 }
2401}
2402
2403static NICKSERV_FUNC(cmd_oaddmask)
2404{
2405 struct handle_info *hi;
2406
2407 NICKSERV_MIN_PARMS(3);
c092fcad 2408 if (!(hi = get_victim_oper(cmd, user, argv[1])))
d76ed9a9 2409 return 0;
c092fcad 2410 return nickserv_addmask(cmd, user, hi, argv[2]);
d76ed9a9 2411}
2412
2413static int
c092fcad 2414nickserv_delmask(struct svccmd *cmd, struct userNode *user, struct handle_info *hi, const char *del_mask)
d76ed9a9 2415{
2416 unsigned int i;
2417 for (i=0; i<hi->masks->used; i++) {
2418 if (!strcmp(del_mask, hi->masks->list[i])) {
2419 char *old_mask = hi->masks->list[i];
2420 if (hi->masks->used == 1) {
c092fcad 2421 reply("NSMSG_DELMASK_NOTLAST");
d76ed9a9 2422 return 0;
2423 }
2424 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
c092fcad 2425 reply("NSMSG_DELMASK_SUCCESS", old_mask);
d76ed9a9 2426 free(old_mask);
2427 return 1;
2428 }
2429 }
c092fcad 2430 reply("NSMSG_DELMASK_NOT_FOUND");
d76ed9a9 2431 return 0;
2432}
2433
2434static NICKSERV_FUNC(cmd_delmask)
2435{
2436 NICKSERV_MIN_PARMS(2);
c092fcad 2437 return nickserv_delmask(cmd, user, user->handle_info, argv[1]);
d76ed9a9 2438}
2439
2440static NICKSERV_FUNC(cmd_odelmask)
2441{
2442 struct handle_info *hi;
2443 NICKSERV_MIN_PARMS(3);
c092fcad 2444 if (!(hi = get_victim_oper(cmd, user, argv[1])))
d76ed9a9 2445 return 0;
c092fcad 2446 return nickserv_delmask(cmd, user, hi, argv[2]);
d76ed9a9 2447}
2448
2449int
2450nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2451 unsigned int nn, add = 1, pos;
2452 unsigned long added, removed, flag;
2453
2454 for (added=removed=nn=0; str[nn]; nn++) {
2455 switch (str[nn]) {
2456 case '+': add = 1; break;
2457 case '-': add = 0; break;
2458 default:
2459 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2460 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2461 return 0;
2462 }
2463 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2464 /* cheesy avoidance of looking up the flag name.. */
2465 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2466 return 0;
2467 }
2468 flag = 1 << (pos - 1);
2469 if (add)
2470 added |= flag, removed &= ~flag;
2471 else
2472 removed |= flag, added &= ~flag;
2473 break;
2474 }
2475 }
2476 *padded = added;
2477 *premoved = removed;
2478 return 1;
2479}
2480
2481static int
2482nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2483{
2484 unsigned long before, after, added, removed;
2485 struct userNode *uNode;
2486
2487 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2488 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2489 return 0;
2490 hi->flags = (hi->flags | added) & ~removed;
2491 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2492
2493 /* Strip helping flag if they're only a support helper and not
2494 * currently in #support. */
2495 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2496 struct channelList *schannels;
2497 unsigned int ii;
2498 schannels = chanserv_support_channels();
2499 for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
2500 for (ii = 0; ii < schannels->used; ++ii)
2501 if (GetUserMode(schannels->list[ii], uNode))
2502 break;
2503 if (ii < schannels->used)
2504 break;
2505 }
2506 if (!uNode)
2507 HANDLE_CLEAR_FLAG(hi, HELPING);
2508 }
2509
2510 if (after && !before) {
2511 /* Add user to current helper list. */
2512 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2513 userList_append(&curr_helpers, uNode);
2514 } else if (!after && before) {
2515 /* Remove user from current helper list. */
2516 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2517 userList_remove(&curr_helpers, uNode);
2518 }
2519
2520 return 1;
2521}
2522
2523static void
c092fcad 2524set_list(struct svccmd *cmd, struct userNode *user, struct handle_info *hi, int override)
d76ed9a9 2525{
2526 option_func_t *opt;
2527 unsigned int i;
2528 char *set_display[] = {
338a82b5 2529 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
7fdb7639 2530 "EMAIL", "ANNOUNCEMENTS", "AUTOHIDE", "MAXLOGINS", "LANGUAGE",
0b587959 2531 "FAKEHOST", "TITLE", "EPITHET", "ADVANCED"
d76ed9a9 2532 };
2533
c092fcad 2534 reply("NSMSG_SETTING_LIST");
2535 reply("NSMSG_SETTING_LIST_HEADER");
d76ed9a9 2536
2537 /* Do this so options are presented in a consistent order. */
2538 for (i = 0; i < ArrayLength(set_display); ++i)
2539 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
c092fcad 2540 opt(cmd, user, hi, override, 0, NULL);
2541 reply("NSMSG_SETTING_LIST_END");
d76ed9a9 2542}
2543
2544static NICKSERV_FUNC(cmd_set)
2545{
2546 struct handle_info *hi;
2547 option_func_t *opt;
2548
2549 hi = user->handle_info;
2550 if (argc < 2) {
c092fcad 2551 set_list(cmd, user, hi, 0);
d76ed9a9 2552 return 1;
2553 }
2554 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2555 reply("NSMSG_INVALID_OPTION", argv[1]);
2556 return 0;
2557 }
c092fcad 2558 return opt(cmd, user, hi, 0, argc-1, argv+1);
d76ed9a9 2559}
2560
2561static NICKSERV_FUNC(cmd_oset)
2562{
2563 struct handle_info *hi;
2564 option_func_t *opt;
2565
2566 NICKSERV_MIN_PARMS(2);
2567
c092fcad 2568 if (!(hi = get_victim_oper(cmd, user, argv[1])))
d76ed9a9 2569 return 0;
2570
2571 if (argc < 3) {
c092fcad 2572 set_list(cmd, user, hi, 0);
d76ed9a9 2573 return 1;
2574 }
2575
2576 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2577 reply("NSMSG_INVALID_OPTION", argv[2]);
2578 return 0;
2579 }
2580
c092fcad 2581 return opt(cmd, user, hi, 1, argc-2, argv+2);
d76ed9a9 2582}
2583
2584static OPTION_FUNC(opt_info)
2585{
2586 const char *info;
2587 if (argc > 1) {
2588 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2589 free(hi->infoline);
2590 hi->infoline = NULL;
2591 } else {
2592 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2593 }
2594 }
2595
2596 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
c092fcad 2597 reply("NSMSG_SET_INFO", info);
d76ed9a9 2598 return 1;
2599}
2600
2601static OPTION_FUNC(opt_width)
2602{
2603 if (argc > 1)
2604 hi->screen_width = strtoul(argv[1], NULL, 0);
2605
2606 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2607 hi->screen_width = MIN_LINE_SIZE;
2608 else if (hi->screen_width > MAX_LINE_SIZE)
2609 hi->screen_width = MAX_LINE_SIZE;
2610
c092fcad 2611 reply("NSMSG_SET_WIDTH", hi->screen_width);
d76ed9a9 2612 return 1;
2613}
2614
2615static OPTION_FUNC(opt_tablewidth)
2616{
2617 if (argc > 1)
2618 hi->table_width = strtoul(argv[1], NULL, 0);
2619
2620 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2621 hi->table_width = MIN_LINE_SIZE;
2622 else if (hi->screen_width > MAX_LINE_SIZE)
2623 hi->table_width = MAX_LINE_SIZE;
2624
c092fcad 2625 reply("NSMSG_SET_TABLEWIDTH", hi->table_width);
d76ed9a9 2626 return 1;
2627}
2628
2629static OPTION_FUNC(opt_color)
2630{
2631 if (argc > 1) {
2632 if (enabled_string(argv[1]))
2633 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2634 else if (disabled_string(argv[1]))
2635 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2636 else {
c092fcad 2637 reply("MSG_INVALID_BINARY", argv[1]);
d76ed9a9 2638 return 0;
2639 }
2640 }
2641
c092fcad 2642 reply("NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
d76ed9a9 2643 return 1;
2644}
2645
2646static OPTION_FUNC(opt_privmsg)
2647{
2648 if (argc > 1) {
2649 if (enabled_string(argv[1]))
2650 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2651 else if (disabled_string(argv[1]))
2652 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2653 else {
c092fcad 2654 reply("MSG_INVALID_BINARY", argv[1]);
d76ed9a9 2655 return 0;
2656 }
2657 }
2658
c092fcad 2659 reply("NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
d76ed9a9 2660 return 1;
2661}
2662
7fdb7639 2663static OPTION_FUNC(opt_autohide)
2664{
2665 if (argc > 1) {
2666 if (enabled_string(argv[1]))
2667 HANDLE_SET_FLAG(hi, AUTOHIDE);
2668 else if (disabled_string(argv[1]))
2669 HANDLE_CLEAR_FLAG(hi, AUTOHIDE);
2670 else {
c092fcad 2671 reply("MSG_INVALID_BINARY", argv[1]);
7fdb7639 2672 return 0;
2673 }
2674 }
2675
c092fcad 2676 reply("NSMSG_SET_AUTOHIDE", user_find_message(user, HANDLE_FLAGGED(hi, AUTOHIDE) ? "MSG_ON" : "MSG_OFF"));
7fdb7639 2677 return 1;
2678}
2679
d76ed9a9 2680static OPTION_FUNC(opt_style)
2681{
2682 char *style;
2683
2684 if (argc > 1) {
338a82b5 2685 if (!irccasecmp(argv[1], "Clean"))
2686 hi->userlist_style = HI_STYLE_CLEAN;
2687 else if (!irccasecmp(argv[1], "Advanced"))
2688 hi->userlist_style = HI_STYLE_ADVANCED;
d9896a83 2689 else if (!irccasecmp(argv[1], "Classic"))
2690 hi->userlist_style = HI_STYLE_CLASSIC;
338a82b5 2691 else /* Default to normal */
2692 hi->userlist_style = HI_STYLE_NORMAL;
2693 } /* TODO: give error if unknow style is chosen */
d76ed9a9 2694
2695 switch (hi->userlist_style) {
338a82b5 2696 case HI_STYLE_ADVANCED:
2697 style = "Advanced";
2698 break;
d9896a83 2699 case HI_STYLE_CLASSIC:
2700 style = "Classic";
2701 break;
338a82b5 2702 case HI_STYLE_CLEAN:
2703 style = "Clean";
2704 break;
2705 case HI_STYLE_NORMAL:
2706 default:
2707 style = "Normal";
d76ed9a9 2708 }
2709
c092fcad 2710 reply("NSMSG_SET_STYLE", style);
d76ed9a9 2711 return 1;
2712}
2713
2714static OPTION_FUNC(opt_announcements)
2715{
2716 const char *choice;
2717
2718 if (argc > 1) {
2719 if (enabled_string(argv[1]))
2720 hi->announcements = 'y';
2721 else if (disabled_string(argv[1]))
2722 hi->announcements = 'n';
2723 else if (!strcmp(argv[1], "?") || !irccasecmp(argv[1], "default"))
2724 hi->announcements = '?';
2725 else {
c092fcad 2726 reply("NSMSG_INVALID_ANNOUNCE", argv[1]);
d76ed9a9 2727 return 0;
2728 }
2729 }
2730
2731 switch (hi->announcements) {
2732 case 'y': choice = user_find_message(user, "MSG_ON"); break;
2733 case 'n': choice = user_find_message(user, "MSG_OFF"); break;
2734 case '?': choice = "default"; break;
2735 default: choice = "unknown"; break;
2736 }
c092fcad 2737 reply("NSMSG_SET_ANNOUNCEMENTS", choice);
d76ed9a9 2738 return 1;
2739}
2740
2741static OPTION_FUNC(opt_password)
2742{
2743 if (!override) {
c092fcad 2744 reply("NSMSG_USE_CMD_PASS");
d76ed9a9 2745 return 0;
2746 }
2747
2748 if (argc > 1)
2749 cryptpass(argv[1], hi->passwd);
2750
4ae3fc8b 2751 if (nickserv_conf.sync_log)
2752 SyncLog("PASSCHANGE %s %s", hi->handle, hi->passwd);
2753
c092fcad 2754 reply("NSMSG_SET_PASSWORD", "***");
d76ed9a9 2755 return 1;
2756}
2757
2758static OPTION_FUNC(opt_flags)
2759{
2760 char flags[33];
2761 unsigned int ii, flen;
2762
2763 if (!override) {
c092fcad 2764 reply("MSG_SETTING_PRIVILEGED", argv[0]);
d76ed9a9 2765 return 0;
2766 }
2767
2768 if (argc > 1)
2769 nickserv_apply_flags(user, hi, argv[1]);
2770
2771 for (ii = flen = 0; handle_flags[ii]; ii++)
2772 if (hi->flags & (1 << ii))
2773 flags[flen++] = handle_flags[ii];
2774 flags[flen] = '\0';
2775 if (hi->flags)
c092fcad 2776 reply("NSMSG_SET_FLAGS", flags);
d76ed9a9 2777 else
c092fcad 2778 reply("NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
d76ed9a9 2779 return 1;
2780}
2781
2782static OPTION_FUNC(opt_email)
2783{
2784 if (argc > 1) {
2785 const char *str;
4c26ef3e 2786 if (!valid_email(argv[1])) {
c092fcad 2787 reply("NSMSG_BAD_EMAIL_ADDR");
d76ed9a9 2788 return 0;
2789 }
2790 if ((str = sendmail_prohibited_address(argv[1]))) {
c092fcad 2791 reply("NSMSG_EMAIL_PROHIBITED", argv[1], str);
d76ed9a9 2792 return 0;
2793 }
2794 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
c092fcad 2795 reply("NSMSG_EMAIL_SAME");
d76ed9a9 2796 else if (!override)
1c0d5243 2797 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1], 0);
d76ed9a9 2798 else {
2799 nickserv_set_email_addr(hi, argv[1]);
2800 if (hi->cookie)
2801 nickserv_eat_cookie(hi->cookie);
c092fcad 2802 reply("NSMSG_SET_EMAIL", visible_email_addr(user, hi));
d76ed9a9 2803 }
2804 } else
c092fcad 2805 reply("NSMSG_SET_EMAIL", visible_email_addr(user, hi));
d76ed9a9 2806 return 1;
2807}
2808
2809static OPTION_FUNC(opt_maxlogins)
2810{
2811 unsigned char maxlogins;
2812 if (argc > 1) {
2813 maxlogins = strtoul(argv[1], NULL, 0);
2814 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
c092fcad 2815 reply("NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
d76ed9a9 2816 return 0;
2817 }
2818 hi->maxlogins = maxlogins;
2819 }
2820 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
c092fcad 2821 reply("NSMSG_SET_MAXLOGINS", maxlogins);
d76ed9a9 2822 return 1;
2823}
2824
0b587959 2825static OPTION_FUNC(opt_advanced)
2826{
2827 if (argc > 1) {
2828 if (enabled_string(argv[1]))
2829 HANDLE_SET_FLAG(hi, ADVANCED);
2830 else if (disabled_string(argv[1]))
2831 HANDLE_CLEAR_FLAG(hi, ADVANCED);
2832 else {
c092fcad 2833 reply("MSG_INVALID_BINARY", argv[1]);
0b587959 2834 return 0;
2835 }
2836 }
2837
c092fcad 2838 reply("NSMSG_SET_ADVANCED", user_find_message(user, HANDLE_FLAGGED(hi, ADVANCED) ? "MSG_ON" : "MSG_OFF"));
0b587959 2839 return 1;
2840}
2841
d76ed9a9 2842static OPTION_FUNC(opt_language)
2843{
2844 struct language *lang;
2845 if (argc > 1) {
2846 lang = language_find(argv[1]);
2847 if (irccasecmp(lang->name, argv[1]))
c092fcad 2848 reply("NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
d76ed9a9 2849 hi->language = lang;
2850 }
c092fcad 2851 reply("NSMSG_SET_LANGUAGE", hi->language->name);
d76ed9a9 2852 return 1;
2853}
2854
2855int
2856oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2857 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2858 return 0;
2859 if ((user->handle_info->opserv_level < target->opserv_level)
2860 || ((user->handle_info->opserv_level == target->opserv_level)
2861 && (user->handle_info->opserv_level < 1000))) {
2862 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2863 return 0;
2864 }
2865 if ((user->handle_info->opserv_level < new_level)
2866 || ((user->handle_info->opserv_level == new_level)
2867 && (user->handle_info->opserv_level < 1000))) {
2868 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2869 return 0;
2870 }
2871 if (user->handle_info == target) {
2872 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2873 return 0;
2874 }
2875 if (target->opserv_level == new_level)
2876 return 0;
2877 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2878 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2879 target->opserv_level = new_level;
2880 return 1;
2881}
2882
2883static OPTION_FUNC(opt_level)
2884{
2885 int res;
2886
2887 if (!override) {
c092fcad 2888 reply("MSG_SETTING_PRIVILEGED", argv[0]);
d76ed9a9 2889 return 0;
2890 }
2891
2892 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
c092fcad 2893 reply("NSMSG_SET_LEVEL", hi->opserv_level);
d76ed9a9 2894 return res;
2895}
2896
2897static OPTION_FUNC(opt_epithet)
2898{
d76ed9a9 2899 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
180e0971 2900 char *epithet;
56958740 2901 struct userNode *target, *next_un;
2902
180e0971 2903 if (!override) {
c092fcad 2904 reply("MSG_SETTING_PRIVILEGED", argv[0]);
180e0971 2905 return 0;
2906 }
2907
2908 epithet = unsplit_string(argv+1, argc-1, NULL);
2909
d76ed9a9 2910 if (hi->epithet)
2911 free(hi->epithet);
2912 if ((epithet[0] == '*') && !epithet[1])
2913 hi->epithet = NULL;
2914 else
2915 hi->epithet = strdup(epithet);
56958740 2916
2917 for (target = hi->users; target; target = next_un) {
2918 irc_swhois(nickserv, target, hi->epithet);
2919
2920 next_un = target->next_authed;
2921 }
d76ed9a9 2922 }
2923
2924 if (hi->epithet)
c092fcad 2925 reply("NSMSG_SET_EPITHET", hi->epithet);
d76ed9a9 2926 else
c092fcad 2927 reply("NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
d76ed9a9 2928 return 1;
2929}
2930
2931static OPTION_FUNC(opt_title)
2932{
116d100f 2933 char *title;
2934 const char *none;
2935 char *sptr;
d76ed9a9 2936
d76ed9a9 2937 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
180e0971 2938 if (!override) {
c092fcad 2939 reply("MSG_SETTING_PRIVILEGED", argv[0]);
180e0971 2940 return 0;
2941 }
2942
d76ed9a9 2943 title = argv[1];
574bfc14 2944 if(!strcmp(title, "*")) {
2945 free(hi->fakehost);
2946 hi->fakehost = NULL;
d76ed9a9 2947 }
574bfc14 2948 else {
2949 if (strchr(title, '.')) {
116d100f 2950 reply("NSMSG_TITLE_INVALID");
2951 return 0;
2952 }
574bfc14 2953 /* Alphanumeric titles only. */
2954 for(sptr = title; *sptr; sptr++) {
2955 if(!isalnum(*sptr) && *sptr != '-') {
2956 reply("NSMSG_TITLE_INVALID");
2957 return 0;
2958 }
2959 }
2960 if ((strlen(user->handle_info->handle) + strlen(title) +
2961 strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) {
2962 reply("NSMSG_TITLE_TRUNCATED");
2963 return 0;
2964 }
2965 free(hi->fakehost);
d76ed9a9 2966 hi->fakehost = malloc(strlen(title)+2);
2967 hi->fakehost[0] = '.';
2968 strcpy(hi->fakehost+1, title);
2969 }
2970 apply_fakehost(hi);
2971 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2972 title = hi->fakehost + 1;
bae4525d 2973 else {
2974 /* If theres no title set then the default title will therefore
2975 be the first part of hidden_host in x3.conf.example, so for
2976 consistency with opt_fakehost we will print this here */
2977 char *hs, *hidden_suffix, *rest;
2978
2979 hs = conf_get_data("server/hidden_host", RECDB_QSTRING);
2980 hidden_suffix = strdup(hs);
2981
2982 /* Yes we do this twice */
2983 rest = strrchr(hidden_suffix, '.');
2984 *rest++ = '\0';
2985 rest = strrchr(hidden_suffix, '.');
2986 *rest++ = '\0';
2987
2988 title = hidden_suffix;
2989 }
2990
d76ed9a9 2991 if (!title)
116d100f 2992 none = user_find_message(user, "MSG_NONE");
2993 send_message(user, nickserv, "NSMSG_SET_TITLE", title ? title : none);
d76ed9a9 2994 return 1;
2995}
2996
7637f48f 2997int
c092fcad 2998check_vhost(char *vhost, struct userNode *user, struct svccmd *cmd)
7637f48f 2999{
116d100f 3000 unsigned int y;
7637f48f 3001
3002 // check for a dot in the vhost
3003 if(strchr(vhost, '.') == NULL) {
c092fcad 3004 reply("NSMSG_NOT_VALID_FAKEHOST_DOT", vhost);
7637f48f 3005 return 0;
3006 }
3007
3008 // check for a @ in the vhost
3009 if(strchr(vhost, '@') != NULL) {
c092fcad 3010 reply("NSMSG_NOT_VALID_FAKEHOST_AT", vhost);
7637f48f 3011 return 0;
3012 }
3013
3014 // check for denied words, inspired by monk at paki.sex
3015 for(y = 0; y < nickserv_conf.denied_fakehost_words->used; y++) {
3016 if(strstr(vhost, nickserv_conf.denied_fakehost_words->list[y]) != NULL) {
c092fcad 3017 reply("NSMSG_DENIED_FAKEHOST_WORD", vhost, nickserv_conf.denied_fakehost_words->list[y]);
7637f48f 3018 return 0;
3019 }
3020 }
3021
3022 // check for ircu's HOSTLEN length.
3023 if(strlen(vhost) >= HOSTLEN) {
c092fcad 3024 reply("NSMSG_NOT_VALID_FAKEHOST_LEN", vhost);
7637f48f 3025 return 0;
3026 }
3027
bf93ca8d 3028 /* This can be handled by the regex now if desired.
7637f48f 3029 if (vhost[strspn(vhost, "0123456789.")]) {
3030 hostname = vhost + strlen(vhost);
3031 for (depth = 1; depth && (hostname > vhost); depth--) {
3032 hostname--;
3033 while ((hostname > vhost) && (*hostname != '.')) hostname--;
3034 }
3035
bf93ca8d 3036 if (*hostname == '.') hostname++; * advance past last dot we saw *
7637f48f 3037 if(strlen(hostname) > 4) {
c092fcad 3038 reply("NSMSG_NOT_VALID_FAKEHOST_TLD_LEN", vhost);
7637f48f 3039 return 0;
3040 }
3041 }
bf93ca8d 3042 */
3043 /* test either regex or as valid handle */
3044 if (nickserv_conf.valid_fakehost_regex_set) {
3045 int err = regexec(&nickserv_conf.valid_fakehost_regex, vhost, 0, 0, 0);
3046 if (err) {
3047 char buff[256];
3048 buff[regerror(err, &nickserv_conf.valid_fakehost_regex, buff, sizeof(buff))] = 0;
3049 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
3050 }
3051 if(err == REG_NOMATCH) {
3052 reply("NSMSG_NOT_VALID_FAKEHOST_REGEX", vhost);
3053 return 0;
3054 }
3055 }
3056
7637f48f 3057
3058 return 1;
3059}
3060
d76ed9a9 3061static OPTION_FUNC(opt_fakehost)
3062{
3063 const char *fake;
3064
d76ed9a9 3065 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
180e0971 3066 if (!override) {
c092fcad 3067 reply("MSG_SETTING_PRIVILEGED", argv[0]);
180e0971 3068 return 0;
3069 }
3070
d76ed9a9 3071 fake = argv[1];
3072 if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) {
c092fcad 3073 reply("NSMSG_FAKEHOST_INVALID", HOSTLEN);
d76ed9a9 3074 return 0;
3075 }
7637f48f 3076 if (!strcmp(fake, "*")) {
bf93ca8d 3077 if(hi->fakehost) {
3078 free(hi->fakehost);
3079 hi->fakehost = NULL;
3080 }
3081 }
3082 else if (!check_vhost(argv[1], user, cmd)) {
3083 /* check_vhost takes care of error reply */
3084 return 0;
3085 }
3086 else {
3087 if(hi->fakehost)
3088 free(hi->fakehost);
d76ed9a9 3089 hi->fakehost = strdup(fake);
7637f48f 3090 }
d76ed9a9 3091 apply_fakehost(hi);
bf93ca8d 3092 fake = hi->fakehost;
88b0672a 3093 } else
3094 fake = generate_fakehost(hi);
3095
bf93ca8d 3096 /* Tell them we set the host */
d76ed9a9 3097 if (!fake)
3098 fake = user_find_message(user, "MSG_NONE");
c092fcad 3099 reply("NSMSG_SET_FAKEHOST", fake);
d76ed9a9 3100 return 1;
3101}
3102
2362161a 3103static OPTION_FUNC(opt_note)
3104{
3105 if (!override) {
c092fcad 3106 reply("MSG_SETTING_PRIVILEGED", argv[0]);
2362161a 3107 return 0;
3108 }
3109
3110 if (argc > 1) {
3111 char *text = unsplit_string(argv + 1, argc - 1, NULL);
3112
3113 if (hi->note)
3114 free(hi->note);
3115
3116 if ((text[0] == '*') && !text[1])
3117 hi->note = NULL;
3118 else {
3119 if (!(hi->note = nickserv_add_note(user->handle_info->handle, now, text)))
3120 hi->note = NULL;
3121 }
3122 }
3123
c092fcad 3124 reply("NSMSG_SET_NOTE", hi->note->note);
2362161a 3125 return 1;
3126}
3127
d76ed9a9 3128static NICKSERV_FUNC(cmd_reclaim)
3129{
3130 struct handle_info *hi;
3131 struct nick_info *ni;
3132 struct userNode *victim;
3133
3134 NICKSERV_MIN_PARMS(2);
3135 hi = user->handle_info;
3136 ni = dict_find(nickserv_nick_dict, argv[1], 0);
3137 if (!ni) {
3138 reply("NSMSG_UNKNOWN_NICK", argv[1]);
3139 return 0;
3140 }
3141 if (ni->owner != user->handle_info) {
3142 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
3143 return 0;
3144 }
3145 victim = GetUserH(ni->nick);
3146 if (!victim) {
3147 reply("MSG_NICK_UNKNOWN", ni->nick);
3148 return 0;
3149 }
3150 if (victim == user) {
3151 reply("NSMSG_NICK_USER_YOU");
3152 return 0;
3153 }
3154 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
3155 switch (nickserv_conf.reclaim_action) {
3156 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
3157 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
3158 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
3159 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
3160 }
3161 return 1;
3162}
3163
3164static NICKSERV_FUNC(cmd_unregnick)
3165{
3166 const char *nick;
3167 struct handle_info *hi;
3168 struct nick_info *ni;
3169
3170 hi = user->handle_info;
3171 nick = (argc < 2) ? user->nick : (const char*)argv[1];
3172 ni = dict_find(nickserv_nick_dict, nick, NULL);
3173 if (!ni) {
3174 reply("NSMSG_UNKNOWN_NICK", nick);
3175 return 0;
3176 }
3177 if (hi != ni->owner) {
3178 reply("NSMSG_NOT_YOUR_NICK", nick);
3179 return 0;
3180 }
3181 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3182 delete_nick(ni);
3183 return 1;
3184}
3185
3186static NICKSERV_FUNC(cmd_ounregnick)
3187{
3188 struct nick_info *ni;
3189
3190 NICKSERV_MIN_PARMS(2);
3191 if (!(ni = get_nick_info(argv[1]))) {
3192 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
3193 return 0;
3194 }
3195 if (ni->owner->opserv_level >= user->handle_info->opserv_level) {
3196 reply("MSG_USER_OUTRANKED", ni->nick);
3197 return 0;
3198 }
3199 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3200 delete_nick(ni);
3201 return 1;
3202}
3203
3204static NICKSERV_FUNC(cmd_unregister)
3205{
3206 struct handle_info *hi;
3207 char *passwd;
3208
3209 NICKSERV_MIN_PARMS(2);
3210 hi = user->handle_info;
3211 passwd = argv[1];
3212 argv[1] = "****";
3213 if (checkpass(passwd, hi->passwd)) {
258d1427 3214 nickserv_unregister_handle(hi, user, cmd->parent->bot);
d76ed9a9 3215 return 1;
3216 } else {
3217 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
3218 reply("NSMSG_PASSWORD_INVALID");
3219 return 0;
3220 }
3221}
3222
3223static NICKSERV_FUNC(cmd_ounregister)
3224{
3225 struct handle_info *hi;
3226
3227 NICKSERV_MIN_PARMS(2);
c092fcad 3228 if (!(hi = get_victim_oper(cmd, user, argv[1])))
d76ed9a9 3229 return 0;
258d1427 3230 nickserv_unregister_handle(hi, user, cmd->parent->bot);
a32da4c7 3231 return 1;
d76ed9a9 3232}
3233
3234static NICKSERV_FUNC(cmd_status)
3235{
3236 if (nickserv_conf.disable_nicks) {
3237 reply("NSMSG_GLOBAL_STATS_NONICK",
3238 dict_size(nickserv_handle_dict));
3239 } else {
3240 if (user->handle_info) {
3241 int cnt=0;
3242 struct nick_info *ni;
3243 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
3244 reply("NSMSG_HANDLE_STATS", cnt);
3245 } else {
3246 reply("NSMSG_HANDLE_NONE");
3247 }
3248 reply("NSMSG_GLOBAL_STATS",
3249 dict_size(nickserv_handle_dict),
3250 dict_size(nickserv_nick_dict));
3251 }
3252 return 1;
3253}
3254
3255static NICKSERV_FUNC(cmd_ghost)
3256{
3257 struct userNode *target;
3258 char reason[MAXLEN];
3259
3260 NICKSERV_MIN_PARMS(2);
3261 if (!(target = GetUserH(argv[1]))) {
3262 reply("MSG_NICK_UNKNOWN", argv[1]);
3263 return 0;
3264 }
3265 if (target == user) {
3266 reply("NSMSG_CANNOT_GHOST_SELF");
3267 return 0;
3268 }
3269 if (!target->handle_info || (target->handle_info != user->handle_info)) {
3270 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
3271 return 0;
3272 }
3273 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
3274 DelUser(target, nickserv, 1, reason);
3275 reply("NSMSG_GHOST_KILLED", argv[1]);
3276 return 1;
3277}
3278
3279static NICKSERV_FUNC(cmd_vacation)
3280{
3281 HANDLE_SET_FLAG(user->handle_info, FROZEN);
3282 reply("NSMSG_ON_VACATION");
3283 return 1;
3284}
3285
3286static int
3287nickserv_saxdb_write(struct saxdb_context *ctx) {
3288 dict_iterator_t it;
3289 struct handle_info *hi;
3290 char flags[33];
3291
3292 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3293 hi = iter_data(it);
d76ed9a9 3294 saxdb_start_record(ctx, iter_key(it), 0);
3295 if (hi->announcements != '?') {
3296 flags[0] = hi->announcements;
3297 flags[1] = 0;
3298 saxdb_write_string(ctx, KEY_ANNOUNCEMENTS, flags);
3299 }
3300 if (hi->cookie) {
3301 struct handle_cookie *cookie = hi->cookie;
3302 char *type;
3303
3304 switch (cookie->type) {
3305 case ACTIVATION: type = KEY_ACTIVATION; break;
3306 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
3307 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
3308 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
3309 default: type = NULL; break;
3310 }
3311 if (type) {
3312 saxdb_start_record(ctx, KEY_COOKIE, 0);
3313 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
3314 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
3315 if (cookie->data)
3316 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
3317 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
3318 saxdb_end_record(ctx);
3319 }
3320 }
3321 if (hi->email_addr)
3322 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
3323 if (hi->epithet)
3324 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2362161a 3325 if (hi->note) {
3326 saxdb_start_record(ctx, KEY_NOTE_NOTE, 0);
3327 saxdb_write_string(ctx, KEY_NOTE_SETTER, hi->note->setter);
3328 saxdb_write_int(ctx, KEY_NOTE_DATE, hi->note->date);
3329 saxdb_write_string(ctx, KEY_NOTE_NOTE, hi->note->note);
3330 saxdb_end_record(ctx);
3331 }
3332
d76ed9a9 3333 if (hi->fakehost)
3334 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
3335 if (hi->flags) {
3336 int ii, flen;
3337
3338 for (ii=flen=0; handle_flags[ii]; ++ii)
3339 if (hi->flags & (1 << ii))
3340 flags[flen++] = handle_flags[ii];
3341 flags[flen] = 0;
3342 saxdb_write_string(ctx, KEY_FLAGS, flags);
3343 }
d76ed9a9 3344 if (hi->infoline)
3345 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
3346 if (hi->last_quit_host[0])
3347 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
3348 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
3349 if (hi->masks->used)
3350 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
5177fd21 3351 if (hi->ignores->used)
3352 saxdb_write_string_list(ctx, KEY_IGNORES, hi->ignores);
d76ed9a9 3353 if (hi->maxlogins)
3354 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
3355 if (hi->nicks) {
3356 struct string_list *slist;
3357 struct nick_info *ni;
3358
3359 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
3360 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
3361 saxdb_write_string_list(ctx, KEY_NICKS, slist);
3362 free(slist->list);
3363 free(slist);
3364 }
3365 if (hi->opserv_level)
3366 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
3367 if (hi->language != lang_C)
3368 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
3369 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
3370 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
3371 if (hi->screen_width)
3372 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
3373 if (hi->table_width)
3374 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
3375 flags[0] = hi->userlist_style;
3376 flags[1] = 0;
3377 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
3378 saxdb_end_record(ctx);
3379 }
5177fd21 3380
d76ed9a9 3381 return 0;
3382}
3383
3384static handle_merge_func_t *handle_merge_func_list;
3385static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
3386
3387void
3388reg_handle_merge_func(handle_merge_func_t func)
3389{
3390 if (handle_merge_func_used == handle_merge_func_size) {
3391 if (handle_merge_func_size) {
3392 handle_merge_func_size <<= 1;
3393 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
3394 } else {
3395 handle_merge_func_size = 8;
3396 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
3397 }
3398 }
3399 handle_merge_func_list[handle_merge_func_used++] = func;
3400}
3401
3402static NICKSERV_FUNC(cmd_merge)
3403{
3404 struct handle_info *hi_from, *hi_to;
3405 struct userNode *last_user;
3406 struct userData *cList, *cListNext;
3407 unsigned int ii, jj, n;
d76ed9a9 3408
3409 NICKSERV_MIN_PARMS(3);
3410
c092fcad 3411 if (!(hi_from = get_victim_oper(cmd, user, argv[1])))
d76ed9a9 3412 return 0;
c092fcad 3413 if (!(hi_to = get_victim_oper(cmd, user, argv[2])))
d76ed9a9 3414 return 0;
3415 if (hi_to == hi_from) {
3416 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
3417 return 0;
3418 }
3419
3420 for (n=0; n<handle_merge_func_used; n++)
3421 handle_merge_func_list[n](user, hi_to, hi_from);
3422
3423 /* Append "from" handle's nicks to "to" handle's nick list. */
3424 if (hi_to->nicks) {
3425 struct nick_info *last_ni;
3426 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3427 last_ni->next = hi_from->nicks;
3428 }
3429 while (hi_from->nicks) {
3430 hi_from->nicks->owner = hi_to;
3431 hi_from->nicks = hi_from->nicks->next;
3432 }
3433
3434 /* Merge the hostmasks. */
3435 for (ii=0; ii<hi_from->masks->used; ii++) {
3436 char *mask = hi_from->masks->list[ii];
3437 for (jj=0; jj<hi_to->masks->used; jj++)
3438 if (match_ircglobs(hi_to->masks->list[jj], mask))
3439 break;
3440 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3441 string_list_append(hi_to->masks, strdup(mask));
3442 }
3443
5177fd21 3444 /* Merge the ignores. */
3445 for (ii=0; ii<hi_from->ignores->used; ii++) {
3446 char *ignore = hi_from->ignores->list[ii];
3447 for (jj=0; jj<hi_to->ignores->used; jj++)
3448 if (match_ircglobs(hi_to->ignores->list[jj], ignore))
3449 break;
3450 if (jj==hi_to->ignores->used) /* Nothing from the "to" handle covered this mask, so add it. */
3451 string_list_append(hi_to->ignores, strdup(ignore));
3452 }
3453
d76ed9a9 3454 /* Merge the lists of authed users. */
3455 if (hi_to->users) {
3456 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3457 last_user->next_authed = hi_from->users;
3458 } else {
3459 hi_to->users = hi_from->users;
3460 }
3461 /* Repoint the old "from" handle's users. */
3462 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3463 last_user->handle_info = hi_to;
3464 }
3465 hi_from->users = NULL;
3466
3467 /* Merge channel userlists. */
3468 for (cList=hi_from->channels; cList; cList=cListNext) {
3469 struct userData *cList2;
3470 cListNext = cList->u_next;
3471 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3472 if (cList->channel == cList2->channel)
3473 break;
3474 if (cList2 && (cList2->access >= cList->access)) {
3475 log_module(NS_LOG, LOG_INFO, "Merge: %s had only %d access in %s (versus %d for %s)", hi_from->handle, cList->access, cList->channel->channel->name, cList2->access, hi_to->handle);
3476 /* keep cList2 in hi_to; remove cList from hi_from */
3477 del_channel_user(cList, 1);
3478 } else {
3479 if (cList2) {
3480 log_module(NS_LOG, LOG_INFO, "Merge: %s had only %d access in %s (versus %d for %s)", hi_to->handle, cList2->access, cList->channel->channel->name, cList->access, hi_from->handle);
3481 /* remove the lower-ranking cList2 from hi_to */
3482 del_channel_user(cList2, 1);
3483 } else {
3484 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3485 }
3486 /* cList needs to be moved from hi_from to hi_to */
3487 cList->handle = hi_to;
3488 /* Remove from linked list for hi_from */
3489 assert(!cList->u_prev);
3490 hi_from->channels = cList->u_next;
3491 if (cList->u_next)
3492 cList->u_next->u_prev = cList->u_prev;
3493 /* Add to linked list for hi_to */
3494 cList->u_prev = NULL;
3495 cList->u_next = hi_to->channels;
3496 if (hi_to->channels)
3497 hi_to->channels->u_prev = cList;
3498 hi_to->channels = cList;
3499 }
3500 }
3501
3502 /* Do they get an OpServ level promotion? */
3503 if (hi_from->opserv_level > hi_to->opserv_level)
3504 hi_to->opserv_level = hi_from->opserv_level;
3505
3506 /* What about last seen time? */
3507 if (hi_from->lastseen > hi_to->lastseen)
3508 hi_to->lastseen = hi_from->lastseen;
3509
0d16e639 3510 /* Does a fakehost carry over? (This intentionally doesn't set it
3511 * for users previously attached to hi_to. They'll just have to
3512 * reconnect.)
3513 */
3514 if (hi_from->fakehost && !hi_to->fakehost)
3515 hi_to->fakehost = strdup(hi_from->fakehost);
3516
d76ed9a9 3517 /* Notify of success. */
d76ed9a9 3518 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
57692f5e 3519 global_message_args(MESSAGE_RECIPIENT_OPERS, "NSMSG_ACCOUNT_MERGED", user->nick,
3520 user->handle_info->handle, hi_from->handle, hi_to->handle);
d76ed9a9 3521
3522 /* Unregister the "from" handle. */
258d1427 3523 nickserv_unregister_handle(hi_from, NULL, cmd->parent->bot);
d76ed9a9 3524
3525 return 1;
3526}
3527
3528struct nickserv_discrim {
3529 unsigned int limit, min_level, max_level;
3530 unsigned long flags_on, flags_off;
3531 time_t min_registered, max_registered;
3532 time_t lastseen;
3533 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3534 const char *nickmask;
3535 const char *hostmask;
3536 const char *handlemask;
3537 const char *emailmask;
3538};
3539
3540typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
3541
3542struct discrim_apply_info {
3543 struct nickserv_discrim *discrim;
3544 discrim_search_func func;
3545 struct userNode *source;
3546 unsigned int matched;
3547};
3548
3549static struct nickserv_discrim *
c092fcad 3550nickserv_discrim_create(struct svccmd *cmd, struct userNode *user, unsigned int argc, char *argv[])
d76ed9a9 3551{
3552 unsigned int i;
3553 struct nickserv_discrim *discrim;
3554
3555 discrim = malloc(sizeof(*discrim));
3556 memset(discrim, 0, sizeof(*discrim));
3557 discrim->min_level = 0;
3558 discrim->max_level = ~0;
3559 discrim->limit = 50;
3560 discrim->min_registered = 0;
3561 discrim->max_registered = INT_MAX;
3562 discrim->lastseen = now;
3563
3564 for (i=0; i<argc; i++) {
3565 if (i == argc - 1) {
c092fcad 3566 reply("MSG_MISSING_PARAMS", argv[i]);
d76ed9a9 3567 goto fail;
3568 }
3569 if (!irccasecmp(argv[i], "limit")) {
3570 discrim->limit = strtoul(argv[++i], NULL, 0);
3571 } else if (!irccasecmp(argv[i], "flags")) {
3572 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3573 } else if (!irccasecmp(argv[i], "registered")) {
3574 const char *cmp = argv[++i];
3575 if (cmp[0] == '<') {
3576 if (cmp[1] == '=') {
3577 discrim->min_registered = now - ParseInterval(cmp+2);
3578 } else {
3579 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3580 }
3581 } else if (cmp[0] == '=') {
3582 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3583 } else if (cmp[0] == '>') {
3584 if (cmp[1] == '=') {
3585 discrim->max_registered = now - ParseInterval(cmp+2);
3586 } else {
3587 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3588 }
3589 } else {
c092fcad 3590 reply("MSG_INVALID_CRITERIA", cmp);
d76ed9a9 3591 }
3592 } else if (!irccasecmp(argv[i], "seen")) {
3593 discrim->lastseen = now - ParseInterval(argv[++i]);
3594 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3595 discrim->nickmask = argv[++i];
3596 } else if (!irccasecmp(argv[i], "hostmask")) {
3597 i++;
3598 if (!irccasecmp(argv[i], "exact")) {
3599 if (i == argc - 1) {
c092fcad 3600 reply("MSG_MISSING_PARAMS", argv[i]);
d76ed9a9 3601 goto fail;
3602 }
3603 discrim->hostmask_type = EXACT;
3604 } else if (!irccasecmp(argv[i], "subset")) {
3605 if (i == argc - 1) {
c092fcad 3606 reply("MSG_MISSING_PARAMS", argv[i]);
d76ed9a9 3607 goto fail;
3608 }
3609 discrim->hostmask_type = SUBSET;
3610 } else if (!irccasecmp(argv[i], "superset")) {
3611 if (i == argc - 1) {
c092fcad 3612 reply("MSG_MISSING_PARAMS", argv[i]);
d76ed9a9 3613 goto fail;
3614 }
3615 discrim->hostmask_type = SUPERSET;
3616 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3617 if (i == argc - 1) {
c092fcad 3618 reply("MSG_MISSING_PARAMS", argv[i]);
d76ed9a9 3619 goto fail;
3620 }
3621 discrim->hostmask_type = LASTQUIT;
3622 } else {
3623 i--;
3624 discrim->hostmask_type = SUPERSET;
3625 }
3626 discrim->hostmask = argv[++i];
3627 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3628 if (!irccasecmp(argv[++i], "*")) {
3629 discrim->handlemask = 0;
3630 } else {
3631 discrim->handlemask = argv[i];
3632 }
3633 } else if (!irccasecmp(argv[i], "email")) {
3634 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
c092fcad 3635 reply("MSG_NO_SEARCH_ACCESS", "email");
d76ed9a9 3636 goto fail;
3637 } else if (!irccasecmp(argv[++i], "*")) {
3638 discrim->emailmask = 0;
3639 } else {
3640 discrim->emailmask = argv[i];
3641 }
3642 } else if (!irccasecmp(argv[i], "access")) {
3643 const char *cmp = argv[++i];
3644 if (cmp[0] == '<') {
3645 if (discrim->min_level == 0) discrim->min_level = 1;
3646 if (cmp[1] == '=') {
3647 discrim->max_level = strtoul(cmp+2, NULL, 0);
3648 } else {
3649 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3650 }
3651 } else if (cmp[0] == '=') {
3652 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3653 } else if (cmp[0] == '>') {
3654 if (cmp[1] == '=') {
3655 discrim->min_level = strtoul(cmp+2, NULL, 0);
3656 } else {
3657 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3658 }
3659 } else {
c092fcad 3660 reply("MSG_INVALID_CRITERIA", cmp);
d76ed9a9 3661 }
3662 } else {
c092fcad 3663 reply("MSG_INVALID_CRITERIA", argv[i]);
d76ed9a9 3664 goto fail;
3665 }
3666 }
3667 return discrim;
3668 fail:
3669 free(discrim);
3670 return NULL;
3671}
3672
3673static int
3674nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3675{
3676 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3677 || (discrim->flags_off & hi->flags)
3678 || (discrim->min_registered > hi->registered)
3679 || (discrim->max_registered < hi->registered)
3680 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3681 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3682 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3683 || (discrim->min_level > hi->opserv_level)
3684 || (discrim->max_level < hi->opserv_level)) {
3685 return 0;
3686 }
3687 if (discrim->hostmask) {
3688 unsigned int i;
3689 for (i=0; i<hi->masks->used; i++) {
3690 const char *mask = hi->masks->list[i];
3691 if ((discrim->hostmask_type == SUBSET)
3692 && (match_ircglobs(discrim->hostmask, mask))) break;
3693 else if ((discrim->hostmask_type == EXACT)
3694 && !irccasecmp(discrim->hostmask, mask)) break;
3695 else if ((discrim->hostmask_type == SUPERSET)
3696 && (match_ircglobs(mask, discrim->hostmask))) break;
3697 else if ((discrim->hostmask_type == LASTQUIT)
3698 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3699 }
3700 if (i==hi->masks->used) return 0;
3701 }
3702 if (discrim->nickmask) {
3703 struct nick_info *nick = hi->nicks;
3704 while (nick) {
3705 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3706 nick = nick->next;
3707 }
3708 if (!nick) return 0;
3709 }
3710 return 1;
3711}
3712
3713static unsigned int
3714nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3715{
3716 dict_iterator_t it, next;
3717 unsigned int matched;
3718
3719 for (it = dict_first(nickserv_handle_dict), matched = 0;
3720 it && (matched < discrim->limit);
3721 it = next) {
3722 next = iter_next(it);
3723 if (nickserv_discrim_match(discrim, iter_data(it))) {
3724 dsf(source, iter_data(it));
3725 matched++;
3726 }
3727 }
3728 return matched;
3729}
3730
3731static void
3732search_print_func(struct userNode *source, struct handle_info *match)
3733{
3734 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3735}
3736
3737static void
3738search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3739{
3740}
3741
3742static void
3743search_unregister_func (struct userNode *source, struct handle_info *match)
3744{
3745 if (oper_has_access(source, nickserv, match->opserv_level, 0))
258d1427 3746 nickserv_unregister_handle(match, source, nickserv); // XXX nickserv hard coded
d76ed9a9 3747}
3748
3749static int
3750nickserv_sort_accounts_by_access(const void *a, const void *b)
3751{
3752 const struct handle_info *hi_a = *(const struct handle_info**)a;
3753 const struct handle_info *hi_b = *(const struct handle_info**)b;
3754 if (hi_a->opserv_level != hi_b->opserv_level)
3755 return hi_b->opserv_level - hi_a->opserv_level;
3756 return irccasecmp(hi_a->handle, hi_b->handle);
3757}
3758
3759void
3760nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3761{
3762 struct handle_info_list hil;
3763 struct helpfile_table tbl;
3764 unsigned int ii;
3765 dict_iterator_t it;
3766 const char **ary;
3767
3768 memset(&hil, 0, sizeof(hil));
3769 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3770 struct handle_info *hi = iter_data(it);
3771 if (hi->opserv_level)
3772 handle_info_list_append(&hil, hi);
3773 }
3774 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3775 tbl.length = hil.used + 1;
3776 tbl.width = 2;
a8370a20 3777 tbl.flags = TABLE_NO_FREE | TABLE_REPEAT_ROWS | TABLE_REPEAT_HEADERS;
d76ed9a9 3778 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3779 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3780 ary[0] = "Account";
3781 ary[1] = "Level";
3782 for (ii = 0; ii < hil.used; ) {
3783 ary = malloc(tbl.width * sizeof(ary[0]));
3784 ary[0] = hil.list[ii]->handle;
3785 ary[1] = strtab(hil.list[ii]->opserv_level);
3786 tbl.contents[++ii] = ary;
3787 }
3788 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
a8370a20 3789 /*reply("MSG_MATCH_COUNT", hil.used); */
d76ed9a9 3790 for (ii = 0; ii < hil.used; ii++)
3791 free(tbl.contents[ii]);
3792 free(tbl.contents);
3793 free(hil.list);
3794}
3795
3796static NICKSERV_FUNC(cmd_search)
3797{
3798 struct nickserv_discrim *discrim;
3799 discrim_search_func action;
3800 struct svccmd *subcmd;
3801 unsigned int matches;
3802 char buf[MAXLEN];
3803
3804 NICKSERV_MIN_PARMS(3);
3805 sprintf(buf, "search %s", argv[1]);
3806 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3807 if (!irccasecmp(argv[1], "print"))
3808 action = search_print_func;
3809 else if (!irccasecmp(argv[1], "count"))
3810 action = search_count_func;
3811 else if (!irccasecmp(argv[1], "unregister"))
3812 action = search_unregister_func;
3813 else {
3814 reply("NSMSG_INVALID_ACTION", argv[1]);
3815 return 0;
3816 }
3817
3818 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3819 return 0;
3820
c092fcad 3821 discrim = nickserv_discrim_create(cmd, user, argc-2, argv+2);
d76ed9a9 3822 if (!discrim)
3823 return 0;
3824
3825 if (action == search_print_func)
3826 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3827 else if (action == search_count_func)
3828 discrim->limit = INT_MAX;
3829
3830 matches = nickserv_discrim_search(discrim, action, user);
3831
3832 if (matches)
3833 reply("MSG_MATCH_COUNT", matches);
3834 else
3835 reply("MSG_NO_MATCHES");
3836
3837 free(discrim);
3838 return 0;
3839}
3840
3841static MODCMD_FUNC(cmd_checkpass)
3842{
3843 struct handle_info *hi;
3844
3845 NICKSERV_MIN_PARMS(3);
3846 if (!(hi = get_handle_info(argv[1]))) {
3847 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3848 return 0;
3849 }
3850 if (checkpass(argv[2], hi->passwd))
3851 reply("CHECKPASS_YES");
3852 else
3853 reply("CHECKPASS_NO");
3854 argv[2] = "****";
3855 return 1;
3856}
3857
3858static void
3859nickserv_db_read_handle(const char *handle, dict_t obj)
3860{
3861 const char *str;
5177fd21 3862 struct string_list *masks, *slist, *ignores;
d76ed9a9 3863 struct handle_info *hi;
3864 struct userNode *authed_users;
02c37249 3865 struct userData *channels;
d76ed9a9 3866 unsigned long int id;
3867 unsigned int ii;
3868 dict_t subdb;
2362161a 3869 char *setter, *note;
3870 time_t date;
d76ed9a9 3871
3872 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3873 id = str ? strtoul(str, NULL, 0) : 0;
3874 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3875 if (!str) {
3876 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3877 return;
3878 }
3879 if ((hi = get_handle_info(handle))) {
3880 authed_users = hi->users;
02c37249 3881 channels = hi->channels;
d76ed9a9 3882 hi->users = NULL;
02c37249 3883 hi->channels = NULL;
d76ed9a9 3884 dict_remove(nickserv_handle_dict, hi->handle);
3885 } else {
3886 authed_users = NULL;
02c37249 3887 channels = NULL;
d76ed9a9 3888 }
3889 hi = register_handle(handle, str, id);
3890 if (authed_users) {
3891 hi->users = authed_users;
3892 while (authed_users) {
3893 authed_users->handle_info = hi;
3894 authed_users = authed_users->next_authed;
3895 }
3896 }
02c37249 3897 hi->channels = channels;
d76ed9a9 3898 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3899 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
5177fd21 3900 ignores = database_get_data(obj, KEY_IGNORES, RECDB_STRING_LIST);
3901 hi->ignores = ignores ? string_list_copy(ignores) : alloc_string_list(1);
d76ed9a9 3902 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3903 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3904 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3905 hi->language = language_find(str ? str : "C");
3906 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3907 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3908 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3909 if (str)
3910 hi->infoline = strdup(str);
3911 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3912 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
3913 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3914 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
3915 /* We want to read the nicks even if disable_nicks is set. This is so
3916 * that we don't lose the nick data entirely. */
3917 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3918 if (slist) {
3919 for (ii=0; ii<slist->used; ii++)
3920 register_nick(slist->list[ii], hi);
3921 }
3922 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3923 if (str) {
3924 for (ii=0; str[ii]; ii++)
3925 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3926 }
3927 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
338a82b5 3928 hi->userlist_style = str ? str[0] : HI_DEFAULT_STYLE;
d76ed9a9 3929 str = database_get_data(obj, KEY_ANNOUNCEMENTS, RECDB_QSTRING);
3930 hi->announcements = str ? str[0] : '?';
3931 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3932 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3933 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3934 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3935 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3936 if (!str)
3937 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3938 if (str)
3939 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3940 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3941 if (str)
3942 nickserv_set_email_addr(hi, str);
3943 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3944 if (str)
3945 hi->epithet = strdup(str);
2362161a 3946 subdb = database_get_data(obj, KEY_NOTE_NOTE, RECDB_OBJECT);
3947 if (subdb) {
3948 setter = database_get_data(subdb, KEY_NOTE_SETTER, RECDB_QSTRING);
3949 str = database_get_data(subdb, KEY_NOTE_DATE, RECDB_QSTRING);
3950 date = str ? (time_t)strtoul(str, NULL, 0) : now;
3951 note = database_get_data(subdb, KEY_NOTE_NOTE, RECDB_QSTRING);
3952 if (setter && date && note)
3953 {
3954 if (!(hi->note = nickserv_add_note(setter, date, note)))
3955 hi->note = NULL;
3956 }
3957 }
3958
d76ed9a9 3959 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3960 if (str)
3961 hi->fakehost = strdup(str);
7637f48f 3962
d76ed9a9 3963 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3964 if (subdb) {
3965 const char *data, *type, *expires, *cookie_str;
3966 struct handle_cookie *cookie;
3967
3968 cookie = calloc(1, sizeof(*cookie));
3969 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3970 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3971 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3972 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3973 if (!type || !expires || !cookie_str) {
3974 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3975 goto cookie_out;
3976 }
3977 if (!irccasecmp(type, KEY_ACTIVATION))
3978 cookie->type = ACTIVATION;
3979 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3980 cookie->type = PASSWORD_CHANGE;
3981 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3982 cookie->type = EMAIL_CHANGE;
3983 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3984 cookie->type = ALLOWAUTH;
3985 else {
3986 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3987 goto cookie_out;
3988 }
3989 cookie->expires = strtoul(expires, NULL, 0);
3990 if (cookie->expires < now)
3991 goto cookie_out;
3992 if (data)
3993 cookie->data = strdup(data);
3994 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3995 cookie->hi = hi;
3996 cookie_out:
3997 if (cookie->hi)
3998 nickserv_bake_cookie(cookie);
3999 else
4000 nickserv_free_cookie(cookie);
4001 }
4002}
4003
4004static int
4005nickserv_saxdb_read(dict_t db) {
4006 dict_iterator_t it;
4007 struct record_data *rd;
4008
4009 for (it=dict_first(db); it; it=iter_next(it)) {
4010 rd = iter_data(it);
4011 nickserv_db_read_handle(iter_key(it), rd->d.object);
4012 }
4013 return 0;
4014}
4015
4016static NICKSERV_FUNC(cmd_mergedb)
4017{
4018 struct timeval start, stop;
4019 dict_t db;
4020
4021 NICKSERV_MIN_PARMS(2);
4022 gettimeofday(&start, NULL);
4023 if (!(db = parse_database(argv[1]))) {
4024 reply("NSMSG_DB_UNREADABLE", argv[1]);
4025 return 0;
4026 }
4027 nickserv_saxdb_read(db);
4028 free_database(db);
4029 gettimeofday(&stop, NULL);
4030 stop.tv_sec -= start.tv_sec;
4031 stop.tv_usec -= start.tv_usec;
4032 if (stop.tv_usec < 0) {
4033 stop.tv_sec -= 1;
4034 stop.tv_usec += 1000000;
4035 }
4036 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
4037 return 1;
4038}
4039
4040static void
4041expire_handles(UNUSED_ARG(void *data))
4042{
4043 dict_iterator_t it, next;
4044 time_t expiry;
4045 struct handle_info *hi;
4046
4047 for (it=dict_first(nickserv_handle_dict); it; it=next) {
4048 next = iter_next(it);
4049 hi = iter_data(it);
4050 if ((hi->opserv_level > 0)
4051 || hi->users
4052 || HANDLE_FLAGGED(hi, FROZEN)
4053 || HANDLE_FLAGGED(hi, NODELETE)) {
4054 continue;
4055 }
4056 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
4057 if ((now - hi->lastseen) > expiry) {
4058 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
258d1427 4059 nickserv_unregister_handle(hi, NULL, NULL);
d76ed9a9 4060 }
4061 }
4062
4063 if (nickserv_conf.handle_expire_frequency)
4064 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4065}
4066
4067static void
4068nickserv_load_dict(const char *fname)
4069{
4070 FILE *file;
4071 char line[128];
4072 if (!(file = fopen(fname, "r"))) {
4073 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
4074 return;
4075 }
4076 while (!feof(file)) {
4077 fgets(line, sizeof(line), file);
4078 if (!line[0])
4079 continue;
4080 if (line[strlen(line)-1] == '\n')
4081 line[strlen(line)-1] = 0;
4082 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
4083 }
4084 fclose(file);
4085 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
4086}
4087
4088static enum reclaim_action
4089reclaim_action_from_string(const char *str) {
4090 if (!str)
4091 return RECLAIM_NONE;
4092 else if (!irccasecmp(str, "warn"))
4093 return RECLAIM_WARN;
4094 else if (!irccasecmp(str, "svsnick"))
4095 return RECLAIM_SVSNICK;
4096 else if (!irccasecmp(str, "kill"))
4097 return RECLAIM_KILL;
4098 else
4099 return RECLAIM_NONE;
4100}
4101
4102static void
4103nickserv_conf_read(void)
4104{
4105 dict_t conf_node, child;
4106 const char *str;
4107 dict_iterator_t it;
7637f48f 4108 struct string_list *strlist;
d76ed9a9 4109
4110 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
4111 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
4112 return;
4113 }
4114 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
4115 if (!str)
4116 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
4117 if (nickserv_conf.valid_handle_regex_set)
4118 regfree(&nickserv_conf.valid_handle_regex);
4119 if (str) {
4120 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4121 nickserv_conf.valid_handle_regex_set = !err;
4122 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
4123 } else {
4124 nickserv_conf.valid_handle_regex_set = 0;
4125 }
4126 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
4127 if (nickserv_conf.valid_nick_regex_set)
4128 regfree(&nickserv_conf.valid_nick_regex);
4129 if (str) {
4130 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4131 nickserv_conf.valid_nick_regex_set = !err;
4132 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
4133 } else {
4134 nickserv_conf.valid_nick_regex_set = 0;
4135 }
bf93ca8d 4136 str = database_get_data(conf_node, KEY_VALID_FAKEHOST_REGEX, RECDB_QSTRING);
4137 if (nickserv_conf.valid_fakehost_regex_set)
4138 regfree(&nickserv_conf.valid_fakehost_regex);
4139 if (str) {
4140 int err = regcomp(&nickserv_conf.valid_fakehost_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4141 nickserv_conf.valid_fakehost_regex_set = !err;
4142 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_fakehost_regex (error %d)", err);
4143 } else {
4144 nickserv_conf.valid_fakehost_regex_set = 0;
4145 }
d76ed9a9 4146 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
4147 if (!str)
4148 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
4149 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
4150 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
4151 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
4152 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
4153 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
4154 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
4155 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
4156 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
4157 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
4158 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
4159 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
4160 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
4161 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
4162 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
4163 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
4164 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
4165 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
4166 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
4167 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
4168 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
4169 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
4170 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
4171 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
4172 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
4173 if (!str)
4174 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
4175 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
4176 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4177 if (!str)
4178 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4179 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
4180 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4181 if (!str)
4182 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4183 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
4184 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
4185 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
4186 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
4187 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
4188 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
4189 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
4190 if (!nickserv_conf.disable_nicks) {
4191 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
4192 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4193 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
4194 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
4195 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
4196 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4197 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
4198 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
4199 }
4200 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
4201 for (it=dict_first(child); it; it=iter_next(it)) {
4202 const char *key = iter_key(it), *value;
4203 unsigned char flag;
4204 int pos;
4205
4206 if (!strncasecmp(key, "uc_", 3))
4207 flag = toupper(key[3]);
4208 else if (!strncasecmp(key, "lc_", 3))
4209 flag = tolower(key[3]);
4210 else
4211 flag = key[0];
4212
4213 if ((pos = handle_inverse_flags[flag])) {
4214 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
4215 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
4216 }
4217 }
4218 if (nickserv_conf.weak_password_dict)
4219 dict_delete(nickserv_conf.weak_password_dict);
4220 nickserv_conf.weak_password_dict = dict_new();
4221 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
4222 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
4223 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
4224 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
4225 if (str)
4226 nickserv_load_dict(str);
4227 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
4228 if (nickserv && str)
4229 NickChange(nickserv, str, 0);
4230 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
4231 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
4232 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
4233 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
4234 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
4235 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
4236 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
4237 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
8dc1d9ae 4238 str = database_get_data(conf_node, KEY_SYNC_LOG, RECDB_QSTRING);
4239 nickserv_conf.sync_log = str ? enabled_string(str) : 0;
d76ed9a9 4240 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
4241 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
4242 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
4243 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
4244 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
4245 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
4246 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
4247 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
4248 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
4249 nickserv_conf.titlehost_suffix = str ? str : "example.net";
5a1daaab 4250
7637f48f 4251 free_string_list(nickserv_conf.denied_fakehost_words);
4252 strlist = database_get_data(conf_node, KEY_DENIED_FAKEHOST_WORDS, RECDB_STRING_LIST);
4253 if(strlist)
4254 strlist = string_list_copy(strlist);
4255 else {
4256 strlist = alloc_string_list(4);
4257 string_list_append(strlist, strdup("sex"));
4258 string_list_append(strlist, strdup("fuck"));
4259 }
4260 nickserv_conf.denied_fakehost_words = strlist;
4261
338a82b5 4262 str = database_get_data(conf_node, KEY_DEFAULT_STYLE, RECDB_QSTRING);
4263 nickserv_conf.default_style = str ? str[0] : HI_DEFAULT_STYLE;
4264
5a1daaab 4265 str = database_get_data(conf_node, KEY_AUTO_OPER, RECDB_QSTRING);
4266 nickserv_conf.auto_oper = str ? str : "";
4267
4268 str = database_get_data(conf_node, KEY_AUTO_ADMIN, RECDB_QSTRING);
4269 nickserv_conf.auto_admin = str ? str : "";
4270
d76ed9a9 4271 str = conf_get_data("server/network", RECDB_QSTRING);
4272 nickserv_conf.network_name = str ? str : "some IRC network";
4273 if (!nickserv_conf.auth_policer_params) {
4274 nickserv_conf.auth_policer_params = policer_params_new();
4275 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
4276 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
4277 }
4278 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
4279 for (it=dict_first(child); it; it=iter_next(it))
4280 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
4281}
4282
4283static void
4284nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
4285 const char *msg;
4286 char newnick[NICKLEN+1];
4287
4288 assert(user);
4289 assert(ni);
4290 switch (action) {
4291 case RECLAIM_NONE:
4292 /* do nothing */
4293 break;
4294 case RECLAIM_WARN:
4295 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4296 break;
4297 case RECLAIM_SVSNICK:
4298 do {
4299 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
4300 } while (GetUserH(newnick));
4301 irc_svsnick(nickserv, user, newnick);
4302 break;
4303 case RECLAIM_KILL:
4304 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
4305 irc_kill(nickserv, user, msg);
4306 break;
4307 }
4308}
4309
4310static void
4311nickserv_reclaim_p(void *data) {
4312 struct userNode *user = data;
4313 struct nick_info *ni = get_nick_info(user->nick);
4314 if (ni)
4315 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4316}
4317
4318static int
4319check_user_nick(struct userNode *user) {
4320 struct nick_info *ni;
4321 user->modes &= ~FLAGS_REGNICK;
4322 if (!(ni = get_nick_info(user->nick)))
4323 return 0;
4324 if (user->handle_info == ni->owner) {
4325 user->modes |= FLAGS_REGNICK;
4326 irc_regnick(user);
4327 return 0;
4328 }
4329 if (nickserv_conf.warn_nick_owned)
4330 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4331 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
4332 return 0;
4333 if (nickserv_conf.auto_reclaim_delay)
4334 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
4335 else
4336 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4337 return 0;
4338}
4339
4340int
4341handle_new_user(struct userNode *user)
4342{
4343 return check_user_nick(user);
4344}
4345
4346void
4347handle_account(struct userNode *user, const char *stamp)
4348{
4349 struct handle_info *hi;
b21e2cfe 4350 char *colon;
d76ed9a9 4351
4352#ifdef WITH_PROTOCOL_P10
a9b5e3de 4353 time_t timestamp = 0;
4354
4355 colon = strchr(stamp, ':');
4356 if(colon && colon[1])
4357 {
4358 *colon = 0;
4359 timestamp = atoi(colon+1);
4360 }
d76ed9a9 4361 hi = dict_find(nickserv_handle_dict, stamp, NULL);
c0e47c86 4362 if(hi && timestamp && hi->registered != timestamp)
a9b5e3de 4363 {
4364 log_module(MAIN_LOG, LOG_WARNING, "%s using account %s but timestamp does not match %lu is not %lu.", user->nick, stamp, timestamp, hi->registered);
4365 return;
4366 }
d76ed9a9 4367#else
4368 hi = dict_find(nickserv_id_dict, stamp, NULL);
9b2d838a 4369 log_module(MAIN_LOG, LOG_WARNING, "Using non-P10 code in accounts, not tested at all!");
d76ed9a9 4370#endif
4371
4372 if (hi) {
4373 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
4374 return;
4375 }
4376 set_user_handle_info(user, hi, 0);
4377 } else {
4378 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
4379 }
4380}
4381
4382void
4383handle_nick_change(struct userNode *user, const char *old_nick)
4384{
4385 struct handle_info *hi;
4386
4387 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4388 dict_remove(nickserv_allow_auth_dict, old_nick);
4389 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4390 }
4391 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4392 check_user_nick(user);
4393}
4394
4395void
4396nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
4397{
4398 dict_remove(nickserv_allow_auth_dict, user->nick);
4399 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4400 set_user_handle_info(user, NULL, 0);
4401}
4402
4403static struct modcmd *
4404nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4405{
4406 if (min_level > 0) {
4407 char buf[16];
4408 sprintf(buf, "%u", min_level);
4409 if (must_be_qualified) {
4410 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4411 } else {
4412 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4413 }
4414 } else if (min_level == 0) {
4415 if (must_be_qualified) {
4416 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4417 } else {
4418 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4419 }
4420 } else {
4421 if (must_be_qualified) {
4422 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4423 } else {
4424 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4425 }
4426 }
4427}
4428
4429static void
4430nickserv_db_cleanup(void)
4431{
4432 unreg_del_user_func(nickserv_remove_user);
4433 userList_clean(&curr_helpers);
4434 policer_params_delete(nickserv_conf.auth_policer_params);
4435 dict_delete(nickserv_handle_dict);
4436 dict_delete(nickserv_nick_dict);
4437 dict_delete(nickserv_opt_dict);
4438 dict_delete(nickserv_allow_auth_dict);
4439 dict_delete(nickserv_email_dict);
4440 dict_delete(nickserv_id_dict);
4441 dict_delete(nickserv_conf.weak_password_dict);
4442 free(auth_func_list);
4443 free(unreg_func_list);
4444 free(rf_list);
4445 free(allowauth_func_list);
4446 free(handle_merge_func_list);
4447 free(failpw_func_list);
4448 if (nickserv_conf.valid_handle_regex_set)
4449 regfree(&nickserv_conf.valid_handle_regex);
4450 if (nickserv_conf.valid_nick_regex_set)
4451 regfree(&nickserv_conf.valid_nick_regex);
4452}
4453
4454void
4455init_nickserv(const char *nick)
4456{
7637f48f 4457 struct chanNode *chan;
d76ed9a9 4458 unsigned int i;
4459 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4460 reg_new_user_func(handle_new_user);
4461 reg_nick_change_func(handle_nick_change);
4462 reg_del_user_func(nickserv_remove_user);
4463 reg_account_func(handle_account);
4464
4465 /* set up handle_inverse_flags */
4466 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4467 for (i=0; handle_flags[i]; i++) {
4468 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4469 flag_access_levels[i] = 0;
4470 }
4471
4472 conf_register_reload(nickserv_conf_read);
4473 nickserv_opt_dict = dict_new();
4474 nickserv_email_dict = dict_new();
5177fd21 4475
d76ed9a9 4476 dict_set_free_keys(nickserv_email_dict, free);
4477 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4478
4479 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4048352e 4480/* Removed qualified_host as default requirement for AUTH, REGISTER, PASS, etc. nets
4481 * can enable it per command using modcmd. (its a shitty default IMO, and now in 1.3
4482 * a big pain to disable since its nolonger in the config file. ) -Rubin
4483 */
4484 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+loghostmask", NULL);
d76ed9a9 4485 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4048352e 4486 nickserv_define_func("REGISTER", cmd_register, -1, 0, 0);
d76ed9a9 4487 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4048352e 4488 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 0);
d76ed9a9 4489 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4490 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4491 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4492 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4493 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4048352e 4494 nickserv_define_func("PASS", cmd_pass, -1, 1, 0);
d76ed9a9 4495 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4496 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4497 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4498 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4499 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4500 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4501 nickserv_define_func("MERGE", cmd_merge, 0, 1, 0);
4502 if (!nickserv_conf.disable_nicks) {
4503 /* nick management commands */
4504 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4505 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4506 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4507 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4508 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4509 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4510 }
4511 if (nickserv_conf.email_enabled) {
4512 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4048352e 4513 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 0);
4514 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 0);
d76ed9a9 4515 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
34938510 4516 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
d76ed9a9 4517 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4518 }
4519 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
5177fd21 4520 /* ignore commands */
4521 nickserv_define_func("ADDIGNORE", cmd_addignore, -1, 1, 0);
4522 nickserv_define_func("OADDIGNORE", cmd_oaddignore, 0, 1, 0);
4523 nickserv_define_func("DELIGNORE", cmd_delignore, -1, 1, 0);
4524 nickserv_define_func("ODELIGNORE", cmd_odelignore, 0, 1, 0);
d76ed9a9 4525 /* miscellaneous commands */
4526 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4527 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4528 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4529 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4530 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4531 /* other options */
4532 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4533 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4534 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4535 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4536 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
7fdb7639 4537 dict_insert(nickserv_opt_dict, "AUTOHIDE", opt_autohide);
338a82b5 4538 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
d76ed9a9 4539 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4540 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4541 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4542 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4543 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4544 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
2362161a 4545 dict_insert(nickserv_opt_dict, "NOTE", opt_note);
d76ed9a9 4546 if (nickserv_conf.titlehost_suffix) {
4547 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4548 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4549 }
4550 dict_insert(nickserv_opt_dict, "ANNOUNCEMENTS", opt_announcements);
4551 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
0b587959 4552 dict_insert(nickserv_opt_dict, "ADVANCED", opt_advanced);
d76ed9a9 4553 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4554
4555 nickserv_handle_dict = dict_new();
4556 dict_set_free_keys(nickserv_handle_dict, free);
4557 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4558
4559 nickserv_id_dict = dict_new();
4560 dict_set_free_keys(nickserv_id_dict, free);
4561
4562 nickserv_nick_dict = dict_new();
1117fc5a 4563 dict_set_free_data(nickserv_nick_dict, free);
d76ed9a9 4564
4565 nickserv_allow_auth_dict = dict_new();
4566
4567 userList_init(&curr_helpers);
4568
4569 if (nick) {
a32da4c7 4570 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4571 nickserv = AddService(nick, modes ? modes : NULL, "Nick Services", NULL);
d76ed9a9 4572 nickserv_service = service_register(nickserv);
4573 }
4574 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4575 reg_exit_func(nickserv_db_cleanup);
4576 if(nickserv_conf.handle_expire_frequency)
4577 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
7637f48f 4578
4579 if(autojoin_channels && nickserv) {
4580 for (i = 0; i < autojoin_channels->used; i++) {
4581 chan = AddChannel(autojoin_channels->list[i], now, "+nt", NULL, NULL);
4582 AddChannelUser(nickserv, chan)->modes |= MODE_CHANOP;
4583 }
4584 }
4585
d76ed9a9 4586 message_register_table(msgtab);
4587}