1 /* nickserv.c - Nick/authentication service
2 * Copyright 2000-2008 srvx Development Team
4 * This file is part of srvx.
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.
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.
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.
25 #include "opserv.h" /* for gag_create(), opserv_bad_channel() */
33 # include "rx/rxposix.h"
36 #define NICKSERV_CONF_NAME "services/nickserv"
38 #define KEY_DISABLE_NICKS "disable_nicks"
39 #define KEY_DEFAULT_HOSTMASK "default_hostmask"
40 #define KEY_NICKS_PER_HANDLE "nicks_per_handle"
41 #define KEY_NICKS_PER_ACCOUNT "nicks_per_account"
42 #define KEY_PASSWORD_MIN_LENGTH "password_min_length"
43 #define KEY_PASSWORD_MIN_DIGITS "password_min_digits"
44 #define KEY_PASSWORD_MIN_UPPER "password_min_upper"
45 #define KEY_PASSWORD_MIN_LOWER "password_min_lower"
46 #define KEY_VALID_HANDLE_REGEX "valid_handle_regex"
47 #define KEY_VALID_ACCOUNT_REGEX "valid_account_regex"
48 #define KEY_VALID_NICK_REGEX "valid_nick_regex"
49 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
50 #define KEY_MODOPER_LEVEL "modoper_level"
51 #define KEY_SET_EPITHET_LEVEL "set_epithet_level"
52 #define KEY_SET_TITLE_LEVEL "set_title_level"
53 #define KEY_SET_FAKEHOST_LEVEL "set_fakehost_level"
54 #define KEY_SET_FAKEIDENT_LEVEL "set_fakeident_level"
55 #define KEY_TITLEHOST_SUFFIX "titlehost_suffix"
56 #define KEY_FLAG_LEVELS "flag_levels"
57 #define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq"
58 #define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq"
59 #define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay"
60 #define KEY_ACCOUNT_EXPIRE_DELAY "account_expire_delay"
61 #define KEY_NOCHAN_HANDLE_EXPIRE_DELAY "nochan_handle_expire_delay"
62 #define KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY "nochan_account_expire_delay"
63 #define KEY_DICT_FILE "dict_file"
64 #define KEY_NICK "nick"
65 #define KEY_LANGUAGE "language"
66 #define KEY_AUTOGAG_ENABLED "autogag_enabled"
67 #define KEY_AUTOGAG_DURATION "autogag_duration"
68 #define KEY_AUTH_POLICER "auth_policer"
69 #define KEY_EMAIL_VISIBLE_LEVEL "email_visible_level"
70 #define KEY_EMAIL_ENABLED "email_enabled"
71 #define KEY_EMAIL_REQUIRED "email_required"
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 #define KEY_OUNREGISTER_INACTIVE "ounregister_inactive"
76 #define KEY_OUNREGISTER_FLAGS "ounregister_flags"
77 #define KEY_HANDLE_TS_MODE "account_timestamp_mode"
80 #define KEY_PASSWD "passwd"
81 #define KEY_NICKS "nicks"
82 #define KEY_MASKS "masks"
83 #define KEY_OPSERV_LEVEL "opserv_level"
84 #define KEY_FLAGS "flags"
85 #define KEY_REGISTER_ON "register"
86 #define KEY_LAST_SEEN "lastseen"
87 #define KEY_INFO "info"
88 #define KEY_USERLIST_STYLE "user_style"
89 #define KEY_SCREEN_WIDTH "screen_width"
90 #define KEY_LAST_AUTHED_HOST "last_authed_host"
91 #define KEY_LAST_QUIT_HOST "last_quit_host"
92 #define KEY_EMAIL_ADDR "email_addr"
93 #define KEY_COOKIE "cookie"
94 #define KEY_COOKIE_DATA "data"
95 #define KEY_COOKIE_TYPE "type"
96 #define KEY_COOKIE_EXPIRES "expires"
97 #define KEY_ACTIVATION "activation"
98 #define KEY_PASSWORD_CHANGE "password change"
99 #define KEY_EMAIL_CHANGE "email change"
100 #define KEY_ALLOWAUTH "allowauth"
101 #define KEY_EPITHET "epithet"
102 #define KEY_TABLE_WIDTH "table_width"
103 #define KEY_MAXLOGINS "maxlogins"
104 #define KEY_FAKEHOST "fakehost"
105 #define KEY_FAKEIDENT "fakeident"
106 #define KEY_NOTES "notes"
107 #define KEY_NOTE_EXPIRES "expires"
108 #define KEY_NOTE_SET "set"
109 #define KEY_NOTE_SETTER "setter"
110 #define KEY_NOTE_NOTE "note"
111 #define KEY_KARMA "karma"
113 #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
115 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
116 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
117 typedef OPTION_FUNC(option_func_t);
119 DEFINE_LIST(handle_info_list, struct handle_info*)
121 #define NICKSERV_MIN_PARMS(N) do { \
123 reply("MSG_MISSING_PARAMS", argv[0]); \
124 svccmd_send_help(user, nickserv, cmd); \
128 struct userNode *nickserv;
129 struct userList curr_helpers;
130 const char *handle_flags = HANDLE_FLAGS;
132 static struct module *nickserv_module;
133 static struct service *nickserv_service;
134 static struct log_type *NS_LOG;
135 static dict_t nickserv_handle_dict; /* contains struct handle_info* */
136 static dict_t nickserv_id_dict; /* contains struct handle_info* */
137 static dict_t nickserv_nick_dict; /* contains struct nick_info* */
138 static dict_t nickserv_opt_dict; /* contains option_func_t* */
139 static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
140 static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
141 static char handle_inverse_flags[256];
142 static unsigned int flag_access_levels[32];
143 static const struct message_entry msgtab[] = {
144 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
145 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
146 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
147 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
148 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
149 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
150 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
151 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
152 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
153 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
154 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
155 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
156 { "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." },
157 { "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." },
158 { "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." },
159 { "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." },
160 { "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." },
161 { "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." },
162 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
163 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
164 { "NSMSG_NO_COOKIE_FOREIGN", "The account $b%s$b does not have any cookie issued right now." },
165 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
166 { "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." },
167 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
168 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
169 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
170 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
171 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
172 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
173 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
174 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
175 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
176 { "NSMSG_ATE_COOKIE_FOREIGN", "I ate the cookie for account $b%s$b. It may now have another." },
177 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
178 { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
179 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
180 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
181 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
182 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
183 { "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)" },
184 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
185 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
186 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
187 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
188 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
189 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
190 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
191 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
192 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
193 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
194 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
195 { "NSMSG_TITLE_INVALID", "Titles cannot contain any dots; please choose another." },
196 { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." },
197 { "NSMSG_TITLE_TRUNCATED_RENAME", "That account name combined with the user's title would result in a truncated host; please choose a shorter account name." },
198 { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
199 { "NSMSG_FAKEIDENT_INVALID", "Fake idents must be shorter than %d characters." },
200 { "NSMSG_FAKEMASK_INVALID", "Fake ident@hosts must be shorter than %d characters." },
201 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
202 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
203 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
204 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
205 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
206 { "NSMSG_HANDLEINFO_KARMA", " Karma: %d" },
207 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
208 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
209 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
210 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
211 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
212 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
213 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
214 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
215 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
216 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
217 { "NSMSG_HANDLEINFO_FAKEIDENT", " Fake ident: %s" },
218 { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" },
219 { "NSMSG_HANDLEINFO_FAKEIDENTHOST", " Fake host: %s@%s" },
220 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
221 { "NSMSG_HANDLEINFO_NO_NOTES", " Notes: None" },
222 { "NSMSG_HANDLEINFO_NOTE_EXPIRES", " Note %d (%s ago by %s, expires %s): %s" },
223 { "NSMSG_HANDLEINFO_NOTE", " Note %d (%s ago by %s): %s" },
224 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
225 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
226 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
227 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
228 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
229 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
230 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
231 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
232 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
233 { "NSMSG_NOTE_EXPIRES", "Note %d (%s ago by %s, expires %s): %s" },
234 { "NSMSG_NOTE", "Note %d (%s ago by %s): %s" },
235 { "NSMSG_NOTE_COUNT", "%u note(s) for %s." },
236 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
237 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
238 { "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)." },
239 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
240 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
241 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
242 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
243 { "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." },
244 { "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." },
245 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
246 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
247 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
248 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
249 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
250 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
251 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
252 { "NSMSG_PASS_SUCCESS", "Password changed." },
253 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
254 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
255 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
256 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
257 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
258 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
259 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
260 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
261 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
262 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
263 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
264 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
265 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
266 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
267 { "NSMSG_UNREGISTER_MUST_FORCE", "Account $b%s$b is not inactive or has special flags set; use FORCE to unregister it." },
268 { "NSMSG_UNREGISTER_CANNOT_FORCE", "Account $b%s$b is not inactive or has special flags set; have an IRCOp use FORCE to unregister it." },
269 { "NSMSG_UNREGISTER_NODELETE", "Account $b%s$b is protected from unregistration." },
270 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
271 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
272 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
273 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
274 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
275 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
276 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
277 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
278 { "NSMSG_EXCESSIVE_DURATION", "$b%s$b is too long for this command." },
279 { "NSMSG_NOTE_ADDED", "Note $b%d$b added to $b%s$b." },
280 { "NSMSG_NOTE_REMOVED", "Note $b%d$b removed from $b%s$b." },
281 { "NSMSG_NO_SUCH_NOTE", "Account $b%s$b does not have a note with ID $b%d$b." },
282 { "NSMSG_NO_ACCESS", "Access denied." },
283 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
284 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
285 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
286 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
287 { "NSMSG_DB_MERGED", "$N merged DB from %s (in %lu.%03lu seconds)." },
288 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
289 { "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." },
290 { "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." },
291 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
292 { "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." },
293 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
294 { "NSMSG_SEARCH_MATCH", "Match: %s" },
295 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
296 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
297 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
298 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
299 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
300 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
301 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
302 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
303 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
304 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
305 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
306 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
307 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
308 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
309 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
310 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
311 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
312 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
313 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
314 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
315 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
316 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
317 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
318 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
319 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
320 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
321 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
322 { "NSMSG_SET_FAKEIDENT", "$bFAKEIDENT: $b%s" },
323 { "NSMSG_SET_FAKEIDENTHOST", "$bFAKEHOST: $b%s@%s" },
324 { "NSMSG_INVALID_KARMA", "$b%s$b is not a valid karma modifier." },
325 { "NSMSG_SET_KARMA", "$bKARMA: $b%d$b" },
326 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
327 { "NSEMAIL_ACTIVATION_BODY", "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 %2$s\nTo verify your email address and complete the account registration, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nThis 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 /msg %3$s@%4$s AUTH %5$s your-password\n Please remember to fill in 'your-password' with the actual password you gave to us when you registered.\n\nIf you did NOT request this account, you do not need to do anything. Please contact the %1$s staff if you have questions, and be sure to check our website." },
328 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
329 { "NSEMAIL_PASSWORD_CHANGE_BODY", "This email has been sent to verify that you wish to change the password on your account %5$s. Your cookie is %2$s.\nTo complete the password change, 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 your password to be changed, you do not need to do anything. Please contact the %1$s staff if you have questions." },
330 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
331 { "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." },
332 { "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." },
333 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
334 { "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." },
335 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
336 { "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." },
337 { "CHECKPASS_YES", "Yes." },
338 { "CHECKPASS_NO", "No." },
339 { "CHECKEMAIL_NOT_SET", "No email set." },
340 { "CHECKEMAIL_YES", "Yes." },
341 { "CHECKEMAIL_NO", "No." },
345 enum reclaim_action {
351 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
352 static void nickserv_reclaim_p(void *data);
353 static int nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask);
355 enum handle_ts_mode {
361 unsigned int disable_nicks : 1;
362 unsigned int valid_handle_regex_set : 1;
363 unsigned int valid_nick_regex_set : 1;
364 unsigned int autogag_enabled : 1;
365 unsigned int email_enabled : 1;
366 unsigned int email_required : 1;
367 unsigned int default_hostmask : 1;
368 unsigned int warn_nick_owned : 1;
369 unsigned int warn_clone_auth : 1;
370 unsigned long nicks_per_handle;
371 unsigned long password_min_length;
372 unsigned long password_min_digits;
373 unsigned long password_min_upper;
374 unsigned long password_min_lower;
375 unsigned long db_backup_frequency;
376 unsigned long handle_expire_frequency;
377 unsigned long autogag_duration;
378 unsigned long email_visible_level;
379 unsigned long cookie_timeout;
380 unsigned long handle_expire_delay;
381 unsigned long nochan_handle_expire_delay;
382 unsigned long modoper_level;
383 unsigned long set_epithet_level;
384 unsigned long set_title_level;
385 unsigned long set_fakehost_level;
386 unsigned long set_fakeident_level;
387 unsigned long handles_per_email;
388 unsigned long email_search_level;
389 const char *network_name;
390 const char *titlehost_suffix;
391 regex_t valid_handle_regex;
392 regex_t valid_nick_regex;
393 dict_t weak_password_dict;
394 struct policer_params *auth_policer_params;
395 enum reclaim_action reclaim_action;
396 enum reclaim_action auto_reclaim_action;
397 enum handle_ts_mode handle_ts_mode;
398 unsigned long auto_reclaim_delay;
399 unsigned char default_maxlogins;
400 unsigned char hard_maxlogins;
401 unsigned long ounregister_inactive;
402 unsigned long ounregister_flags;
405 /* We have 2^32 unique account IDs to use. */
406 unsigned long int highest_id = 0;
408 #define WALK_NOTES(HANDLE, PREV, NOTE) \
409 for (PREV = NULL, NOTE = (HANDLE)->notes; NOTE != NULL; PREV = NOTE, NOTE = NOTE->next) \
410 if (NOTE->expires && NOTE->expires < now) { \
411 if (PREV) PREV->next = NOTE->next; else (HANDLE)->notes = NOTE->next; \
413 if (!(NOTE = PREV ? PREV : (HANDLE)->notes)) break; \
417 canonicalize_hostmask(char *mask)
419 char *out = mask, *temp;
420 if ((temp = strchr(mask, '!'))) {
422 while (*temp) *out++ = *temp++;
428 static struct handle_info *
429 register_handle(const char *handle, const char *passwd, unsigned long id)
431 struct handle_info *hi;
433 char id_base64[IDLEN + 1];
436 /* Assign a unique account ID to the account; note that 0 is
437 an invalid account ID. 1 is therefore the first account ID. */
439 id = 1 + highest_id++;
441 /* Note: highest_id is and must always be the highest ID. */
442 if (id > highest_id) {
446 inttobase64(id_base64, id, IDLEN);
448 /* Make sure an account with the same ID doesn't exist. If a
449 duplicate is found, log some details and assign a new one.
450 This should be impossible, but it never hurts to expect it. */
451 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
452 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
457 hi = calloc(1, sizeof(*hi));
458 hi->userlist_style = HI_DEFAULT_STYLE;
459 hi->handle = strdup(handle);
460 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
462 dict_insert(nickserv_handle_dict, hi->handle, hi);
465 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
471 register_nick(const char *nick, struct handle_info *owner)
473 struct nick_info *ni;
474 ni = malloc(sizeof(struct nick_info));
475 safestrncpy(ni->nick, nick, sizeof(ni->nick));
477 ni->next = owner->nicks;
479 dict_insert(nickserv_nick_dict, ni->nick, ni);
483 delete_nick(struct nick_info *ni)
485 struct nick_info *last, *next;
486 struct userNode *user;
487 /* Check to see if we should mark a user as unregistered. */
488 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
489 user->modes &= ~FLAGS_REGNICK;
492 /* Remove ni from the nick_info linked list. */
493 if (ni == ni->owner->nicks) {
494 ni->owner->nicks = ni->next;
496 last = ni->owner->nicks;
502 last->next = next->next;
504 dict_remove(nickserv_nick_dict, ni->nick);
507 static unreg_func_t *unreg_func_list;
508 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
511 reg_unreg_func(unreg_func_t func)
513 if (unreg_func_used == unreg_func_size) {
514 if (unreg_func_size) {
515 unreg_func_size <<= 1;
516 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
519 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
522 unreg_func_list[unreg_func_used++] = func;
526 nickserv_free_cookie(void *data)
528 struct handle_cookie *cookie = data;
529 if (cookie->hi) cookie->hi->cookie = NULL;
530 if (cookie->data) free(cookie->data);
535 free_handle_info(void *vhi)
537 struct handle_info *hi = vhi;
540 inttobase64(id, hi->id, IDLEN);
541 dict_remove(nickserv_id_dict, id);
543 free_string_list(hi->masks);
547 delete_nick(hi->nicks);
553 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
554 nickserv_free_cookie(hi->cookie);
557 struct handle_note *note = hi->notes;
558 hi->notes = note->next;
561 if (hi->email_addr) {
562 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
563 handle_info_list_remove(hil, hi);
565 dict_remove(nickserv_email_dict, hi->email_addr);
570 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
573 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
577 for (n=0; n<unreg_func_used; n++)
578 unreg_func_list[n](notify, hi);
580 set_user_handle_info(hi->users, NULL, 0);
582 if (nickserv_conf.disable_nicks)
583 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
585 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
587 dict_remove(nickserv_handle_dict, hi->handle);
591 get_handle_info(const char *handle)
593 return dict_find(nickserv_handle_dict, handle, 0);
597 get_nick_info(const char *nick)
599 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
603 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
608 for (nn=0; nn<channel->members.used; ++nn) {
609 mn = channel->members.list[nn];
610 if ((mn->user != except) && (mn->user->handle_info == handle))
617 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
618 if (!user->handle_info) {
620 send_message(user, bot, "MSG_AUTHENTICATE");
624 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
626 send_message(user, bot, "NSMSG_NO_ACCESS");
630 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
632 send_message(user, bot, "MSG_OPER_SUSPENDED");
636 if (user->handle_info->opserv_level < min_level) {
638 send_message(user, bot, "NSMSG_NO_ACCESS");
646 is_valid_handle(const char *handle)
648 struct userNode *user;
649 /* cant register a juped nick/service nick as handle, to prevent confusion */
650 user = GetUserH(handle);
651 if (user && IsLocal(user))
653 /* check against maximum length */
654 if (strlen(handle) > NICKSERV_HANDLE_LEN)
656 /* for consistency, only allow account names that could be nicks */
657 if (!is_valid_nick(handle))
659 /* disallow account names that look like bad words */
660 if (opserv_bad_channel(handle))
662 /* test either regex or containing all valid chars */
663 if (nickserv_conf.valid_handle_regex_set) {
664 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
667 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
668 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
672 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
677 is_registerable_nick(const char *nick)
679 /* make sure it could be used as an account name */
680 if (!is_valid_handle(nick))
683 if (strlen(nick) > NICKLEN)
685 /* test either regex or as valid handle */
686 if (nickserv_conf.valid_nick_regex_set) {
687 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
690 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
691 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
699 is_valid_email_addr(const char *email)
701 return strchr(email, '@') != NULL;
705 visible_email_addr(struct userNode *user, struct handle_info *hi)
707 if (hi->email_addr) {
708 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
709 return hi->email_addr;
719 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
721 struct handle_info *hi;
722 struct userNode *target;
726 if (!(hi = get_handle_info(++name))) {
727 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
732 if (!(target = GetUserH(name))) {
733 send_message(user, service, "MSG_NICK_UNKNOWN", name);
736 if (IsLocal(target)) {
737 if (IsService(target))
738 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
740 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
743 if (!(hi = target->handle_info)) {
744 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
752 oper_outranks(struct userNode *user, struct handle_info *hi) {
753 if (user->handle_info->opserv_level > hi->opserv_level)
755 if (user->handle_info->opserv_level == hi->opserv_level) {
756 if ((user->handle_info->opserv_level == 1000)
757 || (user->handle_info == hi)
758 || ((user->handle_info->opserv_level == 0)
759 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
760 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
764 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
768 static struct handle_info *
769 get_victim_oper(struct userNode *user, const char *target)
771 struct handle_info *hi;
772 if (!(hi = smart_get_handle_info(nickserv, user, target)))
774 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
775 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
778 return oper_outranks(user, hi) ? hi : NULL;
782 valid_user_for(struct userNode *user, struct handle_info *hi)
786 /* If no hostmasks on the account, allow it. */
787 if (!hi->masks->used)
789 /* If any hostmask matches, allow it. */
790 for (ii=0; ii<hi->masks->used; ii++)
791 if (user_matches_glob(user, hi->masks->list[ii], 0))
793 /* If they are allowauthed to this account, allow it (removing the aa). */
794 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
795 dict_remove(nickserv_allow_auth_dict, user->nick);
798 /* The user is not allowed to use this account. */
803 is_secure_password(const char *handle, const char *pass, struct userNode *user)
806 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
810 if (len < nickserv_conf.password_min_length) {
812 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
815 if (!irccasecmp(pass, handle)) {
817 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
820 dict_find(nickserv_conf.weak_password_dict, pass, &p);
823 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
826 for (i=0; i<len; i++) {
827 if (isdigit(pass[i]))
829 if (isupper(pass[i]))
831 if (islower(pass[i]))
834 if ((cnt_lower < nickserv_conf.password_min_lower)
835 || (cnt_upper < nickserv_conf.password_min_upper)
836 || (cnt_digits < nickserv_conf.password_min_digits)) {
838 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
844 static auth_func_t *auth_func_list;
845 static unsigned int auth_func_size = 0, auth_func_used = 0;
848 reg_auth_func(auth_func_t func)
850 if (auth_func_used == auth_func_size) {
851 if (auth_func_size) {
852 auth_func_size <<= 1;
853 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
856 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
859 auth_func_list[auth_func_used++] = func;
862 static handle_rename_func_t *rf_list;
863 static unsigned int rf_list_size, rf_list_used;
866 reg_handle_rename_func(handle_rename_func_t func)
868 if (rf_list_used == rf_list_size) {
871 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
874 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
877 rf_list[rf_list_used++] = func;
881 generate_fakehost(struct handle_info *handle)
883 extern const char *hidden_host_suffix;
884 static char buffer[HOSTLEN+1];
886 if (!handle->fakehost) {
887 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
889 } else if (handle->fakehost[0] == '.') {
890 /* A leading dot indicates the stored value is actually a title. */
891 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, nickserv_conf.titlehost_suffix);
894 return handle->fakehost;
898 generate_fakeident(struct handle_info *handle, struct userNode *user)
900 static char buffer[USERLEN+1];
902 if (!handle->fakeident) {
905 safestrncpy(buffer, user->ident, sizeof(buffer));
908 return handle->fakeident;
912 apply_fakehost(struct handle_info *handle, struct userNode *user)
914 struct userNode *target;
915 char *fakehost, *fakeident;
920 fakehost = generate_fakehost(handle);
923 fakeident = generate_fakeident(handle, user);
924 assign_fakehost(user, fakehost, fakeident, 0, 1);
928 for (target = handle->users; target; target = target->next_authed) {
929 fakeident = generate_fakeident(handle, target);
930 assign_fakehost(target, fakehost, fakeident, 0, 1);
935 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
938 struct handle_info *old_info;
940 /* This can happen if somebody uses COOKIE while authed, or if
941 * they re-auth to their current handle (which is silly, but users
943 if (user->handle_info == hi)
946 if (user->handle_info) {
947 struct userNode *other;
950 userList_remove(&curr_helpers, user);
952 /* remove from next_authed linked list */
953 if (user->handle_info->users == user) {
954 user->handle_info->users = user->next_authed;
955 } else if (user->handle_info->users != NULL) {
956 for (other = user->handle_info->users;
957 other->next_authed != user;
958 other = other->next_authed) ;
959 other->next_authed = user->next_authed;
961 /* No users authed to the account - can happen if they get
962 * killed for authing. */
964 /* if nobody left on old handle, and they're not an oper, remove !god */
965 if (!user->handle_info->users && !user->handle_info->opserv_level)
966 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
967 /* record them as being last seen at this time */
968 user->handle_info->lastseen = now;
969 /* and record their hostmask */
970 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
972 old_info = user->handle_info;
973 user->handle_info = hi;
974 if (hi && !hi->users && !hi->opserv_level)
975 HANDLE_CLEAR_FLAG(hi, HELPING);
976 for (n=0; n<auth_func_used; n++) {
977 auth_func_list[n](user, old_info);
982 struct nick_info *ni;
984 HANDLE_CLEAR_FLAG(hi, FROZEN);
985 if (nickserv_conf.warn_clone_auth) {
986 struct userNode *other;
987 for (other = hi->users; other; other = other->next_authed)
988 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
990 user->next_authed = hi->users;
993 if (IsHelper(user) && !userList_contains(&curr_helpers, user))
994 userList_append(&curr_helpers, user);
996 if (hi->fakehost || hi->fakeident || old_info)
997 apply_fakehost(hi, user);
1000 if (!nickserv_conf.disable_nicks) {
1001 struct nick_info *ni2;
1002 for (ni2 = hi->nicks; ni2; ni2 = ni2->next) {
1003 if (!irccasecmp(user->nick, ni2->nick)) {
1004 user->modes |= FLAGS_REGNICK;
1009 StampUser(user, hi->handle, hi->registered, hi->id);
1012 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
1013 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
1015 /* We cannot clear the user's account ID, unfortunately. */
1016 user->next_authed = NULL;
1020 static struct handle_info*
1021 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
1023 struct handle_info *hi;
1024 struct nick_info *ni;
1025 char crypted[MD5_CRYPT_LENGTH];
1027 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
1028 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
1032 if (!is_secure_password(handle, passwd, user))
1035 cryptpass(passwd, crypted);
1036 hi = register_handle(handle, crypted, 0);
1037 hi->masks = alloc_string_list(1);
1039 hi->language = lang_C;
1040 hi->registered = now;
1042 hi->flags = HI_DEFAULT_FLAGS;
1043 if (settee && !no_auth)
1044 set_user_handle_info(settee, hi, 1);
1047 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
1048 else if (nickserv_conf.disable_nicks)
1049 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
1050 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
1051 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
1053 register_nick(user->nick, hi);
1054 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
1056 if (settee && (user != settee))
1057 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
1062 nickserv_bake_cookie(struct handle_cookie *cookie)
1064 cookie->hi->cookie = cookie;
1065 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
1069 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
1071 struct handle_cookie *cookie;
1072 char subject[128], body[4096], *misc;
1073 const char *netname, *fmt;
1077 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1081 cookie = calloc(1, sizeof(*cookie));
1083 cookie->type = type;
1084 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1085 cookie->expires = now + nickserv_conf.cookie_timeout;
1086 inttobase64(cookie->cookie, rand(), 5);
1087 inttobase64(cookie->cookie+5, rand(), 5);
1089 netname = nickserv_conf.network_name;
1092 switch (cookie->type) {
1094 hi->passwd[0] = 0; /* invalidate password */
1095 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1096 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1097 snprintf(subject, sizeof(subject), fmt, netname);
1098 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1099 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1102 case PASSWORD_CHANGE:
1103 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1104 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1105 snprintf(subject, sizeof(subject), fmt, netname);
1106 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1107 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1110 misc = hi->email_addr;
1111 hi->email_addr = cookie->data;
1113 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1114 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1115 snprintf(subject, sizeof(subject), fmt, netname);
1116 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1117 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1118 mail_send(nickserv, hi, subject, body, 1);
1119 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1120 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1122 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1123 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1124 snprintf(subject, sizeof(subject), fmt, netname);
1125 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1126 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1127 mail_send(nickserv, hi, subject, body, 1);
1130 hi->email_addr = misc;
1133 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1134 snprintf(subject, sizeof(subject), fmt, netname);
1135 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1136 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1137 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1140 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1144 mail_send(nickserv, hi, subject, body, first_time);
1145 nickserv_bake_cookie(cookie);
1149 nickserv_eat_cookie(struct handle_cookie *cookie)
1151 cookie->hi->cookie = NULL;
1152 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1153 nickserv_free_cookie(cookie);
1157 nickserv_free_email_addr(void *data)
1159 handle_info_list_clean(data);
1164 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1166 struct handle_info_list *hil;
1167 /* Remove from old handle_info_list ... */
1168 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1169 handle_info_list_remove(hil, hi);
1170 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1171 hi->email_addr = NULL;
1173 /* Add to the new list.. */
1174 if (new_email_addr) {
1175 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1176 hil = calloc(1, sizeof(*hil));
1177 hil->tag = strdup(new_email_addr);
1178 handle_info_list_init(hil);
1179 dict_insert(nickserv_email_dict, hil->tag, hil);
1181 handle_info_list_append(hil, hi);
1182 hi->email_addr = hil->tag;
1186 static NICKSERV_FUNC(cmd_register)
1189 struct handle_info *hi;
1190 const char *email_addr, *password;
1193 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1194 /* Require the first handle registered to belong to someone +o. */
1195 reply("NSMSG_REQUIRE_OPER");
1199 if (user->handle_info) {
1200 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1204 if (IsRegistering(user)) {
1205 reply("NSMSG_ALREADY_REGISTERING");
1209 if (IsStamped(user)) {
1210 /* Unauthenticated users might still have been stamped
1211 previously and could therefore have a hidden host;
1212 do not allow them to register a new account. */
1213 reply("NSMSG_STAMPED_REGISTER");
1217 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1219 if (!is_valid_handle(argv[1])) {
1220 reply("NSMSG_BAD_HANDLE", argv[1]);
1224 if ((argc >= 4) && nickserv_conf.email_enabled) {
1225 struct handle_info_list *hil;
1228 /* Remember email address. */
1229 email_addr = argv[3];
1231 /* Check that the email address looks valid.. */
1232 if (!is_valid_email_addr(email_addr)) {
1233 reply("NSMSG_BAD_EMAIL_ADDR");
1237 /* .. and that we are allowed to send to it. */
1238 if ((str = mail_prohibited_address(email_addr))) {
1239 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1243 /* If we do email verify, make sure we don't spam the address. */
1244 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1246 for (nn=0; nn<hil->used; nn++) {
1247 if (hil->list[nn]->cookie) {
1248 reply("NSMSG_EMAIL_UNACTIVATED");
1252 if (hil->used >= nickserv_conf.handles_per_email) {
1253 reply("NSMSG_EMAIL_OVERUSED");
1266 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1268 /* Add any masks they should get. */
1269 if (nickserv_conf.default_hostmask) {
1270 string_list_append(hi->masks, strdup("*@*"));
1272 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1273 if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
1274 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1277 /* If they're the first to register, give them level 1000. */
1278 if (dict_size(nickserv_handle_dict) == 1) {
1279 hi->opserv_level = 1000;
1280 reply("NSMSG_ROOT_HANDLE", argv[1]);
1283 /* Set their email address. */
1285 nickserv_set_email_addr(hi, email_addr);
1287 /* If they need to do email verification, tell them. */
1289 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1291 /* Set registering flag.. */
1292 user->modes |= FLAGS_REGISTERING;
1297 static NICKSERV_FUNC(cmd_oregister)
1300 struct userNode *settee;
1301 struct handle_info *hi;
1303 NICKSERV_MIN_PARMS(3);
1305 if (!is_valid_handle(argv[1])) {
1306 reply("NSMSG_BAD_HANDLE", argv[1]);
1313 } else if (strchr(argv[3], '@')) {
1314 mask = canonicalize_hostmask(strdup(argv[3]));
1316 settee = GetUserH(argv[4]);
1318 reply("MSG_NICK_UNKNOWN", argv[4]);
1325 } else if ((settee = GetUserH(argv[3]))) {
1326 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1328 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1331 if (settee && settee->handle_info) {
1332 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1336 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1341 string_list_append(hi->masks, mask);
1345 static NICKSERV_FUNC(cmd_handleinfo)
1348 unsigned int i, pos=0, herelen;
1349 struct userNode *target, *next_un;
1350 struct handle_info *hi;
1351 const char *nsmsg_none;
1355 if (!(hi = user->handle_info)) {
1356 reply("NSMSG_MUST_AUTH");
1359 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1363 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1364 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1365 feh = hi->registered;
1366 reply("NSMSG_HANDLEINFO_REGGED", ctime(&feh));
1369 intervalString(buff, now - hi->lastseen, user->handle_info);
1370 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1372 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1375 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1376 if (HANDLE_FLAGGED(hi, FROZEN))
1377 reply("NSMSG_HANDLEINFO_VACATION");
1379 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1380 struct do_not_register *dnr;
1381 if ((dnr = chanserv_is_dnr(NULL, hi)))
1382 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1383 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
1385 } else if (hi != user->handle_info)
1389 reply("NSMSG_HANDLEINFO_KARMA", hi->karma);
1391 if (nickserv_conf.email_enabled)
1392 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1396 switch (hi->cookie->type) {
1397 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1398 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1399 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1400 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1401 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1406 if (oper_has_access(user, cmd->parent->bot, 601, 1))
1407 reply("NSMSG_HANDLEINFO_ID", hi->id);
1409 if (oper_has_access(user, cmd->parent->bot, 0, 1) || IsStaff(user)) {
1411 reply("NSMSG_HANDLEINFO_NO_NOTES");
1413 struct handle_note *prev, *note;
1415 WALK_NOTES(hi, prev, note) {
1416 char set_time[INTERVALLEN];
1417 intervalString(set_time, now - note->set, user->handle_info);
1418 if (note->expires) {
1419 char exp_time[INTERVALLEN];
1420 intervalString(exp_time, note->expires - now, user->handle_info);
1421 reply("NSMSG_HANDLEINFO_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1423 reply("NSMSG_HANDLEINFO_NOTE", note->id, set_time, note->setter, note->note);
1430 unsigned long flen = 1;
1431 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1433 for (i=0, flen=1; handle_flags[i]; i++)
1434 if (hi->flags & 1 << i)
1435 flags[flen++] = handle_flags[i];
1437 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1439 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1442 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1443 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1444 || (hi->opserv_level > 0)) {
1445 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1448 if (hi->fakeident && hi->fakehost)
1449 reply("NSMSG_HANDLEINFO_FAKEIDENTHOST", hi->fakeident, hi->fakehost);
1450 else if (hi->fakeident)
1451 reply("NSMSG_HANDLEINFO_FAKEIDENT", hi->fakeident);
1452 else if (hi->fakehost)
1453 reply("NSMSG_HANDLEINFO_FAKEHOST", hi->fakehost);
1455 if (hi->last_quit_host[0])
1456 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1458 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1460 if (nickserv_conf.disable_nicks) {
1461 /* nicks disabled; don't show anything about registered nicks */
1462 } else if (hi->nicks) {
1463 struct nick_info *ni, *next_ni;
1464 for (ni = hi->nicks; ni; ni = next_ni) {
1465 herelen = strlen(ni->nick);
1466 if (pos + herelen + 1 > ArrayLength(buff)) {
1468 goto print_nicks_buff;
1472 memcpy(buff+pos, ni->nick, herelen);
1473 pos += herelen; buff[pos++] = ' ';
1477 reply("NSMSG_HANDLEINFO_NICKS", buff);
1482 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1485 if (hi->masks->used) {
1486 for (i=0; i < hi->masks->used; i++) {
1487 herelen = strlen(hi->masks->list[i]);
1488 if (pos + herelen + 1 > ArrayLength(buff)) {
1490 goto print_mask_buff;
1492 memcpy(buff+pos, hi->masks->list[i], herelen);
1493 pos += herelen; buff[pos++] = ' ';
1494 if (i+1 == hi->masks->used) {
1497 reply("NSMSG_HANDLEINFO_MASKS", buff);
1502 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1506 struct userData *chan, *next;
1509 for (chan = hi->channels; chan; chan = next) {
1510 next = chan->u_next;
1511 name = chan->channel->channel->name;
1512 herelen = strlen(name);
1513 if (pos + herelen + 7 > ArrayLength(buff)) {
1515 goto print_chans_buff;
1517 if (IsUserSuspended(chan))
1519 pos += sprintf(buff+pos, "%d:%s ", chan->access, name);
1523 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1528 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1531 for (target = hi->users; target; target = next_un) {
1532 herelen = strlen(target->nick);
1533 if (pos + herelen + 1 > ArrayLength(buff)) {
1535 goto print_cnick_buff;
1537 next_un = target->next_authed;
1539 memcpy(buff+pos, target->nick, herelen);
1540 pos += herelen; buff[pos++] = ' ';
1544 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1549 return 1 | ((hi != user->handle_info) ? CMD_LOG_STAFF : 0);
1552 static NICKSERV_FUNC(cmd_userinfo)
1554 struct userNode *target;
1556 NICKSERV_MIN_PARMS(2);
1557 if (!(target = GetUserH(argv[1]))) {
1558 reply("MSG_NICK_UNKNOWN", argv[1]);
1561 if (target->handle_info)
1562 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1564 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1568 static NICKSERV_FUNC(cmd_nickinfo)
1570 struct nick_info *ni;
1572 NICKSERV_MIN_PARMS(2);
1573 if (!(ni = get_nick_info(argv[1]))) {
1574 reply("MSG_NICK_UNKNOWN", argv[1]);
1577 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1581 static NICKSERV_FUNC(cmd_notes)
1583 struct handle_info *hi;
1584 struct handle_note *prev, *note;
1587 NICKSERV_MIN_PARMS(2);
1588 if (!(hi = get_victim_oper(user, argv[1])))
1591 WALK_NOTES(hi, prev, note) {
1592 char set_time[INTERVALLEN];
1593 intervalString(set_time, now - note->set, user->handle_info);
1594 if (note->expires) {
1595 char exp_time[INTERVALLEN];
1596 intervalString(exp_time, note->expires - now, user->handle_info);
1597 reply("NSMSG_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1599 reply("NSMSG_NOTE", note->id, set_time, note->setter, note->note);
1603 reply("NSMSG_NOTE_COUNT", hits, argv[1]);
1607 static NICKSERV_FUNC(cmd_rename_handle)
1609 struct handle_info *hi;
1610 char msgbuf[MAXLEN], *old_handle;
1613 NICKSERV_MIN_PARMS(3);
1614 if (!(hi = get_victim_oper(user, argv[1])))
1616 if (!is_valid_handle(argv[2])) {
1617 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1620 if (get_handle_info(argv[2])) {
1621 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1624 if (hi->fakehost && hi->fakehost[0] == '.' &&
1625 (strlen(argv[2]) + strlen(hi->fakehost+1) +
1626 strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) {
1627 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED_RENAME");
1631 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1632 hi->handle = strdup(argv[2]);
1633 dict_insert(nickserv_handle_dict, hi->handle, hi);
1634 for (nn=0; nn<rf_list_used; nn++)
1635 rf_list[nn](hi, old_handle);
1636 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1637 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1638 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1643 static failpw_func_t *failpw_func_list;
1644 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1647 reg_failpw_func(failpw_func_t func)
1649 if (failpw_func_used == failpw_func_size) {
1650 if (failpw_func_size) {
1651 failpw_func_size <<= 1;
1652 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1654 failpw_func_size = 8;
1655 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1658 failpw_func_list[failpw_func_used++] = func;
1661 static NICKSERV_FUNC(cmd_auth)
1663 int pw_arg, used, maxlogins;
1664 struct handle_info *hi;
1666 struct userNode *other;
1668 if (user->handle_info) {
1669 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1672 if (IsStamped(user)) {
1673 /* Unauthenticated users might still have been stamped
1674 previously and could therefore have a hidden host;
1675 do not allow them to authenticate. */
1676 reply("NSMSG_STAMPED_AUTH");
1680 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1682 } else if (argc == 2) {
1683 if (nickserv_conf.disable_nicks) {
1684 if (!(hi = get_handle_info(user->nick))) {
1685 reply("NSMSG_HANDLE_NOT_FOUND");
1689 /* try to look up their handle from their nick */
1690 struct nick_info *ni;
1691 ni = get_nick_info(user->nick);
1693 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1700 reply("MSG_MISSING_PARAMS", argv[0]);
1701 svccmd_send_help(user, nickserv, cmd);
1705 reply("NSMSG_HANDLE_NOT_FOUND");
1708 /* Responses from here on look up the language used by the handle they asked about. */
1709 passwd = argv[pw_arg];
1710 if (!valid_user_for(user, hi)) {
1711 if (hi->email_addr && nickserv_conf.email_enabled)
1712 send_message_type(4, user, cmd->parent->bot,
1713 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1716 send_message_type(4, user, cmd->parent->bot,
1717 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1719 argv[pw_arg] = "BADMASK";
1722 if (!checkpass(passwd, hi->passwd)) {
1724 send_message_type(4, user, cmd->parent->bot,
1725 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1726 argv[pw_arg] = "BADPASS";
1727 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1728 if (nickserv_conf.autogag_enabled) {
1729 if (!user->auth_policer.params) {
1730 user->auth_policer.last_req = now;
1731 user->auth_policer.params = nickserv_conf.auth_policer_params;
1733 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1735 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1736 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1737 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1739 argv[pw_arg] = "GAGGED";
1744 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1745 send_message_type(4, user, cmd->parent->bot,
1746 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1747 argv[pw_arg] = "SUSPENDED";
1750 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1751 for (used = 0, other = hi->users; other; other = other->next_authed) {
1752 if (++used >= maxlogins) {
1753 send_message_type(4, user, cmd->parent->bot,
1754 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1756 argv[pw_arg] = "MAXLOGINS";
1761 set_user_handle_info(user, hi, 1);
1762 if (nickserv_conf.email_required && !hi->email_addr)
1763 reply("NSMSG_PLEASE_SET_EMAIL");
1764 if (!is_secure_password(hi->handle, passwd, NULL))
1765 reply("NSMSG_WEAK_PASSWORD");
1766 if (hi->passwd[0] != '$')
1767 cryptpass(passwd, hi->passwd);
1768 if (!hi->masks->used) {
1770 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1771 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
1772 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1774 argv[pw_arg] = "****";
1775 reply("NSMSG_AUTH_SUCCESS");
1779 static allowauth_func_t *allowauth_func_list;
1780 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1783 reg_allowauth_func(allowauth_func_t func)
1785 if (allowauth_func_used == allowauth_func_size) {
1786 if (allowauth_func_size) {
1787 allowauth_func_size <<= 1;
1788 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1790 allowauth_func_size = 8;
1791 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1794 allowauth_func_list[allowauth_func_used++] = func;
1797 static NICKSERV_FUNC(cmd_allowauth)
1799 struct userNode *target;
1800 struct handle_info *hi;
1803 NICKSERV_MIN_PARMS(2);
1804 if (!(target = GetUserH(argv[1]))) {
1805 reply("MSG_NICK_UNKNOWN", argv[1]);
1808 if (target->handle_info) {
1809 reply("NSMSG_USER_PREV_AUTH", target->nick);
1812 if (IsStamped(target)) {
1813 /* Unauthenticated users might still have been stamped
1814 previously and could therefore have a hidden host;
1815 do not allow them to authenticate to an account. */
1816 reply("NSMSG_USER_PREV_STAMP", target->nick);
1821 else if (!(hi = get_handle_info(argv[2]))) {
1822 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1826 if (hi->opserv_level > user->handle_info->opserv_level) {
1827 reply("MSG_USER_OUTRANKED", hi->handle);
1830 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1831 || (hi->opserv_level > 0))
1832 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1833 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1836 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1837 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1838 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1839 if (nickserv_conf.email_enabled)
1840 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1842 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1843 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1845 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1847 for (n=0; n<allowauth_func_used; n++)
1848 allowauth_func_list[n](user, target, hi);
1852 static NICKSERV_FUNC(cmd_authcookie)
1854 struct handle_info *hi;
1856 NICKSERV_MIN_PARMS(2);
1857 if (user->handle_info) {
1858 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1861 if (IsStamped(user)) {
1862 /* Unauthenticated users might still have been stamped
1863 previously and could therefore have a hidden host;
1864 do not allow them to authenticate to an account. */
1865 reply("NSMSG_STAMPED_AUTHCOOKIE");
1868 if (!(hi = get_handle_info(argv[1]))) {
1869 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1872 if (!hi->email_addr) {
1873 reply("MSG_SET_EMAIL_ADDR");
1876 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1880 static NICKSERV_FUNC(cmd_delcookie)
1882 struct handle_info *hi;
1884 hi = user->handle_info;
1886 reply("NSMSG_NO_COOKIE");
1889 switch (hi->cookie->type) {
1892 reply("NSMSG_MUST_TIME_OUT");
1895 nickserv_eat_cookie(hi->cookie);
1896 reply("NSMSG_ATE_COOKIE");
1902 static NICKSERV_FUNC(cmd_odelcookie)
1904 struct handle_info *hi;
1906 NICKSERV_MIN_PARMS(2);
1908 if (!(hi = get_victim_oper(user, argv[1])))
1912 reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle);
1916 nickserv_eat_cookie(hi->cookie);
1917 reply("NSMSG_ATE_COOKIE_FOREIGN", hi->handle);
1922 static NICKSERV_FUNC(cmd_resetpass)
1924 struct handle_info *hi;
1925 char crypted[MD5_CRYPT_LENGTH];
1927 NICKSERV_MIN_PARMS(3);
1928 if (user->handle_info) {
1929 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1932 if (IsStamped(user)) {
1933 /* Unauthenticated users might still have been stamped
1934 previously and could therefore have a hidden host;
1935 do not allow them to activate an account. */
1936 reply("NSMSG_STAMPED_RESETPASS");
1939 if (!(hi = get_handle_info(argv[1]))) {
1940 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1943 if (!hi->email_addr) {
1944 reply("MSG_SET_EMAIL_ADDR");
1947 cryptpass(argv[2], crypted);
1949 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1953 static NICKSERV_FUNC(cmd_cookie)
1955 struct handle_info *hi;
1958 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1961 NICKSERV_MIN_PARMS(3);
1962 if (!(hi = get_handle_info(argv[1]))) {
1963 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1969 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1970 reply("NSMSG_HANDLE_SUSPENDED");
1975 reply("NSMSG_NO_COOKIE");
1979 /* Check validity of operation before comparing cookie to
1980 * prohibit guessing by authed users. */
1981 if (user->handle_info
1982 && (hi->cookie->type != EMAIL_CHANGE)
1983 && (hi->cookie->type != PASSWORD_CHANGE)) {
1984 reply("NSMSG_CANNOT_COOKIE");
1988 if (strcmp(cookie, hi->cookie->cookie)) {
1989 reply("NSMSG_BAD_COOKIE");
1993 switch (hi->cookie->type) {
1995 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1996 set_user_handle_info(user, hi, 1);
1997 reply("NSMSG_HANDLE_ACTIVATED");
1999 case PASSWORD_CHANGE:
2000 set_user_handle_info(user, hi, 1);
2001 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2002 reply("NSMSG_PASSWORD_CHANGED");
2005 nickserv_set_email_addr(hi, hi->cookie->data);
2006 reply("NSMSG_EMAIL_CHANGED");
2009 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2010 set_user_handle_info(user, hi, 1);
2011 nickserv_addmask(user, hi, mask);
2012 reply("NSMSG_AUTH_SUCCESS");
2017 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
2018 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
2022 nickserv_eat_cookie(hi->cookie);
2027 static NICKSERV_FUNC(cmd_oregnick) {
2029 struct handle_info *target;
2030 struct nick_info *ni;
2032 NICKSERV_MIN_PARMS(3);
2033 if (!(target = modcmd_get_handle_info(user, argv[1])))
2036 if (!is_registerable_nick(nick)) {
2037 reply("NSMSG_BAD_NICK", nick);
2040 ni = dict_find(nickserv_nick_dict, nick, NULL);
2042 reply("NSMSG_NICK_EXISTS", nick);
2045 register_nick(nick, target);
2046 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
2050 static NICKSERV_FUNC(cmd_regnick) {
2052 struct nick_info *ni;
2054 if (!is_registerable_nick(user->nick)) {
2055 reply("NSMSG_BAD_NICK", user->nick);
2058 /* count their nicks, see if it's too many */
2059 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
2060 if (n >= nickserv_conf.nicks_per_handle) {
2061 reply("NSMSG_TOO_MANY_NICKS");
2064 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
2066 reply("NSMSG_NICK_EXISTS", user->nick);
2069 register_nick(user->nick, user->handle_info);
2070 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2074 static NICKSERV_FUNC(cmd_pass)
2076 struct handle_info *hi;
2077 const char *old_pass, *new_pass;
2079 NICKSERV_MIN_PARMS(3);
2080 hi = user->handle_info;
2084 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
2085 if (!checkpass(old_pass, hi->passwd)) {
2086 argv[1] = "BADPASS";
2087 reply("NSMSG_PASSWORD_INVALID");
2090 cryptpass(new_pass, hi->passwd);
2092 reply("NSMSG_PASS_SUCCESS");
2097 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
2100 char *new_mask = canonicalize_hostmask(strdup(mask));
2101 for (i=0; i<hi->masks->used; i++) {
2102 if (!irccasecmp(new_mask, hi->masks->list[i])) {
2103 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2108 string_list_append(hi->masks, new_mask);
2109 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2113 static NICKSERV_FUNC(cmd_addmask)
2116 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2117 int res = nickserv_addmask(user, user->handle_info, mask);
2121 if (!is_gline(argv[1])) {
2122 reply("NSMSG_MASK_INVALID", argv[1]);
2125 return nickserv_addmask(user, user->handle_info, argv[1]);
2129 static NICKSERV_FUNC(cmd_oaddmask)
2131 struct handle_info *hi;
2133 NICKSERV_MIN_PARMS(3);
2134 if (!(hi = get_victim_oper(user, argv[1])))
2136 return nickserv_addmask(user, hi, argv[2]);
2140 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2143 for (i=0; i<hi->masks->used; i++) {
2144 if (!strcmp(del_mask, hi->masks->list[i])) {
2145 char *old_mask = hi->masks->list[i];
2146 if (hi->masks->used == 1 && !force) {
2147 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2150 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2151 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2156 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2160 static NICKSERV_FUNC(cmd_delmask)
2162 NICKSERV_MIN_PARMS(2);
2163 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2166 static NICKSERV_FUNC(cmd_odelmask)
2168 struct handle_info *hi;
2169 NICKSERV_MIN_PARMS(3);
2170 if (!(hi = get_victim_oper(user, argv[1])))
2172 return nickserv_delmask(user, hi, argv[2], 1);
2176 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2177 unsigned int nn, add = 1, pos;
2178 unsigned long added, removed, flag;
2180 for (added=removed=nn=0; str[nn]; nn++) {
2182 case '+': add = 1; break;
2183 case '-': add = 0; break;
2185 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2186 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2189 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2190 /* cheesy avoidance of looking up the flag name.. */
2191 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2194 flag = 1 << (pos - 1);
2196 added |= flag, removed &= ~flag;
2198 removed |= flag, added &= ~flag;
2203 *premoved = removed;
2208 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2210 unsigned long before, after, added, removed;
2211 struct userNode *uNode;
2213 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2214 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2216 hi->flags = (hi->flags | added) & ~removed;
2217 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2219 /* Strip helping flag if they're only a support helper and not
2220 * currently in #support. */
2221 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2222 struct channelList *schannels;
2224 schannels = chanserv_support_channels();
2225 for (ii = 0; ii < schannels->used; ++ii)
2226 if (find_handle_in_channel(schannels->list[ii], hi, NULL))
2228 if (ii == schannels->used)
2229 HANDLE_CLEAR_FLAG(hi, HELPING);
2232 if (after && !before) {
2233 /* Add user to current helper list. */
2234 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2235 userList_append(&curr_helpers, uNode);
2236 } else if (!after && before) {
2237 /* Remove user from current helper list. */
2238 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2239 userList_remove(&curr_helpers, uNode);
2246 set_list(struct userNode *user, struct handle_info *hi, int override)
2250 char *set_display[] = {
2251 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2252 "EMAIL", "MAXLOGINS", "LANGUAGE"
2255 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2257 /* Do this so options are presented in a consistent order. */
2258 for (i = 0; i < ArrayLength(set_display); ++i)
2259 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2260 opt(user, hi, override, 0, NULL);
2263 static NICKSERV_FUNC(cmd_set)
2265 struct handle_info *hi;
2268 hi = user->handle_info;
2270 set_list(user, hi, 0);
2273 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2274 reply("NSMSG_INVALID_OPTION", argv[1]);
2277 return opt(user, hi, 0, argc-1, argv+1);
2280 static NICKSERV_FUNC(cmd_oset)
2282 struct handle_info *hi;
2283 struct svccmd *subcmd;
2285 char cmdname[MAXLEN];
2287 NICKSERV_MIN_PARMS(2);
2289 if (!(hi = get_victim_oper(user, argv[1])))
2293 set_list(user, hi, 0);
2297 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2298 reply("NSMSG_INVALID_OPTION", argv[2]);
2302 sprintf(cmdname, "%s %s", cmd->name, argv[2]);
2303 subcmd = dict_find(cmd->parent->commands, cmdname, NULL);
2304 if (subcmd && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, NULL, SVCCMD_NOISY))
2307 return opt(user, hi, 1, argc-2, argv+2);
2310 static OPTION_FUNC(opt_info)
2314 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2316 hi->infoline = NULL;
2318 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2322 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2323 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2327 static OPTION_FUNC(opt_width)
2330 hi->screen_width = strtoul(argv[1], NULL, 0);
2332 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2333 hi->screen_width = MIN_LINE_SIZE;
2334 else if (hi->screen_width > MAX_LINE_SIZE)
2335 hi->screen_width = MAX_LINE_SIZE;
2337 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2341 static OPTION_FUNC(opt_tablewidth)
2344 hi->table_width = strtoul(argv[1], NULL, 0);
2346 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2347 hi->table_width = MIN_LINE_SIZE;
2348 else if (hi->screen_width > MAX_LINE_SIZE)
2349 hi->table_width = MAX_LINE_SIZE;
2351 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2355 static OPTION_FUNC(opt_color)
2358 if (enabled_string(argv[1]))
2359 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2360 else if (disabled_string(argv[1]))
2361 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2363 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2368 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2372 static OPTION_FUNC(opt_privmsg)
2375 if (enabled_string(argv[1]))
2376 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2377 else if (disabled_string(argv[1]))
2378 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2380 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2385 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2389 static OPTION_FUNC(opt_style)
2394 if (!irccasecmp(argv[1], "Zoot"))
2395 hi->userlist_style = HI_STYLE_ZOOT;
2396 else if (!irccasecmp(argv[1], "def"))
2397 hi->userlist_style = HI_STYLE_DEF;
2400 switch (hi->userlist_style) {
2409 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2413 static OPTION_FUNC(opt_password)
2416 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2421 cryptpass(argv[1], hi->passwd);
2423 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2427 static OPTION_FUNC(opt_flags)
2430 unsigned int ii, flen;
2433 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2438 nickserv_apply_flags(user, hi, argv[1]);
2440 for (ii = flen = 0; handle_flags[ii]; ii++)
2441 if (hi->flags & (1 << ii))
2442 flags[flen++] = handle_flags[ii];
2445 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2447 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2451 static OPTION_FUNC(opt_email)
2455 if (!is_valid_email_addr(argv[1])) {
2456 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2459 if ((str = mail_prohibited_address(argv[1]))) {
2460 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2463 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2464 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2466 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2468 nickserv_set_email_addr(hi, argv[1]);
2470 nickserv_eat_cookie(hi->cookie);
2471 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2474 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2478 static OPTION_FUNC(opt_maxlogins)
2480 unsigned char maxlogins;
2482 maxlogins = strtoul(argv[1], NULL, 0);
2483 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2484 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2487 hi->maxlogins = maxlogins;
2489 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2490 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2494 static OPTION_FUNC(opt_language)
2496 struct language *lang;
2498 lang = language_find(argv[1]);
2499 if (irccasecmp(lang->name, argv[1]))
2500 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2501 hi->language = lang;
2503 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2507 static OPTION_FUNC(opt_karma)
2510 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2515 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2516 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2517 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2518 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2520 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2524 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2529 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2530 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2532 if ((user->handle_info->opserv_level < target->opserv_level)
2533 || ((user->handle_info->opserv_level == target->opserv_level)
2534 && (user->handle_info->opserv_level < 1000))) {
2535 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2538 if ((user->handle_info->opserv_level < new_level)
2539 || ((user->handle_info->opserv_level == new_level)
2540 && (user->handle_info->opserv_level < 1000))) {
2541 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2544 if (user->handle_info == target) {
2545 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2548 if (target->opserv_level == new_level)
2550 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2551 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2552 target->opserv_level = new_level;
2556 static OPTION_FUNC(opt_level)
2561 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2565 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2566 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2570 static OPTION_FUNC(opt_epithet)
2573 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2577 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2578 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2581 if ((epithet[0] == '*') && !epithet[1])
2584 hi->epithet = strdup(epithet);
2588 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2590 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2594 static OPTION_FUNC(opt_title)
2599 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2603 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2605 if (strchr(title, '.')) {
2606 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2609 if ((strlen(user->handle_info->handle) + strlen(title) +
2610 strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) {
2611 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2616 if (!strcmp(title, "*")) {
2617 hi->fakehost = NULL;
2619 hi->fakehost = malloc(strlen(title)+2);
2620 hi->fakehost[0] = '.';
2621 strcpy(hi->fakehost+1, title);
2623 apply_fakehost(hi, NULL);
2624 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2625 title = hi->fakehost + 1;
2629 title = user_find_message(user, "MSG_NONE");
2630 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2634 static OPTION_FUNC(opt_fakehost)
2636 char mask[USERLEN + HOSTLEN + 2];
2640 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2644 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2645 if(strlen(argv[1]) >= sizeof(mask)) {
2646 send_message(user, nickserv, "NSMSG_FAKEMASK_INVALID", USERLEN + HOSTLEN + 1);
2650 safestrncpy(mask, argv[1], sizeof(mask));
2652 if ((host = strrchr(mask, '@')) && host != mask) {
2653 // If ident@host was used and the user doesn't have access to set idents, do not change anything.
2654 if (!oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
2666 if (ident && strlen(ident) > USERLEN) {
2667 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
2671 if (host && ((strlen(host) > HOSTLEN) || (host[0] == '.'))) {
2672 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2676 if (host && host[0]) {
2678 if (!strcmp(host, "*"))
2679 hi->fakehost = NULL;
2681 hi->fakehost = strdup(host);
2682 host = hi->fakehost;
2685 host = generate_fakehost(hi);
2688 free(hi->fakeident);
2689 if (!strcmp(ident, "*"))
2690 hi->fakeident = NULL;
2692 hi->fakeident = strdup(ident);
2693 ident = hi->fakeident;
2696 ident = generate_fakeident(hi, NULL);
2698 apply_fakehost(hi, NULL);
2700 host = generate_fakehost(hi);
2701 ident = generate_fakeident(hi, NULL);
2704 host = (char *) user_find_message(user, "MSG_NONE");
2706 send_message(user, nickserv, "NSMSG_SET_FAKEIDENTHOST", ident, host);
2708 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", host);
2712 static OPTION_FUNC(opt_fakeident)
2717 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2721 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
2723 if (strlen(ident) > USERLEN) {
2724 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
2727 free(hi->fakeident);
2728 if (!strcmp(ident, "*"))
2729 hi->fakeident = NULL;
2731 hi->fakeident = strdup(ident);
2732 ident = hi->fakeident;
2733 apply_fakehost(hi, NULL);
2735 ident = generate_fakeident(hi, NULL); /* NULL if no fake ident set */
2737 ident = user_find_message(user, "MSG_NONE");
2738 send_message(user, nickserv, "NSMSG_SET_FAKEIDENT", ident);
2742 static NICKSERV_FUNC(cmd_reclaim)
2744 struct handle_info *hi;
2745 struct nick_info *ni;
2746 struct userNode *victim;
2748 NICKSERV_MIN_PARMS(2);
2749 hi = user->handle_info;
2750 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2752 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2755 if (ni->owner != user->handle_info) {
2756 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2759 victim = GetUserH(ni->nick);
2761 reply("MSG_NICK_UNKNOWN", ni->nick);
2764 if (victim == user) {
2765 reply("NSMSG_NICK_USER_YOU");
2768 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2769 switch (nickserv_conf.reclaim_action) {
2770 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2771 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2772 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2773 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2778 static NICKSERV_FUNC(cmd_unregnick)
2781 struct handle_info *hi;
2782 struct nick_info *ni;
2784 hi = user->handle_info;
2785 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2786 ni = dict_find(nickserv_nick_dict, nick, NULL);
2788 reply("NSMSG_UNKNOWN_NICK", nick);
2791 if (hi != ni->owner) {
2792 reply("NSMSG_NOT_YOUR_NICK", nick);
2795 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2800 static NICKSERV_FUNC(cmd_ounregnick)
2802 struct nick_info *ni;
2804 NICKSERV_MIN_PARMS(2);
2805 if (!(ni = get_nick_info(argv[1]))) {
2806 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2809 if (!oper_outranks(user, ni->owner))
2811 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2816 static NICKSERV_FUNC(cmd_unregister)
2818 struct handle_info *hi;
2821 NICKSERV_MIN_PARMS(2);
2822 hi = user->handle_info;
2825 if (checkpass(passwd, hi->passwd)) {
2826 nickserv_unregister_handle(hi, user);
2829 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2830 reply("NSMSG_PASSWORD_INVALID");
2835 static NICKSERV_FUNC(cmd_ounregister)
2837 struct handle_info *hi;
2838 char reason[MAXLEN];
2841 NICKSERV_MIN_PARMS(2);
2842 if (!(hi = get_victim_oper(user, argv[1])))
2845 if (HANDLE_FLAGGED(hi, NODELETE)) {
2846 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
2850 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
2852 ((hi->flags & nickserv_conf.ounregister_flags)
2854 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
2855 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
2859 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
2860 global_message(MESSAGE_RECIPIENT_STAFF, reason);
2861 nickserv_unregister_handle(hi, user);
2865 static NICKSERV_FUNC(cmd_status)
2867 if (nickserv_conf.disable_nicks) {
2868 reply("NSMSG_GLOBAL_STATS_NONICK",
2869 dict_size(nickserv_handle_dict));
2871 if (user->handle_info) {
2873 struct nick_info *ni;
2874 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2875 reply("NSMSG_HANDLE_STATS", cnt);
2877 reply("NSMSG_HANDLE_NONE");
2879 reply("NSMSG_GLOBAL_STATS",
2880 dict_size(nickserv_handle_dict),
2881 dict_size(nickserv_nick_dict));
2886 static NICKSERV_FUNC(cmd_ghost)
2888 struct userNode *target;
2889 char reason[MAXLEN];
2891 NICKSERV_MIN_PARMS(2);
2892 if (!(target = GetUserH(argv[1]))) {
2893 reply("MSG_NICK_UNKNOWN", argv[1]);
2896 if (target == user) {
2897 reply("NSMSG_CANNOT_GHOST_SELF");
2900 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2901 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2904 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2905 DelUser(target, nickserv, 1, reason);
2906 reply("NSMSG_GHOST_KILLED", argv[1]);
2910 static NICKSERV_FUNC(cmd_vacation)
2912 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2913 reply("NSMSG_ON_VACATION");
2917 static NICKSERV_FUNC(cmd_addnote)
2919 struct handle_info *hi;
2920 unsigned long duration;
2923 struct handle_note *prev;
2924 struct handle_note *note;
2926 /* Parse parameters and figure out values for note's fields. */
2927 NICKSERV_MIN_PARMS(4);
2928 hi = get_victim_oper(user, argv[1]);
2931 if(!strcmp(argv[2], "0"))
2933 else if(!(duration = ParseInterval(argv[2])))
2935 reply("MSG_INVALID_DURATION", argv[2]);
2938 if (duration > 2*365*86400) {
2939 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
2942 unsplit_string(argv + 3, argc - 3, text);
2943 WALK_NOTES(hi, prev, note) {}
2944 id = prev ? (prev->id + 1) : 1;
2946 /* Create the new note structure. */
2947 note = calloc(1, sizeof(*note) + strlen(text));
2949 note->expires = duration ? (now + duration) : 0;
2952 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
2953 strcpy(note->note, text);
2958 reply("NSMSG_NOTE_ADDED", id, hi->handle);
2962 static NICKSERV_FUNC(cmd_delnote)
2964 struct handle_info *hi;
2965 struct handle_note *prev;
2966 struct handle_note *note;
2969 NICKSERV_MIN_PARMS(3);
2970 hi = get_victim_oper(user, argv[1]);
2973 id = strtoul(argv[2], NULL, 10);
2974 WALK_NOTES(hi, prev, note) {
2975 if (id == note->id) {
2977 prev->next = note->next;
2979 hi->notes = note->next;
2981 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
2985 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
2990 nickserv_saxdb_write(struct saxdb_context *ctx) {
2992 struct handle_info *hi;
2995 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2997 assert(hi->id != 0);
2998 saxdb_start_record(ctx, iter_key(it), 0);
3000 struct handle_cookie *cookie = hi->cookie;
3003 switch (cookie->type) {
3004 case ACTIVATION: type = KEY_ACTIVATION; break;
3005 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
3006 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
3007 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
3008 default: type = NULL; break;
3011 saxdb_start_record(ctx, KEY_COOKIE, 0);
3012 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
3013 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
3015 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
3016 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
3017 saxdb_end_record(ctx);
3021 struct handle_note *prev, *note;
3022 saxdb_start_record(ctx, KEY_NOTES, 0);
3023 WALK_NOTES(hi, prev, note) {
3024 snprintf(flags, sizeof(flags), "%d", note->id);
3025 saxdb_start_record(ctx, flags, 0);
3027 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
3028 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
3029 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
3030 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
3031 saxdb_end_record(ctx);
3033 saxdb_end_record(ctx);
3036 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
3038 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
3040 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
3042 saxdb_write_string(ctx, KEY_FAKEIDENT, hi->fakeident);
3046 for (ii=flen=0; handle_flags[ii]; ++ii)
3047 if (hi->flags & (1 << ii))
3048 flags[flen++] = handle_flags[ii];
3050 saxdb_write_string(ctx, KEY_FLAGS, flags);
3052 saxdb_write_int(ctx, KEY_ID, hi->id);
3054 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
3055 if (hi->last_quit_host[0])
3056 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
3057 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
3059 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
3060 if (hi->masks->used)
3061 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
3063 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
3065 struct string_list *slist;
3066 struct nick_info *ni;
3068 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
3069 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
3070 saxdb_write_string_list(ctx, KEY_NICKS, slist);
3074 if (hi->opserv_level)
3075 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
3076 if (hi->language != lang_C)
3077 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
3078 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
3079 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
3080 if (hi->screen_width)
3081 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
3082 if (hi->table_width)
3083 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
3084 flags[0] = hi->userlist_style;
3086 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
3087 saxdb_end_record(ctx);
3092 static handle_merge_func_t *handle_merge_func_list;
3093 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
3096 reg_handle_merge_func(handle_merge_func_t func)
3098 if (handle_merge_func_used == handle_merge_func_size) {
3099 if (handle_merge_func_size) {
3100 handle_merge_func_size <<= 1;
3101 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
3103 handle_merge_func_size = 8;
3104 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
3107 handle_merge_func_list[handle_merge_func_used++] = func;
3110 static NICKSERV_FUNC(cmd_merge)
3112 struct handle_info *hi_from, *hi_to;
3113 struct userNode *last_user;
3114 struct userData *cList, *cListNext;
3115 unsigned int ii, jj, n;
3116 char buffer[MAXLEN];
3118 NICKSERV_MIN_PARMS(3);
3120 if (!(hi_from = get_victim_oper(user, argv[1])))
3122 if (!(hi_to = get_victim_oper(user, argv[2])))
3124 if (hi_to == hi_from) {
3125 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
3129 for (n=0; n<handle_merge_func_used; n++)
3130 handle_merge_func_list[n](user, hi_to, hi_from);
3132 /* Append "from" handle's nicks to "to" handle's nick list. */
3134 struct nick_info *last_ni;
3135 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3136 last_ni->next = hi_from->nicks;
3138 while (hi_from->nicks) {
3139 hi_from->nicks->owner = hi_to;
3140 hi_from->nicks = hi_from->nicks->next;
3143 /* Merge the hostmasks. */
3144 for (ii=0; ii<hi_from->masks->used; ii++) {
3145 char *mask = hi_from->masks->list[ii];
3146 for (jj=0; jj<hi_to->masks->used; jj++)
3147 if (match_ircglobs(hi_to->masks->list[jj], mask))
3149 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3150 string_list_append(hi_to->masks, strdup(mask));
3153 /* Merge the lists of authed users. */
3155 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3156 last_user->next_authed = hi_from->users;
3158 hi_to->users = hi_from->users;
3160 /* Repoint the old "from" handle's users. */
3161 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3162 last_user->handle_info = hi_to;
3164 hi_from->users = NULL;
3166 /* Merge channel userlists. */
3167 for (cList=hi_from->channels; cList; cList=cListNext) {
3168 struct userData *cList2;
3169 cListNext = cList->u_next;
3170 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3171 if (cList->channel == cList2->channel)
3173 if (cList2 && (cList2->access >= cList->access)) {
3174 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);
3175 /* keep cList2 in hi_to; remove cList from hi_from */
3176 del_channel_user(cList, 1);
3179 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);
3180 /* remove the lower-ranking cList2 from hi_to */
3181 del_channel_user(cList2, 1);
3183 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3185 /* cList needs to be moved from hi_from to hi_to */
3186 cList->handle = hi_to;
3187 /* Remove from linked list for hi_from */
3188 assert(!cList->u_prev);
3189 hi_from->channels = cList->u_next;
3191 cList->u_next->u_prev = cList->u_prev;
3192 /* Add to linked list for hi_to */
3193 cList->u_prev = NULL;
3194 cList->u_next = hi_to->channels;
3195 if (hi_to->channels)
3196 hi_to->channels->u_prev = cList;
3197 hi_to->channels = cList;
3201 /* Do they get an OpServ level promotion? */
3202 if (hi_from->opserv_level > hi_to->opserv_level)
3203 hi_to->opserv_level = hi_from->opserv_level;
3205 /* What about last seen time? */
3206 if (hi_from->lastseen > hi_to->lastseen)
3207 hi_to->lastseen = hi_from->lastseen;
3209 /* New karma is the sum of the two original karmas. */
3210 hi_to->karma += hi_from->karma;
3212 /* Does a fakehost carry over? (This intentionally doesn't set it
3213 * for users previously attached to hi_to. They'll just have to
3216 if (hi_from->fakehost && !hi_to->fakehost)
3217 hi_to->fakehost = strdup(hi_from->fakehost);
3218 if (hi_from->fakeident && !hi_to->fakeident)
3219 hi_to->fakeident = strdup(hi_from->fakeident);
3221 /* Notify of success. */
3222 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3223 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3224 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3226 /* Unregister the "from" handle. */
3227 nickserv_unregister_handle(hi_from, NULL);
3232 struct nickserv_discrim {
3233 unsigned long flags_on, flags_off;
3234 unsigned long min_registered, max_registered;
3235 unsigned long lastseen;
3237 int min_level, max_level;
3238 int min_karma, max_karma;
3239 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3240 const char *nickmask;
3241 const char *hostmask;
3242 const char *fakehostmask;
3243 const char *fakeidentmask;
3244 const char *handlemask;
3245 const char *emailmask;
3248 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
3250 struct discrim_apply_info {
3251 struct nickserv_discrim *discrim;
3252 discrim_search_func func;
3253 struct userNode *source;
3254 unsigned int matched;
3257 static struct nickserv_discrim *
3258 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3261 struct nickserv_discrim *discrim;
3263 discrim = malloc(sizeof(*discrim));
3264 memset(discrim, 0, sizeof(*discrim));
3265 discrim->min_level = 0;
3266 discrim->max_level = INT_MAX;
3267 discrim->limit = 50;
3268 discrim->min_registered = 0;
3269 discrim->max_registered = ULONG_MAX;
3270 discrim->lastseen = ULONG_MAX;
3271 discrim->min_karma = INT_MIN;
3272 discrim->max_karma = INT_MAX;
3274 for (i=0; i<argc; i++) {
3275 if (i == argc - 1) {
3276 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3279 if (!irccasecmp(argv[i], "limit")) {
3280 discrim->limit = strtoul(argv[++i], NULL, 0);
3281 } else if (!irccasecmp(argv[i], "flags")) {
3282 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3283 } else if (!irccasecmp(argv[i], "registered")) {
3284 const char *cmp = argv[++i];
3285 if (cmp[0] == '<') {
3286 if (cmp[1] == '=') {
3287 discrim->min_registered = now - ParseInterval(cmp+2);
3289 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3291 } else if (cmp[0] == '=') {
3292 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3293 } else if (cmp[0] == '>') {
3294 if (cmp[1] == '=') {
3295 discrim->max_registered = now - ParseInterval(cmp+2);
3297 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3300 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3302 } else if (!irccasecmp(argv[i], "seen")) {
3303 discrim->lastseen = now - ParseInterval(argv[++i]);
3304 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3305 discrim->nickmask = argv[++i];
3306 } else if (!irccasecmp(argv[i], "hostmask")) {
3308 if (!irccasecmp(argv[i], "exact")) {
3309 if (i == argc - 1) {
3310 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3313 discrim->hostmask_type = EXACT;
3314 } else if (!irccasecmp(argv[i], "subset")) {
3315 if (i == argc - 1) {
3316 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3319 discrim->hostmask_type = SUBSET;
3320 } else if (!irccasecmp(argv[i], "superset")) {
3321 if (i == argc - 1) {
3322 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3325 discrim->hostmask_type = SUPERSET;
3326 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3327 if (i == argc - 1) {
3328 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3331 discrim->hostmask_type = LASTQUIT;
3334 discrim->hostmask_type = SUPERSET;
3336 discrim->hostmask = argv[++i];
3337 } else if (!irccasecmp(argv[i], "fakehost")) {
3338 if (!irccasecmp(argv[++i], "*")) {
3339 discrim->fakehostmask = 0;
3341 discrim->fakehostmask = argv[i];
3343 } else if (!irccasecmp(argv[i], "fakeident")) {
3344 if (!irccasecmp(argv[++i], "*")) {
3345 discrim->fakeidentmask = 0;
3347 discrim->fakeidentmask = argv[i];
3349 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3350 if (!irccasecmp(argv[++i], "*")) {
3351 discrim->handlemask = 0;
3353 discrim->handlemask = argv[i];
3355 } else if (!irccasecmp(argv[i], "email")) {
3356 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3357 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3359 } else if (!irccasecmp(argv[++i], "*")) {
3360 discrim->emailmask = 0;
3362 discrim->emailmask = argv[i];
3364 } else if (!irccasecmp(argv[i], "access")) {
3365 const char *cmp = argv[++i];
3366 if (cmp[0] == '<') {
3367 if (discrim->min_level == 0) discrim->min_level = 1;
3368 if (cmp[1] == '=') {
3369 discrim->max_level = strtoul(cmp+2, NULL, 0);
3371 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3373 } else if (cmp[0] == '=') {
3374 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3375 } else if (cmp[0] == '>') {
3376 if (cmp[1] == '=') {
3377 discrim->min_level = strtoul(cmp+2, NULL, 0);
3379 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3382 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3384 } else if (!irccasecmp(argv[i], "karma")) {
3385 const char *cmp = argv[++i];
3386 if (cmp[0] == '<') {
3387 if (cmp[1] == '=') {
3388 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3390 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3392 } else if (cmp[0] == '=') {
3393 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3394 } else if (cmp[0] == '>') {
3395 if (cmp[1] == '=') {
3396 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3398 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3401 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3404 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3415 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3417 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3418 || (discrim->flags_off & hi->flags)
3419 || (discrim->min_registered > hi->registered)
3420 || (discrim->max_registered < hi->registered)
3421 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3422 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3423 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3424 || (discrim->fakeidentmask && (!hi->fakeident || !match_ircglob(hi->fakeident, discrim->fakeidentmask)))
3425 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3426 || (discrim->min_level > hi->opserv_level)
3427 || (discrim->max_level < hi->opserv_level)
3428 || (discrim->min_karma > hi->karma)
3429 || (discrim->max_karma < hi->karma)
3433 if (discrim->hostmask) {
3435 for (i=0; i<hi->masks->used; i++) {
3436 const char *mask = hi->masks->list[i];
3437 if ((discrim->hostmask_type == SUBSET)
3438 && (match_ircglobs(discrim->hostmask, mask))) break;
3439 else if ((discrim->hostmask_type == EXACT)
3440 && !irccasecmp(discrim->hostmask, mask)) break;
3441 else if ((discrim->hostmask_type == SUPERSET)
3442 && (match_ircglobs(mask, discrim->hostmask))) break;
3443 else if ((discrim->hostmask_type == LASTQUIT)
3444 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3446 if (i==hi->masks->used) return 0;
3448 if (discrim->nickmask) {
3449 struct nick_info *nick = hi->nicks;
3451 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3454 if (!nick) return 0;
3460 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3462 dict_iterator_t it, next;
3463 unsigned int matched;
3465 for (it = dict_first(nickserv_handle_dict), matched = 0;
3466 it && (matched < discrim->limit);
3468 next = iter_next(it);
3469 if (nickserv_discrim_match(discrim, iter_data(it))) {
3470 dsf(source, iter_data(it));
3478 search_print_func(struct userNode *source, struct handle_info *match)
3480 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3484 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3489 search_unregister_func (struct userNode *source, struct handle_info *match)
3491 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3492 nickserv_unregister_handle(match, source);
3496 nickserv_sort_accounts_by_access(const void *a, const void *b)
3498 const struct handle_info *hi_a = *(const struct handle_info**)a;
3499 const struct handle_info *hi_b = *(const struct handle_info**)b;
3500 if (hi_a->opserv_level != hi_b->opserv_level)
3501 return hi_b->opserv_level - hi_a->opserv_level;
3502 return irccasecmp(hi_a->handle, hi_b->handle);
3506 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3508 struct handle_info_list hil;
3509 struct helpfile_table tbl;
3514 memset(&hil, 0, sizeof(hil));
3515 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3516 struct handle_info *hi = iter_data(it);
3517 if (hi->opserv_level)
3518 handle_info_list_append(&hil, hi);
3520 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3521 tbl.length = hil.used + 1;
3523 tbl.flags = TABLE_NO_FREE;
3524 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3525 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3528 for (ii = 0; ii < hil.used; ) {
3529 ary = malloc(tbl.width * sizeof(ary[0]));
3530 ary[0] = hil.list[ii]->handle;
3531 ary[1] = strtab(hil.list[ii]->opserv_level);
3532 tbl.contents[++ii] = ary;
3534 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3535 reply("MSG_MATCH_COUNT", hil.used);
3536 for (ii = 0; ii < hil.used; ii++)
3537 free(tbl.contents[ii]);
3542 static NICKSERV_FUNC(cmd_search)
3544 struct nickserv_discrim *discrim;
3545 discrim_search_func action;
3546 struct svccmd *subcmd;
3547 unsigned int matches;
3550 NICKSERV_MIN_PARMS(3);
3551 sprintf(buf, "search %s", argv[1]);
3552 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3553 if (!irccasecmp(argv[1], "print"))
3554 action = search_print_func;
3555 else if (!irccasecmp(argv[1], "count"))
3556 action = search_count_func;
3557 else if (!irccasecmp(argv[1], "unregister"))
3558 action = search_unregister_func;
3560 reply("NSMSG_INVALID_ACTION", argv[1]);
3564 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3567 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3571 if (action == search_print_func)
3572 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3573 else if (action == search_count_func)
3574 discrim->limit = INT_MAX;
3576 matches = nickserv_discrim_search(discrim, action, user);
3579 reply("MSG_MATCH_COUNT", matches);
3581 reply("MSG_NO_MATCHES");
3587 static MODCMD_FUNC(cmd_checkpass)
3589 struct handle_info *hi;
3591 NICKSERV_MIN_PARMS(3);
3592 if (!(hi = get_handle_info(argv[1]))) {
3593 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3596 if (checkpass(argv[2], hi->passwd))
3597 reply("CHECKPASS_YES");
3599 reply("CHECKPASS_NO");
3604 static MODCMD_FUNC(cmd_checkemail)
3606 struct handle_info *hi;
3608 NICKSERV_MIN_PARMS(3);
3609 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
3612 if (!hi->email_addr)
3613 reply("CHECKEMAIL_NOT_SET");
3614 else if (!irccasecmp(argv[2], hi->email_addr))
3615 reply("CHECKEMAIL_YES");
3617 reply("CHECKEMAIL_NO");
3623 nickserv_db_read_handle(const char *handle, dict_t obj)
3626 struct string_list *masks, *slist;
3627 struct handle_info *hi;
3628 struct userNode *authed_users;
3629 struct userData *channel_list;
3634 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3635 id = str ? strtoul(str, NULL, 0) : 0;
3636 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3638 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3641 if ((hi = get_handle_info(handle))) {
3642 authed_users = hi->users;
3643 channel_list = hi->channels;
3645 hi->channels = NULL;
3646 dict_remove(nickserv_handle_dict, hi->handle);
3648 authed_users = NULL;
3649 channel_list = NULL;
3651 hi = register_handle(handle, str, id);
3653 hi->users = authed_users;
3654 while (authed_users) {
3655 authed_users->handle_info = hi;
3656 authed_users = authed_users->next_authed;
3659 hi->channels = channel_list;
3660 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3661 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3662 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3663 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3664 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3665 hi->language = language_find(str ? str : "C");
3666 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3667 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3668 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3670 hi->infoline = strdup(str);
3671 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3672 hi->registered = str ? strtoul(str, NULL, 0) : now;
3673 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3674 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
3675 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
3676 hi->karma = str ? strtoul(str, NULL, 0) : 0;
3677 /* We want to read the nicks even if disable_nicks is set. This is so
3678 * that we don't lose the nick data entirely. */
3679 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3681 for (ii=0; ii<slist->used; ii++)
3682 register_nick(slist->list[ii], hi);
3684 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3686 for (ii=0; str[ii]; ii++)
3687 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3689 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3690 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3691 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3692 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3693 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3694 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3695 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3697 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3699 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3700 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3702 nickserv_set_email_addr(hi, str);
3703 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3705 hi->epithet = strdup(str);
3706 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3708 hi->fakehost = strdup(str);
3709 str = database_get_data(obj, KEY_FAKEIDENT, RECDB_QSTRING);
3711 hi->fakeident = strdup(str);
3712 /* Read the "cookie" sub-database (if it exists). */
3713 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3715 const char *data, *type, *expires, *cookie_str;
3716 struct handle_cookie *cookie;
3718 cookie = calloc(1, sizeof(*cookie));
3719 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3720 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3721 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3722 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3723 if (!type || !expires || !cookie_str) {
3724 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3727 if (!irccasecmp(type, KEY_ACTIVATION))
3728 cookie->type = ACTIVATION;
3729 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3730 cookie->type = PASSWORD_CHANGE;
3731 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3732 cookie->type = EMAIL_CHANGE;
3733 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3734 cookie->type = ALLOWAUTH;
3736 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3739 cookie->expires = strtoul(expires, NULL, 0);
3740 if (cookie->expires < now)
3743 cookie->data = strdup(data);
3744 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3748 nickserv_bake_cookie(cookie);
3750 nickserv_free_cookie(cookie);
3752 /* Read the "notes" sub-database (if it exists). */
3753 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
3756 struct handle_note *last_note;
3757 struct handle_note *note;
3760 for (it = dict_first(subdb); it; it = iter_next(it)) {
3761 const char *expires;
3765 const char *note_id;
3768 note_id = iter_key(it);
3769 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
3771 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", note_id, hi->handle);
3774 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
3775 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
3776 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
3777 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
3778 if (!setter || !text || !set) {
3779 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", note_id, hi->handle);
3782 note = calloc(1, sizeof(*note) + strlen(text));
3784 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
3785 note->set = strtoul(set, NULL, 10);
3786 note->id = strtoul(note_id, NULL, 10);
3787 safestrncpy(note->setter, setter, sizeof(note->setter));
3788 strcpy(note->note, text);
3790 last_note->next = note;
3799 nickserv_saxdb_read(dict_t db) {
3801 struct record_data *rd;
3803 for (it=dict_first(db); it; it=iter_next(it)) {
3805 nickserv_db_read_handle(iter_key(it), rd->d.object);
3810 static NICKSERV_FUNC(cmd_mergedb)
3812 struct timeval start, stop;
3815 NICKSERV_MIN_PARMS(2);
3816 gettimeofday(&start, NULL);
3817 if (!(db = parse_database(argv[1]))) {
3818 reply("NSMSG_DB_UNREADABLE", argv[1]);
3821 nickserv_saxdb_read(db);
3823 gettimeofday(&stop, NULL);
3824 stop.tv_sec -= start.tv_sec;
3825 stop.tv_usec -= start.tv_usec;
3826 if (stop.tv_usec < 0) {
3828 stop.tv_usec += 1000000;
3830 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
3835 expire_handles(UNUSED_ARG(void *data))
3837 dict_iterator_t it, next;
3838 unsigned long expiry;
3839 struct handle_info *hi;
3841 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3842 next = iter_next(it);
3844 if ((hi->opserv_level > 0)
3846 || HANDLE_FLAGGED(hi, FROZEN)
3847 || HANDLE_FLAGGED(hi, NODELETE)) {
3850 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3851 if ((now - hi->lastseen) > expiry) {
3852 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3853 nickserv_unregister_handle(hi, NULL);
3857 if (nickserv_conf.handle_expire_frequency)
3858 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3862 nickserv_load_dict(const char *fname)
3866 if (!(file = fopen(fname, "r"))) {
3867 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3870 while (fgets(line, sizeof(line), file)) {
3873 if (line[strlen(line)-1] == '\n')
3874 line[strlen(line)-1] = 0;
3875 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3878 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3881 static enum reclaim_action
3882 reclaim_action_from_string(const char *str) {
3884 return RECLAIM_NONE;
3885 else if (!irccasecmp(str, "warn"))
3886 return RECLAIM_WARN;
3887 else if (!irccasecmp(str, "svsnick"))
3888 return RECLAIM_SVSNICK;
3889 else if (!irccasecmp(str, "kill"))
3890 return RECLAIM_KILL;
3892 return RECLAIM_NONE;
3896 nickserv_conf_read(void)
3898 dict_t conf_node, child;
3902 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3903 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3906 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3908 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3909 if (nickserv_conf.valid_handle_regex_set)
3910 regfree(&nickserv_conf.valid_handle_regex);
3912 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3913 nickserv_conf.valid_handle_regex_set = !err;
3914 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3916 nickserv_conf.valid_handle_regex_set = 0;
3918 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3919 if (nickserv_conf.valid_nick_regex_set)
3920 regfree(&nickserv_conf.valid_nick_regex);
3922 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3923 nickserv_conf.valid_nick_regex_set = !err;
3924 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3926 nickserv_conf.valid_nick_regex_set = 0;
3928 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3930 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3931 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3932 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3933 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3934 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3935 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3936 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3937 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3938 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3939 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3940 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3941 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3942 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3943 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3944 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3945 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3946 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3947 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3948 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3949 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3950 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3951 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3952 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3953 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3954 str = database_get_data(conf_node, KEY_SET_FAKEIDENT_LEVEL, RECDB_QSTRING);
3955 nickserv_conf.set_fakeident_level = str ? strtoul(str, NULL, 0) : 1000;
3956 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3958 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3959 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3960 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3962 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3963 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3964 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3966 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3967 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3968 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3969 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3970 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3971 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3972 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3973 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3974 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
3975 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
3976 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
3979 nickserv_conf.ounregister_flags = 0;
3981 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
3984 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
3986 str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING);
3988 nickserv_conf.handle_ts_mode = TS_IGNORE;
3989 else if (!irccasecmp(str, "ircu"))
3990 nickserv_conf.handle_ts_mode = TS_IRCU;
3992 nickserv_conf.handle_ts_mode = TS_IGNORE;
3993 if (!nickserv_conf.disable_nicks) {
3994 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3995 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3996 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3997 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3998 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3999 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4000 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
4001 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
4003 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
4004 for (it=dict_first(child); it; it=iter_next(it)) {
4005 const char *key = iter_key(it), *value;
4009 if (!strncasecmp(key, "uc_", 3))
4010 flag = toupper(key[3]);
4011 else if (!strncasecmp(key, "lc_", 3))
4012 flag = tolower(key[3]);
4016 if ((pos = handle_inverse_flags[flag])) {
4017 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
4018 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
4021 if (nickserv_conf.weak_password_dict)
4022 dict_delete(nickserv_conf.weak_password_dict);
4023 nickserv_conf.weak_password_dict = dict_new();
4024 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
4025 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
4026 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
4027 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
4029 nickserv_load_dict(str);
4030 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
4031 if (nickserv && str)
4032 NickChange(nickserv, str, 0);
4033 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
4034 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
4035 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
4036 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
4037 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
4038 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
4039 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
4040 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
4041 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
4042 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
4043 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
4044 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
4045 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
4046 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
4047 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
4048 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
4049 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
4050 nickserv_conf.titlehost_suffix = str ? str : "example.net";
4051 str = conf_get_data("server/network", RECDB_QSTRING);
4052 nickserv_conf.network_name = str ? str : "some IRC network";
4053 if (!nickserv_conf.auth_policer_params) {
4054 nickserv_conf.auth_policer_params = policer_params_new();
4055 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
4056 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
4058 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
4059 for (it=dict_first(child); it; it=iter_next(it))
4060 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
4064 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
4066 char newnick[NICKLEN+1];
4075 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4077 case RECLAIM_SVSNICK:
4079 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
4080 } while (GetUserH(newnick));
4081 irc_svsnick(nickserv, user, newnick);
4084 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
4085 DelUser(user, nickserv, 1, msg);
4091 nickserv_reclaim_p(void *data) {
4092 struct userNode *user = data;
4093 struct nick_info *ni = get_nick_info(user->nick);
4095 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4099 check_user_nick(struct userNode *user) {
4100 struct nick_info *ni;
4101 user->modes &= ~FLAGS_REGNICK;
4102 if (!(ni = get_nick_info(user->nick)))
4104 if (user->handle_info == ni->owner) {
4105 user->modes |= FLAGS_REGNICK;
4109 if (nickserv_conf.warn_nick_owned)
4110 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4111 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
4113 if (nickserv_conf.auto_reclaim_delay)
4114 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
4116 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4120 handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
4122 struct handle_info *hi = NULL;
4125 hi = dict_find(nickserv_handle_dict, stamp, NULL);
4126 if ((hi == NULL) && (serial != 0)) {
4128 inttobase64(id, serial, IDLEN);
4129 hi = dict_find(nickserv_id_dict, id, NULL);
4133 if ((nickserv_conf.handle_ts_mode == TS_IRCU)
4134 && (timestamp != hi->registered)) {
4137 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
4140 set_user_handle_info(user, hi, 0);
4142 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial);
4147 handle_nick_change(struct userNode *user, const char *old_nick)
4149 struct handle_info *hi;
4151 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4152 dict_remove(nickserv_allow_auth_dict, old_nick);
4153 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4155 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4156 check_user_nick(user);
4160 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
4162 dict_remove(nickserv_allow_auth_dict, user->nick);
4163 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4164 set_user_handle_info(user, NULL, 0);
4167 static struct modcmd *
4168 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4170 if (min_level > 0) {
4172 sprintf(buf, "%u", min_level);
4173 if (must_be_qualified) {
4174 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4176 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4178 } else if (min_level == 0) {
4179 if (must_be_qualified) {
4180 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4182 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4185 if (must_be_qualified) {
4186 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4188 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4194 nickserv_db_cleanup(void)
4196 unreg_del_user_func(nickserv_remove_user);
4197 userList_clean(&curr_helpers);
4198 policer_params_delete(nickserv_conf.auth_policer_params);
4199 dict_delete(nickserv_handle_dict);
4200 dict_delete(nickserv_nick_dict);
4201 dict_delete(nickserv_opt_dict);
4202 dict_delete(nickserv_allow_auth_dict);
4203 dict_delete(nickserv_email_dict);
4204 dict_delete(nickserv_id_dict);
4205 dict_delete(nickserv_conf.weak_password_dict);
4206 free(auth_func_list);
4207 free(unreg_func_list);
4209 free(allowauth_func_list);
4210 free(handle_merge_func_list);
4211 free(failpw_func_list);
4212 if (nickserv_conf.valid_handle_regex_set)
4213 regfree(&nickserv_conf.valid_handle_regex);
4214 if (nickserv_conf.valid_nick_regex_set)
4215 regfree(&nickserv_conf.valid_nick_regex);
4219 init_nickserv(const char *nick)
4222 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4223 reg_new_user_func(check_user_nick);
4224 reg_nick_change_func(handle_nick_change);
4225 reg_del_user_func(nickserv_remove_user);
4226 reg_account_func(handle_account);
4228 /* set up handle_inverse_flags */
4229 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4230 for (i=0; handle_flags[i]; i++) {
4231 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4232 flag_access_levels[i] = 0;
4235 conf_register_reload(nickserv_conf_read);
4236 nickserv_opt_dict = dict_new();
4237 nickserv_email_dict = dict_new();
4238 dict_set_free_keys(nickserv_email_dict, free);
4239 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4241 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4242 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4243 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4244 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4245 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4246 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4247 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4248 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4249 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4250 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4251 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4252 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4253 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4254 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4255 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4256 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4257 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4258 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4259 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4260 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4261 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4262 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4263 if (!nickserv_conf.disable_nicks) {
4264 /* nick management commands */
4265 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4266 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4267 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4268 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4269 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4270 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4272 if (nickserv_conf.email_enabled) {
4273 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4274 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4275 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4276 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4277 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
4278 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4280 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4281 /* miscellaneous commands */
4282 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4283 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4284 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4285 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4286 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4287 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
4289 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4290 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4291 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4292 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4293 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4294 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4295 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4296 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4297 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4298 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4299 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4300 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4301 if (nickserv_conf.titlehost_suffix) {
4302 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4303 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4304 dict_insert(nickserv_opt_dict, "FAKEIDENT", opt_fakeident);
4306 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4307 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4308 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4309 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
4311 nickserv_handle_dict = dict_new();
4312 dict_set_free_keys(nickserv_handle_dict, free);
4313 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4315 nickserv_id_dict = dict_new();
4316 dict_set_free_keys(nickserv_id_dict, free);
4318 nickserv_nick_dict = dict_new();
4319 dict_set_free_data(nickserv_nick_dict, free);
4321 nickserv_allow_auth_dict = dict_new();
4323 userList_init(&curr_helpers);
4326 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4327 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4328 nickserv_service = service_register(nickserv);
4330 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4331 reg_exit_func(nickserv_db_cleanup);
4332 if(nickserv_conf.handle_expire_frequency)
4333 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4334 message_register_table(msgtab);