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