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;
1303 const char *pass, *email;
1305 NICKSERV_MIN_PARMS(3);
1310 if (!is_valid_handle(argv[1])) {
1311 reply("NSMSG_BAD_HANDLE", argv[1]);
1315 if (argc < 5 || !nickserv_conf.email_enabled) {
1320 if (!is_valid_email_addr(email)) {
1321 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
1324 if ((str = mail_prohibited_address(email))) {
1325 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", email, str);
1330 if (argc < 4 || !strcmp(argv[3], "*")) {
1333 } else if (strchr(argv[3], '@')) {
1334 mask = canonicalize_hostmask(strdup(argv[3]));
1336 settee = GetUserH(argv[4]);
1338 reply("MSG_NICK_UNKNOWN", argv[4]);
1345 } else if ((settee = GetUserH(argv[3]))) {
1346 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1348 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1351 if (settee && settee->handle_info) {
1352 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1356 if (!(hi = nickserv_register(user, settee, argv[1], pass, 0))) {
1361 string_list_append(hi->masks, mask);
1363 nickserv_set_email_addr(hi, email);
1367 static NICKSERV_FUNC(cmd_handleinfo)
1370 unsigned int i, pos=0, herelen;
1371 struct userNode *target, *next_un;
1372 struct handle_info *hi;
1373 const char *nsmsg_none;
1377 if (!(hi = user->handle_info)) {
1378 reply("NSMSG_MUST_AUTH");
1381 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1385 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1386 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1387 feh = hi->registered;
1388 reply("NSMSG_HANDLEINFO_REGGED", ctime(&feh));
1391 intervalString(buff, now - hi->lastseen, user->handle_info);
1392 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1394 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1397 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1398 if (HANDLE_FLAGGED(hi, FROZEN))
1399 reply("NSMSG_HANDLEINFO_VACATION");
1401 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1402 struct do_not_register *dnr;
1403 if ((dnr = chanserv_is_dnr(NULL, hi)))
1404 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1405 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
1407 } else if (hi != user->handle_info)
1411 reply("NSMSG_HANDLEINFO_KARMA", hi->karma);
1413 if (nickserv_conf.email_enabled)
1414 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1418 switch (hi->cookie->type) {
1419 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1420 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1421 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1422 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1423 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1428 if (oper_has_access(user, cmd->parent->bot, 601, 1))
1429 reply("NSMSG_HANDLEINFO_ID", hi->id);
1431 if (oper_has_access(user, cmd->parent->bot, 0, 1) || IsStaff(user)) {
1433 reply("NSMSG_HANDLEINFO_NO_NOTES");
1435 struct handle_note *prev, *note;
1437 WALK_NOTES(hi, prev, note) {
1438 char set_time[INTERVALLEN];
1439 intervalString(set_time, now - note->set, user->handle_info);
1440 if (note->expires) {
1441 char exp_time[INTERVALLEN];
1442 intervalString(exp_time, note->expires - now, user->handle_info);
1443 reply("NSMSG_HANDLEINFO_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1445 reply("NSMSG_HANDLEINFO_NOTE", note->id, set_time, note->setter, note->note);
1452 unsigned long flen = 1;
1453 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1455 for (i=0, flen=1; handle_flags[i]; i++)
1456 if (hi->flags & 1 << i)
1457 flags[flen++] = handle_flags[i];
1459 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1461 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1464 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1465 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1466 || (hi->opserv_level > 0)) {
1467 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1470 if (hi->fakeident && hi->fakehost)
1471 reply("NSMSG_HANDLEINFO_FAKEIDENTHOST", hi->fakeident, hi->fakehost);
1472 else if (hi->fakeident)
1473 reply("NSMSG_HANDLEINFO_FAKEIDENT", hi->fakeident);
1474 else if (hi->fakehost)
1475 reply("NSMSG_HANDLEINFO_FAKEHOST", hi->fakehost);
1477 if (hi->last_quit_host[0])
1478 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1480 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1482 if (nickserv_conf.disable_nicks) {
1483 /* nicks disabled; don't show anything about registered nicks */
1484 } else if (hi->nicks) {
1485 struct nick_info *ni, *next_ni;
1486 for (ni = hi->nicks; ni; ni = next_ni) {
1487 herelen = strlen(ni->nick);
1488 if (pos + herelen + 1 > ArrayLength(buff)) {
1490 goto print_nicks_buff;
1494 memcpy(buff+pos, ni->nick, herelen);
1495 pos += herelen; buff[pos++] = ' ';
1499 reply("NSMSG_HANDLEINFO_NICKS", buff);
1504 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1507 if (hi->masks->used) {
1508 for (i=0; i < hi->masks->used; i++) {
1509 herelen = strlen(hi->masks->list[i]);
1510 if (pos + herelen + 1 > ArrayLength(buff)) {
1512 goto print_mask_buff;
1514 memcpy(buff+pos, hi->masks->list[i], herelen);
1515 pos += herelen; buff[pos++] = ' ';
1516 if (i+1 == hi->masks->used) {
1519 reply("NSMSG_HANDLEINFO_MASKS", buff);
1524 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1528 struct userData *chan, *next;
1531 for (chan = hi->channels; chan; chan = next) {
1532 next = chan->u_next;
1533 name = chan->channel->channel->name;
1534 herelen = strlen(name);
1535 if (pos + herelen + 7 > ArrayLength(buff)) {
1537 goto print_chans_buff;
1539 if (IsUserSuspended(chan))
1541 pos += sprintf(buff+pos, "%d:%s ", chan->access, name);
1545 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1550 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1553 for (target = hi->users; target; target = next_un) {
1554 herelen = strlen(target->nick);
1555 if (pos + herelen + 1 > ArrayLength(buff)) {
1557 goto print_cnick_buff;
1559 next_un = target->next_authed;
1561 memcpy(buff+pos, target->nick, herelen);
1562 pos += herelen; buff[pos++] = ' ';
1566 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1571 return 1 | ((hi != user->handle_info) ? CMD_LOG_STAFF : 0);
1574 static NICKSERV_FUNC(cmd_userinfo)
1576 struct userNode *target;
1578 NICKSERV_MIN_PARMS(2);
1579 if (!(target = GetUserH(argv[1]))) {
1580 reply("MSG_NICK_UNKNOWN", argv[1]);
1583 if (target->handle_info)
1584 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1586 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1590 static NICKSERV_FUNC(cmd_nickinfo)
1592 struct nick_info *ni;
1594 NICKSERV_MIN_PARMS(2);
1595 if (!(ni = get_nick_info(argv[1]))) {
1596 reply("MSG_NICK_UNKNOWN", argv[1]);
1599 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1603 static NICKSERV_FUNC(cmd_notes)
1605 struct handle_info *hi;
1606 struct handle_note *prev, *note;
1609 NICKSERV_MIN_PARMS(2);
1610 if (!(hi = get_victim_oper(user, argv[1])))
1613 WALK_NOTES(hi, prev, note) {
1614 char set_time[INTERVALLEN];
1615 intervalString(set_time, now - note->set, user->handle_info);
1616 if (note->expires) {
1617 char exp_time[INTERVALLEN];
1618 intervalString(exp_time, note->expires - now, user->handle_info);
1619 reply("NSMSG_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1621 reply("NSMSG_NOTE", note->id, set_time, note->setter, note->note);
1625 reply("NSMSG_NOTE_COUNT", hits, argv[1]);
1629 static NICKSERV_FUNC(cmd_rename_handle)
1631 struct handle_info *hi;
1632 char msgbuf[MAXLEN], *old_handle;
1635 NICKSERV_MIN_PARMS(3);
1636 if (!(hi = get_victim_oper(user, argv[1])))
1638 if (!is_valid_handle(argv[2])) {
1639 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1642 if (get_handle_info(argv[2])) {
1643 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1646 if (hi->fakehost && hi->fakehost[0] == '.' &&
1647 (strlen(argv[2]) + strlen(hi->fakehost+1) +
1648 strlen(titlehost_suffix) + 2) > HOSTLEN) {
1649 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED_RENAME");
1653 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1654 hi->handle = strdup(argv[2]);
1655 dict_insert(nickserv_handle_dict, hi->handle, hi);
1656 for (nn=0; nn<rf_list_used; nn++)
1657 rf_list[nn](hi, old_handle);
1658 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1659 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1660 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1665 static failpw_func_t *failpw_func_list;
1666 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1669 reg_failpw_func(failpw_func_t func)
1671 if (failpw_func_used == failpw_func_size) {
1672 if (failpw_func_size) {
1673 failpw_func_size <<= 1;
1674 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1676 failpw_func_size = 8;
1677 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1680 failpw_func_list[failpw_func_used++] = func;
1683 static NICKSERV_FUNC(cmd_auth)
1685 int pw_arg, used, maxlogins;
1686 struct handle_info *hi;
1688 struct userNode *other;
1690 if (user->handle_info) {
1691 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1694 if (IsStamped(user)) {
1695 /* Unauthenticated users might still have been stamped
1696 previously and could therefore have a hidden host;
1697 do not allow them to authenticate. */
1698 reply("NSMSG_STAMPED_AUTH");
1702 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1704 } else if (argc == 2) {
1705 if (nickserv_conf.disable_nicks) {
1706 if (!(hi = get_handle_info(user->nick))) {
1707 reply("NSMSG_HANDLE_NOT_FOUND");
1711 /* try to look up their handle from their nick */
1712 struct nick_info *ni;
1713 ni = get_nick_info(user->nick);
1715 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1722 reply("MSG_MISSING_PARAMS", argv[0]);
1723 svccmd_send_help(user, nickserv, cmd);
1727 reply("NSMSG_HANDLE_NOT_FOUND");
1730 /* Responses from here on look up the language used by the handle they asked about. */
1731 passwd = argv[pw_arg];
1732 if (!valid_user_for(user, hi)) {
1733 if (hi->email_addr && nickserv_conf.email_enabled)
1734 send_message_type(4, user, cmd->parent->bot,
1735 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1738 send_message_type(4, user, cmd->parent->bot,
1739 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1741 argv[pw_arg] = "BADMASK";
1744 if (!checkpass(passwd, hi->passwd)) {
1746 send_message_type(4, user, cmd->parent->bot,
1747 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1748 argv[pw_arg] = "BADPASS";
1749 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1750 if (nickserv_conf.autogag_enabled) {
1751 if (!user->auth_policer.params) {
1752 user->auth_policer.last_req = now;
1753 user->auth_policer.params = nickserv_conf.auth_policer_params;
1755 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1757 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1758 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1759 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1761 argv[pw_arg] = "GAGGED";
1766 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1767 send_message_type(4, user, cmd->parent->bot,
1768 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1769 argv[pw_arg] = "SUSPENDED";
1772 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1773 for (used = 0, other = hi->users; other; other = other->next_authed) {
1774 if (++used >= maxlogins) {
1775 send_message_type(4, user, cmd->parent->bot,
1776 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1778 argv[pw_arg] = "MAXLOGINS";
1783 set_user_handle_info(user, hi, 1);
1784 if (nickserv_conf.email_required && !hi->email_addr)
1785 reply("NSMSG_PLEASE_SET_EMAIL");
1786 if (!is_secure_password(hi->handle, passwd, NULL))
1787 reply("NSMSG_WEAK_PASSWORD");
1788 if (hi->passwd[0] != '$')
1789 cryptpass(passwd, hi->passwd);
1790 if (!hi->masks->used) {
1792 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1793 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
1794 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1796 argv[pw_arg] = "****";
1797 reply("NSMSG_AUTH_SUCCESS");
1801 static allowauth_func_t *allowauth_func_list;
1802 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1805 reg_allowauth_func(allowauth_func_t func)
1807 if (allowauth_func_used == allowauth_func_size) {
1808 if (allowauth_func_size) {
1809 allowauth_func_size <<= 1;
1810 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1812 allowauth_func_size = 8;
1813 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1816 allowauth_func_list[allowauth_func_used++] = func;
1819 static NICKSERV_FUNC(cmd_allowauth)
1821 struct userNode *target;
1822 struct handle_info *hi;
1825 NICKSERV_MIN_PARMS(2);
1826 if (!(target = GetUserH(argv[1]))) {
1827 reply("MSG_NICK_UNKNOWN", argv[1]);
1830 if (target->handle_info) {
1831 reply("NSMSG_USER_PREV_AUTH", target->nick);
1834 if (IsStamped(target)) {
1835 /* Unauthenticated users might still have been stamped
1836 previously and could therefore have a hidden host;
1837 do not allow them to authenticate to an account. */
1838 reply("NSMSG_USER_PREV_STAMP", target->nick);
1843 else if (!(hi = get_handle_info(argv[2]))) {
1844 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1848 if (hi->opserv_level > user->handle_info->opserv_level) {
1849 reply("MSG_USER_OUTRANKED", hi->handle);
1852 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1853 || (hi->opserv_level > 0))
1854 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1855 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1858 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1859 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1860 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1861 if (nickserv_conf.email_enabled)
1862 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1864 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1865 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1867 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1869 for (n=0; n<allowauth_func_used; n++)
1870 allowauth_func_list[n](user, target, hi);
1874 static NICKSERV_FUNC(cmd_authcookie)
1876 struct handle_info *hi;
1878 NICKSERV_MIN_PARMS(2);
1879 if (user->handle_info) {
1880 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1883 if (IsStamped(user)) {
1884 /* Unauthenticated users might still have been stamped
1885 previously and could therefore have a hidden host;
1886 do not allow them to authenticate to an account. */
1887 reply("NSMSG_STAMPED_AUTHCOOKIE");
1890 if (!(hi = get_handle_info(argv[1]))) {
1891 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1894 if (!hi->email_addr) {
1895 reply("MSG_SET_EMAIL_ADDR");
1898 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1902 static NICKSERV_FUNC(cmd_delcookie)
1904 struct handle_info *hi;
1906 hi = user->handle_info;
1908 reply("NSMSG_NO_COOKIE");
1911 switch (hi->cookie->type) {
1914 reply("NSMSG_MUST_TIME_OUT");
1917 nickserv_eat_cookie(hi->cookie);
1918 reply("NSMSG_ATE_COOKIE");
1924 static NICKSERV_FUNC(cmd_odelcookie)
1926 struct handle_info *hi;
1928 NICKSERV_MIN_PARMS(2);
1930 if (!(hi = get_victim_oper(user, argv[1])))
1934 reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle);
1938 nickserv_eat_cookie(hi->cookie);
1939 reply("NSMSG_ATE_COOKIE_FOREIGN", hi->handle);
1944 static NICKSERV_FUNC(cmd_resetpass)
1946 struct handle_info *hi;
1947 char crypted[MD5_CRYPT_LENGTH];
1949 NICKSERV_MIN_PARMS(3);
1950 if (user->handle_info) {
1951 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1954 if (IsStamped(user)) {
1955 /* Unauthenticated users might still have been stamped
1956 previously and could therefore have a hidden host;
1957 do not allow them to activate an account. */
1958 reply("NSMSG_STAMPED_RESETPASS");
1961 if (!(hi = get_handle_info(argv[1]))) {
1962 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1965 if (!hi->email_addr) {
1966 reply("MSG_SET_EMAIL_ADDR");
1969 cryptpass(argv[2], crypted);
1971 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1975 static NICKSERV_FUNC(cmd_cookie)
1977 struct handle_info *hi;
1980 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1983 NICKSERV_MIN_PARMS(3);
1984 if (!(hi = get_handle_info(argv[1]))) {
1985 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1991 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1992 reply("NSMSG_HANDLE_SUSPENDED");
1997 reply("NSMSG_NO_COOKIE");
2001 /* Check validity of operation before comparing cookie to
2002 * prohibit guessing by authed users. */
2003 if (user->handle_info
2004 && (hi->cookie->type != EMAIL_CHANGE)
2005 && (hi->cookie->type != PASSWORD_CHANGE)) {
2006 reply("NSMSG_CANNOT_COOKIE");
2010 if (strcmp(cookie, hi->cookie->cookie)) {
2011 reply("NSMSG_BAD_COOKIE");
2015 switch (hi->cookie->type) {
2017 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2018 set_user_handle_info(user, hi, 1);
2019 reply("NSMSG_HANDLE_ACTIVATED");
2021 case PASSWORD_CHANGE:
2022 set_user_handle_info(user, hi, 1);
2023 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2024 reply("NSMSG_PASSWORD_CHANGED");
2027 nickserv_set_email_addr(hi, hi->cookie->data);
2028 reply("NSMSG_EMAIL_CHANGED");
2031 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2032 set_user_handle_info(user, hi, 1);
2033 nickserv_addmask(user, hi, mask);
2034 reply("NSMSG_AUTH_SUCCESS");
2039 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
2040 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
2044 nickserv_eat_cookie(hi->cookie);
2049 static NICKSERV_FUNC(cmd_oregnick) {
2051 struct handle_info *target;
2052 struct nick_info *ni;
2054 NICKSERV_MIN_PARMS(3);
2055 if (!(target = modcmd_get_handle_info(user, argv[1])))
2058 if (!is_registerable_nick(nick)) {
2059 reply("NSMSG_BAD_NICK", nick);
2062 ni = dict_find(nickserv_nick_dict, nick, NULL);
2064 reply("NSMSG_NICK_EXISTS", nick);
2067 register_nick(nick, target);
2068 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
2072 static NICKSERV_FUNC(cmd_regnick) {
2074 struct nick_info *ni;
2076 if (!is_registerable_nick(user->nick)) {
2077 reply("NSMSG_BAD_NICK", user->nick);
2080 /* count their nicks, see if it's too many */
2081 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
2082 if (n >= nickserv_conf.nicks_per_handle) {
2083 reply("NSMSG_TOO_MANY_NICKS");
2086 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
2088 reply("NSMSG_NICK_EXISTS", user->nick);
2091 register_nick(user->nick, user->handle_info);
2092 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2096 static NICKSERV_FUNC(cmd_pass)
2098 struct handle_info *hi;
2099 const char *old_pass, *new_pass;
2101 NICKSERV_MIN_PARMS(3);
2102 hi = user->handle_info;
2106 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
2107 if (!checkpass(old_pass, hi->passwd)) {
2108 argv[1] = "BADPASS";
2109 reply("NSMSG_PASSWORD_INVALID");
2112 cryptpass(new_pass, hi->passwd);
2114 reply("NSMSG_PASS_SUCCESS");
2119 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
2122 char *new_mask = canonicalize_hostmask(strdup(mask));
2123 for (i=0; i<hi->masks->used; i++) {
2124 if (!irccasecmp(new_mask, hi->masks->list[i])) {
2125 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2130 string_list_append(hi->masks, new_mask);
2131 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2135 static NICKSERV_FUNC(cmd_addmask)
2138 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2139 int res = nickserv_addmask(user, user->handle_info, mask);
2143 if (!is_gline(argv[1])) {
2144 reply("NSMSG_MASK_INVALID", argv[1]);
2147 return nickserv_addmask(user, user->handle_info, argv[1]);
2151 static NICKSERV_FUNC(cmd_oaddmask)
2153 struct handle_info *hi;
2155 NICKSERV_MIN_PARMS(3);
2156 if (!(hi = get_victim_oper(user, argv[1])))
2158 return nickserv_addmask(user, hi, argv[2]);
2162 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2165 for (i=0; i<hi->masks->used; i++) {
2166 if (!strcmp(del_mask, hi->masks->list[i])) {
2167 char *old_mask = hi->masks->list[i];
2168 if (hi->masks->used == 1 && !force) {
2169 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2172 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2173 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2178 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2182 static NICKSERV_FUNC(cmd_delmask)
2184 NICKSERV_MIN_PARMS(2);
2185 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2188 static NICKSERV_FUNC(cmd_odelmask)
2190 struct handle_info *hi;
2191 NICKSERV_MIN_PARMS(3);
2192 if (!(hi = get_victim_oper(user, argv[1])))
2194 return nickserv_delmask(user, hi, argv[2], 1);
2198 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2199 unsigned int nn, add = 1, pos;
2200 unsigned long added, removed, flag;
2202 for (added=removed=nn=0; str[nn]; nn++) {
2204 case '+': add = 1; break;
2205 case '-': add = 0; break;
2207 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2208 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2211 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2212 /* cheesy avoidance of looking up the flag name.. */
2213 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2216 flag = 1 << (pos - 1);
2218 added |= flag, removed &= ~flag;
2220 removed |= flag, added &= ~flag;
2225 *premoved = removed;
2230 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2232 unsigned long before, after, added, removed;
2233 struct userNode *uNode;
2235 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2236 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2238 hi->flags = (hi->flags | added) & ~removed;
2239 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2241 /* Strip helping flag if they're only a support helper and not
2242 * currently in #support. */
2243 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2244 struct channelList *schannels;
2246 schannels = chanserv_support_channels();
2247 for (ii = 0; ii < schannels->used; ++ii)
2248 if (find_handle_in_channel(schannels->list[ii], hi, NULL))
2250 if (ii == schannels->used)
2251 HANDLE_CLEAR_FLAG(hi, HELPING);
2254 if (after && !before) {
2255 /* Add user to current helper list. */
2256 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2257 userList_append(&curr_helpers, uNode);
2258 } else if (!after && before) {
2259 /* Remove user from current helper list. */
2260 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2261 userList_remove(&curr_helpers, uNode);
2268 set_list(struct userNode *user, struct handle_info *hi, int override)
2272 char *set_display[] = {
2273 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2274 "EMAIL", "MAXLOGINS", "LANGUAGE"
2277 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2279 /* Do this so options are presented in a consistent order. */
2280 for (i = 0; i < ArrayLength(set_display); ++i)
2281 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2282 opt(user, hi, override, 0, NULL);
2285 static NICKSERV_FUNC(cmd_set)
2287 struct handle_info *hi;
2290 hi = user->handle_info;
2292 set_list(user, hi, 0);
2295 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2296 reply("NSMSG_INVALID_OPTION", argv[1]);
2299 return opt(user, hi, 0, argc-1, argv+1);
2302 static NICKSERV_FUNC(cmd_oset)
2304 struct handle_info *hi;
2305 struct svccmd *subcmd;
2307 char cmdname[MAXLEN];
2309 NICKSERV_MIN_PARMS(2);
2311 if (!(hi = get_victim_oper(user, argv[1])))
2315 set_list(user, hi, 0);
2319 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2320 reply("NSMSG_INVALID_OPTION", argv[2]);
2324 sprintf(cmdname, "%s %s", cmd->name, argv[2]);
2325 subcmd = dict_find(cmd->parent->commands, cmdname, NULL);
2326 if (subcmd && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, NULL, SVCCMD_NOISY))
2329 return opt(user, hi, 1, argc-2, argv+2);
2332 static OPTION_FUNC(opt_info)
2336 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2338 hi->infoline = NULL;
2340 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2344 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2345 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2349 static OPTION_FUNC(opt_width)
2352 hi->screen_width = strtoul(argv[1], NULL, 0);
2354 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2355 hi->screen_width = MIN_LINE_SIZE;
2356 else if (hi->screen_width > MAX_LINE_SIZE)
2357 hi->screen_width = MAX_LINE_SIZE;
2359 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2363 static OPTION_FUNC(opt_tablewidth)
2366 hi->table_width = strtoul(argv[1], NULL, 0);
2368 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2369 hi->table_width = MIN_LINE_SIZE;
2370 else if (hi->screen_width > MAX_LINE_SIZE)
2371 hi->table_width = MAX_LINE_SIZE;
2373 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2377 static OPTION_FUNC(opt_color)
2380 if (enabled_string(argv[1]))
2381 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2382 else if (disabled_string(argv[1]))
2383 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2385 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2390 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2394 static OPTION_FUNC(opt_privmsg)
2397 if (enabled_string(argv[1]))
2398 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2399 else if (disabled_string(argv[1]))
2400 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2402 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2407 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2411 static OPTION_FUNC(opt_style)
2416 if (!irccasecmp(argv[1], "Zoot"))
2417 hi->userlist_style = HI_STYLE_ZOOT;
2418 else if (!irccasecmp(argv[1], "def"))
2419 hi->userlist_style = HI_STYLE_DEF;
2422 switch (hi->userlist_style) {
2431 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2435 static OPTION_FUNC(opt_password)
2438 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2443 cryptpass(argv[1], hi->passwd);
2445 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2449 static OPTION_FUNC(opt_flags)
2452 unsigned int ii, flen;
2455 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2460 nickserv_apply_flags(user, hi, argv[1]);
2462 for (ii = flen = 0; handle_flags[ii]; ii++)
2463 if (hi->flags & (1 << ii))
2464 flags[flen++] = handle_flags[ii];
2467 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2469 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2473 static OPTION_FUNC(opt_email)
2477 if (!is_valid_email_addr(argv[1])) {
2478 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2481 if ((str = mail_prohibited_address(argv[1]))) {
2482 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2485 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2486 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2488 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2490 nickserv_set_email_addr(hi, argv[1]);
2492 nickserv_eat_cookie(hi->cookie);
2493 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2496 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2500 static OPTION_FUNC(opt_maxlogins)
2502 unsigned char maxlogins;
2504 maxlogins = strtoul(argv[1], NULL, 0);
2505 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2506 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2509 hi->maxlogins = maxlogins;
2511 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2512 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2516 static OPTION_FUNC(opt_language)
2518 struct language *lang;
2520 lang = language_find(argv[1]);
2521 if (irccasecmp(lang->name, argv[1]))
2522 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2523 hi->language = lang;
2525 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2529 static OPTION_FUNC(opt_karma)
2532 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2537 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2538 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2539 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2540 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2542 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2546 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2551 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2552 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2554 if ((user->handle_info->opserv_level < target->opserv_level)
2555 || ((user->handle_info->opserv_level == target->opserv_level)
2556 && (user->handle_info->opserv_level < 1000))) {
2557 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2560 if ((user->handle_info->opserv_level < new_level)
2561 || ((user->handle_info->opserv_level == new_level)
2562 && (user->handle_info->opserv_level < 1000))) {
2563 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2566 if (user->handle_info == target) {
2567 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2570 if (target->opserv_level == new_level)
2572 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2573 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2574 target->opserv_level = new_level;
2578 static OPTION_FUNC(opt_level)
2583 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2587 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2588 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2592 static OPTION_FUNC(opt_epithet)
2595 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2599 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2600 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2603 if ((epithet[0] == '*') && !epithet[1])
2606 hi->epithet = strdup(epithet);
2610 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2612 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2616 static OPTION_FUNC(opt_title)
2621 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2625 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2627 if (strchr(title, '.')) {
2628 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2631 if ((strlen(user->handle_info->handle) + strlen(title) +
2632 strlen(titlehost_suffix) + 2) > HOSTLEN) {
2633 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2638 if (!strcmp(title, "*")) {
2639 hi->fakehost = NULL;
2641 hi->fakehost = malloc(strlen(title)+2);
2642 hi->fakehost[0] = '.';
2643 strcpy(hi->fakehost+1, title);
2645 apply_fakehost(hi, NULL);
2646 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2647 title = hi->fakehost + 1;
2651 title = user_find_message(user, "MSG_NONE");
2652 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2656 static OPTION_FUNC(opt_fakehost)
2658 char mask[USERLEN + HOSTLEN + 2];
2662 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2666 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2667 if(strlen(argv[1]) >= sizeof(mask)) {
2668 send_message(user, nickserv, "NSMSG_FAKEMASK_INVALID", USERLEN + HOSTLEN + 1);
2672 safestrncpy(mask, argv[1], sizeof(mask));
2674 if ((host = strrchr(mask, '@')) && host != mask) {
2675 // If ident@host was used and the user doesn't have access to set idents, do not change anything.
2676 if (!oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
2688 if (ident && strlen(ident) > USERLEN) {
2689 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
2693 if (host && ((strlen(host) > HOSTLEN) || (host[0] == '.'))) {
2694 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2698 if (host && host[0]) {
2700 if (!strcmp(host, "*"))
2701 hi->fakehost = NULL;
2703 hi->fakehost = strdup(host);
2704 host = hi->fakehost;
2707 host = generate_fakehost(hi);
2710 free(hi->fakeident);
2711 if (!strcmp(ident, "*"))
2712 hi->fakeident = NULL;
2714 hi->fakeident = strdup(ident);
2715 ident = hi->fakeident;
2718 ident = generate_fakeident(hi, NULL);
2720 apply_fakehost(hi, NULL);
2722 host = generate_fakehost(hi);
2723 ident = generate_fakeident(hi, NULL);
2726 host = (char *) user_find_message(user, "MSG_NONE");
2728 send_message(user, nickserv, "NSMSG_SET_FAKEIDENTHOST", ident, host);
2730 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", host);
2734 static OPTION_FUNC(opt_fakeident)
2739 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2743 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
2745 if (strlen(ident) > USERLEN) {
2746 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
2749 free(hi->fakeident);
2750 if (!strcmp(ident, "*"))
2751 hi->fakeident = NULL;
2753 hi->fakeident = strdup(ident);
2754 ident = hi->fakeident;
2755 apply_fakehost(hi, NULL);
2757 ident = generate_fakeident(hi, NULL); /* NULL if no fake ident set */
2759 ident = user_find_message(user, "MSG_NONE");
2760 send_message(user, nickserv, "NSMSG_SET_FAKEIDENT", ident);
2764 static NICKSERV_FUNC(cmd_reclaim)
2766 struct handle_info *hi;
2767 struct nick_info *ni;
2768 struct userNode *victim;
2770 NICKSERV_MIN_PARMS(2);
2771 hi = user->handle_info;
2772 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2774 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2777 if (ni->owner != user->handle_info) {
2778 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2781 victim = GetUserH(ni->nick);
2783 reply("MSG_NICK_UNKNOWN", ni->nick);
2786 if (victim == user) {
2787 reply("NSMSG_NICK_USER_YOU");
2790 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2791 switch (nickserv_conf.reclaim_action) {
2792 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2793 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2794 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2795 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2800 static NICKSERV_FUNC(cmd_unregnick)
2803 struct handle_info *hi;
2804 struct nick_info *ni;
2806 hi = user->handle_info;
2807 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2808 ni = dict_find(nickserv_nick_dict, nick, NULL);
2810 reply("NSMSG_UNKNOWN_NICK", nick);
2813 if (hi != ni->owner) {
2814 reply("NSMSG_NOT_YOUR_NICK", nick);
2817 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2822 static NICKSERV_FUNC(cmd_ounregnick)
2824 struct nick_info *ni;
2826 NICKSERV_MIN_PARMS(2);
2827 if (!(ni = get_nick_info(argv[1]))) {
2828 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2831 if (!oper_outranks(user, ni->owner))
2833 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2838 static NICKSERV_FUNC(cmd_unregister)
2840 struct handle_info *hi;
2843 NICKSERV_MIN_PARMS(2);
2844 hi = user->handle_info;
2847 if (checkpass(passwd, hi->passwd)) {
2848 nickserv_unregister_handle(hi, user);
2851 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2852 reply("NSMSG_PASSWORD_INVALID");
2857 static NICKSERV_FUNC(cmd_ounregister)
2859 struct handle_info *hi;
2860 char reason[MAXLEN];
2863 NICKSERV_MIN_PARMS(2);
2864 if (!(hi = get_victim_oper(user, argv[1])))
2867 if (HANDLE_FLAGGED(hi, NODELETE)) {
2868 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
2872 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
2874 ((hi->flags & nickserv_conf.ounregister_flags)
2876 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
2877 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
2881 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
2882 global_message(MESSAGE_RECIPIENT_STAFF, reason);
2883 nickserv_unregister_handle(hi, user);
2887 static NICKSERV_FUNC(cmd_status)
2889 if (nickserv_conf.disable_nicks) {
2890 reply("NSMSG_GLOBAL_STATS_NONICK",
2891 dict_size(nickserv_handle_dict));
2893 if (user->handle_info) {
2895 struct nick_info *ni;
2896 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2897 reply("NSMSG_HANDLE_STATS", cnt);
2899 reply("NSMSG_HANDLE_NONE");
2901 reply("NSMSG_GLOBAL_STATS",
2902 dict_size(nickserv_handle_dict),
2903 dict_size(nickserv_nick_dict));
2908 static NICKSERV_FUNC(cmd_ghost)
2910 struct userNode *target;
2911 char reason[MAXLEN];
2913 NICKSERV_MIN_PARMS(2);
2914 if (!(target = GetUserH(argv[1]))) {
2915 reply("MSG_NICK_UNKNOWN", argv[1]);
2918 if (target == user) {
2919 reply("NSMSG_CANNOT_GHOST_SELF");
2922 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2923 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2926 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2927 DelUser(target, nickserv, 1, reason);
2928 reply("NSMSG_GHOST_KILLED", argv[1]);
2932 static NICKSERV_FUNC(cmd_vacation)
2934 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2935 reply("NSMSG_ON_VACATION");
2939 static NICKSERV_FUNC(cmd_addnote)
2941 struct handle_info *hi;
2942 unsigned long duration;
2945 struct handle_note *prev;
2946 struct handle_note *note;
2948 /* Parse parameters and figure out values for note's fields. */
2949 NICKSERV_MIN_PARMS(4);
2950 hi = get_victim_oper(user, argv[1]);
2953 if(!strcmp(argv[2], "0"))
2955 else if(!(duration = ParseInterval(argv[2])))
2957 reply("MSG_INVALID_DURATION", argv[2]);
2960 if (duration > 2*365*86400) {
2961 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
2964 unsplit_string(argv + 3, argc - 3, text);
2965 WALK_NOTES(hi, prev, note) {}
2966 id = prev ? (prev->id + 1) : 1;
2968 /* Create the new note structure. */
2969 note = calloc(1, sizeof(*note) + strlen(text));
2971 note->expires = duration ? (now + duration) : 0;
2974 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
2975 strcpy(note->note, text);
2980 reply("NSMSG_NOTE_ADDED", id, hi->handle);
2984 static NICKSERV_FUNC(cmd_delnote)
2986 struct handle_info *hi;
2987 struct handle_note *prev;
2988 struct handle_note *note;
2991 NICKSERV_MIN_PARMS(3);
2992 hi = get_victim_oper(user, argv[1]);
2995 id = strtoul(argv[2], NULL, 10);
2996 WALK_NOTES(hi, prev, note) {
2997 if (id == note->id) {
2999 prev->next = note->next;
3001 hi->notes = note->next;
3003 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
3007 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
3012 nickserv_saxdb_write(struct saxdb_context *ctx) {
3014 struct handle_info *hi;
3017 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3019 assert(hi->id != 0);
3020 saxdb_start_record(ctx, iter_key(it), 0);
3022 struct handle_cookie *cookie = hi->cookie;
3025 switch (cookie->type) {
3026 case ACTIVATION: type = KEY_ACTIVATION; break;
3027 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
3028 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
3029 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
3030 default: type = NULL; break;
3033 saxdb_start_record(ctx, KEY_COOKIE, 0);
3034 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
3035 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
3037 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
3038 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
3039 saxdb_end_record(ctx);
3043 struct handle_note *prev, *note;
3044 saxdb_start_record(ctx, KEY_NOTES, 0);
3045 WALK_NOTES(hi, prev, note) {
3046 snprintf(flags, sizeof(flags), "%d", note->id);
3047 saxdb_start_record(ctx, flags, 0);
3049 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
3050 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
3051 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
3052 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
3053 saxdb_end_record(ctx);
3055 saxdb_end_record(ctx);
3058 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
3060 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
3062 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
3064 saxdb_write_string(ctx, KEY_FAKEIDENT, hi->fakeident);
3068 for (ii=flen=0; handle_flags[ii]; ++ii)
3069 if (hi->flags & (1 << ii))
3070 flags[flen++] = handle_flags[ii];
3072 saxdb_write_string(ctx, KEY_FLAGS, flags);
3074 saxdb_write_int(ctx, KEY_ID, hi->id);
3076 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
3077 if (hi->last_quit_host[0])
3078 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
3079 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
3081 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
3082 if (hi->masks->used)
3083 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
3085 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
3087 struct string_list *slist;
3088 struct nick_info *ni;
3090 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
3091 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
3092 saxdb_write_string_list(ctx, KEY_NICKS, slist);
3096 if (hi->opserv_level)
3097 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
3098 if (hi->language != lang_C)
3099 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
3100 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
3101 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
3102 if (hi->screen_width)
3103 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
3104 if (hi->table_width)
3105 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
3106 flags[0] = hi->userlist_style;
3108 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
3109 saxdb_end_record(ctx);
3114 static handle_merge_func_t *handle_merge_func_list;
3115 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
3118 reg_handle_merge_func(handle_merge_func_t func)
3120 if (handle_merge_func_used == handle_merge_func_size) {
3121 if (handle_merge_func_size) {
3122 handle_merge_func_size <<= 1;
3123 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
3125 handle_merge_func_size = 8;
3126 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
3129 handle_merge_func_list[handle_merge_func_used++] = func;
3132 static NICKSERV_FUNC(cmd_merge)
3134 struct handle_info *hi_from, *hi_to;
3135 struct userNode *last_user;
3136 struct userData *cList, *cListNext;
3137 unsigned int ii, jj, n;
3138 char buffer[MAXLEN];
3140 NICKSERV_MIN_PARMS(3);
3142 if (!(hi_from = get_victim_oper(user, argv[1])))
3144 if (!(hi_to = get_victim_oper(user, argv[2])))
3146 if (hi_to == hi_from) {
3147 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
3151 for (n=0; n<handle_merge_func_used; n++)
3152 handle_merge_func_list[n](user, hi_to, hi_from);
3154 /* Append "from" handle's nicks to "to" handle's nick list. */
3156 struct nick_info *last_ni;
3157 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3158 last_ni->next = hi_from->nicks;
3160 while (hi_from->nicks) {
3161 hi_from->nicks->owner = hi_to;
3162 hi_from->nicks = hi_from->nicks->next;
3165 /* Merge the hostmasks. */
3166 for (ii=0; ii<hi_from->masks->used; ii++) {
3167 char *mask = hi_from->masks->list[ii];
3168 for (jj=0; jj<hi_to->masks->used; jj++)
3169 if (match_ircglobs(hi_to->masks->list[jj], mask))
3171 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3172 string_list_append(hi_to->masks, strdup(mask));
3175 /* Merge the lists of authed users. */
3177 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3178 last_user->next_authed = hi_from->users;
3180 hi_to->users = hi_from->users;
3182 /* Repoint the old "from" handle's users. */
3183 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3184 last_user->handle_info = hi_to;
3186 hi_from->users = NULL;
3188 /* Merge channel userlists. */
3189 for (cList=hi_from->channels; cList; cList=cListNext) {
3190 struct userData *cList2;
3191 cListNext = cList->u_next;
3192 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3193 if (cList->channel == cList2->channel)
3195 if (cList2 && (cList2->access >= cList->access)) {
3196 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);
3197 /* keep cList2 in hi_to; remove cList from hi_from */
3198 del_channel_user(cList, 1);
3201 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);
3202 /* remove the lower-ranking cList2 from hi_to */
3203 del_channel_user(cList2, 1);
3205 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3207 /* cList needs to be moved from hi_from to hi_to */
3208 cList->handle = hi_to;
3209 /* Remove from linked list for hi_from */
3210 assert(!cList->u_prev);
3211 hi_from->channels = cList->u_next;
3213 cList->u_next->u_prev = cList->u_prev;
3214 /* Add to linked list for hi_to */
3215 cList->u_prev = NULL;
3216 cList->u_next = hi_to->channels;
3217 if (hi_to->channels)
3218 hi_to->channels->u_prev = cList;
3219 hi_to->channels = cList;
3223 /* Do they get an OpServ level promotion? */
3224 if (hi_from->opserv_level > hi_to->opserv_level)
3225 hi_to->opserv_level = hi_from->opserv_level;
3227 /* What about last seen time? */
3228 if (hi_from->lastseen > hi_to->lastseen)
3229 hi_to->lastseen = hi_from->lastseen;
3231 /* New karma is the sum of the two original karmas. */
3232 hi_to->karma += hi_from->karma;
3234 /* Does a fakehost carry over? (This intentionally doesn't set it
3235 * for users previously attached to hi_to. They'll just have to
3238 if (hi_from->fakehost && !hi_to->fakehost)
3239 hi_to->fakehost = strdup(hi_from->fakehost);
3240 if (hi_from->fakeident && !hi_to->fakeident)
3241 hi_to->fakeident = strdup(hi_from->fakeident);
3243 /* Notify of success. */
3244 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3245 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3246 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3248 /* Unregister the "from" handle. */
3249 nickserv_unregister_handle(hi_from, NULL);
3254 struct nickserv_discrim {
3255 unsigned long flags_on, flags_off;
3256 unsigned long min_registered, max_registered;
3257 unsigned long lastseen;
3259 int min_level, max_level;
3260 int min_karma, max_karma;
3261 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3262 const char *nickmask;
3263 const char *hostmask;
3264 const char *fakehostmask;
3265 const char *fakeidentmask;
3266 const char *handlemask;
3267 const char *emailmask;
3270 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
3272 struct discrim_apply_info {
3273 struct nickserv_discrim *discrim;
3274 discrim_search_func func;
3275 struct userNode *source;
3276 unsigned int matched;
3279 static struct nickserv_discrim *
3280 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3283 struct nickserv_discrim *discrim;
3285 discrim = malloc(sizeof(*discrim));
3286 memset(discrim, 0, sizeof(*discrim));
3287 discrim->min_level = 0;
3288 discrim->max_level = INT_MAX;
3289 discrim->limit = 50;
3290 discrim->min_registered = 0;
3291 discrim->max_registered = ULONG_MAX;
3292 discrim->lastseen = ULONG_MAX;
3293 discrim->min_karma = INT_MIN;
3294 discrim->max_karma = INT_MAX;
3296 for (i=0; i<argc; i++) {
3297 if (i == argc - 1) {
3298 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3301 if (!irccasecmp(argv[i], "limit")) {
3302 discrim->limit = strtoul(argv[++i], NULL, 0);
3303 } else if (!irccasecmp(argv[i], "flags")) {
3304 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3305 } else if (!irccasecmp(argv[i], "registered")) {
3306 const char *cmp = argv[++i];
3307 if (cmp[0] == '<') {
3308 if (cmp[1] == '=') {
3309 discrim->min_registered = now - ParseInterval(cmp+2);
3311 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3313 } else if (cmp[0] == '=') {
3314 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3315 } else if (cmp[0] == '>') {
3316 if (cmp[1] == '=') {
3317 discrim->max_registered = now - ParseInterval(cmp+2);
3319 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3322 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3324 } else if (!irccasecmp(argv[i], "seen")) {
3325 discrim->lastseen = now - ParseInterval(argv[++i]);
3326 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3327 discrim->nickmask = argv[++i];
3328 } else if (!irccasecmp(argv[i], "hostmask")) {
3330 if (!irccasecmp(argv[i], "exact")) {
3331 if (i == argc - 1) {
3332 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3335 discrim->hostmask_type = EXACT;
3336 } else if (!irccasecmp(argv[i], "subset")) {
3337 if (i == argc - 1) {
3338 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3341 discrim->hostmask_type = SUBSET;
3342 } else if (!irccasecmp(argv[i], "superset")) {
3343 if (i == argc - 1) {
3344 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3347 discrim->hostmask_type = SUPERSET;
3348 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3349 if (i == argc - 1) {
3350 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3353 discrim->hostmask_type = LASTQUIT;
3356 discrim->hostmask_type = SUPERSET;
3358 discrim->hostmask = argv[++i];
3359 } else if (!irccasecmp(argv[i], "fakehost")) {
3360 if (!irccasecmp(argv[++i], "*")) {
3361 discrim->fakehostmask = 0;
3363 discrim->fakehostmask = argv[i];
3365 } else if (!irccasecmp(argv[i], "fakeident")) {
3366 if (!irccasecmp(argv[++i], "*")) {
3367 discrim->fakeidentmask = 0;
3369 discrim->fakeidentmask = argv[i];
3371 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3372 if (!irccasecmp(argv[++i], "*")) {
3373 discrim->handlemask = 0;
3375 discrim->handlemask = argv[i];
3377 } else if (!irccasecmp(argv[i], "email")) {
3378 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3379 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3381 } else if (!irccasecmp(argv[++i], "*")) {
3382 discrim->emailmask = 0;
3384 discrim->emailmask = argv[i];
3386 } else if (!irccasecmp(argv[i], "access")) {
3387 const char *cmp = argv[++i];
3388 if (cmp[0] == '<') {
3389 if (discrim->min_level == 0) discrim->min_level = 1;
3390 if (cmp[1] == '=') {
3391 discrim->max_level = strtoul(cmp+2, NULL, 0);
3393 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3395 } else if (cmp[0] == '=') {
3396 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3397 } else if (cmp[0] == '>') {
3398 if (cmp[1] == '=') {
3399 discrim->min_level = strtoul(cmp+2, NULL, 0);
3401 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3404 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3406 } else if (!irccasecmp(argv[i], "karma")) {
3407 const char *cmp = argv[++i];
3408 if (cmp[0] == '<') {
3409 if (cmp[1] == '=') {
3410 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3412 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3414 } else if (cmp[0] == '=') {
3415 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3416 } else if (cmp[0] == '>') {
3417 if (cmp[1] == '=') {
3418 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3420 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3423 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3426 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3437 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3439 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3440 || (discrim->flags_off & hi->flags)
3441 || (discrim->min_registered > hi->registered)
3442 || (discrim->max_registered < hi->registered)
3443 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3444 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3445 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3446 || (discrim->fakeidentmask && (!hi->fakeident || !match_ircglob(hi->fakeident, discrim->fakeidentmask)))
3447 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3448 || (discrim->min_level > hi->opserv_level)
3449 || (discrim->max_level < hi->opserv_level)
3450 || (discrim->min_karma > hi->karma)
3451 || (discrim->max_karma < hi->karma)
3455 if (discrim->hostmask) {
3457 for (i=0; i<hi->masks->used; i++) {
3458 const char *mask = hi->masks->list[i];
3459 if ((discrim->hostmask_type == SUBSET)
3460 && (match_ircglobs(discrim->hostmask, mask))) break;
3461 else if ((discrim->hostmask_type == EXACT)
3462 && !irccasecmp(discrim->hostmask, mask)) break;
3463 else if ((discrim->hostmask_type == SUPERSET)
3464 && (match_ircglobs(mask, discrim->hostmask))) break;
3465 else if ((discrim->hostmask_type == LASTQUIT)
3466 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3468 if (i==hi->masks->used) return 0;
3470 if (discrim->nickmask) {
3471 struct nick_info *nick = hi->nicks;
3473 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3476 if (!nick) return 0;
3482 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3484 dict_iterator_t it, next;
3485 unsigned int matched;
3487 for (it = dict_first(nickserv_handle_dict), matched = 0;
3488 it && (matched < discrim->limit);
3490 next = iter_next(it);
3491 if (nickserv_discrim_match(discrim, iter_data(it))) {
3492 dsf(source, iter_data(it));
3500 search_print_func(struct userNode *source, struct handle_info *match)
3502 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3506 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3511 search_unregister_func (struct userNode *source, struct handle_info *match)
3513 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3514 nickserv_unregister_handle(match, source);
3518 nickserv_sort_accounts_by_access(const void *a, const void *b)
3520 const struct handle_info *hi_a = *(const struct handle_info**)a;
3521 const struct handle_info *hi_b = *(const struct handle_info**)b;
3522 if (hi_a->opserv_level != hi_b->opserv_level)
3523 return hi_b->opserv_level - hi_a->opserv_level;
3524 return irccasecmp(hi_a->handle, hi_b->handle);
3528 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3530 struct handle_info_list hil;
3531 struct helpfile_table tbl;
3536 memset(&hil, 0, sizeof(hil));
3537 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3538 struct handle_info *hi = iter_data(it);
3539 if (hi->opserv_level)
3540 handle_info_list_append(&hil, hi);
3542 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3543 tbl.length = hil.used + 1;
3545 tbl.flags = TABLE_NO_FREE;
3546 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3547 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3550 for (ii = 0; ii < hil.used; ) {
3551 ary = malloc(tbl.width * sizeof(ary[0]));
3552 ary[0] = hil.list[ii]->handle;
3553 ary[1] = strtab(hil.list[ii]->opserv_level);
3554 tbl.contents[++ii] = ary;
3556 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3557 reply("MSG_MATCH_COUNT", hil.used);
3558 for (ii = 0; ii < hil.used; ii++)
3559 free(tbl.contents[ii]);
3564 static NICKSERV_FUNC(cmd_search)
3566 struct nickserv_discrim *discrim;
3567 discrim_search_func action;
3568 struct svccmd *subcmd;
3569 unsigned int matches;
3572 NICKSERV_MIN_PARMS(3);
3573 sprintf(buf, "search %s", argv[1]);
3574 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3575 if (!irccasecmp(argv[1], "print"))
3576 action = search_print_func;
3577 else if (!irccasecmp(argv[1], "count"))
3578 action = search_count_func;
3579 else if (!irccasecmp(argv[1], "unregister"))
3580 action = search_unregister_func;
3582 reply("NSMSG_INVALID_ACTION", argv[1]);
3586 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3589 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3593 if (action == search_print_func)
3594 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3595 else if (action == search_count_func)
3596 discrim->limit = INT_MAX;
3598 matches = nickserv_discrim_search(discrim, action, user);
3601 reply("MSG_MATCH_COUNT", matches);
3603 reply("MSG_NO_MATCHES");
3609 static MODCMD_FUNC(cmd_checkpass)
3611 struct handle_info *hi;
3613 NICKSERV_MIN_PARMS(3);
3614 if (!(hi = get_handle_info(argv[1]))) {
3615 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3618 if (checkpass(argv[2], hi->passwd))
3619 reply("CHECKPASS_YES");
3621 reply("CHECKPASS_NO");
3626 static MODCMD_FUNC(cmd_checkemail)
3628 struct handle_info *hi;
3630 NICKSERV_MIN_PARMS(3);
3631 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
3634 if (!hi->email_addr)
3635 reply("CHECKEMAIL_NOT_SET");
3636 else if (!irccasecmp(argv[2], hi->email_addr))
3637 reply("CHECKEMAIL_YES");
3639 reply("CHECKEMAIL_NO");
3645 nickserv_db_read_handle(const char *handle, dict_t obj)
3648 struct string_list *masks, *slist;
3649 struct handle_info *hi;
3650 struct userNode *authed_users;
3651 struct userData *channel_list;
3656 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3657 id = str ? strtoul(str, NULL, 0) : 0;
3658 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3660 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3663 if ((hi = get_handle_info(handle))) {
3664 authed_users = hi->users;
3665 channel_list = hi->channels;
3667 hi->channels = NULL;
3668 dict_remove(nickserv_handle_dict, hi->handle);
3670 authed_users = NULL;
3671 channel_list = NULL;
3673 hi = register_handle(handle, str, id);
3675 hi->users = authed_users;
3676 while (authed_users) {
3677 authed_users->handle_info = hi;
3678 authed_users = authed_users->next_authed;
3681 hi->channels = channel_list;
3682 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3683 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3684 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3685 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3686 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3687 hi->language = language_find(str ? str : "C");
3688 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3689 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3690 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3692 hi->infoline = strdup(str);
3693 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3694 hi->registered = str ? strtoul(str, NULL, 0) : now;
3695 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3696 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
3697 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
3698 hi->karma = str ? strtoul(str, NULL, 0) : 0;
3699 /* We want to read the nicks even if disable_nicks is set. This is so
3700 * that we don't lose the nick data entirely. */
3701 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3703 for (ii=0; ii<slist->used; ii++)
3704 register_nick(slist->list[ii], hi);
3706 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3708 for (ii=0; str[ii]; ii++)
3709 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3711 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3712 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3713 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3714 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3715 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3716 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3717 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3719 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3721 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3722 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3724 nickserv_set_email_addr(hi, str);
3725 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3727 hi->epithet = strdup(str);
3728 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3730 hi->fakehost = strdup(str);
3731 str = database_get_data(obj, KEY_FAKEIDENT, RECDB_QSTRING);
3733 hi->fakeident = strdup(str);
3734 /* Read the "cookie" sub-database (if it exists). */
3735 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3737 const char *data, *type, *expires, *cookie_str;
3738 struct handle_cookie *cookie;
3740 cookie = calloc(1, sizeof(*cookie));
3741 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3742 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3743 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3744 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3745 if (!type || !expires || !cookie_str) {
3746 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3749 if (!irccasecmp(type, KEY_ACTIVATION))
3750 cookie->type = ACTIVATION;
3751 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3752 cookie->type = PASSWORD_CHANGE;
3753 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3754 cookie->type = EMAIL_CHANGE;
3755 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3756 cookie->type = ALLOWAUTH;
3758 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3761 cookie->expires = strtoul(expires, NULL, 0);
3762 if (cookie->expires < now)
3765 cookie->data = strdup(data);
3766 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3770 nickserv_bake_cookie(cookie);
3772 nickserv_free_cookie(cookie);
3774 /* Read the "notes" sub-database (if it exists). */
3775 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
3778 struct handle_note *last_note;
3779 struct handle_note *note;
3782 for (it = dict_first(subdb); it; it = iter_next(it)) {
3783 const char *expires;
3787 const char *note_id;
3790 note_id = iter_key(it);
3791 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
3793 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", note_id, hi->handle);
3796 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
3797 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
3798 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
3799 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
3800 if (!setter || !text || !set) {
3801 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", note_id, hi->handle);
3804 note = calloc(1, sizeof(*note) + strlen(text));
3806 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
3807 note->set = strtoul(set, NULL, 10);
3808 note->id = strtoul(note_id, NULL, 10);
3809 safestrncpy(note->setter, setter, sizeof(note->setter));
3810 strcpy(note->note, text);
3812 last_note->next = note;
3821 nickserv_saxdb_read(dict_t db) {
3823 struct record_data *rd;
3825 for (it=dict_first(db); it; it=iter_next(it)) {
3827 nickserv_db_read_handle(iter_key(it), rd->d.object);
3832 static NICKSERV_FUNC(cmd_mergedb)
3834 struct timeval start, stop;
3837 NICKSERV_MIN_PARMS(2);
3838 gettimeofday(&start, NULL);
3839 if (!(db = parse_database(argv[1]))) {
3840 reply("NSMSG_DB_UNREADABLE", argv[1]);
3843 nickserv_saxdb_read(db);
3845 gettimeofday(&stop, NULL);
3846 stop.tv_sec -= start.tv_sec;
3847 stop.tv_usec -= start.tv_usec;
3848 if (stop.tv_usec < 0) {
3850 stop.tv_usec += 1000000;
3852 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
3857 expire_handles(UNUSED_ARG(void *data))
3859 dict_iterator_t it, next;
3860 unsigned long expiry;
3861 struct handle_info *hi;
3863 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3864 next = iter_next(it);
3866 if ((hi->opserv_level > 0)
3868 || HANDLE_FLAGGED(hi, FROZEN)
3869 || HANDLE_FLAGGED(hi, NODELETE)) {
3872 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3873 if ((now - hi->lastseen) > expiry) {
3874 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3875 nickserv_unregister_handle(hi, NULL);
3879 if (nickserv_conf.handle_expire_frequency)
3880 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3884 nickserv_load_dict(const char *fname)
3888 if (!(file = fopen(fname, "r"))) {
3889 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3892 while (fgets(line, sizeof(line), file)) {
3895 if (line[strlen(line)-1] == '\n')
3896 line[strlen(line)-1] = 0;
3897 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3900 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3903 static enum reclaim_action
3904 reclaim_action_from_string(const char *str) {
3906 return RECLAIM_NONE;
3907 else if (!irccasecmp(str, "warn"))
3908 return RECLAIM_WARN;
3909 else if (!irccasecmp(str, "svsnick"))
3910 return RECLAIM_SVSNICK;
3911 else if (!irccasecmp(str, "kill"))
3912 return RECLAIM_KILL;
3914 return RECLAIM_NONE;
3918 nickserv_conf_read(void)
3920 dict_t conf_node, child;
3924 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3925 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3928 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3930 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3931 if (nickserv_conf.valid_handle_regex_set)
3932 regfree(&nickserv_conf.valid_handle_regex);
3934 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3935 nickserv_conf.valid_handle_regex_set = !err;
3936 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3938 nickserv_conf.valid_handle_regex_set = 0;
3940 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3941 if (nickserv_conf.valid_nick_regex_set)
3942 regfree(&nickserv_conf.valid_nick_regex);
3944 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3945 nickserv_conf.valid_nick_regex_set = !err;
3946 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3948 nickserv_conf.valid_nick_regex_set = 0;
3950 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3952 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3953 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3954 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3955 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3956 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3957 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3958 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3959 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3960 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3961 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3962 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3963 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3964 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3965 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3966 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3967 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3968 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3969 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3970 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3971 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3972 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3973 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3974 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3975 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3976 str = database_get_data(conf_node, KEY_SET_FAKEIDENT_LEVEL, RECDB_QSTRING);
3977 nickserv_conf.set_fakeident_level = str ? strtoul(str, NULL, 0) : 1000;
3978 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3980 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3981 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3982 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3984 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3985 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3986 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3988 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3989 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3990 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3991 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3992 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3993 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3994 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3995 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3996 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
3997 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
3998 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
4001 nickserv_conf.ounregister_flags = 0;
4003 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
4006 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
4008 str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING);
4010 nickserv_conf.handle_ts_mode = TS_IGNORE;
4011 else if (!irccasecmp(str, "ircu"))
4012 nickserv_conf.handle_ts_mode = TS_IRCU;
4014 nickserv_conf.handle_ts_mode = TS_IGNORE;
4015 if (!nickserv_conf.disable_nicks) {
4016 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
4017 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4018 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
4019 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
4020 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
4021 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4022 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
4023 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
4025 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
4026 for (it=dict_first(child); it; it=iter_next(it)) {
4027 const char *key = iter_key(it), *value;
4031 if (!strncasecmp(key, "uc_", 3))
4032 flag = toupper(key[3]);
4033 else if (!strncasecmp(key, "lc_", 3))
4034 flag = tolower(key[3]);
4038 if ((pos = handle_inverse_flags[flag])) {
4039 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
4040 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
4043 if (nickserv_conf.weak_password_dict)
4044 dict_delete(nickserv_conf.weak_password_dict);
4045 nickserv_conf.weak_password_dict = dict_new();
4046 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
4047 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
4048 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
4049 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
4051 nickserv_load_dict(str);
4052 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
4053 if (nickserv && str)
4054 NickChange(nickserv, str, 0);
4055 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
4056 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
4057 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
4058 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
4059 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
4060 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
4061 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
4062 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
4063 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
4064 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
4065 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
4066 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
4067 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
4068 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
4069 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
4070 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
4071 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
4072 titlehost_suffix = str ? str : "example.net";
4073 str = conf_get_data("server/network", RECDB_QSTRING);
4074 nickserv_conf.network_name = str ? str : "some IRC network";
4075 if (!nickserv_conf.auth_policer_params) {
4076 nickserv_conf.auth_policer_params = policer_params_new();
4077 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
4078 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
4080 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
4081 for (it=dict_first(child); it; it=iter_next(it))
4082 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
4086 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
4088 char newnick[NICKLEN+1];
4097 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4099 case RECLAIM_SVSNICK:
4101 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
4102 } while (GetUserH(newnick));
4103 irc_svsnick(nickserv, user, newnick);
4106 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
4107 DelUser(user, nickserv, 1, msg);
4113 nickserv_reclaim_p(void *data) {
4114 struct userNode *user = data;
4115 struct nick_info *ni = get_nick_info(user->nick);
4117 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4121 check_user_nick(struct userNode *user) {
4122 struct nick_info *ni;
4123 user->modes &= ~FLAGS_REGNICK;
4124 if (!(ni = get_nick_info(user->nick)))
4126 if (user->handle_info == ni->owner) {
4127 user->modes |= FLAGS_REGNICK;
4131 if (nickserv_conf.warn_nick_owned)
4132 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4133 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
4135 if (nickserv_conf.auto_reclaim_delay)
4136 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
4138 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4142 handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
4144 struct handle_info *hi = NULL;
4147 hi = dict_find(nickserv_handle_dict, stamp, NULL);
4148 if ((hi == NULL) && (serial != 0)) {
4150 inttobase64(id, serial, IDLEN);
4151 hi = dict_find(nickserv_id_dict, id, NULL);
4155 if ((nickserv_conf.handle_ts_mode == TS_IRCU)
4156 && (timestamp != hi->registered)) {
4159 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
4162 set_user_handle_info(user, hi, 0);
4164 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial);
4169 handle_nick_change(struct userNode *user, const char *old_nick)
4171 struct handle_info *hi;
4173 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4174 dict_remove(nickserv_allow_auth_dict, old_nick);
4175 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4177 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4178 check_user_nick(user);
4182 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
4184 dict_remove(nickserv_allow_auth_dict, user->nick);
4185 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4186 set_user_handle_info(user, NULL, 0);
4189 static struct modcmd *
4190 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4192 if (min_level > 0) {
4194 sprintf(buf, "%u", min_level);
4195 if (must_be_qualified) {
4196 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4198 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4200 } else if (min_level == 0) {
4201 if (must_be_qualified) {
4202 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4204 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4207 if (must_be_qualified) {
4208 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4210 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4216 nickserv_db_cleanup(void)
4218 unreg_del_user_func(nickserv_remove_user);
4219 userList_clean(&curr_helpers);
4220 policer_params_delete(nickserv_conf.auth_policer_params);
4221 dict_delete(nickserv_handle_dict);
4222 dict_delete(nickserv_nick_dict);
4223 dict_delete(nickserv_opt_dict);
4224 dict_delete(nickserv_allow_auth_dict);
4225 dict_delete(nickserv_email_dict);
4226 dict_delete(nickserv_id_dict);
4227 dict_delete(nickserv_conf.weak_password_dict);
4228 free(auth_func_list);
4229 free(unreg_func_list);
4231 free(allowauth_func_list);
4232 free(handle_merge_func_list);
4233 free(failpw_func_list);
4234 if (nickserv_conf.valid_handle_regex_set)
4235 regfree(&nickserv_conf.valid_handle_regex);
4236 if (nickserv_conf.valid_nick_regex_set)
4237 regfree(&nickserv_conf.valid_nick_regex);
4241 init_nickserv(const char *nick)
4244 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4245 reg_new_user_func(check_user_nick);
4246 reg_nick_change_func(handle_nick_change);
4247 reg_del_user_func(nickserv_remove_user);
4248 reg_account_func(handle_account);
4250 /* set up handle_inverse_flags */
4251 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4252 for (i=0; handle_flags[i]; i++) {
4253 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4254 flag_access_levels[i] = 0;
4257 conf_register_reload(nickserv_conf_read);
4258 nickserv_opt_dict = dict_new();
4259 nickserv_email_dict = dict_new();
4260 dict_set_free_keys(nickserv_email_dict, free);
4261 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4263 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4264 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4265 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4266 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4267 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4268 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4269 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4270 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4271 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4272 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4273 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4274 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4275 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4276 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4277 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4278 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4279 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4280 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4281 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4282 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4283 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4284 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4285 if (!nickserv_conf.disable_nicks) {
4286 /* nick management commands */
4287 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4288 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4289 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4290 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4291 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4292 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4294 if (nickserv_conf.email_enabled) {
4295 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4296 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4297 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4298 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4299 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
4300 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4302 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4303 /* miscellaneous commands */
4304 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4305 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4306 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4307 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4308 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4309 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
4311 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4312 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4313 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4314 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4315 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4316 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4317 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4318 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4319 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4320 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4321 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4322 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4323 if (titlehost_suffix) {
4324 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4325 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4326 dict_insert(nickserv_opt_dict, "FAKEIDENT", opt_fakeident);
4328 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4329 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4330 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4331 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
4333 nickserv_handle_dict = dict_new();
4334 dict_set_free_keys(nickserv_handle_dict, free);
4335 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4337 nickserv_id_dict = dict_new();
4338 dict_set_free_keys(nickserv_id_dict, free);
4340 nickserv_nick_dict = dict_new();
4341 dict_set_free_data(nickserv_nick_dict, free);
4343 nickserv_allow_auth_dict = dict_new();
4345 userList_init(&curr_helpers);
4348 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4349 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4350 nickserv_service = service_register(nickserv);
4352 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4353 reg_exit_func(nickserv_db_cleanup);
4354 if(nickserv_conf.handle_expire_frequency)
4355 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4356 message_register_table(msgtab);