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(!oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0))
2662 if (ident && strlen(ident) > USERLEN) {
2663 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
2667 if ((strlen(host) > HOSTLEN) || (host[0] == '.')) {
2668 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2674 if (!strcmp(host, "*"))
2675 hi->fakehost = NULL;
2677 hi->fakehost = strdup(host);
2678 host = hi->fakehost;
2681 host = generate_fakehost(hi);
2684 free(hi->fakeident);
2685 if (!strcmp(ident, "*"))
2686 hi->fakeident = NULL;
2688 hi->fakeident = strdup(ident);
2689 ident = hi->fakeident;
2692 ident = generate_fakeident(hi, NULL);
2694 apply_fakehost(hi, NULL);
2697 host = generate_fakehost(hi);
2698 ident = generate_fakeident(hi, NULL);
2701 host = (char *) user_find_message(user, "MSG_NONE");
2703 send_message(user, nickserv, "NSMSG_SET_FAKEIDENTHOST", ident, host);
2705 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", host);
2709 static OPTION_FUNC(opt_fakeident)
2714 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2718 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
2720 if (strlen(ident) > USERLEN) {
2721 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
2724 free(hi->fakeident);
2725 if (!strcmp(ident, "*"))
2726 hi->fakeident = NULL;
2728 hi->fakeident = strdup(ident);
2729 ident = hi->fakeident;
2730 apply_fakehost(hi, NULL);
2732 ident = generate_fakeident(hi, NULL); /* NULL if no fake ident set */
2734 ident = user_find_message(user, "MSG_NONE");
2735 send_message(user, nickserv, "NSMSG_SET_FAKEIDENT", ident);
2739 static NICKSERV_FUNC(cmd_reclaim)
2741 struct handle_info *hi;
2742 struct nick_info *ni;
2743 struct userNode *victim;
2745 NICKSERV_MIN_PARMS(2);
2746 hi = user->handle_info;
2747 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2749 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2752 if (ni->owner != user->handle_info) {
2753 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2756 victim = GetUserH(ni->nick);
2758 reply("MSG_NICK_UNKNOWN", ni->nick);
2761 if (victim == user) {
2762 reply("NSMSG_NICK_USER_YOU");
2765 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2766 switch (nickserv_conf.reclaim_action) {
2767 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2768 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2769 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2770 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2775 static NICKSERV_FUNC(cmd_unregnick)
2778 struct handle_info *hi;
2779 struct nick_info *ni;
2781 hi = user->handle_info;
2782 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2783 ni = dict_find(nickserv_nick_dict, nick, NULL);
2785 reply("NSMSG_UNKNOWN_NICK", nick);
2788 if (hi != ni->owner) {
2789 reply("NSMSG_NOT_YOUR_NICK", nick);
2792 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2797 static NICKSERV_FUNC(cmd_ounregnick)
2799 struct nick_info *ni;
2801 NICKSERV_MIN_PARMS(2);
2802 if (!(ni = get_nick_info(argv[1]))) {
2803 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2806 if (!oper_outranks(user, ni->owner))
2808 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2813 static NICKSERV_FUNC(cmd_unregister)
2815 struct handle_info *hi;
2818 NICKSERV_MIN_PARMS(2);
2819 hi = user->handle_info;
2822 if (checkpass(passwd, hi->passwd)) {
2823 nickserv_unregister_handle(hi, user);
2826 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2827 reply("NSMSG_PASSWORD_INVALID");
2832 static NICKSERV_FUNC(cmd_ounregister)
2834 struct handle_info *hi;
2835 char reason[MAXLEN];
2838 NICKSERV_MIN_PARMS(2);
2839 if (!(hi = get_victim_oper(user, argv[1])))
2842 if (HANDLE_FLAGGED(hi, NODELETE)) {
2843 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
2847 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
2849 ((hi->flags & nickserv_conf.ounregister_flags)
2851 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
2852 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
2856 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
2857 global_message(MESSAGE_RECIPIENT_STAFF, reason);
2858 nickserv_unregister_handle(hi, user);
2862 static NICKSERV_FUNC(cmd_status)
2864 if (nickserv_conf.disable_nicks) {
2865 reply("NSMSG_GLOBAL_STATS_NONICK",
2866 dict_size(nickserv_handle_dict));
2868 if (user->handle_info) {
2870 struct nick_info *ni;
2871 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2872 reply("NSMSG_HANDLE_STATS", cnt);
2874 reply("NSMSG_HANDLE_NONE");
2876 reply("NSMSG_GLOBAL_STATS",
2877 dict_size(nickserv_handle_dict),
2878 dict_size(nickserv_nick_dict));
2883 static NICKSERV_FUNC(cmd_ghost)
2885 struct userNode *target;
2886 char reason[MAXLEN];
2888 NICKSERV_MIN_PARMS(2);
2889 if (!(target = GetUserH(argv[1]))) {
2890 reply("MSG_NICK_UNKNOWN", argv[1]);
2893 if (target == user) {
2894 reply("NSMSG_CANNOT_GHOST_SELF");
2897 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2898 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2901 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2902 DelUser(target, nickserv, 1, reason);
2903 reply("NSMSG_GHOST_KILLED", argv[1]);
2907 static NICKSERV_FUNC(cmd_vacation)
2909 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2910 reply("NSMSG_ON_VACATION");
2914 static NICKSERV_FUNC(cmd_addnote)
2916 struct handle_info *hi;
2917 unsigned long duration;
2920 struct handle_note *prev;
2921 struct handle_note *note;
2923 /* Parse parameters and figure out values for note's fields. */
2924 NICKSERV_MIN_PARMS(4);
2925 hi = get_victim_oper(user, argv[1]);
2928 if(!strcmp(argv[2], "0"))
2930 else if(!(duration = ParseInterval(argv[2])))
2932 reply("MSG_INVALID_DURATION", argv[2]);
2935 if (duration > 2*365*86400) {
2936 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
2939 unsplit_string(argv + 3, argc - 3, text);
2940 WALK_NOTES(hi, prev, note) {}
2941 id = prev ? (prev->id + 1) : 1;
2943 /* Create the new note structure. */
2944 note = calloc(1, sizeof(*note) + strlen(text));
2946 note->expires = duration ? (now + duration) : 0;
2949 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
2950 strcpy(note->note, text);
2955 reply("NSMSG_NOTE_ADDED", id, hi->handle);
2959 static NICKSERV_FUNC(cmd_delnote)
2961 struct handle_info *hi;
2962 struct handle_note *prev;
2963 struct handle_note *note;
2966 NICKSERV_MIN_PARMS(3);
2967 hi = get_victim_oper(user, argv[1]);
2970 id = strtoul(argv[2], NULL, 10);
2971 WALK_NOTES(hi, prev, note) {
2972 if (id == note->id) {
2974 prev->next = note->next;
2976 hi->notes = note->next;
2978 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
2982 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
2987 nickserv_saxdb_write(struct saxdb_context *ctx) {
2989 struct handle_info *hi;
2992 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2994 assert(hi->id != 0);
2995 saxdb_start_record(ctx, iter_key(it), 0);
2997 struct handle_cookie *cookie = hi->cookie;
3000 switch (cookie->type) {
3001 case ACTIVATION: type = KEY_ACTIVATION; break;
3002 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
3003 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
3004 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
3005 default: type = NULL; break;
3008 saxdb_start_record(ctx, KEY_COOKIE, 0);
3009 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
3010 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
3012 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
3013 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
3014 saxdb_end_record(ctx);
3018 struct handle_note *prev, *note;
3019 saxdb_start_record(ctx, KEY_NOTES, 0);
3020 WALK_NOTES(hi, prev, note) {
3021 snprintf(flags, sizeof(flags), "%d", note->id);
3022 saxdb_start_record(ctx, flags, 0);
3024 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
3025 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
3026 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
3027 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
3028 saxdb_end_record(ctx);
3030 saxdb_end_record(ctx);
3033 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
3035 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
3037 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
3039 saxdb_write_string(ctx, KEY_FAKEIDENT, hi->fakeident);
3043 for (ii=flen=0; handle_flags[ii]; ++ii)
3044 if (hi->flags & (1 << ii))
3045 flags[flen++] = handle_flags[ii];
3047 saxdb_write_string(ctx, KEY_FLAGS, flags);
3049 saxdb_write_int(ctx, KEY_ID, hi->id);
3051 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
3052 if (hi->last_quit_host[0])
3053 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
3054 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
3056 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
3057 if (hi->masks->used)
3058 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
3060 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
3062 struct string_list *slist;
3063 struct nick_info *ni;
3065 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
3066 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
3067 saxdb_write_string_list(ctx, KEY_NICKS, slist);
3071 if (hi->opserv_level)
3072 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
3073 if (hi->language != lang_C)
3074 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
3075 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
3076 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
3077 if (hi->screen_width)
3078 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
3079 if (hi->table_width)
3080 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
3081 flags[0] = hi->userlist_style;
3083 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
3084 saxdb_end_record(ctx);
3089 static handle_merge_func_t *handle_merge_func_list;
3090 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
3093 reg_handle_merge_func(handle_merge_func_t func)
3095 if (handle_merge_func_used == handle_merge_func_size) {
3096 if (handle_merge_func_size) {
3097 handle_merge_func_size <<= 1;
3098 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
3100 handle_merge_func_size = 8;
3101 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
3104 handle_merge_func_list[handle_merge_func_used++] = func;
3107 static NICKSERV_FUNC(cmd_merge)
3109 struct handle_info *hi_from, *hi_to;
3110 struct userNode *last_user;
3111 struct userData *cList, *cListNext;
3112 unsigned int ii, jj, n;
3113 char buffer[MAXLEN];
3115 NICKSERV_MIN_PARMS(3);
3117 if (!(hi_from = get_victim_oper(user, argv[1])))
3119 if (!(hi_to = get_victim_oper(user, argv[2])))
3121 if (hi_to == hi_from) {
3122 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
3126 for (n=0; n<handle_merge_func_used; n++)
3127 handle_merge_func_list[n](user, hi_to, hi_from);
3129 /* Append "from" handle's nicks to "to" handle's nick list. */
3131 struct nick_info *last_ni;
3132 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3133 last_ni->next = hi_from->nicks;
3135 while (hi_from->nicks) {
3136 hi_from->nicks->owner = hi_to;
3137 hi_from->nicks = hi_from->nicks->next;
3140 /* Merge the hostmasks. */
3141 for (ii=0; ii<hi_from->masks->used; ii++) {
3142 char *mask = hi_from->masks->list[ii];
3143 for (jj=0; jj<hi_to->masks->used; jj++)
3144 if (match_ircglobs(hi_to->masks->list[jj], mask))
3146 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3147 string_list_append(hi_to->masks, strdup(mask));
3150 /* Merge the lists of authed users. */
3152 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3153 last_user->next_authed = hi_from->users;
3155 hi_to->users = hi_from->users;
3157 /* Repoint the old "from" handle's users. */
3158 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3159 last_user->handle_info = hi_to;
3161 hi_from->users = NULL;
3163 /* Merge channel userlists. */
3164 for (cList=hi_from->channels; cList; cList=cListNext) {
3165 struct userData *cList2;
3166 cListNext = cList->u_next;
3167 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3168 if (cList->channel == cList2->channel)
3170 if (cList2 && (cList2->access >= cList->access)) {
3171 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);
3172 /* keep cList2 in hi_to; remove cList from hi_from */
3173 del_channel_user(cList, 1);
3176 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);
3177 /* remove the lower-ranking cList2 from hi_to */
3178 del_channel_user(cList2, 1);
3180 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3182 /* cList needs to be moved from hi_from to hi_to */
3183 cList->handle = hi_to;
3184 /* Remove from linked list for hi_from */
3185 assert(!cList->u_prev);
3186 hi_from->channels = cList->u_next;
3188 cList->u_next->u_prev = cList->u_prev;
3189 /* Add to linked list for hi_to */
3190 cList->u_prev = NULL;
3191 cList->u_next = hi_to->channels;
3192 if (hi_to->channels)
3193 hi_to->channels->u_prev = cList;
3194 hi_to->channels = cList;
3198 /* Do they get an OpServ level promotion? */
3199 if (hi_from->opserv_level > hi_to->opserv_level)
3200 hi_to->opserv_level = hi_from->opserv_level;
3202 /* What about last seen time? */
3203 if (hi_from->lastseen > hi_to->lastseen)
3204 hi_to->lastseen = hi_from->lastseen;
3206 /* New karma is the sum of the two original karmas. */
3207 hi_to->karma += hi_from->karma;
3209 /* Does a fakehost carry over? (This intentionally doesn't set it
3210 * for users previously attached to hi_to. They'll just have to
3213 if (hi_from->fakehost && !hi_to->fakehost)
3214 hi_to->fakehost = strdup(hi_from->fakehost);
3215 if (hi_from->fakeident && !hi_to->fakeident)
3216 hi_to->fakeident = strdup(hi_from->fakeident);
3218 /* Notify of success. */
3219 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3220 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3221 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3223 /* Unregister the "from" handle. */
3224 nickserv_unregister_handle(hi_from, NULL);
3229 struct nickserv_discrim {
3230 unsigned long flags_on, flags_off;
3231 unsigned long min_registered, max_registered;
3232 unsigned long lastseen;
3234 int min_level, max_level;
3235 int min_karma, max_karma;
3236 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3237 const char *nickmask;
3238 const char *hostmask;
3239 const char *fakehostmask;
3240 const char *fakeidentmask;
3241 const char *handlemask;
3242 const char *emailmask;
3245 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
3247 struct discrim_apply_info {
3248 struct nickserv_discrim *discrim;
3249 discrim_search_func func;
3250 struct userNode *source;
3251 unsigned int matched;
3254 static struct nickserv_discrim *
3255 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3258 struct nickserv_discrim *discrim;
3260 discrim = malloc(sizeof(*discrim));
3261 memset(discrim, 0, sizeof(*discrim));
3262 discrim->min_level = 0;
3263 discrim->max_level = INT_MAX;
3264 discrim->limit = 50;
3265 discrim->min_registered = 0;
3266 discrim->max_registered = ULONG_MAX;
3267 discrim->lastseen = ULONG_MAX;
3268 discrim->min_karma = INT_MIN;
3269 discrim->max_karma = INT_MAX;
3271 for (i=0; i<argc; i++) {
3272 if (i == argc - 1) {
3273 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3276 if (!irccasecmp(argv[i], "limit")) {
3277 discrim->limit = strtoul(argv[++i], NULL, 0);
3278 } else if (!irccasecmp(argv[i], "flags")) {
3279 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3280 } else if (!irccasecmp(argv[i], "registered")) {
3281 const char *cmp = argv[++i];
3282 if (cmp[0] == '<') {
3283 if (cmp[1] == '=') {
3284 discrim->min_registered = now - ParseInterval(cmp+2);
3286 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3288 } else if (cmp[0] == '=') {
3289 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3290 } else if (cmp[0] == '>') {
3291 if (cmp[1] == '=') {
3292 discrim->max_registered = now - ParseInterval(cmp+2);
3294 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3297 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3299 } else if (!irccasecmp(argv[i], "seen")) {
3300 discrim->lastseen = now - ParseInterval(argv[++i]);
3301 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3302 discrim->nickmask = argv[++i];
3303 } else if (!irccasecmp(argv[i], "hostmask")) {
3305 if (!irccasecmp(argv[i], "exact")) {
3306 if (i == argc - 1) {
3307 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3310 discrim->hostmask_type = EXACT;
3311 } else if (!irccasecmp(argv[i], "subset")) {
3312 if (i == argc - 1) {
3313 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3316 discrim->hostmask_type = SUBSET;
3317 } else if (!irccasecmp(argv[i], "superset")) {
3318 if (i == argc - 1) {
3319 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3322 discrim->hostmask_type = SUPERSET;
3323 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3324 if (i == argc - 1) {
3325 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3328 discrim->hostmask_type = LASTQUIT;
3331 discrim->hostmask_type = SUPERSET;
3333 discrim->hostmask = argv[++i];
3334 } else if (!irccasecmp(argv[i], "fakehost")) {
3335 if (!irccasecmp(argv[++i], "*")) {
3336 discrim->fakehostmask = 0;
3338 discrim->fakehostmask = argv[i];
3340 } else if (!irccasecmp(argv[i], "fakeident")) {
3341 if (!irccasecmp(argv[++i], "*")) {
3342 discrim->fakeidentmask = 0;
3344 discrim->fakeidentmask = argv[i];
3346 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3347 if (!irccasecmp(argv[++i], "*")) {
3348 discrim->handlemask = 0;
3350 discrim->handlemask = argv[i];
3352 } else if (!irccasecmp(argv[i], "email")) {
3353 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3354 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3356 } else if (!irccasecmp(argv[++i], "*")) {
3357 discrim->emailmask = 0;
3359 discrim->emailmask = argv[i];
3361 } else if (!irccasecmp(argv[i], "access")) {
3362 const char *cmp = argv[++i];
3363 if (cmp[0] == '<') {
3364 if (discrim->min_level == 0) discrim->min_level = 1;
3365 if (cmp[1] == '=') {
3366 discrim->max_level = strtoul(cmp+2, NULL, 0);
3368 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3370 } else if (cmp[0] == '=') {
3371 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3372 } else if (cmp[0] == '>') {
3373 if (cmp[1] == '=') {
3374 discrim->min_level = strtoul(cmp+2, NULL, 0);
3376 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3379 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3381 } else if (!irccasecmp(argv[i], "karma")) {
3382 const char *cmp = argv[++i];
3383 if (cmp[0] == '<') {
3384 if (cmp[1] == '=') {
3385 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3387 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3389 } else if (cmp[0] == '=') {
3390 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3391 } else if (cmp[0] == '>') {
3392 if (cmp[1] == '=') {
3393 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3395 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3398 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3401 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3412 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3414 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3415 || (discrim->flags_off & hi->flags)
3416 || (discrim->min_registered > hi->registered)
3417 || (discrim->max_registered < hi->registered)
3418 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3419 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3420 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3421 || (discrim->fakeidentmask && (!hi->fakeident || !match_ircglob(hi->fakeident, discrim->fakeidentmask)))
3422 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3423 || (discrim->min_level > hi->opserv_level)
3424 || (discrim->max_level < hi->opserv_level)
3425 || (discrim->min_karma > hi->karma)
3426 || (discrim->max_karma < hi->karma)
3430 if (discrim->hostmask) {
3432 for (i=0; i<hi->masks->used; i++) {
3433 const char *mask = hi->masks->list[i];
3434 if ((discrim->hostmask_type == SUBSET)
3435 && (match_ircglobs(discrim->hostmask, mask))) break;
3436 else if ((discrim->hostmask_type == EXACT)
3437 && !irccasecmp(discrim->hostmask, mask)) break;
3438 else if ((discrim->hostmask_type == SUPERSET)
3439 && (match_ircglobs(mask, discrim->hostmask))) break;
3440 else if ((discrim->hostmask_type == LASTQUIT)
3441 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3443 if (i==hi->masks->used) return 0;
3445 if (discrim->nickmask) {
3446 struct nick_info *nick = hi->nicks;
3448 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3451 if (!nick) return 0;
3457 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3459 dict_iterator_t it, next;
3460 unsigned int matched;
3462 for (it = dict_first(nickserv_handle_dict), matched = 0;
3463 it && (matched < discrim->limit);
3465 next = iter_next(it);
3466 if (nickserv_discrim_match(discrim, iter_data(it))) {
3467 dsf(source, iter_data(it));
3475 search_print_func(struct userNode *source, struct handle_info *match)
3477 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3481 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3486 search_unregister_func (struct userNode *source, struct handle_info *match)
3488 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3489 nickserv_unregister_handle(match, source);
3493 nickserv_sort_accounts_by_access(const void *a, const void *b)
3495 const struct handle_info *hi_a = *(const struct handle_info**)a;
3496 const struct handle_info *hi_b = *(const struct handle_info**)b;
3497 if (hi_a->opserv_level != hi_b->opserv_level)
3498 return hi_b->opserv_level - hi_a->opserv_level;
3499 return irccasecmp(hi_a->handle, hi_b->handle);
3503 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3505 struct handle_info_list hil;
3506 struct helpfile_table tbl;
3511 memset(&hil, 0, sizeof(hil));
3512 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3513 struct handle_info *hi = iter_data(it);
3514 if (hi->opserv_level)
3515 handle_info_list_append(&hil, hi);
3517 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3518 tbl.length = hil.used + 1;
3520 tbl.flags = TABLE_NO_FREE;
3521 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3522 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3525 for (ii = 0; ii < hil.used; ) {
3526 ary = malloc(tbl.width * sizeof(ary[0]));
3527 ary[0] = hil.list[ii]->handle;
3528 ary[1] = strtab(hil.list[ii]->opserv_level);
3529 tbl.contents[++ii] = ary;
3531 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3532 reply("MSG_MATCH_COUNT", hil.used);
3533 for (ii = 0; ii < hil.used; ii++)
3534 free(tbl.contents[ii]);
3539 static NICKSERV_FUNC(cmd_search)
3541 struct nickserv_discrim *discrim;
3542 discrim_search_func action;
3543 struct svccmd *subcmd;
3544 unsigned int matches;
3547 NICKSERV_MIN_PARMS(3);
3548 sprintf(buf, "search %s", argv[1]);
3549 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3550 if (!irccasecmp(argv[1], "print"))
3551 action = search_print_func;
3552 else if (!irccasecmp(argv[1], "count"))
3553 action = search_count_func;
3554 else if (!irccasecmp(argv[1], "unregister"))
3555 action = search_unregister_func;
3557 reply("NSMSG_INVALID_ACTION", argv[1]);
3561 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3564 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3568 if (action == search_print_func)
3569 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3570 else if (action == search_count_func)
3571 discrim->limit = INT_MAX;
3573 matches = nickserv_discrim_search(discrim, action, user);
3576 reply("MSG_MATCH_COUNT", matches);
3578 reply("MSG_NO_MATCHES");
3584 static MODCMD_FUNC(cmd_checkpass)
3586 struct handle_info *hi;
3588 NICKSERV_MIN_PARMS(3);
3589 if (!(hi = get_handle_info(argv[1]))) {
3590 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3593 if (checkpass(argv[2], hi->passwd))
3594 reply("CHECKPASS_YES");
3596 reply("CHECKPASS_NO");
3601 static MODCMD_FUNC(cmd_checkemail)
3603 struct handle_info *hi;
3605 NICKSERV_MIN_PARMS(3);
3606 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
3609 if (!hi->email_addr)
3610 reply("CHECKEMAIL_NOT_SET");
3611 else if (!irccasecmp(argv[2], hi->email_addr))
3612 reply("CHECKEMAIL_YES");
3614 reply("CHECKEMAIL_NO");
3620 nickserv_db_read_handle(const char *handle, dict_t obj)
3623 struct string_list *masks, *slist;
3624 struct handle_info *hi;
3625 struct userNode *authed_users;
3626 struct userData *channel_list;
3631 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3632 id = str ? strtoul(str, NULL, 0) : 0;
3633 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3635 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3638 if ((hi = get_handle_info(handle))) {
3639 authed_users = hi->users;
3640 channel_list = hi->channels;
3642 hi->channels = NULL;
3643 dict_remove(nickserv_handle_dict, hi->handle);
3645 authed_users = NULL;
3646 channel_list = NULL;
3648 hi = register_handle(handle, str, id);
3650 hi->users = authed_users;
3651 while (authed_users) {
3652 authed_users->handle_info = hi;
3653 authed_users = authed_users->next_authed;
3656 hi->channels = channel_list;
3657 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3658 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3659 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3660 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3661 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3662 hi->language = language_find(str ? str : "C");
3663 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3664 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3665 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3667 hi->infoline = strdup(str);
3668 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3669 hi->registered = str ? strtoul(str, NULL, 0) : now;
3670 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3671 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
3672 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
3673 hi->karma = str ? strtoul(str, NULL, 0) : 0;
3674 /* We want to read the nicks even if disable_nicks is set. This is so
3675 * that we don't lose the nick data entirely. */
3676 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3678 for (ii=0; ii<slist->used; ii++)
3679 register_nick(slist->list[ii], hi);
3681 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3683 for (ii=0; str[ii]; ii++)
3684 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3686 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3687 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3688 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3689 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3690 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3691 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3692 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3694 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3696 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3697 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3699 nickserv_set_email_addr(hi, str);
3700 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3702 hi->epithet = strdup(str);
3703 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3705 hi->fakehost = strdup(str);
3706 str = database_get_data(obj, KEY_FAKEIDENT, RECDB_QSTRING);
3708 hi->fakeident = strdup(str);
3709 /* Read the "cookie" sub-database (if it exists). */
3710 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3712 const char *data, *type, *expires, *cookie_str;
3713 struct handle_cookie *cookie;
3715 cookie = calloc(1, sizeof(*cookie));
3716 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3717 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3718 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3719 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3720 if (!type || !expires || !cookie_str) {
3721 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3724 if (!irccasecmp(type, KEY_ACTIVATION))
3725 cookie->type = ACTIVATION;
3726 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3727 cookie->type = PASSWORD_CHANGE;
3728 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3729 cookie->type = EMAIL_CHANGE;
3730 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3731 cookie->type = ALLOWAUTH;
3733 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3736 cookie->expires = strtoul(expires, NULL, 0);
3737 if (cookie->expires < now)
3740 cookie->data = strdup(data);
3741 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3745 nickserv_bake_cookie(cookie);
3747 nickserv_free_cookie(cookie);
3749 /* Read the "notes" sub-database (if it exists). */
3750 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
3753 struct handle_note *last_note;
3754 struct handle_note *note;
3757 for (it = dict_first(subdb); it; it = iter_next(it)) {
3758 const char *expires;
3762 const char *note_id;
3765 note_id = iter_key(it);
3766 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
3768 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", note_id, hi->handle);
3771 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
3772 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
3773 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
3774 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
3775 if (!setter || !text || !set) {
3776 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", note_id, hi->handle);
3779 note = calloc(1, sizeof(*note) + strlen(text));
3781 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
3782 note->set = strtoul(set, NULL, 10);
3783 note->id = strtoul(note_id, NULL, 10);
3784 safestrncpy(note->setter, setter, sizeof(note->setter));
3785 strcpy(note->note, text);
3787 last_note->next = note;
3796 nickserv_saxdb_read(dict_t db) {
3798 struct record_data *rd;
3800 for (it=dict_first(db); it; it=iter_next(it)) {
3802 nickserv_db_read_handle(iter_key(it), rd->d.object);
3807 static NICKSERV_FUNC(cmd_mergedb)
3809 struct timeval start, stop;
3812 NICKSERV_MIN_PARMS(2);
3813 gettimeofday(&start, NULL);
3814 if (!(db = parse_database(argv[1]))) {
3815 reply("NSMSG_DB_UNREADABLE", argv[1]);
3818 nickserv_saxdb_read(db);
3820 gettimeofday(&stop, NULL);
3821 stop.tv_sec -= start.tv_sec;
3822 stop.tv_usec -= start.tv_usec;
3823 if (stop.tv_usec < 0) {
3825 stop.tv_usec += 1000000;
3827 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
3832 expire_handles(UNUSED_ARG(void *data))
3834 dict_iterator_t it, next;
3835 unsigned long expiry;
3836 struct handle_info *hi;
3838 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3839 next = iter_next(it);
3841 if ((hi->opserv_level > 0)
3843 || HANDLE_FLAGGED(hi, FROZEN)
3844 || HANDLE_FLAGGED(hi, NODELETE)) {
3847 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3848 if ((now - hi->lastseen) > expiry) {
3849 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3850 nickserv_unregister_handle(hi, NULL);
3854 if (nickserv_conf.handle_expire_frequency)
3855 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3859 nickserv_load_dict(const char *fname)
3863 if (!(file = fopen(fname, "r"))) {
3864 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3867 while (fgets(line, sizeof(line), file)) {
3870 if (line[strlen(line)-1] == '\n')
3871 line[strlen(line)-1] = 0;
3872 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3875 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3878 static enum reclaim_action
3879 reclaim_action_from_string(const char *str) {
3881 return RECLAIM_NONE;
3882 else if (!irccasecmp(str, "warn"))
3883 return RECLAIM_WARN;
3884 else if (!irccasecmp(str, "svsnick"))
3885 return RECLAIM_SVSNICK;
3886 else if (!irccasecmp(str, "kill"))
3887 return RECLAIM_KILL;
3889 return RECLAIM_NONE;
3893 nickserv_conf_read(void)
3895 dict_t conf_node, child;
3899 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3900 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3903 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3905 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3906 if (nickserv_conf.valid_handle_regex_set)
3907 regfree(&nickserv_conf.valid_handle_regex);
3909 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3910 nickserv_conf.valid_handle_regex_set = !err;
3911 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3913 nickserv_conf.valid_handle_regex_set = 0;
3915 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3916 if (nickserv_conf.valid_nick_regex_set)
3917 regfree(&nickserv_conf.valid_nick_regex);
3919 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3920 nickserv_conf.valid_nick_regex_set = !err;
3921 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3923 nickserv_conf.valid_nick_regex_set = 0;
3925 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3927 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3928 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3929 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3930 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3931 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3932 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3933 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3934 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3935 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3936 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3937 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3938 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3939 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3940 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3941 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3942 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3943 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3944 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3945 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3946 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3947 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3948 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3949 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3950 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3951 str = database_get_data(conf_node, KEY_SET_FAKEIDENT_LEVEL, RECDB_QSTRING);
3952 nickserv_conf.set_fakeident_level = str ? strtoul(str, NULL, 0) : 1000;
3953 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3955 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3956 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3957 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3959 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3960 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3961 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3963 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3964 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3965 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3966 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3967 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3968 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3969 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3970 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3971 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
3972 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
3973 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
3976 nickserv_conf.ounregister_flags = 0;
3978 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
3981 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
3983 str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING);
3985 nickserv_conf.handle_ts_mode = TS_IGNORE;
3986 else if (!irccasecmp(str, "ircu"))
3987 nickserv_conf.handle_ts_mode = TS_IRCU;
3989 nickserv_conf.handle_ts_mode = TS_IGNORE;
3990 if (!nickserv_conf.disable_nicks) {
3991 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3992 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3993 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3994 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3995 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3996 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3997 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3998 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
4000 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
4001 for (it=dict_first(child); it; it=iter_next(it)) {
4002 const char *key = iter_key(it), *value;
4006 if (!strncasecmp(key, "uc_", 3))
4007 flag = toupper(key[3]);
4008 else if (!strncasecmp(key, "lc_", 3))
4009 flag = tolower(key[3]);
4013 if ((pos = handle_inverse_flags[flag])) {
4014 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
4015 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
4018 if (nickserv_conf.weak_password_dict)
4019 dict_delete(nickserv_conf.weak_password_dict);
4020 nickserv_conf.weak_password_dict = dict_new();
4021 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
4022 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
4023 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
4024 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
4026 nickserv_load_dict(str);
4027 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
4028 if (nickserv && str)
4029 NickChange(nickserv, str, 0);
4030 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
4031 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
4032 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
4033 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
4034 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
4035 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
4036 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
4037 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
4038 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
4039 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
4040 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
4041 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
4042 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
4043 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
4044 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
4045 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
4046 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
4047 nickserv_conf.titlehost_suffix = str ? str : "example.net";
4048 str = conf_get_data("server/network", RECDB_QSTRING);
4049 nickserv_conf.network_name = str ? str : "some IRC network";
4050 if (!nickserv_conf.auth_policer_params) {
4051 nickserv_conf.auth_policer_params = policer_params_new();
4052 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
4053 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
4055 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
4056 for (it=dict_first(child); it; it=iter_next(it))
4057 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
4061 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
4063 char newnick[NICKLEN+1];
4072 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4074 case RECLAIM_SVSNICK:
4076 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
4077 } while (GetUserH(newnick));
4078 irc_svsnick(nickserv, user, newnick);
4081 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
4082 DelUser(user, nickserv, 1, msg);
4088 nickserv_reclaim_p(void *data) {
4089 struct userNode *user = data;
4090 struct nick_info *ni = get_nick_info(user->nick);
4092 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4096 check_user_nick(struct userNode *user) {
4097 struct nick_info *ni;
4098 user->modes &= ~FLAGS_REGNICK;
4099 if (!(ni = get_nick_info(user->nick)))
4101 if (user->handle_info == ni->owner) {
4102 user->modes |= FLAGS_REGNICK;
4106 if (nickserv_conf.warn_nick_owned)
4107 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4108 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
4110 if (nickserv_conf.auto_reclaim_delay)
4111 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
4113 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4117 handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
4119 struct handle_info *hi = NULL;
4122 hi = dict_find(nickserv_handle_dict, stamp, NULL);
4123 if ((hi == NULL) && (serial != 0)) {
4125 inttobase64(id, serial, IDLEN);
4126 hi = dict_find(nickserv_id_dict, id, NULL);
4130 if ((nickserv_conf.handle_ts_mode == TS_IRCU)
4131 && (timestamp != hi->registered)) {
4134 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
4137 set_user_handle_info(user, hi, 0);
4139 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial);
4144 handle_nick_change(struct userNode *user, const char *old_nick)
4146 struct handle_info *hi;
4148 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4149 dict_remove(nickserv_allow_auth_dict, old_nick);
4150 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4152 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4153 check_user_nick(user);
4157 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
4159 dict_remove(nickserv_allow_auth_dict, user->nick);
4160 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4161 set_user_handle_info(user, NULL, 0);
4164 static struct modcmd *
4165 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4167 if (min_level > 0) {
4169 sprintf(buf, "%u", min_level);
4170 if (must_be_qualified) {
4171 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4173 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4175 } else if (min_level == 0) {
4176 if (must_be_qualified) {
4177 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4179 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4182 if (must_be_qualified) {
4183 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4185 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4191 nickserv_db_cleanup(void)
4193 unreg_del_user_func(nickserv_remove_user);
4194 userList_clean(&curr_helpers);
4195 policer_params_delete(nickserv_conf.auth_policer_params);
4196 dict_delete(nickserv_handle_dict);
4197 dict_delete(nickserv_nick_dict);
4198 dict_delete(nickserv_opt_dict);
4199 dict_delete(nickserv_allow_auth_dict);
4200 dict_delete(nickserv_email_dict);
4201 dict_delete(nickserv_id_dict);
4202 dict_delete(nickserv_conf.weak_password_dict);
4203 free(auth_func_list);
4204 free(unreg_func_list);
4206 free(allowauth_func_list);
4207 free(handle_merge_func_list);
4208 free(failpw_func_list);
4209 if (nickserv_conf.valid_handle_regex_set)
4210 regfree(&nickserv_conf.valid_handle_regex);
4211 if (nickserv_conf.valid_nick_regex_set)
4212 regfree(&nickserv_conf.valid_nick_regex);
4216 init_nickserv(const char *nick)
4219 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4220 reg_new_user_func(check_user_nick);
4221 reg_nick_change_func(handle_nick_change);
4222 reg_del_user_func(nickserv_remove_user);
4223 reg_account_func(handle_account);
4225 /* set up handle_inverse_flags */
4226 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4227 for (i=0; handle_flags[i]; i++) {
4228 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4229 flag_access_levels[i] = 0;
4232 conf_register_reload(nickserv_conf_read);
4233 nickserv_opt_dict = dict_new();
4234 nickserv_email_dict = dict_new();
4235 dict_set_free_keys(nickserv_email_dict, free);
4236 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4238 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4239 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4240 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4241 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4242 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4243 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4244 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4245 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4246 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4247 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4248 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4249 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4250 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4251 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4252 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4253 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4254 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4255 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4256 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4257 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4258 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4259 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4260 if (!nickserv_conf.disable_nicks) {
4261 /* nick management commands */
4262 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4263 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4264 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4265 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4266 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4267 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4269 if (nickserv_conf.email_enabled) {
4270 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4271 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4272 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4273 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4274 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
4275 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4277 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4278 /* miscellaneous commands */
4279 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4280 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4281 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4282 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4283 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4284 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
4286 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4287 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4288 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4289 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4290 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4291 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4292 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4293 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4294 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4295 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4296 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4297 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4298 if (nickserv_conf.titlehost_suffix) {
4299 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4300 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4301 dict_insert(nickserv_opt_dict, "FAKEIDENT", opt_fakeident);
4303 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4304 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4305 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4306 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
4308 nickserv_handle_dict = dict_new();
4309 dict_set_free_keys(nickserv_handle_dict, free);
4310 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4312 nickserv_id_dict = dict_new();
4313 dict_set_free_keys(nickserv_id_dict, free);
4315 nickserv_nick_dict = dict_new();
4316 dict_set_free_data(nickserv_nick_dict, free);
4318 nickserv_allow_auth_dict = dict_new();
4320 userList_init(&curr_helpers);
4323 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4324 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4325 nickserv_service = service_register(nickserv);
4327 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4328 reg_exit_func(nickserv_db_cleanup);
4329 if(nickserv_conf.handle_expire_frequency)
4330 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4331 message_register_table(msgtab);