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