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_FLAG_LEVELS "flag_levels"
51 #define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq"
52 #define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq"
53 #define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay"
54 #define KEY_ACCOUNT_EXPIRE_DELAY "account_expire_delay"
55 #define KEY_NOCHAN_HANDLE_EXPIRE_DELAY "nochan_handle_expire_delay"
56 #define KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY "nochan_account_expire_delay"
57 #define KEY_DICT_FILE "dict_file"
58 #define KEY_NICK "nick"
59 #define KEY_LANGUAGE "language"
60 #define KEY_AUTOGAG_ENABLED "autogag_enabled"
61 #define KEY_AUTOGAG_DURATION "autogag_duration"
62 #define KEY_AUTH_POLICER "auth_policer"
63 #define KEY_EMAIL_VISIBLE_LEVEL "email_visible_level"
64 #define KEY_EMAIL_ENABLED "email_enabled"
65 #define KEY_EMAIL_REQUIRED "email_required"
66 #define KEY_COOKIE_TIMEOUT "cookie_timeout"
67 #define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
68 #define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
71 #define KEY_PASSWD "passwd"
72 #define KEY_NICKS "nicks"
73 #define KEY_MASKS "masks"
74 #define KEY_OPSERV_LEVEL "opserv_level"
75 #define KEY_FLAGS "flags"
76 #define KEY_REGISTER_ON "register"
77 #define KEY_LAST_SEEN "lastseen"
78 #define KEY_INFO "info"
79 #define KEY_USERLIST_STYLE "user_style"
80 #define KEY_SCREEN_WIDTH "screen_width"
81 #define KEY_LAST_AUTHED_HOST "last_authed_host"
82 #define KEY_LAST_QUIT_HOST "last_quit_host"
83 #define KEY_EMAIL_ADDR "email_addr"
84 #define KEY_COOKIE "cookie"
85 #define KEY_COOKIE_DATA "data"
86 #define KEY_COOKIE_TYPE "type"
87 #define KEY_COOKIE_EXPIRES "expires"
88 #define KEY_ACTIVATION "activation"
89 #define KEY_PASSWORD_CHANGE "password change"
90 #define KEY_EMAIL_CHANGE "email change"
91 #define KEY_ALLOWAUTH "allowauth"
92 #define KEY_EPITHET "epithet"
93 #define KEY_TABLE_WIDTH "table_width"
94 #define KEY_ANNOUNCEMENTS "announcements"
95 #define KEY_MAXLOGINS "maxlogins"
97 #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
99 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
100 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
101 typedef OPTION_FUNC(option_func_t);
103 DEFINE_LIST(handle_info_list, struct handle_info*);
105 #define NICKSERV_MIN_PARMS(N) do { \
107 reply("MSG_MISSING_PARAMS", argv[0]); \
108 svccmd_send_help(user, nickserv, cmd); \
112 struct userNode *nickserv;
113 struct userList curr_helpers;
114 const char *handle_flags = HANDLE_FLAGS;
116 static struct module *nickserv_module;
117 static struct service *nickserv_service;
118 static struct log_type *NS_LOG;
119 static dict_t nickserv_handle_dict; /* contains struct handle_info* */
120 static dict_t nickserv_id_dict; /* contains struct handle_info* */
121 static dict_t nickserv_nick_dict; /* contains struct nick_info* */
122 static dict_t nickserv_opt_dict; /* contains option_func_t* */
123 static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
124 static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
125 static char handle_inverse_flags[256];
126 static unsigned int flag_access_levels[32];
127 static const struct message_entry msgtab[] = {
128 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
129 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
130 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
131 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
132 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
133 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
134 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
135 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
136 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
137 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
138 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
139 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
140 { "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." },
141 { "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." },
142 { "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." },
143 { "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." },
144 { "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." },
145 { "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." },
146 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
147 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
148 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
149 { "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." },
150 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
151 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
152 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
153 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
154 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
155 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
156 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
157 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
158 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
159 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
160 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
161 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
162 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
163 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
164 { "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)" },
165 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
166 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
167 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
168 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
169 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
170 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
171 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
172 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
173 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
174 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
175 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
176 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
177 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
178 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
179 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
180 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
181 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
182 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
183 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
184 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
185 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
186 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
187 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
188 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
189 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
190 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
191 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
192 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
193 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
194 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
195 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
196 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
197 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
198 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
199 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
200 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
201 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
202 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
203 { "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)." },
204 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
205 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
206 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
207 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
208 { "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." },
209 { "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." },
210 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
211 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
212 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
213 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
214 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
215 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
216 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
217 { "NSMSG_PASS_SUCCESS", "Password changed." },
218 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
219 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
220 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
221 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
222 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
223 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
224 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
225 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
226 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
227 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
228 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
229 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
230 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
231 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
232 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
233 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
234 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
235 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
236 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
237 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
238 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
239 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
240 { "NSMSG_NO_ACCESS", "Access denied." },
241 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
242 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
243 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
244 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
245 { "NSMSG_DB_MERGED", "$N merged DB from %s (in "FMT_TIME_T".%03lu seconds)." },
246 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
247 { "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." },
248 { "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." },
249 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
250 { "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." },
251 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
252 { "NSMSG_SEARCH_MATCH", "Match: %s" },
253 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
254 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
255 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
256 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
257 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
258 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
259 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
260 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
261 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
262 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
263 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
264 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
265 { "NSMSG_INVALID_ANNOUNCE", "$b%s$b is an invalid announcements value." },
266 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
267 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
268 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
269 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
270 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
271 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
272 { "NSMSG_SET_ANNOUNCEMENTS", "$bANNOUNCEMENTS: $b%s" },
273 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
274 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
275 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
276 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
277 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
278 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
279 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
280 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
281 { "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\nIf you did NOT request this account, you do not need to do anything. Please contact the %1$s staff if you have questions." },
282 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
283 { "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." },
284 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
285 { "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." },
286 { "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." },
287 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
288 { "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." },
289 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
290 { "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." },
291 { "CHECKPASS_YES", "Yes." },
292 { "CHECKPASS_NO", "No." },
296 enum reclaim_action {
302 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
303 static void nickserv_reclaim_p(void *data);
306 unsigned int disable_nicks : 1;
307 unsigned int valid_handle_regex_set : 1;
308 unsigned int valid_nick_regex_set : 1;
309 unsigned int autogag_enabled : 1;
310 unsigned int email_enabled : 1;
311 unsigned int email_required : 1;
312 unsigned int default_hostmask : 1;
313 unsigned int warn_nick_owned : 1;
314 unsigned int warn_clone_auth : 1;
315 unsigned long nicks_per_handle;
316 unsigned long password_min_length;
317 unsigned long password_min_digits;
318 unsigned long password_min_upper;
319 unsigned long password_min_lower;
320 unsigned long db_backup_frequency;
321 unsigned long handle_expire_frequency;
322 unsigned long autogag_duration;
323 unsigned long email_visible_level;
324 unsigned long cookie_timeout;
325 unsigned long handle_expire_delay;
326 unsigned long nochan_handle_expire_delay;
327 unsigned long modoper_level;
328 unsigned long set_epithet_level;
329 unsigned long handles_per_email;
330 unsigned long email_search_level;
331 const char *network_name;
332 const char *titlehost_suffix;
333 regex_t valid_handle_regex;
334 regex_t valid_nick_regex;
335 dict_t weak_password_dict;
336 struct policer_params *auth_policer_params;
337 enum reclaim_action reclaim_action;
338 enum reclaim_action auto_reclaim_action;
339 unsigned long auto_reclaim_delay;
340 unsigned char default_maxlogins;
341 unsigned char hard_maxlogins;
344 /* We have 2^32 unique account IDs to use. */
345 unsigned long int highest_id = 0;
348 canonicalize_hostmask(char *mask)
350 char *out = mask, *temp;
351 if ((temp = strchr(mask, '!'))) {
353 while (*temp) *out++ = *temp++;
359 static struct handle_info *
360 register_handle(const char *handle, const char *passwd, UNUSED_ARG(unsigned long id))
362 struct handle_info *hi;
364 #ifdef WITH_PROTOCOL_BAHAMUT
365 char id_base64[IDLEN + 1];
368 /* Assign a unique account ID to the account; note that 0 is
369 an invalid account ID. 1 is therefore the first account ID. */
371 id = 1 + highest_id++;
373 /* Note: highest_id is and must always be the highest ID. */
374 if(id > highest_id) {
378 inttobase64(id_base64, id, IDLEN);
380 /* Make sure an account with the same ID doesn't exist. If a
381 duplicate is found, log some details and assign a new one.
382 This should be impossible, but it never hurts to expect it. */
383 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
384 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
390 hi = calloc(1, sizeof(*hi));
391 hi->userlist_style = HI_DEFAULT_STYLE;
392 hi->announcements = '?';
393 hi->handle = strdup(handle);
394 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
396 dict_insert(nickserv_handle_dict, hi->handle, hi);
398 #ifdef WITH_PROTOCOL_BAHAMUT
400 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
407 register_nick(const char *nick, struct handle_info *owner)
409 struct nick_info *ni;
410 ni = malloc(sizeof(struct nick_info));
411 safestrncpy(ni->nick, nick, sizeof(ni->nick));
413 ni->next = owner->nicks;
415 dict_insert(nickserv_nick_dict, ni->nick, ni);
419 free_nick_info(void *vni)
421 struct nick_info *ni = vni;
426 delete_nick(struct nick_info *ni)
428 struct nick_info *last, *next;
429 struct userNode *user;
430 /* Check to see if we should mark a user as unregistered. */
431 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
432 user->modes &= ~FLAGS_REGNICK;
435 /* Remove ni from the nick_info linked list. */
436 if (ni == ni->owner->nicks) {
437 ni->owner->nicks = ni->next;
439 last = ni->owner->nicks;
445 last->next = next->next;
447 dict_remove(nickserv_nick_dict, ni->nick);
450 static unreg_func_t *unreg_func_list;
451 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
454 reg_unreg_func(unreg_func_t func)
456 if (unreg_func_used == unreg_func_size) {
457 if (unreg_func_size) {
458 unreg_func_size <<= 1;
459 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
462 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
465 unreg_func_list[unreg_func_used++] = func;
469 nickserv_free_cookie(void *data)
471 struct handle_cookie *cookie = data;
472 if (cookie->hi) cookie->hi->cookie = NULL;
473 if (cookie->data) free(cookie->data);
478 free_handle_info(void *vhi)
480 struct handle_info *hi = vhi;
482 #ifdef WITH_PROTOCOL_BAHAMUT
485 inttobase64(id, hi->id, IDLEN);
486 dict_remove(nickserv_id_dict, id);
489 free_string_list(hi->masks);
493 delete_nick(hi->nicks);
497 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
498 nickserv_free_cookie(hi->cookie);
500 if (hi->email_addr) {
501 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
502 handle_info_list_remove(hil, hi);
504 dict_remove(nickserv_email_dict, hi->email_addr);
509 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
512 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
516 for (n=0; n<unreg_func_used; n++)
517 unreg_func_list[n](notify, hi);
519 set_user_handle_info(hi->users, NULL, 0);
521 if (nickserv_conf.disable_nicks)
522 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
524 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
526 dict_remove(nickserv_handle_dict, hi->handle);
530 get_handle_info(const char *handle)
532 return dict_find(nickserv_handle_dict, handle, 0);
536 get_nick_info(const char *nick)
538 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
542 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
547 for (nn=0; nn<channel->members.used; ++nn) {
548 mn = channel->members.list[nn];
549 if ((mn->user != except) && (mn->user->handle_info == handle))
556 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
557 if (!user->handle_info) {
559 send_message(user, bot, "MSG_AUTHENTICATE");
563 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
565 send_message(user, bot, "NSMSG_NO_ACCESS");
569 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
571 send_message(user, bot, "MSG_OPER_SUSPENDED");
575 if (user->handle_info->opserv_level < min_level) {
577 send_message(user, bot, "NSMSG_NO_ACCESS");
585 is_valid_handle(const char *handle)
587 struct userNode *user;
588 /* cant register a juped nick/service nick as handle, to prevent confusion */
589 user = GetUserH(handle);
590 if (user && IsLocal(user))
592 /* check against maximum length */
593 if (strlen(handle) > NICKSERV_HANDLE_LEN)
595 /* for consistency, only allow account names that could be nicks */
596 if (!is_valid_nick(handle))
598 /* disallow account names that look like bad words */
599 if (opserv_bad_channel(handle))
601 /* test either regex or containing all valid chars */
602 if (nickserv_conf.valid_handle_regex_set) {
603 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
606 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
607 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
611 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
616 is_registerable_nick(const char *nick)
618 /* make sure it could be used as an account name */
619 if (!is_valid_handle(nick))
622 if (strlen(nick) > NICKLEN)
624 /* test either regex or as valid handle */
625 if (nickserv_conf.valid_nick_regex_set) {
626 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
629 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
630 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
638 is_valid_email_addr(const char *email)
640 return strchr(email, '@') != NULL;
644 visible_email_addr(struct userNode *user, struct handle_info *hi)
646 if (hi->email_addr) {
647 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
648 return hi->email_addr;
658 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
660 struct handle_info *hi;
661 struct userNode *target;
665 if (!(hi = get_handle_info(++name))) {
666 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
671 if (!(target = GetUserH(name))) {
672 send_message(user, service, "MSG_NICK_UNKNOWN", name);
675 if (IsLocal(target)) {
676 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
679 if (!(hi = target->handle_info)) {
680 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
688 oper_outranks(struct userNode *user, struct handle_info *hi) {
689 if (user->handle_info->opserv_level > hi->opserv_level)
691 if (user->handle_info->opserv_level == hi->opserv_level) {
692 if ((user->handle_info->opserv_level == 1000)
693 || (user->handle_info == hi)
694 || ((user->handle_info->opserv_level == 0)
695 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
696 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
700 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
704 static struct handle_info *
705 get_victim_oper(struct userNode *user, const char *target)
707 struct handle_info *hi;
708 if (!(hi = smart_get_handle_info(nickserv, user, target)))
710 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
711 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
714 return oper_outranks(user, hi) ? hi : NULL;
718 valid_user_for(struct userNode *user, struct handle_info *hi)
722 /* If no hostmasks on the account, allow it. */
723 if (!hi->masks->used)
725 /* If any hostmask matches, allow it. */
726 for (ii=0; ii<hi->masks->used; ii++)
727 if (user_matches_glob(user, hi->masks->list[ii], 0))
729 /* If they are allowauthed to this account, allow it (removing the aa). */
730 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
731 dict_remove(nickserv_allow_auth_dict, user->nick);
734 /* The user is not allowed to use this account. */
739 is_secure_password(const char *handle, const char *pass, struct userNode *user)
742 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
744 if (len < nickserv_conf.password_min_length) {
746 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
749 if (!irccasecmp(pass, handle)) {
751 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
754 dict_find(nickserv_conf.weak_password_dict, pass, &i);
757 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
760 for (i=0; i<len; i++) {
761 if (isdigit(pass[i]))
763 if (isupper(pass[i]))
765 if (islower(pass[i]))
768 if ((cnt_lower < nickserv_conf.password_min_lower)
769 || (cnt_upper < nickserv_conf.password_min_upper)
770 || (cnt_digits < nickserv_conf.password_min_digits)) {
772 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
778 static auth_func_t *auth_func_list;
779 static unsigned int auth_func_size = 0, auth_func_used = 0;
782 reg_auth_func(auth_func_t func)
784 if (auth_func_used == auth_func_size) {
785 if (auth_func_size) {
786 auth_func_size <<= 1;
787 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
790 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
793 auth_func_list[auth_func_used++] = func;
796 static handle_rename_func_t *rf_list;
797 static unsigned int rf_list_size, rf_list_used;
800 reg_handle_rename_func(handle_rename_func_t func)
802 if (rf_list_used == rf_list_size) {
805 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
808 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
811 rf_list[rf_list_used++] = func;
815 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
818 struct handle_info *old_info;
820 /* This can happen if somebody uses COOKIE while authed, or if
821 * they re-auth to their current handle (which is silly, but users
823 if (user->handle_info == hi)
826 if (user->handle_info) {
827 struct userNode *other;
830 userList_remove(&curr_helpers, user);
832 /* remove from next_authed linked list */
833 if (user->handle_info->users == user) {
834 user->handle_info->users = user->next_authed;
836 for (other = user->handle_info->users;
837 other->next_authed != user;
838 other = other->next_authed) ;
839 other->next_authed = user->next_authed;
841 /* if nobody left on old handle, and they're not an oper, remove !god */
842 if (!user->handle_info->users && !user->handle_info->opserv_level)
843 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
844 /* record them as being last seen at this time */
845 user->handle_info->lastseen = now;
846 /* and record their hostmask */
847 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
849 old_info = user->handle_info;
850 user->handle_info = hi;
851 if (hi && !hi->users && !hi->opserv_level)
852 HANDLE_CLEAR_FLAG(hi, HELPING);
853 for (n=0; n<auth_func_used; n++)
854 auth_func_list[n](user, old_info);
856 struct nick_info *ni;
858 HANDLE_CLEAR_FLAG(hi, FROZEN);
859 if (nickserv_conf.warn_clone_auth) {
860 struct userNode *other;
861 for (other = hi->users; other; other = other->next_authed)
862 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
864 user->next_authed = hi->users;
868 userList_append(&curr_helpers, user);
871 #ifdef WITH_PROTOCOL_BAHAMUT
872 /* Stamp users with their account ID. */
874 inttobase64(id, hi->id, IDLEN);
875 #elif WITH_PROTOCOL_P10
876 /* Stamp users with their account name. */
877 char *id = hi->handle;
879 const char *id = "???";
881 if (!nickserv_conf.disable_nicks) {
882 struct nick_info *ni;
883 for (ni = hi->nicks; ni; ni = ni->next) {
884 if (!irccasecmp(user->nick, ni->nick)) {
885 user->modes |= FLAGS_REGNICK;
893 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
894 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
896 /* We cannot clear the user's account ID, unfortunately. */
897 user->next_authed = NULL;
901 static struct handle_info*
902 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
904 struct handle_info *hi;
905 struct nick_info *ni;
906 char crypted[MD5_CRYPT_LENGTH];
908 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
909 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
913 if (!is_secure_password(handle, passwd, user))
916 cryptpass(passwd, crypted);
917 hi = register_handle(handle, crypted, 0);
918 hi->masks = alloc_string_list(1);
920 hi->language = lang_C;
921 hi->registered = now;
923 hi->flags = HI_DEFAULT_FLAGS;
924 if (settee && !no_auth)
925 set_user_handle_info(settee, hi, 1);
928 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
929 else if (nickserv_conf.disable_nicks)
930 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
931 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
932 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
934 register_nick(user->nick, hi);
935 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
937 if (settee && (user != settee))
938 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
943 nickserv_bake_cookie(struct handle_cookie *cookie)
945 cookie->hi->cookie = cookie;
946 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
950 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
952 struct handle_cookie *cookie;
953 char subject[128], body[4096], *misc;
954 const char *netname, *fmt;
958 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
962 cookie = calloc(1, sizeof(*cookie));
965 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
966 cookie->expires = now + nickserv_conf.cookie_timeout;
967 inttobase64(cookie->cookie, rand(), 5);
968 inttobase64(cookie->cookie+5, rand(), 5);
970 netname = nickserv_conf.network_name;
973 switch (cookie->type) {
975 hi->passwd[0] = 0; /* invalidate password */
976 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
977 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
978 snprintf(subject, sizeof(subject), fmt, netname);
979 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
980 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
983 case PASSWORD_CHANGE:
984 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
985 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
986 snprintf(subject, sizeof(subject), fmt, netname);
987 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
988 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
991 misc = hi->email_addr;
992 hi->email_addr = cookie->data;
994 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
995 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
996 snprintf(subject, sizeof(subject), fmt, netname);
997 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
998 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
999 sendmail(nickserv, hi, subject, body, 1);
1000 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1001 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1003 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1004 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1005 snprintf(subject, sizeof(subject), fmt, netname);
1006 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1007 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1008 sendmail(nickserv, hi, subject, body, 1);
1011 hi->email_addr = misc;
1014 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1015 snprintf(subject, sizeof(subject), fmt, netname);
1016 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1017 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1018 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1021 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1025 sendmail(nickserv, hi, subject, body, first_time);
1026 nickserv_bake_cookie(cookie);
1030 nickserv_eat_cookie(struct handle_cookie *cookie)
1032 cookie->hi->cookie = NULL;
1033 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1034 nickserv_free_cookie(cookie);
1038 nickserv_free_email_addr(void *data)
1040 handle_info_list_clean(data);
1045 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1047 struct handle_info_list *hil;
1048 /* Remove from old handle_info_list ... */
1049 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1050 handle_info_list_remove(hil, hi);
1051 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1052 hi->email_addr = NULL;
1054 /* Add to the new list.. */
1055 if (new_email_addr) {
1056 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1057 hil = calloc(1, sizeof(*hil));
1058 hil->tag = strdup(new_email_addr);
1059 handle_info_list_init(hil);
1060 dict_insert(nickserv_email_dict, hil->tag, hil);
1062 handle_info_list_append(hil, hi);
1063 hi->email_addr = hil->tag;
1067 static NICKSERV_FUNC(cmd_register)
1069 struct handle_info *hi;
1070 const char *email_addr, *password;
1073 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1074 /* Require the first handle registered to belong to someone +o. */
1075 reply("NSMSG_REQUIRE_OPER");
1079 if (user->handle_info) {
1080 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1084 if (IsStamped(user)) {
1085 /* Unauthenticated users might still have been stamped
1086 previously and could therefore have a hidden host;
1087 do not allow them to register a new account. */
1088 reply("NSMSG_STAMPED_REGISTER");
1092 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1094 if (!is_valid_handle(argv[1])) {
1095 reply("NSMSG_BAD_HANDLE", argv[1]);
1099 if ((argc >= 4) && nickserv_conf.email_enabled) {
1100 struct handle_info_list *hil;
1103 /* Remember email address. */
1104 email_addr = argv[3];
1106 /* Check that the email address looks valid.. */
1107 if (!is_valid_email_addr(email_addr)) {
1108 reply("NSMSG_BAD_EMAIL_ADDR");
1112 /* .. and that we are allowed to send to it. */
1113 if ((str = sendmail_prohibited_address(email_addr))) {
1114 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1118 /* If we do email verify, make sure we don't spam the address. */
1119 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1121 for (nn=0; nn<hil->used; nn++) {
1122 if (hil->list[nn]->cookie) {
1123 reply("NSMSG_EMAIL_UNACTIVATED");
1127 if (hil->used >= nickserv_conf.handles_per_email) {
1128 reply("NSMSG_EMAIL_OVERUSED");
1141 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1143 /* Add any masks they should get. */
1144 if (nickserv_conf.default_hostmask) {
1145 string_list_append(hi->masks, strdup("*@*"));
1147 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1148 if (user->ip.s_addr && user->hostname[strspn(user->hostname, "0123456789.")])
1149 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1152 /* If they're the first to register, give them level 1000. */
1153 if (dict_size(nickserv_handle_dict) == 1) {
1154 hi->opserv_level = 1000;
1155 reply("NSMSG_ROOT_HANDLE", argv[1]);
1158 /* Set their email address. */
1160 nickserv_set_email_addr(hi, email_addr);
1162 /* If they need to do email verification, tell them. */
1164 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1169 static NICKSERV_FUNC(cmd_oregister)
1172 struct userNode *settee;
1173 struct handle_info *hi;
1175 NICKSERV_MIN_PARMS(4);
1177 if (!is_valid_handle(argv[1])) {
1178 reply("NSMSG_BAD_HANDLE", argv[1]);
1182 if (strchr(argv[3], '@')) {
1183 mask = canonicalize_hostmask(strdup(argv[3]));
1185 settee = GetUserH(argv[4]);
1187 reply("MSG_NICK_UNKNOWN", argv[4]);
1194 } else if ((settee = GetUserH(argv[3]))) {
1195 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1197 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1200 if (settee && settee->handle_info) {
1201 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1205 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1209 string_list_append(hi->masks, mask);
1213 static NICKSERV_FUNC(cmd_handleinfo)
1216 unsigned int i, pos=0, herelen;
1217 struct userNode *target, *next_un;
1218 struct handle_info *hi;
1219 const char *nsmsg_none;
1222 if (!(hi = user->handle_info)) {
1223 reply("NSMSG_MUST_AUTH");
1226 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1230 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1231 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1232 #ifdef WITH_PROTOCOL_BAHAMUT
1233 reply("NSMSG_HANDLEINFO_ID", hi->id);
1235 reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi->registered));
1238 intervalString(buff, now - hi->lastseen, user->handle_info);
1239 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1241 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1244 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1245 if (HANDLE_FLAGGED(hi, FROZEN))
1246 reply("NSMSG_HANDLEINFO_VACATION");
1248 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1249 struct do_not_register *dnr;
1250 if ((dnr = chanserv_is_dnr(NULL, hi)))
1251 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1252 if (!oper_outranks(user, hi))
1254 } else if (hi != user->handle_info)
1257 if (nickserv_conf.email_enabled)
1258 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1262 switch (hi->cookie->type) {
1263 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1264 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1265 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1266 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1267 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1273 unsigned long flen = 1;
1274 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1276 for (i=0, flen=1; handle_flags[i]; i++)
1277 if (hi->flags & 1 << i)
1278 flags[flen++] = handle_flags[i];
1280 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1282 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1285 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1286 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1287 || (hi->opserv_level > 0)) {
1288 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1291 if (hi->last_quit_host[0])
1292 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1294 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1296 if (nickserv_conf.disable_nicks) {
1297 /* nicks disabled; don't show anything about registered nicks */
1298 } else if (hi->nicks) {
1299 struct nick_info *ni, *next_ni;
1300 for (ni = hi->nicks; ni; ni = next_ni) {
1301 herelen = strlen(ni->nick);
1302 if (pos + herelen + 1 > ArrayLength(buff)) {
1304 goto print_nicks_buff;
1308 memcpy(buff+pos, ni->nick, herelen);
1309 pos += herelen; buff[pos++] = ' ';
1313 reply("NSMSG_HANDLEINFO_NICKS", buff);
1318 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1321 if (hi->masks->used) {
1322 for (i=0; i < hi->masks->used; i++) {
1323 herelen = strlen(hi->masks->list[i]);
1324 if (pos + herelen + 1 > ArrayLength(buff)) {
1326 goto print_mask_buff;
1328 memcpy(buff+pos, hi->masks->list[i], herelen);
1329 pos += herelen; buff[pos++] = ' ';
1330 if (i+1 == hi->masks->used) {
1333 reply("NSMSG_HANDLEINFO_MASKS", buff);
1338 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1342 struct userData *channel, *next;
1345 for (channel = hi->channels; channel; channel = next) {
1346 next = channel->u_next;
1347 name = channel->channel->channel->name;
1348 herelen = strlen(name);
1349 if (pos + herelen + 7 > ArrayLength(buff)) {
1351 goto print_chans_buff;
1353 if (IsUserSuspended(channel))
1355 pos += sprintf(buff+pos, "%d:%s ", channel->access, name);
1359 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1364 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1367 for (target = hi->users; target; target = next_un) {
1368 herelen = strlen(target->nick);
1369 if (pos + herelen + 1 > ArrayLength(buff)) {
1371 goto print_cnick_buff;
1373 next_un = target->next_authed;
1375 memcpy(buff+pos, target->nick, herelen);
1376 pos += herelen; buff[pos++] = ' ';
1380 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1388 static NICKSERV_FUNC(cmd_userinfo)
1390 struct userNode *target;
1392 NICKSERV_MIN_PARMS(2);
1393 if (!(target = GetUserH(argv[1]))) {
1394 reply("MSG_NICK_UNKNOWN", argv[1]);
1397 if (target->handle_info)
1398 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1400 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1404 static NICKSERV_FUNC(cmd_nickinfo)
1406 struct nick_info *ni;
1408 NICKSERV_MIN_PARMS(2);
1409 if (!(ni = get_nick_info(argv[1]))) {
1410 reply("MSG_NICK_UNKNOWN", argv[1]);
1413 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1417 static NICKSERV_FUNC(cmd_rename_handle)
1419 struct handle_info *hi;
1420 char msgbuf[MAXLEN], *old_handle;
1423 NICKSERV_MIN_PARMS(3);
1424 if (!(hi = get_victim_oper(user, argv[1])))
1426 if (!is_valid_handle(argv[2])) {
1427 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1430 if (get_handle_info(argv[2])) {
1431 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1435 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1436 hi->handle = strdup(argv[2]);
1437 dict_insert(nickserv_handle_dict, hi->handle, hi);
1438 for (nn=0; nn<rf_list_used; nn++)
1439 rf_list[nn](hi, old_handle);
1440 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1441 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1442 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1447 static failpw_func_t *failpw_func_list;
1448 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1451 reg_failpw_func(failpw_func_t func)
1453 if (failpw_func_used == failpw_func_size) {
1454 if (failpw_func_size) {
1455 failpw_func_size <<= 1;
1456 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1458 failpw_func_size = 8;
1459 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1462 failpw_func_list[failpw_func_used++] = func;
1465 static NICKSERV_FUNC(cmd_auth)
1467 int pw_arg, used, maxlogins;
1468 struct handle_info *hi;
1470 struct userNode *other;
1472 if (user->handle_info) {
1473 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1476 if (IsStamped(user)) {
1477 /* Unauthenticated users might still have been stamped
1478 previously and could therefore have a hidden host;
1479 do not allow them to authenticate. */
1480 reply("NSMSG_STAMPED_AUTH");
1484 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1486 } else if (argc == 2) {
1487 if (nickserv_conf.disable_nicks) {
1488 if (!(hi = get_handle_info(user->nick))) {
1489 reply("NSMSG_HANDLE_NOT_FOUND");
1493 /* try to look up their handle from their nick */
1494 struct nick_info *ni;
1495 ni = get_nick_info(user->nick);
1497 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1504 reply("MSG_MISSING_PARAMS", argv[0]);
1505 svccmd_send_help(user, nickserv, cmd);
1509 reply("NSMSG_HANDLE_NOT_FOUND");
1512 /* Responses from here on look up the language used by the handle they asked about. */
1513 passwd = argv[pw_arg];
1514 if (!valid_user_for(user, hi)) {
1515 if (hi->email_addr && nickserv_conf.email_enabled)
1516 send_message_type(4, user, cmd->parent->bot,
1517 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1520 send_message_type(4, user, cmd->parent->bot,
1521 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1523 argv[pw_arg] = "BADMASK";
1526 if (!checkpass(passwd, hi->passwd)) {
1528 send_message_type(4, user, cmd->parent->bot,
1529 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1530 argv[pw_arg] = "BADPASS";
1531 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1532 if (nickserv_conf.autogag_enabled) {
1533 if (!user->auth_policer.params) {
1534 user->auth_policer.last_req = now;
1535 user->auth_policer.params = nickserv_conf.auth_policer_params;
1537 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1539 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1540 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1541 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1543 argv[pw_arg] = "GAGGED";
1548 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1549 send_message_type(4, user, cmd->parent->bot,
1550 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1551 argv[pw_arg] = "SUSPENDED";
1554 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1555 for (used = 0, other = hi->users; other; other = other->next_authed) {
1556 if (++used >= maxlogins) {
1557 send_message_type(4, user, cmd->parent->bot,
1558 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1560 argv[pw_arg] = "MAXLOGINS";
1565 set_user_handle_info(user, hi, 1);
1566 if (nickserv_conf.email_required && !hi->email_addr)
1567 reply("NSMSG_PLEASE_SET_EMAIL");
1568 if (!is_secure_password(hi->handle, passwd, NULL))
1569 reply("NSMSG_WEAK_PASSWORD");
1570 if (hi->passwd[0] != '$')
1571 cryptpass(passwd, hi->passwd);
1572 reply("NSMSG_AUTH_SUCCESS");
1573 argv[pw_arg] = "****";
1577 static allowauth_func_t *allowauth_func_list;
1578 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1581 reg_allowauth_func(allowauth_func_t func)
1583 if (allowauth_func_used == allowauth_func_size) {
1584 if (allowauth_func_size) {
1585 allowauth_func_size <<= 1;
1586 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1588 allowauth_func_size = 8;
1589 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1592 allowauth_func_list[allowauth_func_used++] = func;
1595 static NICKSERV_FUNC(cmd_allowauth)
1597 struct userNode *target;
1598 struct handle_info *hi;
1601 NICKSERV_MIN_PARMS(2);
1602 if (!(target = GetUserH(argv[1]))) {
1603 reply("MSG_NICK_UNKNOWN", argv[1]);
1606 if (target->handle_info) {
1607 reply("NSMSG_USER_PREV_AUTH", target->nick);
1610 if (IsStamped(target)) {
1611 /* Unauthenticated users might still have been stamped
1612 previously and could therefore have a hidden host;
1613 do not allow them to authenticate to an account. */
1614 send_message(target, nickserv, "NSMSG_USER_PREV_STAMP", target->nick);
1619 else if (!(hi = get_handle_info(argv[2]))) {
1620 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1624 if (hi->opserv_level > user->handle_info->opserv_level) {
1625 reply("MSG_USER_OUTRANKED", hi->handle);
1628 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1629 || (hi->opserv_level > 0))
1630 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1631 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1634 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1635 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1636 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1637 if (nickserv_conf.email_enabled)
1638 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1640 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1641 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1643 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1645 for (n=0; n<allowauth_func_used; n++)
1646 allowauth_func_list[n](user, target, hi);
1650 static NICKSERV_FUNC(cmd_authcookie)
1652 struct handle_info *hi;
1654 NICKSERV_MIN_PARMS(2);
1655 if (user->handle_info) {
1656 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1659 if (IsStamped(user)) {
1660 /* Unauthenticated users might still have been stamped
1661 previously and could therefore have a hidden host;
1662 do not allow them to authenticate to an account. */
1663 reply("NSMSG_STAMPED_AUTHCOOKIE");
1666 if (!(hi = get_handle_info(argv[1]))) {
1667 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1670 if (!hi->email_addr) {
1671 reply("MSG_SET_EMAIL_ADDR");
1674 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1678 static NICKSERV_FUNC(cmd_delcookie)
1680 struct handle_info *hi;
1682 hi = user->handle_info;
1684 reply("NSMSG_NO_COOKIE");
1687 switch (hi->cookie->type) {
1690 reply("NSMSG_MUST_TIME_OUT");
1693 nickserv_eat_cookie(hi->cookie);
1694 reply("NSMSG_ATE_COOKIE");
1700 static NICKSERV_FUNC(cmd_resetpass)
1702 struct handle_info *hi;
1703 char crypted[MD5_CRYPT_LENGTH];
1705 NICKSERV_MIN_PARMS(3);
1706 if (user->handle_info) {
1707 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1710 if (IsStamped(user)) {
1711 /* Unauthenticated users might still have been stamped
1712 previously and could therefore have a hidden host;
1713 do not allow them to activate an account. */
1714 reply("NSMSG_STAMPED_RESETPASS");
1717 if (!(hi = get_handle_info(argv[1]))) {
1718 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1721 if (!hi->email_addr) {
1722 reply("MSG_SET_EMAIL_ADDR");
1725 cryptpass(argv[2], crypted);
1727 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1731 static NICKSERV_FUNC(cmd_cookie)
1733 struct handle_info *hi;
1736 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1739 NICKSERV_MIN_PARMS(3);
1740 if (!(hi = get_handle_info(argv[1]))) {
1741 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1747 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1748 reply("NSMSG_HANDLE_SUSPENDED");
1753 reply("NSMSG_NO_COOKIE");
1757 /* Check validity of operation before comparing cookie to
1758 * prohibit guessing by authed users. */
1759 if (user->handle_info
1760 && (hi->cookie->type != EMAIL_CHANGE)
1761 && (hi->cookie->type != PASSWORD_CHANGE)) {
1762 reply("NSMSG_CANNOT_COOKIE");
1766 if (strcmp(cookie, hi->cookie->cookie)) {
1767 reply("NSMSG_BAD_COOKIE");
1771 switch (hi->cookie->type) {
1773 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1774 set_user_handle_info(user, hi, 1);
1775 reply("NSMSG_HANDLE_ACTIVATED");
1777 case PASSWORD_CHANGE:
1778 set_user_handle_info(user, hi, 1);
1779 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1780 reply("NSMSG_PASSWORD_CHANGED");
1783 nickserv_set_email_addr(hi, hi->cookie->data);
1784 reply("NSMSG_EMAIL_CHANGED");
1787 set_user_handle_info(user, hi, 1);
1788 reply("NSMSG_AUTH_SUCCESS");
1791 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
1792 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
1796 nickserv_eat_cookie(hi->cookie);
1801 static NICKSERV_FUNC(cmd_oregnick) {
1803 struct handle_info *target;
1804 struct nick_info *ni;
1806 NICKSERV_MIN_PARMS(3);
1807 if (!(target = modcmd_get_handle_info(user, argv[1])))
1810 if (!is_registerable_nick(nick)) {
1811 reply("NSMSG_BAD_NICK", nick);
1814 ni = dict_find(nickserv_nick_dict, nick, NULL);
1816 reply("NSMSG_NICK_EXISTS", nick);
1819 register_nick(nick, target);
1820 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
1824 static NICKSERV_FUNC(cmd_regnick) {
1826 struct nick_info *ni;
1828 if (!is_registerable_nick(user->nick)) {
1829 reply("NSMSG_BAD_NICK", user->nick);
1832 /* count their nicks, see if it's too many */
1833 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
1834 if (n >= nickserv_conf.nicks_per_handle) {
1835 reply("NSMSG_TOO_MANY_NICKS");
1838 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
1840 reply("NSMSG_NICK_EXISTS", user->nick);
1843 register_nick(user->nick, user->handle_info);
1844 reply("NSMSG_REGNICK_SUCCESS", user->nick);
1848 static NICKSERV_FUNC(cmd_pass)
1850 struct handle_info *hi;
1851 const char *old_pass, *new_pass;
1853 NICKSERV_MIN_PARMS(3);
1854 hi = user->handle_info;
1858 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
1859 if (!checkpass(old_pass, hi->passwd)) {
1860 argv[1] = "BADPASS";
1861 reply("NSMSG_PASSWORD_INVALID");
1864 cryptpass(new_pass, hi->passwd);
1866 reply("NSMSG_PASS_SUCCESS");
1871 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
1874 char *new_mask = canonicalize_hostmask(strdup(mask));
1875 for (i=0; i<hi->masks->used; i++) {
1876 if (!irccasecmp(new_mask, hi->masks->list[i])) {
1877 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
1882 string_list_append(hi->masks, new_mask);
1883 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
1887 static NICKSERV_FUNC(cmd_addmask)
1890 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1891 int res = nickserv_addmask(user, user->handle_info, mask);
1895 if (!is_gline(argv[1])) {
1896 reply("NSMSG_MASK_INVALID", argv[1]);
1899 return nickserv_addmask(user, user->handle_info, argv[1]);
1903 static NICKSERV_FUNC(cmd_oaddmask)
1905 struct handle_info *hi;
1907 NICKSERV_MIN_PARMS(3);
1908 if (!(hi = get_victim_oper(user, argv[1])))
1910 return nickserv_addmask(user, hi, argv[2]);
1914 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask)
1917 for (i=0; i<hi->masks->used; i++) {
1918 if (!strcmp(del_mask, hi->masks->list[i])) {
1919 char *old_mask = hi->masks->list[i];
1920 if (hi->masks->used == 1) {
1921 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
1924 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
1925 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
1930 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
1934 static NICKSERV_FUNC(cmd_delmask)
1936 NICKSERV_MIN_PARMS(2);
1937 return nickserv_delmask(user, user->handle_info, argv[1]);
1940 static NICKSERV_FUNC(cmd_odelmask)
1942 struct handle_info *hi;
1943 NICKSERV_MIN_PARMS(3);
1944 if (!(hi = get_victim_oper(user, argv[1])))
1946 return nickserv_delmask(user, hi, argv[2]);
1950 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
1951 unsigned int nn, add = 1, pos;
1952 unsigned long added, removed, flag;
1954 for (added=removed=nn=0; str[nn]; nn++) {
1956 case '+': add = 1; break;
1957 case '-': add = 0; break;
1959 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
1960 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
1963 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
1964 /* cheesy avoidance of looking up the flag name.. */
1965 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
1968 flag = 1 << (pos - 1);
1970 added |= flag, removed &= ~flag;
1972 removed |= flag, added &= ~flag;
1977 *premoved = removed;
1982 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
1984 unsigned long before, after, added, removed;
1985 struct userNode *uNode;
1987 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
1988 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
1990 hi->flags = (hi->flags | added) & ~removed;
1991 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
1993 /* Strip helping flag if they're only a support helper and not
1994 * currently in #support. */
1995 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
1996 struct channelList *schannels;
1998 schannels = chanserv_support_channels();
1999 for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
2000 for (ii = 0; ii < schannels->used; ++ii)
2001 if (GetUserMode(schannels->list[ii], uNode))
2003 if (ii < schannels->used)
2007 HANDLE_CLEAR_FLAG(hi, HELPING);
2010 if (after && !before) {
2011 /* Add user to current helper list. */
2012 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2013 userList_append(&curr_helpers, uNode);
2014 } else if (!after && before) {
2015 /* Remove user from current helper list. */
2016 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2017 userList_remove(&curr_helpers, uNode);
2024 set_list(struct userNode *user, struct handle_info *hi, int override)
2028 char *set_display[] = {
2029 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2030 "EMAIL", "ANNOUNCEMENTS", "MAXLOGINS", "LANGUAGE"
2033 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2035 /* Do this so options are presented in a consistent order. */
2036 for (i = 0; i < ArrayLength(set_display); ++i)
2037 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2038 opt(user, hi, override, 0, NULL);
2041 static NICKSERV_FUNC(cmd_set)
2043 struct handle_info *hi;
2046 hi = user->handle_info;
2048 set_list(user, hi, 0);
2051 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2052 reply("NSMSG_INVALID_OPTION", argv[1]);
2055 return opt(user, hi, 0, argc-1, argv+1);
2058 static NICKSERV_FUNC(cmd_oset)
2060 struct handle_info *hi;
2063 NICKSERV_MIN_PARMS(2);
2065 if (!(hi = get_victim_oper(user, argv[1])))
2069 set_list(user, hi, 0);
2073 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2074 reply("NSMSG_INVALID_OPTION", argv[2]);
2078 return opt(user, hi, 1, argc-2, argv+2);
2081 static OPTION_FUNC(opt_info)
2085 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2087 hi->infoline = NULL;
2089 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2093 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2094 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2098 static OPTION_FUNC(opt_width)
2101 hi->screen_width = strtoul(argv[1], NULL, 0);
2103 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2104 hi->screen_width = MIN_LINE_SIZE;
2105 else if (hi->screen_width > MAX_LINE_SIZE)
2106 hi->screen_width = MAX_LINE_SIZE;
2108 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2112 static OPTION_FUNC(opt_tablewidth)
2115 hi->table_width = strtoul(argv[1], NULL, 0);
2117 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2118 hi->table_width = MIN_LINE_SIZE;
2119 else if (hi->screen_width > MAX_LINE_SIZE)
2120 hi->table_width = MAX_LINE_SIZE;
2122 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2126 static OPTION_FUNC(opt_color)
2129 if (enabled_string(argv[1]))
2130 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2131 else if (disabled_string(argv[1]))
2132 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2134 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2139 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2143 static OPTION_FUNC(opt_privmsg)
2146 if (enabled_string(argv[1]))
2147 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2148 else if (disabled_string(argv[1]))
2149 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2151 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2156 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2160 static OPTION_FUNC(opt_style)
2165 if (!irccasecmp(argv[1], "Zoot"))
2166 hi->userlist_style = HI_STYLE_ZOOT;
2167 else if (!irccasecmp(argv[1], "def"))
2168 hi->userlist_style = HI_STYLE_DEF;
2171 switch (hi->userlist_style) {
2180 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2184 static OPTION_FUNC(opt_announcements)
2189 if (enabled_string(argv[1]))
2190 hi->announcements = 'y';
2191 else if (disabled_string(argv[1]))
2192 hi->announcements = 'n';
2193 else if (!strcmp(argv[1], "?") || !irccasecmp(argv[1], "default"))
2194 hi->announcements = '?';
2196 send_message(user, nickserv, "NSMSG_INVALID_ANNOUNCE", argv[1]);
2201 switch (hi->announcements) {
2202 case 'y': choice = user_find_message(user, "MSG_ON"); break;
2203 case 'n': choice = user_find_message(user, "MSG_OFF"); break;
2204 case '?': choice = "default"; break;
2205 default: choice = "unknown"; break;
2207 send_message(user, nickserv, "NSMSG_SET_ANNOUNCEMENTS", choice);
2211 static OPTION_FUNC(opt_password)
2214 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2219 cryptpass(argv[1], hi->passwd);
2221 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2225 static OPTION_FUNC(opt_flags)
2228 unsigned int ii, flen;
2231 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2236 nickserv_apply_flags(user, hi, argv[1]);
2238 for (ii = flen = 0; handle_flags[ii]; ii++)
2239 if (hi->flags & (1 << ii))
2240 flags[flen++] = handle_flags[ii];
2243 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2245 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2249 static OPTION_FUNC(opt_email)
2253 if (!is_valid_email_addr(argv[1])) {
2254 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2257 if ((str = sendmail_prohibited_address(argv[1]))) {
2258 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2261 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2262 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2264 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2266 nickserv_set_email_addr(hi, argv[1]);
2268 nickserv_eat_cookie(hi->cookie);
2269 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2272 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2276 static OPTION_FUNC(opt_maxlogins)
2278 unsigned char maxlogins;
2280 maxlogins = strtoul(argv[1], NULL, 0);
2281 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2282 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2285 hi->maxlogins = maxlogins;
2287 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2288 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2292 static OPTION_FUNC(opt_language)
2294 struct language *lang;
2296 lang = language_find(argv[1]);
2297 if (irccasecmp(lang->name, argv[1]))
2298 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2299 hi->language = lang;
2301 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2306 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2307 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2309 if ((user->handle_info->opserv_level < target->opserv_level)
2310 || ((user->handle_info->opserv_level == target->opserv_level)
2311 && (user->handle_info->opserv_level < 1000))) {
2312 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2315 if ((user->handle_info->opserv_level < new_level)
2316 || ((user->handle_info->opserv_level == new_level)
2317 && (user->handle_info->opserv_level < 1000))) {
2318 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2321 if (user->handle_info == target) {
2322 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2325 if (target->opserv_level == new_level)
2327 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2328 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2329 target->opserv_level = new_level;
2333 static OPTION_FUNC(opt_level)
2338 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2342 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2343 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2347 static OPTION_FUNC(opt_epithet)
2350 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2354 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2355 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2358 if ((epithet[0] == '*') && !epithet[1])
2361 hi->epithet = strdup(epithet);
2365 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2367 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2371 static NICKSERV_FUNC(cmd_reclaim)
2373 struct handle_info *hi;
2374 struct nick_info *ni;
2375 struct userNode *victim;
2377 NICKSERV_MIN_PARMS(2);
2378 hi = user->handle_info;
2379 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2381 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2384 if (ni->owner != user->handle_info) {
2385 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2388 victim = GetUserH(ni->nick);
2390 reply("MSG_NICK_UNKNOWN", ni->nick);
2393 if (victim == user) {
2394 reply("NSMSG_NICK_USER_YOU");
2397 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2398 switch (nickserv_conf.reclaim_action) {
2399 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2400 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2401 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2402 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2407 static NICKSERV_FUNC(cmd_unregnick)
2410 struct handle_info *hi;
2411 struct nick_info *ni;
2413 hi = user->handle_info;
2414 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2415 ni = dict_find(nickserv_nick_dict, nick, NULL);
2417 reply("NSMSG_UNKNOWN_NICK", nick);
2420 if (hi != ni->owner) {
2421 reply("NSMSG_NOT_YOUR_NICK", nick);
2424 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2429 static NICKSERV_FUNC(cmd_ounregnick)
2431 struct nick_info *ni;
2433 NICKSERV_MIN_PARMS(2);
2434 if (!(ni = get_nick_info(argv[1]))) {
2435 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2438 if (ni->owner->opserv_level >= user->handle_info->opserv_level) {
2439 reply("MSG_USER_OUTRANKED", ni->nick);
2442 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2447 static NICKSERV_FUNC(cmd_unregister)
2449 struct handle_info *hi;
2452 NICKSERV_MIN_PARMS(2);
2453 hi = user->handle_info;
2456 if (checkpass(passwd, hi->passwd)) {
2457 nickserv_unregister_handle(hi, user);
2460 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2461 reply("NSMSG_PASSWORD_INVALID");
2466 static NICKSERV_FUNC(cmd_ounregister)
2468 struct handle_info *hi;
2470 NICKSERV_MIN_PARMS(2);
2471 if (!(hi = get_victim_oper(user, argv[1])))
2473 nickserv_unregister_handle(hi, user);
2477 static NICKSERV_FUNC(cmd_status)
2479 if (nickserv_conf.disable_nicks) {
2480 reply("NSMSG_GLOBAL_STATS_NONICK",
2481 dict_size(nickserv_handle_dict));
2483 if (user->handle_info) {
2485 struct nick_info *ni;
2486 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2487 reply("NSMSG_HANDLE_STATS", cnt);
2489 reply("NSMSG_HANDLE_NONE");
2491 reply("NSMSG_GLOBAL_STATS",
2492 dict_size(nickserv_handle_dict),
2493 dict_size(nickserv_nick_dict));
2498 static NICKSERV_FUNC(cmd_ghost)
2500 struct userNode *target;
2501 char reason[MAXLEN];
2503 NICKSERV_MIN_PARMS(2);
2504 if (!(target = GetUserH(argv[1]))) {
2505 reply("MSG_NICK_UNKNOWN", argv[1]);
2508 if (target == user) {
2509 reply("NSMSG_CANNOT_GHOST_SELF");
2512 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2513 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2516 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2517 DelUser(target, nickserv, 1, reason);
2518 reply("NSMSG_GHOST_KILLED", argv[1]);
2522 static NICKSERV_FUNC(cmd_vacation)
2524 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2525 reply("NSMSG_ON_VACATION");
2530 nickserv_saxdb_write(struct saxdb_context *ctx) {
2532 struct handle_info *hi;
2535 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2537 #ifdef WITH_PROTOCOL_BAHAMUT
2540 saxdb_start_record(ctx, iter_key(it), 0);
2541 if (hi->announcements != '?') {
2542 flags[0] = hi->announcements;
2544 saxdb_write_string(ctx, KEY_ANNOUNCEMENTS, flags);
2547 struct handle_cookie *cookie = hi->cookie;
2550 switch (cookie->type) {
2551 case ACTIVATION: type = KEY_ACTIVATION; break;
2552 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2553 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2554 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2555 default: type = NULL; break;
2558 saxdb_start_record(ctx, KEY_COOKIE, 0);
2559 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2560 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2562 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2563 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2564 saxdb_end_record(ctx);
2568 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2570 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2574 for (ii=flen=0; handle_flags[ii]; ++ii)
2575 if (hi->flags & (1 << ii))
2576 flags[flen++] = handle_flags[ii];
2578 saxdb_write_string(ctx, KEY_FLAGS, flags);
2580 #ifdef WITH_PROTOCOL_BAHAMUT
2581 saxdb_write_int(ctx, KEY_ID, hi->id);
2584 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2585 if (hi->last_quit_host[0])
2586 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2587 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2588 if (hi->masks->used)
2589 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2591 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2593 struct string_list *slist;
2594 struct nick_info *ni;
2596 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2597 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2598 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2602 if (hi->opserv_level)
2603 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2604 if (hi->language != lang_C)
2605 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2606 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2607 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2608 if (hi->screen_width)
2609 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2610 if (hi->table_width)
2611 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2612 flags[0] = hi->userlist_style;
2614 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2615 saxdb_end_record(ctx);
2620 static handle_merge_func_t *handle_merge_func_list;
2621 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2624 reg_handle_merge_func(handle_merge_func_t func)
2626 if (handle_merge_func_used == handle_merge_func_size) {
2627 if (handle_merge_func_size) {
2628 handle_merge_func_size <<= 1;
2629 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2631 handle_merge_func_size = 8;
2632 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2635 handle_merge_func_list[handle_merge_func_used++] = func;
2638 static NICKSERV_FUNC(cmd_merge)
2640 struct handle_info *hi_from, *hi_to;
2641 struct userNode *last_user;
2642 struct userData *cList, *cListNext;
2643 unsigned int ii, jj, n;
2644 char buffer[MAXLEN];
2646 NICKSERV_MIN_PARMS(3);
2648 if (!(hi_from = get_victim_oper(user, argv[1])))
2650 if (!(hi_to = get_victim_oper(user, argv[2])))
2652 if (hi_to == hi_from) {
2653 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2657 for (n=0; n<handle_merge_func_used; n++)
2658 handle_merge_func_list[n](user, hi_to, hi_from);
2660 /* Append "from" handle's nicks to "to" handle's nick list. */
2662 struct nick_info *last_ni;
2663 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
2664 last_ni->next = hi_from->nicks;
2666 while (hi_from->nicks) {
2667 hi_from->nicks->owner = hi_to;
2668 hi_from->nicks = hi_from->nicks->next;
2671 /* Merge the hostmasks. */
2672 for (ii=0; ii<hi_from->masks->used; ii++) {
2673 char *mask = hi_from->masks->list[ii];
2674 for (jj=0; jj<hi_to->masks->used; jj++)
2675 if (match_ircglobs(hi_to->masks->list[jj], mask))
2677 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
2678 string_list_append(hi_to->masks, strdup(mask));
2681 /* Merge the lists of authed users. */
2683 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
2684 last_user->next_authed = hi_from->users;
2686 hi_to->users = hi_from->users;
2688 /* Repoint the old "from" handle's users. */
2689 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
2690 last_user->handle_info = hi_to;
2692 hi_from->users = NULL;
2694 /* Merge channel userlists. */
2695 for (cList=hi_from->channels; cList; cList=cListNext) {
2696 struct userData *cList2;
2697 cListNext = cList->u_next;
2698 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
2699 if (cList->channel == cList2->channel)
2701 log_module(NS_LOG, LOG_DEBUG, "Merging %s->%s@%s: before %p->%p->%-p, %p->%p->%p",
2702 hi_from->handle, hi_to->handle, cList->channel->channel->name,
2703 cList->u_prev, cList, cList->u_next,
2704 (cList2?cList2->u_prev:0), cList2, (cList2?cList2->u_next:0));
2705 if (cList2 && (cList2->access >= cList->access)) {
2706 /* keep cList2 in hi_to; remove cList from hi_from */
2707 log_module(NS_LOG, LOG_DEBUG, "Deleting %p", cList);
2708 del_channel_user(cList, 1);
2711 /* remove the lower-ranking cList2 from hi_to */
2712 log_module(NS_LOG, LOG_DEBUG, "Deleting %p", cList2);
2713 del_channel_user(cList2, 1);
2715 /* cList needs to be moved from hi_from to hi_to */
2716 cList->handle = hi_to;
2717 /* Remove from linked list for hi_from */
2718 assert(!cList->u_prev);
2719 hi_from->channels = cList->u_next;
2721 cList->u_next->u_prev = cList->u_prev;
2722 /* Add to linked list for hi_to */
2723 cList->u_prev = NULL;
2724 cList->u_next = hi_to->channels;
2725 if (hi_to->channels)
2726 hi_to->channels->u_prev = cList;
2727 hi_to->channels = cList;
2728 log_module(NS_LOG, LOG_DEBUG, "Now %p->%p->%p",
2729 cList->u_prev, cList, cList->u_next);
2733 /* Do they get an OpServ level promotion? */
2734 if (hi_from->opserv_level > hi_to->opserv_level)
2735 hi_to->opserv_level = hi_from->opserv_level;
2737 /* What about last seen time? */
2738 if (hi_from->lastseen > hi_to->lastseen)
2739 hi_to->lastseen = hi_from->lastseen;
2741 /* Notify of success. */
2742 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
2743 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
2744 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
2746 /* Unregister the "from" handle. */
2747 nickserv_unregister_handle(hi_from, NULL);
2752 struct nickserv_discrim {
2753 unsigned int limit, min_level, max_level;
2754 unsigned long flags_on, flags_off;
2755 time_t min_registered, max_registered;
2757 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
2758 const char *nickmask;
2759 const char *hostmask;
2760 const char *handlemask;
2761 const char *emailmask;
2764 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
2766 struct discrim_apply_info {
2767 struct nickserv_discrim *discrim;
2768 discrim_search_func func;
2769 struct userNode *source;
2770 unsigned int matched;
2773 static struct nickserv_discrim *
2774 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
2777 struct nickserv_discrim *discrim;
2779 discrim = malloc(sizeof(*discrim));
2780 memset(discrim, 0, sizeof(*discrim));
2781 discrim->min_level = 0;
2782 discrim->max_level = ~0;
2783 discrim->limit = 50;
2784 discrim->min_registered = 0;
2785 discrim->max_registered = INT_MAX;
2786 discrim->lastseen = now;
2788 for (i=0; i<argc; i++) {
2789 if (i == argc - 1) {
2790 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2793 if (!irccasecmp(argv[i], "limit")) {
2794 discrim->limit = strtoul(argv[++i], NULL, 0);
2795 } else if (!irccasecmp(argv[i], "flags")) {
2796 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
2797 } else if (!irccasecmp(argv[i], "registered")) {
2798 const char *cmp = argv[++i];
2799 if (cmp[0] == '<') {
2800 if (cmp[1] == '=') {
2801 discrim->min_registered = now - ParseInterval(cmp+2);
2803 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
2805 } else if (cmp[0] == '=') {
2806 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
2807 } else if (cmp[0] == '>') {
2808 if (cmp[1] == '=') {
2809 discrim->max_registered = now - ParseInterval(cmp+2);
2811 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
2814 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2816 } else if (!irccasecmp(argv[i], "seen")) {
2817 discrim->lastseen = now - ParseInterval(argv[++i]);
2818 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
2819 discrim->nickmask = argv[++i];
2820 } else if (!irccasecmp(argv[i], "hostmask")) {
2822 if (!irccasecmp(argv[i], "exact")) {
2823 if (i == argc - 1) {
2824 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2827 discrim->hostmask_type = EXACT;
2828 } else if (!irccasecmp(argv[i], "subset")) {
2829 if (i == argc - 1) {
2830 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2833 discrim->hostmask_type = SUBSET;
2834 } else if (!irccasecmp(argv[i], "superset")) {
2835 if (i == argc - 1) {
2836 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2839 discrim->hostmask_type = SUPERSET;
2840 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
2841 if (i == argc - 1) {
2842 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2845 discrim->hostmask_type = LASTQUIT;
2848 discrim->hostmask_type = SUPERSET;
2850 discrim->hostmask = argv[++i];
2851 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
2852 if (!irccasecmp(argv[++i], "*")) {
2853 discrim->handlemask = 0;
2855 discrim->handlemask = argv[i];
2857 } else if (!irccasecmp(argv[i], "email")) {
2858 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
2859 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
2861 } else if (!irccasecmp(argv[++i], "*")) {
2862 discrim->emailmask = 0;
2864 discrim->emailmask = argv[i];
2866 } else if (!irccasecmp(argv[i], "access")) {
2867 const char *cmp = argv[++i];
2868 if (cmp[0] == '<') {
2869 if (discrim->min_level == 0) discrim->min_level = 1;
2870 if (cmp[1] == '=') {
2871 discrim->max_level = strtoul(cmp+2, NULL, 0);
2873 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
2875 } else if (cmp[0] == '=') {
2876 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
2877 } else if (cmp[0] == '>') {
2878 if (cmp[1] == '=') {
2879 discrim->min_level = strtoul(cmp+2, NULL, 0);
2881 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
2884 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2887 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
2898 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
2900 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
2901 || (discrim->flags_off & hi->flags)
2902 || (discrim->min_registered > hi->registered)
2903 || (discrim->max_registered < hi->registered)
2904 || (discrim->lastseen < (hi->users?now:hi->lastseen))
2905 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
2906 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
2907 || (discrim->min_level > hi->opserv_level)
2908 || (discrim->max_level < hi->opserv_level)) {
2911 if (discrim->hostmask) {
2913 for (i=0; i<hi->masks->used; i++) {
2914 const char *mask = hi->masks->list[i];
2915 if ((discrim->hostmask_type == SUBSET)
2916 && (match_ircglobs(discrim->hostmask, mask))) break;
2917 else if ((discrim->hostmask_type == EXACT)
2918 && !irccasecmp(discrim->hostmask, mask)) break;
2919 else if ((discrim->hostmask_type == SUPERSET)
2920 && (match_ircglobs(mask, discrim->hostmask))) break;
2921 else if ((discrim->hostmask_type == LASTQUIT)
2922 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
2924 if (i==hi->masks->used) return 0;
2926 if (discrim->nickmask) {
2927 struct nick_info *nick = hi->nicks;
2929 if (match_ircglob(nick->nick, discrim->nickmask)) break;
2932 if (!nick) return 0;
2938 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
2940 dict_iterator_t it, next;
2941 unsigned int matched;
2943 for (it = dict_first(nickserv_handle_dict), matched = 0;
2944 it && (matched < discrim->limit);
2946 next = iter_next(it);
2947 if (nickserv_discrim_match(discrim, iter_data(it))) {
2948 dsf(source, iter_data(it));
2956 search_print_func(struct userNode *source, struct handle_info *match)
2958 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
2962 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
2967 search_unregister_func (struct userNode *source, struct handle_info *match)
2969 if (oper_has_access(source, nickserv, match->opserv_level, 0))
2970 nickserv_unregister_handle(match, source);
2974 nickserv_sort_accounts_by_access(const void *a, const void *b)
2976 const struct handle_info *hi_a = *(const struct handle_info**)a;
2977 const struct handle_info *hi_b = *(const struct handle_info**)b;
2978 if (hi_a->opserv_level != hi_b->opserv_level)
2979 return hi_b->opserv_level - hi_a->opserv_level;
2980 return irccasecmp(hi_a->handle, hi_b->handle);
2984 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
2986 struct handle_info_list hil;
2987 struct helpfile_table tbl;
2992 memset(&hil, 0, sizeof(hil));
2993 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2994 struct handle_info *hi = iter_data(it);
2995 if (hi->opserv_level)
2996 handle_info_list_append(&hil, hi);
2998 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
2999 tbl.length = hil.used + 1;
3001 tbl.flags = TABLE_NO_FREE;
3002 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3003 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3006 for (ii = 0; ii < hil.used; ) {
3007 ary = malloc(tbl.width * sizeof(ary[0]));
3008 ary[0] = hil.list[ii]->handle;
3009 ary[1] = strtab(hil.list[ii]->opserv_level);
3010 tbl.contents[++ii] = ary;
3012 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3013 reply("MSG_MATCH_COUNT", hil.used);
3014 for (ii = 0; ii < hil.used; )
3015 free(tbl.contents[++ii]);
3020 static NICKSERV_FUNC(cmd_search)
3022 struct nickserv_discrim *discrim;
3023 discrim_search_func action;
3024 struct svccmd *subcmd;
3025 unsigned int matches;
3028 NICKSERV_MIN_PARMS(3);
3029 sprintf(buf, "search %s", argv[1]);
3030 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3031 if (!irccasecmp(argv[1], "print"))
3032 action = search_print_func;
3033 else if (!irccasecmp(argv[1], "count"))
3034 action = search_count_func;
3035 else if (!irccasecmp(argv[1], "unregister"))
3036 action = search_unregister_func;
3038 reply("NSMSG_INVALID_ACTION", argv[1]);
3042 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3045 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3049 if (action == search_print_func)
3050 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3051 else if (action == search_count_func)
3052 discrim->limit = INT_MAX;
3054 matches = nickserv_discrim_search(discrim, action, user);
3057 reply("MSG_MATCH_COUNT", matches);
3059 reply("MSG_NO_MATCHES");
3065 static MODCMD_FUNC(cmd_checkpass)
3067 struct handle_info *hi;
3069 NICKSERV_MIN_PARMS(3);
3070 if (!(hi = get_handle_info(argv[1]))) {
3071 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3074 if (checkpass(argv[2], hi->passwd))
3075 reply("CHECKPASS_YES");
3077 reply("CHECKPASS_NO");
3083 nickserv_db_read_handle(const char *handle, dict_t obj)
3086 struct string_list *masks, *slist;
3087 struct handle_info *hi;
3088 struct userNode *authed_users;
3089 unsigned long int id;
3093 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3094 id = str ? strtoul(str, NULL, 0) : 0;
3095 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3097 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3100 if ((hi = get_handle_info(handle))) {
3101 authed_users = hi->users;
3103 dict_remove(nickserv_handle_dict, hi->handle);
3105 authed_users = NULL;
3107 hi = register_handle(handle, str, id);
3109 hi->users = authed_users;
3110 while (authed_users) {
3111 authed_users->handle_info = hi;
3112 authed_users = authed_users->next_authed;
3115 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3116 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3117 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3118 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3119 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3120 hi->language = language_find(str ? str : "C");
3121 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3122 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3123 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3125 hi->infoline = strdup(str);
3126 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3127 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
3128 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3129 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
3130 /* We want to read the nicks even if disable_nicks is set. This is so
3131 * that we don't lose the nick data entirely. */
3132 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3134 for (ii=0; ii<slist->used; ii++)
3135 register_nick(slist->list[ii], hi);
3137 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3139 for (ii=0; str[ii]; ii++)
3140 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3142 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3143 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3144 str = database_get_data(obj, KEY_ANNOUNCEMENTS, RECDB_QSTRING);
3145 hi->announcements = str ? str[0] : '?';
3146 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3147 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3148 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3149 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3150 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3152 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3154 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3155 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3157 nickserv_set_email_addr(hi, str);
3158 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3160 hi->epithet = strdup(str);
3161 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3163 const char *data, *type, *expires, *cookie_str;
3164 struct handle_cookie *cookie;
3166 cookie = calloc(1, sizeof(*cookie));
3167 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3168 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3169 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3170 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3171 if (!type || !expires || !cookie_str) {
3172 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3175 if (!irccasecmp(type, KEY_ACTIVATION))
3176 cookie->type = ACTIVATION;
3177 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3178 cookie->type = PASSWORD_CHANGE;
3179 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3180 cookie->type = EMAIL_CHANGE;
3181 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3182 cookie->type = ALLOWAUTH;
3184 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3187 cookie->expires = strtoul(expires, NULL, 0);
3188 if (cookie->expires < now)
3191 cookie->data = strdup(data);
3192 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3196 nickserv_bake_cookie(cookie);
3198 nickserv_free_cookie(cookie);
3203 nickserv_saxdb_read(dict_t db) {
3205 struct record_data *rd;
3207 for (it=dict_first(db); it; it=iter_next(it)) {
3209 nickserv_db_read_handle(iter_key(it), rd->d.object);
3214 static NICKSERV_FUNC(cmd_mergedb)
3216 struct timeval start, stop;
3219 NICKSERV_MIN_PARMS(2);
3220 gettimeofday(&start, NULL);
3221 if (!(db = parse_database(argv[1]))) {
3222 reply("NSMSG_DB_UNREADABLE", argv[1]);
3225 nickserv_saxdb_read(db);
3227 gettimeofday(&stop, NULL);
3228 stop.tv_sec -= start.tv_sec;
3229 stop.tv_usec -= start.tv_usec;
3230 if (stop.tv_usec < 0) {
3232 stop.tv_usec += 1000000;
3234 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
3239 expire_handles(UNUSED_ARG(void *data))
3241 dict_iterator_t it, next;
3243 struct handle_info *hi;
3245 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3246 next = iter_next(it);
3248 if ((hi->opserv_level > 0)
3250 || HANDLE_FLAGGED(hi, FROZEN)
3251 || HANDLE_FLAGGED(hi, NODELETE)) {
3254 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3255 if ((now - hi->lastseen) > expiry) {
3256 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3257 nickserv_unregister_handle(hi, NULL);
3261 if (nickserv_conf.handle_expire_frequency)
3262 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3266 nickserv_load_dict(const char *fname)
3270 if (!(file = fopen(fname, "r"))) {
3271 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3274 while (!feof(file)) {
3275 fgets(line, sizeof(line), file);
3278 if (line[strlen(line)-1] == '\n')
3279 line[strlen(line)-1] = 0;
3280 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3283 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3286 static enum reclaim_action
3287 reclaim_action_from_string(const char *str) {
3289 return RECLAIM_NONE;
3290 else if (!irccasecmp(str, "warn"))
3291 return RECLAIM_WARN;
3292 else if (!irccasecmp(str, "svsnick"))
3293 return RECLAIM_SVSNICK;
3294 else if (!irccasecmp(str, "kill"))
3295 return RECLAIM_KILL;
3297 return RECLAIM_NONE;
3301 nickserv_conf_read(void)
3303 dict_t conf_node, child;
3307 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3308 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3311 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3313 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3314 if (nickserv_conf.valid_handle_regex_set) regfree(&nickserv_conf.valid_handle_regex);
3316 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3317 nickserv_conf.valid_handle_regex_set = !err;
3318 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3320 nickserv_conf.valid_handle_regex_set = 0;
3322 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3323 if (nickserv_conf.valid_nick_regex_set) regfree(&nickserv_conf.valid_nick_regex);
3325 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3326 nickserv_conf.valid_nick_regex_set = !err;
3327 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3329 nickserv_conf.valid_nick_regex_set = 0;
3331 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3333 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3334 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3335 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3336 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3337 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3338 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3339 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3340 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3341 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3342 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3343 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3344 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3345 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3346 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3347 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3348 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3349 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3350 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3351 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3352 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3353 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3355 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3356 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3357 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3359 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3360 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3361 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3363 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3364 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3365 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3366 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3367 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3368 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3369 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3370 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3371 if (!nickserv_conf.disable_nicks) {
3372 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3373 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3374 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3375 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3376 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3377 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3378 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3379 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3381 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3382 for (it=dict_first(child); it; it=iter_next(it)) {
3383 const char *key = iter_key(it), *value;
3387 if (!strncasecmp(key, "uc_", 3))
3388 flag = toupper(key[3]);
3389 else if (!strncasecmp(key, "lc_", 3))
3390 flag = tolower(key[3]);
3394 if ((pos = handle_inverse_flags[flag])) {
3395 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3396 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3399 if (nickserv_conf.weak_password_dict)
3400 dict_delete(nickserv_conf.weak_password_dict);
3401 nickserv_conf.weak_password_dict = dict_new();
3402 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3403 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3404 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3405 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3407 nickserv_load_dict(str);
3408 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3409 if (nickserv && str)
3410 NickChange(nickserv, str, 0);
3411 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3412 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3413 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3414 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3415 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3416 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3417 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3418 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3419 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3420 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3421 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3422 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3423 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3424 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3425 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3426 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3427 str = conf_get_data("server/network", RECDB_QSTRING);
3428 nickserv_conf.network_name = str ? str : "some IRC network";
3429 if (!nickserv_conf.auth_policer_params) {
3430 nickserv_conf.auth_policer_params = policer_params_new();
3431 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3432 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3434 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3435 for (it=dict_first(child); it; it=iter_next(it))
3436 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3440 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3441 char newnick[NICKLEN+1];
3450 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3452 case RECLAIM_SVSNICK:
3454 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3455 } while (GetUserH(newnick));
3456 irc_svsnick(nickserv, user, newnick);
3459 irc_kill(nickserv, user, "NSMSG_RECLAIM_KILL");
3465 nickserv_reclaim_p(void *data) {
3466 struct userNode *user = data;
3467 struct nick_info *ni = get_nick_info(user->nick);
3469 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3473 check_user_nick(struct userNode *user) {
3474 struct nick_info *ni;
3475 user->modes &= ~FLAGS_REGNICK;
3476 if (!(ni = get_nick_info(user->nick)))
3478 if (user->handle_info == ni->owner) {
3479 user->modes |= FLAGS_REGNICK;
3483 if (nickserv_conf.warn_nick_owned)
3484 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3485 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3487 if (nickserv_conf.auto_reclaim_delay)
3488 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3490 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3495 handle_new_user(struct userNode *user)
3497 return check_user_nick(user);
3501 handle_account(struct userNode *user, const char *stamp)
3503 struct handle_info *hi;
3505 #ifdef WITH_PROTOCOL_P10
3506 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3508 hi = dict_find(nickserv_id_dict, stamp, NULL);
3512 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3515 set_user_handle_info(user, hi, 0);
3517 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
3522 handle_nick_change(struct userNode *user, const char *old_nick)
3524 struct handle_info *hi;
3526 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
3527 dict_remove(nickserv_allow_auth_dict, old_nick);
3528 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
3530 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3531 check_user_nick(user);
3535 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
3537 dict_remove(nickserv_allow_auth_dict, user->nick);
3538 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3539 set_user_handle_info(user, NULL, 0);
3542 static struct modcmd *
3543 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
3545 if (min_level > 0) {
3547 sprintf(buf, "%u", min_level);
3548 if (must_be_qualified) {
3549 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
3551 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
3553 } else if (min_level == 0) {
3554 if (must_be_qualified) {
3555 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3557 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3560 if (must_be_qualified) {
3561 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
3563 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
3569 nickserv_db_cleanup(void)
3571 unreg_del_user_func(nickserv_remove_user);
3572 userList_clean(&curr_helpers);
3573 policer_params_delete(nickserv_conf.auth_policer_params);
3574 dict_delete(nickserv_handle_dict);
3575 dict_delete(nickserv_nick_dict);
3576 dict_delete(nickserv_opt_dict);
3577 dict_delete(nickserv_allow_auth_dict);
3578 dict_delete(nickserv_email_dict);
3579 dict_delete(nickserv_id_dict);
3580 dict_delete(nickserv_conf.weak_password_dict);
3581 free(auth_func_list);
3582 free(unreg_func_list);
3584 free(allowauth_func_list);
3585 free(handle_merge_func_list);
3586 free(failpw_func_list);
3587 if (nickserv_conf.valid_handle_regex_set)
3588 regfree(&nickserv_conf.valid_handle_regex);
3589 if (nickserv_conf.valid_nick_regex_set)
3590 regfree(&nickserv_conf.valid_nick_regex);
3594 init_nickserv(const char *nick)
3597 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
3598 reg_new_user_func(handle_new_user);
3599 reg_nick_change_func(handle_nick_change);
3600 reg_del_user_func(nickserv_remove_user);
3601 reg_account_func(handle_account);
3603 /* set up handle_inverse_flags */
3604 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
3605 for (i=0; handle_flags[i]; i++) {
3606 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
3607 flag_access_levels[i] = 0;
3610 conf_register_reload(nickserv_conf_read);
3611 nickserv_opt_dict = dict_new();
3612 nickserv_email_dict = dict_new();
3613 dict_set_free_keys(nickserv_email_dict, free);
3614 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
3616 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
3617 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
3618 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
3619 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
3620 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
3621 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
3622 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
3623 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
3624 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
3625 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
3626 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
3627 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
3628 nickserv_define_func("SET", cmd_set, -1, 1, 0);
3629 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
3630 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
3631 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
3632 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
3633 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
3634 nickserv_define_func("MERGE", cmd_merge, 0, 1, 0);
3635 if (!nickserv_conf.disable_nicks) {
3636 /* nick management commands */
3637 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
3638 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
3639 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
3640 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
3641 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
3642 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
3644 if (nickserv_conf.email_enabled) {
3645 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
3646 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
3647 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
3648 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
3649 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
3651 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
3652 /* miscellaneous commands */
3653 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
3654 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
3655 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
3656 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
3657 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
3659 dict_insert(nickserv_opt_dict, "INFO", opt_info);
3660 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
3661 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
3662 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
3663 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
3664 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
3665 dict_insert(nickserv_opt_dict, "PASS", opt_password);
3666 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
3667 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
3668 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
3669 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
3670 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
3671 dict_insert(nickserv_opt_dict, "ANNOUNCEMENTS", opt_announcements);
3672 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
3673 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
3675 nickserv_handle_dict = dict_new();
3676 dict_set_free_keys(nickserv_handle_dict, free);
3677 dict_set_free_data(nickserv_handle_dict, free_handle_info);
3679 nickserv_id_dict = dict_new();
3680 dict_set_free_keys(nickserv_id_dict, free);
3682 nickserv_nick_dict = dict_new();
3683 dict_set_free_data(nickserv_nick_dict, free_nick_info);
3685 nickserv_allow_auth_dict = dict_new();
3687 userList_init(&curr_helpers);
3690 nickserv = AddService(nick, "Nick Services");
3691 nickserv_service = service_register(nickserv, 0);
3693 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
3694 reg_exit_func(nickserv_db_cleanup);
3695 if(nickserv_conf.handle_expire_frequency)
3696 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3697 message_register_table(msgtab);