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 regex_t valid_handle_regex;
391 regex_t valid_nick_regex;
392 dict_t weak_password_dict;
393 struct policer_params *auth_policer_params;
394 enum reclaim_action reclaim_action;
395 enum reclaim_action auto_reclaim_action;
396 enum handle_ts_mode handle_ts_mode;
397 unsigned long auto_reclaim_delay;
398 unsigned char default_maxlogins;
399 unsigned char hard_maxlogins;
400 unsigned long ounregister_inactive;
401 unsigned long ounregister_flags;
404 const char *titlehost_suffix = NULL;
406 /* We have 2^32 unique account IDs to use. */
407 unsigned long int highest_id = 0;
409 #define WALK_NOTES(HANDLE, PREV, NOTE) \
410 for (PREV = NULL, NOTE = (HANDLE)->notes; NOTE != NULL; PREV = NOTE, NOTE = NOTE->next) \
411 if (NOTE->expires && NOTE->expires < now) { \
412 if (PREV) PREV->next = NOTE->next; else (HANDLE)->notes = NOTE->next; \
414 if (!(NOTE = PREV ? PREV : (HANDLE)->notes)) break; \
418 canonicalize_hostmask(char *mask)
420 char *out = mask, *temp;
421 if ((temp = strchr(mask, '!'))) {
423 while (*temp) *out++ = *temp++;
429 static struct handle_info *
430 register_handle(const char *handle, const char *passwd, unsigned long id)
432 struct handle_info *hi;
434 char id_base64[IDLEN + 1];
437 /* Assign a unique account ID to the account; note that 0 is
438 an invalid account ID. 1 is therefore the first account ID. */
440 id = 1 + highest_id++;
442 /* Note: highest_id is and must always be the highest ID. */
443 if (id > highest_id) {
447 inttobase64(id_base64, id, IDLEN);
449 /* Make sure an account with the same ID doesn't exist. If a
450 duplicate is found, log some details and assign a new one.
451 This should be impossible, but it never hurts to expect it. */
452 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
453 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
458 hi = calloc(1, sizeof(*hi));
459 hi->userlist_style = HI_DEFAULT_STYLE;
460 hi->handle = strdup(handle);
461 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
463 dict_insert(nickserv_handle_dict, hi->handle, hi);
466 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
472 register_nick(const char *nick, struct handle_info *owner)
474 struct nick_info *ni;
475 ni = malloc(sizeof(struct nick_info));
476 safestrncpy(ni->nick, nick, sizeof(ni->nick));
478 ni->next = owner->nicks;
480 dict_insert(nickserv_nick_dict, ni->nick, ni);
484 delete_nick(struct nick_info *ni)
486 struct nick_info *last, *next;
487 struct userNode *user;
488 /* Check to see if we should mark a user as unregistered. */
489 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
490 user->modes &= ~FLAGS_REGNICK;
493 /* Remove ni from the nick_info linked list. */
494 if (ni == ni->owner->nicks) {
495 ni->owner->nicks = ni->next;
497 last = ni->owner->nicks;
503 last->next = next->next;
505 dict_remove(nickserv_nick_dict, ni->nick);
508 static unreg_func_t *unreg_func_list;
509 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
512 reg_unreg_func(unreg_func_t func)
514 if (unreg_func_used == unreg_func_size) {
515 if (unreg_func_size) {
516 unreg_func_size <<= 1;
517 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
520 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
523 unreg_func_list[unreg_func_used++] = func;
527 nickserv_free_cookie(void *data)
529 struct handle_cookie *cookie = data;
530 if (cookie->hi) cookie->hi->cookie = NULL;
531 if (cookie->data) free(cookie->data);
536 free_handle_info(void *vhi)
538 struct handle_info *hi = vhi;
541 inttobase64(id, hi->id, IDLEN);
542 dict_remove(nickserv_id_dict, id);
544 free_string_list(hi->masks);
548 delete_nick(hi->nicks);
554 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
555 nickserv_free_cookie(hi->cookie);
558 struct handle_note *note = hi->notes;
559 hi->notes = note->next;
562 if (hi->email_addr) {
563 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
564 handle_info_list_remove(hil, hi);
566 dict_remove(nickserv_email_dict, hi->email_addr);
571 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
574 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
578 for (n=0; n<unreg_func_used; n++)
579 unreg_func_list[n](notify, hi);
581 set_user_handle_info(hi->users, NULL, 0);
583 if (nickserv_conf.disable_nicks)
584 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
586 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
588 dict_remove(nickserv_handle_dict, hi->handle);
592 get_handle_info(const char *handle)
594 return dict_find(nickserv_handle_dict, handle, 0);
598 get_nick_info(const char *nick)
600 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
604 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
609 for (nn=0; nn<channel->members.used; ++nn) {
610 mn = channel->members.list[nn];
611 if ((mn->user != except) && (mn->user->handle_info == handle))
618 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
619 if (!user->handle_info) {
621 send_message(user, bot, "MSG_AUTHENTICATE");
625 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
627 send_message(user, bot, "NSMSG_NO_ACCESS");
631 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
633 send_message(user, bot, "MSG_OPER_SUSPENDED");
637 if (user->handle_info->opserv_level < min_level) {
639 send_message(user, bot, "NSMSG_NO_ACCESS");
647 is_valid_handle(const char *handle)
649 struct userNode *user;
650 /* cant register a juped nick/service nick as handle, to prevent confusion */
651 user = GetUserH(handle);
652 if (user && IsLocal(user))
654 /* check against maximum length */
655 if (strlen(handle) > NICKSERV_HANDLE_LEN)
657 /* for consistency, only allow account names that could be nicks */
658 if (!is_valid_nick(handle))
660 /* disallow account names that look like bad words */
661 if (opserv_bad_channel(handle))
663 /* test either regex or containing all valid chars */
664 if (nickserv_conf.valid_handle_regex_set) {
665 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
668 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
669 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
673 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
678 is_registerable_nick(const char *nick)
680 /* make sure it could be used as an account name */
681 if (!is_valid_handle(nick))
684 if (strlen(nick) > NICKLEN)
686 /* test either regex or as valid handle */
687 if (nickserv_conf.valid_nick_regex_set) {
688 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
691 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
692 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
700 is_valid_email_addr(const char *email)
702 return strchr(email, '@') != NULL;
706 visible_email_addr(struct userNode *user, struct handle_info *hi)
708 if (hi->email_addr) {
709 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
710 return hi->email_addr;
720 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
722 struct handle_info *hi;
723 struct userNode *target;
727 if (!(hi = get_handle_info(++name))) {
728 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
733 if (!(target = GetUserH(name))) {
734 send_message(user, service, "MSG_NICK_UNKNOWN", name);
737 if (IsLocal(target)) {
738 if (IsService(target))
739 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
741 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
744 if (!(hi = target->handle_info)) {
745 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
753 oper_outranks(struct userNode *user, struct handle_info *hi) {
754 if (user->handle_info->opserv_level > hi->opserv_level)
756 if (user->handle_info->opserv_level == hi->opserv_level) {
757 if ((user->handle_info->opserv_level == 1000)
758 || (user->handle_info == hi)
759 || ((user->handle_info->opserv_level == 0)
760 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
761 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
765 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
769 static struct handle_info *
770 get_victim_oper(struct userNode *user, const char *target)
772 struct handle_info *hi;
773 if (!(hi = smart_get_handle_info(nickserv, user, target)))
775 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
776 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
779 return oper_outranks(user, hi) ? hi : NULL;
783 valid_user_for(struct userNode *user, struct handle_info *hi)
787 /* If no hostmasks on the account, allow it. */
788 if (!hi->masks->used)
790 /* If any hostmask matches, allow it. */
791 for (ii=0; ii<hi->masks->used; ii++)
792 if (user_matches_glob(user, hi->masks->list[ii], 0))
794 /* If they are allowauthed to this account, allow it (removing the aa). */
795 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
796 dict_remove(nickserv_allow_auth_dict, user->nick);
799 /* The user is not allowed to use this account. */
804 is_secure_password(const char *handle, const char *pass, struct userNode *user)
807 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
811 if (len < nickserv_conf.password_min_length) {
813 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
816 if (!irccasecmp(pass, handle)) {
818 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
821 dict_find(nickserv_conf.weak_password_dict, pass, &p);
824 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
827 for (i=0; i<len; i++) {
828 if (isdigit(pass[i]))
830 if (isupper(pass[i]))
832 if (islower(pass[i]))
835 if ((cnt_lower < nickserv_conf.password_min_lower)
836 || (cnt_upper < nickserv_conf.password_min_upper)
837 || (cnt_digits < nickserv_conf.password_min_digits)) {
839 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
845 static auth_func_t *auth_func_list;
846 static unsigned int auth_func_size = 0, auth_func_used = 0;
849 reg_auth_func(auth_func_t func)
851 if (auth_func_used == auth_func_size) {
852 if (auth_func_size) {
853 auth_func_size <<= 1;
854 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
857 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
860 auth_func_list[auth_func_used++] = func;
863 static handle_rename_func_t *rf_list;
864 static unsigned int rf_list_size, rf_list_used;
867 reg_handle_rename_func(handle_rename_func_t func)
869 if (rf_list_used == rf_list_size) {
872 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
875 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
878 rf_list[rf_list_used++] = func;
882 generate_fakehost(struct handle_info *handle)
884 extern const char *hidden_host_suffix;
885 static char buffer[HOSTLEN+1];
887 if (!handle->fakehost) {
888 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
890 } else if (handle->fakehost[0] == '.') {
891 /* A leading dot indicates the stored value is actually a title. */
892 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, titlehost_suffix);
895 return handle->fakehost;
899 generate_fakeident(struct handle_info *handle, struct userNode *user)
901 static char buffer[USERLEN+1];
903 if (!handle->fakeident) {
906 safestrncpy(buffer, user->ident, sizeof(buffer));
909 return handle->fakeident;
913 apply_fakehost(struct handle_info *handle, struct userNode *user)
915 struct userNode *target;
916 char *fakehost, *fakeident;
921 fakehost = generate_fakehost(handle);
924 fakeident = generate_fakeident(handle, user);
925 assign_fakehost(user, fakehost, fakeident, 0, 1);
929 for (target = handle->users; target; target = target->next_authed) {
930 fakeident = generate_fakeident(handle, target);
931 assign_fakehost(target, fakehost, fakeident, 0, 1);
936 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
939 struct handle_info *old_info;
941 /* This can happen if somebody uses COOKIE while authed, or if
942 * they re-auth to their current handle (which is silly, but users
944 if (user->handle_info == hi)
947 if (user->handle_info) {
948 struct userNode *other;
951 userList_remove(&curr_helpers, user);
953 /* remove from next_authed linked list */
954 if (user->handle_info->users == user) {
955 user->handle_info->users = user->next_authed;
956 } else if (user->handle_info->users != NULL) {
957 for (other = user->handle_info->users;
958 other->next_authed != user;
959 other = other->next_authed) ;
960 other->next_authed = user->next_authed;
962 /* No users authed to the account - can happen if they get
963 * killed for authing. */
965 /* if nobody left on old handle, and they're not an oper, remove !god */
966 if (!user->handle_info->users && !user->handle_info->opserv_level)
967 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
968 /* record them as being last seen at this time */
969 user->handle_info->lastseen = now;
970 /* and record their hostmask */
971 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
973 old_info = user->handle_info;
974 user->handle_info = hi;
975 if (hi && !hi->users && !hi->opserv_level)
976 HANDLE_CLEAR_FLAG(hi, HELPING);
977 for (n=0; n<auth_func_used; n++) {
978 auth_func_list[n](user, old_info);
983 struct nick_info *ni;
985 HANDLE_CLEAR_FLAG(hi, FROZEN);
986 if (nickserv_conf.warn_clone_auth) {
987 struct userNode *other;
988 for (other = hi->users; other; other = other->next_authed)
989 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
991 user->next_authed = hi->users;
994 if (IsHelper(user) && !userList_contains(&curr_helpers, user))
995 userList_append(&curr_helpers, user);
997 if (hi->fakehost || hi->fakeident || old_info)
998 apply_fakehost(hi, user);
1001 if (!nickserv_conf.disable_nicks) {
1002 struct nick_info *ni2;
1003 for (ni2 = hi->nicks; ni2; ni2 = ni2->next) {
1004 if (!irccasecmp(user->nick, ni2->nick)) {
1005 user->modes |= FLAGS_REGNICK;
1010 StampUser(user, hi->handle, hi->registered, hi->id);
1013 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
1014 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
1016 /* We cannot clear the user's account ID, unfortunately. */
1017 user->next_authed = NULL;
1021 static struct handle_info*
1022 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
1024 struct handle_info *hi;
1025 struct nick_info *ni;
1026 char crypted[MD5_CRYPT_LENGTH];
1028 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
1029 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
1033 if (!is_secure_password(handle, passwd, user))
1036 cryptpass(passwd, crypted);
1037 hi = register_handle(handle, crypted, 0);
1038 hi->masks = alloc_string_list(1);
1040 hi->language = lang_C;
1041 hi->registered = now;
1043 hi->flags = HI_DEFAULT_FLAGS;
1044 if (settee && !no_auth)
1045 set_user_handle_info(settee, hi, 1);
1048 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
1049 else if (nickserv_conf.disable_nicks)
1050 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
1051 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
1052 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
1054 register_nick(user->nick, hi);
1055 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
1057 if (settee && (user != settee))
1058 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
1063 nickserv_bake_cookie(struct handle_cookie *cookie)
1065 cookie->hi->cookie = cookie;
1066 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
1070 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
1072 struct handle_cookie *cookie;
1073 char subject[128], body[4096], *misc;
1074 const char *netname, *fmt;
1078 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1082 cookie = calloc(1, sizeof(*cookie));
1084 cookie->type = type;
1085 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1086 cookie->expires = now + nickserv_conf.cookie_timeout;
1087 inttobase64(cookie->cookie, rand(), 5);
1088 inttobase64(cookie->cookie+5, rand(), 5);
1090 netname = nickserv_conf.network_name;
1093 switch (cookie->type) {
1095 hi->passwd[0] = 0; /* invalidate password */
1096 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1097 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1098 snprintf(subject, sizeof(subject), fmt, netname);
1099 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1100 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1103 case PASSWORD_CHANGE:
1104 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1105 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1106 snprintf(subject, sizeof(subject), fmt, netname);
1107 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1108 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1111 misc = hi->email_addr;
1112 hi->email_addr = cookie->data;
1114 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1115 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1116 snprintf(subject, sizeof(subject), fmt, netname);
1117 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1118 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1119 mail_send(nickserv, hi, subject, body, 1);
1120 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1121 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1123 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1124 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1125 snprintf(subject, sizeof(subject), fmt, netname);
1126 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1127 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1128 mail_send(nickserv, hi, subject, body, 1);
1131 hi->email_addr = misc;
1134 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1135 snprintf(subject, sizeof(subject), fmt, netname);
1136 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1137 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1138 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1141 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1145 mail_send(nickserv, hi, subject, body, first_time);
1146 nickserv_bake_cookie(cookie);
1150 nickserv_eat_cookie(struct handle_cookie *cookie)
1152 cookie->hi->cookie = NULL;
1153 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1154 nickserv_free_cookie(cookie);
1158 nickserv_free_email_addr(void *data)
1160 handle_info_list_clean(data);
1165 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1167 struct handle_info_list *hil;
1168 /* Remove from old handle_info_list ... */
1169 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1170 handle_info_list_remove(hil, hi);
1171 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1172 hi->email_addr = NULL;
1174 /* Add to the new list.. */
1175 if (new_email_addr) {
1176 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1177 hil = calloc(1, sizeof(*hil));
1178 hil->tag = strdup(new_email_addr);
1179 handle_info_list_init(hil);
1180 dict_insert(nickserv_email_dict, hil->tag, hil);
1182 handle_info_list_append(hil, hi);
1183 hi->email_addr = hil->tag;
1187 static NICKSERV_FUNC(cmd_register)
1190 struct handle_info *hi;
1191 const char *email_addr, *password;
1194 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1195 /* Require the first handle registered to belong to someone +o. */
1196 reply("NSMSG_REQUIRE_OPER");
1200 if (user->handle_info) {
1201 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1205 if (IsRegistering(user)) {
1206 reply("NSMSG_ALREADY_REGISTERING");
1210 if (IsStamped(user)) {
1211 /* Unauthenticated users might still have been stamped
1212 previously and could therefore have a hidden host;
1213 do not allow them to register a new account. */
1214 reply("NSMSG_STAMPED_REGISTER");
1218 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1220 if (!is_valid_handle(argv[1])) {
1221 reply("NSMSG_BAD_HANDLE", argv[1]);
1225 if ((argc >= 4) && nickserv_conf.email_enabled) {
1226 struct handle_info_list *hil;
1229 /* Remember email address. */
1230 email_addr = argv[3];
1232 /* Check that the email address looks valid.. */
1233 if (!is_valid_email_addr(email_addr)) {
1234 reply("NSMSG_BAD_EMAIL_ADDR");
1238 /* .. and that we are allowed to send to it. */
1239 if ((str = mail_prohibited_address(email_addr))) {
1240 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1244 /* If we do email verify, make sure we don't spam the address. */
1245 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1247 for (nn=0; nn<hil->used; nn++) {
1248 if (hil->list[nn]->cookie) {
1249 reply("NSMSG_EMAIL_UNACTIVATED");
1253 if (hil->used >= nickserv_conf.handles_per_email) {
1254 reply("NSMSG_EMAIL_OVERUSED");
1267 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1269 /* Add any masks they should get. */
1270 if (nickserv_conf.default_hostmask) {
1271 string_list_append(hi->masks, strdup("*@*"));
1273 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1274 if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
1275 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1278 /* If they're the first to register, give them level 1000. */
1279 if (dict_size(nickserv_handle_dict) == 1) {
1280 hi->opserv_level = 1000;
1281 reply("NSMSG_ROOT_HANDLE", argv[1]);
1284 /* Set their email address. */
1286 nickserv_set_email_addr(hi, email_addr);
1288 /* If they need to do email verification, tell them. */
1290 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1292 /* Set registering flag.. */
1293 user->modes |= FLAGS_REGISTERING;
1298 static NICKSERV_FUNC(cmd_oregister)
1301 struct userNode *settee;
1302 struct handle_info *hi;
1304 NICKSERV_MIN_PARMS(3);
1306 if (!is_valid_handle(argv[1])) {
1307 reply("NSMSG_BAD_HANDLE", argv[1]);
1314 } else if (strchr(argv[3], '@')) {
1315 mask = canonicalize_hostmask(strdup(argv[3]));
1317 settee = GetUserH(argv[4]);
1319 reply("MSG_NICK_UNKNOWN", argv[4]);
1326 } else if ((settee = GetUserH(argv[3]))) {
1327 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1329 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1332 if (settee && settee->handle_info) {
1333 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1337 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1342 string_list_append(hi->masks, mask);
1346 static NICKSERV_FUNC(cmd_handleinfo)
1349 unsigned int i, pos=0, herelen;
1350 struct userNode *target, *next_un;
1351 struct handle_info *hi;
1352 const char *nsmsg_none;
1356 if (!(hi = user->handle_info)) {
1357 reply("NSMSG_MUST_AUTH");
1360 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1364 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1365 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1366 feh = hi->registered;
1367 reply("NSMSG_HANDLEINFO_REGGED", ctime(&feh));
1370 intervalString(buff, now - hi->lastseen, user->handle_info);
1371 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1373 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1376 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1377 if (HANDLE_FLAGGED(hi, FROZEN))
1378 reply("NSMSG_HANDLEINFO_VACATION");
1380 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1381 struct do_not_register *dnr;
1382 if ((dnr = chanserv_is_dnr(NULL, hi)))
1383 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1384 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
1386 } else if (hi != user->handle_info)
1390 reply("NSMSG_HANDLEINFO_KARMA", hi->karma);
1392 if (nickserv_conf.email_enabled)
1393 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1397 switch (hi->cookie->type) {
1398 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1399 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1400 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1401 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1402 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1407 if (oper_has_access(user, cmd->parent->bot, 601, 1))
1408 reply("NSMSG_HANDLEINFO_ID", hi->id);
1410 if (oper_has_access(user, cmd->parent->bot, 0, 1) || IsStaff(user)) {
1412 reply("NSMSG_HANDLEINFO_NO_NOTES");
1414 struct handle_note *prev, *note;
1416 WALK_NOTES(hi, prev, note) {
1417 char set_time[INTERVALLEN];
1418 intervalString(set_time, now - note->set, user->handle_info);
1419 if (note->expires) {
1420 char exp_time[INTERVALLEN];
1421 intervalString(exp_time, note->expires - now, user->handle_info);
1422 reply("NSMSG_HANDLEINFO_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1424 reply("NSMSG_HANDLEINFO_NOTE", note->id, set_time, note->setter, note->note);
1431 unsigned long flen = 1;
1432 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1434 for (i=0, flen=1; handle_flags[i]; i++)
1435 if (hi->flags & 1 << i)
1436 flags[flen++] = handle_flags[i];
1438 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1440 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1443 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1444 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1445 || (hi->opserv_level > 0)) {
1446 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1449 if (hi->fakeident && hi->fakehost)
1450 reply("NSMSG_HANDLEINFO_FAKEIDENTHOST", hi->fakeident, hi->fakehost);
1451 else if (hi->fakeident)
1452 reply("NSMSG_HANDLEINFO_FAKEIDENT", hi->fakeident);
1453 else if (hi->fakehost)
1454 reply("NSMSG_HANDLEINFO_FAKEHOST", hi->fakehost);
1456 if (hi->last_quit_host[0])
1457 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1459 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1461 if (nickserv_conf.disable_nicks) {
1462 /* nicks disabled; don't show anything about registered nicks */
1463 } else if (hi->nicks) {
1464 struct nick_info *ni, *next_ni;
1465 for (ni = hi->nicks; ni; ni = next_ni) {
1466 herelen = strlen(ni->nick);
1467 if (pos + herelen + 1 > ArrayLength(buff)) {
1469 goto print_nicks_buff;
1473 memcpy(buff+pos, ni->nick, herelen);
1474 pos += herelen; buff[pos++] = ' ';
1478 reply("NSMSG_HANDLEINFO_NICKS", buff);
1483 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1486 if (hi->masks->used) {
1487 for (i=0; i < hi->masks->used; i++) {
1488 herelen = strlen(hi->masks->list[i]);
1489 if (pos + herelen + 1 > ArrayLength(buff)) {
1491 goto print_mask_buff;
1493 memcpy(buff+pos, hi->masks->list[i], herelen);
1494 pos += herelen; buff[pos++] = ' ';
1495 if (i+1 == hi->masks->used) {
1498 reply("NSMSG_HANDLEINFO_MASKS", buff);
1503 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1507 struct userData *chan, *next;
1510 for (chan = hi->channels; chan; chan = next) {
1511 next = chan->u_next;
1512 name = chan->channel->channel->name;
1513 herelen = strlen(name);
1514 if (pos + herelen + 7 > ArrayLength(buff)) {
1516 goto print_chans_buff;
1518 if (IsUserSuspended(chan))
1520 pos += sprintf(buff+pos, "%d:%s ", chan->access, name);
1524 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1529 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1532 for (target = hi->users; target; target = next_un) {
1533 herelen = strlen(target->nick);
1534 if (pos + herelen + 1 > ArrayLength(buff)) {
1536 goto print_cnick_buff;
1538 next_un = target->next_authed;
1540 memcpy(buff+pos, target->nick, herelen);
1541 pos += herelen; buff[pos++] = ' ';
1545 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1550 return 1 | ((hi != user->handle_info) ? CMD_LOG_STAFF : 0);
1553 static NICKSERV_FUNC(cmd_userinfo)
1555 struct userNode *target;
1557 NICKSERV_MIN_PARMS(2);
1558 if (!(target = GetUserH(argv[1]))) {
1559 reply("MSG_NICK_UNKNOWN", argv[1]);
1562 if (target->handle_info)
1563 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1565 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1569 static NICKSERV_FUNC(cmd_nickinfo)
1571 struct nick_info *ni;
1573 NICKSERV_MIN_PARMS(2);
1574 if (!(ni = get_nick_info(argv[1]))) {
1575 reply("MSG_NICK_UNKNOWN", argv[1]);
1578 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1582 static NICKSERV_FUNC(cmd_notes)
1584 struct handle_info *hi;
1585 struct handle_note *prev, *note;
1588 NICKSERV_MIN_PARMS(2);
1589 if (!(hi = get_victim_oper(user, argv[1])))
1592 WALK_NOTES(hi, prev, note) {
1593 char set_time[INTERVALLEN];
1594 intervalString(set_time, now - note->set, user->handle_info);
1595 if (note->expires) {
1596 char exp_time[INTERVALLEN];
1597 intervalString(exp_time, note->expires - now, user->handle_info);
1598 reply("NSMSG_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1600 reply("NSMSG_NOTE", note->id, set_time, note->setter, note->note);
1604 reply("NSMSG_NOTE_COUNT", hits, argv[1]);
1608 static NICKSERV_FUNC(cmd_rename_handle)
1610 struct handle_info *hi;
1611 char msgbuf[MAXLEN], *old_handle;
1614 NICKSERV_MIN_PARMS(3);
1615 if (!(hi = get_victim_oper(user, argv[1])))
1617 if (!is_valid_handle(argv[2])) {
1618 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1621 if (get_handle_info(argv[2])) {
1622 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1625 if (hi->fakehost && hi->fakehost[0] == '.' &&
1626 (strlen(argv[2]) + strlen(hi->fakehost+1) +
1627 strlen(titlehost_suffix) + 2) > HOSTLEN) {
1628 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED_RENAME");
1632 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1633 hi->handle = strdup(argv[2]);
1634 dict_insert(nickserv_handle_dict, hi->handle, hi);
1635 for (nn=0; nn<rf_list_used; nn++)
1636 rf_list[nn](hi, old_handle);
1637 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1638 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1639 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1644 static failpw_func_t *failpw_func_list;
1645 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1648 reg_failpw_func(failpw_func_t func)
1650 if (failpw_func_used == failpw_func_size) {
1651 if (failpw_func_size) {
1652 failpw_func_size <<= 1;
1653 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1655 failpw_func_size = 8;
1656 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1659 failpw_func_list[failpw_func_used++] = func;
1662 static NICKSERV_FUNC(cmd_auth)
1664 int pw_arg, used, maxlogins;
1665 struct handle_info *hi;
1667 struct userNode *other;
1669 if (user->handle_info) {
1670 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1673 if (IsStamped(user)) {
1674 /* Unauthenticated users might still have been stamped
1675 previously and could therefore have a hidden host;
1676 do not allow them to authenticate. */
1677 reply("NSMSG_STAMPED_AUTH");
1681 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1683 } else if (argc == 2) {
1684 if (nickserv_conf.disable_nicks) {
1685 if (!(hi = get_handle_info(user->nick))) {
1686 reply("NSMSG_HANDLE_NOT_FOUND");
1690 /* try to look up their handle from their nick */
1691 struct nick_info *ni;
1692 ni = get_nick_info(user->nick);
1694 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1701 reply("MSG_MISSING_PARAMS", argv[0]);
1702 svccmd_send_help(user, nickserv, cmd);
1706 reply("NSMSG_HANDLE_NOT_FOUND");
1709 /* Responses from here on look up the language used by the handle they asked about. */
1710 passwd = argv[pw_arg];
1711 if (!valid_user_for(user, hi)) {
1712 if (hi->email_addr && nickserv_conf.email_enabled)
1713 send_message_type(4, user, cmd->parent->bot,
1714 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1717 send_message_type(4, user, cmd->parent->bot,
1718 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1720 argv[pw_arg] = "BADMASK";
1723 if (!checkpass(passwd, hi->passwd)) {
1725 send_message_type(4, user, cmd->parent->bot,
1726 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1727 argv[pw_arg] = "BADPASS";
1728 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1729 if (nickserv_conf.autogag_enabled) {
1730 if (!user->auth_policer.params) {
1731 user->auth_policer.last_req = now;
1732 user->auth_policer.params = nickserv_conf.auth_policer_params;
1734 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1736 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1737 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1738 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1740 argv[pw_arg] = "GAGGED";
1745 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1746 send_message_type(4, user, cmd->parent->bot,
1747 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1748 argv[pw_arg] = "SUSPENDED";
1751 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1752 for (used = 0, other = hi->users; other; other = other->next_authed) {
1753 if (++used >= maxlogins) {
1754 send_message_type(4, user, cmd->parent->bot,
1755 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1757 argv[pw_arg] = "MAXLOGINS";
1762 set_user_handle_info(user, hi, 1);
1763 if (nickserv_conf.email_required && !hi->email_addr)
1764 reply("NSMSG_PLEASE_SET_EMAIL");
1765 if (!is_secure_password(hi->handle, passwd, NULL))
1766 reply("NSMSG_WEAK_PASSWORD");
1767 if (hi->passwd[0] != '$')
1768 cryptpass(passwd, hi->passwd);
1769 if (!hi->masks->used) {
1771 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1772 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
1773 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1775 argv[pw_arg] = "****";
1776 reply("NSMSG_AUTH_SUCCESS");
1780 static allowauth_func_t *allowauth_func_list;
1781 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1784 reg_allowauth_func(allowauth_func_t func)
1786 if (allowauth_func_used == allowauth_func_size) {
1787 if (allowauth_func_size) {
1788 allowauth_func_size <<= 1;
1789 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1791 allowauth_func_size = 8;
1792 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1795 allowauth_func_list[allowauth_func_used++] = func;
1798 static NICKSERV_FUNC(cmd_allowauth)
1800 struct userNode *target;
1801 struct handle_info *hi;
1804 NICKSERV_MIN_PARMS(2);
1805 if (!(target = GetUserH(argv[1]))) {
1806 reply("MSG_NICK_UNKNOWN", argv[1]);
1809 if (target->handle_info) {
1810 reply("NSMSG_USER_PREV_AUTH", target->nick);
1813 if (IsStamped(target)) {
1814 /* Unauthenticated users might still have been stamped
1815 previously and could therefore have a hidden host;
1816 do not allow them to authenticate to an account. */
1817 reply("NSMSG_USER_PREV_STAMP", target->nick);
1822 else if (!(hi = get_handle_info(argv[2]))) {
1823 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1827 if (hi->opserv_level > user->handle_info->opserv_level) {
1828 reply("MSG_USER_OUTRANKED", hi->handle);
1831 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1832 || (hi->opserv_level > 0))
1833 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1834 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1837 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1838 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1839 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1840 if (nickserv_conf.email_enabled)
1841 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1843 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1844 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1846 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1848 for (n=0; n<allowauth_func_used; n++)
1849 allowauth_func_list[n](user, target, hi);
1853 static NICKSERV_FUNC(cmd_authcookie)
1855 struct handle_info *hi;
1857 NICKSERV_MIN_PARMS(2);
1858 if (user->handle_info) {
1859 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1862 if (IsStamped(user)) {
1863 /* Unauthenticated users might still have been stamped
1864 previously and could therefore have a hidden host;
1865 do not allow them to authenticate to an account. */
1866 reply("NSMSG_STAMPED_AUTHCOOKIE");
1869 if (!(hi = get_handle_info(argv[1]))) {
1870 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1873 if (!hi->email_addr) {
1874 reply("MSG_SET_EMAIL_ADDR");
1877 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1881 static NICKSERV_FUNC(cmd_delcookie)
1883 struct handle_info *hi;
1885 hi = user->handle_info;
1887 reply("NSMSG_NO_COOKIE");
1890 switch (hi->cookie->type) {
1893 reply("NSMSG_MUST_TIME_OUT");
1896 nickserv_eat_cookie(hi->cookie);
1897 reply("NSMSG_ATE_COOKIE");
1903 static NICKSERV_FUNC(cmd_odelcookie)
1905 struct handle_info *hi;
1907 NICKSERV_MIN_PARMS(2);
1909 if (!(hi = get_victim_oper(user, argv[1])))
1913 reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle);
1917 nickserv_eat_cookie(hi->cookie);
1918 reply("NSMSG_ATE_COOKIE_FOREIGN", hi->handle);
1923 static NICKSERV_FUNC(cmd_resetpass)
1925 struct handle_info *hi;
1926 char crypted[MD5_CRYPT_LENGTH];
1928 NICKSERV_MIN_PARMS(3);
1929 if (user->handle_info) {
1930 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1933 if (IsStamped(user)) {
1934 /* Unauthenticated users might still have been stamped
1935 previously and could therefore have a hidden host;
1936 do not allow them to activate an account. */
1937 reply("NSMSG_STAMPED_RESETPASS");
1940 if (!(hi = get_handle_info(argv[1]))) {
1941 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1944 if (!hi->email_addr) {
1945 reply("MSG_SET_EMAIL_ADDR");
1948 cryptpass(argv[2], crypted);
1950 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1954 static NICKSERV_FUNC(cmd_cookie)
1956 struct handle_info *hi;
1959 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1962 NICKSERV_MIN_PARMS(3);
1963 if (!(hi = get_handle_info(argv[1]))) {
1964 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1970 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1971 reply("NSMSG_HANDLE_SUSPENDED");
1976 reply("NSMSG_NO_COOKIE");
1980 /* Check validity of operation before comparing cookie to
1981 * prohibit guessing by authed users. */
1982 if (user->handle_info
1983 && (hi->cookie->type != EMAIL_CHANGE)
1984 && (hi->cookie->type != PASSWORD_CHANGE)) {
1985 reply("NSMSG_CANNOT_COOKIE");
1989 if (strcmp(cookie, hi->cookie->cookie)) {
1990 reply("NSMSG_BAD_COOKIE");
1994 switch (hi->cookie->type) {
1996 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1997 set_user_handle_info(user, hi, 1);
1998 reply("NSMSG_HANDLE_ACTIVATED");
2000 case PASSWORD_CHANGE:
2001 set_user_handle_info(user, hi, 1);
2002 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2003 reply("NSMSG_PASSWORD_CHANGED");
2006 nickserv_set_email_addr(hi, hi->cookie->data);
2007 reply("NSMSG_EMAIL_CHANGED");
2010 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2011 set_user_handle_info(user, hi, 1);
2012 nickserv_addmask(user, hi, mask);
2013 reply("NSMSG_AUTH_SUCCESS");
2018 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
2019 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
2023 nickserv_eat_cookie(hi->cookie);
2028 static NICKSERV_FUNC(cmd_oregnick) {
2030 struct handle_info *target;
2031 struct nick_info *ni;
2033 NICKSERV_MIN_PARMS(3);
2034 if (!(target = modcmd_get_handle_info(user, argv[1])))
2037 if (!is_registerable_nick(nick)) {
2038 reply("NSMSG_BAD_NICK", nick);
2041 ni = dict_find(nickserv_nick_dict, nick, NULL);
2043 reply("NSMSG_NICK_EXISTS", nick);
2046 register_nick(nick, target);
2047 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
2051 static NICKSERV_FUNC(cmd_regnick) {
2053 struct nick_info *ni;
2055 if (!is_registerable_nick(user->nick)) {
2056 reply("NSMSG_BAD_NICK", user->nick);
2059 /* count their nicks, see if it's too many */
2060 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
2061 if (n >= nickserv_conf.nicks_per_handle) {
2062 reply("NSMSG_TOO_MANY_NICKS");
2065 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
2067 reply("NSMSG_NICK_EXISTS", user->nick);
2070 register_nick(user->nick, user->handle_info);
2071 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2075 static NICKSERV_FUNC(cmd_pass)
2077 struct handle_info *hi;
2078 const char *old_pass, *new_pass;
2080 NICKSERV_MIN_PARMS(3);
2081 hi = user->handle_info;
2085 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
2086 if (!checkpass(old_pass, hi->passwd)) {
2087 argv[1] = "BADPASS";
2088 reply("NSMSG_PASSWORD_INVALID");
2091 cryptpass(new_pass, hi->passwd);
2093 reply("NSMSG_PASS_SUCCESS");
2098 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
2101 char *new_mask = canonicalize_hostmask(strdup(mask));
2102 for (i=0; i<hi->masks->used; i++) {
2103 if (!irccasecmp(new_mask, hi->masks->list[i])) {
2104 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2109 string_list_append(hi->masks, new_mask);
2110 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2114 static NICKSERV_FUNC(cmd_addmask)
2117 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2118 int res = nickserv_addmask(user, user->handle_info, mask);
2122 if (!is_gline(argv[1])) {
2123 reply("NSMSG_MASK_INVALID", argv[1]);
2126 return nickserv_addmask(user, user->handle_info, argv[1]);
2130 static NICKSERV_FUNC(cmd_oaddmask)
2132 struct handle_info *hi;
2134 NICKSERV_MIN_PARMS(3);
2135 if (!(hi = get_victim_oper(user, argv[1])))
2137 return nickserv_addmask(user, hi, argv[2]);
2141 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2144 for (i=0; i<hi->masks->used; i++) {
2145 if (!strcmp(del_mask, hi->masks->list[i])) {
2146 char *old_mask = hi->masks->list[i];
2147 if (hi->masks->used == 1 && !force) {
2148 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2151 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2152 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2157 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2161 static NICKSERV_FUNC(cmd_delmask)
2163 NICKSERV_MIN_PARMS(2);
2164 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2167 static NICKSERV_FUNC(cmd_odelmask)
2169 struct handle_info *hi;
2170 NICKSERV_MIN_PARMS(3);
2171 if (!(hi = get_victim_oper(user, argv[1])))
2173 return nickserv_delmask(user, hi, argv[2], 1);
2177 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2178 unsigned int nn, add = 1, pos;
2179 unsigned long added, removed, flag;
2181 for (added=removed=nn=0; str[nn]; nn++) {
2183 case '+': add = 1; break;
2184 case '-': add = 0; break;
2186 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2187 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2190 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2191 /* cheesy avoidance of looking up the flag name.. */
2192 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2195 flag = 1 << (pos - 1);
2197 added |= flag, removed &= ~flag;
2199 removed |= flag, added &= ~flag;
2204 *premoved = removed;
2209 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2211 unsigned long before, after, added, removed;
2212 struct userNode *uNode;
2214 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2215 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2217 hi->flags = (hi->flags | added) & ~removed;
2218 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2220 /* Strip helping flag if they're only a support helper and not
2221 * currently in #support. */
2222 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2223 struct channelList *schannels;
2225 schannels = chanserv_support_channels();
2226 for (ii = 0; ii < schannels->used; ++ii)
2227 if (find_handle_in_channel(schannels->list[ii], hi, NULL))
2229 if (ii == schannels->used)
2230 HANDLE_CLEAR_FLAG(hi, HELPING);
2233 if (after && !before) {
2234 /* Add user to current helper list. */
2235 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2236 userList_append(&curr_helpers, uNode);
2237 } else if (!after && before) {
2238 /* Remove user from current helper list. */
2239 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2240 userList_remove(&curr_helpers, uNode);
2247 set_list(struct userNode *user, struct handle_info *hi, int override)
2251 char *set_display[] = {
2252 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2253 "EMAIL", "MAXLOGINS", "LANGUAGE"
2256 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2258 /* Do this so options are presented in a consistent order. */
2259 for (i = 0; i < ArrayLength(set_display); ++i)
2260 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2261 opt(user, hi, override, 0, NULL);
2264 static NICKSERV_FUNC(cmd_set)
2266 struct handle_info *hi;
2269 hi = user->handle_info;
2271 set_list(user, hi, 0);
2274 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2275 reply("NSMSG_INVALID_OPTION", argv[1]);
2278 return opt(user, hi, 0, argc-1, argv+1);
2281 static NICKSERV_FUNC(cmd_oset)
2283 struct handle_info *hi;
2284 struct svccmd *subcmd;
2286 char cmdname[MAXLEN];
2288 NICKSERV_MIN_PARMS(2);
2290 if (!(hi = get_victim_oper(user, argv[1])))
2294 set_list(user, hi, 0);
2298 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2299 reply("NSMSG_INVALID_OPTION", argv[2]);
2303 sprintf(cmdname, "%s %s", cmd->name, argv[2]);
2304 subcmd = dict_find(cmd->parent->commands, cmdname, NULL);
2305 if (subcmd && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, NULL, SVCCMD_NOISY))
2308 return opt(user, hi, 1, argc-2, argv+2);
2311 static OPTION_FUNC(opt_info)
2315 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2317 hi->infoline = NULL;
2319 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2323 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2324 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2328 static OPTION_FUNC(opt_width)
2331 hi->screen_width = strtoul(argv[1], NULL, 0);
2333 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2334 hi->screen_width = MIN_LINE_SIZE;
2335 else if (hi->screen_width > MAX_LINE_SIZE)
2336 hi->screen_width = MAX_LINE_SIZE;
2338 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2342 static OPTION_FUNC(opt_tablewidth)
2345 hi->table_width = strtoul(argv[1], NULL, 0);
2347 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2348 hi->table_width = MIN_LINE_SIZE;
2349 else if (hi->screen_width > MAX_LINE_SIZE)
2350 hi->table_width = MAX_LINE_SIZE;
2352 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2356 static OPTION_FUNC(opt_color)
2359 if (enabled_string(argv[1]))
2360 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2361 else if (disabled_string(argv[1]))
2362 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2364 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2369 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2373 static OPTION_FUNC(opt_privmsg)
2376 if (enabled_string(argv[1]))
2377 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2378 else if (disabled_string(argv[1]))
2379 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2381 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2386 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2390 static OPTION_FUNC(opt_style)
2395 if (!irccasecmp(argv[1], "Zoot"))
2396 hi->userlist_style = HI_STYLE_ZOOT;
2397 else if (!irccasecmp(argv[1], "def"))
2398 hi->userlist_style = HI_STYLE_DEF;
2401 switch (hi->userlist_style) {
2410 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2414 static OPTION_FUNC(opt_password)
2417 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2422 cryptpass(argv[1], hi->passwd);
2424 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2428 static OPTION_FUNC(opt_flags)
2431 unsigned int ii, flen;
2434 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2439 nickserv_apply_flags(user, hi, argv[1]);
2441 for (ii = flen = 0; handle_flags[ii]; ii++)
2442 if (hi->flags & (1 << ii))
2443 flags[flen++] = handle_flags[ii];
2446 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2448 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2452 static OPTION_FUNC(opt_email)
2456 if (!is_valid_email_addr(argv[1])) {
2457 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2460 if ((str = mail_prohibited_address(argv[1]))) {
2461 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2464 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2465 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2467 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2469 nickserv_set_email_addr(hi, argv[1]);
2471 nickserv_eat_cookie(hi->cookie);
2472 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2475 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2479 static OPTION_FUNC(opt_maxlogins)
2481 unsigned char maxlogins;
2483 maxlogins = strtoul(argv[1], NULL, 0);
2484 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2485 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2488 hi->maxlogins = maxlogins;
2490 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2491 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2495 static OPTION_FUNC(opt_language)
2497 struct language *lang;
2499 lang = language_find(argv[1]);
2500 if (irccasecmp(lang->name, argv[1]))
2501 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2502 hi->language = lang;
2504 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2508 static OPTION_FUNC(opt_karma)
2511 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2516 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2517 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2518 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2519 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2521 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2525 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2530 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2531 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2533 if ((user->handle_info->opserv_level < target->opserv_level)
2534 || ((user->handle_info->opserv_level == target->opserv_level)
2535 && (user->handle_info->opserv_level < 1000))) {
2536 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2539 if ((user->handle_info->opserv_level < new_level)
2540 || ((user->handle_info->opserv_level == new_level)
2541 && (user->handle_info->opserv_level < 1000))) {
2542 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2545 if (user->handle_info == target) {
2546 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2549 if (target->opserv_level == new_level)
2551 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2552 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2553 target->opserv_level = new_level;
2557 static OPTION_FUNC(opt_level)
2562 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2566 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2567 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2571 static OPTION_FUNC(opt_epithet)
2574 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2578 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2579 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2582 if ((epithet[0] == '*') && !epithet[1])
2585 hi->epithet = strdup(epithet);
2589 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2591 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2595 static OPTION_FUNC(opt_title)
2600 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2604 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2606 if (strchr(title, '.')) {
2607 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2610 if ((strlen(user->handle_info->handle) + strlen(title) +
2611 strlen(titlehost_suffix) + 2) > HOSTLEN) {
2612 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2617 if (!strcmp(title, "*")) {
2618 hi->fakehost = NULL;
2620 hi->fakehost = malloc(strlen(title)+2);
2621 hi->fakehost[0] = '.';
2622 strcpy(hi->fakehost+1, title);
2624 apply_fakehost(hi, NULL);
2625 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2626 title = hi->fakehost + 1;
2630 title = user_find_message(user, "MSG_NONE");
2631 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2635 static OPTION_FUNC(opt_fakehost)
2637 char mask[USERLEN + HOSTLEN + 2];
2641 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2645 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2646 if(strlen(argv[1]) >= sizeof(mask)) {
2647 send_message(user, nickserv, "NSMSG_FAKEMASK_INVALID", USERLEN + HOSTLEN + 1);
2651 safestrncpy(mask, argv[1], sizeof(mask));
2653 if ((host = strrchr(mask, '@')) && host != mask) {
2654 // If ident@host was used and the user doesn't have access to set idents, do not change anything.
2655 if (!oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
2667 if (ident && strlen(ident) > USERLEN) {
2668 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
2672 if (host && ((strlen(host) > HOSTLEN) || (host[0] == '.'))) {
2673 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2677 if (host && host[0]) {
2679 if (!strcmp(host, "*"))
2680 hi->fakehost = NULL;
2682 hi->fakehost = strdup(host);
2683 host = hi->fakehost;
2686 host = generate_fakehost(hi);
2689 free(hi->fakeident);
2690 if (!strcmp(ident, "*"))
2691 hi->fakeident = NULL;
2693 hi->fakeident = strdup(ident);
2694 ident = hi->fakeident;
2697 ident = generate_fakeident(hi, NULL);
2699 apply_fakehost(hi, NULL);
2701 host = generate_fakehost(hi);
2702 ident = generate_fakeident(hi, NULL);
2705 host = (char *) user_find_message(user, "MSG_NONE");
2707 send_message(user, nickserv, "NSMSG_SET_FAKEIDENTHOST", ident, host);
2709 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", host);
2713 static OPTION_FUNC(opt_fakeident)
2718 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2722 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
2724 if (strlen(ident) > USERLEN) {
2725 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
2728 free(hi->fakeident);
2729 if (!strcmp(ident, "*"))
2730 hi->fakeident = NULL;
2732 hi->fakeident = strdup(ident);
2733 ident = hi->fakeident;
2734 apply_fakehost(hi, NULL);
2736 ident = generate_fakeident(hi, NULL); /* NULL if no fake ident set */
2738 ident = user_find_message(user, "MSG_NONE");
2739 send_message(user, nickserv, "NSMSG_SET_FAKEIDENT", ident);
2743 static NICKSERV_FUNC(cmd_reclaim)
2745 struct handle_info *hi;
2746 struct nick_info *ni;
2747 struct userNode *victim;
2749 NICKSERV_MIN_PARMS(2);
2750 hi = user->handle_info;
2751 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2753 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2756 if (ni->owner != user->handle_info) {
2757 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2760 victim = GetUserH(ni->nick);
2762 reply("MSG_NICK_UNKNOWN", ni->nick);
2765 if (victim == user) {
2766 reply("NSMSG_NICK_USER_YOU");
2769 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2770 switch (nickserv_conf.reclaim_action) {
2771 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2772 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2773 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2774 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2779 static NICKSERV_FUNC(cmd_unregnick)
2782 struct handle_info *hi;
2783 struct nick_info *ni;
2785 hi = user->handle_info;
2786 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2787 ni = dict_find(nickserv_nick_dict, nick, NULL);
2789 reply("NSMSG_UNKNOWN_NICK", nick);
2792 if (hi != ni->owner) {
2793 reply("NSMSG_NOT_YOUR_NICK", nick);
2796 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2801 static NICKSERV_FUNC(cmd_ounregnick)
2803 struct nick_info *ni;
2805 NICKSERV_MIN_PARMS(2);
2806 if (!(ni = get_nick_info(argv[1]))) {
2807 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2810 if (!oper_outranks(user, ni->owner))
2812 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2817 static NICKSERV_FUNC(cmd_unregister)
2819 struct handle_info *hi;
2822 NICKSERV_MIN_PARMS(2);
2823 hi = user->handle_info;
2826 if (checkpass(passwd, hi->passwd)) {
2827 nickserv_unregister_handle(hi, user);
2830 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2831 reply("NSMSG_PASSWORD_INVALID");
2836 static NICKSERV_FUNC(cmd_ounregister)
2838 struct handle_info *hi;
2839 char reason[MAXLEN];
2842 NICKSERV_MIN_PARMS(2);
2843 if (!(hi = get_victim_oper(user, argv[1])))
2846 if (HANDLE_FLAGGED(hi, NODELETE)) {
2847 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
2851 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
2853 ((hi->flags & nickserv_conf.ounregister_flags)
2855 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
2856 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
2860 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
2861 global_message(MESSAGE_RECIPIENT_STAFF, reason);
2862 nickserv_unregister_handle(hi, user);
2866 static NICKSERV_FUNC(cmd_status)
2868 if (nickserv_conf.disable_nicks) {
2869 reply("NSMSG_GLOBAL_STATS_NONICK",
2870 dict_size(nickserv_handle_dict));
2872 if (user->handle_info) {
2874 struct nick_info *ni;
2875 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2876 reply("NSMSG_HANDLE_STATS", cnt);
2878 reply("NSMSG_HANDLE_NONE");
2880 reply("NSMSG_GLOBAL_STATS",
2881 dict_size(nickserv_handle_dict),
2882 dict_size(nickserv_nick_dict));
2887 static NICKSERV_FUNC(cmd_ghost)
2889 struct userNode *target;
2890 char reason[MAXLEN];
2892 NICKSERV_MIN_PARMS(2);
2893 if (!(target = GetUserH(argv[1]))) {
2894 reply("MSG_NICK_UNKNOWN", argv[1]);
2897 if (target == user) {
2898 reply("NSMSG_CANNOT_GHOST_SELF");
2901 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2902 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2905 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2906 DelUser(target, nickserv, 1, reason);
2907 reply("NSMSG_GHOST_KILLED", argv[1]);
2911 static NICKSERV_FUNC(cmd_vacation)
2913 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2914 reply("NSMSG_ON_VACATION");
2918 static NICKSERV_FUNC(cmd_addnote)
2920 struct handle_info *hi;
2921 unsigned long duration;
2924 struct handle_note *prev;
2925 struct handle_note *note;
2927 /* Parse parameters and figure out values for note's fields. */
2928 NICKSERV_MIN_PARMS(4);
2929 hi = get_victim_oper(user, argv[1]);
2932 if(!strcmp(argv[2], "0"))
2934 else if(!(duration = ParseInterval(argv[2])))
2936 reply("MSG_INVALID_DURATION", argv[2]);
2939 if (duration > 2*365*86400) {
2940 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
2943 unsplit_string(argv + 3, argc - 3, text);
2944 WALK_NOTES(hi, prev, note) {}
2945 id = prev ? (prev->id + 1) : 1;
2947 /* Create the new note structure. */
2948 note = calloc(1, sizeof(*note) + strlen(text));
2950 note->expires = duration ? (now + duration) : 0;
2953 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
2954 strcpy(note->note, text);
2959 reply("NSMSG_NOTE_ADDED", id, hi->handle);
2963 static NICKSERV_FUNC(cmd_delnote)
2965 struct handle_info *hi;
2966 struct handle_note *prev;
2967 struct handle_note *note;
2970 NICKSERV_MIN_PARMS(3);
2971 hi = get_victim_oper(user, argv[1]);
2974 id = strtoul(argv[2], NULL, 10);
2975 WALK_NOTES(hi, prev, note) {
2976 if (id == note->id) {
2978 prev->next = note->next;
2980 hi->notes = note->next;
2982 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
2986 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
2991 nickserv_saxdb_write(struct saxdb_context *ctx) {
2993 struct handle_info *hi;
2996 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2998 assert(hi->id != 0);
2999 saxdb_start_record(ctx, iter_key(it), 0);
3001 struct handle_cookie *cookie = hi->cookie;
3004 switch (cookie->type) {
3005 case ACTIVATION: type = KEY_ACTIVATION; break;
3006 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
3007 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
3008 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
3009 default: type = NULL; break;
3012 saxdb_start_record(ctx, KEY_COOKIE, 0);
3013 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
3014 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
3016 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
3017 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
3018 saxdb_end_record(ctx);
3022 struct handle_note *prev, *note;
3023 saxdb_start_record(ctx, KEY_NOTES, 0);
3024 WALK_NOTES(hi, prev, note) {
3025 snprintf(flags, sizeof(flags), "%d", note->id);
3026 saxdb_start_record(ctx, flags, 0);
3028 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
3029 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
3030 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
3031 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
3032 saxdb_end_record(ctx);
3034 saxdb_end_record(ctx);
3037 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
3039 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
3041 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
3043 saxdb_write_string(ctx, KEY_FAKEIDENT, hi->fakeident);
3047 for (ii=flen=0; handle_flags[ii]; ++ii)
3048 if (hi->flags & (1 << ii))
3049 flags[flen++] = handle_flags[ii];
3051 saxdb_write_string(ctx, KEY_FLAGS, flags);
3053 saxdb_write_int(ctx, KEY_ID, hi->id);
3055 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
3056 if (hi->last_quit_host[0])
3057 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
3058 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
3060 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
3061 if (hi->masks->used)
3062 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
3064 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
3066 struct string_list *slist;
3067 struct nick_info *ni;
3069 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
3070 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
3071 saxdb_write_string_list(ctx, KEY_NICKS, slist);
3075 if (hi->opserv_level)
3076 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
3077 if (hi->language != lang_C)
3078 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
3079 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
3080 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
3081 if (hi->screen_width)
3082 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
3083 if (hi->table_width)
3084 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
3085 flags[0] = hi->userlist_style;
3087 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
3088 saxdb_end_record(ctx);
3093 static handle_merge_func_t *handle_merge_func_list;
3094 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
3097 reg_handle_merge_func(handle_merge_func_t func)
3099 if (handle_merge_func_used == handle_merge_func_size) {
3100 if (handle_merge_func_size) {
3101 handle_merge_func_size <<= 1;
3102 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
3104 handle_merge_func_size = 8;
3105 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
3108 handle_merge_func_list[handle_merge_func_used++] = func;
3111 static NICKSERV_FUNC(cmd_merge)
3113 struct handle_info *hi_from, *hi_to;
3114 struct userNode *last_user;
3115 struct userData *cList, *cListNext;
3116 unsigned int ii, jj, n;
3117 char buffer[MAXLEN];
3119 NICKSERV_MIN_PARMS(3);
3121 if (!(hi_from = get_victim_oper(user, argv[1])))
3123 if (!(hi_to = get_victim_oper(user, argv[2])))
3125 if (hi_to == hi_from) {
3126 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
3130 for (n=0; n<handle_merge_func_used; n++)
3131 handle_merge_func_list[n](user, hi_to, hi_from);
3133 /* Append "from" handle's nicks to "to" handle's nick list. */
3135 struct nick_info *last_ni;
3136 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3137 last_ni->next = hi_from->nicks;
3139 while (hi_from->nicks) {
3140 hi_from->nicks->owner = hi_to;
3141 hi_from->nicks = hi_from->nicks->next;
3144 /* Merge the hostmasks. */
3145 for (ii=0; ii<hi_from->masks->used; ii++) {
3146 char *mask = hi_from->masks->list[ii];
3147 for (jj=0; jj<hi_to->masks->used; jj++)
3148 if (match_ircglobs(hi_to->masks->list[jj], mask))
3150 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3151 string_list_append(hi_to->masks, strdup(mask));
3154 /* Merge the lists of authed users. */
3156 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3157 last_user->next_authed = hi_from->users;
3159 hi_to->users = hi_from->users;
3161 /* Repoint the old "from" handle's users. */
3162 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3163 last_user->handle_info = hi_to;
3165 hi_from->users = NULL;
3167 /* Merge channel userlists. */
3168 for (cList=hi_from->channels; cList; cList=cListNext) {
3169 struct userData *cList2;
3170 cListNext = cList->u_next;
3171 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3172 if (cList->channel == cList2->channel)
3174 if (cList2 && (cList2->access >= cList->access)) {
3175 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);
3176 /* keep cList2 in hi_to; remove cList from hi_from */
3177 del_channel_user(cList, 1);
3180 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);
3181 /* remove the lower-ranking cList2 from hi_to */
3182 del_channel_user(cList2, 1);
3184 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3186 /* cList needs to be moved from hi_from to hi_to */
3187 cList->handle = hi_to;
3188 /* Remove from linked list for hi_from */
3189 assert(!cList->u_prev);
3190 hi_from->channels = cList->u_next;
3192 cList->u_next->u_prev = cList->u_prev;
3193 /* Add to linked list for hi_to */
3194 cList->u_prev = NULL;
3195 cList->u_next = hi_to->channels;
3196 if (hi_to->channels)
3197 hi_to->channels->u_prev = cList;
3198 hi_to->channels = cList;
3202 /* Do they get an OpServ level promotion? */
3203 if (hi_from->opserv_level > hi_to->opserv_level)
3204 hi_to->opserv_level = hi_from->opserv_level;
3206 /* What about last seen time? */
3207 if (hi_from->lastseen > hi_to->lastseen)
3208 hi_to->lastseen = hi_from->lastseen;
3210 /* New karma is the sum of the two original karmas. */
3211 hi_to->karma += hi_from->karma;
3213 /* Does a fakehost carry over? (This intentionally doesn't set it
3214 * for users previously attached to hi_to. They'll just have to
3217 if (hi_from->fakehost && !hi_to->fakehost)
3218 hi_to->fakehost = strdup(hi_from->fakehost);
3219 if (hi_from->fakeident && !hi_to->fakeident)
3220 hi_to->fakeident = strdup(hi_from->fakeident);
3222 /* Notify of success. */
3223 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3224 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3225 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3227 /* Unregister the "from" handle. */
3228 nickserv_unregister_handle(hi_from, NULL);
3233 struct nickserv_discrim {
3234 unsigned long flags_on, flags_off;
3235 unsigned long min_registered, max_registered;
3236 unsigned long lastseen;
3238 int min_level, max_level;
3239 int min_karma, max_karma;
3240 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3241 const char *nickmask;
3242 const char *hostmask;
3243 const char *fakehostmask;
3244 const char *fakeidentmask;
3245 const char *handlemask;
3246 const char *emailmask;
3249 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
3251 struct discrim_apply_info {
3252 struct nickserv_discrim *discrim;
3253 discrim_search_func func;
3254 struct userNode *source;
3255 unsigned int matched;
3258 static struct nickserv_discrim *
3259 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3262 struct nickserv_discrim *discrim;
3264 discrim = malloc(sizeof(*discrim));
3265 memset(discrim, 0, sizeof(*discrim));
3266 discrim->min_level = 0;
3267 discrim->max_level = INT_MAX;
3268 discrim->limit = 50;
3269 discrim->min_registered = 0;
3270 discrim->max_registered = ULONG_MAX;
3271 discrim->lastseen = ULONG_MAX;
3272 discrim->min_karma = INT_MIN;
3273 discrim->max_karma = INT_MAX;
3275 for (i=0; i<argc; i++) {
3276 if (i == argc - 1) {
3277 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3280 if (!irccasecmp(argv[i], "limit")) {
3281 discrim->limit = strtoul(argv[++i], NULL, 0);
3282 } else if (!irccasecmp(argv[i], "flags")) {
3283 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3284 } else if (!irccasecmp(argv[i], "registered")) {
3285 const char *cmp = argv[++i];
3286 if (cmp[0] == '<') {
3287 if (cmp[1] == '=') {
3288 discrim->min_registered = now - ParseInterval(cmp+2);
3290 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3292 } else if (cmp[0] == '=') {
3293 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3294 } else if (cmp[0] == '>') {
3295 if (cmp[1] == '=') {
3296 discrim->max_registered = now - ParseInterval(cmp+2);
3298 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3301 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3303 } else if (!irccasecmp(argv[i], "seen")) {
3304 discrim->lastseen = now - ParseInterval(argv[++i]);
3305 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3306 discrim->nickmask = argv[++i];
3307 } else if (!irccasecmp(argv[i], "hostmask")) {
3309 if (!irccasecmp(argv[i], "exact")) {
3310 if (i == argc - 1) {
3311 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3314 discrim->hostmask_type = EXACT;
3315 } else if (!irccasecmp(argv[i], "subset")) {
3316 if (i == argc - 1) {
3317 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3320 discrim->hostmask_type = SUBSET;
3321 } else if (!irccasecmp(argv[i], "superset")) {
3322 if (i == argc - 1) {
3323 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3326 discrim->hostmask_type = SUPERSET;
3327 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3328 if (i == argc - 1) {
3329 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3332 discrim->hostmask_type = LASTQUIT;
3335 discrim->hostmask_type = SUPERSET;
3337 discrim->hostmask = argv[++i];
3338 } else if (!irccasecmp(argv[i], "fakehost")) {
3339 if (!irccasecmp(argv[++i], "*")) {
3340 discrim->fakehostmask = 0;
3342 discrim->fakehostmask = argv[i];
3344 } else if (!irccasecmp(argv[i], "fakeident")) {
3345 if (!irccasecmp(argv[++i], "*")) {
3346 discrim->fakeidentmask = 0;
3348 discrim->fakeidentmask = argv[i];
3350 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3351 if (!irccasecmp(argv[++i], "*")) {
3352 discrim->handlemask = 0;
3354 discrim->handlemask = argv[i];
3356 } else if (!irccasecmp(argv[i], "email")) {
3357 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3358 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3360 } else if (!irccasecmp(argv[++i], "*")) {
3361 discrim->emailmask = 0;
3363 discrim->emailmask = argv[i];
3365 } else if (!irccasecmp(argv[i], "access")) {
3366 const char *cmp = argv[++i];
3367 if (cmp[0] == '<') {
3368 if (discrim->min_level == 0) discrim->min_level = 1;
3369 if (cmp[1] == '=') {
3370 discrim->max_level = strtoul(cmp+2, NULL, 0);
3372 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3374 } else if (cmp[0] == '=') {
3375 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3376 } else if (cmp[0] == '>') {
3377 if (cmp[1] == '=') {
3378 discrim->min_level = strtoul(cmp+2, NULL, 0);
3380 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3383 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3385 } else if (!irccasecmp(argv[i], "karma")) {
3386 const char *cmp = argv[++i];
3387 if (cmp[0] == '<') {
3388 if (cmp[1] == '=') {
3389 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3391 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3393 } else if (cmp[0] == '=') {
3394 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3395 } else if (cmp[0] == '>') {
3396 if (cmp[1] == '=') {
3397 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3399 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3402 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3405 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3416 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3418 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3419 || (discrim->flags_off & hi->flags)
3420 || (discrim->min_registered > hi->registered)
3421 || (discrim->max_registered < hi->registered)
3422 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3423 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3424 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3425 || (discrim->fakeidentmask && (!hi->fakeident || !match_ircglob(hi->fakeident, discrim->fakeidentmask)))
3426 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3427 || (discrim->min_level > hi->opserv_level)
3428 || (discrim->max_level < hi->opserv_level)
3429 || (discrim->min_karma > hi->karma)
3430 || (discrim->max_karma < hi->karma)
3434 if (discrim->hostmask) {
3436 for (i=0; i<hi->masks->used; i++) {
3437 const char *mask = hi->masks->list[i];
3438 if ((discrim->hostmask_type == SUBSET)
3439 && (match_ircglobs(discrim->hostmask, mask))) break;
3440 else if ((discrim->hostmask_type == EXACT)
3441 && !irccasecmp(discrim->hostmask, mask)) break;
3442 else if ((discrim->hostmask_type == SUPERSET)
3443 && (match_ircglobs(mask, discrim->hostmask))) break;
3444 else if ((discrim->hostmask_type == LASTQUIT)
3445 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3447 if (i==hi->masks->used) return 0;
3449 if (discrim->nickmask) {
3450 struct nick_info *nick = hi->nicks;
3452 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3455 if (!nick) return 0;
3461 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3463 dict_iterator_t it, next;
3464 unsigned int matched;
3466 for (it = dict_first(nickserv_handle_dict), matched = 0;
3467 it && (matched < discrim->limit);
3469 next = iter_next(it);
3470 if (nickserv_discrim_match(discrim, iter_data(it))) {
3471 dsf(source, iter_data(it));
3479 search_print_func(struct userNode *source, struct handle_info *match)
3481 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3485 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3490 search_unregister_func (struct userNode *source, struct handle_info *match)
3492 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3493 nickserv_unregister_handle(match, source);
3497 nickserv_sort_accounts_by_access(const void *a, const void *b)
3499 const struct handle_info *hi_a = *(const struct handle_info**)a;
3500 const struct handle_info *hi_b = *(const struct handle_info**)b;
3501 if (hi_a->opserv_level != hi_b->opserv_level)
3502 return hi_b->opserv_level - hi_a->opserv_level;
3503 return irccasecmp(hi_a->handle, hi_b->handle);
3507 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3509 struct handle_info_list hil;
3510 struct helpfile_table tbl;
3515 memset(&hil, 0, sizeof(hil));
3516 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3517 struct handle_info *hi = iter_data(it);
3518 if (hi->opserv_level)
3519 handle_info_list_append(&hil, hi);
3521 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3522 tbl.length = hil.used + 1;
3524 tbl.flags = TABLE_NO_FREE;
3525 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3526 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3529 for (ii = 0; ii < hil.used; ) {
3530 ary = malloc(tbl.width * sizeof(ary[0]));
3531 ary[0] = hil.list[ii]->handle;
3532 ary[1] = strtab(hil.list[ii]->opserv_level);
3533 tbl.contents[++ii] = ary;
3535 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3536 reply("MSG_MATCH_COUNT", hil.used);
3537 for (ii = 0; ii < hil.used; ii++)
3538 free(tbl.contents[ii]);
3543 static NICKSERV_FUNC(cmd_search)
3545 struct nickserv_discrim *discrim;
3546 discrim_search_func action;
3547 struct svccmd *subcmd;
3548 unsigned int matches;
3551 NICKSERV_MIN_PARMS(3);
3552 sprintf(buf, "search %s", argv[1]);
3553 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3554 if (!irccasecmp(argv[1], "print"))
3555 action = search_print_func;
3556 else if (!irccasecmp(argv[1], "count"))
3557 action = search_count_func;
3558 else if (!irccasecmp(argv[1], "unregister"))
3559 action = search_unregister_func;
3561 reply("NSMSG_INVALID_ACTION", argv[1]);
3565 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3568 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3572 if (action == search_print_func)
3573 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3574 else if (action == search_count_func)
3575 discrim->limit = INT_MAX;
3577 matches = nickserv_discrim_search(discrim, action, user);
3580 reply("MSG_MATCH_COUNT", matches);
3582 reply("MSG_NO_MATCHES");
3588 static MODCMD_FUNC(cmd_checkpass)
3590 struct handle_info *hi;
3592 NICKSERV_MIN_PARMS(3);
3593 if (!(hi = get_handle_info(argv[1]))) {
3594 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3597 if (checkpass(argv[2], hi->passwd))
3598 reply("CHECKPASS_YES");
3600 reply("CHECKPASS_NO");
3605 static MODCMD_FUNC(cmd_checkemail)
3607 struct handle_info *hi;
3609 NICKSERV_MIN_PARMS(3);
3610 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
3613 if (!hi->email_addr)
3614 reply("CHECKEMAIL_NOT_SET");
3615 else if (!irccasecmp(argv[2], hi->email_addr))
3616 reply("CHECKEMAIL_YES");
3618 reply("CHECKEMAIL_NO");
3624 nickserv_db_read_handle(const char *handle, dict_t obj)
3627 struct string_list *masks, *slist;
3628 struct handle_info *hi;
3629 struct userNode *authed_users;
3630 struct userData *channel_list;
3635 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3636 id = str ? strtoul(str, NULL, 0) : 0;
3637 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3639 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3642 if ((hi = get_handle_info(handle))) {
3643 authed_users = hi->users;
3644 channel_list = hi->channels;
3646 hi->channels = NULL;
3647 dict_remove(nickserv_handle_dict, hi->handle);
3649 authed_users = NULL;
3650 channel_list = NULL;
3652 hi = register_handle(handle, str, id);
3654 hi->users = authed_users;
3655 while (authed_users) {
3656 authed_users->handle_info = hi;
3657 authed_users = authed_users->next_authed;
3660 hi->channels = channel_list;
3661 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3662 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3663 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3664 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3665 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3666 hi->language = language_find(str ? str : "C");
3667 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3668 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3669 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3671 hi->infoline = strdup(str);
3672 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3673 hi->registered = str ? strtoul(str, NULL, 0) : now;
3674 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3675 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
3676 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
3677 hi->karma = str ? strtoul(str, NULL, 0) : 0;
3678 /* We want to read the nicks even if disable_nicks is set. This is so
3679 * that we don't lose the nick data entirely. */
3680 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3682 for (ii=0; ii<slist->used; ii++)
3683 register_nick(slist->list[ii], hi);
3685 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3687 for (ii=0; str[ii]; ii++)
3688 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3690 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3691 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3692 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3693 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3694 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3695 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3696 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3698 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3700 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3701 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3703 nickserv_set_email_addr(hi, str);
3704 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3706 hi->epithet = strdup(str);
3707 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3709 hi->fakehost = strdup(str);
3710 str = database_get_data(obj, KEY_FAKEIDENT, RECDB_QSTRING);
3712 hi->fakeident = strdup(str);
3713 /* Read the "cookie" sub-database (if it exists). */
3714 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3716 const char *data, *type, *expires, *cookie_str;
3717 struct handle_cookie *cookie;
3719 cookie = calloc(1, sizeof(*cookie));
3720 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3721 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3722 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3723 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3724 if (!type || !expires || !cookie_str) {
3725 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3728 if (!irccasecmp(type, KEY_ACTIVATION))
3729 cookie->type = ACTIVATION;
3730 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3731 cookie->type = PASSWORD_CHANGE;
3732 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3733 cookie->type = EMAIL_CHANGE;
3734 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3735 cookie->type = ALLOWAUTH;
3737 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3740 cookie->expires = strtoul(expires, NULL, 0);
3741 if (cookie->expires < now)
3744 cookie->data = strdup(data);
3745 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3749 nickserv_bake_cookie(cookie);
3751 nickserv_free_cookie(cookie);
3753 /* Read the "notes" sub-database (if it exists). */
3754 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
3757 struct handle_note *last_note;
3758 struct handle_note *note;
3761 for (it = dict_first(subdb); it; it = iter_next(it)) {
3762 const char *expires;
3766 const char *note_id;
3769 note_id = iter_key(it);
3770 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
3772 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", note_id, hi->handle);
3775 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
3776 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
3777 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
3778 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
3779 if (!setter || !text || !set) {
3780 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", note_id, hi->handle);
3783 note = calloc(1, sizeof(*note) + strlen(text));
3785 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
3786 note->set = strtoul(set, NULL, 10);
3787 note->id = strtoul(note_id, NULL, 10);
3788 safestrncpy(note->setter, setter, sizeof(note->setter));
3789 strcpy(note->note, text);
3791 last_note->next = note;
3800 nickserv_saxdb_read(dict_t db) {
3802 struct record_data *rd;
3804 for (it=dict_first(db); it; it=iter_next(it)) {
3806 nickserv_db_read_handle(iter_key(it), rd->d.object);
3811 static NICKSERV_FUNC(cmd_mergedb)
3813 struct timeval start, stop;
3816 NICKSERV_MIN_PARMS(2);
3817 gettimeofday(&start, NULL);
3818 if (!(db = parse_database(argv[1]))) {
3819 reply("NSMSG_DB_UNREADABLE", argv[1]);
3822 nickserv_saxdb_read(db);
3824 gettimeofday(&stop, NULL);
3825 stop.tv_sec -= start.tv_sec;
3826 stop.tv_usec -= start.tv_usec;
3827 if (stop.tv_usec < 0) {
3829 stop.tv_usec += 1000000;
3831 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
3836 expire_handles(UNUSED_ARG(void *data))
3838 dict_iterator_t it, next;
3839 unsigned long expiry;
3840 struct handle_info *hi;
3842 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3843 next = iter_next(it);
3845 if ((hi->opserv_level > 0)
3847 || HANDLE_FLAGGED(hi, FROZEN)
3848 || HANDLE_FLAGGED(hi, NODELETE)) {
3851 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3852 if ((now - hi->lastseen) > expiry) {
3853 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3854 nickserv_unregister_handle(hi, NULL);
3858 if (nickserv_conf.handle_expire_frequency)
3859 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3863 nickserv_load_dict(const char *fname)
3867 if (!(file = fopen(fname, "r"))) {
3868 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3871 while (fgets(line, sizeof(line), file)) {
3874 if (line[strlen(line)-1] == '\n')
3875 line[strlen(line)-1] = 0;
3876 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3879 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3882 static enum reclaim_action
3883 reclaim_action_from_string(const char *str) {
3885 return RECLAIM_NONE;
3886 else if (!irccasecmp(str, "warn"))
3887 return RECLAIM_WARN;
3888 else if (!irccasecmp(str, "svsnick"))
3889 return RECLAIM_SVSNICK;
3890 else if (!irccasecmp(str, "kill"))
3891 return RECLAIM_KILL;
3893 return RECLAIM_NONE;
3897 nickserv_conf_read(void)
3899 dict_t conf_node, child;
3903 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3904 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3907 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3909 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3910 if (nickserv_conf.valid_handle_regex_set)
3911 regfree(&nickserv_conf.valid_handle_regex);
3913 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3914 nickserv_conf.valid_handle_regex_set = !err;
3915 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3917 nickserv_conf.valid_handle_regex_set = 0;
3919 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3920 if (nickserv_conf.valid_nick_regex_set)
3921 regfree(&nickserv_conf.valid_nick_regex);
3923 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3924 nickserv_conf.valid_nick_regex_set = !err;
3925 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3927 nickserv_conf.valid_nick_regex_set = 0;
3929 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3931 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3932 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3933 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3934 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3935 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3936 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3937 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3938 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3939 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3940 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3941 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3942 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3943 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3944 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3945 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3946 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3947 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3948 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3949 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3950 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3951 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3952 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3953 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3954 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3955 str = database_get_data(conf_node, KEY_SET_FAKEIDENT_LEVEL, RECDB_QSTRING);
3956 nickserv_conf.set_fakeident_level = str ? strtoul(str, NULL, 0) : 1000;
3957 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3959 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3960 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3961 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3963 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3964 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3965 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3967 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3968 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3969 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3970 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3971 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3972 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3973 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3974 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3975 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
3976 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
3977 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
3980 nickserv_conf.ounregister_flags = 0;
3982 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
3985 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
3987 str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING);
3989 nickserv_conf.handle_ts_mode = TS_IGNORE;
3990 else if (!irccasecmp(str, "ircu"))
3991 nickserv_conf.handle_ts_mode = TS_IRCU;
3993 nickserv_conf.handle_ts_mode = TS_IGNORE;
3994 if (!nickserv_conf.disable_nicks) {
3995 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3996 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3997 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3998 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3999 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
4000 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4001 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
4002 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
4004 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
4005 for (it=dict_first(child); it; it=iter_next(it)) {
4006 const char *key = iter_key(it), *value;
4010 if (!strncasecmp(key, "uc_", 3))
4011 flag = toupper(key[3]);
4012 else if (!strncasecmp(key, "lc_", 3))
4013 flag = tolower(key[3]);
4017 if ((pos = handle_inverse_flags[flag])) {
4018 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
4019 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
4022 if (nickserv_conf.weak_password_dict)
4023 dict_delete(nickserv_conf.weak_password_dict);
4024 nickserv_conf.weak_password_dict = dict_new();
4025 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
4026 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
4027 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
4028 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
4030 nickserv_load_dict(str);
4031 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
4032 if (nickserv && str)
4033 NickChange(nickserv, str, 0);
4034 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
4035 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
4036 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
4037 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
4038 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
4039 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
4040 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
4041 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
4042 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
4043 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
4044 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
4045 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
4046 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
4047 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
4048 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
4049 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
4050 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
4051 titlehost_suffix = str ? str : "example.net";
4052 str = conf_get_data("server/network", RECDB_QSTRING);
4053 nickserv_conf.network_name = str ? str : "some IRC network";
4054 if (!nickserv_conf.auth_policer_params) {
4055 nickserv_conf.auth_policer_params = policer_params_new();
4056 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
4057 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
4059 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
4060 for (it=dict_first(child); it; it=iter_next(it))
4061 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
4065 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
4067 char newnick[NICKLEN+1];
4076 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4078 case RECLAIM_SVSNICK:
4080 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
4081 } while (GetUserH(newnick));
4082 irc_svsnick(nickserv, user, newnick);
4085 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
4086 DelUser(user, nickserv, 1, msg);
4092 nickserv_reclaim_p(void *data) {
4093 struct userNode *user = data;
4094 struct nick_info *ni = get_nick_info(user->nick);
4096 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4100 check_user_nick(struct userNode *user) {
4101 struct nick_info *ni;
4102 user->modes &= ~FLAGS_REGNICK;
4103 if (!(ni = get_nick_info(user->nick)))
4105 if (user->handle_info == ni->owner) {
4106 user->modes |= FLAGS_REGNICK;
4110 if (nickserv_conf.warn_nick_owned)
4111 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4112 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
4114 if (nickserv_conf.auto_reclaim_delay)
4115 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
4117 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4121 handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
4123 struct handle_info *hi = NULL;
4126 hi = dict_find(nickserv_handle_dict, stamp, NULL);
4127 if ((hi == NULL) && (serial != 0)) {
4129 inttobase64(id, serial, IDLEN);
4130 hi = dict_find(nickserv_id_dict, id, NULL);
4134 if ((nickserv_conf.handle_ts_mode == TS_IRCU)
4135 && (timestamp != hi->registered)) {
4138 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
4141 set_user_handle_info(user, hi, 0);
4143 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial);
4148 handle_nick_change(struct userNode *user, const char *old_nick)
4150 struct handle_info *hi;
4152 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4153 dict_remove(nickserv_allow_auth_dict, old_nick);
4154 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4156 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4157 check_user_nick(user);
4161 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
4163 dict_remove(nickserv_allow_auth_dict, user->nick);
4164 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4165 set_user_handle_info(user, NULL, 0);
4168 static struct modcmd *
4169 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4171 if (min_level > 0) {
4173 sprintf(buf, "%u", min_level);
4174 if (must_be_qualified) {
4175 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4177 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4179 } else if (min_level == 0) {
4180 if (must_be_qualified) {
4181 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4183 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4186 if (must_be_qualified) {
4187 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4189 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4195 nickserv_db_cleanup(void)
4197 unreg_del_user_func(nickserv_remove_user);
4198 userList_clean(&curr_helpers);
4199 policer_params_delete(nickserv_conf.auth_policer_params);
4200 dict_delete(nickserv_handle_dict);
4201 dict_delete(nickserv_nick_dict);
4202 dict_delete(nickserv_opt_dict);
4203 dict_delete(nickserv_allow_auth_dict);
4204 dict_delete(nickserv_email_dict);
4205 dict_delete(nickserv_id_dict);
4206 dict_delete(nickserv_conf.weak_password_dict);
4207 free(auth_func_list);
4208 free(unreg_func_list);
4210 free(allowauth_func_list);
4211 free(handle_merge_func_list);
4212 free(failpw_func_list);
4213 if (nickserv_conf.valid_handle_regex_set)
4214 regfree(&nickserv_conf.valid_handle_regex);
4215 if (nickserv_conf.valid_nick_regex_set)
4216 regfree(&nickserv_conf.valid_nick_regex);
4220 init_nickserv(const char *nick)
4223 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4224 reg_new_user_func(check_user_nick);
4225 reg_nick_change_func(handle_nick_change);
4226 reg_del_user_func(nickserv_remove_user);
4227 reg_account_func(handle_account);
4229 /* set up handle_inverse_flags */
4230 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4231 for (i=0; handle_flags[i]; i++) {
4232 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4233 flag_access_levels[i] = 0;
4236 conf_register_reload(nickserv_conf_read);
4237 nickserv_opt_dict = dict_new();
4238 nickserv_email_dict = dict_new();
4239 dict_set_free_keys(nickserv_email_dict, free);
4240 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4242 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4243 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4244 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4245 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4246 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4247 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4248 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4249 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4250 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4251 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4252 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4253 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4254 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4255 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4256 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4257 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4258 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4259 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4260 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4261 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4262 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4263 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4264 if (!nickserv_conf.disable_nicks) {
4265 /* nick management commands */
4266 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4267 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4268 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4269 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4270 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4271 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4273 if (nickserv_conf.email_enabled) {
4274 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4275 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4276 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4277 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4278 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
4279 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4281 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4282 /* miscellaneous commands */
4283 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4284 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4285 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4286 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4287 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4288 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
4290 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4291 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4292 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4293 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4294 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4295 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4296 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4297 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4298 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4299 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4300 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4301 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4302 if (titlehost_suffix) {
4303 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4304 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4305 dict_insert(nickserv_opt_dict, "FAKEIDENT", opt_fakeident);
4307 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4308 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4309 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4310 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
4312 nickserv_handle_dict = dict_new();
4313 dict_set_free_keys(nickserv_handle_dict, free);
4314 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4316 nickserv_id_dict = dict_new();
4317 dict_set_free_keys(nickserv_id_dict, free);
4319 nickserv_nick_dict = dict_new();
4320 dict_set_free_data(nickserv_nick_dict, free);
4322 nickserv_allow_auth_dict = dict_new();
4324 userList_init(&curr_helpers);
4327 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4328 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4329 nickserv_service = service_register(nickserv);
4331 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4332 reg_exit_func(nickserv_db_cleanup);
4333 if(nickserv_conf.handle_expire_frequency)
4334 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4335 message_register_table(msgtab);