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 nick_info *ni;
2767 struct userNode *victim;
2769 NICKSERV_MIN_PARMS(2);
2770 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2772 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2775 if (ni->owner != user->handle_info) {
2776 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2779 victim = GetUserH(ni->nick);
2781 reply("MSG_NICK_UNKNOWN", ni->nick);
2784 if (victim == user) {
2785 reply("NSMSG_NICK_USER_YOU");
2788 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2789 switch (nickserv_conf.reclaim_action) {
2790 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2791 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2792 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2793 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2798 static NICKSERV_FUNC(cmd_unregnick)
2801 struct handle_info *hi;
2802 struct nick_info *ni;
2804 hi = user->handle_info;
2805 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2806 ni = dict_find(nickserv_nick_dict, nick, NULL);
2808 reply("NSMSG_UNKNOWN_NICK", nick);
2811 if (hi != ni->owner) {
2812 reply("NSMSG_NOT_YOUR_NICK", nick);
2815 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2820 static NICKSERV_FUNC(cmd_ounregnick)
2822 struct nick_info *ni;
2824 NICKSERV_MIN_PARMS(2);
2825 if (!(ni = get_nick_info(argv[1]))) {
2826 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2829 if (!oper_outranks(user, ni->owner))
2831 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2836 static NICKSERV_FUNC(cmd_unregister)
2838 struct handle_info *hi;
2841 NICKSERV_MIN_PARMS(2);
2842 hi = user->handle_info;
2845 if (checkpass(passwd, hi->passwd)) {
2846 nickserv_unregister_handle(hi, user);
2849 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2850 reply("NSMSG_PASSWORD_INVALID");
2855 static NICKSERV_FUNC(cmd_ounregister)
2857 struct handle_info *hi;
2858 char reason[MAXLEN];
2861 NICKSERV_MIN_PARMS(2);
2862 if (!(hi = get_victim_oper(user, argv[1])))
2865 if (HANDLE_FLAGGED(hi, NODELETE)) {
2866 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
2870 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
2872 ((hi->flags & nickserv_conf.ounregister_flags)
2874 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
2875 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
2879 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
2880 global_message(MESSAGE_RECIPIENT_STAFF, reason);
2881 nickserv_unregister_handle(hi, user);
2885 static NICKSERV_FUNC(cmd_status)
2887 if (nickserv_conf.disable_nicks) {
2888 reply("NSMSG_GLOBAL_STATS_NONICK",
2889 dict_size(nickserv_handle_dict));
2891 if (user->handle_info) {
2893 struct nick_info *ni;
2894 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2895 reply("NSMSG_HANDLE_STATS", cnt);
2897 reply("NSMSG_HANDLE_NONE");
2899 reply("NSMSG_GLOBAL_STATS",
2900 dict_size(nickserv_handle_dict),
2901 dict_size(nickserv_nick_dict));
2906 static NICKSERV_FUNC(cmd_ghost)
2908 struct userNode *target;
2909 char reason[MAXLEN];
2911 NICKSERV_MIN_PARMS(2);
2912 if (!(target = GetUserH(argv[1]))) {
2913 reply("MSG_NICK_UNKNOWN", argv[1]);
2916 if (target == user) {
2917 reply("NSMSG_CANNOT_GHOST_SELF");
2920 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2921 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2924 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2925 DelUser(target, nickserv, 1, reason);
2926 reply("NSMSG_GHOST_KILLED", argv[1]);
2930 static NICKSERV_FUNC(cmd_vacation)
2932 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2933 reply("NSMSG_ON_VACATION");
2937 static NICKSERV_FUNC(cmd_addnote)
2939 struct handle_info *hi;
2940 unsigned long duration;
2943 struct handle_note *prev;
2944 struct handle_note *note;
2946 /* Parse parameters and figure out values for note's fields. */
2947 NICKSERV_MIN_PARMS(4);
2948 hi = get_victim_oper(user, argv[1]);
2951 if(!strcmp(argv[2], "0"))
2953 else if(!(duration = ParseInterval(argv[2])))
2955 reply("MSG_INVALID_DURATION", argv[2]);
2958 if (duration > 2*365*86400) {
2959 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
2962 unsplit_string(argv + 3, argc - 3, text);
2963 WALK_NOTES(hi, prev, note) {}
2964 id = prev ? (prev->id + 1) : 1;
2966 /* Create the new note structure. */
2967 note = calloc(1, sizeof(*note) + strlen(text));
2969 note->expires = duration ? (now + duration) : 0;
2972 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
2973 strcpy(note->note, text);
2978 reply("NSMSG_NOTE_ADDED", id, hi->handle);
2982 static NICKSERV_FUNC(cmd_delnote)
2984 struct handle_info *hi;
2985 struct handle_note *prev;
2986 struct handle_note *note;
2989 NICKSERV_MIN_PARMS(3);
2990 hi = get_victim_oper(user, argv[1]);
2993 id = strtoul(argv[2], NULL, 10);
2994 WALK_NOTES(hi, prev, note) {
2995 if (id == note->id) {
2997 prev->next = note->next;
2999 hi->notes = note->next;
3001 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
3005 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
3010 nickserv_saxdb_write(struct saxdb_context *ctx) {
3012 struct handle_info *hi;
3015 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3017 assert(hi->id != 0);
3018 saxdb_start_record(ctx, iter_key(it), 0);
3020 struct handle_cookie *cookie = hi->cookie;
3023 switch (cookie->type) {
3024 case ACTIVATION: type = KEY_ACTIVATION; break;
3025 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
3026 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
3027 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
3028 default: type = NULL; break;
3031 saxdb_start_record(ctx, KEY_COOKIE, 0);
3032 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
3033 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
3035 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
3036 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
3037 saxdb_end_record(ctx);
3041 struct handle_note *prev, *note;
3042 saxdb_start_record(ctx, KEY_NOTES, 0);
3043 WALK_NOTES(hi, prev, note) {
3044 snprintf(flags, sizeof(flags), "%d", note->id);
3045 saxdb_start_record(ctx, flags, 0);
3047 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
3048 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
3049 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
3050 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
3051 saxdb_end_record(ctx);
3053 saxdb_end_record(ctx);
3056 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
3058 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
3060 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
3062 saxdb_write_string(ctx, KEY_FAKEIDENT, hi->fakeident);
3066 for (ii=flen=0; handle_flags[ii]; ++ii)
3067 if (hi->flags & (1 << ii))
3068 flags[flen++] = handle_flags[ii];
3070 saxdb_write_string(ctx, KEY_FLAGS, flags);
3072 saxdb_write_int(ctx, KEY_ID, hi->id);
3074 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
3075 if (hi->last_quit_host[0])
3076 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
3077 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
3079 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
3080 if (hi->masks->used)
3081 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
3083 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
3085 struct string_list *slist;
3086 struct nick_info *ni;
3088 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
3089 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
3090 saxdb_write_string_list(ctx, KEY_NICKS, slist);
3094 if (hi->opserv_level)
3095 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
3096 if (hi->language != lang_C)
3097 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
3098 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
3099 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
3100 if (hi->screen_width)
3101 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
3102 if (hi->table_width)
3103 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
3104 flags[0] = hi->userlist_style;
3106 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
3107 saxdb_end_record(ctx);
3112 static handle_merge_func_t *handle_merge_func_list;
3113 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
3116 reg_handle_merge_func(handle_merge_func_t func)
3118 if (handle_merge_func_used == handle_merge_func_size) {
3119 if (handle_merge_func_size) {
3120 handle_merge_func_size <<= 1;
3121 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
3123 handle_merge_func_size = 8;
3124 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
3127 handle_merge_func_list[handle_merge_func_used++] = func;
3130 static NICKSERV_FUNC(cmd_merge)
3132 struct handle_info *hi_from, *hi_to;
3133 struct userNode *last_user;
3134 struct userData *cList, *cListNext;
3135 unsigned int ii, jj, n;
3136 char buffer[MAXLEN];
3138 NICKSERV_MIN_PARMS(3);
3140 if (!(hi_from = get_victim_oper(user, argv[1])))
3142 if (!(hi_to = get_victim_oper(user, argv[2])))
3144 if (hi_to == hi_from) {
3145 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
3149 for (n=0; n<handle_merge_func_used; n++)
3150 handle_merge_func_list[n](user, hi_to, hi_from);
3152 /* Append "from" handle's nicks to "to" handle's nick list. */
3154 struct nick_info *last_ni;
3155 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3156 last_ni->next = hi_from->nicks;
3158 while (hi_from->nicks) {
3159 hi_from->nicks->owner = hi_to;
3160 hi_from->nicks = hi_from->nicks->next;
3163 /* Merge the hostmasks. */
3164 for (ii=0; ii<hi_from->masks->used; ii++) {
3165 char *mask = hi_from->masks->list[ii];
3166 for (jj=0; jj<hi_to->masks->used; jj++)
3167 if (match_ircglobs(hi_to->masks->list[jj], mask))
3169 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3170 string_list_append(hi_to->masks, strdup(mask));
3173 /* Merge the lists of authed users. */
3175 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3176 last_user->next_authed = hi_from->users;
3178 hi_to->users = hi_from->users;
3180 /* Repoint the old "from" handle's users. */
3181 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3182 last_user->handle_info = hi_to;
3184 hi_from->users = NULL;
3186 /* Merge channel userlists. */
3187 for (cList=hi_from->channels; cList; cList=cListNext) {
3188 struct userData *cList2;
3189 cListNext = cList->u_next;
3190 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3191 if (cList->channel == cList2->channel)
3193 if (cList2 && (cList2->access >= cList->access)) {
3194 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);
3195 /* keep cList2 in hi_to; remove cList from hi_from */
3196 del_channel_user(cList, 1);
3199 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);
3200 /* remove the lower-ranking cList2 from hi_to */
3201 del_channel_user(cList2, 1);
3203 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3205 /* cList needs to be moved from hi_from to hi_to */
3206 cList->handle = hi_to;
3207 /* Remove from linked list for hi_from */
3208 assert(!cList->u_prev);
3209 hi_from->channels = cList->u_next;
3211 cList->u_next->u_prev = cList->u_prev;
3212 /* Add to linked list for hi_to */
3213 cList->u_prev = NULL;
3214 cList->u_next = hi_to->channels;
3215 if (hi_to->channels)
3216 hi_to->channels->u_prev = cList;
3217 hi_to->channels = cList;
3221 /* Do they get an OpServ level promotion? */
3222 if (hi_from->opserv_level > hi_to->opserv_level)
3223 hi_to->opserv_level = hi_from->opserv_level;
3225 /* What about last seen time? */
3226 if (hi_from->lastseen > hi_to->lastseen)
3227 hi_to->lastseen = hi_from->lastseen;
3229 /* New karma is the sum of the two original karmas. */
3230 hi_to->karma += hi_from->karma;
3232 /* Does a fakehost carry over? (This intentionally doesn't set it
3233 * for users previously attached to hi_to. They'll just have to
3236 if (hi_from->fakehost && !hi_to->fakehost)
3237 hi_to->fakehost = strdup(hi_from->fakehost);
3238 if (hi_from->fakeident && !hi_to->fakeident)
3239 hi_to->fakeident = strdup(hi_from->fakeident);
3241 /* Notify of success. */
3242 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3243 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3244 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3246 /* Unregister the "from" handle. */
3247 nickserv_unregister_handle(hi_from, NULL);
3252 struct nickserv_discrim {
3253 unsigned long flags_on, flags_off;
3254 unsigned long min_registered, max_registered;
3255 unsigned long lastseen;
3257 int min_level, max_level;
3258 int min_karma, max_karma;
3259 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3260 const char *nickmask;
3261 const char *hostmask;
3262 const char *fakehostmask;
3263 const char *fakeidentmask;
3264 const char *handlemask;
3265 const char *emailmask;
3268 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
3270 struct discrim_apply_info {
3271 struct nickserv_discrim *discrim;
3272 discrim_search_func func;
3273 struct userNode *source;
3274 unsigned int matched;
3277 static struct nickserv_discrim *
3278 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3281 struct nickserv_discrim *discrim;
3283 discrim = malloc(sizeof(*discrim));
3284 memset(discrim, 0, sizeof(*discrim));
3285 discrim->min_level = 0;
3286 discrim->max_level = INT_MAX;
3287 discrim->limit = 50;
3288 discrim->min_registered = 0;
3289 discrim->max_registered = ULONG_MAX;
3290 discrim->lastseen = ULONG_MAX;
3291 discrim->min_karma = INT_MIN;
3292 discrim->max_karma = INT_MAX;
3294 for (i=0; i<argc; i++) {
3295 if (i == argc - 1) {
3296 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3299 if (!irccasecmp(argv[i], "limit")) {
3300 discrim->limit = strtoul(argv[++i], NULL, 0);
3301 } else if (!irccasecmp(argv[i], "flags")) {
3302 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3303 } else if (!irccasecmp(argv[i], "registered")) {
3304 const char *cmp = argv[++i];
3305 if (cmp[0] == '<') {
3306 if (cmp[1] == '=') {
3307 discrim->min_registered = now - ParseInterval(cmp+2);
3309 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3311 } else if (cmp[0] == '=') {
3312 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3313 } else if (cmp[0] == '>') {
3314 if (cmp[1] == '=') {
3315 discrim->max_registered = now - ParseInterval(cmp+2);
3317 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3320 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3322 } else if (!irccasecmp(argv[i], "seen")) {
3323 discrim->lastseen = now - ParseInterval(argv[++i]);
3324 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3325 discrim->nickmask = argv[++i];
3326 } else if (!irccasecmp(argv[i], "hostmask")) {
3328 if (!irccasecmp(argv[i], "exact")) {
3329 if (i == argc - 1) {
3330 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3333 discrim->hostmask_type = EXACT;
3334 } else if (!irccasecmp(argv[i], "subset")) {
3335 if (i == argc - 1) {
3336 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3339 discrim->hostmask_type = SUBSET;
3340 } else if (!irccasecmp(argv[i], "superset")) {
3341 if (i == argc - 1) {
3342 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3345 discrim->hostmask_type = SUPERSET;
3346 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3347 if (i == argc - 1) {
3348 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3351 discrim->hostmask_type = LASTQUIT;
3354 discrim->hostmask_type = SUPERSET;
3356 discrim->hostmask = argv[++i];
3357 } else if (!irccasecmp(argv[i], "fakehost")) {
3358 if (!irccasecmp(argv[++i], "*")) {
3359 discrim->fakehostmask = 0;
3361 discrim->fakehostmask = argv[i];
3363 } else if (!irccasecmp(argv[i], "fakeident")) {
3364 if (!irccasecmp(argv[++i], "*")) {
3365 discrim->fakeidentmask = 0;
3367 discrim->fakeidentmask = argv[i];
3369 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3370 if (!irccasecmp(argv[++i], "*")) {
3371 discrim->handlemask = 0;
3373 discrim->handlemask = argv[i];
3375 } else if (!irccasecmp(argv[i], "email")) {
3376 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3377 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3379 } else if (!irccasecmp(argv[++i], "*")) {
3380 discrim->emailmask = 0;
3382 discrim->emailmask = argv[i];
3384 } else if (!irccasecmp(argv[i], "access")) {
3385 const char *cmp = argv[++i];
3386 if (cmp[0] == '<') {
3387 if (discrim->min_level == 0) discrim->min_level = 1;
3388 if (cmp[1] == '=') {
3389 discrim->max_level = strtoul(cmp+2, NULL, 0);
3391 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3393 } else if (cmp[0] == '=') {
3394 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3395 } else if (cmp[0] == '>') {
3396 if (cmp[1] == '=') {
3397 discrim->min_level = strtoul(cmp+2, NULL, 0);
3399 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3402 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3404 } else if (!irccasecmp(argv[i], "karma")) {
3405 const char *cmp = argv[++i];
3406 if (cmp[0] == '<') {
3407 if (cmp[1] == '=') {
3408 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3410 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3412 } else if (cmp[0] == '=') {
3413 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3414 } else if (cmp[0] == '>') {
3415 if (cmp[1] == '=') {
3416 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3418 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3421 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3424 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3435 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3437 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3438 || (discrim->flags_off & hi->flags)
3439 || (discrim->min_registered > hi->registered)
3440 || (discrim->max_registered < hi->registered)
3441 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3442 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3443 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3444 || (discrim->fakeidentmask && (!hi->fakeident || !match_ircglob(hi->fakeident, discrim->fakeidentmask)))
3445 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3446 || (discrim->min_level > hi->opserv_level)
3447 || (discrim->max_level < hi->opserv_level)
3448 || (discrim->min_karma > hi->karma)
3449 || (discrim->max_karma < hi->karma)
3453 if (discrim->hostmask) {
3455 for (i=0; i<hi->masks->used; i++) {
3456 const char *mask = hi->masks->list[i];
3457 if ((discrim->hostmask_type == SUBSET)
3458 && (match_ircglobs(discrim->hostmask, mask))) break;
3459 else if ((discrim->hostmask_type == EXACT)
3460 && !irccasecmp(discrim->hostmask, mask)) break;
3461 else if ((discrim->hostmask_type == SUPERSET)
3462 && (match_ircglobs(mask, discrim->hostmask))) break;
3463 else if ((discrim->hostmask_type == LASTQUIT)
3464 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3466 if (i==hi->masks->used) return 0;
3468 if (discrim->nickmask) {
3469 struct nick_info *nick = hi->nicks;
3471 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3474 if (!nick) return 0;
3480 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3482 dict_iterator_t it, next;
3483 unsigned int matched;
3485 for (it = dict_first(nickserv_handle_dict), matched = 0;
3486 it && (matched < discrim->limit);
3488 next = iter_next(it);
3489 if (nickserv_discrim_match(discrim, iter_data(it))) {
3490 dsf(source, iter_data(it));
3498 search_print_func(struct userNode *source, struct handle_info *match)
3500 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3504 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3509 search_unregister_func (struct userNode *source, struct handle_info *match)
3511 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3512 nickserv_unregister_handle(match, source);
3516 nickserv_sort_accounts_by_access(const void *a, const void *b)
3518 const struct handle_info *hi_a = *(const struct handle_info**)a;
3519 const struct handle_info *hi_b = *(const struct handle_info**)b;
3520 if (hi_a->opserv_level != hi_b->opserv_level)
3521 return hi_b->opserv_level - hi_a->opserv_level;
3522 return irccasecmp(hi_a->handle, hi_b->handle);
3526 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3528 struct handle_info_list hil;
3529 struct helpfile_table tbl;
3534 memset(&hil, 0, sizeof(hil));
3535 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3536 struct handle_info *hi = iter_data(it);
3537 if (hi->opserv_level)
3538 handle_info_list_append(&hil, hi);
3540 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3541 tbl.length = hil.used + 1;
3543 tbl.flags = TABLE_NO_FREE;
3544 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3545 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3548 for (ii = 0; ii < hil.used; ) {
3549 ary = malloc(tbl.width * sizeof(ary[0]));
3550 ary[0] = hil.list[ii]->handle;
3551 ary[1] = strtab(hil.list[ii]->opserv_level);
3552 tbl.contents[++ii] = ary;
3554 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3555 reply("MSG_MATCH_COUNT", hil.used);
3556 for (ii = 0; ii < hil.used; ii++)
3557 free(tbl.contents[ii]);
3562 static NICKSERV_FUNC(cmd_search)
3564 struct nickserv_discrim *discrim;
3565 discrim_search_func action;
3566 struct svccmd *subcmd;
3567 unsigned int matches;
3570 NICKSERV_MIN_PARMS(3);
3571 sprintf(buf, "search %s", argv[1]);
3572 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3573 if (!irccasecmp(argv[1], "print"))
3574 action = search_print_func;
3575 else if (!irccasecmp(argv[1], "count"))
3576 action = search_count_func;
3577 else if (!irccasecmp(argv[1], "unregister"))
3578 action = search_unregister_func;
3580 reply("NSMSG_INVALID_ACTION", argv[1]);
3584 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3587 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3591 if (action == search_print_func)
3592 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3593 else if (action == search_count_func)
3594 discrim->limit = INT_MAX;
3596 matches = nickserv_discrim_search(discrim, action, user);
3599 reply("MSG_MATCH_COUNT", matches);
3601 reply("MSG_NO_MATCHES");
3607 static MODCMD_FUNC(cmd_checkpass)
3609 struct handle_info *hi;
3611 NICKSERV_MIN_PARMS(3);
3612 if (!(hi = get_handle_info(argv[1]))) {
3613 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3616 if (checkpass(argv[2], hi->passwd))
3617 reply("CHECKPASS_YES");
3619 reply("CHECKPASS_NO");
3624 static MODCMD_FUNC(cmd_checkemail)
3626 struct handle_info *hi;
3628 NICKSERV_MIN_PARMS(3);
3629 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
3632 if (!hi->email_addr)
3633 reply("CHECKEMAIL_NOT_SET");
3634 else if (!irccasecmp(argv[2], hi->email_addr))
3635 reply("CHECKEMAIL_YES");
3637 reply("CHECKEMAIL_NO");
3643 nickserv_db_read_handle(const char *handle, dict_t obj)
3646 struct string_list *masks, *slist;
3647 struct handle_info *hi;
3648 struct userNode *authed_users;
3649 struct userData *channel_list;
3654 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3655 id = str ? strtoul(str, NULL, 0) : 0;
3656 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3658 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3661 if ((hi = get_handle_info(handle))) {
3662 authed_users = hi->users;
3663 channel_list = hi->channels;
3665 hi->channels = NULL;
3666 dict_remove(nickserv_handle_dict, hi->handle);
3668 authed_users = NULL;
3669 channel_list = NULL;
3671 hi = register_handle(handle, str, id);
3673 hi->users = authed_users;
3674 while (authed_users) {
3675 authed_users->handle_info = hi;
3676 authed_users = authed_users->next_authed;
3679 hi->channels = channel_list;
3680 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3681 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3682 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3683 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3684 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3685 hi->language = language_find(str ? str : "C");
3686 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3687 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3688 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3690 hi->infoline = strdup(str);
3691 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3692 hi->registered = str ? strtoul(str, NULL, 0) : now;
3693 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3694 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
3695 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
3696 hi->karma = str ? strtoul(str, NULL, 0) : 0;
3697 /* We want to read the nicks even if disable_nicks is set. This is so
3698 * that we don't lose the nick data entirely. */
3699 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3701 for (ii=0; ii<slist->used; ii++)
3702 register_nick(slist->list[ii], hi);
3704 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3706 for (ii=0; str[ii]; ii++)
3707 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3709 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3710 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3711 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3712 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3713 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3714 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3715 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3717 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3719 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3720 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3722 nickserv_set_email_addr(hi, str);
3723 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3725 hi->epithet = strdup(str);
3726 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3728 hi->fakehost = strdup(str);
3729 str = database_get_data(obj, KEY_FAKEIDENT, RECDB_QSTRING);
3731 hi->fakeident = strdup(str);
3732 /* Read the "cookie" sub-database (if it exists). */
3733 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3735 const char *data, *type, *expires, *cookie_str;
3736 struct handle_cookie *cookie;
3738 cookie = calloc(1, sizeof(*cookie));
3739 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3740 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3741 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3742 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3743 if (!type || !expires || !cookie_str) {
3744 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3747 if (!irccasecmp(type, KEY_ACTIVATION))
3748 cookie->type = ACTIVATION;
3749 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3750 cookie->type = PASSWORD_CHANGE;
3751 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3752 cookie->type = EMAIL_CHANGE;
3753 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3754 cookie->type = ALLOWAUTH;
3756 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3759 cookie->expires = strtoul(expires, NULL, 0);
3760 if (cookie->expires < now)
3763 cookie->data = strdup(data);
3764 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3768 nickserv_bake_cookie(cookie);
3770 nickserv_free_cookie(cookie);
3772 /* Read the "notes" sub-database (if it exists). */
3773 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
3776 struct handle_note *last_note;
3777 struct handle_note *note;
3780 for (it = dict_first(subdb); it; it = iter_next(it)) {
3781 const char *expires;
3785 const char *note_id;
3788 note_id = iter_key(it);
3789 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
3791 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", note_id, hi->handle);
3794 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
3795 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
3796 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
3797 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
3798 if (!setter || !text || !set) {
3799 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", note_id, hi->handle);
3802 note = calloc(1, sizeof(*note) + strlen(text));
3804 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
3805 note->set = strtoul(set, NULL, 10);
3806 note->id = strtoul(note_id, NULL, 10);
3807 safestrncpy(note->setter, setter, sizeof(note->setter));
3808 strcpy(note->note, text);
3810 last_note->next = note;
3819 nickserv_saxdb_read(dict_t db) {
3821 struct record_data *rd;
3823 for (it=dict_first(db); it; it=iter_next(it)) {
3825 nickserv_db_read_handle(iter_key(it), rd->d.object);
3830 static NICKSERV_FUNC(cmd_mergedb)
3832 struct timeval start, stop;
3835 NICKSERV_MIN_PARMS(2);
3836 gettimeofday(&start, NULL);
3837 if (!(db = parse_database(argv[1]))) {
3838 reply("NSMSG_DB_UNREADABLE", argv[1]);
3841 nickserv_saxdb_read(db);
3843 gettimeofday(&stop, NULL);
3844 stop.tv_sec -= start.tv_sec;
3845 stop.tv_usec -= start.tv_usec;
3846 if (stop.tv_usec < 0) {
3848 stop.tv_usec += 1000000;
3850 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
3855 expire_handles(UNUSED_ARG(void *data))
3857 dict_iterator_t it, next;
3858 unsigned long expiry;
3859 struct handle_info *hi;
3861 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3862 next = iter_next(it);
3864 if ((hi->opserv_level > 0)
3866 || HANDLE_FLAGGED(hi, FROZEN)
3867 || HANDLE_FLAGGED(hi, NODELETE)) {
3870 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3871 if ((now - hi->lastseen) > expiry) {
3872 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3873 nickserv_unregister_handle(hi, NULL);
3877 if (nickserv_conf.handle_expire_frequency)
3878 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3882 nickserv_load_dict(const char *fname)
3886 if (!(file = fopen(fname, "r"))) {
3887 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3890 while (fgets(line, sizeof(line), file)) {
3893 if (line[strlen(line)-1] == '\n')
3894 line[strlen(line)-1] = 0;
3895 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3898 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3901 static enum reclaim_action
3902 reclaim_action_from_string(const char *str) {
3904 return RECLAIM_NONE;
3905 else if (!irccasecmp(str, "warn"))
3906 return RECLAIM_WARN;
3907 else if (!irccasecmp(str, "svsnick"))
3908 return RECLAIM_SVSNICK;
3909 else if (!irccasecmp(str, "kill"))
3910 return RECLAIM_KILL;
3912 return RECLAIM_NONE;
3916 nickserv_conf_read(void)
3918 dict_t conf_node, child;
3922 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3923 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3926 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3928 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3929 if (nickserv_conf.valid_handle_regex_set)
3930 regfree(&nickserv_conf.valid_handle_regex);
3932 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3933 nickserv_conf.valid_handle_regex_set = !err;
3934 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3936 nickserv_conf.valid_handle_regex_set = 0;
3938 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3939 if (nickserv_conf.valid_nick_regex_set)
3940 regfree(&nickserv_conf.valid_nick_regex);
3942 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3943 nickserv_conf.valid_nick_regex_set = !err;
3944 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3946 nickserv_conf.valid_nick_regex_set = 0;
3948 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3950 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3951 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3952 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3953 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3954 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3955 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3956 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3957 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3958 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3959 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3960 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3961 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3962 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3963 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3964 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3965 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3966 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3967 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3968 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3969 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3970 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3971 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3972 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3973 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3974 str = database_get_data(conf_node, KEY_SET_FAKEIDENT_LEVEL, RECDB_QSTRING);
3975 nickserv_conf.set_fakeident_level = str ? strtoul(str, NULL, 0) : 1000;
3976 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3978 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3979 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3980 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3982 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3983 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3984 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3986 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3987 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3988 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3989 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3990 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3991 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3992 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3993 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3994 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
3995 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
3996 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
3999 nickserv_conf.ounregister_flags = 0;
4001 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
4004 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
4006 str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING);
4008 nickserv_conf.handle_ts_mode = TS_IGNORE;
4009 else if (!irccasecmp(str, "ircu"))
4010 nickserv_conf.handle_ts_mode = TS_IRCU;
4012 nickserv_conf.handle_ts_mode = TS_IGNORE;
4013 if (!nickserv_conf.disable_nicks) {
4014 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
4015 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4016 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
4017 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
4018 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
4019 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4020 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
4021 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
4023 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
4024 for (it=dict_first(child); it; it=iter_next(it)) {
4025 const char *key = iter_key(it), *value;
4029 if (!strncasecmp(key, "uc_", 3))
4030 flag = toupper(key[3]);
4031 else if (!strncasecmp(key, "lc_", 3))
4032 flag = tolower(key[3]);
4036 if ((pos = handle_inverse_flags[flag])) {
4037 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
4038 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
4041 if (nickserv_conf.weak_password_dict)
4042 dict_delete(nickserv_conf.weak_password_dict);
4043 nickserv_conf.weak_password_dict = dict_new();
4044 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
4045 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
4046 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
4047 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
4049 nickserv_load_dict(str);
4050 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
4051 if (nickserv && str)
4052 NickChange(nickserv, str, 0);
4053 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
4054 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
4055 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
4056 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
4057 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
4058 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
4059 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
4060 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
4061 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
4062 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
4063 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
4064 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
4065 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
4066 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
4067 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
4068 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
4069 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
4070 titlehost_suffix = str ? str : "example.net";
4071 str = conf_get_data("server/network", RECDB_QSTRING);
4072 nickserv_conf.network_name = str ? str : "some IRC network";
4073 if (!nickserv_conf.auth_policer_params) {
4074 nickserv_conf.auth_policer_params = policer_params_new();
4075 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
4076 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
4078 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
4079 for (it=dict_first(child); it; it=iter_next(it))
4080 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
4084 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
4086 char newnick[NICKLEN+1];
4095 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4097 case RECLAIM_SVSNICK:
4099 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
4100 } while (GetUserH(newnick));
4101 irc_svsnick(nickserv, user, newnick);
4104 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
4105 DelUser(user, nickserv, 1, msg);
4111 nickserv_reclaim_p(void *data) {
4112 struct userNode *user = data;
4113 struct nick_info *ni = get_nick_info(user->nick);
4115 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4119 check_user_nick(struct userNode *user) {
4120 struct nick_info *ni;
4121 user->modes &= ~FLAGS_REGNICK;
4122 if (!(ni = get_nick_info(user->nick)))
4124 if (user->handle_info == ni->owner) {
4125 user->modes |= FLAGS_REGNICK;
4129 if (nickserv_conf.warn_nick_owned)
4130 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4131 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
4133 if (nickserv_conf.auto_reclaim_delay)
4134 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
4136 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4140 handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
4142 struct handle_info *hi = NULL;
4145 hi = dict_find(nickserv_handle_dict, stamp, NULL);
4146 if ((hi == NULL) && (serial != 0)) {
4148 inttobase64(id, serial, IDLEN);
4149 hi = dict_find(nickserv_id_dict, id, NULL);
4153 if ((nickserv_conf.handle_ts_mode == TS_IRCU)
4154 && (timestamp != hi->registered)) {
4157 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
4160 set_user_handle_info(user, hi, 0);
4162 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial);
4167 handle_nick_change(struct userNode *user, const char *old_nick)
4169 struct handle_info *hi;
4171 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4172 dict_remove(nickserv_allow_auth_dict, old_nick);
4173 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4175 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4176 check_user_nick(user);
4180 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
4182 dict_remove(nickserv_allow_auth_dict, user->nick);
4183 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4184 set_user_handle_info(user, NULL, 0);
4187 static struct modcmd *
4188 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4190 if (min_level > 0) {
4192 sprintf(buf, "%u", min_level);
4193 if (must_be_qualified) {
4194 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4196 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4198 } else if (min_level == 0) {
4199 if (must_be_qualified) {
4200 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4202 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4205 if (must_be_qualified) {
4206 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4208 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4214 nickserv_db_cleanup(void)
4216 unreg_del_user_func(nickserv_remove_user);
4217 userList_clean(&curr_helpers);
4218 policer_params_delete(nickserv_conf.auth_policer_params);
4219 dict_delete(nickserv_handle_dict);
4220 dict_delete(nickserv_nick_dict);
4221 dict_delete(nickserv_opt_dict);
4222 dict_delete(nickserv_allow_auth_dict);
4223 dict_delete(nickserv_email_dict);
4224 dict_delete(nickserv_id_dict);
4225 dict_delete(nickserv_conf.weak_password_dict);
4226 free(auth_func_list);
4227 free(unreg_func_list);
4229 free(allowauth_func_list);
4230 free(handle_merge_func_list);
4231 free(failpw_func_list);
4232 if (nickserv_conf.valid_handle_regex_set)
4233 regfree(&nickserv_conf.valid_handle_regex);
4234 if (nickserv_conf.valid_nick_regex_set)
4235 regfree(&nickserv_conf.valid_nick_regex);
4239 init_nickserv(const char *nick)
4242 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4243 reg_new_user_func(check_user_nick);
4244 reg_nick_change_func(handle_nick_change);
4245 reg_del_user_func(nickserv_remove_user);
4246 reg_account_func(handle_account);
4248 /* set up handle_inverse_flags */
4249 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4250 for (i=0; handle_flags[i]; i++) {
4251 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4252 flag_access_levels[i] = 0;
4255 conf_register_reload(nickserv_conf_read);
4256 nickserv_opt_dict = dict_new();
4257 nickserv_email_dict = dict_new();
4258 dict_set_free_keys(nickserv_email_dict, free);
4259 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4261 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4262 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4263 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4264 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4265 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4266 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4267 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4268 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4269 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4270 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4271 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4272 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4273 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4274 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4275 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4276 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4277 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4278 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4279 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4280 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4281 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4282 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4283 if (!nickserv_conf.disable_nicks) {
4284 /* nick management commands */
4285 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4286 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4287 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4288 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4289 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4290 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4292 if (nickserv_conf.email_enabled) {
4293 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4294 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4295 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4296 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4297 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
4298 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4300 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4301 /* miscellaneous commands */
4302 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4303 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4304 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4305 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4306 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4307 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
4309 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4310 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4311 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4312 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4313 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4314 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4315 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4316 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4317 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4318 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4319 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4320 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4321 if (titlehost_suffix) {
4322 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4323 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4324 dict_insert(nickserv_opt_dict, "FAKEIDENT", opt_fakeident);
4326 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4327 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4328 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4329 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
4331 nickserv_handle_dict = dict_new();
4332 dict_set_free_keys(nickserv_handle_dict, free);
4333 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4335 nickserv_id_dict = dict_new();
4336 dict_set_free_keys(nickserv_id_dict, free);
4338 nickserv_nick_dict = dict_new();
4339 dict_set_free_data(nickserv_nick_dict, free);
4341 nickserv_allow_auth_dict = dict_new();
4343 userList_init(&curr_helpers);
4346 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4347 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4348 nickserv_service = service_register(nickserv);
4350 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4351 reg_exit_func(nickserv_db_cleanup);
4352 if(nickserv_conf.handle_expire_frequency)
4353 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4354 message_register_table(msgtab);