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