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);
894 } else if (handle->fakehost[0] == '$') {
895 /* A leading $ indicates the stored value begins with the user handle. */
896 snprintf(buffer, sizeof(buffer), "%s%s", handle->handle, handle->fakehost+1);
899 return handle->fakehost;
903 generate_fakeident(struct handle_info *handle, struct userNode *user)
905 static char buffer[USERLEN+1];
907 if (!handle->fakeident) {
910 safestrncpy(buffer, user->ident, sizeof(buffer));
913 return handle->fakeident;
917 apply_fakehost(struct handle_info *handle, struct userNode *user)
919 struct userNode *target;
920 char *fakehost, *fakeident;
925 fakehost = generate_fakehost(handle);
928 fakeident = generate_fakeident(handle, user);
929 assign_fakehost(user, fakehost, fakeident, 0, 1);
933 for (target = handle->users; target; target = target->next_authed) {
934 fakeident = generate_fakeident(handle, target);
935 assign_fakehost(target, fakehost, fakeident, 0, 1);
940 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
943 struct handle_info *old_info;
945 /* This can happen if somebody uses COOKIE while authed, or if
946 * they re-auth to their current handle (which is silly, but users
948 if (user->handle_info == hi)
951 if (user->handle_info) {
952 struct userNode *other;
955 userList_remove(&curr_helpers, user);
957 /* remove from next_authed linked list */
958 if (user->handle_info->users == user) {
959 user->handle_info->users = user->next_authed;
960 } else if (user->handle_info->users != NULL) {
961 for (other = user->handle_info->users;
962 other->next_authed != user;
963 other = other->next_authed) ;
964 other->next_authed = user->next_authed;
966 /* No users authed to the account - can happen if they get
967 * killed for authing. */
969 /* if nobody left on old handle, and they're not an oper, remove !god */
970 if (!user->handle_info->users && !user->handle_info->opserv_level)
971 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
972 /* record them as being last seen at this time */
973 user->handle_info->lastseen = now;
974 /* and record their hostmask */
975 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
977 old_info = user->handle_info;
978 user->handle_info = hi;
979 if (hi && !hi->users && !hi->opserv_level)
980 HANDLE_CLEAR_FLAG(hi, HELPING);
981 for (n=0; n<auth_func_used; n++) {
982 auth_func_list[n](user, old_info);
987 struct nick_info *ni;
989 HANDLE_CLEAR_FLAG(hi, FROZEN);
990 if (nickserv_conf.warn_clone_auth) {
991 struct userNode *other;
992 for (other = hi->users; other; other = other->next_authed)
993 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
995 user->next_authed = hi->users;
998 if (IsHelper(user) && !userList_contains(&curr_helpers, user))
999 userList_append(&curr_helpers, user);
1001 if (hi->fakehost || hi->fakeident || old_info)
1002 apply_fakehost(hi, user);
1005 if (!nickserv_conf.disable_nicks) {
1006 struct nick_info *ni2;
1007 for (ni2 = hi->nicks; ni2; ni2 = ni2->next) {
1008 if (!irccasecmp(user->nick, ni2->nick)) {
1009 user->modes |= FLAGS_REGNICK;
1014 StampUser(user, hi->handle, hi->registered, hi->id);
1017 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
1018 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
1020 /* We cannot clear the user's account ID, unfortunately. */
1021 user->next_authed = NULL;
1025 static struct handle_info*
1026 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
1028 struct handle_info *hi;
1029 struct nick_info *ni;
1030 char crypted[MD5_CRYPT_LENGTH];
1032 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
1033 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
1037 if (!is_secure_password(handle, passwd, user))
1040 cryptpass(passwd, crypted);
1041 hi = register_handle(handle, crypted, 0);
1042 hi->masks = alloc_string_list(1);
1044 hi->language = lang_C;
1045 hi->registered = now;
1047 hi->flags = HI_DEFAULT_FLAGS;
1048 if (settee && !no_auth)
1049 set_user_handle_info(settee, hi, 1);
1052 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
1053 else if (nickserv_conf.disable_nicks)
1054 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
1055 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
1056 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
1058 register_nick(user->nick, hi);
1059 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
1061 if (settee && (user != settee))
1062 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
1067 nickserv_bake_cookie(struct handle_cookie *cookie)
1069 cookie->hi->cookie = cookie;
1070 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
1074 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
1076 struct handle_cookie *cookie;
1077 char subject[128], body[4096], *misc;
1078 const char *netname, *fmt;
1082 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1086 cookie = calloc(1, sizeof(*cookie));
1088 cookie->type = type;
1089 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1090 cookie->expires = now + nickserv_conf.cookie_timeout;
1091 inttobase64(cookie->cookie, rand(), 5);
1092 inttobase64(cookie->cookie+5, rand(), 5);
1094 netname = nickserv_conf.network_name;
1097 switch (cookie->type) {
1099 hi->passwd[0] = 0; /* invalidate password */
1100 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1101 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1102 snprintf(subject, sizeof(subject), fmt, netname);
1103 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1104 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1107 case PASSWORD_CHANGE:
1108 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1109 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1110 snprintf(subject, sizeof(subject), fmt, netname);
1111 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1112 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1115 misc = hi->email_addr;
1116 hi->email_addr = cookie->data;
1118 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1119 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1120 snprintf(subject, sizeof(subject), fmt, netname);
1121 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1122 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1123 mail_send(nickserv, hi, subject, body, 1);
1124 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1125 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1127 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1128 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1129 snprintf(subject, sizeof(subject), fmt, netname);
1130 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1131 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1132 mail_send(nickserv, hi, subject, body, 1);
1135 hi->email_addr = misc;
1138 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1139 snprintf(subject, sizeof(subject), fmt, netname);
1140 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1141 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1142 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1145 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1149 mail_send(nickserv, hi, subject, body, first_time);
1150 nickserv_bake_cookie(cookie);
1154 nickserv_eat_cookie(struct handle_cookie *cookie)
1156 cookie->hi->cookie = NULL;
1157 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1158 nickserv_free_cookie(cookie);
1162 nickserv_free_email_addr(void *data)
1164 handle_info_list_clean(data);
1169 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1171 struct handle_info_list *hil;
1172 /* Remove from old handle_info_list ... */
1173 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1174 handle_info_list_remove(hil, hi);
1175 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1176 hi->email_addr = NULL;
1178 /* Add to the new list.. */
1179 if (new_email_addr) {
1180 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1181 hil = calloc(1, sizeof(*hil));
1182 hil->tag = strdup(new_email_addr);
1183 handle_info_list_init(hil);
1184 dict_insert(nickserv_email_dict, hil->tag, hil);
1186 handle_info_list_append(hil, hi);
1187 hi->email_addr = hil->tag;
1191 static NICKSERV_FUNC(cmd_register)
1194 struct handle_info *hi;
1195 const char *email_addr, *password;
1198 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1199 /* Require the first handle registered to belong to someone +o. */
1200 reply("NSMSG_REQUIRE_OPER");
1204 if (user->handle_info) {
1205 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1209 if (IsRegistering(user)) {
1210 reply("NSMSG_ALREADY_REGISTERING");
1214 if (IsStamped(user)) {
1215 /* Unauthenticated users might still have been stamped
1216 previously and could therefore have a hidden host;
1217 do not allow them to register a new account. */
1218 reply("NSMSG_STAMPED_REGISTER");
1222 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1224 if (!is_valid_handle(argv[1])) {
1225 reply("NSMSG_BAD_HANDLE", argv[1]);
1229 if ((argc >= 4) && nickserv_conf.email_enabled) {
1230 struct handle_info_list *hil;
1233 /* Remember email address. */
1234 email_addr = argv[3];
1236 /* Check that the email address looks valid.. */
1237 if (!is_valid_email_addr(email_addr)) {
1238 reply("NSMSG_BAD_EMAIL_ADDR");
1242 /* .. and that we are allowed to send to it. */
1243 if ((str = mail_prohibited_address(email_addr))) {
1244 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1248 /* If we do email verify, make sure we don't spam the address. */
1249 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1251 for (nn=0; nn<hil->used; nn++) {
1252 if (hil->list[nn]->cookie) {
1253 reply("NSMSG_EMAIL_UNACTIVATED");
1257 if (hil->used >= nickserv_conf.handles_per_email) {
1258 reply("NSMSG_EMAIL_OVERUSED");
1271 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1273 /* Add any masks they should get. */
1274 if (nickserv_conf.default_hostmask) {
1275 string_list_append(hi->masks, strdup("*@*"));
1277 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1278 if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
1279 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1282 /* If they're the first to register, give them level 1000. */
1283 if (dict_size(nickserv_handle_dict) == 1) {
1284 hi->opserv_level = 1000;
1285 reply("NSMSG_ROOT_HANDLE", argv[1]);
1288 /* Set their email address. */
1290 nickserv_set_email_addr(hi, email_addr);
1292 /* If they need to do email verification, tell them. */
1294 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1296 /* Set registering flag.. */
1297 user->modes |= FLAGS_REGISTERING;
1302 static NICKSERV_FUNC(cmd_oregister)
1305 struct userNode *settee;
1306 struct handle_info *hi;
1307 const char *pass, *email;
1309 NICKSERV_MIN_PARMS(3);
1314 if (!is_valid_handle(argv[1])) {
1315 reply("NSMSG_BAD_HANDLE", argv[1]);
1319 if (argc < 5 || !nickserv_conf.email_enabled) {
1324 if (!is_valid_email_addr(email)) {
1325 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
1328 if ((str = mail_prohibited_address(email))) {
1329 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", email, str);
1334 if (argc < 4 || !strcmp(argv[3], "*")) {
1337 } else if (strchr(argv[3], '@')) {
1338 mask = canonicalize_hostmask(strdup(argv[3]));
1340 settee = GetUserH(argv[4]);
1342 reply("MSG_NICK_UNKNOWN", argv[4]);
1349 } else if ((settee = GetUserH(argv[3]))) {
1350 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1352 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1355 if (settee && settee->handle_info) {
1356 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1360 if (!(hi = nickserv_register(user, settee, argv[1], pass, 0))) {
1365 string_list_append(hi->masks, mask);
1367 nickserv_set_email_addr(hi, email);
1371 static NICKSERV_FUNC(cmd_handleinfo)
1374 unsigned int i, pos=0, herelen;
1375 struct userNode *target, *next_un;
1376 struct handle_info *hi;
1377 const char *nsmsg_none;
1381 if (!(hi = user->handle_info)) {
1382 reply("NSMSG_MUST_AUTH");
1385 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1389 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1390 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1391 feh = hi->registered;
1392 reply("NSMSG_HANDLEINFO_REGGED", ctime(&feh));
1395 intervalString(buff, now - hi->lastseen, user->handle_info);
1396 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1398 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1401 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1402 if (HANDLE_FLAGGED(hi, FROZEN))
1403 reply("NSMSG_HANDLEINFO_VACATION");
1405 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1406 struct do_not_register *dnr;
1407 if ((dnr = chanserv_is_dnr(NULL, hi)))
1408 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1409 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
1411 } else if (hi != user->handle_info)
1415 reply("NSMSG_HANDLEINFO_KARMA", hi->karma);
1417 if (nickserv_conf.email_enabled)
1418 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1422 switch (hi->cookie->type) {
1423 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1424 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1425 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1426 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1427 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1432 if (oper_has_access(user, cmd->parent->bot, 601, 1))
1433 reply("NSMSG_HANDLEINFO_ID", hi->id);
1435 if (oper_has_access(user, cmd->parent->bot, 0, 1) || IsStaff(user)) {
1437 reply("NSMSG_HANDLEINFO_NO_NOTES");
1439 struct handle_note *prev, *note;
1441 WALK_NOTES(hi, prev, note) {
1442 char set_time[INTERVALLEN];
1443 intervalString(set_time, now - note->set, user->handle_info);
1444 if (note->expires) {
1445 char exp_time[INTERVALLEN];
1446 intervalString(exp_time, note->expires - now, user->handle_info);
1447 reply("NSMSG_HANDLEINFO_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1449 reply("NSMSG_HANDLEINFO_NOTE", note->id, set_time, note->setter, note->note);
1456 unsigned long flen = 1;
1457 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1459 for (i=0, flen=1; handle_flags[i]; i++)
1460 if (hi->flags & 1 << i)
1461 flags[flen++] = handle_flags[i];
1463 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1465 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1468 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1469 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1470 || (hi->opserv_level > 0)) {
1471 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1474 if (hi->fakeident && hi->fakehost)
1475 reply("NSMSG_HANDLEINFO_FAKEIDENTHOST", hi->fakeident, hi->fakehost);
1476 else if (hi->fakeident)
1477 reply("NSMSG_HANDLEINFO_FAKEIDENT", hi->fakeident);
1478 else if (hi->fakehost)
1479 reply("NSMSG_HANDLEINFO_FAKEHOST", hi->fakehost);
1481 if (hi->last_quit_host[0])
1482 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1484 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1486 if (nickserv_conf.disable_nicks) {
1487 /* nicks disabled; don't show anything about registered nicks */
1488 } else if (hi->nicks) {
1489 struct nick_info *ni, *next_ni;
1490 for (ni = hi->nicks; ni; ni = next_ni) {
1491 herelen = strlen(ni->nick);
1492 if (pos + herelen + 1 > ArrayLength(buff)) {
1494 goto print_nicks_buff;
1498 memcpy(buff+pos, ni->nick, herelen);
1499 pos += herelen; buff[pos++] = ' ';
1503 reply("NSMSG_HANDLEINFO_NICKS", buff);
1508 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1511 if (hi->masks->used) {
1512 for (i=0; i < hi->masks->used; i++) {
1513 herelen = strlen(hi->masks->list[i]);
1514 if (pos + herelen + 1 > ArrayLength(buff)) {
1516 goto print_mask_buff;
1518 memcpy(buff+pos, hi->masks->list[i], herelen);
1519 pos += herelen; buff[pos++] = ' ';
1520 if (i+1 == hi->masks->used) {
1523 reply("NSMSG_HANDLEINFO_MASKS", buff);
1528 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1532 struct userData *chan, *next;
1535 for (chan = hi->channels; chan; chan = next) {
1536 next = chan->u_next;
1537 name = chan->channel->channel->name;
1538 herelen = strlen(name);
1539 if (pos + herelen + 7 > ArrayLength(buff)) {
1541 goto print_chans_buff;
1543 if (IsUserSuspended(chan))
1545 pos += sprintf(buff+pos, "%d:%s ", chan->access, name);
1549 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1554 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1557 for (target = hi->users; target; target = next_un) {
1558 herelen = strlen(target->nick);
1559 if (pos + herelen + 1 > ArrayLength(buff)) {
1561 goto print_cnick_buff;
1563 next_un = target->next_authed;
1565 memcpy(buff+pos, target->nick, herelen);
1566 pos += herelen; buff[pos++] = ' ';
1570 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1575 return 1 | ((hi != user->handle_info) ? CMD_LOG_STAFF : 0);
1578 static NICKSERV_FUNC(cmd_userinfo)
1580 struct userNode *target;
1582 NICKSERV_MIN_PARMS(2);
1583 if (!(target = GetUserH(argv[1]))) {
1584 reply("MSG_NICK_UNKNOWN", argv[1]);
1587 if (target->handle_info)
1588 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1590 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1594 static NICKSERV_FUNC(cmd_nickinfo)
1596 struct nick_info *ni;
1598 NICKSERV_MIN_PARMS(2);
1599 if (!(ni = get_nick_info(argv[1]))) {
1600 reply("MSG_NICK_UNKNOWN", argv[1]);
1603 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1607 static NICKSERV_FUNC(cmd_notes)
1609 struct handle_info *hi;
1610 struct handle_note *prev, *note;
1613 NICKSERV_MIN_PARMS(2);
1614 if (!(hi = get_victim_oper(user, argv[1])))
1617 WALK_NOTES(hi, prev, note) {
1618 char set_time[INTERVALLEN];
1619 intervalString(set_time, now - note->set, user->handle_info);
1620 if (note->expires) {
1621 char exp_time[INTERVALLEN];
1622 intervalString(exp_time, note->expires - now, user->handle_info);
1623 reply("NSMSG_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1625 reply("NSMSG_NOTE", note->id, set_time, note->setter, note->note);
1629 reply("NSMSG_NOTE_COUNT", hits, argv[1]);
1633 static NICKSERV_FUNC(cmd_rename_handle)
1635 struct handle_info *hi;
1636 char msgbuf[MAXLEN], *old_handle;
1639 NICKSERV_MIN_PARMS(3);
1640 if (!(hi = get_victim_oper(user, argv[1])))
1642 if (!is_valid_handle(argv[2])) {
1643 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1646 if (get_handle_info(argv[2])) {
1647 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1650 if (hi->fakehost && hi->fakehost[0] == '.' &&
1651 (strlen(argv[2]) + strlen(hi->fakehost+1) +
1652 strlen(titlehost_suffix) + 2) > HOSTLEN) {
1653 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED_RENAME");
1657 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1658 hi->handle = strdup(argv[2]);
1659 dict_insert(nickserv_handle_dict, hi->handle, hi);
1660 for (nn=0; nn<rf_list_used; nn++)
1661 rf_list[nn](hi, old_handle);
1662 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1663 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1664 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1670 static failpw_func_t *failpw_func_list;
1671 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1674 reg_failpw_func(failpw_func_t func)
1676 if (failpw_func_used == failpw_func_size) {
1677 if (failpw_func_size) {
1678 failpw_func_size <<= 1;
1679 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1681 failpw_func_size = 8;
1682 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1685 failpw_func_list[failpw_func_used++] = func;
1688 static NICKSERV_FUNC(cmd_auth)
1690 int pw_arg, used, maxlogins;
1691 struct handle_info *hi;
1693 struct userNode *other;
1695 if (user->handle_info) {
1696 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1699 if (IsStamped(user)) {
1700 /* Unauthenticated users might still have been stamped
1701 previously and could therefore have a hidden host;
1702 do not allow them to authenticate. */
1703 reply("NSMSG_STAMPED_AUTH");
1707 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1709 } else if (argc == 2) {
1710 if (nickserv_conf.disable_nicks) {
1711 if (!(hi = get_handle_info(user->nick))) {
1712 reply("NSMSG_HANDLE_NOT_FOUND");
1716 /* try to look up their handle from their nick */
1717 struct nick_info *ni;
1718 ni = get_nick_info(user->nick);
1720 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1727 reply("MSG_MISSING_PARAMS", argv[0]);
1728 svccmd_send_help(user, nickserv, cmd);
1732 reply("NSMSG_HANDLE_NOT_FOUND");
1735 /* Responses from here on look up the language used by the handle they asked about. */
1736 passwd = argv[pw_arg];
1737 if (!valid_user_for(user, hi)) {
1738 if (hi->email_addr && nickserv_conf.email_enabled)
1739 send_message_type(4, user, cmd->parent->bot,
1740 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1743 send_message_type(4, user, cmd->parent->bot,
1744 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1746 argv[pw_arg] = "BADMASK";
1749 if (!checkpass(passwd, hi->passwd)) {
1751 send_message_type(4, user, cmd->parent->bot,
1752 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1753 argv[pw_arg] = "BADPASS";
1754 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1755 if (nickserv_conf.autogag_enabled) {
1756 if (!user->auth_policer.params) {
1757 user->auth_policer.last_req = now;
1758 user->auth_policer.params = nickserv_conf.auth_policer_params;
1760 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1762 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1763 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1764 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1766 argv[pw_arg] = "GAGGED";
1771 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1772 send_message_type(4, user, cmd->parent->bot,
1773 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1774 argv[pw_arg] = "SUSPENDED";
1777 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1778 for (used = 0, other = hi->users; other; other = other->next_authed) {
1779 if (++used >= maxlogins) {
1780 send_message_type(4, user, cmd->parent->bot,
1781 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1783 argv[pw_arg] = "MAXLOGINS";
1788 set_user_handle_info(user, hi, 1);
1789 if (nickserv_conf.email_required && !hi->email_addr)
1790 reply("NSMSG_PLEASE_SET_EMAIL");
1791 if (!is_secure_password(hi->handle, passwd, NULL))
1792 reply("NSMSG_WEAK_PASSWORD");
1793 if (hi->passwd[0] != '$')
1794 cryptpass(passwd, hi->passwd);
1795 if (!hi->masks->used) {
1797 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1798 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
1799 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1801 argv[pw_arg] = "****";
1802 reply("NSMSG_AUTH_SUCCESS");
1806 static allowauth_func_t *allowauth_func_list;
1807 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1810 reg_allowauth_func(allowauth_func_t func)
1812 if (allowauth_func_used == allowauth_func_size) {
1813 if (allowauth_func_size) {
1814 allowauth_func_size <<= 1;
1815 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1817 allowauth_func_size = 8;
1818 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1821 allowauth_func_list[allowauth_func_used++] = func;
1824 static NICKSERV_FUNC(cmd_allowauth)
1826 struct userNode *target;
1827 struct handle_info *hi;
1830 NICKSERV_MIN_PARMS(2);
1831 if (!(target = GetUserH(argv[1]))) {
1832 reply("MSG_NICK_UNKNOWN", argv[1]);
1835 if (target->handle_info) {
1836 reply("NSMSG_USER_PREV_AUTH", target->nick);
1839 if (IsStamped(target)) {
1840 /* Unauthenticated users might still have been stamped
1841 previously and could therefore have a hidden host;
1842 do not allow them to authenticate to an account. */
1843 reply("NSMSG_USER_PREV_STAMP", target->nick);
1848 else if (!(hi = get_handle_info(argv[2]))) {
1849 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1853 if (hi->opserv_level > user->handle_info->opserv_level) {
1854 reply("MSG_USER_OUTRANKED", hi->handle);
1857 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1858 || (hi->opserv_level > 0))
1859 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1860 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1863 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1864 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1865 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1866 if (nickserv_conf.email_enabled)
1867 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1869 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1870 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1872 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1874 for (n=0; n<allowauth_func_used; n++)
1875 allowauth_func_list[n](user, target, hi);
1879 static NICKSERV_FUNC(cmd_authcookie)
1881 struct handle_info *hi;
1883 NICKSERV_MIN_PARMS(2);
1884 if (user->handle_info) {
1885 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1888 if (IsStamped(user)) {
1889 /* Unauthenticated users might still have been stamped
1890 previously and could therefore have a hidden host;
1891 do not allow them to authenticate to an account. */
1892 reply("NSMSG_STAMPED_AUTHCOOKIE");
1895 if (!(hi = get_handle_info(argv[1]))) {
1896 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1899 if (!hi->email_addr) {
1900 reply("MSG_SET_EMAIL_ADDR");
1903 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1907 static NICKSERV_FUNC(cmd_delcookie)
1909 struct handle_info *hi;
1911 hi = user->handle_info;
1913 reply("NSMSG_NO_COOKIE");
1916 switch (hi->cookie->type) {
1919 reply("NSMSG_MUST_TIME_OUT");
1922 nickserv_eat_cookie(hi->cookie);
1923 reply("NSMSG_ATE_COOKIE");
1929 static NICKSERV_FUNC(cmd_odelcookie)
1931 struct handle_info *hi;
1933 NICKSERV_MIN_PARMS(2);
1935 if (!(hi = get_victim_oper(user, argv[1])))
1939 reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle);
1943 nickserv_eat_cookie(hi->cookie);
1944 reply("NSMSG_ATE_COOKIE_FOREIGN", hi->handle);
1949 static NICKSERV_FUNC(cmd_resetpass)
1951 struct handle_info *hi;
1952 char crypted[MD5_CRYPT_LENGTH];
1954 NICKSERV_MIN_PARMS(3);
1955 if (user->handle_info) {
1956 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1959 if (IsStamped(user)) {
1960 /* Unauthenticated users might still have been stamped
1961 previously and could therefore have a hidden host;
1962 do not allow them to activate an account. */
1963 reply("NSMSG_STAMPED_RESETPASS");
1966 if (!(hi = get_handle_info(argv[1]))) {
1967 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1970 if (!hi->email_addr) {
1971 reply("MSG_SET_EMAIL_ADDR");
1974 cryptpass(argv[2], crypted);
1976 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1980 static NICKSERV_FUNC(cmd_cookie)
1982 struct handle_info *hi;
1985 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1988 NICKSERV_MIN_PARMS(3);
1989 if (!(hi = get_handle_info(argv[1]))) {
1990 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1996 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1997 reply("NSMSG_HANDLE_SUSPENDED");
2002 reply("NSMSG_NO_COOKIE");
2006 /* Check validity of operation before comparing cookie to
2007 * prohibit guessing by authed users. */
2008 if (user->handle_info
2009 && (hi->cookie->type != EMAIL_CHANGE)
2010 && (hi->cookie->type != PASSWORD_CHANGE)) {
2011 reply("NSMSG_CANNOT_COOKIE");
2015 if (strcmp(cookie, hi->cookie->cookie)) {
2016 reply("NSMSG_BAD_COOKIE");
2020 switch (hi->cookie->type) {
2022 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2023 set_user_handle_info(user, hi, 1);
2024 reply("NSMSG_HANDLE_ACTIVATED");
2026 case PASSWORD_CHANGE:
2027 set_user_handle_info(user, hi, 1);
2028 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2029 reply("NSMSG_PASSWORD_CHANGED");
2032 nickserv_set_email_addr(hi, hi->cookie->data);
2033 reply("NSMSG_EMAIL_CHANGED");
2036 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2037 set_user_handle_info(user, hi, 1);
2038 nickserv_addmask(user, hi, mask);
2039 reply("NSMSG_AUTH_SUCCESS");
2044 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
2045 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
2049 nickserv_eat_cookie(hi->cookie);
2054 static NICKSERV_FUNC(cmd_oregnick) {
2056 struct handle_info *target;
2057 struct nick_info *ni;
2059 NICKSERV_MIN_PARMS(3);
2060 if (!(target = modcmd_get_handle_info(user, argv[1])))
2063 if (!is_registerable_nick(nick)) {
2064 reply("NSMSG_BAD_NICK", nick);
2067 ni = dict_find(nickserv_nick_dict, nick, NULL);
2069 reply("NSMSG_NICK_EXISTS", nick);
2072 register_nick(nick, target);
2073 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
2077 static NICKSERV_FUNC(cmd_regnick) {
2079 struct nick_info *ni;
2081 if (!is_registerable_nick(user->nick)) {
2082 reply("NSMSG_BAD_NICK", user->nick);
2085 /* count their nicks, see if it's too many */
2086 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
2087 if (n >= nickserv_conf.nicks_per_handle) {
2088 reply("NSMSG_TOO_MANY_NICKS");
2091 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
2093 reply("NSMSG_NICK_EXISTS", user->nick);
2096 register_nick(user->nick, user->handle_info);
2097 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2101 static NICKSERV_FUNC(cmd_pass)
2103 struct handle_info *hi;
2104 const char *old_pass, *new_pass;
2106 NICKSERV_MIN_PARMS(3);
2107 hi = user->handle_info;
2111 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
2112 if (!checkpass(old_pass, hi->passwd)) {
2113 argv[1] = "BADPASS";
2114 reply("NSMSG_PASSWORD_INVALID");
2117 cryptpass(new_pass, hi->passwd);
2119 reply("NSMSG_PASS_SUCCESS");
2124 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
2127 char *new_mask = canonicalize_hostmask(strdup(mask));
2128 for (i=0; i<hi->masks->used; i++) {
2129 if (!irccasecmp(new_mask, hi->masks->list[i])) {
2130 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2135 string_list_append(hi->masks, new_mask);
2136 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2140 static NICKSERV_FUNC(cmd_addmask)
2143 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2144 int res = nickserv_addmask(user, user->handle_info, mask);
2148 if (!is_gline(argv[1])) {
2149 reply("NSMSG_MASK_INVALID", argv[1]);
2152 return nickserv_addmask(user, user->handle_info, argv[1]);
2156 static NICKSERV_FUNC(cmd_oaddmask)
2158 struct handle_info *hi;
2160 NICKSERV_MIN_PARMS(3);
2161 if (!(hi = get_victim_oper(user, argv[1])))
2163 return nickserv_addmask(user, hi, argv[2]);
2167 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2170 for (i=0; i<hi->masks->used; i++) {
2171 if (!strcmp(del_mask, hi->masks->list[i])) {
2172 char *old_mask = hi->masks->list[i];
2173 if (hi->masks->used == 1 && !force) {
2174 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2177 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2178 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2183 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2187 static NICKSERV_FUNC(cmd_delmask)
2189 NICKSERV_MIN_PARMS(2);
2190 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2193 static NICKSERV_FUNC(cmd_odelmask)
2195 struct handle_info *hi;
2196 NICKSERV_MIN_PARMS(3);
2197 if (!(hi = get_victim_oper(user, argv[1])))
2199 return nickserv_delmask(user, hi, argv[2], 1);
2203 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2204 unsigned int nn, add = 1, pos;
2205 unsigned long added, removed, flag;
2207 for (added=removed=nn=0; str[nn]; nn++) {
2209 case '+': add = 1; break;
2210 case '-': add = 0; break;
2212 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2213 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2216 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2217 /* cheesy avoidance of looking up the flag name.. */
2218 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2221 flag = 1 << (pos - 1);
2223 added |= flag, removed &= ~flag;
2225 removed |= flag, added &= ~flag;
2230 *premoved = removed;
2235 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2237 unsigned long before, after, added, removed;
2238 struct userNode *uNode;
2240 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2241 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2243 hi->flags = (hi->flags | added) & ~removed;
2244 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2246 /* Strip helping flag if they're only a support helper and not
2247 * currently in #support. */
2248 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2249 struct channelList *schannels;
2251 schannels = chanserv_support_channels();
2252 for (ii = 0; ii < schannels->used; ++ii)
2253 if (find_handle_in_channel(schannels->list[ii], hi, NULL))
2255 if (ii == schannels->used)
2256 HANDLE_CLEAR_FLAG(hi, HELPING);
2259 if (after && !before) {
2260 /* Add user to current helper list. */
2261 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2262 userList_append(&curr_helpers, uNode);
2263 } else if (!after && before) {
2264 /* Remove user from current helper list. */
2265 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2266 userList_remove(&curr_helpers, uNode);
2273 set_list(struct userNode *user, struct handle_info *hi, int override)
2277 char *set_display[] = {
2278 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2279 "EMAIL", "MAXLOGINS", "LANGUAGE"
2282 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2284 /* Do this so options are presented in a consistent order. */
2285 for (i = 0; i < ArrayLength(set_display); ++i)
2286 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2287 opt(user, hi, override, 0, NULL);
2290 static NICKSERV_FUNC(cmd_set)
2292 struct handle_info *hi;
2295 hi = user->handle_info;
2297 set_list(user, hi, 0);
2300 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2301 reply("NSMSG_INVALID_OPTION", argv[1]);
2304 return opt(user, hi, 0, argc-1, argv+1);
2307 static NICKSERV_FUNC(cmd_oset)
2309 struct handle_info *hi;
2310 struct svccmd *subcmd;
2312 char cmdname[MAXLEN];
2314 NICKSERV_MIN_PARMS(2);
2316 if (!(hi = get_victim_oper(user, argv[1])))
2320 set_list(user, hi, 0);
2324 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2325 reply("NSMSG_INVALID_OPTION", argv[2]);
2329 sprintf(cmdname, "%s %s", cmd->name, argv[2]);
2330 subcmd = dict_find(cmd->parent->commands, cmdname, NULL);
2331 if (subcmd && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, NULL, SVCCMD_NOISY))
2334 return opt(user, hi, 1, argc-2, argv+2);
2337 static OPTION_FUNC(opt_info)
2341 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2343 hi->infoline = NULL;
2345 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2349 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2350 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2354 static OPTION_FUNC(opt_width)
2357 hi->screen_width = strtoul(argv[1], NULL, 0);
2359 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2360 hi->screen_width = MIN_LINE_SIZE;
2361 else if (hi->screen_width > MAX_LINE_SIZE)
2362 hi->screen_width = MAX_LINE_SIZE;
2364 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2368 static OPTION_FUNC(opt_tablewidth)
2371 hi->table_width = strtoul(argv[1], NULL, 0);
2373 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2374 hi->table_width = MIN_LINE_SIZE;
2375 else if (hi->screen_width > MAX_LINE_SIZE)
2376 hi->table_width = MAX_LINE_SIZE;
2378 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2382 static OPTION_FUNC(opt_color)
2385 if (enabled_string(argv[1]))
2386 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2387 else if (disabled_string(argv[1]))
2388 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2390 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2395 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2399 static OPTION_FUNC(opt_privmsg)
2402 if (enabled_string(argv[1]))
2403 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2404 else if (disabled_string(argv[1]))
2405 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2407 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2412 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2416 static OPTION_FUNC(opt_style)
2421 if (!irccasecmp(argv[1], "Zoot"))
2422 hi->userlist_style = HI_STYLE_ZOOT;
2423 else if (!irccasecmp(argv[1], "def"))
2424 hi->userlist_style = HI_STYLE_DEF;
2427 switch (hi->userlist_style) {
2436 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2440 static OPTION_FUNC(opt_password)
2443 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2448 cryptpass(argv[1], hi->passwd);
2450 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2454 static OPTION_FUNC(opt_flags)
2457 unsigned int ii, flen;
2460 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2465 nickserv_apply_flags(user, hi, argv[1]);
2467 for (ii = flen = 0; handle_flags[ii]; ii++)
2468 if (hi->flags & (1 << ii))
2469 flags[flen++] = handle_flags[ii];
2472 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2474 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2478 static OPTION_FUNC(opt_email)
2482 if (!is_valid_email_addr(argv[1])) {
2483 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2486 if ((str = mail_prohibited_address(argv[1]))) {
2487 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2490 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2491 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2493 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2495 nickserv_set_email_addr(hi, argv[1]);
2497 nickserv_eat_cookie(hi->cookie);
2498 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2501 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2505 static OPTION_FUNC(opt_maxlogins)
2507 unsigned char maxlogins;
2509 maxlogins = strtoul(argv[1], NULL, 0);
2510 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2511 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2514 hi->maxlogins = maxlogins;
2516 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2517 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2521 static OPTION_FUNC(opt_language)
2523 struct language *lang;
2525 lang = language_find(argv[1]);
2526 if (irccasecmp(lang->name, argv[1]))
2527 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2528 hi->language = lang;
2530 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2534 static OPTION_FUNC(opt_karma)
2537 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2542 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2543 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2544 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2545 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2547 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2551 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2556 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2557 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2559 if ((user->handle_info->opserv_level < target->opserv_level)
2560 || ((user->handle_info->opserv_level == target->opserv_level)
2561 && (user->handle_info->opserv_level < 1000))) {
2562 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2565 if ((user->handle_info->opserv_level < new_level)
2566 || ((user->handle_info->opserv_level == new_level)
2567 && (user->handle_info->opserv_level < 1000))) {
2568 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2571 if (user->handle_info == target) {
2572 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2575 if (target->opserv_level == new_level)
2577 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2578 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2579 target->opserv_level = new_level;
2583 static OPTION_FUNC(opt_level)
2588 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2592 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2593 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2597 static OPTION_FUNC(opt_epithet)
2600 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2604 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2605 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2608 if ((epithet[0] == '*') && !epithet[1])
2611 hi->epithet = strdup(epithet);
2615 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2617 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2621 static OPTION_FUNC(opt_title)
2626 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2630 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2632 if (strchr(title, '.')) {
2633 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2636 if ((strlen(user->handle_info->handle) + strlen(title) +
2637 strlen(titlehost_suffix) + 2) > HOSTLEN) {
2638 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2643 if (!strcmp(title, "*")) {
2644 hi->fakehost = NULL;
2646 hi->fakehost = malloc(strlen(title)+2);
2647 hi->fakehost[0] = '.';
2648 strcpy(hi->fakehost+1, title);
2650 apply_fakehost(hi, NULL);
2651 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2652 title = hi->fakehost + 1;
2656 title = user_find_message(user, "MSG_NONE");
2657 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2661 static OPTION_FUNC(opt_fakehost)
2663 char mask[USERLEN + HOSTLEN + 2];
2667 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2671 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2672 if(strlen(argv[1]) >= sizeof(mask)) {
2673 send_message(user, nickserv, "NSMSG_FAKEMASK_INVALID", USERLEN + HOSTLEN + 1);
2677 safestrncpy(mask, argv[1], sizeof(mask));
2679 if ((host = strrchr(mask, '@')) && host != mask) {
2680 /* If ident@host was used and the user doesn't have access to set idents, do not change anything. */
2681 if (!oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
2693 if (ident && strlen(ident) > USERLEN) {
2694 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
2698 if (host && ((strlen(host) > HOSTLEN) || (host[0] == '.'))) {
2699 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2703 if (host && host[0]) {
2705 if (!strcmp(host, "*"))
2706 hi->fakehost = NULL;
2708 hi->fakehost = strdup(host);
2709 host = hi->fakehost;
2712 host = generate_fakehost(hi);
2715 free(hi->fakeident);
2716 if (!strcmp(ident, "*"))
2717 hi->fakeident = NULL;
2719 hi->fakeident = strdup(ident);
2720 ident = hi->fakeident;
2723 ident = generate_fakeident(hi, NULL);
2725 apply_fakehost(hi, NULL);
2727 host = generate_fakehost(hi);
2728 ident = generate_fakeident(hi, NULL);
2731 host = (char *) user_find_message(user, "MSG_NONE");
2733 send_message(user, nickserv, "NSMSG_SET_FAKEIDENTHOST", ident, host);
2735 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", host);
2739 static OPTION_FUNC(opt_fakeident)
2744 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2748 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
2750 if (strlen(ident) > USERLEN) {
2751 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
2754 free(hi->fakeident);
2755 if (!strcmp(ident, "*"))
2756 hi->fakeident = NULL;
2758 hi->fakeident = strdup(ident);
2759 ident = hi->fakeident;
2760 apply_fakehost(hi, NULL);
2762 ident = generate_fakeident(hi, NULL); /* NULL if no fake ident set */
2764 ident = user_find_message(user, "MSG_NONE");
2765 send_message(user, nickserv, "NSMSG_SET_FAKEIDENT", ident);
2769 static NICKSERV_FUNC(cmd_reclaim)
2771 struct handle_info *hi;
2772 struct nick_info *ni;
2773 struct userNode *victim;
2775 NICKSERV_MIN_PARMS(2);
2776 hi = user->handle_info;
2777 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2779 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2782 if (ni->owner != user->handle_info) {
2783 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2786 victim = GetUserH(ni->nick);
2788 reply("MSG_NICK_UNKNOWN", ni->nick);
2791 if (victim == user) {
2792 reply("NSMSG_NICK_USER_YOU");
2795 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2796 switch (nickserv_conf.reclaim_action) {
2797 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2798 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2799 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2800 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2805 static NICKSERV_FUNC(cmd_unregnick)
2808 struct handle_info *hi;
2809 struct nick_info *ni;
2811 hi = user->handle_info;
2812 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2813 ni = dict_find(nickserv_nick_dict, nick, NULL);
2815 reply("NSMSG_UNKNOWN_NICK", nick);
2818 if (hi != ni->owner) {
2819 reply("NSMSG_NOT_YOUR_NICK", nick);
2822 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2827 static NICKSERV_FUNC(cmd_ounregnick)
2829 struct nick_info *ni;
2831 NICKSERV_MIN_PARMS(2);
2832 if (!(ni = get_nick_info(argv[1]))) {
2833 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2836 if (!oper_outranks(user, ni->owner))
2838 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2843 static NICKSERV_FUNC(cmd_unregister)
2845 struct handle_info *hi;
2848 NICKSERV_MIN_PARMS(2);
2849 hi = user->handle_info;
2852 if (checkpass(passwd, hi->passwd)) {
2853 nickserv_unregister_handle(hi, user);
2856 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2857 reply("NSMSG_PASSWORD_INVALID");
2862 static NICKSERV_FUNC(cmd_ounregister)
2864 struct handle_info *hi;
2865 char reason[MAXLEN];
2868 NICKSERV_MIN_PARMS(2);
2869 if (!(hi = get_victim_oper(user, argv[1])))
2872 if (HANDLE_FLAGGED(hi, NODELETE)) {
2873 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
2877 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
2879 ((hi->flags & nickserv_conf.ounregister_flags)
2881 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
2882 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
2886 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
2887 global_message(MESSAGE_RECIPIENT_STAFF, reason);
2888 nickserv_unregister_handle(hi, user);
2892 static NICKSERV_FUNC(cmd_status)
2894 if (nickserv_conf.disable_nicks) {
2895 reply("NSMSG_GLOBAL_STATS_NONICK",
2896 dict_size(nickserv_handle_dict));
2898 if (user->handle_info) {
2900 struct nick_info *ni;
2901 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2902 reply("NSMSG_HANDLE_STATS", cnt);
2904 reply("NSMSG_HANDLE_NONE");
2906 reply("NSMSG_GLOBAL_STATS",
2907 dict_size(nickserv_handle_dict),
2908 dict_size(nickserv_nick_dict));
2913 static NICKSERV_FUNC(cmd_ghost)
2915 struct userNode *target;
2916 char reason[MAXLEN];
2918 NICKSERV_MIN_PARMS(2);
2919 if (!(target = GetUserH(argv[1]))) {
2920 reply("MSG_NICK_UNKNOWN", argv[1]);
2923 if (target == user) {
2924 reply("NSMSG_CANNOT_GHOST_SELF");
2927 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2928 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2931 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2932 DelUser(target, nickserv, 1, reason);
2933 reply("NSMSG_GHOST_KILLED", argv[1]);
2937 static NICKSERV_FUNC(cmd_vacation)
2939 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2940 reply("NSMSG_ON_VACATION");
2944 static NICKSERV_FUNC(cmd_addnote)
2946 struct handle_info *hi;
2947 unsigned long duration;
2950 struct handle_note *prev;
2951 struct handle_note *note;
2953 /* Parse parameters and figure out values for note's fields. */
2954 NICKSERV_MIN_PARMS(4);
2955 hi = get_victim_oper(user, argv[1]);
2958 if(!strcmp(argv[2], "0"))
2960 else if(!(duration = ParseInterval(argv[2])))
2962 reply("MSG_INVALID_DURATION", argv[2]);
2965 if (duration > 2*365*86400) {
2966 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
2969 unsplit_string(argv + 3, argc - 3, text);
2970 WALK_NOTES(hi, prev, note) {}
2971 id = prev ? (prev->id + 1) : 1;
2973 /* Create the new note structure. */
2974 note = calloc(1, sizeof(*note) + strlen(text));
2976 note->expires = duration ? (now + duration) : 0;
2979 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
2980 strcpy(note->note, text);
2985 reply("NSMSG_NOTE_ADDED", id, hi->handle);
2989 static NICKSERV_FUNC(cmd_delnote)
2991 struct handle_info *hi;
2992 struct handle_note *prev;
2993 struct handle_note *note;
2996 NICKSERV_MIN_PARMS(3);
2997 hi = get_victim_oper(user, argv[1]);
3000 id = strtoul(argv[2], NULL, 10);
3001 WALK_NOTES(hi, prev, note) {
3002 if (id == note->id) {
3004 prev->next = note->next;
3006 hi->notes = note->next;
3008 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
3012 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
3017 nickserv_saxdb_write(struct saxdb_context *ctx) {
3019 struct handle_info *hi;
3022 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3024 assert(hi->id != 0);
3025 saxdb_start_record(ctx, iter_key(it), 0);
3027 struct handle_cookie *cookie = hi->cookie;
3030 switch (cookie->type) {
3031 case ACTIVATION: type = KEY_ACTIVATION; break;
3032 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
3033 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
3034 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
3035 default: type = NULL; break;
3038 saxdb_start_record(ctx, KEY_COOKIE, 0);
3039 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
3040 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
3042 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
3043 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
3044 saxdb_end_record(ctx);
3048 struct handle_note *prev, *note;
3049 saxdb_start_record(ctx, KEY_NOTES, 0);
3050 WALK_NOTES(hi, prev, note) {
3051 snprintf(flags, sizeof(flags), "%d", note->id);
3052 saxdb_start_record(ctx, flags, 0);
3054 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
3055 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
3056 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
3057 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
3058 saxdb_end_record(ctx);
3060 saxdb_end_record(ctx);
3063 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
3065 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
3067 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
3069 saxdb_write_string(ctx, KEY_FAKEIDENT, hi->fakeident);
3073 for (ii=flen=0; handle_flags[ii]; ++ii)
3074 if (hi->flags & (1 << ii))
3075 flags[flen++] = handle_flags[ii];
3077 saxdb_write_string(ctx, KEY_FLAGS, flags);
3079 saxdb_write_int(ctx, KEY_ID, hi->id);
3081 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
3082 if (hi->last_quit_host[0])
3083 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
3084 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
3086 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
3087 if (hi->masks->used)
3088 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
3090 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
3092 struct string_list *slist;
3093 struct nick_info *ni;
3095 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
3096 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
3097 saxdb_write_string_list(ctx, KEY_NICKS, slist);
3101 if (hi->opserv_level)
3102 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
3103 if (hi->language != lang_C)
3104 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
3105 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
3106 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
3107 if (hi->screen_width)
3108 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
3109 if (hi->table_width)
3110 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
3111 flags[0] = hi->userlist_style;
3113 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
3114 saxdb_end_record(ctx);
3119 static handle_merge_func_t *handle_merge_func_list;
3120 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
3123 reg_handle_merge_func(handle_merge_func_t func)
3125 if (handle_merge_func_used == handle_merge_func_size) {
3126 if (handle_merge_func_size) {
3127 handle_merge_func_size <<= 1;
3128 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
3130 handle_merge_func_size = 8;
3131 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
3134 handle_merge_func_list[handle_merge_func_used++] = func;
3137 static NICKSERV_FUNC(cmd_merge)
3139 struct handle_info *hi_from, *hi_to;
3140 struct userNode *last_user;
3141 struct userData *cList, *cListNext;
3142 unsigned int ii, jj, n;
3143 char buffer[MAXLEN];
3145 NICKSERV_MIN_PARMS(3);
3147 if (!(hi_from = get_victim_oper(user, argv[1])))
3149 if (!(hi_to = get_victim_oper(user, argv[2])))
3151 if (hi_to == hi_from) {
3152 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
3156 for (n=0; n<handle_merge_func_used; n++)
3157 handle_merge_func_list[n](user, hi_to, hi_from);
3159 /* Append "from" handle's nicks to "to" handle's nick list. */
3161 struct nick_info *last_ni;
3162 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3163 last_ni->next = hi_from->nicks;
3165 while (hi_from->nicks) {
3166 hi_from->nicks->owner = hi_to;
3167 hi_from->nicks = hi_from->nicks->next;
3170 /* Merge the hostmasks. */
3171 for (ii=0; ii<hi_from->masks->used; ii++) {
3172 char *mask = hi_from->masks->list[ii];
3173 for (jj=0; jj<hi_to->masks->used; jj++)
3174 if (match_ircglobs(hi_to->masks->list[jj], mask))
3176 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3177 string_list_append(hi_to->masks, strdup(mask));
3180 /* Merge the lists of authed users. */
3182 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3183 last_user->next_authed = hi_from->users;
3185 hi_to->users = hi_from->users;
3187 /* Repoint the old "from" handle's users. */
3188 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3189 last_user->handle_info = hi_to;
3191 hi_from->users = NULL;
3193 /* Merge channel userlists. */
3194 for (cList=hi_from->channels; cList; cList=cListNext) {
3195 struct userData *cList2;
3196 cListNext = cList->u_next;
3197 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3198 if (cList->channel == cList2->channel)
3200 if (cList2 && (cList2->access >= cList->access)) {
3201 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);
3202 /* keep cList2 in hi_to; remove cList from hi_from */
3203 del_channel_user(cList, 1);
3206 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);
3207 /* remove the lower-ranking cList2 from hi_to */
3208 del_channel_user(cList2, 1);
3210 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3212 /* cList needs to be moved from hi_from to hi_to */
3213 cList->handle = hi_to;
3214 /* Remove from linked list for hi_from */
3215 assert(!cList->u_prev);
3216 hi_from->channels = cList->u_next;
3218 cList->u_next->u_prev = cList->u_prev;
3219 /* Add to linked list for hi_to */
3220 cList->u_prev = NULL;
3221 cList->u_next = hi_to->channels;
3222 if (hi_to->channels)
3223 hi_to->channels->u_prev = cList;
3224 hi_to->channels = cList;
3228 /* Do they get an OpServ level promotion? */
3229 if (hi_from->opserv_level > hi_to->opserv_level)
3230 hi_to->opserv_level = hi_from->opserv_level;
3232 /* What about last seen time? */
3233 if (hi_from->lastseen > hi_to->lastseen)
3234 hi_to->lastseen = hi_from->lastseen;
3236 /* New karma is the sum of the two original karmas. */
3237 hi_to->karma += hi_from->karma;
3239 /* Does a fakehost carry over? (This intentionally doesn't set it
3240 * for users previously attached to hi_to. They'll just have to
3243 if (hi_from->fakehost && !hi_to->fakehost)
3244 hi_to->fakehost = strdup(hi_from->fakehost);
3245 if (hi_from->fakeident && !hi_to->fakeident)
3246 hi_to->fakeident = strdup(hi_from->fakeident);
3248 /* Notify of success. */
3249 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3250 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3251 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3253 /* Unregister the "from" handle. */
3254 nickserv_unregister_handle(hi_from, NULL);
3259 struct nickserv_discrim {
3260 unsigned long flags_on, flags_off;
3261 unsigned long min_registered, max_registered;
3262 unsigned long lastseen;
3264 int min_level, max_level;
3265 int min_karma, max_karma;
3266 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3267 const char *nickmask;
3268 const char *hostmask;
3269 const char *fakehostmask;
3270 const char *fakeidentmask;
3271 const char *handlemask;
3272 const char *emailmask;
3275 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
3277 struct discrim_apply_info {
3278 struct nickserv_discrim *discrim;
3279 discrim_search_func func;
3280 struct userNode *source;
3281 unsigned int matched;
3284 static struct nickserv_discrim *
3285 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3288 struct nickserv_discrim *discrim;
3290 discrim = malloc(sizeof(*discrim));
3291 memset(discrim, 0, sizeof(*discrim));
3292 discrim->min_level = 0;
3293 discrim->max_level = INT_MAX;
3294 discrim->limit = 50;
3295 discrim->min_registered = 0;
3296 discrim->max_registered = ULONG_MAX;
3297 discrim->lastseen = ULONG_MAX;
3298 discrim->min_karma = INT_MIN;
3299 discrim->max_karma = INT_MAX;
3301 for (i=0; i<argc; i++) {
3302 if (i == argc - 1) {
3303 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3306 if (!irccasecmp(argv[i], "limit")) {
3307 discrim->limit = strtoul(argv[++i], NULL, 0);
3308 } else if (!irccasecmp(argv[i], "flags")) {
3309 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3310 } else if (!irccasecmp(argv[i], "registered")) {
3311 const char *cmp = argv[++i];
3312 if (cmp[0] == '<') {
3313 if (cmp[1] == '=') {
3314 discrim->min_registered = now - ParseInterval(cmp+2);
3316 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3318 } else if (cmp[0] == '=') {
3319 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3320 } else if (cmp[0] == '>') {
3321 if (cmp[1] == '=') {
3322 discrim->max_registered = now - ParseInterval(cmp+2);
3324 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3327 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3329 } else if (!irccasecmp(argv[i], "seen")) {
3330 discrim->lastseen = now - ParseInterval(argv[++i]);
3331 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3332 discrim->nickmask = argv[++i];
3333 } else if (!irccasecmp(argv[i], "hostmask")) {
3335 if (!irccasecmp(argv[i], "exact")) {
3336 if (i == argc - 1) {
3337 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3340 discrim->hostmask_type = EXACT;
3341 } else if (!irccasecmp(argv[i], "subset")) {
3342 if (i == argc - 1) {
3343 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3346 discrim->hostmask_type = SUBSET;
3347 } else if (!irccasecmp(argv[i], "superset")) {
3348 if (i == argc - 1) {
3349 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3352 discrim->hostmask_type = SUPERSET;
3353 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3354 if (i == argc - 1) {
3355 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3358 discrim->hostmask_type = LASTQUIT;
3361 discrim->hostmask_type = SUPERSET;
3363 discrim->hostmask = argv[++i];
3364 } else if (!irccasecmp(argv[i], "fakehost")) {
3365 if (!irccasecmp(argv[++i], "*")) {
3366 discrim->fakehostmask = 0;
3368 discrim->fakehostmask = argv[i];
3370 } else if (!irccasecmp(argv[i], "fakeident")) {
3371 if (!irccasecmp(argv[++i], "*")) {
3372 discrim->fakeidentmask = 0;
3374 discrim->fakeidentmask = argv[i];
3376 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3377 if (!irccasecmp(argv[++i], "*")) {
3378 discrim->handlemask = 0;
3380 discrim->handlemask = argv[i];
3382 } else if (!irccasecmp(argv[i], "email")) {
3383 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3384 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3386 } else if (!irccasecmp(argv[++i], "*")) {
3387 discrim->emailmask = 0;
3389 discrim->emailmask = argv[i];
3391 } else if (!irccasecmp(argv[i], "access")) {
3392 const char *cmp = argv[++i];
3393 if (cmp[0] == '<') {
3394 if (discrim->min_level == 0) discrim->min_level = 1;
3395 if (cmp[1] == '=') {
3396 discrim->max_level = strtoul(cmp+2, NULL, 0);
3398 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3400 } else if (cmp[0] == '=') {
3401 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3402 } else if (cmp[0] == '>') {
3403 if (cmp[1] == '=') {
3404 discrim->min_level = strtoul(cmp+2, NULL, 0);
3406 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3409 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3411 } else if (!irccasecmp(argv[i], "karma")) {
3412 const char *cmp = argv[++i];
3413 if (cmp[0] == '<') {
3414 if (cmp[1] == '=') {
3415 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3417 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3419 } else if (cmp[0] == '=') {
3420 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3421 } else if (cmp[0] == '>') {
3422 if (cmp[1] == '=') {
3423 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3425 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3428 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3431 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3442 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3444 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3445 || (discrim->flags_off & hi->flags)
3446 || (discrim->min_registered > hi->registered)
3447 || (discrim->max_registered < hi->registered)
3448 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3449 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3450 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3451 || (discrim->fakeidentmask && (!hi->fakeident || !match_ircglob(hi->fakeident, discrim->fakeidentmask)))
3452 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3453 || (discrim->min_level > hi->opserv_level)
3454 || (discrim->max_level < hi->opserv_level)
3455 || (discrim->min_karma > hi->karma)
3456 || (discrim->max_karma < hi->karma)
3460 if (discrim->hostmask) {
3462 for (i=0; i<hi->masks->used; i++) {
3463 const char *mask = hi->masks->list[i];
3464 if ((discrim->hostmask_type == SUBSET)
3465 && (match_ircglobs(discrim->hostmask, mask))) break;
3466 else if ((discrim->hostmask_type == EXACT)
3467 && !irccasecmp(discrim->hostmask, mask)) break;
3468 else if ((discrim->hostmask_type == SUPERSET)
3469 && (match_ircglobs(mask, discrim->hostmask))) break;
3470 else if ((discrim->hostmask_type == LASTQUIT)
3471 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3473 if (i==hi->masks->used) return 0;
3475 if (discrim->nickmask) {
3476 struct nick_info *nick = hi->nicks;
3478 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3481 if (!nick) return 0;
3487 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3489 dict_iterator_t it, next;
3490 unsigned int matched;
3492 for (it = dict_first(nickserv_handle_dict), matched = 0;
3493 it && (matched < discrim->limit);
3495 next = iter_next(it);
3496 if (nickserv_discrim_match(discrim, iter_data(it))) {
3497 dsf(source, iter_data(it));
3505 search_print_func(struct userNode *source, struct handle_info *match)
3507 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3511 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3516 search_unregister_func (struct userNode *source, struct handle_info *match)
3518 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3519 nickserv_unregister_handle(match, source);
3523 nickserv_sort_accounts_by_access(const void *a, const void *b)
3525 const struct handle_info *hi_a = *(const struct handle_info**)a;
3526 const struct handle_info *hi_b = *(const struct handle_info**)b;
3527 if (hi_a->opserv_level != hi_b->opserv_level)
3528 return hi_b->opserv_level - hi_a->opserv_level;
3529 return irccasecmp(hi_a->handle, hi_b->handle);
3533 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3535 struct handle_info_list hil;
3536 struct helpfile_table tbl;
3541 memset(&hil, 0, sizeof(hil));
3542 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3543 struct handle_info *hi = iter_data(it);
3544 if (hi->opserv_level)
3545 handle_info_list_append(&hil, hi);
3547 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3548 tbl.length = hil.used + 1;
3550 tbl.flags = TABLE_NO_FREE;
3551 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3552 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3555 for (ii = 0; ii < hil.used; ) {
3556 ary = malloc(tbl.width * sizeof(ary[0]));
3557 ary[0] = hil.list[ii]->handle;
3558 ary[1] = strtab(hil.list[ii]->opserv_level);
3559 tbl.contents[++ii] = ary;
3561 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3562 reply("MSG_MATCH_COUNT", hil.used);
3563 for (ii = 0; ii < hil.used; ii++)
3564 free(tbl.contents[ii]);
3569 static NICKSERV_FUNC(cmd_search)
3571 struct nickserv_discrim *discrim;
3572 discrim_search_func action;
3573 struct svccmd *subcmd;
3574 unsigned int matches;
3577 NICKSERV_MIN_PARMS(3);
3578 sprintf(buf, "search %s", argv[1]);
3579 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3580 if (!irccasecmp(argv[1], "print"))
3581 action = search_print_func;
3582 else if (!irccasecmp(argv[1], "count"))
3583 action = search_count_func;
3584 else if (!irccasecmp(argv[1], "unregister"))
3585 action = search_unregister_func;
3587 reply("NSMSG_INVALID_ACTION", argv[1]);
3591 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3594 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3598 if (action == search_print_func)
3599 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3600 else if (action == search_count_func)
3601 discrim->limit = INT_MAX;
3603 matches = nickserv_discrim_search(discrim, action, user);
3606 reply("MSG_MATCH_COUNT", matches);
3608 reply("MSG_NO_MATCHES");
3614 static MODCMD_FUNC(cmd_checkpass)
3616 struct handle_info *hi;
3618 NICKSERV_MIN_PARMS(3);
3619 if (!(hi = get_handle_info(argv[1]))) {
3620 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3623 if (checkpass(argv[2], hi->passwd))
3624 reply("CHECKPASS_YES");
3626 reply("CHECKPASS_NO");
3631 static MODCMD_FUNC(cmd_checkemail)
3633 struct handle_info *hi;
3635 NICKSERV_MIN_PARMS(3);
3636 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
3639 if (!hi->email_addr)
3640 reply("CHECKEMAIL_NOT_SET");
3641 else if (!irccasecmp(argv[2], hi->email_addr))
3642 reply("CHECKEMAIL_YES");
3644 reply("CHECKEMAIL_NO");
3650 nickserv_db_read_handle(const char *handle, dict_t obj)
3653 struct string_list *masks, *slist;
3654 struct handle_info *hi;
3655 struct userNode *authed_users;
3656 struct userData *channel_list;
3661 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3662 id = str ? strtoul(str, NULL, 0) : 0;
3663 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3665 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3668 if ((hi = get_handle_info(handle))) {
3669 authed_users = hi->users;
3670 channel_list = hi->channels;
3672 hi->channels = NULL;
3673 dict_remove(nickserv_handle_dict, hi->handle);
3675 authed_users = NULL;
3676 channel_list = NULL;
3678 hi = register_handle(handle, str, id);
3680 hi->users = authed_users;
3681 while (authed_users) {
3682 authed_users->handle_info = hi;
3683 authed_users = authed_users->next_authed;
3686 hi->channels = channel_list;
3687 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3688 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3689 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3690 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3691 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3692 hi->language = language_find(str ? str : "C");
3693 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3694 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3695 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3697 hi->infoline = strdup(str);
3698 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3699 hi->registered = str ? strtoul(str, NULL, 0) : now;
3700 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3701 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
3702 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
3703 hi->karma = str ? strtoul(str, NULL, 0) : 0;
3704 /* We want to read the nicks even if disable_nicks is set. This is so
3705 * that we don't lose the nick data entirely. */
3706 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3708 for (ii=0; ii<slist->used; ii++)
3709 register_nick(slist->list[ii], hi);
3711 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3713 for (ii=0; str[ii]; ii++)
3714 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3716 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3717 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3718 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3719 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3720 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3721 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3722 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3724 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3726 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3727 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3729 nickserv_set_email_addr(hi, str);
3730 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3732 hi->epithet = strdup(str);
3733 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3735 hi->fakehost = strdup(str);
3736 str = database_get_data(obj, KEY_FAKEIDENT, RECDB_QSTRING);
3738 hi->fakeident = strdup(str);
3739 /* Read the "cookie" sub-database (if it exists). */
3740 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3742 const char *data, *type, *expires, *cookie_str;
3743 struct handle_cookie *cookie;
3745 cookie = calloc(1, sizeof(*cookie));
3746 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3747 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3748 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3749 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3750 if (!type || !expires || !cookie_str) {
3751 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3754 if (!irccasecmp(type, KEY_ACTIVATION))
3755 cookie->type = ACTIVATION;
3756 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3757 cookie->type = PASSWORD_CHANGE;
3758 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3759 cookie->type = EMAIL_CHANGE;
3760 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3761 cookie->type = ALLOWAUTH;
3763 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3766 cookie->expires = strtoul(expires, NULL, 0);
3767 if (cookie->expires < now)
3770 cookie->data = strdup(data);
3771 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3775 nickserv_bake_cookie(cookie);
3777 nickserv_free_cookie(cookie);
3779 /* Read the "notes" sub-database (if it exists). */
3780 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
3783 struct handle_note *last_note;
3784 struct handle_note *note;
3787 for (it = dict_first(subdb); it; it = iter_next(it)) {
3788 const char *expires;
3792 const char *note_id;
3795 note_id = iter_key(it);
3796 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
3798 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", note_id, hi->handle);
3801 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
3802 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
3803 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
3804 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
3805 if (!setter || !text || !set) {
3806 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", note_id, hi->handle);
3809 note = calloc(1, sizeof(*note) + strlen(text));
3811 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
3812 note->set = strtoul(set, NULL, 10);
3813 note->id = strtoul(note_id, NULL, 10);
3814 safestrncpy(note->setter, setter, sizeof(note->setter));
3815 strcpy(note->note, text);
3817 last_note->next = note;
3826 nickserv_saxdb_read(dict_t db) {
3828 struct record_data *rd;
3830 for (it=dict_first(db); it; it=iter_next(it)) {
3832 nickserv_db_read_handle(iter_key(it), rd->d.object);
3837 static NICKSERV_FUNC(cmd_mergedb)
3839 struct timeval start, stop;
3842 NICKSERV_MIN_PARMS(2);
3843 gettimeofday(&start, NULL);
3844 if (!(db = parse_database(argv[1]))) {
3845 reply("NSMSG_DB_UNREADABLE", argv[1]);
3848 nickserv_saxdb_read(db);
3850 gettimeofday(&stop, NULL);
3851 stop.tv_sec -= start.tv_sec;
3852 stop.tv_usec -= start.tv_usec;
3853 if (stop.tv_usec < 0) {
3855 stop.tv_usec += 1000000;
3857 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
3862 expire_handles(UNUSED_ARG(void *data))
3864 dict_iterator_t it, next;
3865 unsigned long expiry;
3866 struct handle_info *hi;
3868 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3869 next = iter_next(it);
3871 if ((hi->opserv_level > 0)
3873 || HANDLE_FLAGGED(hi, FROZEN)
3874 || HANDLE_FLAGGED(hi, NODELETE)) {
3877 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3878 if ((now - hi->lastseen) > expiry) {
3879 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3880 nickserv_unregister_handle(hi, NULL);
3884 if (nickserv_conf.handle_expire_frequency)
3885 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3889 nickserv_load_dict(const char *fname)
3893 if (!(file = fopen(fname, "r"))) {
3894 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3897 while (fgets(line, sizeof(line), file)) {
3900 if (line[strlen(line)-1] == '\n')
3901 line[strlen(line)-1] = 0;
3902 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3905 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3908 static enum reclaim_action
3909 reclaim_action_from_string(const char *str) {
3911 return RECLAIM_NONE;
3912 else if (!irccasecmp(str, "warn"))
3913 return RECLAIM_WARN;
3914 else if (!irccasecmp(str, "svsnick"))
3915 return RECLAIM_SVSNICK;
3916 else if (!irccasecmp(str, "kill"))
3917 return RECLAIM_KILL;
3919 return RECLAIM_NONE;
3923 nickserv_conf_read(void)
3925 dict_t conf_node, child;
3929 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3930 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3933 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3935 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3936 if (nickserv_conf.valid_handle_regex_set)
3937 regfree(&nickserv_conf.valid_handle_regex);
3939 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3940 nickserv_conf.valid_handle_regex_set = !err;
3941 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3943 nickserv_conf.valid_handle_regex_set = 0;
3945 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3946 if (nickserv_conf.valid_nick_regex_set)
3947 regfree(&nickserv_conf.valid_nick_regex);
3949 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3950 nickserv_conf.valid_nick_regex_set = !err;
3951 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3953 nickserv_conf.valid_nick_regex_set = 0;
3955 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3957 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3958 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3959 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3960 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3961 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3962 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3963 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3964 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3965 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3966 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3967 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3968 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3969 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3970 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3971 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3972 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3973 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3974 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3975 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3976 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3977 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3978 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3979 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3980 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3981 str = database_get_data(conf_node, KEY_SET_FAKEIDENT_LEVEL, RECDB_QSTRING);
3982 nickserv_conf.set_fakeident_level = str ? strtoul(str, NULL, 0) : 1000;
3983 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3985 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3986 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3987 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3989 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3990 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3991 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3993 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3994 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3995 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3996 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3997 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3998 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3999 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
4000 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
4001 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
4002 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
4003 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
4006 nickserv_conf.ounregister_flags = 0;
4008 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
4011 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
4013 str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING);
4015 nickserv_conf.handle_ts_mode = TS_IGNORE;
4016 else if (!irccasecmp(str, "ircu"))
4017 nickserv_conf.handle_ts_mode = TS_IRCU;
4019 nickserv_conf.handle_ts_mode = TS_IGNORE;
4020 if (!nickserv_conf.disable_nicks) {
4021 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
4022 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4023 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
4024 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
4025 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
4026 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4027 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
4028 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
4030 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
4031 for (it=dict_first(child); it; it=iter_next(it)) {
4032 const char *key = iter_key(it), *value;
4036 if (!strncasecmp(key, "uc_", 3))
4037 flag = toupper(key[3]);
4038 else if (!strncasecmp(key, "lc_", 3))
4039 flag = tolower(key[3]);
4043 if ((pos = handle_inverse_flags[flag])) {
4044 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
4045 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
4048 if (nickserv_conf.weak_password_dict)
4049 dict_delete(nickserv_conf.weak_password_dict);
4050 nickserv_conf.weak_password_dict = dict_new();
4051 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
4052 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
4053 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
4054 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
4056 nickserv_load_dict(str);
4057 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
4058 if (nickserv && str)
4059 NickChange(nickserv, str, 0);
4060 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
4061 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
4062 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
4063 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
4064 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
4065 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
4066 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
4067 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
4068 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
4069 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
4070 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
4071 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
4072 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
4073 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
4074 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
4075 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
4076 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
4077 titlehost_suffix = str ? str : "example.net";
4078 str = conf_get_data("server/network", RECDB_QSTRING);
4079 nickserv_conf.network_name = str ? str : "some IRC network";
4080 if (!nickserv_conf.auth_policer_params) {
4081 nickserv_conf.auth_policer_params = policer_params_new();
4082 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
4083 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
4085 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
4086 for (it=dict_first(child); it; it=iter_next(it))
4087 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
4091 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
4093 char newnick[NICKLEN+1];
4102 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4104 case RECLAIM_SVSNICK:
4106 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
4107 } while (GetUserH(newnick));
4108 irc_svsnick(nickserv, user, newnick);
4111 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
4112 DelUser(user, nickserv, 1, msg);
4118 nickserv_reclaim_p(void *data) {
4119 struct userNode *user = data;
4120 struct nick_info *ni = get_nick_info(user->nick);
4122 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4126 check_user_nick(struct userNode *user) {
4127 struct nick_info *ni;
4128 user->modes &= ~FLAGS_REGNICK;
4129 if (!(ni = get_nick_info(user->nick)))
4131 if (user->handle_info == ni->owner) {
4132 user->modes |= FLAGS_REGNICK;
4136 if (nickserv_conf.warn_nick_owned)
4137 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4138 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
4140 if (nickserv_conf.auto_reclaim_delay)
4141 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
4143 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4147 handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
4149 struct handle_info *hi = NULL;
4152 hi = dict_find(nickserv_handle_dict, stamp, NULL);
4153 if ((hi == NULL) && (serial != 0)) {
4155 inttobase64(id, serial, IDLEN);
4156 hi = dict_find(nickserv_id_dict, id, NULL);
4160 if ((nickserv_conf.handle_ts_mode == TS_IRCU)
4161 && (timestamp != hi->registered)) {
4164 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
4167 set_user_handle_info(user, hi, 0);
4169 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial);
4174 handle_nick_change(struct userNode *user, const char *old_nick)
4176 struct handle_info *hi;
4178 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4179 dict_remove(nickserv_allow_auth_dict, old_nick);
4180 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4182 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4183 check_user_nick(user);
4187 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
4189 dict_remove(nickserv_allow_auth_dict, user->nick);
4190 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4191 set_user_handle_info(user, NULL, 0);
4194 static struct modcmd *
4195 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4197 if (min_level > 0) {
4199 sprintf(buf, "%u", min_level);
4200 if (must_be_qualified) {
4201 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4203 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4205 } else if (min_level == 0) {
4206 if (must_be_qualified) {
4207 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4209 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4212 if (must_be_qualified) {
4213 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4215 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4221 nickserv_db_cleanup(void)
4223 unreg_del_user_func(nickserv_remove_user);
4224 userList_clean(&curr_helpers);
4225 policer_params_delete(nickserv_conf.auth_policer_params);
4226 dict_delete(nickserv_handle_dict);
4227 dict_delete(nickserv_nick_dict);
4228 dict_delete(nickserv_opt_dict);
4229 dict_delete(nickserv_allow_auth_dict);
4230 dict_delete(nickserv_email_dict);
4231 dict_delete(nickserv_id_dict);
4232 dict_delete(nickserv_conf.weak_password_dict);
4233 free(auth_func_list);
4234 free(unreg_func_list);
4236 free(allowauth_func_list);
4237 free(handle_merge_func_list);
4238 free(failpw_func_list);
4239 if (nickserv_conf.valid_handle_regex_set)
4240 regfree(&nickserv_conf.valid_handle_regex);
4241 if (nickserv_conf.valid_nick_regex_set)
4242 regfree(&nickserv_conf.valid_nick_regex);
4246 init_nickserv(const char *nick)
4249 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4250 reg_new_user_func(check_user_nick);
4251 reg_nick_change_func(handle_nick_change);
4252 reg_del_user_func(nickserv_remove_user);
4253 reg_account_func(handle_account);
4255 /* set up handle_inverse_flags */
4256 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4257 for (i=0; handle_flags[i]; i++) {
4258 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4259 flag_access_levels[i] = 0;
4262 conf_register_reload(nickserv_conf_read);
4263 nickserv_opt_dict = dict_new();
4264 nickserv_email_dict = dict_new();
4265 dict_set_free_keys(nickserv_email_dict, free);
4266 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4268 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4269 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4270 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4271 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4272 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4273 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4274 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4275 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4276 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4277 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4278 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4279 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4280 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4281 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4282 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4283 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4284 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4285 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4286 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4287 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4288 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4289 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4290 if (!nickserv_conf.disable_nicks) {
4291 /* nick management commands */
4292 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4293 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4294 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4295 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4296 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4297 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4299 if (nickserv_conf.email_enabled) {
4300 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4301 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4302 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4303 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4304 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
4305 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4307 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4308 /* miscellaneous commands */
4309 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4310 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4311 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4312 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4313 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4314 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
4316 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4317 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4318 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4319 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4320 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4321 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4322 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4323 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4324 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4325 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4326 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4327 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4328 if (titlehost_suffix) {
4329 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4330 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4331 dict_insert(nickserv_opt_dict, "FAKEIDENT", opt_fakeident);
4333 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4334 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4335 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4336 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
4338 nickserv_handle_dict = dict_new();
4339 dict_set_free_keys(nickserv_handle_dict, free);
4340 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4342 nickserv_id_dict = dict_new();
4343 dict_set_free_keys(nickserv_id_dict, free);
4345 nickserv_nick_dict = dict_new();
4346 dict_set_free_data(nickserv_nick_dict, free);
4348 nickserv_allow_auth_dict = dict_new();
4350 userList_init(&curr_helpers);
4353 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4354 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4355 nickserv_service = service_register(nickserv);
4357 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4358 reg_exit_func(nickserv_db_cleanup);
4359 if(nickserv_conf.handle_expire_frequency)
4360 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4361 message_register_table(msgtab);