1 /* nickserv.c - Nick/authentication service
2 * Copyright 2000-2004 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() */
34 #define NICKSERV_CONF_NAME "services/nickserv"
36 #define KEY_DISABLE_NICKS "disable_nicks"
37 #define KEY_DEFAULT_HOSTMASK "default_hostmask"
38 #define KEY_NICKS_PER_HANDLE "nicks_per_handle"
39 #define KEY_NICKS_PER_ACCOUNT "nicks_per_account"
40 #define KEY_PASSWORD_MIN_LENGTH "password_min_length"
41 #define KEY_PASSWORD_MIN_DIGITS "password_min_digits"
42 #define KEY_PASSWORD_MIN_UPPER "password_min_upper"
43 #define KEY_PASSWORD_MIN_LOWER "password_min_lower"
44 #define KEY_VALID_HANDLE_REGEX "valid_handle_regex"
45 #define KEY_VALID_ACCOUNT_REGEX "valid_account_regex"
46 #define KEY_VALID_NICK_REGEX "valid_nick_regex"
47 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
48 #define KEY_MODOPER_LEVEL "modoper_level"
49 #define KEY_SET_EPITHET_LEVEL "set_epithet_level"
50 #define KEY_SET_TITLE_LEVEL "set_title_level"
51 #define KEY_SET_FAKEHOST_LEVEL "set_fakehost_level"
52 #define KEY_TITLEHOST_SUFFIX "titlehost_suffix"
53 #define KEY_FLAG_LEVELS "flag_levels"
54 #define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq"
55 #define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq"
56 #define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay"
57 #define KEY_ACCOUNT_EXPIRE_DELAY "account_expire_delay"
58 #define KEY_NOCHAN_HANDLE_EXPIRE_DELAY "nochan_handle_expire_delay"
59 #define KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY "nochan_account_expire_delay"
60 #define KEY_DICT_FILE "dict_file"
61 #define KEY_NICK "nick"
62 #define KEY_LANGUAGE "language"
63 #define KEY_AUTOGAG_ENABLED "autogag_enabled"
64 #define KEY_AUTOGAG_DURATION "autogag_duration"
65 #define KEY_AUTH_POLICER "auth_policer"
66 #define KEY_EMAIL_VISIBLE_LEVEL "email_visible_level"
67 #define KEY_EMAIL_ENABLED "email_enabled"
68 #define KEY_EMAIL_REQUIRED "email_required"
69 #define KEY_COOKIE_TIMEOUT "cookie_timeout"
70 #define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
71 #define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
74 #define KEY_PASSWD "passwd"
75 #define KEY_NICKS "nicks"
76 #define KEY_MASKS "masks"
77 #define KEY_OPSERV_LEVEL "opserv_level"
78 #define KEY_FLAGS "flags"
79 #define KEY_REGISTER_ON "register"
80 #define KEY_LAST_SEEN "lastseen"
81 #define KEY_INFO "info"
82 #define KEY_USERLIST_STYLE "user_style"
83 #define KEY_SCREEN_WIDTH "screen_width"
84 #define KEY_LAST_AUTHED_HOST "last_authed_host"
85 #define KEY_LAST_QUIT_HOST "last_quit_host"
86 #define KEY_EMAIL_ADDR "email_addr"
87 #define KEY_COOKIE "cookie"
88 #define KEY_COOKIE_DATA "data"
89 #define KEY_COOKIE_TYPE "type"
90 #define KEY_COOKIE_EXPIRES "expires"
91 #define KEY_ACTIVATION "activation"
92 #define KEY_PASSWORD_CHANGE "password change"
93 #define KEY_EMAIL_CHANGE "email change"
94 #define KEY_ALLOWAUTH "allowauth"
95 #define KEY_EPITHET "epithet"
96 #define KEY_TABLE_WIDTH "table_width"
97 #define KEY_ANNOUNCEMENTS "announcements"
98 #define KEY_MAXLOGINS "maxlogins"
99 #define KEY_FAKEHOST "fakehost"
101 #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
103 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
104 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
105 typedef OPTION_FUNC(option_func_t);
107 DEFINE_LIST(handle_info_list, struct handle_info*);
109 #define NICKSERV_MIN_PARMS(N) do { \
111 reply("MSG_MISSING_PARAMS", argv[0]); \
112 svccmd_send_help(user, nickserv, cmd); \
116 struct userNode *nickserv;
117 struct userList curr_helpers;
118 const char *handle_flags = HANDLE_FLAGS;
120 static struct module *nickserv_module;
121 static struct service *nickserv_service;
122 static struct log_type *NS_LOG;
123 static dict_t nickserv_handle_dict; /* contains struct handle_info* */
124 static dict_t nickserv_id_dict; /* contains struct handle_info* */
125 static dict_t nickserv_nick_dict; /* contains struct nick_info* */
126 static dict_t nickserv_opt_dict; /* contains option_func_t* */
127 static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
128 static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
129 static char handle_inverse_flags[256];
130 static unsigned int flag_access_levels[32];
131 static const struct message_entry msgtab[] = {
132 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
133 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
134 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
135 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
136 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
137 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
138 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
139 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
140 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
141 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
142 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
143 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
144 { "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." },
145 { "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." },
146 { "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." },
147 { "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." },
148 { "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." },
149 { "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." },
150 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
151 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
152 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
153 { "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." },
154 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
155 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
156 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
157 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
158 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
159 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
160 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
161 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
162 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
163 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
164 { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
165 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
166 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
167 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
168 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
169 { "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)" },
170 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
171 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
172 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
173 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
174 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
175 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
176 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
177 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
178 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
179 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
180 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
181 { "NSMSG_TITLE_INVALID", "Titles cannot contain any dots; please choose another." },
182 { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
183 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
184 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
185 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
186 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
187 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
188 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
189 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
190 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
191 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
192 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
193 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
194 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
195 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
196 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
197 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
198 { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" },
199 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
200 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
201 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
202 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
203 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
204 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
205 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
206 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
207 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
208 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
209 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
210 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
211 { "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)." },
212 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
213 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
214 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
215 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
216 { "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." },
217 { "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." },
218 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
219 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
220 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
221 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
222 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
223 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
224 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
225 { "NSMSG_PASS_SUCCESS", "Password changed." },
226 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
227 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
228 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
229 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
230 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
231 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
232 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
233 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
234 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
235 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
236 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
237 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
238 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
239 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
240 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
241 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
242 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
243 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
244 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
245 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
246 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
247 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
248 { "NSMSG_NO_ACCESS", "Access denied." },
249 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
250 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
251 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
252 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
253 { "NSMSG_DB_MERGED", "$N merged DB from %s (in "FMT_TIME_T".%03lu seconds)." },
254 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
255 { "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." },
256 { "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." },
257 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
258 { "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." },
259 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
260 { "NSMSG_SEARCH_MATCH", "Match: %s" },
261 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
262 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
263 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
264 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
265 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
266 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
267 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
268 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
269 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
270 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
271 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
272 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
273 { "NSMSG_INVALID_ANNOUNCE", "$b%s$b is an invalid announcements value." },
274 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
275 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
276 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
277 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
278 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
279 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
280 { "NSMSG_SET_ANNOUNCEMENTS", "$bANNOUNCEMENTS: $b%s" },
281 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
282 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
283 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
284 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
285 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
286 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
287 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
288 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
289 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
290 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
291 { "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." },
292 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
293 { "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." },
294 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
295 { "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." },
296 { "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." },
297 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
298 { "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." },
299 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
300 { "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." },
301 { "CHECKPASS_YES", "Yes." },
302 { "CHECKPASS_NO", "No." },
306 enum reclaim_action {
312 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
313 static void nickserv_reclaim_p(void *data);
316 unsigned int disable_nicks : 1;
317 unsigned int valid_handle_regex_set : 1;
318 unsigned int valid_nick_regex_set : 1;
319 unsigned int autogag_enabled : 1;
320 unsigned int email_enabled : 1;
321 unsigned int email_required : 1;
322 unsigned int default_hostmask : 1;
323 unsigned int warn_nick_owned : 1;
324 unsigned int warn_clone_auth : 1;
325 unsigned long nicks_per_handle;
326 unsigned long password_min_length;
327 unsigned long password_min_digits;
328 unsigned long password_min_upper;
329 unsigned long password_min_lower;
330 unsigned long db_backup_frequency;
331 unsigned long handle_expire_frequency;
332 unsigned long autogag_duration;
333 unsigned long email_visible_level;
334 unsigned long cookie_timeout;
335 unsigned long handle_expire_delay;
336 unsigned long nochan_handle_expire_delay;
337 unsigned long modoper_level;
338 unsigned long set_epithet_level;
339 unsigned long set_title_level;
340 unsigned long set_fakehost_level;
341 unsigned long handles_per_email;
342 unsigned long email_search_level;
343 const char *network_name;
344 const char *titlehost_suffix;
345 regex_t valid_handle_regex;
346 regex_t valid_nick_regex;
347 dict_t weak_password_dict;
348 struct policer_params *auth_policer_params;
349 enum reclaim_action reclaim_action;
350 enum reclaim_action auto_reclaim_action;
351 unsigned long auto_reclaim_delay;
352 unsigned char default_maxlogins;
353 unsigned char hard_maxlogins;
356 /* We have 2^32 unique account IDs to use. */
357 unsigned long int highest_id = 0;
360 canonicalize_hostmask(char *mask)
362 char *out = mask, *temp;
363 if ((temp = strchr(mask, '!'))) {
365 while (*temp) *out++ = *temp++;
371 static struct handle_info *
372 register_handle(const char *handle, const char *passwd, UNUSED_ARG(unsigned long id))
374 struct handle_info *hi;
376 #ifdef WITH_PROTOCOL_BAHAMUT
377 char id_base64[IDLEN + 1];
380 /* Assign a unique account ID to the account; note that 0 is
381 an invalid account ID. 1 is therefore the first account ID. */
383 id = 1 + highest_id++;
385 /* Note: highest_id is and must always be the highest ID. */
386 if(id > highest_id) {
390 inttobase64(id_base64, id, IDLEN);
392 /* Make sure an account with the same ID doesn't exist. If a
393 duplicate is found, log some details and assign a new one.
394 This should be impossible, but it never hurts to expect it. */
395 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
396 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
402 hi = calloc(1, sizeof(*hi));
403 hi->userlist_style = HI_DEFAULT_STYLE;
404 hi->announcements = '?';
405 hi->handle = strdup(handle);
406 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
408 dict_insert(nickserv_handle_dict, hi->handle, hi);
410 #ifdef WITH_PROTOCOL_BAHAMUT
412 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
419 register_nick(const char *nick, struct handle_info *owner)
421 struct nick_info *ni;
422 ni = malloc(sizeof(struct nick_info));
423 safestrncpy(ni->nick, nick, sizeof(ni->nick));
425 ni->next = owner->nicks;
427 dict_insert(nickserv_nick_dict, ni->nick, ni);
431 free_nick_info(void *vni)
433 struct nick_info *ni = vni;
438 delete_nick(struct nick_info *ni)
440 struct nick_info *last, *next;
441 struct userNode *user;
442 /* Check to see if we should mark a user as unregistered. */
443 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
444 user->modes &= ~FLAGS_REGNICK;
447 /* Remove ni from the nick_info linked list. */
448 if (ni == ni->owner->nicks) {
449 ni->owner->nicks = ni->next;
451 last = ni->owner->nicks;
457 last->next = next->next;
459 dict_remove(nickserv_nick_dict, ni->nick);
462 static unreg_func_t *unreg_func_list;
463 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
466 reg_unreg_func(unreg_func_t func)
468 if (unreg_func_used == unreg_func_size) {
469 if (unreg_func_size) {
470 unreg_func_size <<= 1;
471 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
474 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
477 unreg_func_list[unreg_func_used++] = func;
481 nickserv_free_cookie(void *data)
483 struct handle_cookie *cookie = data;
484 if (cookie->hi) cookie->hi->cookie = NULL;
485 if (cookie->data) free(cookie->data);
490 free_handle_info(void *vhi)
492 struct handle_info *hi = vhi;
494 #ifdef WITH_PROTOCOL_BAHAMUT
497 inttobase64(id, hi->id, IDLEN);
498 dict_remove(nickserv_id_dict, id);
501 free_string_list(hi->masks);
505 delete_nick(hi->nicks);
510 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
511 nickserv_free_cookie(hi->cookie);
513 if (hi->email_addr) {
514 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
515 handle_info_list_remove(hil, hi);
517 dict_remove(nickserv_email_dict, hi->email_addr);
522 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
525 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
529 for (n=0; n<unreg_func_used; n++)
530 unreg_func_list[n](notify, hi);
532 set_user_handle_info(hi->users, NULL, 0);
534 if (nickserv_conf.disable_nicks)
535 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
537 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
539 dict_remove(nickserv_handle_dict, hi->handle);
543 get_handle_info(const char *handle)
545 return dict_find(nickserv_handle_dict, handle, 0);
549 get_nick_info(const char *nick)
551 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
555 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
560 for (nn=0; nn<channel->members.used; ++nn) {
561 mn = channel->members.list[nn];
562 if ((mn->user != except) && (mn->user->handle_info == handle))
569 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
570 if (!user->handle_info) {
572 send_message(user, bot, "MSG_AUTHENTICATE");
576 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
578 send_message(user, bot, "NSMSG_NO_ACCESS");
582 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
584 send_message(user, bot, "MSG_OPER_SUSPENDED");
588 if (user->handle_info->opserv_level < min_level) {
590 send_message(user, bot, "NSMSG_NO_ACCESS");
598 is_valid_handle(const char *handle)
600 struct userNode *user;
601 /* cant register a juped nick/service nick as handle, to prevent confusion */
602 user = GetUserH(handle);
603 if (user && IsLocal(user))
605 /* check against maximum length */
606 if (strlen(handle) > NICKSERV_HANDLE_LEN)
608 /* for consistency, only allow account names that could be nicks */
609 if (!is_valid_nick(handle))
611 /* disallow account names that look like bad words */
612 if (opserv_bad_channel(handle))
614 /* test either regex or containing all valid chars */
615 if (nickserv_conf.valid_handle_regex_set) {
616 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
619 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
620 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
624 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
629 is_registerable_nick(const char *nick)
631 /* make sure it could be used as an account name */
632 if (!is_valid_handle(nick))
635 if (strlen(nick) > NICKLEN)
637 /* test either regex or as valid handle */
638 if (nickserv_conf.valid_nick_regex_set) {
639 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
642 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
643 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
651 is_valid_email_addr(const char *email)
653 return strchr(email, '@') != NULL;
657 visible_email_addr(struct userNode *user, struct handle_info *hi)
659 if (hi->email_addr) {
660 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
661 return hi->email_addr;
671 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
673 struct handle_info *hi;
674 struct userNode *target;
678 if (!(hi = get_handle_info(++name))) {
679 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
684 if (!(target = GetUserH(name))) {
685 send_message(user, service, "MSG_NICK_UNKNOWN", name);
688 if (IsLocal(target)) {
689 if (IsService(target))
690 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
692 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
695 if (!(hi = target->handle_info)) {
696 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
704 oper_outranks(struct userNode *user, struct handle_info *hi) {
705 if (user->handle_info->opserv_level > hi->opserv_level)
707 if (user->handle_info->opserv_level == hi->opserv_level) {
708 if ((user->handle_info->opserv_level == 1000)
709 || (user->handle_info == hi)
710 || ((user->handle_info->opserv_level == 0)
711 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
712 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
716 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
720 static struct handle_info *
721 get_victim_oper(struct userNode *user, const char *target)
723 struct handle_info *hi;
724 if (!(hi = smart_get_handle_info(nickserv, user, target)))
726 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
727 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
730 return oper_outranks(user, hi) ? hi : NULL;
734 valid_user_for(struct userNode *user, struct handle_info *hi)
738 /* If no hostmasks on the account, allow it. */
739 if (!hi->masks->used)
741 /* If any hostmask matches, allow it. */
742 for (ii=0; ii<hi->masks->used; ii++)
743 if (user_matches_glob(user, hi->masks->list[ii], 0))
745 /* If they are allowauthed to this account, allow it (removing the aa). */
746 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
747 dict_remove(nickserv_allow_auth_dict, user->nick);
750 /* The user is not allowed to use this account. */
755 is_secure_password(const char *handle, const char *pass, struct userNode *user)
758 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
760 if (len < nickserv_conf.password_min_length) {
762 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
765 if (!irccasecmp(pass, handle)) {
767 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
770 dict_find(nickserv_conf.weak_password_dict, pass, &i);
773 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
776 for (i=0; i<len; i++) {
777 if (isdigit(pass[i]))
779 if (isupper(pass[i]))
781 if (islower(pass[i]))
784 if ((cnt_lower < nickserv_conf.password_min_lower)
785 || (cnt_upper < nickserv_conf.password_min_upper)
786 || (cnt_digits < nickserv_conf.password_min_digits)) {
788 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
794 static auth_func_t *auth_func_list;
795 static unsigned int auth_func_size = 0, auth_func_used = 0;
798 reg_auth_func(auth_func_t func)
800 if (auth_func_used == auth_func_size) {
801 if (auth_func_size) {
802 auth_func_size <<= 1;
803 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
806 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
809 auth_func_list[auth_func_used++] = func;
812 static handle_rename_func_t *rf_list;
813 static unsigned int rf_list_size, rf_list_used;
816 reg_handle_rename_func(handle_rename_func_t func)
818 if (rf_list_used == rf_list_size) {
821 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
824 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
827 rf_list[rf_list_used++] = func;
831 generate_fakehost(struct handle_info *handle)
833 extern const char *hidden_host_suffix;
834 static char buffer[HOSTLEN+1];
836 if (!handle->fakehost) {
837 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
839 } else if (handle->fakehost[0] == '.') {
840 /* A leading dot indicates the stored value is actually a title. */
841 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, nickserv_conf.titlehost_suffix);
844 return handle->fakehost;
848 apply_fakehost(struct handle_info *handle)
850 struct userNode *target;
855 fake = generate_fakehost(handle);
856 for (target = handle->users; target; target = target->next_authed)
857 assign_fakehost(target, fake, 1);
861 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
864 struct handle_info *old_info;
866 /* This can happen if somebody uses COOKIE while authed, or if
867 * they re-auth to their current handle (which is silly, but users
869 if (user->handle_info == hi)
872 if (user->handle_info) {
873 struct userNode *other;
876 userList_remove(&curr_helpers, user);
878 /* remove from next_authed linked list */
879 if (user->handle_info->users == user) {
880 user->handle_info->users = user->next_authed;
882 for (other = user->handle_info->users;
883 other->next_authed != user;
884 other = other->next_authed) ;
885 other->next_authed = user->next_authed;
887 /* if nobody left on old handle, and they're not an oper, remove !god */
888 if (!user->handle_info->users && !user->handle_info->opserv_level)
889 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
890 /* record them as being last seen at this time */
891 user->handle_info->lastseen = now;
892 /* and record their hostmask */
893 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
895 old_info = user->handle_info;
896 user->handle_info = hi;
897 if (hi && !hi->users && !hi->opserv_level)
898 HANDLE_CLEAR_FLAG(hi, HELPING);
899 for (n=0; n<auth_func_used; n++)
900 auth_func_list[n](user, old_info);
902 struct nick_info *ni;
904 HANDLE_CLEAR_FLAG(hi, FROZEN);
905 if (nickserv_conf.warn_clone_auth) {
906 struct userNode *other;
907 for (other = hi->users; other; other = other->next_authed)
908 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
910 user->next_authed = hi->users;
914 userList_append(&curr_helpers, user);
916 if (hi->fakehost || old_info)
920 #ifdef WITH_PROTOCOL_BAHAMUT
921 /* Stamp users with their account ID. */
923 inttobase64(id, hi->id, IDLEN);
924 #elif WITH_PROTOCOL_P10
925 /* Stamp users with their account name. */
926 char *id = hi->handle;
928 const char *id = "???";
930 if (!nickserv_conf.disable_nicks) {
931 struct nick_info *ni;
932 for (ni = hi->nicks; ni; ni = ni->next) {
933 if (!irccasecmp(user->nick, ni->nick)) {
934 user->modes |= FLAGS_REGNICK;
942 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
943 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
945 /* We cannot clear the user's account ID, unfortunately. */
946 user->next_authed = NULL;
950 static struct handle_info*
951 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
953 struct handle_info *hi;
954 struct nick_info *ni;
955 char crypted[MD5_CRYPT_LENGTH];
957 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
958 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
962 if (!is_secure_password(handle, passwd, user))
965 cryptpass(passwd, crypted);
966 hi = register_handle(handle, crypted, 0);
967 hi->masks = alloc_string_list(1);
969 hi->language = lang_C;
970 hi->registered = now;
972 hi->flags = HI_DEFAULT_FLAGS;
973 if (settee && !no_auth)
974 set_user_handle_info(settee, hi, 1);
977 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
978 else if (nickserv_conf.disable_nicks)
979 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
980 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
981 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
983 register_nick(user->nick, hi);
984 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
986 if (settee && (user != settee))
987 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
992 nickserv_bake_cookie(struct handle_cookie *cookie)
994 cookie->hi->cookie = cookie;
995 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
999 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
1001 struct handle_cookie *cookie;
1002 char subject[128], body[4096], *misc;
1003 const char *netname, *fmt;
1007 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1011 cookie = calloc(1, sizeof(*cookie));
1013 cookie->type = type;
1014 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1015 cookie->expires = now + nickserv_conf.cookie_timeout;
1016 inttobase64(cookie->cookie, rand(), 5);
1017 inttobase64(cookie->cookie+5, rand(), 5);
1019 netname = nickserv_conf.network_name;
1022 switch (cookie->type) {
1024 hi->passwd[0] = 0; /* invalidate password */
1025 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1026 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1027 snprintf(subject, sizeof(subject), fmt, netname);
1028 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1029 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1032 case PASSWORD_CHANGE:
1033 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1034 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1035 snprintf(subject, sizeof(subject), fmt, netname);
1036 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1037 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1040 misc = hi->email_addr;
1041 hi->email_addr = cookie->data;
1043 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1044 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1045 snprintf(subject, sizeof(subject), fmt, netname);
1046 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1047 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1048 sendmail(nickserv, hi, subject, body, 1);
1049 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1050 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1052 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1053 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1054 snprintf(subject, sizeof(subject), fmt, netname);
1055 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1056 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1057 sendmail(nickserv, hi, subject, body, 1);
1060 hi->email_addr = misc;
1063 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1064 snprintf(subject, sizeof(subject), fmt, netname);
1065 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1066 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1067 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1070 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1074 sendmail(nickserv, hi, subject, body, first_time);
1075 nickserv_bake_cookie(cookie);
1079 nickserv_eat_cookie(struct handle_cookie *cookie)
1081 cookie->hi->cookie = NULL;
1082 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1083 nickserv_free_cookie(cookie);
1087 nickserv_free_email_addr(void *data)
1089 handle_info_list_clean(data);
1094 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1096 struct handle_info_list *hil;
1097 /* Remove from old handle_info_list ... */
1098 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1099 handle_info_list_remove(hil, hi);
1100 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1101 hi->email_addr = NULL;
1103 /* Add to the new list.. */
1104 if (new_email_addr) {
1105 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1106 hil = calloc(1, sizeof(*hil));
1107 hil->tag = strdup(new_email_addr);
1108 handle_info_list_init(hil);
1109 dict_insert(nickserv_email_dict, hil->tag, hil);
1111 handle_info_list_append(hil, hi);
1112 hi->email_addr = hil->tag;
1116 static NICKSERV_FUNC(cmd_register)
1118 struct handle_info *hi;
1119 const char *email_addr, *password;
1122 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1123 /* Require the first handle registered to belong to someone +o. */
1124 reply("NSMSG_REQUIRE_OPER");
1128 if (user->handle_info) {
1129 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1133 if (IsRegistering(user)) {
1134 reply("NSMSG_ALREADY_REGISTERING");
1138 if (IsStamped(user)) {
1139 /* Unauthenticated users might still have been stamped
1140 previously and could therefore have a hidden host;
1141 do not allow them to register a new account. */
1142 reply("NSMSG_STAMPED_REGISTER");
1146 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1148 if (!is_valid_handle(argv[1])) {
1149 reply("NSMSG_BAD_HANDLE", argv[1]);
1153 if ((argc >= 4) && nickserv_conf.email_enabled) {
1154 struct handle_info_list *hil;
1157 /* Remember email address. */
1158 email_addr = argv[3];
1160 /* Check that the email address looks valid.. */
1161 if (!is_valid_email_addr(email_addr)) {
1162 reply("NSMSG_BAD_EMAIL_ADDR");
1166 /* .. and that we are allowed to send to it. */
1167 if ((str = sendmail_prohibited_address(email_addr))) {
1168 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1172 /* If we do email verify, make sure we don't spam the address. */
1173 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1175 for (nn=0; nn<hil->used; nn++) {
1176 if (hil->list[nn]->cookie) {
1177 reply("NSMSG_EMAIL_UNACTIVATED");
1181 if (hil->used >= nickserv_conf.handles_per_email) {
1182 reply("NSMSG_EMAIL_OVERUSED");
1195 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1197 /* Add any masks they should get. */
1198 if (nickserv_conf.default_hostmask) {
1199 string_list_append(hi->masks, strdup("*@*"));
1201 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1202 if (user->ip.s_addr && user->hostname[strspn(user->hostname, "0123456789.")])
1203 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1206 /* If they're the first to register, give them level 1000. */
1207 if (dict_size(nickserv_handle_dict) == 1) {
1208 hi->opserv_level = 1000;
1209 reply("NSMSG_ROOT_HANDLE", argv[1]);
1212 /* Set their email address. */
1214 nickserv_set_email_addr(hi, email_addr);
1216 /* If they need to do email verification, tell them. */
1218 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1220 /* Set registering flag.. */
1221 user->modes |= FLAGS_REGISTERING;
1226 static NICKSERV_FUNC(cmd_oregister)
1229 struct userNode *settee;
1230 struct handle_info *hi;
1232 NICKSERV_MIN_PARMS(4);
1234 if (!is_valid_handle(argv[1])) {
1235 reply("NSMSG_BAD_HANDLE", argv[1]);
1239 if (strchr(argv[3], '@')) {
1240 mask = canonicalize_hostmask(strdup(argv[3]));
1242 settee = GetUserH(argv[4]);
1244 reply("MSG_NICK_UNKNOWN", argv[4]);
1251 } else if ((settee = GetUserH(argv[3]))) {
1252 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1254 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1257 if (settee && settee->handle_info) {
1258 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1262 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1266 string_list_append(hi->masks, mask);
1270 static NICKSERV_FUNC(cmd_handleinfo)
1273 unsigned int i, pos=0, herelen;
1274 struct userNode *target, *next_un;
1275 struct handle_info *hi;
1276 const char *nsmsg_none;
1279 if (!(hi = user->handle_info)) {
1280 reply("NSMSG_MUST_AUTH");
1283 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1287 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1288 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1289 #ifdef WITH_PROTOCOL_BAHAMUT
1290 reply("NSMSG_HANDLEINFO_ID", hi->id);
1292 reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi->registered));
1295 intervalString(buff, now - hi->lastseen, user->handle_info);
1296 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1298 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1301 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1302 if (HANDLE_FLAGGED(hi, FROZEN))
1303 reply("NSMSG_HANDLEINFO_VACATION");
1305 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1306 struct do_not_register *dnr;
1307 if ((dnr = chanserv_is_dnr(NULL, hi)))
1308 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1309 if (!oper_outranks(user, hi))
1311 } else if (hi != user->handle_info)
1314 if (nickserv_conf.email_enabled)
1315 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1319 switch (hi->cookie->type) {
1320 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1321 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1322 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1323 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1324 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1330 unsigned long flen = 1;
1331 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1333 for (i=0, flen=1; handle_flags[i]; i++)
1334 if (hi->flags & 1 << i)
1335 flags[flen++] = handle_flags[i];
1337 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1339 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1342 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1343 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1344 || (hi->opserv_level > 0)) {
1345 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1349 reply("NSMSG_HANDLEINFO_FAKEHOST", (hi->fakehost ? hi->fakehost : handle_find_message(hi, "MSG_NONE")));
1351 if (hi->last_quit_host[0])
1352 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1354 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1356 if (nickserv_conf.disable_nicks) {
1357 /* nicks disabled; don't show anything about registered nicks */
1358 } else if (hi->nicks) {
1359 struct nick_info *ni, *next_ni;
1360 for (ni = hi->nicks; ni; ni = next_ni) {
1361 herelen = strlen(ni->nick);
1362 if (pos + herelen + 1 > ArrayLength(buff)) {
1364 goto print_nicks_buff;
1368 memcpy(buff+pos, ni->nick, herelen);
1369 pos += herelen; buff[pos++] = ' ';
1373 reply("NSMSG_HANDLEINFO_NICKS", buff);
1378 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1381 if (hi->masks->used) {
1382 for (i=0; i < hi->masks->used; i++) {
1383 herelen = strlen(hi->masks->list[i]);
1384 if (pos + herelen + 1 > ArrayLength(buff)) {
1386 goto print_mask_buff;
1388 memcpy(buff+pos, hi->masks->list[i], herelen);
1389 pos += herelen; buff[pos++] = ' ';
1390 if (i+1 == hi->masks->used) {
1393 reply("NSMSG_HANDLEINFO_MASKS", buff);
1398 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1402 struct userData *channel, *next;
1405 for (channel = hi->channels; channel; channel = next) {
1406 next = channel->u_next;
1407 name = channel->channel->channel->name;
1408 herelen = strlen(name);
1409 if (pos + herelen + 7 > ArrayLength(buff)) {
1411 goto print_chans_buff;
1413 if (IsUserSuspended(channel))
1415 pos += sprintf(buff+pos, "%d:%s ", channel->access, name);
1419 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1424 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1427 for (target = hi->users; target; target = next_un) {
1428 herelen = strlen(target->nick);
1429 if (pos + herelen + 1 > ArrayLength(buff)) {
1431 goto print_cnick_buff;
1433 next_un = target->next_authed;
1435 memcpy(buff+pos, target->nick, herelen);
1436 pos += herelen; buff[pos++] = ' ';
1440 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1448 static NICKSERV_FUNC(cmd_userinfo)
1450 struct userNode *target;
1452 NICKSERV_MIN_PARMS(2);
1453 if (!(target = GetUserH(argv[1]))) {
1454 reply("MSG_NICK_UNKNOWN", argv[1]);
1457 if (target->handle_info)
1458 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1460 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1464 static NICKSERV_FUNC(cmd_nickinfo)
1466 struct nick_info *ni;
1468 NICKSERV_MIN_PARMS(2);
1469 if (!(ni = get_nick_info(argv[1]))) {
1470 reply("MSG_NICK_UNKNOWN", argv[1]);
1473 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1477 static NICKSERV_FUNC(cmd_rename_handle)
1479 struct handle_info *hi;
1480 char msgbuf[MAXLEN], *old_handle;
1483 NICKSERV_MIN_PARMS(3);
1484 if (!(hi = get_victim_oper(user, argv[1])))
1486 if (!is_valid_handle(argv[2])) {
1487 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1490 if (get_handle_info(argv[2])) {
1491 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1495 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1496 hi->handle = strdup(argv[2]);
1497 dict_insert(nickserv_handle_dict, hi->handle, hi);
1498 for (nn=0; nn<rf_list_used; nn++)
1499 rf_list[nn](hi, old_handle);
1500 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1501 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1502 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1507 static failpw_func_t *failpw_func_list;
1508 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1511 reg_failpw_func(failpw_func_t func)
1513 if (failpw_func_used == failpw_func_size) {
1514 if (failpw_func_size) {
1515 failpw_func_size <<= 1;
1516 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1518 failpw_func_size = 8;
1519 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1522 failpw_func_list[failpw_func_used++] = func;
1525 static NICKSERV_FUNC(cmd_auth)
1527 int pw_arg, used, maxlogins;
1528 struct handle_info *hi;
1530 struct userNode *other;
1532 if (user->handle_info) {
1533 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1536 if (IsStamped(user)) {
1537 /* Unauthenticated users might still have been stamped
1538 previously and could therefore have a hidden host;
1539 do not allow them to authenticate. */
1540 reply("NSMSG_STAMPED_AUTH");
1544 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1546 } else if (argc == 2) {
1547 if (nickserv_conf.disable_nicks) {
1548 if (!(hi = get_handle_info(user->nick))) {
1549 reply("NSMSG_HANDLE_NOT_FOUND");
1553 /* try to look up their handle from their nick */
1554 struct nick_info *ni;
1555 ni = get_nick_info(user->nick);
1557 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1564 reply("MSG_MISSING_PARAMS", argv[0]);
1565 svccmd_send_help(user, nickserv, cmd);
1569 reply("NSMSG_HANDLE_NOT_FOUND");
1572 /* Responses from here on look up the language used by the handle they asked about. */
1573 passwd = argv[pw_arg];
1574 if (!valid_user_for(user, hi)) {
1575 if (hi->email_addr && nickserv_conf.email_enabled)
1576 send_message_type(4, user, cmd->parent->bot,
1577 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1580 send_message_type(4, user, cmd->parent->bot,
1581 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1583 argv[pw_arg] = "BADMASK";
1586 if (!checkpass(passwd, hi->passwd)) {
1588 send_message_type(4, user, cmd->parent->bot,
1589 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1590 argv[pw_arg] = "BADPASS";
1591 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1592 if (nickserv_conf.autogag_enabled) {
1593 if (!user->auth_policer.params) {
1594 user->auth_policer.last_req = now;
1595 user->auth_policer.params = nickserv_conf.auth_policer_params;
1597 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1599 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1600 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1601 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1603 argv[pw_arg] = "GAGGED";
1608 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1609 send_message_type(4, user, cmd->parent->bot,
1610 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1611 argv[pw_arg] = "SUSPENDED";
1614 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1615 for (used = 0, other = hi->users; other; other = other->next_authed) {
1616 if (++used >= maxlogins) {
1617 send_message_type(4, user, cmd->parent->bot,
1618 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1620 argv[pw_arg] = "MAXLOGINS";
1625 set_user_handle_info(user, hi, 1);
1626 if (nickserv_conf.email_required && !hi->email_addr)
1627 reply("NSMSG_PLEASE_SET_EMAIL");
1628 if (!is_secure_password(hi->handle, passwd, NULL))
1629 reply("NSMSG_WEAK_PASSWORD");
1630 if (hi->passwd[0] != '$')
1631 cryptpass(passwd, hi->passwd);
1632 reply("NSMSG_AUTH_SUCCESS");
1633 argv[pw_arg] = "****";
1637 static allowauth_func_t *allowauth_func_list;
1638 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1641 reg_allowauth_func(allowauth_func_t func)
1643 if (allowauth_func_used == allowauth_func_size) {
1644 if (allowauth_func_size) {
1645 allowauth_func_size <<= 1;
1646 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1648 allowauth_func_size = 8;
1649 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1652 allowauth_func_list[allowauth_func_used++] = func;
1655 static NICKSERV_FUNC(cmd_allowauth)
1657 struct userNode *target;
1658 struct handle_info *hi;
1661 NICKSERV_MIN_PARMS(2);
1662 if (!(target = GetUserH(argv[1]))) {
1663 reply("MSG_NICK_UNKNOWN", argv[1]);
1666 if (target->handle_info) {
1667 reply("NSMSG_USER_PREV_AUTH", target->nick);
1670 if (IsStamped(target)) {
1671 /* Unauthenticated users might still have been stamped
1672 previously and could therefore have a hidden host;
1673 do not allow them to authenticate to an account. */
1674 reply("NSMSG_USER_PREV_STAMP", target->nick);
1679 else if (!(hi = get_handle_info(argv[2]))) {
1680 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1684 if (hi->opserv_level > user->handle_info->opserv_level) {
1685 reply("MSG_USER_OUTRANKED", hi->handle);
1688 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1689 || (hi->opserv_level > 0))
1690 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1691 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1694 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1695 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1696 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1697 if (nickserv_conf.email_enabled)
1698 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1700 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1701 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1703 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1705 for (n=0; n<allowauth_func_used; n++)
1706 allowauth_func_list[n](user, target, hi);
1710 static NICKSERV_FUNC(cmd_authcookie)
1712 struct handle_info *hi;
1714 NICKSERV_MIN_PARMS(2);
1715 if (user->handle_info) {
1716 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1719 if (IsStamped(user)) {
1720 /* Unauthenticated users might still have been stamped
1721 previously and could therefore have a hidden host;
1722 do not allow them to authenticate to an account. */
1723 reply("NSMSG_STAMPED_AUTHCOOKIE");
1726 if (!(hi = get_handle_info(argv[1]))) {
1727 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1730 if (!hi->email_addr) {
1731 reply("MSG_SET_EMAIL_ADDR");
1734 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1738 static NICKSERV_FUNC(cmd_delcookie)
1740 struct handle_info *hi;
1742 hi = user->handle_info;
1744 reply("NSMSG_NO_COOKIE");
1747 switch (hi->cookie->type) {
1750 reply("NSMSG_MUST_TIME_OUT");
1753 nickserv_eat_cookie(hi->cookie);
1754 reply("NSMSG_ATE_COOKIE");
1760 static NICKSERV_FUNC(cmd_resetpass)
1762 struct handle_info *hi;
1763 char crypted[MD5_CRYPT_LENGTH];
1765 NICKSERV_MIN_PARMS(3);
1766 if (user->handle_info) {
1767 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1770 if (IsStamped(user)) {
1771 /* Unauthenticated users might still have been stamped
1772 previously and could therefore have a hidden host;
1773 do not allow them to activate an account. */
1774 reply("NSMSG_STAMPED_RESETPASS");
1777 if (!(hi = get_handle_info(argv[1]))) {
1778 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1781 if (!hi->email_addr) {
1782 reply("MSG_SET_EMAIL_ADDR");
1785 cryptpass(argv[2], crypted);
1787 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1791 static NICKSERV_FUNC(cmd_cookie)
1793 struct handle_info *hi;
1796 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1799 NICKSERV_MIN_PARMS(3);
1800 if (!(hi = get_handle_info(argv[1]))) {
1801 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1807 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1808 reply("NSMSG_HANDLE_SUSPENDED");
1813 reply("NSMSG_NO_COOKIE");
1817 /* Check validity of operation before comparing cookie to
1818 * prohibit guessing by authed users. */
1819 if (user->handle_info
1820 && (hi->cookie->type != EMAIL_CHANGE)
1821 && (hi->cookie->type != PASSWORD_CHANGE)) {
1822 reply("NSMSG_CANNOT_COOKIE");
1826 if (strcmp(cookie, hi->cookie->cookie)) {
1827 reply("NSMSG_BAD_COOKIE");
1831 switch (hi->cookie->type) {
1833 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1834 set_user_handle_info(user, hi, 1);
1835 reply("NSMSG_HANDLE_ACTIVATED");
1837 case PASSWORD_CHANGE:
1838 set_user_handle_info(user, hi, 1);
1839 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1840 reply("NSMSG_PASSWORD_CHANGED");
1843 nickserv_set_email_addr(hi, hi->cookie->data);
1844 reply("NSMSG_EMAIL_CHANGED");
1847 set_user_handle_info(user, hi, 1);
1848 reply("NSMSG_AUTH_SUCCESS");
1851 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
1852 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
1856 nickserv_eat_cookie(hi->cookie);
1861 static NICKSERV_FUNC(cmd_oregnick) {
1863 struct handle_info *target;
1864 struct nick_info *ni;
1866 NICKSERV_MIN_PARMS(3);
1867 if (!(target = modcmd_get_handle_info(user, argv[1])))
1870 if (!is_registerable_nick(nick)) {
1871 reply("NSMSG_BAD_NICK", nick);
1874 ni = dict_find(nickserv_nick_dict, nick, NULL);
1876 reply("NSMSG_NICK_EXISTS", nick);
1879 register_nick(nick, target);
1880 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
1884 static NICKSERV_FUNC(cmd_regnick) {
1886 struct nick_info *ni;
1888 if (!is_registerable_nick(user->nick)) {
1889 reply("NSMSG_BAD_NICK", user->nick);
1892 /* count their nicks, see if it's too many */
1893 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
1894 if (n >= nickserv_conf.nicks_per_handle) {
1895 reply("NSMSG_TOO_MANY_NICKS");
1898 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
1900 reply("NSMSG_NICK_EXISTS", user->nick);
1903 register_nick(user->nick, user->handle_info);
1904 reply("NSMSG_REGNICK_SUCCESS", user->nick);
1908 static NICKSERV_FUNC(cmd_pass)
1910 struct handle_info *hi;
1911 const char *old_pass, *new_pass;
1913 NICKSERV_MIN_PARMS(3);
1914 hi = user->handle_info;
1918 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
1919 if (!checkpass(old_pass, hi->passwd)) {
1920 argv[1] = "BADPASS";
1921 reply("NSMSG_PASSWORD_INVALID");
1924 cryptpass(new_pass, hi->passwd);
1926 reply("NSMSG_PASS_SUCCESS");
1931 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
1934 char *new_mask = canonicalize_hostmask(strdup(mask));
1935 for (i=0; i<hi->masks->used; i++) {
1936 if (!irccasecmp(new_mask, hi->masks->list[i])) {
1937 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
1942 string_list_append(hi->masks, new_mask);
1943 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
1947 static NICKSERV_FUNC(cmd_addmask)
1950 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1951 int res = nickserv_addmask(user, user->handle_info, mask);
1955 if (!is_gline(argv[1])) {
1956 reply("NSMSG_MASK_INVALID", argv[1]);
1959 return nickserv_addmask(user, user->handle_info, argv[1]);
1963 static NICKSERV_FUNC(cmd_oaddmask)
1965 struct handle_info *hi;
1967 NICKSERV_MIN_PARMS(3);
1968 if (!(hi = get_victim_oper(user, argv[1])))
1970 return nickserv_addmask(user, hi, argv[2]);
1974 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask)
1977 for (i=0; i<hi->masks->used; i++) {
1978 if (!strcmp(del_mask, hi->masks->list[i])) {
1979 char *old_mask = hi->masks->list[i];
1980 if (hi->masks->used == 1) {
1981 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
1984 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
1985 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
1990 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
1994 static NICKSERV_FUNC(cmd_delmask)
1996 NICKSERV_MIN_PARMS(2);
1997 return nickserv_delmask(user, user->handle_info, argv[1]);
2000 static NICKSERV_FUNC(cmd_odelmask)
2002 struct handle_info *hi;
2003 NICKSERV_MIN_PARMS(3);
2004 if (!(hi = get_victim_oper(user, argv[1])))
2006 return nickserv_delmask(user, hi, argv[2]);
2010 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2011 unsigned int nn, add = 1, pos;
2012 unsigned long added, removed, flag;
2014 for (added=removed=nn=0; str[nn]; nn++) {
2016 case '+': add = 1; break;
2017 case '-': add = 0; break;
2019 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2020 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2023 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2024 /* cheesy avoidance of looking up the flag name.. */
2025 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2028 flag = 1 << (pos - 1);
2030 added |= flag, removed &= ~flag;
2032 removed |= flag, added &= ~flag;
2037 *premoved = removed;
2042 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2044 unsigned long before, after, added, removed;
2045 struct userNode *uNode;
2047 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2048 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2050 hi->flags = (hi->flags | added) & ~removed;
2051 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2053 /* Strip helping flag if they're only a support helper and not
2054 * currently in #support. */
2055 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2056 struct channelList *schannels;
2058 schannels = chanserv_support_channels();
2059 for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
2060 for (ii = 0; ii < schannels->used; ++ii)
2061 if (GetUserMode(schannels->list[ii], uNode))
2063 if (ii < schannels->used)
2067 HANDLE_CLEAR_FLAG(hi, HELPING);
2070 if (after && !before) {
2071 /* Add user to current helper list. */
2072 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2073 userList_append(&curr_helpers, uNode);
2074 } else if (!after && before) {
2075 /* Remove user from current helper list. */
2076 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2077 userList_remove(&curr_helpers, uNode);
2084 set_list(struct userNode *user, struct handle_info *hi, int override)
2088 char *set_display[] = {
2089 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2090 "EMAIL", "ANNOUNCEMENTS", "MAXLOGINS", "LANGUAGE"
2093 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2095 /* Do this so options are presented in a consistent order. */
2096 for (i = 0; i < ArrayLength(set_display); ++i)
2097 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2098 opt(user, hi, override, 0, NULL);
2101 static NICKSERV_FUNC(cmd_set)
2103 struct handle_info *hi;
2106 hi = user->handle_info;
2108 set_list(user, hi, 0);
2111 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2112 reply("NSMSG_INVALID_OPTION", argv[1]);
2115 return opt(user, hi, 0, argc-1, argv+1);
2118 static NICKSERV_FUNC(cmd_oset)
2120 struct handle_info *hi;
2123 NICKSERV_MIN_PARMS(2);
2125 if (!(hi = get_victim_oper(user, argv[1])))
2129 set_list(user, hi, 0);
2133 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2134 reply("NSMSG_INVALID_OPTION", argv[2]);
2138 return opt(user, hi, 1, argc-2, argv+2);
2141 static OPTION_FUNC(opt_info)
2145 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2147 hi->infoline = NULL;
2149 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2153 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2154 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2158 static OPTION_FUNC(opt_width)
2161 hi->screen_width = strtoul(argv[1], NULL, 0);
2163 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2164 hi->screen_width = MIN_LINE_SIZE;
2165 else if (hi->screen_width > MAX_LINE_SIZE)
2166 hi->screen_width = MAX_LINE_SIZE;
2168 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2172 static OPTION_FUNC(opt_tablewidth)
2175 hi->table_width = strtoul(argv[1], NULL, 0);
2177 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2178 hi->table_width = MIN_LINE_SIZE;
2179 else if (hi->screen_width > MAX_LINE_SIZE)
2180 hi->table_width = MAX_LINE_SIZE;
2182 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2186 static OPTION_FUNC(opt_color)
2189 if (enabled_string(argv[1]))
2190 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2191 else if (disabled_string(argv[1]))
2192 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2194 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2199 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2203 static OPTION_FUNC(opt_privmsg)
2206 if (enabled_string(argv[1]))
2207 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2208 else if (disabled_string(argv[1]))
2209 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2211 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2216 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2220 static OPTION_FUNC(opt_style)
2225 if (!irccasecmp(argv[1], "Zoot"))
2226 hi->userlist_style = HI_STYLE_ZOOT;
2227 else if (!irccasecmp(argv[1], "def"))
2228 hi->userlist_style = HI_STYLE_DEF;
2231 switch (hi->userlist_style) {
2240 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2244 static OPTION_FUNC(opt_announcements)
2249 if (enabled_string(argv[1]))
2250 hi->announcements = 'y';
2251 else if (disabled_string(argv[1]))
2252 hi->announcements = 'n';
2253 else if (!strcmp(argv[1], "?") || !irccasecmp(argv[1], "default"))
2254 hi->announcements = '?';
2256 send_message(user, nickserv, "NSMSG_INVALID_ANNOUNCE", argv[1]);
2261 switch (hi->announcements) {
2262 case 'y': choice = user_find_message(user, "MSG_ON"); break;
2263 case 'n': choice = user_find_message(user, "MSG_OFF"); break;
2264 case '?': choice = "default"; break;
2265 default: choice = "unknown"; break;
2267 send_message(user, nickserv, "NSMSG_SET_ANNOUNCEMENTS", choice);
2271 static OPTION_FUNC(opt_password)
2274 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2279 cryptpass(argv[1], hi->passwd);
2281 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2285 static OPTION_FUNC(opt_flags)
2288 unsigned int ii, flen;
2291 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2296 nickserv_apply_flags(user, hi, argv[1]);
2298 for (ii = flen = 0; handle_flags[ii]; ii++)
2299 if (hi->flags & (1 << ii))
2300 flags[flen++] = handle_flags[ii];
2303 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2305 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2309 static OPTION_FUNC(opt_email)
2313 if (!is_valid_email_addr(argv[1])) {
2314 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2317 if ((str = sendmail_prohibited_address(argv[1]))) {
2318 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2321 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2322 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2324 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2326 nickserv_set_email_addr(hi, argv[1]);
2328 nickserv_eat_cookie(hi->cookie);
2329 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2332 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2336 static OPTION_FUNC(opt_maxlogins)
2338 unsigned char maxlogins;
2340 maxlogins = strtoul(argv[1], NULL, 0);
2341 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2342 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2345 hi->maxlogins = maxlogins;
2347 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2348 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2352 static OPTION_FUNC(opt_language)
2354 struct language *lang;
2356 lang = language_find(argv[1]);
2357 if (irccasecmp(lang->name, argv[1]))
2358 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2359 hi->language = lang;
2361 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2366 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2367 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2369 if ((user->handle_info->opserv_level < target->opserv_level)
2370 || ((user->handle_info->opserv_level == target->opserv_level)
2371 && (user->handle_info->opserv_level < 1000))) {
2372 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2375 if ((user->handle_info->opserv_level < new_level)
2376 || ((user->handle_info->opserv_level == new_level)
2377 && (user->handle_info->opserv_level < 1000))) {
2378 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2381 if (user->handle_info == target) {
2382 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2385 if (target->opserv_level == new_level)
2387 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2388 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2389 target->opserv_level = new_level;
2393 static OPTION_FUNC(opt_level)
2398 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2402 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2403 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2407 static OPTION_FUNC(opt_epithet)
2410 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2414 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2415 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2418 if ((epithet[0] == '*') && !epithet[1])
2421 hi->epithet = strdup(epithet);
2425 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2427 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2431 static OPTION_FUNC(opt_title)
2436 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2440 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2442 if (strchr(title, '.')) {
2443 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2447 if (!strcmp(title, "*")) {
2448 hi->fakehost = NULL;
2450 hi->fakehost = malloc(strlen(title)+2);
2451 hi->fakehost[0] = '.';
2452 strcpy(hi->fakehost+1, title);
2455 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2456 title = hi->fakehost + 1;
2460 title = user_find_message(user, "MSG_NONE");
2461 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2465 static OPTION_FUNC(opt_fakehost)
2470 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2474 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2476 if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) {
2477 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID");
2481 if (!strcmp(fake, "*"))
2482 hi->fakehost = NULL;
2484 hi->fakehost = strdup(fake);
2485 fake = hi->fakehost;
2488 fake = generate_fakehost(hi);
2490 fake = user_find_message(user, "MSG_NONE");
2491 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", fake);
2495 static NICKSERV_FUNC(cmd_reclaim)
2497 struct handle_info *hi;
2498 struct nick_info *ni;
2499 struct userNode *victim;
2501 NICKSERV_MIN_PARMS(2);
2502 hi = user->handle_info;
2503 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2505 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2508 if (ni->owner != user->handle_info) {
2509 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2512 victim = GetUserH(ni->nick);
2514 reply("MSG_NICK_UNKNOWN", ni->nick);
2517 if (victim == user) {
2518 reply("NSMSG_NICK_USER_YOU");
2521 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2522 switch (nickserv_conf.reclaim_action) {
2523 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2524 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2525 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2526 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2531 static NICKSERV_FUNC(cmd_unregnick)
2534 struct handle_info *hi;
2535 struct nick_info *ni;
2537 hi = user->handle_info;
2538 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2539 ni = dict_find(nickserv_nick_dict, nick, NULL);
2541 reply("NSMSG_UNKNOWN_NICK", nick);
2544 if (hi != ni->owner) {
2545 reply("NSMSG_NOT_YOUR_NICK", nick);
2548 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2553 static NICKSERV_FUNC(cmd_ounregnick)
2555 struct nick_info *ni;
2557 NICKSERV_MIN_PARMS(2);
2558 if (!(ni = get_nick_info(argv[1]))) {
2559 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2562 if (ni->owner->opserv_level >= user->handle_info->opserv_level) {
2563 reply("MSG_USER_OUTRANKED", ni->nick);
2566 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2571 static NICKSERV_FUNC(cmd_unregister)
2573 struct handle_info *hi;
2576 NICKSERV_MIN_PARMS(2);
2577 hi = user->handle_info;
2580 if (checkpass(passwd, hi->passwd)) {
2581 nickserv_unregister_handle(hi, user);
2584 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2585 reply("NSMSG_PASSWORD_INVALID");
2590 static NICKSERV_FUNC(cmd_ounregister)
2592 struct handle_info *hi;
2594 NICKSERV_MIN_PARMS(2);
2595 if (!(hi = get_victim_oper(user, argv[1])))
2597 nickserv_unregister_handle(hi, user);
2601 static NICKSERV_FUNC(cmd_status)
2603 if (nickserv_conf.disable_nicks) {
2604 reply("NSMSG_GLOBAL_STATS_NONICK",
2605 dict_size(nickserv_handle_dict));
2607 if (user->handle_info) {
2609 struct nick_info *ni;
2610 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2611 reply("NSMSG_HANDLE_STATS", cnt);
2613 reply("NSMSG_HANDLE_NONE");
2615 reply("NSMSG_GLOBAL_STATS",
2616 dict_size(nickserv_handle_dict),
2617 dict_size(nickserv_nick_dict));
2622 static NICKSERV_FUNC(cmd_ghost)
2624 struct userNode *target;
2625 char reason[MAXLEN];
2627 NICKSERV_MIN_PARMS(2);
2628 if (!(target = GetUserH(argv[1]))) {
2629 reply("MSG_NICK_UNKNOWN", argv[1]);
2632 if (target == user) {
2633 reply("NSMSG_CANNOT_GHOST_SELF");
2636 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2637 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2640 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2641 DelUser(target, nickserv, 1, reason);
2642 reply("NSMSG_GHOST_KILLED", argv[1]);
2646 static NICKSERV_FUNC(cmd_vacation)
2648 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2649 reply("NSMSG_ON_VACATION");
2654 nickserv_saxdb_write(struct saxdb_context *ctx) {
2656 struct handle_info *hi;
2659 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2661 #ifdef WITH_PROTOCOL_BAHAMUT
2664 saxdb_start_record(ctx, iter_key(it), 0);
2665 if (hi->announcements != '?') {
2666 flags[0] = hi->announcements;
2668 saxdb_write_string(ctx, KEY_ANNOUNCEMENTS, flags);
2671 struct handle_cookie *cookie = hi->cookie;
2674 switch (cookie->type) {
2675 case ACTIVATION: type = KEY_ACTIVATION; break;
2676 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2677 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2678 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2679 default: type = NULL; break;
2682 saxdb_start_record(ctx, KEY_COOKIE, 0);
2683 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2684 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2686 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2687 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2688 saxdb_end_record(ctx);
2692 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2694 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2696 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
2700 for (ii=flen=0; handle_flags[ii]; ++ii)
2701 if (hi->flags & (1 << ii))
2702 flags[flen++] = handle_flags[ii];
2704 saxdb_write_string(ctx, KEY_FLAGS, flags);
2706 #ifdef WITH_PROTOCOL_BAHAMUT
2707 saxdb_write_int(ctx, KEY_ID, hi->id);
2710 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2711 if (hi->last_quit_host[0])
2712 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2713 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2714 if (hi->masks->used)
2715 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2717 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2719 struct string_list *slist;
2720 struct nick_info *ni;
2722 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2723 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2724 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2728 if (hi->opserv_level)
2729 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2730 if (hi->language != lang_C)
2731 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2732 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2733 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2734 if (hi->screen_width)
2735 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2736 if (hi->table_width)
2737 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2738 flags[0] = hi->userlist_style;
2740 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2741 saxdb_end_record(ctx);
2746 static handle_merge_func_t *handle_merge_func_list;
2747 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2750 reg_handle_merge_func(handle_merge_func_t func)
2752 if (handle_merge_func_used == handle_merge_func_size) {
2753 if (handle_merge_func_size) {
2754 handle_merge_func_size <<= 1;
2755 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2757 handle_merge_func_size = 8;
2758 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2761 handle_merge_func_list[handle_merge_func_used++] = func;
2764 static NICKSERV_FUNC(cmd_merge)
2766 struct handle_info *hi_from, *hi_to;
2767 struct userNode *last_user;
2768 struct userData *cList, *cListNext;
2769 unsigned int ii, jj, n;
2770 char buffer[MAXLEN];
2772 NICKSERV_MIN_PARMS(3);
2774 if (!(hi_from = get_victim_oper(user, argv[1])))
2776 if (!(hi_to = get_victim_oper(user, argv[2])))
2778 if (hi_to == hi_from) {
2779 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2783 for (n=0; n<handle_merge_func_used; n++)
2784 handle_merge_func_list[n](user, hi_to, hi_from);
2786 /* Append "from" handle's nicks to "to" handle's nick list. */
2788 struct nick_info *last_ni;
2789 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
2790 last_ni->next = hi_from->nicks;
2792 while (hi_from->nicks) {
2793 hi_from->nicks->owner = hi_to;
2794 hi_from->nicks = hi_from->nicks->next;
2797 /* Merge the hostmasks. */
2798 for (ii=0; ii<hi_from->masks->used; ii++) {
2799 char *mask = hi_from->masks->list[ii];
2800 for (jj=0; jj<hi_to->masks->used; jj++)
2801 if (match_ircglobs(hi_to->masks->list[jj], mask))
2803 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
2804 string_list_append(hi_to->masks, strdup(mask));
2807 /* Merge the lists of authed users. */
2809 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
2810 last_user->next_authed = hi_from->users;
2812 hi_to->users = hi_from->users;
2814 /* Repoint the old "from" handle's users. */
2815 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
2816 last_user->handle_info = hi_to;
2818 hi_from->users = NULL;
2820 /* Merge channel userlists. */
2821 for (cList=hi_from->channels; cList; cList=cListNext) {
2822 struct userData *cList2;
2823 cListNext = cList->u_next;
2824 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
2825 if (cList->channel == cList2->channel)
2827 if (cList2 && (cList2->access >= cList->access)) {
2828 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);
2829 /* keep cList2 in hi_to; remove cList from hi_from */
2830 del_channel_user(cList, 1);
2833 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);
2834 /* remove the lower-ranking cList2 from hi_to */
2835 del_channel_user(cList2, 1);
2837 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
2839 /* cList needs to be moved from hi_from to hi_to */
2840 cList->handle = hi_to;
2841 /* Remove from linked list for hi_from */
2842 assert(!cList->u_prev);
2843 hi_from->channels = cList->u_next;
2845 cList->u_next->u_prev = cList->u_prev;
2846 /* Add to linked list for hi_to */
2847 cList->u_prev = NULL;
2848 cList->u_next = hi_to->channels;
2849 if (hi_to->channels)
2850 hi_to->channels->u_prev = cList;
2851 hi_to->channels = cList;
2855 /* Do they get an OpServ level promotion? */
2856 if (hi_from->opserv_level > hi_to->opserv_level)
2857 hi_to->opserv_level = hi_from->opserv_level;
2859 /* What about last seen time? */
2860 if (hi_from->lastseen > hi_to->lastseen)
2861 hi_to->lastseen = hi_from->lastseen;
2863 /* Notify of success. */
2864 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
2865 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
2866 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
2868 /* Unregister the "from" handle. */
2869 nickserv_unregister_handle(hi_from, NULL);
2874 struct nickserv_discrim {
2875 unsigned int limit, min_level, max_level;
2876 unsigned long flags_on, flags_off;
2877 time_t min_registered, max_registered;
2879 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
2880 const char *nickmask;
2881 const char *hostmask;
2882 const char *handlemask;
2883 const char *emailmask;
2886 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
2888 struct discrim_apply_info {
2889 struct nickserv_discrim *discrim;
2890 discrim_search_func func;
2891 struct userNode *source;
2892 unsigned int matched;
2895 static struct nickserv_discrim *
2896 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
2899 struct nickserv_discrim *discrim;
2901 discrim = malloc(sizeof(*discrim));
2902 memset(discrim, 0, sizeof(*discrim));
2903 discrim->min_level = 0;
2904 discrim->max_level = ~0;
2905 discrim->limit = 50;
2906 discrim->min_registered = 0;
2907 discrim->max_registered = INT_MAX;
2908 discrim->lastseen = now;
2910 for (i=0; i<argc; i++) {
2911 if (i == argc - 1) {
2912 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2915 if (!irccasecmp(argv[i], "limit")) {
2916 discrim->limit = strtoul(argv[++i], NULL, 0);
2917 } else if (!irccasecmp(argv[i], "flags")) {
2918 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
2919 } else if (!irccasecmp(argv[i], "registered")) {
2920 const char *cmp = argv[++i];
2921 if (cmp[0] == '<') {
2922 if (cmp[1] == '=') {
2923 discrim->min_registered = now - ParseInterval(cmp+2);
2925 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
2927 } else if (cmp[0] == '=') {
2928 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
2929 } else if (cmp[0] == '>') {
2930 if (cmp[1] == '=') {
2931 discrim->max_registered = now - ParseInterval(cmp+2);
2933 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
2936 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2938 } else if (!irccasecmp(argv[i], "seen")) {
2939 discrim->lastseen = now - ParseInterval(argv[++i]);
2940 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
2941 discrim->nickmask = argv[++i];
2942 } else if (!irccasecmp(argv[i], "hostmask")) {
2944 if (!irccasecmp(argv[i], "exact")) {
2945 if (i == argc - 1) {
2946 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2949 discrim->hostmask_type = EXACT;
2950 } else if (!irccasecmp(argv[i], "subset")) {
2951 if (i == argc - 1) {
2952 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2955 discrim->hostmask_type = SUBSET;
2956 } else if (!irccasecmp(argv[i], "superset")) {
2957 if (i == argc - 1) {
2958 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2961 discrim->hostmask_type = SUPERSET;
2962 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
2963 if (i == argc - 1) {
2964 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2967 discrim->hostmask_type = LASTQUIT;
2970 discrim->hostmask_type = SUPERSET;
2972 discrim->hostmask = argv[++i];
2973 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
2974 if (!irccasecmp(argv[++i], "*")) {
2975 discrim->handlemask = 0;
2977 discrim->handlemask = argv[i];
2979 } else if (!irccasecmp(argv[i], "email")) {
2980 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
2981 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
2983 } else if (!irccasecmp(argv[++i], "*")) {
2984 discrim->emailmask = 0;
2986 discrim->emailmask = argv[i];
2988 } else if (!irccasecmp(argv[i], "access")) {
2989 const char *cmp = argv[++i];
2990 if (cmp[0] == '<') {
2991 if (discrim->min_level == 0) discrim->min_level = 1;
2992 if (cmp[1] == '=') {
2993 discrim->max_level = strtoul(cmp+2, NULL, 0);
2995 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
2997 } else if (cmp[0] == '=') {
2998 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
2999 } else if (cmp[0] == '>') {
3000 if (cmp[1] == '=') {
3001 discrim->min_level = strtoul(cmp+2, NULL, 0);
3003 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3006 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3009 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3020 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3022 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3023 || (discrim->flags_off & hi->flags)
3024 || (discrim->min_registered > hi->registered)
3025 || (discrim->max_registered < hi->registered)
3026 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3027 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3028 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3029 || (discrim->min_level > hi->opserv_level)
3030 || (discrim->max_level < hi->opserv_level)) {
3033 if (discrim->hostmask) {
3035 for (i=0; i<hi->masks->used; i++) {
3036 const char *mask = hi->masks->list[i];
3037 if ((discrim->hostmask_type == SUBSET)
3038 && (match_ircglobs(discrim->hostmask, mask))) break;
3039 else if ((discrim->hostmask_type == EXACT)
3040 && !irccasecmp(discrim->hostmask, mask)) break;
3041 else if ((discrim->hostmask_type == SUPERSET)
3042 && (match_ircglobs(mask, discrim->hostmask))) break;
3043 else if ((discrim->hostmask_type == LASTQUIT)
3044 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3046 if (i==hi->masks->used) return 0;
3048 if (discrim->nickmask) {
3049 struct nick_info *nick = hi->nicks;
3051 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3054 if (!nick) return 0;
3060 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3062 dict_iterator_t it, next;
3063 unsigned int matched;
3065 for (it = dict_first(nickserv_handle_dict), matched = 0;
3066 it && (matched < discrim->limit);
3068 next = iter_next(it);
3069 if (nickserv_discrim_match(discrim, iter_data(it))) {
3070 dsf(source, iter_data(it));
3078 search_print_func(struct userNode *source, struct handle_info *match)
3080 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3084 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3089 search_unregister_func (struct userNode *source, struct handle_info *match)
3091 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3092 nickserv_unregister_handle(match, source);
3096 nickserv_sort_accounts_by_access(const void *a, const void *b)
3098 const struct handle_info *hi_a = *(const struct handle_info**)a;
3099 const struct handle_info *hi_b = *(const struct handle_info**)b;
3100 if (hi_a->opserv_level != hi_b->opserv_level)
3101 return hi_b->opserv_level - hi_a->opserv_level;
3102 return irccasecmp(hi_a->handle, hi_b->handle);
3106 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3108 struct handle_info_list hil;
3109 struct helpfile_table tbl;
3114 memset(&hil, 0, sizeof(hil));
3115 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3116 struct handle_info *hi = iter_data(it);
3117 if (hi->opserv_level)
3118 handle_info_list_append(&hil, hi);
3120 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3121 tbl.length = hil.used + 1;
3123 tbl.flags = TABLE_NO_FREE;
3124 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3125 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3128 for (ii = 0; ii < hil.used; ) {
3129 ary = malloc(tbl.width * sizeof(ary[0]));
3130 ary[0] = hil.list[ii]->handle;
3131 ary[1] = strtab(hil.list[ii]->opserv_level);
3132 tbl.contents[++ii] = ary;
3134 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3135 reply("MSG_MATCH_COUNT", hil.used);
3136 for (ii = 0; ii < hil.used; ii++)
3137 free(tbl.contents[ii]);
3142 static NICKSERV_FUNC(cmd_search)
3144 struct nickserv_discrim *discrim;
3145 discrim_search_func action;
3146 struct svccmd *subcmd;
3147 unsigned int matches;
3150 NICKSERV_MIN_PARMS(3);
3151 sprintf(buf, "search %s", argv[1]);
3152 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3153 if (!irccasecmp(argv[1], "print"))
3154 action = search_print_func;
3155 else if (!irccasecmp(argv[1], "count"))
3156 action = search_count_func;
3157 else if (!irccasecmp(argv[1], "unregister"))
3158 action = search_unregister_func;
3160 reply("NSMSG_INVALID_ACTION", argv[1]);
3164 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3167 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3171 if (action == search_print_func)
3172 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3173 else if (action == search_count_func)
3174 discrim->limit = INT_MAX;
3176 matches = nickserv_discrim_search(discrim, action, user);
3179 reply("MSG_MATCH_COUNT", matches);
3181 reply("MSG_NO_MATCHES");
3187 static MODCMD_FUNC(cmd_checkpass)
3189 struct handle_info *hi;
3191 NICKSERV_MIN_PARMS(3);
3192 if (!(hi = get_handle_info(argv[1]))) {
3193 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3196 if (checkpass(argv[2], hi->passwd))
3197 reply("CHECKPASS_YES");
3199 reply("CHECKPASS_NO");
3205 nickserv_db_read_handle(const char *handle, dict_t obj)
3208 struct string_list *masks, *slist;
3209 struct handle_info *hi;
3210 struct userNode *authed_users;
3211 unsigned long int id;
3215 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3216 id = str ? strtoul(str, NULL, 0) : 0;
3217 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3219 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3222 if ((hi = get_handle_info(handle))) {
3223 authed_users = hi->users;
3225 dict_remove(nickserv_handle_dict, hi->handle);
3227 authed_users = NULL;
3229 hi = register_handle(handle, str, id);
3231 hi->users = authed_users;
3232 while (authed_users) {
3233 authed_users->handle_info = hi;
3234 authed_users = authed_users->next_authed;
3237 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3238 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3239 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3240 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3241 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3242 hi->language = language_find(str ? str : "C");
3243 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3244 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3245 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3247 hi->infoline = strdup(str);
3248 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3249 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
3250 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3251 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
3252 /* We want to read the nicks even if disable_nicks is set. This is so
3253 * that we don't lose the nick data entirely. */
3254 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3256 for (ii=0; ii<slist->used; ii++)
3257 register_nick(slist->list[ii], hi);
3259 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3261 for (ii=0; str[ii]; ii++)
3262 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3264 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3265 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3266 str = database_get_data(obj, KEY_ANNOUNCEMENTS, RECDB_QSTRING);
3267 hi->announcements = str ? str[0] : '?';
3268 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3269 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3270 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3271 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3272 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3274 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3276 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3277 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3279 nickserv_set_email_addr(hi, str);
3280 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3282 hi->epithet = strdup(str);
3283 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3285 hi->fakehost = strdup(str);
3286 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3288 const char *data, *type, *expires, *cookie_str;
3289 struct handle_cookie *cookie;
3291 cookie = calloc(1, sizeof(*cookie));
3292 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3293 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3294 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3295 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3296 if (!type || !expires || !cookie_str) {
3297 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3300 if (!irccasecmp(type, KEY_ACTIVATION))
3301 cookie->type = ACTIVATION;
3302 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3303 cookie->type = PASSWORD_CHANGE;
3304 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3305 cookie->type = EMAIL_CHANGE;
3306 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3307 cookie->type = ALLOWAUTH;
3309 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3312 cookie->expires = strtoul(expires, NULL, 0);
3313 if (cookie->expires < now)
3316 cookie->data = strdup(data);
3317 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3321 nickserv_bake_cookie(cookie);
3323 nickserv_free_cookie(cookie);
3328 nickserv_saxdb_read(dict_t db) {
3330 struct record_data *rd;
3332 for (it=dict_first(db); it; it=iter_next(it)) {
3334 nickserv_db_read_handle(iter_key(it), rd->d.object);
3339 static NICKSERV_FUNC(cmd_mergedb)
3341 struct timeval start, stop;
3344 NICKSERV_MIN_PARMS(2);
3345 gettimeofday(&start, NULL);
3346 if (!(db = parse_database(argv[1]))) {
3347 reply("NSMSG_DB_UNREADABLE", argv[1]);
3350 nickserv_saxdb_read(db);
3352 gettimeofday(&stop, NULL);
3353 stop.tv_sec -= start.tv_sec;
3354 stop.tv_usec -= start.tv_usec;
3355 if (stop.tv_usec < 0) {
3357 stop.tv_usec += 1000000;
3359 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
3364 expire_handles(UNUSED_ARG(void *data))
3366 dict_iterator_t it, next;
3368 struct handle_info *hi;
3370 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3371 next = iter_next(it);
3373 if ((hi->opserv_level > 0)
3375 || HANDLE_FLAGGED(hi, FROZEN)
3376 || HANDLE_FLAGGED(hi, NODELETE)) {
3379 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3380 if ((now - hi->lastseen) > expiry) {
3381 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3382 nickserv_unregister_handle(hi, NULL);
3386 if (nickserv_conf.handle_expire_frequency)
3387 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3391 nickserv_load_dict(const char *fname)
3395 if (!(file = fopen(fname, "r"))) {
3396 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3399 while (!feof(file)) {
3400 fgets(line, sizeof(line), file);
3403 if (line[strlen(line)-1] == '\n')
3404 line[strlen(line)-1] = 0;
3405 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3408 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3411 static enum reclaim_action
3412 reclaim_action_from_string(const char *str) {
3414 return RECLAIM_NONE;
3415 else if (!irccasecmp(str, "warn"))
3416 return RECLAIM_WARN;
3417 else if (!irccasecmp(str, "svsnick"))
3418 return RECLAIM_SVSNICK;
3419 else if (!irccasecmp(str, "kill"))
3420 return RECLAIM_KILL;
3422 return RECLAIM_NONE;
3426 nickserv_conf_read(void)
3428 dict_t conf_node, child;
3432 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3433 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3436 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3438 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3439 if (nickserv_conf.valid_handle_regex_set)
3440 regfree(&nickserv_conf.valid_handle_regex);
3442 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3443 nickserv_conf.valid_handle_regex_set = !err;
3444 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3446 nickserv_conf.valid_handle_regex_set = 0;
3448 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3449 if (nickserv_conf.valid_nick_regex_set)
3450 regfree(&nickserv_conf.valid_nick_regex);
3452 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3453 nickserv_conf.valid_nick_regex_set = !err;
3454 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3456 nickserv_conf.valid_nick_regex_set = 0;
3458 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3460 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3461 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3462 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3463 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3464 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3465 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3466 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3467 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3468 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3469 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3470 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3471 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3472 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3473 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3474 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3475 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3476 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3477 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3478 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3479 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3480 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3481 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3482 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3483 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3484 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3486 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3487 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3488 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3490 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3491 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3492 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3494 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3495 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3496 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3497 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3498 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3499 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3500 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3501 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3502 if (!nickserv_conf.disable_nicks) {
3503 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3504 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3505 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3506 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3507 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3508 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3509 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3510 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3512 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3513 for (it=dict_first(child); it; it=iter_next(it)) {
3514 const char *key = iter_key(it), *value;
3518 if (!strncasecmp(key, "uc_", 3))
3519 flag = toupper(key[3]);
3520 else if (!strncasecmp(key, "lc_", 3))
3521 flag = tolower(key[3]);
3525 if ((pos = handle_inverse_flags[flag])) {
3526 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3527 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3530 if (nickserv_conf.weak_password_dict)
3531 dict_delete(nickserv_conf.weak_password_dict);
3532 nickserv_conf.weak_password_dict = dict_new();
3533 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3534 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3535 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3536 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3538 nickserv_load_dict(str);
3539 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3540 if (nickserv && str)
3541 NickChange(nickserv, str, 0);
3542 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3543 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3544 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3545 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3546 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3547 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3548 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3549 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3550 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3551 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3552 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3553 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3554 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3555 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3556 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3557 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3558 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
3559 nickserv_conf.titlehost_suffix = str ? str : "example.net";
3560 str = conf_get_data("server/network", RECDB_QSTRING);
3561 nickserv_conf.network_name = str ? str : "some IRC network";
3562 if (!nickserv_conf.auth_policer_params) {
3563 nickserv_conf.auth_policer_params = policer_params_new();
3564 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3565 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3567 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3568 for (it=dict_first(child); it; it=iter_next(it))
3569 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3573 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3575 char newnick[NICKLEN+1];
3584 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3586 case RECLAIM_SVSNICK:
3588 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3589 } while (GetUserH(newnick));
3590 irc_svsnick(nickserv, user, newnick);
3593 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
3594 irc_kill(nickserv, user, msg);
3600 nickserv_reclaim_p(void *data) {
3601 struct userNode *user = data;
3602 struct nick_info *ni = get_nick_info(user->nick);
3604 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3608 check_user_nick(struct userNode *user) {
3609 struct nick_info *ni;
3610 user->modes &= ~FLAGS_REGNICK;
3611 if (!(ni = get_nick_info(user->nick)))
3613 if (user->handle_info == ni->owner) {
3614 user->modes |= FLAGS_REGNICK;
3618 if (nickserv_conf.warn_nick_owned)
3619 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3620 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3622 if (nickserv_conf.auto_reclaim_delay)
3623 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3625 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3630 handle_new_user(struct userNode *user)
3632 return check_user_nick(user);
3636 handle_account(struct userNode *user, const char *stamp)
3638 struct handle_info *hi;
3640 #ifdef WITH_PROTOCOL_P10
3641 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3643 hi = dict_find(nickserv_id_dict, stamp, NULL);
3647 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3650 set_user_handle_info(user, hi, 0);
3652 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
3657 handle_nick_change(struct userNode *user, const char *old_nick)
3659 struct handle_info *hi;
3661 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
3662 dict_remove(nickserv_allow_auth_dict, old_nick);
3663 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
3665 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3666 check_user_nick(user);
3670 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
3672 dict_remove(nickserv_allow_auth_dict, user->nick);
3673 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3674 set_user_handle_info(user, NULL, 0);
3677 static struct modcmd *
3678 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
3680 if (min_level > 0) {
3682 sprintf(buf, "%u", min_level);
3683 if (must_be_qualified) {
3684 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
3686 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
3688 } else if (min_level == 0) {
3689 if (must_be_qualified) {
3690 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3692 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3695 if (must_be_qualified) {
3696 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
3698 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
3704 nickserv_db_cleanup(void)
3706 unreg_del_user_func(nickserv_remove_user);
3707 userList_clean(&curr_helpers);
3708 policer_params_delete(nickserv_conf.auth_policer_params);
3709 dict_delete(nickserv_handle_dict);
3710 dict_delete(nickserv_nick_dict);
3711 dict_delete(nickserv_opt_dict);
3712 dict_delete(nickserv_allow_auth_dict);
3713 dict_delete(nickserv_email_dict);
3714 dict_delete(nickserv_id_dict);
3715 dict_delete(nickserv_conf.weak_password_dict);
3716 free(auth_func_list);
3717 free(unreg_func_list);
3719 free(allowauth_func_list);
3720 free(handle_merge_func_list);
3721 free(failpw_func_list);
3722 if (nickserv_conf.valid_handle_regex_set)
3723 regfree(&nickserv_conf.valid_handle_regex);
3724 if (nickserv_conf.valid_nick_regex_set)
3725 regfree(&nickserv_conf.valid_nick_regex);
3729 init_nickserv(const char *nick)
3732 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
3733 reg_new_user_func(handle_new_user);
3734 reg_nick_change_func(handle_nick_change);
3735 reg_del_user_func(nickserv_remove_user);
3736 reg_account_func(handle_account);
3738 /* set up handle_inverse_flags */
3739 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
3740 for (i=0; handle_flags[i]; i++) {
3741 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
3742 flag_access_levels[i] = 0;
3745 conf_register_reload(nickserv_conf_read);
3746 nickserv_opt_dict = dict_new();
3747 nickserv_email_dict = dict_new();
3748 dict_set_free_keys(nickserv_email_dict, free);
3749 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
3751 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
3752 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
3753 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
3754 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
3755 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
3756 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
3757 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
3758 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
3759 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
3760 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
3761 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
3762 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
3763 nickserv_define_func("SET", cmd_set, -1, 1, 0);
3764 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
3765 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
3766 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
3767 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
3768 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
3769 nickserv_define_func("MERGE", cmd_merge, 0, 1, 0);
3770 if (!nickserv_conf.disable_nicks) {
3771 /* nick management commands */
3772 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
3773 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
3774 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
3775 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
3776 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
3777 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
3779 if (nickserv_conf.email_enabled) {
3780 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
3781 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
3782 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
3783 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
3784 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
3786 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
3787 /* miscellaneous commands */
3788 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
3789 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
3790 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
3791 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
3792 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
3794 dict_insert(nickserv_opt_dict, "INFO", opt_info);
3795 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
3796 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
3797 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
3798 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
3799 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
3800 dict_insert(nickserv_opt_dict, "PASS", opt_password);
3801 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
3802 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
3803 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
3804 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
3805 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
3806 if (nickserv_conf.titlehost_suffix) {
3807 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
3808 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
3810 dict_insert(nickserv_opt_dict, "ANNOUNCEMENTS", opt_announcements);
3811 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
3812 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
3814 nickserv_handle_dict = dict_new();
3815 dict_set_free_keys(nickserv_handle_dict, free);
3816 dict_set_free_data(nickserv_handle_dict, free_handle_info);
3818 nickserv_id_dict = dict_new();
3819 dict_set_free_keys(nickserv_id_dict, free);
3821 nickserv_nick_dict = dict_new();
3822 dict_set_free_data(nickserv_nick_dict, free_nick_info);
3824 nickserv_allow_auth_dict = dict_new();
3826 userList_init(&curr_helpers);
3829 nickserv = AddService(nick, "Nick Services", NULL);
3830 nickserv_service = service_register(nickserv);
3832 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
3833 reg_exit_func(nickserv_db_cleanup);
3834 if(nickserv_conf.handle_expire_frequency)
3835 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3836 message_register_table(msgtab);