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_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." },
183 { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
184 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
185 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
186 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
187 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
188 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
189 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
190 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
191 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
192 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
193 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
194 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
195 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
196 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
197 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
198 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
199 { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" },
200 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
201 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
202 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
203 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
204 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
205 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
206 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
207 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
208 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
209 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
210 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
211 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
212 { "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)." },
213 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
214 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
215 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
216 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
217 { "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." },
218 { "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." },
219 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
220 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
221 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
222 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
223 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
224 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
225 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
226 { "NSMSG_PASS_SUCCESS", "Password changed." },
227 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
228 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
229 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
230 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
231 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
232 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
233 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
234 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
235 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
236 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
237 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
238 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
239 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
240 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
241 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
242 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
243 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
244 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
245 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
246 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
247 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
248 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
249 { "NSMSG_NO_ACCESS", "Access denied." },
250 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
251 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
252 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
253 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
254 { "NSMSG_DB_MERGED", "$N merged DB from %s (in "FMT_TIME_T".%03lu seconds)." },
255 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
256 { "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." },
257 { "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." },
258 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
259 { "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." },
260 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
261 { "NSMSG_SEARCH_MATCH", "Match: %s" },
262 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
263 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
264 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
265 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
266 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
267 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
268 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
269 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
270 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
271 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
272 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
273 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
274 { "NSMSG_INVALID_ANNOUNCE", "$b%s$b is an invalid announcements value." },
275 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
276 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
277 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
278 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
279 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
280 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
281 { "NSMSG_SET_ANNOUNCEMENTS", "$bANNOUNCEMENTS: $b%s" },
282 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
283 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
284 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
285 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
286 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
287 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
288 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
289 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
290 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
291 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
292 { "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." },
293 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
294 { "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." },
295 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
296 { "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." },
297 { "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." },
298 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
299 { "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." },
300 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
301 { "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." },
302 { "CHECKPASS_YES", "Yes." },
303 { "CHECKPASS_NO", "No." },
307 enum reclaim_action {
313 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
314 static void nickserv_reclaim_p(void *data);
317 unsigned int disable_nicks : 1;
318 unsigned int valid_handle_regex_set : 1;
319 unsigned int valid_nick_regex_set : 1;
320 unsigned int autogag_enabled : 1;
321 unsigned int email_enabled : 1;
322 unsigned int email_required : 1;
323 unsigned int default_hostmask : 1;
324 unsigned int warn_nick_owned : 1;
325 unsigned int warn_clone_auth : 1;
326 unsigned long nicks_per_handle;
327 unsigned long password_min_length;
328 unsigned long password_min_digits;
329 unsigned long password_min_upper;
330 unsigned long password_min_lower;
331 unsigned long db_backup_frequency;
332 unsigned long handle_expire_frequency;
333 unsigned long autogag_duration;
334 unsigned long email_visible_level;
335 unsigned long cookie_timeout;
336 unsigned long handle_expire_delay;
337 unsigned long nochan_handle_expire_delay;
338 unsigned long modoper_level;
339 unsigned long set_epithet_level;
340 unsigned long set_title_level;
341 unsigned long set_fakehost_level;
342 unsigned long handles_per_email;
343 unsigned long email_search_level;
344 const char *network_name;
345 const char *titlehost_suffix;
346 regex_t valid_handle_regex;
347 regex_t valid_nick_regex;
348 dict_t weak_password_dict;
349 struct policer_params *auth_policer_params;
350 enum reclaim_action reclaim_action;
351 enum reclaim_action auto_reclaim_action;
352 unsigned long auto_reclaim_delay;
353 unsigned char default_maxlogins;
354 unsigned char hard_maxlogins;
357 /* We have 2^32 unique account IDs to use. */
358 unsigned long int highest_id = 0;
361 canonicalize_hostmask(char *mask)
363 char *out = mask, *temp;
364 if ((temp = strchr(mask, '!'))) {
366 while (*temp) *out++ = *temp++;
372 static struct handle_info *
373 register_handle(const char *handle, const char *passwd, UNUSED_ARG(unsigned long id))
375 struct handle_info *hi;
377 #ifdef WITH_PROTOCOL_BAHAMUT
378 char id_base64[IDLEN + 1];
381 /* Assign a unique account ID to the account; note that 0 is
382 an invalid account ID. 1 is therefore the first account ID. */
384 id = 1 + highest_id++;
386 /* Note: highest_id is and must always be the highest ID. */
387 if(id > highest_id) {
391 inttobase64(id_base64, id, IDLEN);
393 /* Make sure an account with the same ID doesn't exist. If a
394 duplicate is found, log some details and assign a new one.
395 This should be impossible, but it never hurts to expect it. */
396 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
397 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
403 hi = calloc(1, sizeof(*hi));
404 hi->userlist_style = HI_DEFAULT_STYLE;
405 hi->announcements = '?';
406 hi->handle = strdup(handle);
407 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
409 dict_insert(nickserv_handle_dict, hi->handle, hi);
411 #ifdef WITH_PROTOCOL_BAHAMUT
413 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
420 register_nick(const char *nick, struct handle_info *owner)
422 struct nick_info *ni;
423 ni = malloc(sizeof(struct nick_info));
424 safestrncpy(ni->nick, nick, sizeof(ni->nick));
426 ni->next = owner->nicks;
428 dict_insert(nickserv_nick_dict, ni->nick, ni);
432 free_nick_info(void *vni)
434 struct nick_info *ni = vni;
439 delete_nick(struct nick_info *ni)
441 struct nick_info *last, *next;
442 struct userNode *user;
443 /* Check to see if we should mark a user as unregistered. */
444 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
445 user->modes &= ~FLAGS_REGNICK;
448 /* Remove ni from the nick_info linked list. */
449 if (ni == ni->owner->nicks) {
450 ni->owner->nicks = ni->next;
452 last = ni->owner->nicks;
458 last->next = next->next;
460 dict_remove(nickserv_nick_dict, ni->nick);
463 static unreg_func_t *unreg_func_list;
464 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
467 reg_unreg_func(unreg_func_t func)
469 if (unreg_func_used == unreg_func_size) {
470 if (unreg_func_size) {
471 unreg_func_size <<= 1;
472 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
475 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
478 unreg_func_list[unreg_func_used++] = func;
482 nickserv_free_cookie(void *data)
484 struct handle_cookie *cookie = data;
485 if (cookie->hi) cookie->hi->cookie = NULL;
486 if (cookie->data) free(cookie->data);
491 free_handle_info(void *vhi)
493 struct handle_info *hi = vhi;
495 #ifdef WITH_PROTOCOL_BAHAMUT
498 inttobase64(id, hi->id, IDLEN);
499 dict_remove(nickserv_id_dict, id);
502 free_string_list(hi->masks);
506 delete_nick(hi->nicks);
511 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
512 nickserv_free_cookie(hi->cookie);
514 if (hi->email_addr) {
515 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
516 handle_info_list_remove(hil, hi);
518 dict_remove(nickserv_email_dict, hi->email_addr);
523 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
526 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
530 for (n=0; n<unreg_func_used; n++)
531 unreg_func_list[n](notify, hi);
533 set_user_handle_info(hi->users, NULL, 0);
535 if (nickserv_conf.disable_nicks)
536 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
538 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
540 dict_remove(nickserv_handle_dict, hi->handle);
544 get_handle_info(const char *handle)
546 return dict_find(nickserv_handle_dict, handle, 0);
550 get_nick_info(const char *nick)
552 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
556 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
561 for (nn=0; nn<channel->members.used; ++nn) {
562 mn = channel->members.list[nn];
563 if ((mn->user != except) && (mn->user->handle_info == handle))
570 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
571 if (!user->handle_info) {
573 send_message(user, bot, "MSG_AUTHENTICATE");
577 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
579 send_message(user, bot, "NSMSG_NO_ACCESS");
583 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
585 send_message(user, bot, "MSG_OPER_SUSPENDED");
589 if (user->handle_info->opserv_level < min_level) {
591 send_message(user, bot, "NSMSG_NO_ACCESS");
599 is_valid_handle(const char *handle)
601 struct userNode *user;
602 /* cant register a juped nick/service nick as handle, to prevent confusion */
603 user = GetUserH(handle);
604 if (user && IsLocal(user))
606 /* check against maximum length */
607 if (strlen(handle) > NICKSERV_HANDLE_LEN)
609 /* for consistency, only allow account names that could be nicks */
610 if (!is_valid_nick(handle))
612 /* disallow account names that look like bad words */
613 if (opserv_bad_channel(handle))
615 /* test either regex or containing all valid chars */
616 if (nickserv_conf.valid_handle_regex_set) {
617 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
620 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
621 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
625 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
630 is_registerable_nick(const char *nick)
632 /* make sure it could be used as an account name */
633 if (!is_valid_handle(nick))
636 if (strlen(nick) > NICKLEN)
638 /* test either regex or as valid handle */
639 if (nickserv_conf.valid_nick_regex_set) {
640 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
643 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
644 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
652 is_valid_email_addr(const char *email)
654 return strchr(email, '@') != NULL;
658 visible_email_addr(struct userNode *user, struct handle_info *hi)
660 if (hi->email_addr) {
661 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
662 return hi->email_addr;
672 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
674 struct handle_info *hi;
675 struct userNode *target;
679 if (!(hi = get_handle_info(++name))) {
680 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
685 if (!(target = GetUserH(name))) {
686 send_message(user, service, "MSG_NICK_UNKNOWN", name);
689 if (IsLocal(target)) {
690 if (IsService(target))
691 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
693 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
696 if (!(hi = target->handle_info)) {
697 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
705 oper_outranks(struct userNode *user, struct handle_info *hi) {
706 if (user->handle_info->opserv_level > hi->opserv_level)
708 if (user->handle_info->opserv_level == hi->opserv_level) {
709 if ((user->handle_info->opserv_level == 1000)
710 || (user->handle_info == hi)
711 || ((user->handle_info->opserv_level == 0)
712 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
713 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
717 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
721 static struct handle_info *
722 get_victim_oper(struct userNode *user, const char *target)
724 struct handle_info *hi;
725 if (!(hi = smart_get_handle_info(nickserv, user, target)))
727 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
728 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
731 return oper_outranks(user, hi) ? hi : NULL;
735 valid_user_for(struct userNode *user, struct handle_info *hi)
739 /* If no hostmasks on the account, allow it. */
740 if (!hi->masks->used)
742 /* If any hostmask matches, allow it. */
743 for (ii=0; ii<hi->masks->used; ii++)
744 if (user_matches_glob(user, hi->masks->list[ii], 0))
746 /* If they are allowauthed to this account, allow it (removing the aa). */
747 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
748 dict_remove(nickserv_allow_auth_dict, user->nick);
751 /* The user is not allowed to use this account. */
756 is_secure_password(const char *handle, const char *pass, struct userNode *user)
759 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
761 if (len < nickserv_conf.password_min_length) {
763 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
766 if (!irccasecmp(pass, handle)) {
768 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
771 dict_find(nickserv_conf.weak_password_dict, pass, &i);
774 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
777 for (i=0; i<len; i++) {
778 if (isdigit(pass[i]))
780 if (isupper(pass[i]))
782 if (islower(pass[i]))
785 if ((cnt_lower < nickserv_conf.password_min_lower)
786 || (cnt_upper < nickserv_conf.password_min_upper)
787 || (cnt_digits < nickserv_conf.password_min_digits)) {
789 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
795 static auth_func_t *auth_func_list;
796 static unsigned int auth_func_size = 0, auth_func_used = 0;
799 reg_auth_func(auth_func_t func)
801 if (auth_func_used == auth_func_size) {
802 if (auth_func_size) {
803 auth_func_size <<= 1;
804 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
807 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
810 auth_func_list[auth_func_used++] = func;
813 static handle_rename_func_t *rf_list;
814 static unsigned int rf_list_size, rf_list_used;
817 reg_handle_rename_func(handle_rename_func_t func)
819 if (rf_list_used == rf_list_size) {
822 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
825 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
828 rf_list[rf_list_used++] = func;
832 generate_fakehost(struct handle_info *handle)
834 extern const char *hidden_host_suffix;
835 static char buffer[HOSTLEN+1];
837 if (!handle->fakehost) {
838 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
840 } else if (handle->fakehost[0] == '.') {
841 /* A leading dot indicates the stored value is actually a title. */
842 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, nickserv_conf.titlehost_suffix);
845 return handle->fakehost;
849 apply_fakehost(struct handle_info *handle)
851 struct userNode *target;
856 fake = generate_fakehost(handle);
857 for (target = handle->users; target; target = target->next_authed)
858 assign_fakehost(target, fake, 1);
862 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
865 struct handle_info *old_info;
867 /* This can happen if somebody uses COOKIE while authed, or if
868 * they re-auth to their current handle (which is silly, but users
870 if (user->handle_info == hi)
873 if (user->handle_info) {
874 struct userNode *other;
877 userList_remove(&curr_helpers, user);
879 /* remove from next_authed linked list */
880 if (user->handle_info->users == user) {
881 user->handle_info->users = user->next_authed;
883 for (other = user->handle_info->users;
884 other->next_authed != user;
885 other = other->next_authed) ;
886 other->next_authed = user->next_authed;
888 /* if nobody left on old handle, and they're not an oper, remove !god */
889 if (!user->handle_info->users && !user->handle_info->opserv_level)
890 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
891 /* record them as being last seen at this time */
892 user->handle_info->lastseen = now;
893 /* and record their hostmask */
894 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
896 old_info = user->handle_info;
897 user->handle_info = hi;
898 if (hi && !hi->users && !hi->opserv_level)
899 HANDLE_CLEAR_FLAG(hi, HELPING);
900 for (n=0; n<auth_func_used; n++)
901 auth_func_list[n](user, old_info);
903 struct nick_info *ni;
905 HANDLE_CLEAR_FLAG(hi, FROZEN);
906 if (nickserv_conf.warn_clone_auth) {
907 struct userNode *other;
908 for (other = hi->users; other; other = other->next_authed)
909 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
911 user->next_authed = hi->users;
915 userList_append(&curr_helpers, user);
917 if (hi->fakehost || old_info)
921 #ifdef WITH_PROTOCOL_BAHAMUT
922 /* Stamp users with their account ID. */
924 inttobase64(id, hi->id, IDLEN);
925 #elif WITH_PROTOCOL_P10
926 /* Stamp users with their account name. */
927 char *id = hi->handle;
929 const char *id = "???";
931 if (!nickserv_conf.disable_nicks) {
932 struct nick_info *ni;
933 for (ni = hi->nicks; ni; ni = ni->next) {
934 if (!irccasecmp(user->nick, ni->nick)) {
935 user->modes |= FLAGS_REGNICK;
943 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
944 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
946 /* We cannot clear the user's account ID, unfortunately. */
947 user->next_authed = NULL;
951 static struct handle_info*
952 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
954 struct handle_info *hi;
955 struct nick_info *ni;
956 char crypted[MD5_CRYPT_LENGTH];
958 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
959 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
963 if (!is_secure_password(handle, passwd, user))
966 cryptpass(passwd, crypted);
967 hi = register_handle(handle, crypted, 0);
968 hi->masks = alloc_string_list(1);
970 hi->language = lang_C;
971 hi->registered = now;
973 hi->flags = HI_DEFAULT_FLAGS;
974 if (settee && !no_auth)
975 set_user_handle_info(settee, hi, 1);
978 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
979 else if (nickserv_conf.disable_nicks)
980 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
981 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
982 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
984 register_nick(user->nick, hi);
985 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
987 if (settee && (user != settee))
988 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
993 nickserv_bake_cookie(struct handle_cookie *cookie)
995 cookie->hi->cookie = cookie;
996 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
1000 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
1002 struct handle_cookie *cookie;
1003 char subject[128], body[4096], *misc;
1004 const char *netname, *fmt;
1008 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1012 cookie = calloc(1, sizeof(*cookie));
1014 cookie->type = type;
1015 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1016 cookie->expires = now + nickserv_conf.cookie_timeout;
1017 inttobase64(cookie->cookie, rand(), 5);
1018 inttobase64(cookie->cookie+5, rand(), 5);
1020 netname = nickserv_conf.network_name;
1023 switch (cookie->type) {
1025 hi->passwd[0] = 0; /* invalidate password */
1026 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1027 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1028 snprintf(subject, sizeof(subject), fmt, netname);
1029 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1030 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1033 case PASSWORD_CHANGE:
1034 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1035 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1036 snprintf(subject, sizeof(subject), fmt, netname);
1037 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1038 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1041 misc = hi->email_addr;
1042 hi->email_addr = cookie->data;
1044 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1045 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1046 snprintf(subject, sizeof(subject), fmt, netname);
1047 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1048 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1049 sendmail(nickserv, hi, subject, body, 1);
1050 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1051 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1053 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1054 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1055 snprintf(subject, sizeof(subject), fmt, netname);
1056 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1057 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1058 sendmail(nickserv, hi, subject, body, 1);
1061 hi->email_addr = misc;
1064 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1065 snprintf(subject, sizeof(subject), fmt, netname);
1066 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1067 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1068 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1071 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1075 sendmail(nickserv, hi, subject, body, first_time);
1076 nickserv_bake_cookie(cookie);
1080 nickserv_eat_cookie(struct handle_cookie *cookie)
1082 cookie->hi->cookie = NULL;
1083 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1084 nickserv_free_cookie(cookie);
1088 nickserv_free_email_addr(void *data)
1090 handle_info_list_clean(data);
1095 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1097 struct handle_info_list *hil;
1098 /* Remove from old handle_info_list ... */
1099 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1100 handle_info_list_remove(hil, hi);
1101 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1102 hi->email_addr = NULL;
1104 /* Add to the new list.. */
1105 if (new_email_addr) {
1106 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1107 hil = calloc(1, sizeof(*hil));
1108 hil->tag = strdup(new_email_addr);
1109 handle_info_list_init(hil);
1110 dict_insert(nickserv_email_dict, hil->tag, hil);
1112 handle_info_list_append(hil, hi);
1113 hi->email_addr = hil->tag;
1117 static NICKSERV_FUNC(cmd_register)
1119 struct handle_info *hi;
1120 const char *email_addr, *password;
1123 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1124 /* Require the first handle registered to belong to someone +o. */
1125 reply("NSMSG_REQUIRE_OPER");
1129 if (user->handle_info) {
1130 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1134 if (IsRegistering(user)) {
1135 reply("NSMSG_ALREADY_REGISTERING");
1139 if (IsStamped(user)) {
1140 /* Unauthenticated users might still have been stamped
1141 previously and could therefore have a hidden host;
1142 do not allow them to register a new account. */
1143 reply("NSMSG_STAMPED_REGISTER");
1147 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1149 if (!is_valid_handle(argv[1])) {
1150 reply("NSMSG_BAD_HANDLE", argv[1]);
1154 if ((argc >= 4) && nickserv_conf.email_enabled) {
1155 struct handle_info_list *hil;
1158 /* Remember email address. */
1159 email_addr = argv[3];
1161 /* Check that the email address looks valid.. */
1162 if (!is_valid_email_addr(email_addr)) {
1163 reply("NSMSG_BAD_EMAIL_ADDR");
1167 /* .. and that we are allowed to send to it. */
1168 if ((str = sendmail_prohibited_address(email_addr))) {
1169 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1173 /* If we do email verify, make sure we don't spam the address. */
1174 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1176 for (nn=0; nn<hil->used; nn++) {
1177 if (hil->list[nn]->cookie) {
1178 reply("NSMSG_EMAIL_UNACTIVATED");
1182 if (hil->used >= nickserv_conf.handles_per_email) {
1183 reply("NSMSG_EMAIL_OVERUSED");
1196 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1198 /* Add any masks they should get. */
1199 if (nickserv_conf.default_hostmask) {
1200 string_list_append(hi->masks, strdup("*@*"));
1202 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1203 if (user->ip.s_addr && user->hostname[strspn(user->hostname, "0123456789.")])
1204 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1207 /* If they're the first to register, give them level 1000. */
1208 if (dict_size(nickserv_handle_dict) == 1) {
1209 hi->opserv_level = 1000;
1210 reply("NSMSG_ROOT_HANDLE", argv[1]);
1213 /* Set their email address. */
1215 nickserv_set_email_addr(hi, email_addr);
1217 /* If they need to do email verification, tell them. */
1219 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1221 /* Set registering flag.. */
1222 user->modes |= FLAGS_REGISTERING;
1227 static NICKSERV_FUNC(cmd_oregister)
1230 struct userNode *settee;
1231 struct handle_info *hi;
1233 NICKSERV_MIN_PARMS(4);
1235 if (!is_valid_handle(argv[1])) {
1236 reply("NSMSG_BAD_HANDLE", argv[1]);
1240 if (strchr(argv[3], '@')) {
1241 mask = canonicalize_hostmask(strdup(argv[3]));
1243 settee = GetUserH(argv[4]);
1245 reply("MSG_NICK_UNKNOWN", argv[4]);
1252 } else if ((settee = GetUserH(argv[3]))) {
1253 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1255 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1258 if (settee && settee->handle_info) {
1259 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1263 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1267 string_list_append(hi->masks, mask);
1271 static NICKSERV_FUNC(cmd_handleinfo)
1274 unsigned int i, pos=0, herelen;
1275 struct userNode *target, *next_un;
1276 struct handle_info *hi;
1277 const char *nsmsg_none;
1280 if (!(hi = user->handle_info)) {
1281 reply("NSMSG_MUST_AUTH");
1284 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1288 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1289 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1290 #ifdef WITH_PROTOCOL_BAHAMUT
1291 reply("NSMSG_HANDLEINFO_ID", hi->id);
1293 reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi->registered));
1296 intervalString(buff, now - hi->lastseen, user->handle_info);
1297 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1299 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1302 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1303 if (HANDLE_FLAGGED(hi, FROZEN))
1304 reply("NSMSG_HANDLEINFO_VACATION");
1306 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1307 struct do_not_register *dnr;
1308 if ((dnr = chanserv_is_dnr(NULL, hi)))
1309 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1310 if (!oper_outranks(user, hi))
1312 } else if (hi != user->handle_info)
1315 if (nickserv_conf.email_enabled)
1316 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1320 switch (hi->cookie->type) {
1321 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1322 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1323 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1324 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1325 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1331 unsigned long flen = 1;
1332 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1334 for (i=0, flen=1; handle_flags[i]; i++)
1335 if (hi->flags & 1 << i)
1336 flags[flen++] = handle_flags[i];
1338 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1340 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1343 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1344 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1345 || (hi->opserv_level > 0)) {
1346 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1350 reply("NSMSG_HANDLEINFO_FAKEHOST", (hi->fakehost ? hi->fakehost : handle_find_message(hi, "MSG_NONE")));
1352 if (hi->last_quit_host[0])
1353 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1355 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1357 if (nickserv_conf.disable_nicks) {
1358 /* nicks disabled; don't show anything about registered nicks */
1359 } else if (hi->nicks) {
1360 struct nick_info *ni, *next_ni;
1361 for (ni = hi->nicks; ni; ni = next_ni) {
1362 herelen = strlen(ni->nick);
1363 if (pos + herelen + 1 > ArrayLength(buff)) {
1365 goto print_nicks_buff;
1369 memcpy(buff+pos, ni->nick, herelen);
1370 pos += herelen; buff[pos++] = ' ';
1374 reply("NSMSG_HANDLEINFO_NICKS", buff);
1379 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1382 if (hi->masks->used) {
1383 for (i=0; i < hi->masks->used; i++) {
1384 herelen = strlen(hi->masks->list[i]);
1385 if (pos + herelen + 1 > ArrayLength(buff)) {
1387 goto print_mask_buff;
1389 memcpy(buff+pos, hi->masks->list[i], herelen);
1390 pos += herelen; buff[pos++] = ' ';
1391 if (i+1 == hi->masks->used) {
1394 reply("NSMSG_HANDLEINFO_MASKS", buff);
1399 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1403 struct userData *channel, *next;
1406 for (channel = hi->channels; channel; channel = next) {
1407 next = channel->u_next;
1408 name = channel->channel->channel->name;
1409 herelen = strlen(name);
1410 if (pos + herelen + 7 > ArrayLength(buff)) {
1412 goto print_chans_buff;
1414 if (IsUserSuspended(channel))
1416 pos += sprintf(buff+pos, "%d:%s ", channel->access, name);
1420 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1425 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1428 for (target = hi->users; target; target = next_un) {
1429 herelen = strlen(target->nick);
1430 if (pos + herelen + 1 > ArrayLength(buff)) {
1432 goto print_cnick_buff;
1434 next_un = target->next_authed;
1436 memcpy(buff+pos, target->nick, herelen);
1437 pos += herelen; buff[pos++] = ' ';
1441 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1449 static NICKSERV_FUNC(cmd_userinfo)
1451 struct userNode *target;
1453 NICKSERV_MIN_PARMS(2);
1454 if (!(target = GetUserH(argv[1]))) {
1455 reply("MSG_NICK_UNKNOWN", argv[1]);
1458 if (target->handle_info)
1459 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1461 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1465 static NICKSERV_FUNC(cmd_nickinfo)
1467 struct nick_info *ni;
1469 NICKSERV_MIN_PARMS(2);
1470 if (!(ni = get_nick_info(argv[1]))) {
1471 reply("MSG_NICK_UNKNOWN", argv[1]);
1474 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1478 static NICKSERV_FUNC(cmd_rename_handle)
1480 struct handle_info *hi;
1481 char msgbuf[MAXLEN], *old_handle;
1484 NICKSERV_MIN_PARMS(3);
1485 if (!(hi = get_victim_oper(user, argv[1])))
1487 if (!is_valid_handle(argv[2])) {
1488 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1491 if (get_handle_info(argv[2])) {
1492 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1496 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1497 hi->handle = strdup(argv[2]);
1498 dict_insert(nickserv_handle_dict, hi->handle, hi);
1499 for (nn=0; nn<rf_list_used; nn++)
1500 rf_list[nn](hi, old_handle);
1501 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1502 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1503 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1508 static failpw_func_t *failpw_func_list;
1509 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1512 reg_failpw_func(failpw_func_t func)
1514 if (failpw_func_used == failpw_func_size) {
1515 if (failpw_func_size) {
1516 failpw_func_size <<= 1;
1517 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1519 failpw_func_size = 8;
1520 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1523 failpw_func_list[failpw_func_used++] = func;
1526 static NICKSERV_FUNC(cmd_auth)
1528 int pw_arg, used, maxlogins;
1529 struct handle_info *hi;
1531 struct userNode *other;
1533 if (user->handle_info) {
1534 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1537 if (IsStamped(user)) {
1538 /* Unauthenticated users might still have been stamped
1539 previously and could therefore have a hidden host;
1540 do not allow them to authenticate. */
1541 reply("NSMSG_STAMPED_AUTH");
1545 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1547 } else if (argc == 2) {
1548 if (nickserv_conf.disable_nicks) {
1549 if (!(hi = get_handle_info(user->nick))) {
1550 reply("NSMSG_HANDLE_NOT_FOUND");
1554 /* try to look up their handle from their nick */
1555 struct nick_info *ni;
1556 ni = get_nick_info(user->nick);
1558 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1565 reply("MSG_MISSING_PARAMS", argv[0]);
1566 svccmd_send_help(user, nickserv, cmd);
1570 reply("NSMSG_HANDLE_NOT_FOUND");
1573 /* Responses from here on look up the language used by the handle they asked about. */
1574 passwd = argv[pw_arg];
1575 if (!valid_user_for(user, hi)) {
1576 if (hi->email_addr && nickserv_conf.email_enabled)
1577 send_message_type(4, user, cmd->parent->bot,
1578 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1581 send_message_type(4, user, cmd->parent->bot,
1582 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1584 argv[pw_arg] = "BADMASK";
1587 if (!checkpass(passwd, hi->passwd)) {
1589 send_message_type(4, user, cmd->parent->bot,
1590 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1591 argv[pw_arg] = "BADPASS";
1592 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1593 if (nickserv_conf.autogag_enabled) {
1594 if (!user->auth_policer.params) {
1595 user->auth_policer.last_req = now;
1596 user->auth_policer.params = nickserv_conf.auth_policer_params;
1598 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1600 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1601 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1602 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1604 argv[pw_arg] = "GAGGED";
1609 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1610 send_message_type(4, user, cmd->parent->bot,
1611 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1612 argv[pw_arg] = "SUSPENDED";
1615 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1616 for (used = 0, other = hi->users; other; other = other->next_authed) {
1617 if (++used >= maxlogins) {
1618 send_message_type(4, user, cmd->parent->bot,
1619 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1621 argv[pw_arg] = "MAXLOGINS";
1626 set_user_handle_info(user, hi, 1);
1627 if (nickserv_conf.email_required && !hi->email_addr)
1628 reply("NSMSG_PLEASE_SET_EMAIL");
1629 if (!is_secure_password(hi->handle, passwd, NULL))
1630 reply("NSMSG_WEAK_PASSWORD");
1631 if (hi->passwd[0] != '$')
1632 cryptpass(passwd, hi->passwd);
1633 reply("NSMSG_AUTH_SUCCESS");
1634 argv[pw_arg] = "****";
1638 static allowauth_func_t *allowauth_func_list;
1639 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1642 reg_allowauth_func(allowauth_func_t func)
1644 if (allowauth_func_used == allowauth_func_size) {
1645 if (allowauth_func_size) {
1646 allowauth_func_size <<= 1;
1647 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1649 allowauth_func_size = 8;
1650 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1653 allowauth_func_list[allowauth_func_used++] = func;
1656 static NICKSERV_FUNC(cmd_allowauth)
1658 struct userNode *target;
1659 struct handle_info *hi;
1662 NICKSERV_MIN_PARMS(2);
1663 if (!(target = GetUserH(argv[1]))) {
1664 reply("MSG_NICK_UNKNOWN", argv[1]);
1667 if (target->handle_info) {
1668 reply("NSMSG_USER_PREV_AUTH", target->nick);
1671 if (IsStamped(target)) {
1672 /* Unauthenticated users might still have been stamped
1673 previously and could therefore have a hidden host;
1674 do not allow them to authenticate to an account. */
1675 reply("NSMSG_USER_PREV_STAMP", target->nick);
1680 else if (!(hi = get_handle_info(argv[2]))) {
1681 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1685 if (hi->opserv_level > user->handle_info->opserv_level) {
1686 reply("MSG_USER_OUTRANKED", hi->handle);
1689 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1690 || (hi->opserv_level > 0))
1691 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1692 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1695 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1696 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1697 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1698 if (nickserv_conf.email_enabled)
1699 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1701 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1702 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1704 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1706 for (n=0; n<allowauth_func_used; n++)
1707 allowauth_func_list[n](user, target, hi);
1711 static NICKSERV_FUNC(cmd_authcookie)
1713 struct handle_info *hi;
1715 NICKSERV_MIN_PARMS(2);
1716 if (user->handle_info) {
1717 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1720 if (IsStamped(user)) {
1721 /* Unauthenticated users might still have been stamped
1722 previously and could therefore have a hidden host;
1723 do not allow them to authenticate to an account. */
1724 reply("NSMSG_STAMPED_AUTHCOOKIE");
1727 if (!(hi = get_handle_info(argv[1]))) {
1728 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1731 if (!hi->email_addr) {
1732 reply("MSG_SET_EMAIL_ADDR");
1735 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1739 static NICKSERV_FUNC(cmd_delcookie)
1741 struct handle_info *hi;
1743 hi = user->handle_info;
1745 reply("NSMSG_NO_COOKIE");
1748 switch (hi->cookie->type) {
1751 reply("NSMSG_MUST_TIME_OUT");
1754 nickserv_eat_cookie(hi->cookie);
1755 reply("NSMSG_ATE_COOKIE");
1761 static NICKSERV_FUNC(cmd_resetpass)
1763 struct handle_info *hi;
1764 char crypted[MD5_CRYPT_LENGTH];
1766 NICKSERV_MIN_PARMS(3);
1767 if (user->handle_info) {
1768 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1771 if (IsStamped(user)) {
1772 /* Unauthenticated users might still have been stamped
1773 previously and could therefore have a hidden host;
1774 do not allow them to activate an account. */
1775 reply("NSMSG_STAMPED_RESETPASS");
1778 if (!(hi = get_handle_info(argv[1]))) {
1779 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1782 if (!hi->email_addr) {
1783 reply("MSG_SET_EMAIL_ADDR");
1786 cryptpass(argv[2], crypted);
1788 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1792 static NICKSERV_FUNC(cmd_cookie)
1794 struct handle_info *hi;
1797 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1800 NICKSERV_MIN_PARMS(3);
1801 if (!(hi = get_handle_info(argv[1]))) {
1802 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1808 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1809 reply("NSMSG_HANDLE_SUSPENDED");
1814 reply("NSMSG_NO_COOKIE");
1818 /* Check validity of operation before comparing cookie to
1819 * prohibit guessing by authed users. */
1820 if (user->handle_info
1821 && (hi->cookie->type != EMAIL_CHANGE)
1822 && (hi->cookie->type != PASSWORD_CHANGE)) {
1823 reply("NSMSG_CANNOT_COOKIE");
1827 if (strcmp(cookie, hi->cookie->cookie)) {
1828 reply("NSMSG_BAD_COOKIE");
1832 switch (hi->cookie->type) {
1834 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1835 set_user_handle_info(user, hi, 1);
1836 reply("NSMSG_HANDLE_ACTIVATED");
1838 case PASSWORD_CHANGE:
1839 set_user_handle_info(user, hi, 1);
1840 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1841 reply("NSMSG_PASSWORD_CHANGED");
1844 nickserv_set_email_addr(hi, hi->cookie->data);
1845 reply("NSMSG_EMAIL_CHANGED");
1848 set_user_handle_info(user, hi, 1);
1849 reply("NSMSG_AUTH_SUCCESS");
1852 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
1853 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
1857 nickserv_eat_cookie(hi->cookie);
1862 static NICKSERV_FUNC(cmd_oregnick) {
1864 struct handle_info *target;
1865 struct nick_info *ni;
1867 NICKSERV_MIN_PARMS(3);
1868 if (!(target = modcmd_get_handle_info(user, argv[1])))
1871 if (!is_registerable_nick(nick)) {
1872 reply("NSMSG_BAD_NICK", nick);
1875 ni = dict_find(nickserv_nick_dict, nick, NULL);
1877 reply("NSMSG_NICK_EXISTS", nick);
1880 register_nick(nick, target);
1881 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
1885 static NICKSERV_FUNC(cmd_regnick) {
1887 struct nick_info *ni;
1889 if (!is_registerable_nick(user->nick)) {
1890 reply("NSMSG_BAD_NICK", user->nick);
1893 /* count their nicks, see if it's too many */
1894 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
1895 if (n >= nickserv_conf.nicks_per_handle) {
1896 reply("NSMSG_TOO_MANY_NICKS");
1899 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
1901 reply("NSMSG_NICK_EXISTS", user->nick);
1904 register_nick(user->nick, user->handle_info);
1905 reply("NSMSG_REGNICK_SUCCESS", user->nick);
1909 static NICKSERV_FUNC(cmd_pass)
1911 struct handle_info *hi;
1912 const char *old_pass, *new_pass;
1914 NICKSERV_MIN_PARMS(3);
1915 hi = user->handle_info;
1919 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
1920 if (!checkpass(old_pass, hi->passwd)) {
1921 argv[1] = "BADPASS";
1922 reply("NSMSG_PASSWORD_INVALID");
1925 cryptpass(new_pass, hi->passwd);
1927 reply("NSMSG_PASS_SUCCESS");
1932 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
1935 char *new_mask = canonicalize_hostmask(strdup(mask));
1936 for (i=0; i<hi->masks->used; i++) {
1937 if (!irccasecmp(new_mask, hi->masks->list[i])) {
1938 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
1943 string_list_append(hi->masks, new_mask);
1944 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
1948 static NICKSERV_FUNC(cmd_addmask)
1951 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1952 int res = nickserv_addmask(user, user->handle_info, mask);
1956 if (!is_gline(argv[1])) {
1957 reply("NSMSG_MASK_INVALID", argv[1]);
1960 return nickserv_addmask(user, user->handle_info, argv[1]);
1964 static NICKSERV_FUNC(cmd_oaddmask)
1966 struct handle_info *hi;
1968 NICKSERV_MIN_PARMS(3);
1969 if (!(hi = get_victim_oper(user, argv[1])))
1971 return nickserv_addmask(user, hi, argv[2]);
1975 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask)
1978 for (i=0; i<hi->masks->used; i++) {
1979 if (!strcmp(del_mask, hi->masks->list[i])) {
1980 char *old_mask = hi->masks->list[i];
1981 if (hi->masks->used == 1) {
1982 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
1985 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
1986 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
1991 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
1995 static NICKSERV_FUNC(cmd_delmask)
1997 NICKSERV_MIN_PARMS(2);
1998 return nickserv_delmask(user, user->handle_info, argv[1]);
2001 static NICKSERV_FUNC(cmd_odelmask)
2003 struct handle_info *hi;
2004 NICKSERV_MIN_PARMS(3);
2005 if (!(hi = get_victim_oper(user, argv[1])))
2007 return nickserv_delmask(user, hi, argv[2]);
2011 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2012 unsigned int nn, add = 1, pos;
2013 unsigned long added, removed, flag;
2015 for (added=removed=nn=0; str[nn]; nn++) {
2017 case '+': add = 1; break;
2018 case '-': add = 0; break;
2020 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2021 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2024 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2025 /* cheesy avoidance of looking up the flag name.. */
2026 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2029 flag = 1 << (pos - 1);
2031 added |= flag, removed &= ~flag;
2033 removed |= flag, added &= ~flag;
2038 *premoved = removed;
2043 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2045 unsigned long before, after, added, removed;
2046 struct userNode *uNode;
2048 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2049 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2051 hi->flags = (hi->flags | added) & ~removed;
2052 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2054 /* Strip helping flag if they're only a support helper and not
2055 * currently in #support. */
2056 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2057 struct channelList *schannels;
2059 schannels = chanserv_support_channels();
2060 for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
2061 for (ii = 0; ii < schannels->used; ++ii)
2062 if (GetUserMode(schannels->list[ii], uNode))
2064 if (ii < schannels->used)
2068 HANDLE_CLEAR_FLAG(hi, HELPING);
2071 if (after && !before) {
2072 /* Add user to current helper list. */
2073 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2074 userList_append(&curr_helpers, uNode);
2075 } else if (!after && before) {
2076 /* Remove user from current helper list. */
2077 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2078 userList_remove(&curr_helpers, uNode);
2085 set_list(struct userNode *user, struct handle_info *hi, int override)
2089 char *set_display[] = {
2090 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2091 "EMAIL", "ANNOUNCEMENTS", "MAXLOGINS", "LANGUAGE"
2094 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2096 /* Do this so options are presented in a consistent order. */
2097 for (i = 0; i < ArrayLength(set_display); ++i)
2098 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2099 opt(user, hi, override, 0, NULL);
2102 static NICKSERV_FUNC(cmd_set)
2104 struct handle_info *hi;
2107 hi = user->handle_info;
2109 set_list(user, hi, 0);
2112 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2113 reply("NSMSG_INVALID_OPTION", argv[1]);
2116 return opt(user, hi, 0, argc-1, argv+1);
2119 static NICKSERV_FUNC(cmd_oset)
2121 struct handle_info *hi;
2124 NICKSERV_MIN_PARMS(2);
2126 if (!(hi = get_victim_oper(user, argv[1])))
2130 set_list(user, hi, 0);
2134 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2135 reply("NSMSG_INVALID_OPTION", argv[2]);
2139 return opt(user, hi, 1, argc-2, argv+2);
2142 static OPTION_FUNC(opt_info)
2146 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2148 hi->infoline = NULL;
2150 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2154 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2155 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2159 static OPTION_FUNC(opt_width)
2162 hi->screen_width = strtoul(argv[1], NULL, 0);
2164 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2165 hi->screen_width = MIN_LINE_SIZE;
2166 else if (hi->screen_width > MAX_LINE_SIZE)
2167 hi->screen_width = MAX_LINE_SIZE;
2169 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2173 static OPTION_FUNC(opt_tablewidth)
2176 hi->table_width = strtoul(argv[1], NULL, 0);
2178 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2179 hi->table_width = MIN_LINE_SIZE;
2180 else if (hi->screen_width > MAX_LINE_SIZE)
2181 hi->table_width = MAX_LINE_SIZE;
2183 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2187 static OPTION_FUNC(opt_color)
2190 if (enabled_string(argv[1]))
2191 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2192 else if (disabled_string(argv[1]))
2193 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2195 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2200 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2204 static OPTION_FUNC(opt_privmsg)
2207 if (enabled_string(argv[1]))
2208 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2209 else if (disabled_string(argv[1]))
2210 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2212 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2217 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2221 static OPTION_FUNC(opt_style)
2226 if (!irccasecmp(argv[1], "Zoot"))
2227 hi->userlist_style = HI_STYLE_ZOOT;
2228 else if (!irccasecmp(argv[1], "def"))
2229 hi->userlist_style = HI_STYLE_DEF;
2232 switch (hi->userlist_style) {
2241 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2245 static OPTION_FUNC(opt_announcements)
2250 if (enabled_string(argv[1]))
2251 hi->announcements = 'y';
2252 else if (disabled_string(argv[1]))
2253 hi->announcements = 'n';
2254 else if (!strcmp(argv[1], "?") || !irccasecmp(argv[1], "default"))
2255 hi->announcements = '?';
2257 send_message(user, nickserv, "NSMSG_INVALID_ANNOUNCE", argv[1]);
2262 switch (hi->announcements) {
2263 case 'y': choice = user_find_message(user, "MSG_ON"); break;
2264 case 'n': choice = user_find_message(user, "MSG_OFF"); break;
2265 case '?': choice = "default"; break;
2266 default: choice = "unknown"; break;
2268 send_message(user, nickserv, "NSMSG_SET_ANNOUNCEMENTS", choice);
2272 static OPTION_FUNC(opt_password)
2275 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2280 cryptpass(argv[1], hi->passwd);
2282 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2286 static OPTION_FUNC(opt_flags)
2289 unsigned int ii, flen;
2292 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2297 nickserv_apply_flags(user, hi, argv[1]);
2299 for (ii = flen = 0; handle_flags[ii]; ii++)
2300 if (hi->flags & (1 << ii))
2301 flags[flen++] = handle_flags[ii];
2304 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2306 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2310 static OPTION_FUNC(opt_email)
2314 if (!is_valid_email_addr(argv[1])) {
2315 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2318 if ((str = sendmail_prohibited_address(argv[1]))) {
2319 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2322 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2323 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2325 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2327 nickserv_set_email_addr(hi, argv[1]);
2329 nickserv_eat_cookie(hi->cookie);
2330 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2333 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2337 static OPTION_FUNC(opt_maxlogins)
2339 unsigned char maxlogins;
2341 maxlogins = strtoul(argv[1], NULL, 0);
2342 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2343 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2346 hi->maxlogins = maxlogins;
2348 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2349 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2353 static OPTION_FUNC(opt_language)
2355 struct language *lang;
2357 lang = language_find(argv[1]);
2358 if (irccasecmp(lang->name, argv[1]))
2359 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2360 hi->language = lang;
2362 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2367 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2368 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2370 if ((user->handle_info->opserv_level < target->opserv_level)
2371 || ((user->handle_info->opserv_level == target->opserv_level)
2372 && (user->handle_info->opserv_level < 1000))) {
2373 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2376 if ((user->handle_info->opserv_level < new_level)
2377 || ((user->handle_info->opserv_level == new_level)
2378 && (user->handle_info->opserv_level < 1000))) {
2379 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2382 if (user->handle_info == target) {
2383 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2386 if (target->opserv_level == new_level)
2388 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2389 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2390 target->opserv_level = new_level;
2394 static OPTION_FUNC(opt_level)
2399 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2403 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2404 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2408 static OPTION_FUNC(opt_epithet)
2411 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2415 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2416 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2419 if ((epithet[0] == '*') && !epithet[1])
2422 hi->epithet = strdup(epithet);
2426 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2428 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2432 static OPTION_FUNC(opt_title)
2437 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2441 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2443 if (strchr(title, '.')) {
2444 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2447 if ((strlen(user->handle_info->handle) + strlen(title) +
2448 strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) {
2449 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2454 if (!strcmp(title, "*")) {
2455 hi->fakehost = NULL;
2457 hi->fakehost = malloc(strlen(title)+2);
2458 hi->fakehost[0] = '.';
2459 strcpy(hi->fakehost+1, title);
2462 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2463 title = hi->fakehost + 1;
2467 title = user_find_message(user, "MSG_NONE");
2468 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2472 static OPTION_FUNC(opt_fakehost)
2477 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2481 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2483 if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) {
2484 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2488 if (!strcmp(fake, "*"))
2489 hi->fakehost = NULL;
2491 hi->fakehost = strdup(fake);
2492 fake = hi->fakehost;
2495 fake = generate_fakehost(hi);
2497 fake = user_find_message(user, "MSG_NONE");
2498 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", fake);
2502 static NICKSERV_FUNC(cmd_reclaim)
2504 struct handle_info *hi;
2505 struct nick_info *ni;
2506 struct userNode *victim;
2508 NICKSERV_MIN_PARMS(2);
2509 hi = user->handle_info;
2510 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2512 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2515 if (ni->owner != user->handle_info) {
2516 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2519 victim = GetUserH(ni->nick);
2521 reply("MSG_NICK_UNKNOWN", ni->nick);
2524 if (victim == user) {
2525 reply("NSMSG_NICK_USER_YOU");
2528 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2529 switch (nickserv_conf.reclaim_action) {
2530 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2531 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2532 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2533 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2538 static NICKSERV_FUNC(cmd_unregnick)
2541 struct handle_info *hi;
2542 struct nick_info *ni;
2544 hi = user->handle_info;
2545 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2546 ni = dict_find(nickserv_nick_dict, nick, NULL);
2548 reply("NSMSG_UNKNOWN_NICK", nick);
2551 if (hi != ni->owner) {
2552 reply("NSMSG_NOT_YOUR_NICK", nick);
2555 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2560 static NICKSERV_FUNC(cmd_ounregnick)
2562 struct nick_info *ni;
2564 NICKSERV_MIN_PARMS(2);
2565 if (!(ni = get_nick_info(argv[1]))) {
2566 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2569 if (ni->owner->opserv_level >= user->handle_info->opserv_level) {
2570 reply("MSG_USER_OUTRANKED", ni->nick);
2573 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2578 static NICKSERV_FUNC(cmd_unregister)
2580 struct handle_info *hi;
2583 NICKSERV_MIN_PARMS(2);
2584 hi = user->handle_info;
2587 if (checkpass(passwd, hi->passwd)) {
2588 nickserv_unregister_handle(hi, user);
2591 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2592 reply("NSMSG_PASSWORD_INVALID");
2597 static NICKSERV_FUNC(cmd_ounregister)
2599 struct handle_info *hi;
2601 NICKSERV_MIN_PARMS(2);
2602 if (!(hi = get_victim_oper(user, argv[1])))
2604 nickserv_unregister_handle(hi, user);
2608 static NICKSERV_FUNC(cmd_status)
2610 if (nickserv_conf.disable_nicks) {
2611 reply("NSMSG_GLOBAL_STATS_NONICK",
2612 dict_size(nickserv_handle_dict));
2614 if (user->handle_info) {
2616 struct nick_info *ni;
2617 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2618 reply("NSMSG_HANDLE_STATS", cnt);
2620 reply("NSMSG_HANDLE_NONE");
2622 reply("NSMSG_GLOBAL_STATS",
2623 dict_size(nickserv_handle_dict),
2624 dict_size(nickserv_nick_dict));
2629 static NICKSERV_FUNC(cmd_ghost)
2631 struct userNode *target;
2632 char reason[MAXLEN];
2634 NICKSERV_MIN_PARMS(2);
2635 if (!(target = GetUserH(argv[1]))) {
2636 reply("MSG_NICK_UNKNOWN", argv[1]);
2639 if (target == user) {
2640 reply("NSMSG_CANNOT_GHOST_SELF");
2643 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2644 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2647 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2648 DelUser(target, nickserv, 1, reason);
2649 reply("NSMSG_GHOST_KILLED", argv[1]);
2653 static NICKSERV_FUNC(cmd_vacation)
2655 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2656 reply("NSMSG_ON_VACATION");
2661 nickserv_saxdb_write(struct saxdb_context *ctx) {
2663 struct handle_info *hi;
2666 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2668 #ifdef WITH_PROTOCOL_BAHAMUT
2671 saxdb_start_record(ctx, iter_key(it), 0);
2672 if (hi->announcements != '?') {
2673 flags[0] = hi->announcements;
2675 saxdb_write_string(ctx, KEY_ANNOUNCEMENTS, flags);
2678 struct handle_cookie *cookie = hi->cookie;
2681 switch (cookie->type) {
2682 case ACTIVATION: type = KEY_ACTIVATION; break;
2683 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2684 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2685 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2686 default: type = NULL; break;
2689 saxdb_start_record(ctx, KEY_COOKIE, 0);
2690 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2691 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2693 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2694 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2695 saxdb_end_record(ctx);
2699 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2701 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2703 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
2707 for (ii=flen=0; handle_flags[ii]; ++ii)
2708 if (hi->flags & (1 << ii))
2709 flags[flen++] = handle_flags[ii];
2711 saxdb_write_string(ctx, KEY_FLAGS, flags);
2713 #ifdef WITH_PROTOCOL_BAHAMUT
2714 saxdb_write_int(ctx, KEY_ID, hi->id);
2717 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2718 if (hi->last_quit_host[0])
2719 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2720 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2721 if (hi->masks->used)
2722 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2724 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2726 struct string_list *slist;
2727 struct nick_info *ni;
2729 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2730 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2731 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2735 if (hi->opserv_level)
2736 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2737 if (hi->language != lang_C)
2738 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2739 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2740 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2741 if (hi->screen_width)
2742 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2743 if (hi->table_width)
2744 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2745 flags[0] = hi->userlist_style;
2747 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2748 saxdb_end_record(ctx);
2753 static handle_merge_func_t *handle_merge_func_list;
2754 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2757 reg_handle_merge_func(handle_merge_func_t func)
2759 if (handle_merge_func_used == handle_merge_func_size) {
2760 if (handle_merge_func_size) {
2761 handle_merge_func_size <<= 1;
2762 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2764 handle_merge_func_size = 8;
2765 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2768 handle_merge_func_list[handle_merge_func_used++] = func;
2771 static NICKSERV_FUNC(cmd_merge)
2773 struct handle_info *hi_from, *hi_to;
2774 struct userNode *last_user;
2775 struct userData *cList, *cListNext;
2776 unsigned int ii, jj, n;
2777 char buffer[MAXLEN];
2779 NICKSERV_MIN_PARMS(3);
2781 if (!(hi_from = get_victim_oper(user, argv[1])))
2783 if (!(hi_to = get_victim_oper(user, argv[2])))
2785 if (hi_to == hi_from) {
2786 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2790 for (n=0; n<handle_merge_func_used; n++)
2791 handle_merge_func_list[n](user, hi_to, hi_from);
2793 /* Append "from" handle's nicks to "to" handle's nick list. */
2795 struct nick_info *last_ni;
2796 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
2797 last_ni->next = hi_from->nicks;
2799 while (hi_from->nicks) {
2800 hi_from->nicks->owner = hi_to;
2801 hi_from->nicks = hi_from->nicks->next;
2804 /* Merge the hostmasks. */
2805 for (ii=0; ii<hi_from->masks->used; ii++) {
2806 char *mask = hi_from->masks->list[ii];
2807 for (jj=0; jj<hi_to->masks->used; jj++)
2808 if (match_ircglobs(hi_to->masks->list[jj], mask))
2810 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
2811 string_list_append(hi_to->masks, strdup(mask));
2814 /* Merge the lists of authed users. */
2816 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
2817 last_user->next_authed = hi_from->users;
2819 hi_to->users = hi_from->users;
2821 /* Repoint the old "from" handle's users. */
2822 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
2823 last_user->handle_info = hi_to;
2825 hi_from->users = NULL;
2827 /* Merge channel userlists. */
2828 for (cList=hi_from->channels; cList; cList=cListNext) {
2829 struct userData *cList2;
2830 cListNext = cList->u_next;
2831 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
2832 if (cList->channel == cList2->channel)
2834 if (cList2 && (cList2->access >= cList->access)) {
2835 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);
2836 /* keep cList2 in hi_to; remove cList from hi_from */
2837 del_channel_user(cList, 1);
2840 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);
2841 /* remove the lower-ranking cList2 from hi_to */
2842 del_channel_user(cList2, 1);
2844 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
2846 /* cList needs to be moved from hi_from to hi_to */
2847 cList->handle = hi_to;
2848 /* Remove from linked list for hi_from */
2849 assert(!cList->u_prev);
2850 hi_from->channels = cList->u_next;
2852 cList->u_next->u_prev = cList->u_prev;
2853 /* Add to linked list for hi_to */
2854 cList->u_prev = NULL;
2855 cList->u_next = hi_to->channels;
2856 if (hi_to->channels)
2857 hi_to->channels->u_prev = cList;
2858 hi_to->channels = cList;
2862 /* Do they get an OpServ level promotion? */
2863 if (hi_from->opserv_level > hi_to->opserv_level)
2864 hi_to->opserv_level = hi_from->opserv_level;
2866 /* What about last seen time? */
2867 if (hi_from->lastseen > hi_to->lastseen)
2868 hi_to->lastseen = hi_from->lastseen;
2870 /* Notify of success. */
2871 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
2872 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
2873 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
2875 /* Unregister the "from" handle. */
2876 nickserv_unregister_handle(hi_from, NULL);
2881 struct nickserv_discrim {
2882 unsigned int limit, min_level, max_level;
2883 unsigned long flags_on, flags_off;
2884 time_t min_registered, max_registered;
2886 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
2887 const char *nickmask;
2888 const char *hostmask;
2889 const char *handlemask;
2890 const char *emailmask;
2893 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
2895 struct discrim_apply_info {
2896 struct nickserv_discrim *discrim;
2897 discrim_search_func func;
2898 struct userNode *source;
2899 unsigned int matched;
2902 static struct nickserv_discrim *
2903 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
2906 struct nickserv_discrim *discrim;
2908 discrim = malloc(sizeof(*discrim));
2909 memset(discrim, 0, sizeof(*discrim));
2910 discrim->min_level = 0;
2911 discrim->max_level = ~0;
2912 discrim->limit = 50;
2913 discrim->min_registered = 0;
2914 discrim->max_registered = INT_MAX;
2915 discrim->lastseen = now;
2917 for (i=0; i<argc; i++) {
2918 if (i == argc - 1) {
2919 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2922 if (!irccasecmp(argv[i], "limit")) {
2923 discrim->limit = strtoul(argv[++i], NULL, 0);
2924 } else if (!irccasecmp(argv[i], "flags")) {
2925 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
2926 } else if (!irccasecmp(argv[i], "registered")) {
2927 const char *cmp = argv[++i];
2928 if (cmp[0] == '<') {
2929 if (cmp[1] == '=') {
2930 discrim->min_registered = now - ParseInterval(cmp+2);
2932 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
2934 } else if (cmp[0] == '=') {
2935 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
2936 } else if (cmp[0] == '>') {
2937 if (cmp[1] == '=') {
2938 discrim->max_registered = now - ParseInterval(cmp+2);
2940 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
2943 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2945 } else if (!irccasecmp(argv[i], "seen")) {
2946 discrim->lastseen = now - ParseInterval(argv[++i]);
2947 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
2948 discrim->nickmask = argv[++i];
2949 } else if (!irccasecmp(argv[i], "hostmask")) {
2951 if (!irccasecmp(argv[i], "exact")) {
2952 if (i == argc - 1) {
2953 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2956 discrim->hostmask_type = EXACT;
2957 } else if (!irccasecmp(argv[i], "subset")) {
2958 if (i == argc - 1) {
2959 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2962 discrim->hostmask_type = SUBSET;
2963 } else if (!irccasecmp(argv[i], "superset")) {
2964 if (i == argc - 1) {
2965 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2968 discrim->hostmask_type = SUPERSET;
2969 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
2970 if (i == argc - 1) {
2971 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2974 discrim->hostmask_type = LASTQUIT;
2977 discrim->hostmask_type = SUPERSET;
2979 discrim->hostmask = argv[++i];
2980 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
2981 if (!irccasecmp(argv[++i], "*")) {
2982 discrim->handlemask = 0;
2984 discrim->handlemask = argv[i];
2986 } else if (!irccasecmp(argv[i], "email")) {
2987 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
2988 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
2990 } else if (!irccasecmp(argv[++i], "*")) {
2991 discrim->emailmask = 0;
2993 discrim->emailmask = argv[i];
2995 } else if (!irccasecmp(argv[i], "access")) {
2996 const char *cmp = argv[++i];
2997 if (cmp[0] == '<') {
2998 if (discrim->min_level == 0) discrim->min_level = 1;
2999 if (cmp[1] == '=') {
3000 discrim->max_level = strtoul(cmp+2, NULL, 0);
3002 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3004 } else if (cmp[0] == '=') {
3005 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3006 } else if (cmp[0] == '>') {
3007 if (cmp[1] == '=') {
3008 discrim->min_level = strtoul(cmp+2, NULL, 0);
3010 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3013 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3016 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3027 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3029 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3030 || (discrim->flags_off & hi->flags)
3031 || (discrim->min_registered > hi->registered)
3032 || (discrim->max_registered < hi->registered)
3033 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3034 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3035 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3036 || (discrim->min_level > hi->opserv_level)
3037 || (discrim->max_level < hi->opserv_level)) {
3040 if (discrim->hostmask) {
3042 for (i=0; i<hi->masks->used; i++) {
3043 const char *mask = hi->masks->list[i];
3044 if ((discrim->hostmask_type == SUBSET)
3045 && (match_ircglobs(discrim->hostmask, mask))) break;
3046 else if ((discrim->hostmask_type == EXACT)
3047 && !irccasecmp(discrim->hostmask, mask)) break;
3048 else if ((discrim->hostmask_type == SUPERSET)
3049 && (match_ircglobs(mask, discrim->hostmask))) break;
3050 else if ((discrim->hostmask_type == LASTQUIT)
3051 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3053 if (i==hi->masks->used) return 0;
3055 if (discrim->nickmask) {
3056 struct nick_info *nick = hi->nicks;
3058 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3061 if (!nick) return 0;
3067 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3069 dict_iterator_t it, next;
3070 unsigned int matched;
3072 for (it = dict_first(nickserv_handle_dict), matched = 0;
3073 it && (matched < discrim->limit);
3075 next = iter_next(it);
3076 if (nickserv_discrim_match(discrim, iter_data(it))) {
3077 dsf(source, iter_data(it));
3085 search_print_func(struct userNode *source, struct handle_info *match)
3087 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3091 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3096 search_unregister_func (struct userNode *source, struct handle_info *match)
3098 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3099 nickserv_unregister_handle(match, source);
3103 nickserv_sort_accounts_by_access(const void *a, const void *b)
3105 const struct handle_info *hi_a = *(const struct handle_info**)a;
3106 const struct handle_info *hi_b = *(const struct handle_info**)b;
3107 if (hi_a->opserv_level != hi_b->opserv_level)
3108 return hi_b->opserv_level - hi_a->opserv_level;
3109 return irccasecmp(hi_a->handle, hi_b->handle);
3113 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3115 struct handle_info_list hil;
3116 struct helpfile_table tbl;
3121 memset(&hil, 0, sizeof(hil));
3122 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3123 struct handle_info *hi = iter_data(it);
3124 if (hi->opserv_level)
3125 handle_info_list_append(&hil, hi);
3127 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3128 tbl.length = hil.used + 1;
3130 tbl.flags = TABLE_NO_FREE;
3131 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3132 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3135 for (ii = 0; ii < hil.used; ) {
3136 ary = malloc(tbl.width * sizeof(ary[0]));
3137 ary[0] = hil.list[ii]->handle;
3138 ary[1] = strtab(hil.list[ii]->opserv_level);
3139 tbl.contents[++ii] = ary;
3141 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3142 reply("MSG_MATCH_COUNT", hil.used);
3143 for (ii = 0; ii < hil.used; ii++)
3144 free(tbl.contents[ii]);
3149 static NICKSERV_FUNC(cmd_search)
3151 struct nickserv_discrim *discrim;
3152 discrim_search_func action;
3153 struct svccmd *subcmd;
3154 unsigned int matches;
3157 NICKSERV_MIN_PARMS(3);
3158 sprintf(buf, "search %s", argv[1]);
3159 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3160 if (!irccasecmp(argv[1], "print"))
3161 action = search_print_func;
3162 else if (!irccasecmp(argv[1], "count"))
3163 action = search_count_func;
3164 else if (!irccasecmp(argv[1], "unregister"))
3165 action = search_unregister_func;
3167 reply("NSMSG_INVALID_ACTION", argv[1]);
3171 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3174 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3178 if (action == search_print_func)
3179 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3180 else if (action == search_count_func)
3181 discrim->limit = INT_MAX;
3183 matches = nickserv_discrim_search(discrim, action, user);
3186 reply("MSG_MATCH_COUNT", matches);
3188 reply("MSG_NO_MATCHES");
3194 static MODCMD_FUNC(cmd_checkpass)
3196 struct handle_info *hi;
3198 NICKSERV_MIN_PARMS(3);
3199 if (!(hi = get_handle_info(argv[1]))) {
3200 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3203 if (checkpass(argv[2], hi->passwd))
3204 reply("CHECKPASS_YES");
3206 reply("CHECKPASS_NO");
3212 nickserv_db_read_handle(const char *handle, dict_t obj)
3215 struct string_list *masks, *slist;
3216 struct handle_info *hi;
3217 struct userNode *authed_users;
3218 unsigned long int id;
3222 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3223 id = str ? strtoul(str, NULL, 0) : 0;
3224 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3226 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3229 if ((hi = get_handle_info(handle))) {
3230 authed_users = hi->users;
3232 dict_remove(nickserv_handle_dict, hi->handle);
3234 authed_users = NULL;
3236 hi = register_handle(handle, str, id);
3238 hi->users = authed_users;
3239 while (authed_users) {
3240 authed_users->handle_info = hi;
3241 authed_users = authed_users->next_authed;
3244 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3245 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3246 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3247 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3248 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3249 hi->language = language_find(str ? str : "C");
3250 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3251 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3252 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3254 hi->infoline = strdup(str);
3255 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3256 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
3257 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3258 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
3259 /* We want to read the nicks even if disable_nicks is set. This is so
3260 * that we don't lose the nick data entirely. */
3261 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3263 for (ii=0; ii<slist->used; ii++)
3264 register_nick(slist->list[ii], hi);
3266 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3268 for (ii=0; str[ii]; ii++)
3269 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3271 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3272 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3273 str = database_get_data(obj, KEY_ANNOUNCEMENTS, RECDB_QSTRING);
3274 hi->announcements = str ? str[0] : '?';
3275 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3276 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3277 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3278 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3279 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3281 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3283 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3284 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3286 nickserv_set_email_addr(hi, str);
3287 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3289 hi->epithet = strdup(str);
3290 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3292 hi->fakehost = strdup(str);
3293 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3295 const char *data, *type, *expires, *cookie_str;
3296 struct handle_cookie *cookie;
3298 cookie = calloc(1, sizeof(*cookie));
3299 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3300 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3301 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3302 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3303 if (!type || !expires || !cookie_str) {
3304 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3307 if (!irccasecmp(type, KEY_ACTIVATION))
3308 cookie->type = ACTIVATION;
3309 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3310 cookie->type = PASSWORD_CHANGE;
3311 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3312 cookie->type = EMAIL_CHANGE;
3313 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3314 cookie->type = ALLOWAUTH;
3316 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3319 cookie->expires = strtoul(expires, NULL, 0);
3320 if (cookie->expires < now)
3323 cookie->data = strdup(data);
3324 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3328 nickserv_bake_cookie(cookie);
3330 nickserv_free_cookie(cookie);
3335 nickserv_saxdb_read(dict_t db) {
3337 struct record_data *rd;
3339 for (it=dict_first(db); it; it=iter_next(it)) {
3341 nickserv_db_read_handle(iter_key(it), rd->d.object);
3346 static NICKSERV_FUNC(cmd_mergedb)
3348 struct timeval start, stop;
3351 NICKSERV_MIN_PARMS(2);
3352 gettimeofday(&start, NULL);
3353 if (!(db = parse_database(argv[1]))) {
3354 reply("NSMSG_DB_UNREADABLE", argv[1]);
3357 nickserv_saxdb_read(db);
3359 gettimeofday(&stop, NULL);
3360 stop.tv_sec -= start.tv_sec;
3361 stop.tv_usec -= start.tv_usec;
3362 if (stop.tv_usec < 0) {
3364 stop.tv_usec += 1000000;
3366 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
3371 expire_handles(UNUSED_ARG(void *data))
3373 dict_iterator_t it, next;
3375 struct handle_info *hi;
3377 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3378 next = iter_next(it);
3380 if ((hi->opserv_level > 0)
3382 || HANDLE_FLAGGED(hi, FROZEN)
3383 || HANDLE_FLAGGED(hi, NODELETE)) {
3386 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3387 if ((now - hi->lastseen) > expiry) {
3388 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3389 nickserv_unregister_handle(hi, NULL);
3393 if (nickserv_conf.handle_expire_frequency)
3394 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3398 nickserv_load_dict(const char *fname)
3402 if (!(file = fopen(fname, "r"))) {
3403 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3406 while (!feof(file)) {
3407 fgets(line, sizeof(line), file);
3410 if (line[strlen(line)-1] == '\n')
3411 line[strlen(line)-1] = 0;
3412 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3415 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3418 static enum reclaim_action
3419 reclaim_action_from_string(const char *str) {
3421 return RECLAIM_NONE;
3422 else if (!irccasecmp(str, "warn"))
3423 return RECLAIM_WARN;
3424 else if (!irccasecmp(str, "svsnick"))
3425 return RECLAIM_SVSNICK;
3426 else if (!irccasecmp(str, "kill"))
3427 return RECLAIM_KILL;
3429 return RECLAIM_NONE;
3433 nickserv_conf_read(void)
3435 dict_t conf_node, child;
3439 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3440 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3443 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3445 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3446 if (nickserv_conf.valid_handle_regex_set)
3447 regfree(&nickserv_conf.valid_handle_regex);
3449 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3450 nickserv_conf.valid_handle_regex_set = !err;
3451 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3453 nickserv_conf.valid_handle_regex_set = 0;
3455 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3456 if (nickserv_conf.valid_nick_regex_set)
3457 regfree(&nickserv_conf.valid_nick_regex);
3459 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3460 nickserv_conf.valid_nick_regex_set = !err;
3461 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3463 nickserv_conf.valid_nick_regex_set = 0;
3465 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3467 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3468 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3469 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3470 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3471 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3472 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3473 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3474 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3475 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3476 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3477 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3478 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3479 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3480 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3481 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3482 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3483 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3484 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3485 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3486 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3487 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3488 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3489 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3490 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3491 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3493 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3494 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3495 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3497 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3498 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3499 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3501 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3502 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3503 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3504 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3505 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3506 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3507 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3508 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3509 if (!nickserv_conf.disable_nicks) {
3510 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3511 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3512 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3513 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3514 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3515 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3516 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3517 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3519 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3520 for (it=dict_first(child); it; it=iter_next(it)) {
3521 const char *key = iter_key(it), *value;
3525 if (!strncasecmp(key, "uc_", 3))
3526 flag = toupper(key[3]);
3527 else if (!strncasecmp(key, "lc_", 3))
3528 flag = tolower(key[3]);
3532 if ((pos = handle_inverse_flags[flag])) {
3533 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3534 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3537 if (nickserv_conf.weak_password_dict)
3538 dict_delete(nickserv_conf.weak_password_dict);
3539 nickserv_conf.weak_password_dict = dict_new();
3540 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3541 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3542 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3543 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3545 nickserv_load_dict(str);
3546 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3547 if (nickserv && str)
3548 NickChange(nickserv, str, 0);
3549 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3550 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3551 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3552 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3553 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3554 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3555 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3556 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3557 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3558 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3559 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3560 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3561 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3562 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3563 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3564 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3565 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
3566 nickserv_conf.titlehost_suffix = str ? str : "example.net";
3567 str = conf_get_data("server/network", RECDB_QSTRING);
3568 nickserv_conf.network_name = str ? str : "some IRC network";
3569 if (!nickserv_conf.auth_policer_params) {
3570 nickserv_conf.auth_policer_params = policer_params_new();
3571 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3572 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3574 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3575 for (it=dict_first(child); it; it=iter_next(it))
3576 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3580 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3582 char newnick[NICKLEN+1];
3591 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3593 case RECLAIM_SVSNICK:
3595 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3596 } while (GetUserH(newnick));
3597 irc_svsnick(nickserv, user, newnick);
3600 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
3601 irc_kill(nickserv, user, msg);
3607 nickserv_reclaim_p(void *data) {
3608 struct userNode *user = data;
3609 struct nick_info *ni = get_nick_info(user->nick);
3611 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3615 check_user_nick(struct userNode *user) {
3616 struct nick_info *ni;
3617 user->modes &= ~FLAGS_REGNICK;
3618 if (!(ni = get_nick_info(user->nick)))
3620 if (user->handle_info == ni->owner) {
3621 user->modes |= FLAGS_REGNICK;
3625 if (nickserv_conf.warn_nick_owned)
3626 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3627 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3629 if (nickserv_conf.auto_reclaim_delay)
3630 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3632 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3637 handle_new_user(struct userNode *user)
3639 return check_user_nick(user);
3643 handle_account(struct userNode *user, const char *stamp)
3645 struct handle_info *hi;
3647 #ifdef WITH_PROTOCOL_P10
3648 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3650 hi = dict_find(nickserv_id_dict, stamp, NULL);
3654 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3657 set_user_handle_info(user, hi, 0);
3659 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
3664 handle_nick_change(struct userNode *user, const char *old_nick)
3666 struct handle_info *hi;
3668 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
3669 dict_remove(nickserv_allow_auth_dict, old_nick);
3670 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
3672 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3673 check_user_nick(user);
3677 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
3679 dict_remove(nickserv_allow_auth_dict, user->nick);
3680 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3681 set_user_handle_info(user, NULL, 0);
3684 static struct modcmd *
3685 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
3687 if (min_level > 0) {
3689 sprintf(buf, "%u", min_level);
3690 if (must_be_qualified) {
3691 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
3693 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
3695 } else if (min_level == 0) {
3696 if (must_be_qualified) {
3697 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3699 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3702 if (must_be_qualified) {
3703 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
3705 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
3711 nickserv_db_cleanup(void)
3713 unreg_del_user_func(nickserv_remove_user);
3714 userList_clean(&curr_helpers);
3715 policer_params_delete(nickserv_conf.auth_policer_params);
3716 dict_delete(nickserv_handle_dict);
3717 dict_delete(nickserv_nick_dict);
3718 dict_delete(nickserv_opt_dict);
3719 dict_delete(nickserv_allow_auth_dict);
3720 dict_delete(nickserv_email_dict);
3721 dict_delete(nickserv_id_dict);
3722 dict_delete(nickserv_conf.weak_password_dict);
3723 free(auth_func_list);
3724 free(unreg_func_list);
3726 free(allowauth_func_list);
3727 free(handle_merge_func_list);
3728 free(failpw_func_list);
3729 if (nickserv_conf.valid_handle_regex_set)
3730 regfree(&nickserv_conf.valid_handle_regex);
3731 if (nickserv_conf.valid_nick_regex_set)
3732 regfree(&nickserv_conf.valid_nick_regex);
3736 init_nickserv(const char *nick)
3739 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
3740 reg_new_user_func(handle_new_user);
3741 reg_nick_change_func(handle_nick_change);
3742 reg_del_user_func(nickserv_remove_user);
3743 reg_account_func(handle_account);
3745 /* set up handle_inverse_flags */
3746 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
3747 for (i=0; handle_flags[i]; i++) {
3748 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
3749 flag_access_levels[i] = 0;
3752 conf_register_reload(nickserv_conf_read);
3753 nickserv_opt_dict = dict_new();
3754 nickserv_email_dict = dict_new();
3755 dict_set_free_keys(nickserv_email_dict, free);
3756 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
3758 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
3759 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
3760 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
3761 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
3762 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
3763 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
3764 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
3765 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
3766 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
3767 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
3768 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
3769 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
3770 nickserv_define_func("SET", cmd_set, -1, 1, 0);
3771 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
3772 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
3773 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
3774 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
3775 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
3776 nickserv_define_func("MERGE", cmd_merge, 0, 1, 0);
3777 if (!nickserv_conf.disable_nicks) {
3778 /* nick management commands */
3779 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
3780 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
3781 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
3782 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
3783 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
3784 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
3786 if (nickserv_conf.email_enabled) {
3787 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
3788 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
3789 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
3790 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
3791 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
3793 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
3794 /* miscellaneous commands */
3795 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
3796 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
3797 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
3798 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
3799 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
3801 dict_insert(nickserv_opt_dict, "INFO", opt_info);
3802 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
3803 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
3804 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
3805 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
3806 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
3807 dict_insert(nickserv_opt_dict, "PASS", opt_password);
3808 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
3809 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
3810 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
3811 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
3812 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
3813 if (nickserv_conf.titlehost_suffix) {
3814 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
3815 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
3817 dict_insert(nickserv_opt_dict, "ANNOUNCEMENTS", opt_announcements);
3818 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
3819 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
3821 nickserv_handle_dict = dict_new();
3822 dict_set_free_keys(nickserv_handle_dict, free);
3823 dict_set_free_data(nickserv_handle_dict, free_handle_info);
3825 nickserv_id_dict = dict_new();
3826 dict_set_free_keys(nickserv_id_dict, free);
3828 nickserv_nick_dict = dict_new();
3829 dict_set_free_data(nickserv_nick_dict, free_nick_info);
3831 nickserv_allow_auth_dict = dict_new();
3833 userList_init(&curr_helpers);
3836 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
3837 nickserv = AddService(nick, modes ? modes : NULL, "Nick Services", NULL);
3838 nickserv_service = service_register(nickserv);
3840 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
3841 reg_exit_func(nickserv_db_cleanup);
3842 if(nickserv_conf.handle_expire_frequency)
3843 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3844 message_register_table(msgtab);